Skip to content

Commit

Permalink
feat: add fees and deposit management
Browse files Browse the repository at this point in the history
  • Loading branch information
juangirini committed Feb 25, 2025
1 parent 8d05010 commit d78fe43
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 100 deletions.
65 changes: 40 additions & 25 deletions pallets/idn-manager/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use crate::{
self as pallet_idn_manager,
traits::{DiffFees, FeesDirection, FeesError},
traits::{BalanceDirection, DiffBalance, FeesError},
HoldReason, Subscription, SubscriptionTrait,
};
use codec::Encode;
Expand Down Expand Up @@ -63,46 +63,50 @@ impl<
T: Get<AccountId32>,
B: Get<Balances::Balance>,
S: SubscriptionTrait<AccountId32>,
BlockNumber: Saturating + Ord,
BlockNumber: Saturating + Ord + Clone,
Balances: Mutate<AccountId32>,
> pallet_idn_manager::FeesManager<Balances::Balance, BlockNumber, S, DispatchError, AccountId32>
for FeesManagerImpl<T, B, S, BlockNumber, Balances>
where
Balances::Balance: From<BlockNumber>,
Balances::Reason: From<HoldReason>,
{
fn calculate_subscription_fees(amount: BlockNumber) -> Balances::Balance {
fn calculate_subscription_fees(amount: &BlockNumber) -> Balances::Balance {
let base_fee = B::get();
base_fee.saturating_mul(amount.into())
base_fee.saturating_mul(amount.clone().into())
}
fn calculate_diff_fees(
old_amount: BlockNumber,
new_amount: BlockNumber,
) -> DiffFees<Balances::Balance> {
let mut direction = FeesDirection::None;
old_amount: &BlockNumber,
new_amount: &BlockNumber,
) -> DiffBalance<Balances::Balance> {
let mut direction = BalanceDirection::None;
let fees = match new_amount.cmp(&old_amount) {
Ordering::Greater => {
direction = FeesDirection::Hold;
Self::calculate_subscription_fees(new_amount.saturating_sub(old_amount))
direction = BalanceDirection::Hold;
Self::calculate_subscription_fees(
&new_amount.clone().saturating_sub(old_amount.clone()),
)
},
Ordering::Less => {
direction = FeesDirection::Release;
Self::calculate_subscription_fees(old_amount.saturating_sub(new_amount))
direction = BalanceDirection::Release;
Self::calculate_subscription_fees(
&old_amount.clone().saturating_sub(new_amount.clone()),
)
},
Ordering::Equal => Zero::zero(),
};
DiffFees { fees, direction }
DiffBalance { balance: fees, direction }
}
fn collect_fees(
fees: Balances::Balance,
sub: S,
fees: &Balances::Balance,
sub: &S,
) -> Result<Balances::Balance, FeesError<Balances::Balance, DispatchError>> {
// Collect the held fees from the subscriber
let collected = Balances::transfer_on_hold(
&HoldReason::Fees.into(),
sub.subscriber(),
&T::get(),
fees,
fees.clone(),
Precision::BestEffort,
Restriction::Free,
Fortitude::Polite,
Expand All @@ -111,29 +115,40 @@ where

// Ensure the correct amount was collected.
// TODO: error to bubble up and be handled by caller https://github.com/ideal-lab5/idn-sdk/issues/107
if collected < fees {
return Err(FeesError::NotEnoughBalance { needed: fees, balance: collected });
if collected < *fees {
return Err(FeesError::NotEnoughBalance { needed: *fees, balance: collected });
}

Ok(collected)
}
}

pub struct DepositCalculatorImpl<SDMultiplier: Get<Balance>, Balance> {
pub _phantom: (PhantomData<SDMultiplier>, PhantomData<Balance>),
pub struct DepositCalculatorImpl<SDMultiplier: Get<Deposit>, Deposit> {
pub _phantom: (PhantomData<SDMultiplier>, PhantomData<Deposit>),
}

impl<
S: SubscriptionTrait<AccountId32> + Encode,
SDMultiplier: Get<Balance>,
Balance: From<u32> + Saturating,
> pallet_idn_manager::DepositCalculator<Balance, S>
for DepositCalculatorImpl<SDMultiplier, Balance>
SDMultiplier: Get<Deposit>,
Deposit: From<u32> + Saturating + Ord,
> pallet_idn_manager::DepositCalculator<Deposit, S>
for DepositCalculatorImpl<SDMultiplier, Deposit>
{
fn calculate_storage_deposit(sub: &S) -> Balance {
fn calculate_storage_deposit(sub: &S) -> Deposit {
let storage_deposit_multiplier = SDMultiplier::get();
// calculate the size of scale encoded `sub`
let encoded_size = sub.encode().len() as u32;
storage_deposit_multiplier.saturating_mul(encoded_size.into())
}

fn calculate_diff_deposit(old_sub: &S, new_sub: &S) -> DiffBalance<Deposit> {
let old_deposit = Self::calculate_storage_deposit(old_sub);
let new_deposit = Self::calculate_storage_deposit(new_sub);
let direction = match new_deposit.cmp(&old_deposit) {
Ordering::Greater => BalanceDirection::Hold,
Ordering::Less => BalanceDirection::Release,
Ordering::Equal => BalanceDirection::None,
};
DiffBalance { balance: new_deposit.saturating_sub(old_deposit), direction }
}
}
85 changes: 73 additions & 12 deletions pallets/idn-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ use frame_support::{
sp_runtime::traits::AccountIdConversion,
traits::{
fungible::{hold::Mutate as HoldMutate, Inspect},
tokens::Precision,
Get,
},
BoundedVec,
Expand All @@ -79,7 +80,10 @@ use sp_arithmetic::traits::Unsigned;
use sp_core::H256;
use sp_io::hashing::blake2_256;
use sp_std::fmt::Debug;
use traits::{DepositCalculator, FeesManager, Subscription as SubscriptionTrait};
use traits::{
BalanceDirection, DepositCalculator, DiffBalance, FeesManager,
Subscription as SubscriptionTrait,
};
use xcm::{
v5::{prelude::*, Location},
VersionedLocation, VersionedXcm,
Expand Down Expand Up @@ -246,8 +250,8 @@ pub mod pallet {
SubscriptionAlreadyPaused,
/// The origin isn't the subscriber
NotSubscriber,
/// Insufficient balance for subscription
InsufficientBalance,
// /// Insufficient balance for subscription
// InsufficientBalance,
}

/// A reason for the IDN Manager Pallet placing a hold on funds.
Expand Down Expand Up @@ -352,11 +356,25 @@ pub mod pallet {
Subscriptions::<T>::try_mutate(sub_id, |maybe_sub| {
let sub = maybe_sub.as_mut().ok_or(Error::<T>::SubscriptionDoesNotExist)?;
ensure!(sub.details.subscriber == subscriber, Error::<T>::NotSubscriber);

let fees_diff = T::FeesManager::calculate_diff_fees(&sub.details.amount, &amount);
let deposit_diff = T::DepositCalculator::calculate_diff_deposit(
&sub,
&Subscription {
state: sub.state.clone(),
credits_left: amount,
details: sub.details.clone(),
},
);

sub.details.amount = amount;
sub.details.frequency = frequency;
sub.details.updated_at = frame_system::Pallet::<T>::block_number();
// TODO implement a way to refund or take the difference in fees https://github.com/ideal-lab5/idn-sdk/issues/104
// Self::manage_fees_and_deposit()

// Hold or refund diff fees
Self::manage_diff_fees(&subscriber, &fees_diff)?;
// Hold or refund diff deposit
Self::manage_diff_deposit(&subscriber, &deposit_diff)?;
Self::deposit_event(Event::SubscriptionUpdated { sub_id });
Ok(())
})
Expand Down Expand Up @@ -431,9 +449,9 @@ impl<T: Config> Pallet<T> {
metadata: Option<MetadataOf<T>>,
) -> DispatchResult {
// Calculate and hold the subscription fees
let fees = T::FeesManager::calculate_subscription_fees(amount);
T::Currency::hold(&HoldReason::Fees.into(), &subscriber, fees)
.map_err(|_| Error::<T>::InsufficientBalance)?;
let fees = T::FeesManager::calculate_subscription_fees(&amount);

Self::hold_fees(&subscriber, fees)?;

let current_block = frame_system::Pallet::<T>::block_number();
let details = SubscriptionDetails {
Expand All @@ -448,12 +466,10 @@ impl<T: Config> Pallet<T> {
let subscription =
Subscription { state: SubscriptionState::Active, credits_left: amount, details };

T::Currency::hold(
&HoldReason::StorageDeposit.into(),
Self::hold_deposit(
&subscriber,
T::DepositCalculator::calculate_storage_deposit(&subscription),
)
.map_err(|_| Error::<T>::InsufficientBalance)?;
)?;

let sub_id = subscription.id();

Expand All @@ -466,6 +482,51 @@ impl<T: Config> Pallet<T> {
Ok(())
}

fn hold_fees(subscriber: &T::AccountId, fees: BalanceOf<T>) -> DispatchResult {
T::Currency::hold(&HoldReason::Fees.into(), subscriber, fees)
}

fn release_fees(subscriber: &T::AccountId, fees: BalanceOf<T>) -> DispatchResult {
let _ = T::Currency::release(&HoldReason::Fees.into(), subscriber, fees, Precision::Exact)?;
Ok(())
}

fn manage_diff_fees(
subscriber: &T::AccountId,
diff: &DiffBalance<BalanceOf<T>>,
) -> DispatchResult {
match diff.direction {
BalanceDirection::Hold => Self::hold_fees(subscriber, diff.balance),
BalanceDirection::Release => Self::release_fees(subscriber, diff.balance),
BalanceDirection::None => Ok(()),
}
}

fn hold_deposit(subscriber: &T::AccountId, deposit: BalanceOf<T>) -> DispatchResult {
T::Currency::hold(&HoldReason::StorageDeposit.into(), subscriber, deposit)
}

fn release_deposit(subscriber: &T::AccountId, deposit: BalanceOf<T>) -> DispatchResult {
let _ = T::Currency::release(
&HoldReason::StorageDeposit.into(),
subscriber,
deposit,
Precision::BestEffort,
)?;
Ok(())
}

fn manage_diff_deposit(
subscriber: &T::AccountId,
diff: &DiffBalance<BalanceOf<T>>,
) -> DispatchResult {
match diff.direction {
BalanceDirection::Hold => Self::hold_deposit(subscriber, diff.balance),
BalanceDirection::Release => Self::release_deposit(subscriber, diff.balance),
BalanceDirection::None => Ok(()),
}
}

/// Helper function to construct XCM message for randomness distribution
// TODO: finish this off as part of https://github.com/ideal-lab5/idn-sdk/issues/77
fn construct_randomness_xcm(target: Location, _rnd: &T::Rnd) -> Result<Xcm<()>, Error<T>> {
Expand Down
Loading

0 comments on commit d78fe43

Please sign in to comment.