-
Notifications
You must be signed in to change notification settings - Fork 0
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
Liquidation Visibility - Iteration One #9
Changes from 35 commits
680330c
a3f6cf0
9d7abd6
5d1088b
f35bda1
26dc28f
e603254
ae88a64
7d477ff
b4c8a21
21bb9ca
3739f25
5dfa289
bbd2e48
7292068
d8af7fa
45cea3b
89db737
9fefcf0
e81df01
419c810
637a282
4089361
a5815ae
51eb929
ba95348
59e155b
1a17dd4
b7cdb13
b906859
e680466
c4e2b34
34ecf5c
348707e
e7259fb
4c45f2a
683f56d
732e1d7
6920d1a
dd3fbdb
728d695
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
|
@@ -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; | ||
|
@@ -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} */ ({})); | ||
|
||
|
@@ -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; | ||
|
@@ -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 | ||
|
@@ -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, | ||
|
@@ -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. | ||
|
@@ -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 { | ||
|
@@ -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( | ||
|
@@ -1197,10 +1276,31 @@ export const prepareVaultManagerKit = ( | |
), | ||
); | ||
|
||
const [{ userSeatPromise, deposited }, liquidationRecorderKits] = | ||
await Promise.all([ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. |
||
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(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point in time the auction is already finished. The question is whether or not we get the schedule where I'm not sure if we can depend on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. |
||
deposited, | ||
userSeatPromise, | ||
E( | ||
liquidationRecorderKits.preAuctionRecorderKit.recorder, | ||
).writeFinal(preAuctionState), | ||
]); | ||
|
||
const { storedCollateralQuote } = collateralEphemera( | ||
this.state.collateralBrand, | ||
|
@@ -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.liveAuctionSchedule?.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); | ||
} | ||
|
@@ -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; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to introduce a type guard instead of
M.any()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree.