gradido/shared/src/logic/decay.ts
2025-06-26 08:00:25 +02:00

77 lines
2.1 KiB
TypeScript

import { Decimal } from 'decimal.js-light'
import { DECAY_START_TIME } from '../const'
Decimal.set({
precision: 25,
rounding: Decimal.ROUND_HALF_UP,
})
export interface Decay {
balance: Decimal
decay: Decimal
roundedDecay: Decimal
start: Date | null
end: Date | null
duration: number | null
}
export function decayFormula(value: Decimal, seconds: number): Decimal {
// TODO why do we need to convert this here to a string to work properly?
// chatgpt: We convert to string here to avoid precision loss:
// .pow(seconds) can internally round the result, especially for large values of `seconds`.
// Using .toString() ensures full precision is preserved in the multiplication.
return value.mul(
new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(),
)
}
export function calculateDecay(
amount: Decimal,
from: Date,
to: Date
): Decay {
const fromMs = from.getTime()
const toMs = to.getTime()
const startBlockMs = DECAY_START_TIME.getTime()
if (toMs < fromMs) {
// TODO: refactor, use custom Error Classes which contain context variables
throw new Error('calculateDecay: to < from, reverse decay calculation is invalid')
}
// Initialize with no decay
const decay: Decay = {
balance: amount,
decay: new Decimal(0),
roundedDecay: new Decimal(0),
start: null,
end: null,
duration: null,
}
// decay started after end date; no decay
if (startBlockMs > toMs) {
return decay
}
// decay started before start date; decay for full duration
if (startBlockMs < fromMs) {
decay.start = from
decay.duration = (toMs - fromMs) / 1000
}
// decay started between start and end date; decay from decay start till end date
else {
decay.start = DECAY_START_TIME
decay.duration = (toMs - startBlockMs) / 1000
}
decay.end = to
decay.balance = decayFormula(amount, decay.duration)
decay.decay = decay.balance.minus(amount)
decay.roundedDecay = amount
.toDecimalPlaces(2, Decimal.ROUND_DOWN)
.minus(decay.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN).toString())
.mul(-1)
return decay
}