Skip to content

Commit

Permalink
feat(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 3652615 commit 5a0ed49
Showing 1 changed file with 35 additions and 18 deletions.
53 changes: 35 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 @@ -120,8 +126,22 @@ export const makeInnerVault = (
/** @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 still be ${allegedPhase}`);
};

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

let outerUpdater;
Expand Down Expand Up @@ -288,7 +308,7 @@ export const makeInnerVault = (
outerUpdater = null;
break;
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 +329,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 +375,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 5a0ed49

Please sign in to comment.