I am implementing subscriptions, paid for with balance, rated per block, and the duration measured in block numbers.

I am finding it very difficult, in general, to perform arithmetic that take both balances and block numbers and deliver either of them.

here is a sample implementation attempt:

pub struct SubscriptionFunding<BlockNumber, Balance> {
    /// the block number at which the subscription started, if it has been deployed
    pub deployed_at: Option<BlockNumber>,
    /// until when was this paid, it is never less than deployed_at if set. if there is a
    /// settlement at mid-life of the subscription this is updated to track how long the
    /// remaining funds will last. Also, rate changes require a settlement to date.
    pub paid_until: Option<BlockNumber>,
    /// Remaining locked funds available for the subscription
    pub remaining_funds: Balance,
    /// The rate used
    pub rate: Rate<Balance>,

impl<BlockNumber, Balance> SubscriptionFunding<BlockNumber, Balance>
    BlockNumber: Saturating + Copy,
    Balance: Saturating + Copy + Zero,
    pub fn expected_termination(&self) -> Option<BlockNumber> {
        // If the subscription has not started

        // Subscription has started

        match &self.rate {
            Rate { rate_type: RateType::None, .. } => None,
            Rate { rate: r, .. } if r.is_zero() => None,
            Rate { rate: r, .. } => Some(self.paid_until.unwrap() + self.remaining_funds.saturating_div( *r)),

although this basic implementation works for in unit testing for u64 types for example, I find it impossible to make it work inside my pallet with my BalanceOf and BlockNumberOf which are both pretty standard.

I can add, blocknum + blocknum, I can check if balance is zero, but I have trouble:

  • dividing balance by balance, and adding the result to blockNumber.

Missing traits in my pallet types, and difficulty to convert between the underlying types are the main sources of problems.

Any tips that could help forward?

1 Answer 1


You can utilize SaturatedConversion to convert them to the same type and perform arithmetic. Then, convert the result into the desired final type.

For example:

let block_number = block_number.saturated_into::<u64>();
let balance = balance.saturated_into::<u64>();
let final_result = (block_number + balance).saturated_into::<u128>();

Take care when performing type conversions and choose the appropriate type. If the Balance type is u128, using saturated_into::<u64>() may result in loss of some details.

  • thanks for the information. Yes, I can now perform the calculation in u128. Then the issue is to convert from u128 back to BlockNumberOf<T>, but for some reason, that I was unable to deduce DispatchTime<u128> works with the scheduler pallet just fine, and I cannot see how this pallet is converting back to BlockNumberOf<T>, so I am returning a DispatchTime instead as workaround. Will research later on.
    – rvalle
    Commented Nov 29, 2023 at 10:05
  • OK, I realized now the other issue, BlockNumberFor<T> can be created from up to u32.
    – rvalle
    Commented Nov 29, 2023 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.