Skip to content

Commit

Permalink
[pallet_broker] Fix adapt_price behaviour at zero (paritytech#3636)
Browse files Browse the repository at this point in the history
This fixes the behaviour of `Linear` which is the default implementation
of the `AdaptPrice` trait in the broker pallet. Previously if cores were
offered but not sold in only one sale, the price would be set to zero
and due to the logic being purely multiplicative, the price would stay
at 0 indefinitely.

This could be further paired with a configurable minimum in the broker
pallet itself, which will be a future PR.

This affects the Rococo and Westend Coretime chains, but Kusama has a
different implementation so this isn't required for the Kusama launch. I
actually thought I opened this a while ago.

---------

Co-authored-by: Bastian Köcher <[email protected]>
  • Loading branch information
seadanda and bkchr authored Mar 11, 2024
1 parent 233e93b commit 2030ddd
Showing 1 changed file with 34 additions and 4 deletions.
38 changes: 34 additions & 4 deletions substrate/frame/broker/src/adapt_price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

use crate::CoreIndex;
use sp_arithmetic::{traits::One, FixedU64};
use sp_runtime::Saturating;

/// Type for determining how to set price.
pub trait AdaptPrice {
Expand Down Expand Up @@ -49,14 +50,24 @@ impl AdaptPrice for () {
pub struct Linear;
impl AdaptPrice for Linear {
fn leadin_factor_at(when: FixedU64) -> FixedU64 {
FixedU64::from(2) - when
FixedU64::from(2).saturating_sub(when)
}
fn adapt_price(sold: CoreIndex, target: CoreIndex, limit: CoreIndex) -> FixedU64 {
if sold <= target {
FixedU64::from_rational(sold.into(), target.into())
// Range of [0.5, 1.0].
FixedU64::from_rational(1, 2).saturating_add(FixedU64::from_rational(
sold.into(),
target.saturating_mul(2).into(),
))
} else {
FixedU64::one() +
FixedU64::from_rational((sold - target).into(), (limit - target).into())
// Range of (1.0, 2].

// Unchecked math: In this branch we know that sold > target. The limit must be >= sold
// by construction, and thus target must be < limit.
FixedU64::one().saturating_add(FixedU64::from_rational(
(sold - target).into(),
(limit - target).into(),
))
}
}
}
Expand All @@ -81,4 +92,23 @@ mod tests {
}
}
}

#[test]
fn linear_bound_check() {
// Using constraints from pallet implementation i.e. `limit >= sold`.
// Check extremes
let limit = 10;
let target = 5;

// Maximally sold: `sold == limit`
assert_eq!(Linear::adapt_price(limit, target, limit), FixedU64::from_float(2.0));
// Ideally sold: `sold == target`
assert_eq!(Linear::adapt_price(target, target, limit), FixedU64::one());
// Minimally sold: `sold == 0`
assert_eq!(Linear::adapt_price(0, target, limit), FixedU64::from_float(0.5));
// Optimistic target: `target == limit`
assert_eq!(Linear::adapt_price(limit, limit, limit), FixedU64::one());
// Pessimistic target: `target == 0`
assert_eq!(Linear::adapt_price(limit, 0, limit), FixedU64::from_float(2.0));
}
}

0 comments on commit 2030ddd

Please sign in to comment.