Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jorge/update contacts #6

Closed
wants to merge 9 commits into from
8 changes: 8 additions & 0 deletions packages/inter-protocol/src/vaultFactory/vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const VaultI = M.interface('Vault', {
getCurrentDebt: M.call().returns(AmountShape),
getNormalizedDebt: M.call().returns(AmountShape),
getVaultSeat: M.call().returns(SeatShape),
getVaultState: M.call().returns(M.any()),
initVaultKit: M.call(SeatShape, StorageNodeShape).returns(M.promise()),
liquidated: M.call().returns(undefined),
liquidating: M.call().returns(undefined),
Expand Down Expand Up @@ -597,6 +598,13 @@ export const prepareVault = (baggage, makeRecorderKit, zcf) => {
return this.state.vaultSeat;
},

getVaultState() {
return {
idInManager: this.state.idInManager,
phase: this.state.phase,
};
},

/**
* @param {ZCFSeat} seat
* @param {StorageNode} storageNode
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/vaultFactory/vaultDirector.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ const prepareVaultDirector = (
makeLiquidationWaker() {
return makeWaker('liquidationWaker', _timestamp => {
// XXX floating promise
allManagersDo(vm => vm.liquidateVaults(auctioneer));
allManagersDo(vm => vm.liquidateVaults(auctioneer, _timestamp));
});
},
makeReschedulerWaker() {
Expand Down
154 changes: 143 additions & 11 deletions packages/inter-protocol/src/vaultFactory/vaultManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
} from '@agoric/zoe/src/contractSupport/index.js';
import { PriceQuoteShape, SeatShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/eventual-send';
import { TimestampShape } from '@agoric/time';
import { AuctionPFShape } from '../auction/auctioneer.js';
import {
checkDebtLimit,
Expand Down Expand Up @@ -171,6 +172,7 @@ export const watchQuoteNotifier = async (notifierP, watcher, ...args) => {
* @typedef {{
* assetTopicKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<AssetState>;
* debtBrand: Brand<'nat'>;
* liquidationsStorageNode: StorageNode;
* liquidatingVaults: SetStore<Vault>;
* metricsTopicKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<MetricsNotification>;
* poolIncrementSeat: ZCFSeat;
Expand Down Expand Up @@ -205,6 +207,32 @@ export const watchQuoteNotifier = async (notifierP, watcher, ...args) => {
* storedCollateralQuote: PriceQuote | null;
* }}
*/

/**
* @typedef {(
* | string
* | { collateralAmount: Amount<'nat'>; debtAmount: Amount<'nat'> }
* )[][]} PreAuctionState
*
* @typedef {(string | { phase: string })[][]} PostAuctionState
*
* @typedef {{
* collateralOffered?: Amount<'nat'>;
* istTarget?: Amount<'nat'>;
* collateralForReserve?: Amount<'nat'>;
* shortfallToReserve?: Amount<'nat'>;
* mintedProceeds?: Amount<'nat'>;
* collateralSold?: Amount<'nat'>;
* collateralRemaining?: Amount<'nat'>;
* endTime?: import('@agoric/time').TimestampRecord | null;
* }} AuctionResultState
*
* @typedef {{
* preAuctionRecorderKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<PreAuctionState>;
* postAuctionRecorderKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<PostAuctionState>;
* auctionResultRecorderKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<AuctionResultState>;
* }} LiquidationRecorderKits
*/
// any b/c will be filled after start()
const collateralEphemera = makeEphemeraProvider(() => /** @type {any} */ ({}));

Expand All @@ -227,14 +255,18 @@ export const prepareVaultManagerKit = (
const makeVault = prepareVault(baggage, makeRecorderKit, zcf);

/**
* @param {HeldParams & { metricsStorageNode: StorageNode }} params
* @param {HeldParams & {
* metricsStorageNode: StorageNode;
* liquidationsStorageNode: StorageNode;
* }} params
* @returns {HeldParams & ImmutableState & MutableState}
*/
const initState = params => {
const {
debtMint,
collateralBrand,
metricsStorageNode,
liquidationsStorageNode,
startTimeStamp,
storageNode,
} = params;
Expand All @@ -244,7 +276,7 @@ export const prepareVaultManagerKit = (
const immutable = {
debtBrand,
poolIncrementSeat: zcf.makeEmptySeatKit().zcfSeat,

liquidationsStorageNode,
/**
* Vaults that have been sent for liquidation. When we get proceeds (or
* lack thereof) back from the liquidator, we will allocate them among the
Expand Down Expand Up @@ -336,7 +368,9 @@ export const prepareVaultManagerKit = (
getCollateralQuote: M.call().returns(PriceQuoteShape),
getPublicFacet: M.call().returns(M.remotable('publicFacet')),
lockOraclePrices: M.call().returns(PriceQuoteShape),
liquidateVaults: M.call(AuctionPFShape).returns(M.promise()),
liquidateVaults: M.call(AuctionPFShape, TimestampShape).returns(
M.promise(),
),
}),
},
initState,
Expand Down Expand Up @@ -649,6 +683,48 @@ export const prepareVaultManagerKit = (
return E(metricsTopicKit.recorder).write(payload);
},

/**
* @param {{ absValue: any }} timestamp
* @returns {Promise<LiquidationRecorderKits>}
*/
async makeLiquidationRecorderKits(timestamp) {
const {
state: { liquidationsStorageNode },
} = this;

const timestampStorageNode = E(liquidationsStorageNode).makeChildNode(
`${timestamp.absValue}`,
);

const [
preAuctionStorageNode,
postAuctionStorageNode,
auctionResultStorageNode,
] = await Promise.all([
E(E(timestampStorageNode).makeChildNode('vaults')).makeChildNode(
'preAuction',
),
E(E(timestampStorageNode).makeChildNode('vaults')).makeChildNode(
'postAuction',
),
E(timestampStorageNode).makeChildNode('auctionResult'),
]);

const preAuctionRecorderKit = makeRecorderKit(preAuctionStorageNode);
const postAuctionRecorderKit = makeRecorderKit(
postAuctionStorageNode,
);
const auctionResultRecorderKit = makeRecorderKit(
auctionResultStorageNode,
);

return {
preAuctionRecorderKit,
postAuctionRecorderKit,
auctionResultRecorderKit,
};
},

/**
* This is designed to tolerate an incomplete plan, in case
* calculateDistributionPlan encounters an error during its calculation.
Expand Down Expand Up @@ -1115,8 +1191,11 @@ export const prepareVaultManagerKit = (
void facets.helper.writeMetrics();
return storedCollateralQuote;
},
/** @param {ERef<AuctioneerPublicFacet>} auctionPF */
async liquidateVaults(auctionPF) {
/**
* @param {ERef<AuctioneerPublicFacet>} auctionPF
* @param {{ absValue: bigint }} timestamp
*/
async liquidateVaults(auctionPF, timestamp) {
const { state, facets } = this;
const { self, helper } = facets;
const {
Expand Down Expand Up @@ -1183,7 +1262,7 @@ export const prepareVaultManagerKit = (
helper.markLiquidating(totalDebt, totalCollateral);
void helper.writeMetrics();

const { userSeatPromise, deposited } = await E.when(
const makeDeposit = E.when(
E(auctionPF).makeDepositInvitation(),
depositInvitation =>
offerTo(
Expand All @@ -1197,10 +1276,31 @@ export const prepareVaultManagerKit = (
),
);

const [{ userSeatPromise, deposited }, liquidationRecorderKits] =
await Promise.all([
makeDeposit,
helper.makeLiquidationRecorderKits(timestamp),
]);

/** @type PreAuctionState */
const preAuctionState = [...vaultData.entries()].map(
([vault, data]) => [
`vault${vault.getVaultState().idInManager}`,
{ ...data },
],
);

// This is expected to wait for the duration of the auction, which
// is controlled by the auction parameters startFrequency, clockStep,
// and the difference between startingRate and lowestRate.
const [proceeds] = await Promise.all([deposited, userSeatPromise]);
const [auctionSchedule, proceeds] = await Promise.all([
E(auctionPF).getSchedules(),
deposited,
userSeatPromise,
E(
liquidationRecorderKits.preAuctionRecorderKit.recorder,
).writeFinal(preAuctionState),
]);

const { storedCollateralQuote } = collateralEphemera(
this.state.collateralBrand,
Expand All @@ -1227,6 +1327,35 @@ export const prepareVaultManagerKit = (
totalDebt,
vaultsInPlan,
});

/** @type AuctionResultState */
const auctionResultState = {
collateralOffered: totalCollateral,
istTarget: totalDebt,
collateralForReserve: plan.collateralForReserve,
shortfallToReserve: plan.shortfallToReserve,
mintedProceeds: plan.mintedProceeds,
collateralSold: plan.collateralSold,
collateralRemaining: plan.collatRemaining,
endTime: auctionSchedule.nextAuctionSchedule?.endTime,
};
void E(
liquidationRecorderKits.auctionResultRecorderKit.recorder,
).writeFinal(auctionResultState);

/** @type PostAuctionState */
const postAuctionState = plan.transfersToVault.map(
([id, transfer]) => [
`vault${vaultsInPlan[id].getVaultState().idInManager}`,
{
...transfer,
phase: vaultsInPlan[id].getVaultState().phase,
},
],
);
void E(
liquidationRecorderKits.postAuctionRecorderKit.recorder,
).writeFinal(postAuctionState);
} catch (err) {
console.error('🚨 Error distributing proceeds:', err);
}
Expand Down Expand Up @@ -1265,16 +1394,19 @@ export const prepareVaultManagerKit = (
/**
* @param {Omit<
* Parameters<typeof makeVaultManagerKitInternal>[0],
* 'metricsStorageNode'
* 'metricsStorageNode' | 'liquidationsStorageNode'
* >} externalParams
*/
const makeVaultManagerKit = async externalParams => {
const metricsStorageNode = await E(
externalParams.storageNode,
).makeChildNode('metrics');
const [metricsStorageNode, liquidationsStorageNode] = await Promise.all([
E(externalParams.storageNode).makeChildNode('metrics'),
E(externalParams.storageNode).makeChildNode('liquidations'),
]);

return makeVaultManagerKitInternal({
...externalParams,
metricsStorageNode,
liquidationsStorageNode,
});
};
return makeVaultManagerKit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3421,4 +3421,4 @@ test('Bug 7851 & no bidders', async t => {
});

await aethVaultMetrics.assertNoUpdate();
});
});
Loading