Skip to content

Commit

Permalink
Merge pull request #8084 from Agoric/8079-fix-vaults-upgradability
Browse files Browse the repository at this point in the history
revert StabilityFee schema change
  • Loading branch information
turadg authored Jul 25, 2023
2 parents 9c40cec + c3a6912 commit ced08aa
Show file tree
Hide file tree
Showing 31 changed files with 284 additions and 298 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 compoundedStabilityFee value on the manager that keeps track of interest accrual since its launch
- a compoundedInterest 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
4 changes: 2 additions & 2 deletions packages/inter-protocol/docs/governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ the Inter Protocol Whitepaper, v0.8.
| Governance Key | Type | WP? |
| ------------------ | :---------------- | --- |
| DebtLimit | Amount | Yes |
| StabilityFee | Ratio | Yes |
| InterestRate | Ratio | Yes |
| LiquidationPadding | Ratio | |
| LiquidationMargin | Ratio | Yes |
| LiquidationPenalty | Ratio | Yes |
Expand All @@ -60,7 +60,7 @@ the Inter Protocol Whitepaper, v0.8.
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 StabilityFee and MintFee.
Note that the "stability fee" described in the Whitepaper comprises both InterestRate 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,
stabilityFeeValue = undefined,
interestRateValue = 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),
stabilityFeeValue: stabilityFeeValue && BigInt(stabilityFeeValue),
interestRateValue: interestRateValue && BigInt(interestRateValue),
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} currentCompoundedStabilityFee as coefficient
* @param {Ratio} previousCompoundedStabilityFee as coefficient
* @param {Ratio} currentCompoundedInterest as coefficient
* @param {Ratio} previousCompoundedInterest as coefficient
* @returns {Ratio} additional compounding since the previous
*/
const calculateRelativeCompounding = (
currentCompoundedStabilityFee,
previousCompoundedStabilityFee,
currentCompoundedInterest,
previousCompoundedInterest,
) => {
// divide compounded interest by the snapshot
return multiplyRatios(
currentCompoundedStabilityFee,
invertRatio(previousCompoundedStabilityFee),
currentCompoundedInterest,
invertRatio(previousCompoundedInterest),
);
};

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

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

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

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

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

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

// calculate delta of accrued debt
const debtStatus = interestCalculator.calculateReportingPeriod(
{
latestStabilityFeeUpdate: prior.latestStabilityFeeUpdate,
latestInterestUpdate: prior.latestInterestUpdate,
newDebt: prior.totalDebt.value,
interest: 0n, // XXX this is always zero, doesn't need to be an option
},
Expand All @@ -192,8 +191,8 @@ export const chargeInterest = (powers, params, prior, accruedUntil) => {
// done if none
if (interestAccrued === 0n) {
return {
compoundedStabilityFee: prior.compoundedStabilityFee,
latestStabilityFeeUpdate: debtStatus.latestStabilityFeeUpdate,
compoundedInterest: prior.compoundedInterest,
latestInterestUpdate: debtStatus.latestInterestUpdate,
totalDebt: prior.totalDebt,
};
}
Expand All @@ -203,8 +202,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 compoundedStabilityFee = calculateCompoundedStabilityFee(
prior.compoundedStabilityFee,
const compoundedInterest = calculateCompoundedInterest(
prior.compoundedInterest,
prior.totalDebt.value,
debtStatus.newDebt,
);
Expand All @@ -225,8 +224,8 @@ export const chargeInterest = (powers, params, prior, accruedUntil) => {
);

return {
compoundedStabilityFee,
latestStabilityFeeUpdate: debtStatus.latestStabilityFeeUpdate,
compoundedInterest,
latestInterestUpdate: debtStatus.latestInterestUpdate,
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.stabilityFeeValue
* @param {bigint} config.options.interestRateValue
*/
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.
stabilityFeeValue = 1n,
interestRateValue = 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)),
stabilityFee: makeRatio(stabilityFeeValue, stable),
interestRate: makeRatio(interestRateValue, 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,
stabilityFeeValue,
interestRateValue,
interchainAssetOptions,
scaledPriceAuthorityRef,
},
Expand Down Expand Up @@ -341,7 +341,7 @@ export const getManifestForAddAssetToVault = (
options: {
debtLimitValue,
interchainAssetOptions,
stabilityFeeValue,
interestRateValue,
},
};
};
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 STABILITY_FEE_KEY = 'StabilityFee';
export const INTEREST_RATE_KEY = 'InterestRate';
export const MINT_FEE_KEY = 'MintFee';
export const MIN_INITIAL_DEBT_KEY = 'MinInitialDebt';
export const SHORTFALL_INVITATION_KEY = 'ShortfallInvitation';
Expand Down Expand Up @@ -93,7 +93,7 @@ export const makeVaultParamManager = (
publisherKit,
{
debtLimit,
stabilityFee,
interestRate,
liquidationMargin,
liquidationPadding = zeroRatio(liquidationMargin),
liquidationPenalty,
Expand All @@ -102,7 +102,7 @@ export const makeVaultParamManager = (
) =>
makeParamManagerSync(publisherKit, {
[DEBT_LIMIT_KEY]: [ParamTypes.AMOUNT, debtLimit],
[STABILITY_FEE_KEY]: [ParamTypes.RATIO, stabilityFee],
[INTEREST_RATE_KEY]: [ParamTypes.RATIO, interestRate],
[LIQUIDATION_PADDING_KEY]: [ParamTypes.RATIO, liquidationPadding],
[LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, liquidationMargin],
[LIQUIDATION_PENALTY_KEY]: [ParamTypes.RATIO, liquidationPenalty],
Expand All @@ -114,7 +114,7 @@ export const vaultParamPattern = M.splitRecord(
{
liquidationMargin: ratioPattern,
liquidationPenalty: ratioPattern,
stabilityFee: ratioPattern,
interestRate: ratioPattern,
mintFee: ratioPattern,
debtLimit: amountPattern,
},
Expand Down
18 changes: 7 additions & 11 deletions packages/inter-protocol/src/vaultFactory/storeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ harden(fromVaultKey);
* For use by `normalizedCollRatioKey` and tests.
*
* @param {PriceQuote} quote
* @param {Ratio} compoundedStabilityFee
* @param {Ratio} compoundedInterest
* @param {Ratio} margin
* @returns {number}
*/
export const normalizedCollRatio = (quote, compoundedStabilityFee, margin) => {
export const normalizedCollRatio = (quote, compoundedInterest, margin) => {
const amountIn = getAmountIn(quote).value;
const amountOut = getAmountOut(quote).value;
const interestNumerator = compoundedStabilityFee.numerator.value;
const interestBase = compoundedStabilityFee.denominator.value;
const interestNumerator = compoundedInterest.numerator.value;
const interestBase = compoundedInterest.denominator.value;
const numerator = multiply(
margin.numerator.value,
multiply(amountIn, interestNumerator),
Expand All @@ -155,17 +155,13 @@ harden(normalizedCollRatio);
* out the numerator and the denominator, and divide only once.
*
* @param {PriceQuote} quote
* @param {Ratio} compoundedStabilityFee
* @param {Ratio} compoundedInterest
* @param {Ratio} margin
* @returns {string} lexically sortable string in which highest
* debt-to-collateral is earliest
*/
export const normalizedCollRatioKey = (
quote,
compoundedStabilityFee,
margin,
) => {
const collRatio = normalizedCollRatio(quote, compoundedStabilityFee, margin);
export const normalizedCollRatioKey = (quote, compoundedInterest, margin) => {
const collRatio = normalizedCollRatio(quote, compoundedInterest, 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 @@ -36,7 +36,7 @@
* liquidated to satisfy the debt.
* @property {Ratio} liquidationPenalty - penalty charged upon liquidation as
* proportion of debt
* @property {Ratio} stabilityFee - annual interest rate charged on debt
* @property {Ratio} interestRate - annual interest rate charged on debt
* positions
* @property {Ratio} mintFee - The fee (in BasisPoints) charged when creating or
* increasing a debt position.
Expand Down Expand Up @@ -87,7 +87,7 @@
* @property {() => Ratio} getLiquidationMargin
* @property {() => Ratio} getMintFee
* @property {() => Promise<PriceQuote>} getCollateralQuote
* @property {() => Ratio} getStabilityFee - The annual interest rate on a debt
* @property {() => Ratio} getInterestRate - The annual interest rate on a debt
* position
* @property {() => RelativeTime} getChargingPeriod - The period (in seconds) at
* which interest is charged to the debt position.
Expand Down Expand Up @@ -119,8 +119,8 @@

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

Expand Down
Loading

0 comments on commit ced08aa

Please sign in to comment.