Skip to content

Commit

Permalink
refactor(run-protocol): store liquidation cohort before liquidating (#…
Browse files Browse the repository at this point in the history
…4715)

* rename prioritizedVaults to illiquidVaults

* vaultsToLiquidate queue and attendant methods

* inline liquidateAndRemove

* back to prioritizedVaults

* code review suggestions

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
turadg and mergify[bot] authored Mar 2, 2022
1 parent 2b71fd4 commit 0a320ba
Showing 1 changed file with 55 additions and 34 deletions.
89 changes: 55 additions & 34 deletions packages/run-protocol/src/vaultFactory/vaultManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { makeNotifierKit, observeNotifier } from '@agoric/notifier';
import { AmountMath } from '@agoric/ertp';
import { Far } from '@endo/marshal';

import { makeScalarBigMapStore } from '@agoric/swingset-vat/src/storeModule';
import { makeInnerVault } from './vault.js';
import { makePrioritizedVaults } from './prioritizedVaults.js';
import { liquidate } from './liquidation.js';
Expand Down Expand Up @@ -101,22 +102,32 @@ export const makeVaultManager = (

let vaultCounter = 0;

// A store for vaultKits prioritized by their collaterization ratio.
//
// It should be set only once but it's a `let` because it can't be set until after the
// definition of reschedulePriceCheck, which refers to sortedVaultKits
/**
* A store for vaultKits prioritized by their collaterization ratio.
*
* It should be set only once but it's a `let` because it can't be set until after the
* definition of reschedulePriceCheck, which refers to sortedVaultKits
*
* @type {ReturnType<typeof makePrioritizedVaults>=}
*/
// XXX misleading mutability and confusing flow control; could be refactored with a listener
/** @type {ReturnType<typeof makePrioritizedVaults>=} */
let prioritizedVaults;

// Progress towards durability https://github.com/Agoric/agoric-sdk/issues/4568#issuecomment-1042346271
/** @type {MapStore<string, InnerVault>} */
const vaultsToLiquidate = makeScalarBigMapStore('vaultsToLiquidate');

/** @type {MutableQuote=} */
let outstandingQuote;
/** @type {Amount<NatValue>} */
let totalDebt = AmountMath.makeEmpty(runBrand, 'nat');
/** @type {Ratio}} */
let compoundedInterest = makeRatio(100n, runBrand); // starts at 1.0, no interest

// timestamp of most recent update to interest
/** @type {bigint} */
/**
* timestamp of most recent update to interest
* @type {bigint}
*/
let latestInterestUpdate = startTimeStamp;

const { updater: assetUpdater, notifier: assetNotifer } = makeNotifierKit(
Expand All @@ -129,30 +140,43 @@ export const makeVaultManager = (
);

/**
*
* @param {[key: string, vaultKit: InnerVault]} record
* @param {Iterable<[key: string, vaultKit: InnerVault]>} vaultEntries
*/
const liquidateAndRemove = async ([key, vault]) => {
const enqueueToLiquidate = vaultEntries => {
assert(prioritizedVaults);
trace('liquidating', vault.getVaultSeat().getProposal());

try {
// Start liquidation (vaultState: LIQUIDATING)
await liquidate(
zcf,
vault,
runMint.burnLosses,
liquidationStrategy,
collateralBrand,
);

await prioritizedVaults.removeVault(key);
} catch (e) {
// XXX should notify interested parties
console.error('liquidateAndRemove failed with', e);
for (const [k, v] of vaultEntries) {
vaultsToLiquidate.init(k, v);
prioritizedVaults.removeVault(k);
}
};

const executeLiquidation = async () => {
// Start all promises in parallel
// XXX we should have a direct method to map over entries
const liquidations = Array.from(vaultsToLiquidate.entries()).map(
async ([key, vault]) => {
trace('liquidating', vault.getVaultSeat().getProposal());

try {
// Start liquidation (vaultState: LIQUIDATING)
await liquidate(
zcf,
vault,
runMint.burnLosses,
liquidationStrategy,
collateralBrand,
);

vaultsToLiquidate.delete(key);
} catch (e) {
// XXX should notify interested parties
console.error('liquidateAndRemove failed with', e);
}
},
);
return Promise.all(liquidations);
};

// When any Vault's debt ratio is higher than the current high-water level,
// call reschedulePriceCheck() to request a fresh notification from the
// priceAuthority. There will be extra outstanding requests since we can't
Expand Down Expand Up @@ -211,14 +235,13 @@ export const makeVaultManager = (
getAmountIn(quote),
);

/** @type {Array<Promise<void>>} */
const toLiquidate = Array.from(
enqueueToLiquidate(
prioritizedVaults.entriesPrioritizedGTE(quoteRatioPlusMargin),
).map(liquidateAndRemove);
);

outstandingQuote = undefined;
// Ensure all vaults complete
await Promise.all(toLiquidate);
await executeLiquidation();

reschedulePriceCheck();
};
Expand All @@ -227,10 +250,8 @@ export const makeVaultManager = (
// In extreme situations, system health may require liquidating all vaults.
const liquidateAll = async () => {
assert(prioritizedVaults);
const toLiquidate = Array.from(prioritizedVaults.entries()).map(
liquidateAndRemove,
);
await Promise.all(toLiquidate);
enqueueToLiquidate(prioritizedVaults.entries());
await executeLiquidation();
};

/**
Expand Down

0 comments on commit 0a320ba

Please sign in to comment.