Skip to content

Commit 240d29b

Browse files
feat: add stream emission permission editing (#104)
Closes CHAIN-95 --------- Co-authored-by: Luiz Carvalho <[email protected]>
1 parent 78a6481 commit 240d29b

File tree

4 files changed

+579
-42
lines changed

4 files changed

+579
-42
lines changed

pallets/permission0/src/ext/emission_impl.rs

Lines changed: 141 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::{
2+
generate_permission_id, get_total_allocated_percentage, pallet,
23
permission::{emission::*, *},
3-
*,
4+
update_permission_indices, AccumulatedStreamAmounts, BalanceOf, Config, DistributionControl,
5+
EmissionAllocation, EmissionScope, EnforcementTracking, Error, Event, NegativeImbalanceOf,
6+
Pallet, PermissionContract, PermissionDuration, PermissionId, PermissionScope, Permissions,
47
};
58

69
use pallet_permission0_api::{
@@ -10,7 +13,7 @@ use pallet_permission0_api::{
1013
};
1114
use polkadot_sdk::{
1215
frame_support::{dispatch::DispatchResult, ensure, traits::ReservableCurrency},
13-
frame_system::{self, ensure_signed_or_root},
16+
frame_system::{self, ensure_signed, ensure_signed_or_root},
1417
polkadot_sdk_frame::prelude::{BlockNumberFor, OriginFor},
1518
sp_core::{Get, TryCollect},
1619
sp_runtime::{
@@ -129,32 +132,12 @@ pub(crate) fn grant_emission_permission_impl<T: Config>(
129132
Error::<T>::NotRegisteredAgent
130133
);
131134
ensure!(grantor != grantee, Error::<T>::SelfPermissionNotAllowed);
132-
ensure!(!targets.is_empty(), Error::<T>::NoTargetsSpecified);
133135

134-
for (target, weight) in &targets {
135-
ensure!(*weight > 0, Error::<T>::InvalidTargetWeight);
136-
ensure!(
137-
T::Torus::is_agent_registered(target),
138-
Error::<T>::NotRegisteredAgent
139-
);
140-
}
136+
validate_emission_permission_target_weights::<T>(&targets)?;
141137

142138
match &allocation {
143139
EmissionAllocation::Streams(streams) => {
144-
for (stream, percentage) in streams {
145-
ensure!(*percentage <= Percent::one(), Error::<T>::InvalidPercentage);
146-
147-
let total_allocated = get_total_allocated_percentage::<T>(&grantor, stream);
148-
let new_total_allocated = match total_allocated.checked_add(percentage) {
149-
Some(new_total_allocated) => new_total_allocated,
150-
None => return Err(Error::<T>::TotalAllocationExceeded.into()),
151-
};
152-
153-
ensure!(
154-
new_total_allocated <= Percent::one(),
155-
Error::<T>::TotalAllocationExceeded
156-
);
157-
}
140+
validate_emission_permission_streams::<T>(streams, &grantor)?;
158141
}
159142
EmissionAllocation::FixedAmount(amount) => {
160143
ensure!(*amount > BalanceOf::<T>::zero(), Error::<T>::InvalidAmount);
@@ -172,22 +155,7 @@ pub(crate) fn grant_emission_permission_impl<T: Config>(
172155
}
173156
}
174157

175-
match &distribution {
176-
DistributionControl::Automatic(threshold) => {
177-
ensure!(
178-
*threshold >= T::MinAutoDistributionThreshold::get(),
179-
Error::<T>::InvalidThreshold
180-
);
181-
}
182-
DistributionControl::Interval(interval) => {
183-
ensure!(!interval.is_zero(), Error::<T>::InvalidInterval);
184-
}
185-
DistributionControl::AtBlock(block) => {
186-
let current_block = <polkadot_sdk::frame_system::Pallet<T>>::block_number();
187-
ensure!(*block > current_block, Error::<T>::InvalidInterval);
188-
}
189-
_ => {}
190-
}
158+
validate_emission_permission_distribution::<T>(&distribution)?;
191159

192160
if let Some(parent) = parent_id {
193161
let parent_contract =
@@ -362,3 +330,136 @@ pub fn toggle_permission_accumulation_impl<T: Config>(
362330

363331
Ok(())
364332
}
333+
334+
pub(crate) fn update_emission_permission<T: Config>(
335+
origin: OriginFor<T>,
336+
permission_id: PermissionId,
337+
new_targets: BoundedBTreeMap<T::AccountId, u16, T::MaxTargetsPerPermission>,
338+
new_streams: Option<BoundedBTreeMap<StreamId, Percent, T::MaxStreamsPerPermission>>,
339+
new_distribution_control: Option<DistributionControl<T>>,
340+
) -> DispatchResult {
341+
let who = ensure_signed(origin)?;
342+
343+
let permission = Permissions::<T>::get(permission_id);
344+
345+
let Some(mut permission) = permission else {
346+
return Err(Error::<T>::PermissionNotFound.into());
347+
};
348+
349+
if permission.grantor == who {
350+
if !permission.is_updatable() {
351+
return Err(Error::<T>::NotEditable.into());
352+
}
353+
} else if permission.grantee == who {
354+
if new_streams.is_some() || new_distribution_control.is_some() {
355+
return Err(Error::<T>::NotAuthorizedToEdit.into());
356+
}
357+
} else {
358+
return Err(Error::<T>::NotAuthorizedToEdit.into());
359+
}
360+
361+
let mut scope = permission.scope.clone();
362+
match &mut scope {
363+
PermissionScope::Emission(emission_scope) => {
364+
validate_emission_permission_target_weights::<T>(&new_targets)?;
365+
366+
emission_scope.targets = new_targets;
367+
368+
let EmissionAllocation::Streams(streams) = &mut emission_scope.allocation else {
369+
return Err(Error::<T>::NotEditable.into());
370+
};
371+
372+
if let Some(new_streams) = new_streams {
373+
crate::permission::emission::do_distribute_emission::<T>(
374+
permission_id,
375+
&permission,
376+
DistributionReason::Manual,
377+
);
378+
379+
for stream in streams.keys() {
380+
AccumulatedStreamAmounts::<T>::remove((
381+
&permission.grantor,
382+
stream,
383+
&permission_id,
384+
));
385+
}
386+
387+
validate_emission_permission_streams::<T>(&new_streams, &permission.grantor)?;
388+
389+
*streams = new_streams;
390+
}
391+
392+
if let Some(new_distribution_control) = new_distribution_control {
393+
validate_emission_permission_distribution::<T>(&new_distribution_control)?;
394+
395+
emission_scope.distribution = new_distribution_control;
396+
}
397+
}
398+
_ => return Err(Error::<T>::NotEditable.into()),
399+
}
400+
401+
permission.scope = scope;
402+
Permissions::<T>::set(permission_id, Some(permission));
403+
404+
Ok(())
405+
}
406+
407+
fn validate_emission_permission_target_weights<T: Config>(
408+
targets: &BoundedBTreeMap<T::AccountId, u16, T::MaxTargetsPerPermission>,
409+
) -> DispatchResult {
410+
ensure!(!targets.is_empty(), Error::<T>::NoTargetsSpecified);
411+
412+
for (target, weight) in targets {
413+
ensure!(*weight > 0, Error::<T>::InvalidTargetWeight);
414+
ensure!(
415+
T::Torus::is_agent_registered(target),
416+
Error::<T>::NotRegisteredAgent
417+
);
418+
}
419+
420+
Ok(())
421+
}
422+
fn validate_emission_permission_streams<T: Config>(
423+
streams: &BoundedBTreeMap<StreamId, Percent, T::MaxStreamsPerPermission>,
424+
grantor: &T::AccountId,
425+
) -> DispatchResult {
426+
for (stream, percentage) in streams {
427+
ensure!(*percentage <= Percent::one(), Error::<T>::InvalidPercentage);
428+
429+
let total_allocated = get_total_allocated_percentage::<T>(grantor, stream);
430+
let new_total_allocated = match total_allocated.checked_add(percentage) {
431+
Some(new_total_allocated) => new_total_allocated,
432+
None => return Err(Error::<T>::TotalAllocationExceeded.into()),
433+
};
434+
435+
ensure!(
436+
new_total_allocated <= Percent::one(),
437+
Error::<T>::TotalAllocationExceeded
438+
);
439+
}
440+
441+
Ok(())
442+
}
443+
444+
fn validate_emission_permission_distribution<T: Config>(
445+
distribution: &DistributionControl<T>,
446+
) -> DispatchResult {
447+
match distribution {
448+
DistributionControl::Automatic(threshold) => {
449+
ensure!(
450+
*threshold >= T::MinAutoDistributionThreshold::get(),
451+
Error::<T>::InvalidThreshold
452+
);
453+
}
454+
DistributionControl::Interval(interval) => {
455+
ensure!(!interval.is_zero(), Error::<T>::InvalidInterval);
456+
}
457+
DistributionControl::AtBlock(block) => {
458+
let current_block = <polkadot_sdk::frame_system::Pallet<T>>::block_number();
459+
ensure!(*block > current_block, Error::<T>::InvalidInterval);
460+
}
461+
_ => {}
462+
}
463+
464+
Ok(())
465+
}

pallets/permission0/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ pub mod pallet {
300300
NamespacePathIsInvalid,
301301
/// Exceeded amount of total namespaces allowed in a single permission.
302302
TooManyNamespaces,
303+
/// Not authorized to edit a stream emission permission.
304+
NotAuthorizedToEdit,
305+
/// Stream emission permission is not editable
306+
NotEditable,
303307
}
304308

305309
#[pallet::hooks]
@@ -448,6 +452,27 @@ pub mod pallet {
448452

449453
Ok(())
450454
}
455+
456+
/// Allows Grantor/Grantee to edit stream emission permission
457+
#[pallet::call_index(8)]
458+
#[pallet::weight(T::WeightInfo::grant_curator_permission())]
459+
pub fn update_emission_permission(
460+
origin: OriginFor<T>,
461+
permission_id: PermissionId,
462+
new_targets: BoundedBTreeMap<T::AccountId, u16, T::MaxTargetsPerPermission>,
463+
new_streams: Option<BoundedBTreeMap<StreamId, Percent, T::MaxStreamsPerPermission>>,
464+
new_distribution_control: Option<DistributionControl<T>>,
465+
) -> DispatchResult {
466+
ext::emission_impl::update_emission_permission(
467+
origin,
468+
permission_id,
469+
new_targets,
470+
new_streams,
471+
new_distribution_control,
472+
)?;
473+
474+
Ok(())
475+
}
451476
}
452477
}
453478

pallets/permission0/src/permission.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ impl<T: Config> PermissionContract<T> {
219219
}
220220
}
221221
}
222+
223+
pub fn is_updatable(&self) -> bool {
224+
let current_block = frame_system::Pallet::<T>::block_number();
225+
226+
match &self.revocation {
227+
RevocationTerms::RevocableByGrantor => true,
228+
RevocationTerms::RevocableAfter(block) => &current_block > block,
229+
_ => false,
230+
}
231+
}
222232
}
223233

224234
/// Defines what the permission applies to

0 commit comments

Comments
 (0)