Skip to content

Commit

Permalink
feat(run-protocol): variable rate vault/loan
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Jan 25, 2022
1 parent 402c3aa commit 54d509e
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 30 deletions.
13 changes: 7 additions & 6 deletions packages/run-protocol/src/vaultFactory/vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,6 @@ export const makeVaultKit = (

const { brand: runBrand } = runMint.getIssuerRecord();
let runDebt = AmountMath.makeEmpty(runBrand);
const interestCalculator = makeInterestCalculator(
runBrand,
manager.getInterestRate(),
manager.getChargingPeriod(),
manager.getRecordingPeriod(),
);

const getCollateralAllocated = seat =>
seat.getAmountAllocated('Collateral', collateralBrand);
Expand Down Expand Up @@ -472,6 +466,13 @@ export const makeVaultKit = (
* @returns {Amount} rate of interest used for accrual period
*/
const accrueInterestAndAddToPool = currentTime => {
const interestCalculator = makeInterestCalculator(
runBrand,
manager.getInterestRate(),
manager.getChargingPeriod(),
manager.getRecordingPeriod(),
);

const debtStatus = interestCalculator.calculateReportingPeriod(
{
latestInterestUpdate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js';
import { INTEREST_RATE_KEY } from '../../../../src/vaultFactory/params';
import { ONE_DAY, createCommittee, installContracts, makeVats } from '../setup';

const { quote: q } = assert;
const BASIS_POINTS = 10000n;

export const daysForVoting = 3;

const votersVote = async (detailsP, votersP, selections) => {
const [voters, { positions, questionHandle }] = await Promise.all([
votersP,
Expand Down Expand Up @@ -96,7 +97,6 @@ const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => {
governor,
vaultFactory,
runBrand,
timer,
electorateCreatorFacet,
electorateInstance,
brands: [collateralBrand],
Expand All @@ -117,7 +117,9 @@ const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => {
).getGovernedParams({
collateralBrand,
});
log(`param values before ${q(feeParamsStateAnte)}`);
log(
`before vote, InterestRate numerator is ${feeParamsStateAnte.InterestRate.value.numerator.value}`,
);

const contracts = {
vaultFactory,
Expand All @@ -132,8 +134,8 @@ const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => {
collateralBrand,
};
const counter = await setUpVote(
makeRatio(500n, runBrand, BASIS_POINTS),
3n * ONE_DAY,
makeRatio(4321n, runBrand, BASIS_POINTS),
BigInt(daysForVoting) * ONE_DAY,
votersP,
votes,
interestRateParam,
Expand All @@ -147,17 +149,11 @@ const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => {
vaultFactory.publicFacet,
).getGovernedParams(interestRateParam);
log(
`param values after vote on (${outcome.changeParam.parameterName}) ${q(
feeParamsStatePost,
)}`,
`after vote on (${outcome.changeParam.parameterName}), InterestRate numerator is ${feeParamsStatePost.InterestRate.value.numerator.value}`,
);
})
.catch(e => log(`BOOT fail: ${e}`));

await E(timer).tick();
await E(timer).tick();
await E(timer).tick();

await E(aliceP).startTest(testName, vaultFactory.publicFacet);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,30 @@ async function main(t, argv) {
const expectedVaultFactoryLog = [
'=> alice and the vaultFactory are set up',
'=> voter and electorate vats are set up',
'param values before {"ChargingPeriod":{"type":"nat","value":"[86400n]"},"InitialMargin":{"type":"ratio","value":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[100n]"},"numerator":{"brand":"[Seen]","value":"[120n]"}}},"InterestRate":{"type":"ratio","value":{"denominator":{"brand":"[Seen]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[250n]"}}},"LiquidationMargin":{"type":"ratio","value":{"denominator":{"brand":"[Seen]","value":"[100n]"},"numerator":{"brand":"[Seen]","value":"[105n]"}}},"LoanFee":{"type":"ratio","value":{"denominator":{"brand":"[Seen]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[200n]"}}},"RecordingPeriod":{"type":"nat","value":"[86400n]"}}',
'Voter Bob cast a ballot for {"changeParam":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"},"proposedValue":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[500n]"}}}',
'before vote, InterestRate numerator is 250',
'Voter Bob cast a ballot for {"changeParam":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"},"proposedValue":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[4321n]"}}}',
'Voter Carol cast a ballot for {"noChange":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"}}',
'Voter Dave cast a ballot for {"noChange":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"}}',
'Voter Emma cast a ballot for {"changeParam":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"},"proposedValue":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[500n]"}}}',
'Voter Flora cast a ballot for {"changeParam":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"},"proposedValue":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[500n]"}}}',
'Voter Emma cast a ballot for {"changeParam":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"},"proposedValue":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[4321n]"}}}',
'Voter Flora cast a ballot for {"changeParam":{"collateralBrand":"[Alleged: moola brand]","parameterName":"InterestRate"},"proposedValue":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[4321n]"}}}',
'=> alice.oneLoanWithInterest called',
'param values after vote on (InterestRate) {"ChargingPeriod":{"type":"nat","value":"[86400n]"},"InitialMargin":{"type":"ratio","value":{"denominator":{"brand":"[Alleged: RUN brand]","value":"[100n]"},"numerator":{"brand":"[Seen]","value":"[120n]"}}},"InterestRate":{"type":"ratio","value":{"denominator":{"brand":"[Seen]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[500n]"}}},"LiquidationMargin":{"type":"ratio","value":{"denominator":{"brand":"[Seen]","value":"[100n]"},"numerator":{"brand":"[Seen]","value":"[105n]"}}},"LoanFee":{"type":"ratio","value":{"denominator":{"brand":"[Seen]","value":"[10000n]"},"numerator":{"brand":"[Seen]","value":"[200n]"}}},"RecordingPeriod":{"type":"nat","value":"[86400n]"}}',
'governor from governed matches governor instance',
'Param "InterestRate" is in the question',
'Alice owes {"brand":"[Alleged: RUN brand]","value":"[510000n]"} after borrowing',
'Alice owes {"brand":"[Alleged: RUN brand]","value":"[510069n]"} after interest',
'at 0 days: Alice owes {"brand":"[Alleged: RUN brand]","value":"[510000n]"}',
'at 1 days: Alice owes {"brand":"[Alleged: RUN brand]","value":"[510035n]"}',
'at 2 days: vote ready to close',
'at 2 days: Alice owes {"brand":"[Alleged: RUN brand]","value":"[510070n]"}',
'after vote on (InterestRate), InterestRate numerator is 4321',
'at 3 days: vote closed',
'at 3 days: Alice owes {"brand":"[Alleged: RUN brand]","value":"[510105n]"}',
'at 3 days: 1 day after votes cast, uiNotifier update #4 has interestRate.numerator 250',
'at 4 days: 2 days after votes cast, uiNotifier update #5 has interestRate.numerator 4321',
'at 4 days: Alice owes {"brand":"[Alleged: RUN brand]","value":"[510608n]"}',
];

// NB: yarn build if changing any of the contract bundles under test
test.serial('vaultFactory', async t => {
const startingValues = [[100], [1000]];
const startingValues = [[100], [1000]]; // [aliceValues, ownerValues]
const dump = await main(t, ['oneLoanWithInterest', startingValues]);
t.deepEqual(dump.log, expectedVaultFactoryLog);
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { E } from '@agoric/eventual-send';
import { Far } from '@agoric/marshal';
import { AmountMath } from '@agoric/ertp';
import { daysForVoting } from './bootstrap';
import { ONE_DAY } from '../setup';

const { details: X, quote: q } = assert;

Expand All @@ -12,7 +14,7 @@ const { details: X, quote: q } = assert;
* @param {ZoeService} zoe
* @param {Brand[]} brands
* @param {Payment[]} payments
* @param {ManualTimer} timer
* @param {ManualTimer} timer configured to tick one day (@see setup.js)
* @returns {Promise<{startTest: Function}>}
*/
const build = async (log, zoe, brands, payments, timer) => {
Expand All @@ -39,10 +41,38 @@ const build = async (log, zoe, brands, payments, timer) => {
}),
);

const { vault } = await E(loanSeat).getOfferResult();
log(`Alice owes ${q(await E(vault).getDebtAmount())} after borrowing`);
/** @type {LoanKit} */
const { vault, uiNotifier } = await E(loanSeat).getOfferResult();

const timeLog = async msg =>
log(
`at ${(await E(timer).getCurrentTimestamp()) / ONE_DAY} days: ${msg}`,
);

timeLog(`Alice owes ${q(await E(vault).getDebtAmount())}`);

// accrue one day of interest at initial rate
await E(timer).tick();
timeLog(`Alice owes ${q(await E(vault).getDebtAmount())}`);

// advance time enough that governance updates the interest rate
await Promise.all(new Array(daysForVoting).fill(E(timer).tick()));
timeLog('vote ready to close');
timeLog(`Alice owes ${q(await E(vault).getDebtAmount())}`);

await E(timer).tick();
timeLog('vote closed');
timeLog(`Alice owes ${q(await E(vault).getDebtAmount())}`);

const uiDescription = async () => {
const current = await E(uiNotifier).getUpdateSince();
return `uiNotifier update #${current.updateCount} has interestRate.numerator ${current.value.interestRate.numerator.value}`;
};

timeLog(`1 day after votes cast, ${await uiDescription()}`);
await E(timer).tick();
log(`Alice owes ${q(await E(vault).getDebtAmount())} after interest`);
timeLog(`2 days after votes cast, ${await uiDescription()}`);
timeLog(`Alice owes ${q(await E(vault).getDebtAmount())}`);
};

return Far('build', {
Expand Down

0 comments on commit 54d509e

Please sign in to comment.