pub struct SigmoidPayout;Expand description
The SigmoidPayout model computes rewards using a logistic (S-shaped) function, producing slow start, rapid growth, and eventual saturation.
Concept: S-Curve Reward Transformation
§Formula
f(x) = L / (1 + e^(-k*(x - x0)))where k and x0 are derived from growth_start (a) and growth_end (b):
k = logit(b) - logit(a) (always > 0 when b > a, both in (0, 1))
x0 = -logit(a) / k (midpoint; > 0 when a < 0.5)§Signed Arithmetic
Even when Asset and FixedPoint are unsigned types, several
intermediate values are inherently signed:
logit(a) < 0for anya < 0.5.x - x0 < 0for anyx < x0(the entire left half of the curve).-k*(x - x0) < 0for anyx > x0(the entire right half).
All of these are computed in a concrete FixedI128 workspace via
FixedSignedCast, then the final result (always >= 0) is projected
back. This makes the model correct for both signed and unsigned types
with no Neg bound on FixedPoint.
§Guard Conditions (returns zero)
growth_start <= 0orgrowth_start >= 1.growth_end <= 0orgrowth_end >= 1.k == 0(degenerate; only whengrowth_start == growth_end).
§Precision Note
Fixed-point arithmetic accumulates small rounding errors across the
ln -> k -> x0 -> exp chain. In practice the output may be one integer
unit below the analytically exact value at certain inputs. This is
expected and inconsequential for integer rewards.
§Characteristics:
- Bounded: Reward never exceeds
max_reward. - Smooth growth: Gradual ramp-up instead of abrupt changes.
- Works with unsigned types: No
Negbound; negation inFixedI128. - Deterministic: Same input and parameters yield identical results.
- Context-driven: Controlled via
SigmoidPayoutConfig.
§Applications:
- Adoption-based reward curves.
- Incentive ramp-up systems.
- Gradual onboarding rewards.
- Supply emission with saturation.
§Example
let config = SigmoidPayoutConfig {
max_reward: 100u128,
growth_start: FixedU128::saturating_from_rational(1, 10), // 0.1
growth_end: FixedU128::saturating_from_rational(9, 10), // 0.9
};
// k ~= 4.394, x0 ~= 0.5
// f(0) = 10 (= growth_start * L, lower tail)
// f(1) = 89 (= growth_end * L, upper tail, rounded down)
assert_eq!(SigmoidPayout::compute(0u128, Some(config)), 10);
assert_eq!(SigmoidPayout::compute(1u128, Some(config)), 89);Trait Implementations§
Source§impl Debug for SigmoidPayout
impl Debug for SigmoidPayout
Source§impl Default for SigmoidPayout
impl Default for SigmoidPayout
Source§fn default() -> SigmoidPayout
fn default() -> SigmoidPayout
Source§impl<FixedPoint, Asset> PurePluginModel<Asset, SigmoidPayoutConfig<Asset, FixedPoint>, Asset> for SigmoidPayoutwhere
Asset: Copy + IntegerToFixed + FixedForInteger<FixedPoint = FixedPoint> + Zero,
FixedPoint: FixedPointNumber + FixedSignedCast<Signed = FixedI128>,
impl<FixedPoint, Asset> PurePluginModel<Asset, SigmoidPayoutConfig<Asset, FixedPoint>, Asset> for SigmoidPayoutwhere
Asset: Copy + IntegerToFixed + FixedForInteger<FixedPoint = FixedPoint> + Zero,
FixedPoint: FixedPointNumber + FixedSignedCast<Signed = FixedI128>,
Source§fn compute(
&self,
x: Asset,
context: &SigmoidPayoutConfig<Asset, FixedPoint>,
) -> Asset
fn compute( &self, x: Asset, context: &SigmoidPayoutConfig<Asset, FixedPoint>, ) -> Asset
Auto Trait Implementations§
impl Freeze for SigmoidPayout
impl RefUnwindSafe for SigmoidPayout
impl Send for SigmoidPayout
impl Sync for SigmoidPayout
impl Unpin for SigmoidPayout
impl UnwindSafe for SigmoidPayout
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> CheckedConversion for T
impl<T> CheckedConversion for T
§fn checked_from<T>(t: T) -> Option<Self>where
Self: TryFrom<T>,
fn checked_from<T>(t: T) -> Option<Self>where
Self: TryFrom<T>,
§fn checked_into<T>(self) -> Option<T>where
Self: TryInto<T>,
fn checked_into<T>(self) -> Option<T>where
Self: TryInto<T>,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T, U, Tag> IntoTag<U, Tag> for Twhere
U: FromTag<T, Tag>,
Tag: DiscriminantTag,
impl<T, U, Tag> IntoTag<U, Tag> for Twhere
U: FromTag<T, Tag>,
Tag: DiscriminantTag,
§impl<Src, Dest> IntoTuple<Dest> for Srcwhere
Dest: FromTuple<Src>,
impl<Src, Dest> IntoTuple<Dest> for Srcwhere
Dest: FromTuple<Src>,
fn into_tuple(self) -> Dest
§impl<T> IsType<T> for T
impl<T> IsType<T> for T
§impl<T, Outer> IsWrappedBy<Outer> for T
impl<T, Outer> IsWrappedBy<Outer> for T
Source§impl<T, Time> Logging<Time> for Twhere
Time: Time,
impl<T, Time> Logging<Time> for Twhere
Time: Time,
Source§const FALLBACK_TARGET: &'static str = "routine"
const FALLBACK_TARGET: &'static str = "routine"
Default logging target if none is provided.
Most routines, especially offchain workers or background tasks, use this target for simplicity.
It allows a consistent place to look for routine logs without requiring every call to specify a target.
Note: This target is only a conveninence and may be somewhat vague. To ensure errors can still be traced accurately, the logged messages should include additional metadata (e.g., module name, error index, or contextual info) so that the source of the error can be identified even if the target is generic.
Source§type Logger = DispatchError
type Logger = DispatchError
The type taken and returned for logging.
We simply return the same [DispatchError] that was logged,
so logging does not change control flow or error propagation.
DispatchError is used because in Substrate it encompasses all
runtime errors - including module errors, token errors, arithmetic
issues, and transactional boundaries - making it the universal
substrate-side error representation.
Source§type Level = LogLevel
type Level = LogLevel
The log level type.
We use the LogLevel enum to standardize severity levels
(Info, Warn, Error, Debug) across all routine logs.
Source§fn log(
level: <T as Logging<Time>>::Level,
err: &<T as Logging<Time>>::Logger,
timestamp: Time,
target: Option<&str>,
fmt: Option<fn(Time, &<T as Logging<Time>>::Level, &str, &str) -> String>,
) -> <T as Logging<Time>>::Logger
fn log( level: <T as Logging<Time>>::Level, err: &<T as Logging<Time>>::Logger, timestamp: Time, target: Option<&str>, fmt: Option<fn(Time, &<T as Logging<Time>>::Level, &str, &str) -> String>, ) -> <T as Logging<Time>>::Logger
Source§fn info(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<fn(Timestamp, &Self::Level, &str, &str) -> String>,
) -> Self::Loggerwhere
Self: Sized,
fn info(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<fn(Timestamp, &Self::Level, &str, &str) -> String>,
) -> Self::Loggerwhere
Self: Sized,
Source§fn warn(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<fn(Timestamp, &Self::Level, &str, &str) -> String>,
) -> Self::Loggerwhere
Self: Sized,
fn warn(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<fn(Timestamp, &Self::Level, &str, &str) -> String>,
) -> Self::Loggerwhere
Self: Sized,
§impl<T> SaturatedConversion for T
impl<T> SaturatedConversion for T
§fn saturated_from<T>(t: T) -> Selfwhere
Self: UniqueSaturatedFrom<T>,
fn saturated_from<T>(t: T) -> Selfwhere
Self: UniqueSaturatedFrom<T>,
§fn saturated_into<T>(self) -> Twhere
Self: UniqueSaturatedInto<T>,
fn saturated_into<T>(self) -> Twhere
Self: UniqueSaturatedInto<T>,
T. Read more§impl<T, U> TryIntoKey<U> for Twhere
U: TryFromKey<T>,
impl<T, U> TryIntoKey<U> for Twhere
U: TryFromKey<T>,
type Error = <U as TryFromKey<T>>::Error
fn try_into_key(self) -> Result<U, <U as TryFromKey<T>>::Error>
§impl<S, T> UncheckedInto<T> for Swhere
T: UncheckedFrom<S>,
impl<S, T> UncheckedInto<T> for Swhere
T: UncheckedFrom<S>,
§fn unchecked_into(self) -> T
fn unchecked_into(self) -> T
unchecked_from.§impl<T, S> UniqueSaturatedInto<T> for S
impl<T, S> UniqueSaturatedInto<T> for S
§fn unique_saturated_into(self) -> T
fn unique_saturated_into(self) -> T
T.