UNPKG

@agoric/zoe

Version:

Zoe: the Smart Contract Framework for Offer Enforcement

97 lines (84 loc) 2.94 kB
import { annotateError, X } from '@endo/errors'; import { Far } from '@endo/marshal'; import { makeNotifierKit, observeNotifier } from '@agoric/notifier'; import { AmountMath } from '@agoric/ertp'; import { TimeMath } from '@agoric/time'; import { scheduleLiquidation } from './scheduleLiquidation.js'; import { ceilMultiplyBy } from '../../contractSupport/index.js'; // Update the debt by adding the new interest on every period, as // indicated by the periodNotifier /** * @type {CalcInterestFn} Calculate the interest using an interest * rate in basis points. * i.e. oldDebtValue is 40,000 * interestRate (in basis points) is 5 = 5/10,000 * interest charged this period is 20 loan brand */ export const calculateInterest = (oldDebt, interestRate) => ceilMultiplyBy(oldDebt, interestRate); /** @type {MakeDebtCalculator} */ export const makeDebtCalculator = debtCalculatorConfig => { const { calcInterestFn = calculateInterest, originalDebt, periodNotifier, interestRate, interestPeriod, basetime, zcf, configMinusGetDebt, } = debtCalculatorConfig; let debt = originalDebt; // the last period-end for which interest has been added let lastCalculationTimestamp = basetime; const { updater: debtNotifierUpdater, notifier: debtNotifier } = makeNotifierKit(); const getDebt = () => debt; const config = { ...configMinusGetDebt, getDebt }; const periodObserver = Far('periodObserver', { /** @type {(timestamp: import('@agoric/time').TimestampRecord) => void} */ updateState: timestamp => { let updatedLoan = false; // we could calculate the number of required updates and multiply by a power // of the interest rate, but this seems easier to read. while ( TimeMath.compareAbs( TimeMath.addAbsRel(lastCalculationTimestamp, interestPeriod), timestamp, ) <= 0 ) { lastCalculationTimestamp = TimeMath.addAbsRel( lastCalculationTimestamp, interestPeriod, ); const interest = calcInterestFn(debt, interestRate); debt = AmountMath.add(debt, interest); updatedLoan = true; } if (updatedLoan) { debtNotifierUpdater.updateState(debt); scheduleLiquidation(zcf, config); } }, fail: reason => { annotateError( reason, X`Period problem: ${originalDebt}, started: ${basetime}, debt: ${debt}`, ); console.error(reason); }, }); observeNotifier(periodNotifier, periodObserver).catch(reason => { annotateError( reason, X`Unable to updateDebt originally: ${originalDebt}, started: ${basetime}, debt: ${debt}`, ); console.error(reason); }); debtNotifierUpdater.updateState(debt); return Far('debtCalculator', { getDebt, getLastCalculationTimestamp: _ => lastCalculationTimestamp, getDebtNotifier: _ => debtNotifier, }); };