Skip to content

Commit

Permalink
Merge pull request #7871 from Agoric/sam-rename-interest
Browse files Browse the repository at this point in the history
feat(inter-protocol)!: rename InterestRate to StabilityFee
  • Loading branch information
samsiegart authored Jun 7, 2023
2 parents 13050c2 + 61b5be8 commit 69236fd
Show file tree
Hide file tree
Showing 31 changed files with 296 additions and 283 deletions.
2 changes: 1 addition & 1 deletion packages/inter-protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The above states are robust to system restarts and upgrades. This is accomplishe
Debts are denominated in µIST. (1 million µIST = 1 IST)

Each interest charging period (say daily) the actual debts in all vaults are affected. Materializing that across all vaults would be O(n) writes. Instead, to make charging interest O(1) we virtualize the debt that a vault owes to be a function of stable vault attributes and values that change in the vault manager when it charges interest. Specifically,
- a compoundedInterest value on the manager that keeps track of interest accrual since its launch
- a compoundedStabilityFee value on the manager that keeps track of interest accrual since its launch
- a debtSnapshot on the vault by which one can calculate the actual debt

To maintain that the keys of vaults to liquidate are stable requires that its keys are also time-independent so they're recorded as a "normalized collateralization ratio", with the actual collateral divided by the normalized debt.
Expand Down
6 changes: 3 additions & 3 deletions packages/inter-protocol/docs/governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ the Inter Protocol Whitepaper, v0.8.
| Governance Key | Type | WP? |
| ------------------ | :---------------- | --- |
| DebtLimit | Amount | Yes |
| InterestRate | Ratio | Yes |
| StabilityFee | Ratio | Yes |
| LiquidationPadding | Ratio | |
| LiquidationMargin | Ratio | Yes |
| LiquidationPenalty | Ratio | Yes |
| LoanFee | Ratio | Yes |
| MintFee | Ratio | Yes |

From Inter Protocol Whitepaper, v0.8:
>Governance determines the approved collateral types: the crypto assets that can be used as collateral in vaults. In addition, it sets and manages the parameters associated with each collateral type based on the risk of the asset. These include the total debt limit, the collateralization ratio, the stability fee, and the liquidation penalty.
Note that the "stability fee" described in the Whitepaper comprises both InterestRate and LoanFee.
Note that the "stability fee" described in the Whitepaper comprises both StabilityFee and MintFee.

### Parity Stability Mechanism (PSM)

Expand Down
4 changes: 2 additions & 2 deletions packages/inter-protocol/scripts/add-collateral-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const defaultProposalBuilder = async (
{ publishRef, install: install0, wrapInstall },
{
debtLimitValue = undefined,
interestRateValue = undefined,
stabilityFeeValue = undefined,
interchainAssetOptions = /** @type {object} */ ({}),
} = {},
{ env = process.env } = {},
Expand Down Expand Up @@ -38,7 +38,7 @@ export const defaultProposalBuilder = async (
getManifestForAddAssetToVault.name,
{
debtLimitValue: debtLimitValue && BigInt(debtLimitValue),
interestRateValue: interestRateValue && BigInt(interestRateValue),
stabilityFeeValue: stabilityFeeValue && BigInt(stabilityFeeValue),
interchainAssetOptions: {
denom,
issuerBoardId,
Expand Down
26 changes: 13 additions & 13 deletions packages/inter-protocol/src/interest-math.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,39 @@ import {
} from '@agoric/zoe/src/contractSupport/ratio.js';

/**
* @param {Ratio} currentCompoundedInterest as coefficient
* @param {Ratio} previousCompoundedInterest as coefficient
* @param {Ratio} currentCompoundedStabilityFee as coefficient
* @param {Ratio} previousCompoundedStabilityFee as coefficient
* @returns {Ratio} additional compounding since the previous
*/
const calculateRelativeCompounding = (
currentCompoundedInterest,
previousCompoundedInterest,
currentCompoundedStabilityFee,
previousCompoundedStabilityFee,
) => {
// divide compounded interest by the snapshot
return multiplyRatios(
currentCompoundedInterest,
invertRatio(previousCompoundedInterest),
currentCompoundedStabilityFee,
invertRatio(previousCompoundedStabilityFee),
);
};

/**
* @param {Amount<'nat'>} debtSnapshot
* @param {Ratio} interestSnapshot as coefficient
* @param {Ratio} currentCompoundedInterest as coefficient
* @param {Ratio} stabilityFeeSnapshot as coefficient
* @param {Ratio} currentCompoundedStabilityFee as coefficient
* @returns {Amount<'nat'>}
*/
export const calculateCurrentDebt = (
debtSnapshot,
interestSnapshot,
currentCompoundedInterest,
stabilityFeeSnapshot,
currentCompoundedStabilityFee,
) => {
if (ratiosSame(interestSnapshot, currentCompoundedInterest)) {
if (ratiosSame(stabilityFeeSnapshot, currentCompoundedStabilityFee)) {
return debtSnapshot;
}

const interestSinceSnapshot = calculateRelativeCompounding(
currentCompoundedInterest,
interestSnapshot,
currentCompoundedStabilityFee,
stabilityFeeSnapshot,
);

return multiplyBy(debtSnapshot, interestSinceSnapshot);
Expand Down
48 changes: 24 additions & 24 deletions packages/inter-protocol/src/interest.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export const makeInterestCalculator = (
* @type {Calculate}
*/
const calculate = (debtStatus, currentTime) => {
const { newDebt, latestInterestUpdate } = debtStatus;
let newRecent = latestInterestUpdate;
const { newDebt, latestStabilityFeeUpdate } = debtStatus;
let newRecent = latestStabilityFeeUpdate;
let growingInterest = debtStatus.interest;
let growingDebt = newDebt;
while (
Expand All @@ -78,7 +78,7 @@ export const makeInterestCalculator = (
growingDebt += newInterest;
}
return {
latestInterestUpdate: newRecent,
latestStabilityFeeUpdate: newRecent,
interest: growingInterest,
newDebt: growingDebt,
};
Expand All @@ -93,9 +93,9 @@ export const makeInterestCalculator = (
* @type {Calculate}
*/
const calculateReportingPeriod = (debtStatus, currentTime) => {
const { latestInterestUpdate } = debtStatus;
const { latestStabilityFeeUpdate } = debtStatus;
const overshoot = TimeMath.modRelRel(
TimeMath.subtractAbsAbs(currentTime, latestInterestUpdate),
TimeMath.subtractAbsAbs(currentTime, latestStabilityFeeUpdate),
recordingPeriod,
);
return calculate(
Expand All @@ -111,20 +111,20 @@ export const makeInterestCalculator = (
};

/**
* compoundedInterest *= (new debt) / (prior total debt)
* compoundedStabilityFee *= (new debt) / (prior total debt)
*
* @param {Ratio} priorCompoundedInterest
* @param {Ratio} priorCompoundedStabilityFee
* @param {NatValue} priorDebt
* @param {NatValue} newDebt
*/
export const calculateCompoundedInterest = (
priorCompoundedInterest,
export const calculateCompoundedStabilityFee = (
priorCompoundedStabilityFee,
priorDebt,
newDebt,
) => {
const brand = priorCompoundedInterest.numerator.brand;
const brand = priorCompoundedStabilityFee.numerator.brand;
const compounded = multiplyRatios(
priorCompoundedInterest,
priorCompoundedStabilityFee,
makeRatio(newDebt, brand, priorDebt, brand),
);
return quantize(compounded, COMPOUNDED_INTEREST_DENOMINATOR);
Expand All @@ -144,37 +144,37 @@ const validatedBrand = (mint, debt) => {
};

/**
* Charge interest accrued between `latestInterestUpdate` and `accruedUntil`.
* Charge interest accrued between `latestStabilityFeeUpdate` and `accruedUntil`.
*
* @param {{
* mint: ZCFMint<'nat'>,
* mintAndTransferWithFee: MintAndTransfer,
* poolIncrementSeat: ZCFSeat,
* seatAllocationKeyword: Keyword }} powers
* @param {{
* interestRate: Ratio,
* stabilityFee: Ratio,
* chargingPeriod: RelativeTime,
* recordingPeriod: RelativeTime}} params
* @param {{
* latestInterestUpdate: Timestamp,
* compoundedInterest: Ratio,
* latestStabilityFeeUpdate: Timestamp,
* compoundedStabilityFee: Ratio,
* totalDebt: Amount<'nat'>}} prior
* @param {Timestamp} accruedUntil
* @returns {{compoundedInterest: Ratio, latestInterestUpdate: Timestamp, totalDebt: Amount<'nat'> }}
* @returns {{compoundedStabilityFee: Ratio, latestStabilityFeeUpdate: Timestamp, totalDebt: Amount<'nat'> }}
*/
export const chargeInterest = (powers, params, prior, accruedUntil) => {
const brand = validatedBrand(powers.mint, prior.totalDebt);

const interestCalculator = makeInterestCalculator(
params.interestRate,
params.stabilityFee,
params.chargingPeriod,
params.recordingPeriod,
);

// calculate delta of accrued debt
const debtStatus = interestCalculator.calculateReportingPeriod(
{
latestInterestUpdate: prior.latestInterestUpdate,
latestStabilityFeeUpdate: prior.latestStabilityFeeUpdate,
newDebt: prior.totalDebt.value,
interest: 0n, // XXX this is always zero, doesn't need to be an option
},
Expand All @@ -185,8 +185,8 @@ export const chargeInterest = (powers, params, prior, accruedUntil) => {
// done if none
if (interestAccrued === 0n) {
return {
compoundedInterest: prior.compoundedInterest,
latestInterestUpdate: debtStatus.latestInterestUpdate,
compoundedStabilityFee: prior.compoundedStabilityFee,
latestStabilityFeeUpdate: debtStatus.latestStabilityFeeUpdate,
totalDebt: prior.totalDebt,
};
}
Expand All @@ -196,8 +196,8 @@ export const chargeInterest = (powers, params, prior, accruedUntil) => {
// testing with small numbers there's 5 digits of precision, and with large
// numbers the ratios tend towards ample precision.
// TODO adopt banker's rounding https://github.com/Agoric/agoric-sdk/issues/4573
const compoundedInterest = calculateCompoundedInterest(
prior.compoundedInterest,
const compoundedStabilityFee = calculateCompoundedStabilityFee(
prior.compoundedStabilityFee,
prior.totalDebt.value,
debtStatus.newDebt,
);
Expand All @@ -218,8 +218,8 @@ export const chargeInterest = (powers, params, prior, accruedUntil) => {
);

return {
compoundedInterest,
latestInterestUpdate: debtStatus.latestInterestUpdate,
compoundedStabilityFee,
latestStabilityFeeUpdate: debtStatus.latestStabilityFeeUpdate,
totalDebt,
};
};
10 changes: 5 additions & 5 deletions packages/inter-protocol/src/proposals/addAssetToVault.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export const registerScaledPriceAuthority = async (
* @param {object} config.options
* @param {InterchainAssetOptions} config.options.interchainAssetOptions
* @param {bigint | number | string} config.options.debtLimitValue
* @param {bigint} config.options.interestRateValue
* @param {bigint} config.options.stabilityFeeValue
*/
export const addAssetToVault = async (
{
Expand All @@ -228,7 +228,7 @@ export const addAssetToVault = async (
debtLimitValue = 1_000n * 1_000_000n,
// Default to a safe value. Production will likely set this through governance.
// Allow setting through bootstrap to simplify testing.
interestRateValue = 1n,
stabilityFeeValue = 1n,
interchainAssetOptions,
},
},
Expand All @@ -250,7 +250,7 @@ export const addAssetToVault = async (
const vaultFactoryCreator = E.get(vaultFactoryKit).creatorFacet;
await E(vaultFactoryCreator).addVaultType(interchainIssuer, oracleBrand, {
debtLimit: AmountMath.make(stable, BigInt(debtLimitValue)),
interestRate: makeRatio(interestRateValue, stable),
stabilityFee: makeRatio(stabilityFeeValue, stable),
// The rest of these we use safe defaults.
// In production they will be governed by the Econ Committee.
// Product deployments are also expected to have a low debtLimitValue at the outset,
Expand All @@ -268,7 +268,7 @@ export const getManifestForAddAssetToVault = (
{ restoreRef },
{
debtLimitValue,
interestRateValue,
stabilityFeeValue,
interchainAssetOptions,
scaledPriceAuthorityRef,
},
Expand Down Expand Up @@ -341,7 +341,7 @@ export const getManifestForAddAssetToVault = (
options: {
debtLimitValue,
interchainAssetOptions,
interestRateValue,
stabilityFeeValue,
},
};
};
8 changes: 4 additions & 4 deletions packages/inter-protocol/src/vaultFactory/params.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const DEBT_LIMIT_KEY = 'DebtLimit';
export const LIQUIDATION_MARGIN_KEY = 'LiquidationMargin';
export const LIQUIDATION_PADDING_KEY = 'LiquidationPadding';
export const LIQUIDATION_PENALTY_KEY = 'LiquidationPenalty';
export const INTEREST_RATE_KEY = 'InterestRate';
export const STABILITY_FEE_KEY = 'StabilityFee';
export const MINT_FEE_KEY = 'MintFee';
export const MIN_INITIAL_DEBT_KEY = 'MinInitialDebt';
export const SHORTFALL_INVITATION_KEY = 'ShortfallInvitation';
Expand Down Expand Up @@ -89,7 +89,7 @@ export const makeVaultParamManager = (
publisherKit,
{
debtLimit,
interestRate,
stabilityFee,
liquidationMargin,
liquidationPadding = zeroRatio(liquidationMargin),
liquidationPenalty,
Expand All @@ -98,7 +98,7 @@ export const makeVaultParamManager = (
) =>
makeParamManagerSync(publisherKit, {
[DEBT_LIMIT_KEY]: [ParamTypes.AMOUNT, debtLimit],
[INTEREST_RATE_KEY]: [ParamTypes.RATIO, interestRate],
[STABILITY_FEE_KEY]: [ParamTypes.RATIO, stabilityFee],
[LIQUIDATION_PADDING_KEY]: [ParamTypes.RATIO, liquidationPadding],
[LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, liquidationMargin],
[LIQUIDATION_PENALTY_KEY]: [ParamTypes.RATIO, liquidationPenalty],
Expand All @@ -110,7 +110,7 @@ export const vaultParamPattern = M.splitRecord(
{
liquidationMargin: ratioPattern,
liquidationPenalty: ratioPattern,
interestRate: ratioPattern,
stabilityFee: ratioPattern,
mintFee: ratioPattern,
debtLimit: amountPattern,
},
Expand Down
18 changes: 11 additions & 7 deletions packages/inter-protocol/src/vaultFactory/storeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,15 @@ harden(fromVaultKey);
* For use by `normalizedCollRatioKey` and tests.
*
* @param {PriceQuote} quote
* @param {Ratio} compoundedInterest
* @param {Ratio} compoundedStabilityFee
* @param {Ratio} margin
* @returns {number}
*/
export const normalizedCollRatio = (quote, compoundedInterest, margin) => {
export const normalizedCollRatio = (quote, compoundedStabilityFee, margin) => {
const amountIn = getAmountIn(quote).value;
const amountOut = getAmountOut(quote).value;
const interestNumerator = compoundedInterest.numerator.value;
const interestBase = compoundedInterest.denominator.value;
const interestNumerator = compoundedStabilityFee.numerator.value;
const interestBase = compoundedStabilityFee.denominator.value;
const numerator = multiply(
margin.numerator.value,
multiply(amountIn, interestNumerator),
Expand All @@ -159,13 +159,17 @@ harden(normalizedCollRatio);
* out the numerator and the denominator, and divide only once.
*
* @param {PriceQuote} quote
* @param {Ratio} compoundedInterest
* @param {Ratio} compoundedStabilityFee
* @param {Ratio} margin
* @returns {string} lexically sortable string in which highest
* debt-to-collateral is earliest
*/
export const normalizedCollRatioKey = (quote, compoundedInterest, margin) => {
const collRatio = normalizedCollRatio(quote, compoundedInterest, margin);
export const normalizedCollRatioKey = (
quote,
compoundedStabilityFee,
margin,
) => {
const collRatio = normalizedCollRatio(quote, compoundedStabilityFee, margin);
return `${encodeNumber(collRatio)}:`;
};
harden(normalizedCollRatioKey);
8 changes: 4 additions & 4 deletions packages/inter-protocol/src/vaultFactory/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* @property {Ratio} liquidationMargin - margin below which collateral will be
* liquidated to satisfy the debt.
* @property {Ratio} liquidationPenalty - penalty charged upon liquidation as proportion of debt
* @property {Ratio} interestRate - annual interest rate charged on debt positions
* @property {Ratio} stabilityFee - annual interest rate charged on debt positions
* @property {Ratio} mintFee - The fee (in BasisPoints) charged when creating
* or increasing a debt position.
* @property {Amount<'nat'>} debtLimit
Expand Down Expand Up @@ -78,7 +78,7 @@
* @property {() => Ratio} getLiquidationMargin
* @property {() => Ratio} getMintFee
* @property {() => Promise<PriceQuote>} getCollateralQuote
* @property {() => Ratio} getInterestRate - The annual interest rate on a debt position
* @property {() => Ratio} getStabilityFee - The annual interest rate on a debt position
* @property {() => RelativeTime} getChargingPeriod - The period (in seconds) at
* which interest is charged to the debt position.
* @property {() => RelativeTime} getRecordingPeriod - The period (in seconds)
Expand Down Expand Up @@ -109,8 +109,8 @@

/**
* @typedef {object} DebtStatus
* @property {Timestamp} latestInterestUpdate
* @property {NatValue} interest interest accrued since latestInterestUpdate
* @property {Timestamp} latestStabilityFeeUpdate
* @property {NatValue} interest interest accrued since latestStabilityFeeUpdate
* @property {NatValue} newDebt total including principal and interest
*/

Expand Down
Loading

0 comments on commit 69236fd

Please sign in to comment.