Skip to content

Commit 81a1cc5

Browse files
authored
fix: discount calculation handle variable billing frequency (#626)
1 parent 1616763 commit 81a1cc5

File tree

2 files changed

+58
-7
lines changed

2 files changed

+58
-7
lines changed

substrate-node/pallets/pallet-smart-contract/src/cost.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use frame_support::{dispatch::DispatchErrorWithPostInfo, traits::Get};
88
use pallet_tfgrid::types as pallet_tfgrid_types;
99
use sp_runtime::{Percent, SaturatedConversion};
1010
use substrate_fixed::types::U64F64;
11-
use tfchain_support::{resources::Resources, types::NodeCertification};
11+
use tfchain_support::{
12+
constants::time::SECS_PER_HOUR, resources::Resources, types::NodeCertification,
13+
};
1214

1315
impl<T: Config> Contract<T> {
1416
pub fn get_billing_info(&self) -> ContractBillingInformation {
@@ -40,8 +42,12 @@ impl<T: Config> Contract<T> {
4042
let total_cost_tft_64 = calculate_cost_in_tft_from_musd::<T>(total_cost)?;
4143

4244
// Calculate the amount due and discount received based on the total_cost amount due
43-
let (amount_due, discount_received) =
44-
calculate_discount::<T>(total_cost_tft_64, balance, certification_type);
45+
let (amount_due, discount_received) = calculate_discount::<T>(
46+
total_cost_tft_64,
47+
seconds_elapsed,
48+
balance,
49+
certification_type,
50+
);
4551

4652
return Ok((amount_due, discount_received));
4753
}
@@ -229,6 +235,7 @@ pub(crate) fn calculate_cu(cru: U64F64, mru: U64F64) -> U64F64 {
229235
// (default, bronze, silver, gold or none)
230236
pub fn calculate_discount<T: Config>(
231237
amount_due: u64,
238+
seconds_elapsed: u64,
232239
balance: BalanceOf<T>,
233240
certification_type: NodeCertification,
234241
) -> (BalanceOf<T>, types::DiscountLevel) {
@@ -239,13 +246,15 @@ pub fn calculate_discount<T: Config>(
239246
);
240247
}
241248

242-
let balance_as_u128: u128 = balance.saturated_into::<u128>();
243-
244249
// calculate amount due on a monthly basis
245-
// we bill every one hour so we can infer the amount due monthly (30 days ish)
246-
let amount_due_monthly = amount_due * 24 * 30;
250+
// first get the normalized amount per hour
251+
let amount_due_hourly = U64F64::from_num(amount_due) * U64F64::from_num(seconds_elapsed)
252+
/ U64F64::from_num(SECS_PER_HOUR);
253+
// then we can infer the amount due monthly (30 days ish)
254+
let amount_due_monthly = (amount_due_hourly * 24 * 30).round().to_num::<u64>();
247255

248256
// see how many months a user can pay for this deployment given his balance
257+
let balance_as_u128: u128 = balance.saturated_into::<u128>();
249258
let discount_level = U64F64::from_num(balance_as_u128) / U64F64::from_num(amount_due_monthly);
250259

251260
// predefined discount levels

substrate-node/pallets/pallet-smart-contract/src/tests.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,6 +3123,48 @@ fn test_percent() {
31233123
assert_eq!(new_cost, 248);
31243124
}
31253125

3126+
macro_rules! test_calculate_discount {
3127+
($($name:ident: $value:expr,)*) => {
3128+
$(
3129+
#[test]
3130+
fn $name() {
3131+
let (number_of_months, expected_discount_level) = $value;
3132+
3133+
let amount_due = 1000;
3134+
let seconds_elapsed = SECS_PER_HOUR; // amount due is relative to 1h
3135+
// Give just enough balance for targeted number of months at the rate of 1000 per hour
3136+
let balance = (amount_due * 24 * 30) * number_of_months;
3137+
3138+
let result = cost::calculate_discount::<TestRuntime>(
3139+
amount_due,
3140+
seconds_elapsed,
3141+
balance,
3142+
NodeCertification::Diy,
3143+
);
3144+
3145+
assert_eq!(
3146+
result,
3147+
(
3148+
(U64F64::from_num(amount_due) * expected_discount_level.price_multiplier())
3149+
.ceil()
3150+
.to_num::<u64>(),
3151+
expected_discount_level
3152+
)
3153+
);
3154+
}
3155+
)*
3156+
}
3157+
}
3158+
3159+
// Confirm expected discount level given a number of month of balance autonomy
3160+
test_calculate_discount! {
3161+
test_calculate_discount_none_works: (1, types::DiscountLevel::None),
3162+
test_calculate_discount_default_works: (3, types::DiscountLevel::Default),
3163+
test_calculate_discount_bronze_works: (6, types::DiscountLevel::Bronze),
3164+
test_calculate_discount_silver_works: (12, types::DiscountLevel::Silver),
3165+
test_calculate_gold_discount_gold_works: (36, types::DiscountLevel::Gold),
3166+
}
3167+
31263168
// ***** HELPER FUNCTIONS ***** //
31273169
// ---------------------------- //
31283170
// ---------------------------- //

0 commit comments

Comments
 (0)