Skip to content

Commit

Permalink
refactor(run-protocol): validate vault phase transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Feb 24, 2022
1 parent 6456af2 commit 59ee9b0
Showing 1 changed file with 45 additions and 18 deletions.
63 changes: 45 additions & 18 deletions packages/run-protocol/src/vaultFactory/vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,10 @@ const trace = makeTracer('Vault');
* ACTIVE - vault is in use and can be changed
* LIQUIDATING - vault is being liquidated by the vault manager, and cannot be changed by the user
* TRANSFER - vault is released from the manager and able to be transferred
* TRANSFER - vault is able to be transferred (payments and debits frozen until it has a new owner)
* CLOSED - vault was closed by the user and all assets have been paid out
* LIQUIDATED - vault was closed by the manager, with remaining assets paid to owner
*
* These are the valid state transitions:
* Active -> Liquidating
* Active -> Transferrable
* Active -> Closed
* Liquidating -> Liquidated
* Transferrable -> Active
* Transferrable -> Liquidating
*
* (Liquidated and Closed cannot be changed)
*
* @typedef {VaultPhase[keyof typeof VaultPhase]} VAULT_PHASE
*/
export const VaultPhase = /** @type {const} */ ({
Expand All @@ -56,6 +47,21 @@ export const VaultPhase = /** @type {const} */ ({
TRANSFER: 'transfer',
});

/**
* @type {{[K in VAULT_PHASE]: Array<VAULT_PHASE>}}
*/
const validTransitions = {
[VaultPhase.ACTIVE]: [
VaultPhase.LIQUIDATING,
VaultPhase.TRANSFER,
VaultPhase.CLOSED,
],
[VaultPhase.LIQUIDATING]: [VaultPhase.LIQUIDATED],
[VaultPhase.TRANSFER]: [VaultPhase.ACTIVE, VaultPhase.LIQUIDATING],
[VaultPhase.LIQUIDATED]: [],
[VaultPhase.CLOSED]: [],
};

const makeOuterKit = inner => {
const { updater: uiUpdater, notifier } = makeNotifierKit();

Expand Down Expand Up @@ -117,12 +123,31 @@ export const makeInnerVault = (
const { zcfSeat: liquidationZcfSeat, userSeat: liquidationSeat } =
zcf.makeEmptySeatKit(undefined);

// #region Phase state
/** @type {VAULT_PHASE} */
let phase = VaultPhase.ACTIVE;

/**
* @param {VAULT_PHASE} newPhase
*/
const assignPhase = newPhase => {
const validNewPhases = validTransitions[phase];
if (!validNewPhases.includes(newPhase))
throw new Error(`Vault cannot transition from ${phase} to ${newPhase}`);
phase = newPhase;
};

const assertPhase = allegedPhase => {
assert(
phase === allegedPhase,
X`vault must be ${allegedPhase}, not ${phase}`,
);
};

const assertVaultIsOpen = () => {
assert(phase === VaultPhase.ACTIVE, X`vault must still be active`);
assertPhase(VaultPhase.ACTIVE);
};
// #endregion

let outerUpdater;

Expand Down Expand Up @@ -263,6 +288,8 @@ export const makeInnerVault = (
debtSnapshot,
locked: getCollateralAmount(),
debt: getDebtAmount(),
// newPhase param is so that makeTransferInvitation can finish without setting the vault's phase
// TODO refactor https://github.com/Agoric/agoric-sdk/issues/4415
vaultState: newPhase,
});
};
Expand All @@ -287,8 +314,11 @@ export const makeInnerVault = (
outerUpdater.finish(uiState);
outerUpdater = null;
break;
case VaultPhase.TRANSFER:
// Transfer handles finish()/null itself
throw Error('no UI updates from transfer state');
default:
throw Error(`unreachable vaultState: ${phase}`);
throw Error(`unreachable vault phase: ${phase}`);
}
};
// XXX Echo notifications from the manager through all vaults
Expand All @@ -309,15 +339,12 @@ export const makeInnerVault = (
const liquidated = newDebt => {
updateDebtSnapshot(newDebt);

phase = VaultPhase.LIQUIDATED;
assignPhase(VaultPhase.LIQUIDATED);
updateUiState();
};

const liquidating = () => {
if (phase === VaultPhase.LIQUIDATING) {
throw new Error('Vault already liquidating');
}
phase = VaultPhase.LIQUIDATING;
assignPhase(VaultPhase.LIQUIDATING);
updateUiState();
};

Expand Down Expand Up @@ -358,7 +385,7 @@ export const makeInnerVault = (
runMint.burnLosses(harden({ RUN: currentDebt }), burnSeat);
seat.exit();
burnSeat.exit();
phase = VaultPhase.CLOSED;
assignPhase(VaultPhase.CLOSED);
updateDebtSnapshot(AmountMath.makeEmpty(runBrand));
updateUiState();

Expand Down

0 comments on commit 59ee9b0

Please sign in to comment.