@agoric/inter-protocol 0.16.2-dev-b4286a6.0.b4286a6 → 0.16.2-dev-b95c1c8.0.b95c1c8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/package.json +16 -16
  2. package/src/index.js +1 -0
  3. package/src/proposals/addAssetToVault.js +3 -97
  4. package/src/proposals/committee-proposal.js +3 -9
  5. package/src/proposals/core-proposal.js +1 -32
  6. package/src/proposals/econ-behaviors.js +1 -140
  7. package/src/vaultFactory/params.d.ts.map +1 -1
  8. package/src/vaultFactory/params.js +1 -0
  9. package/src/vaultFactory/types-ambient.d.ts +0 -2
  10. package/src/vaultFactory/types-ambient.d.ts.map +1 -1
  11. package/src/vaultFactory/types-ambient.js +0 -3
  12. package/src/vaultFactory/vaultDirector.d.ts +5 -40
  13. package/src/vaultFactory/vaultDirector.d.ts.map +1 -1
  14. package/src/vaultFactory/vaultDirector.js +3 -58
  15. package/src/vaultFactory/vaultFactory.d.ts +4 -22
  16. package/src/vaultFactory/vaultFactory.d.ts.map +1 -1
  17. package/src/vaultFactory/vaultFactory.js +2 -9
  18. package/src/vaultFactory/vaultManager.d.ts +2 -180
  19. package/src/vaultFactory/vaultManager.d.ts.map +1 -1
  20. package/src/vaultFactory/vaultManager.js +3 -300
  21. package/src/auction/auctionBook.d.ts +0 -150
  22. package/src/auction/auctionBook.d.ts.map +0 -1
  23. package/src/auction/auctionBook.js +0 -796
  24. package/src/auction/auctionMath.d.ts +0 -18
  25. package/src/auction/auctionMath.d.ts.map +0 -1
  26. package/src/auction/auctionMath.js +0 -82
  27. package/src/auction/auctioneer.d.ts +0 -76
  28. package/src/auction/auctioneer.d.ts.map +0 -1
  29. package/src/auction/auctioneer.js +0 -745
  30. package/src/auction/offerBook.d.ts +0 -47
  31. package/src/auction/offerBook.d.ts.map +0 -1
  32. package/src/auction/offerBook.js +0 -227
  33. package/src/auction/params.d.ts +0 -156
  34. package/src/auction/params.d.ts.map +0 -1
  35. package/src/auction/params.js +0 -184
  36. package/src/auction/scheduleMath.d.ts +0 -8
  37. package/src/auction/scheduleMath.d.ts.map +0 -1
  38. package/src/auction/scheduleMath.js +0 -174
  39. package/src/auction/scheduler.d.ts +0 -57
  40. package/src/auction/scheduler.d.ts.map +0 -1
  41. package/src/auction/scheduler.js +0 -391
  42. package/src/auction/sortedOffers.d.ts +0 -9
  43. package/src/auction/sortedOffers.d.ts.map +0 -1
  44. package/src/auction/sortedOffers.js +0 -141
  45. package/src/proposals/add-auction.js +0 -290
  46. package/src/proposals/upgrade-vaults.js +0 -212
  47. package/src/vaultFactory/liquidation.d.ts +0 -29
  48. package/src/vaultFactory/liquidation.d.ts.map +0 -1
  49. package/src/vaultFactory/liquidation.js +0 -313
  50. package/src/vaultFactory/proceeds.d.ts +0 -36
  51. package/src/vaultFactory/proceeds.d.ts.map +0 -1
  52. package/src/vaultFactory/proceeds.js +0 -285
@@ -1,313 +0,0 @@
1
- // @jessie-check
2
-
3
- import { AmountMath } from '@agoric/ertp';
4
- import { makeTracer } from '@agoric/internal';
5
- import { observeIteration, subscribeEach } from '@agoric/notifier';
6
- import { makeScalarMapStore } from '@agoric/store';
7
- import { TimeMath } from '@agoric/time';
8
- import { E } from '@endo/eventual-send';
9
-
10
- import { AUCTION_START_DELAY, PRICE_LOCK_PERIOD } from '../auction/params.js';
11
- import { makeCancelTokenMaker } from '../auction/util.js';
12
-
13
- const trace = makeTracer('LIQ');
14
-
15
- /**
16
- * @import {MapStore, SetStore} from '@agoric/store';
17
- * @import {RelativeTimeRecord, TimerService, TimerWaker} from '@agoric/time';
18
- * @import {PriceQuote} from '@agoric/zoe/tools/types.js';
19
- * @import {Schedule} from '../auction/scheduler.js';
20
- * @import {TimestampRecord} from '@agoric/time';
21
- * @import {AuctioneerPublicFacet} from '../auction/auctioneer.js';
22
- * @import {makePrioritizedVaults} from './prioritizedVaults.js';
23
- * @import {Ratio} from '@agoric/ertp';
24
- * @import {Vault} from './vault.js';
25
- */
26
-
27
- const makeCancelToken = makeCancelTokenMaker('liq');
28
-
29
- /**
30
- * This will normally be set. If the schedule goes sideways, we'll unschedule
31
- * all events and unset it. When auction params are changed, we'll restart the
32
- * schedule
33
- *
34
- * @type {object | undefined}
35
- */
36
- let cancelToken = makeCancelToken();
37
-
38
- const waitForRepair = () => {
39
- // the params observer will setWakeup to resume.
40
- console.warn(
41
- '🛠️ No wake for reschedule. Repair by resetting auction params.',
42
- );
43
- };
44
-
45
- // Generally, canceling wakups has to be followed by setting up new ones but
46
- // not in the case of pausing wakeups. We do that in the event of invalid
47
- // schedule params. When they are repaired by governance, the wakers resume.
48
- /** @param {ERef<TimerService>} timer */
49
- const cancelWakeups = timer => {
50
- if (cancelToken) {
51
- void E(timer).cancel(cancelToken);
52
- }
53
- cancelToken = undefined;
54
- };
55
-
56
- /**
57
- * Schedule wakeups for the _next_ auction round.
58
- *
59
- * In practice, there are these cases to handle (with N as "live" and N+1 is
60
- * "next"):
61
- *
62
- * | when (now within the range) | what |
63
- * | ------------------------------- | ---------------------------------------- |
64
- * | [start N, nominalStart N+1] | good: schedule normally the three wakers |
65
- * | (nominalStart N+1, endTime N+1] | recover: skip round N+1 and schedule N+2 |
66
- * | (endTime N+1, ∞) | give up: wait for repair by governance |
67
- *
68
- * @param {object} opts
69
- * @param {ERef<TimerService>} opts.timer
70
- * @param {TimerWaker} opts.priceLockWaker
71
- * @param {TimerWaker} opts.liquidationWaker
72
- * @param {TimerWaker} opts.reschedulerWaker
73
- * @param {Schedule} opts.nextAuctionSchedule
74
- * @param {TimestampRecord} opts.now
75
- * @param {ParamStateRecord} opts.params
76
- * @returns {void}
77
- */
78
- const setWakeups = ({
79
- nextAuctionSchedule,
80
- now,
81
- timer,
82
- reschedulerWaker,
83
- liquidationWaker,
84
- priceLockWaker,
85
- params,
86
- }) => {
87
- const { startTime, endTime } = nextAuctionSchedule;
88
-
89
- /** @type {RelativeTimeRecord} */
90
- // @ts-expect-error Casting
91
- // eslint-disable-next-line no-restricted-syntax -- https://github.com/Agoric/eslint-config-jessie/issues/33
92
- const priceLockPeriod = params[PRICE_LOCK_PERIOD].value;
93
- /** @type {RelativeTimeRecord} */
94
- // @ts-expect-error Casting
95
- // eslint-disable-next-line no-restricted-syntax -- https://github.com/Agoric/eslint-config-jessie/issues/33
96
- const auctionStartDelay = params[AUCTION_START_DELAY].value;
97
-
98
- // nominal is the declared start time, but the actual auctioning begins after auctionStartDelay
99
- const nominalStart = TimeMath.subtractAbsRel(startTime, auctionStartDelay);
100
-
101
- // Is there a problem (case 2 or 3)?
102
- // setWakeupsForNextAuction is supposed to be called just after an auction
103
- // started. If we're late but there's still time for the nominal start, then
104
- // we'll proceed. Reschedule for the next round if that's still in the future.
105
- // Otherwise, wait for governance params to change.
106
- if (TimeMath.compareAbs(now, nominalStart) > 0) {
107
- // nominalStart is past, so cancel timers and plan to recover
108
- cancelWakeups(timer);
109
-
110
- if (TimeMath.compareAbs(now, endTime) < 0) {
111
- // We're currently scheduling the N+1 (where N is live), but we need to skip it.
112
- // So wake up for the N+2 round's startTime
113
- const afterNextStartTime = TimeMath.addAbsRel(endTime, 1n);
114
- trace(
115
- 'CASE 2: endTime is in the future or now so reschedule waking to startTime of the following round',
116
- afterNextStartTime,
117
- );
118
- void E(timer).setWakeup(afterNextStartTime, reschedulerWaker);
119
- } else {
120
- trace('CASE 3: endTime is past; wait for repair');
121
- waitForRepair();
122
- }
123
-
124
- return;
125
- }
126
- trace('CASE 1: nominalStart is now or in the future');
127
-
128
- cancelToken = cancelToken || makeCancelToken();
129
- const priceLockWakeTime = TimeMath.subtractAbsRel(
130
- nominalStart,
131
- priceLockPeriod,
132
- );
133
- const a = t => TimeMath.absValue(t);
134
- trace('scheduling ', a(priceLockWakeTime), a(nominalStart), a(startTime));
135
- void E(timer).setWakeup(priceLockWakeTime, priceLockWaker, cancelToken);
136
- void E(timer).setWakeup(nominalStart, liquidationWaker, cancelToken);
137
- // Call setWakeupsForNextAuction again one tick after nominalStart
138
- const afterStart = TimeMath.addAbsRel(startTime, 1n);
139
- void E(timer).setWakeup(afterStart, reschedulerWaker, cancelToken);
140
- };
141
-
142
- /**
143
- * Schedule wakeups for the _next_ auction round.
144
- *
145
- * Called by vaultDirector's resetWakeupsForNextAuction at start() and every
146
- * time there's a "reschedule" wakeup.
147
- *
148
- * @param {ERef<AuctioneerPublicFacet>} auctioneerPublicFacet
149
- * @param {ERef<TimerService>} timer
150
- * @param {TimerWaker} priceLockWaker
151
- * @param {TimerWaker} liquidationWaker
152
- * @param {TimerWaker} reschedulerWaker
153
- * @returns {Promise<void>}
154
- */
155
- export const setWakeupsForNextAuction = async (
156
- auctioneerPublicFacet,
157
- timer,
158
- priceLockWaker,
159
- liquidationWaker,
160
- reschedulerWaker,
161
- ) => {
162
- const [{ nextAuctionSchedule }, params, now] = await Promise.all([
163
- E(auctioneerPublicFacet).getSchedules(),
164
- E(auctioneerPublicFacet).getGovernedParams(),
165
- E(timer).getCurrentTimestamp(),
166
- ]);
167
-
168
- trace(
169
- 'setWakeupsForNextAuction at',
170
- now.absValue,
171
- 'with',
172
- nextAuctionSchedule,
173
- );
174
- if (!nextAuctionSchedule) {
175
- // There should always be a nextAuctionSchedule. If there isn't, give up for now.
176
- cancelWakeups(timer);
177
- waitForRepair();
178
- return;
179
- }
180
-
181
- setWakeups({
182
- nextAuctionSchedule,
183
- now,
184
- timer,
185
- reschedulerWaker,
186
- liquidationWaker,
187
- priceLockWaker,
188
- params,
189
- });
190
- };
191
- harden(setWakeupsForNextAuction);
192
-
193
- /**
194
- * @param {Amount<'nat'>} debt
195
- * @param {Amount<'nat'>} minted
196
- * @returns {{ overage: Amount<'nat'>; shortfall: Amount<'nat'> }}
197
- */
198
- export const liquidationResults = (debt, minted) => {
199
- if (AmountMath.isEmpty(minted)) {
200
- return { overage: minted, shortfall: debt };
201
- }
202
-
203
- const [overage, shortfall] = AmountMath.isGTE(debt, minted)
204
- ? [AmountMath.makeEmptyFromAmount(debt), AmountMath.subtract(debt, minted)]
205
- : [AmountMath.subtract(minted, debt), AmountMath.makeEmptyFromAmount(debt)];
206
-
207
- return { overage, shortfall };
208
- };
209
- harden(liquidationResults);
210
-
211
- /**
212
- * Watch governed params for change
213
- *
214
- * @param {ERef<AuctioneerPublicFacet>} auctioneerPublicFacet
215
- * @param {ERef<TimerService>} timer
216
- * @param {TimerWaker} reschedulerWaker
217
- * @returns {void}
218
- */
219
- export const watchForGovernanceChange = (
220
- auctioneerPublicFacet,
221
- timer,
222
- reschedulerWaker,
223
- ) => {
224
- void E.when(E(timer).getCurrentTimestamp(), now =>
225
- // make one observer that will usually ignore the update.
226
- observeIteration(
227
- subscribeEach(E(auctioneerPublicFacet).getSubscription()),
228
- harden({
229
- async updateState(_newState) {
230
- if (!cancelToken) {
231
- cancelToken = makeCancelToken();
232
- void E(timer).setWakeup(
233
- // bump one tick to prevent an infinite loop
234
- TimeMath.addAbsRel(now, 1n),
235
- reschedulerWaker,
236
- cancelToken,
237
- );
238
- }
239
- },
240
- }),
241
- ),
242
- );
243
- };
244
-
245
- /**
246
- * @param {ZCF} zcf
247
- * @param {object} collateralizationDetails
248
- * @param {PriceQuote} collateralizationDetails.quote
249
- * @param {Ratio} collateralizationDetails.interest
250
- * @param {Ratio} collateralizationDetails.margin
251
- * @param {ReturnType<typeof makePrioritizedVaults>} prioritizedVaults
252
- * @param {SetStore<Vault>} liquidatingVaults
253
- * @param {Brand<'nat'>} debtBrand
254
- * @param {Brand<'nat'>} collateralBrand
255
- * @returns {{
256
- * vaultData: MapStore<
257
- * Vault,
258
- * { collateralAmount: Amount<'nat'>; debtAmount: Amount<'nat'> }
259
- * >;
260
- * totalDebt: Amount<'nat'>;
261
- * totalCollateral: Amount<'nat'>;
262
- * liqSeat: ZCFSeat;
263
- * }}
264
- */
265
- export const getLiquidatableVaults = (
266
- zcf,
267
- collateralizationDetails,
268
- prioritizedVaults,
269
- liquidatingVaults,
270
- debtBrand,
271
- collateralBrand,
272
- ) => {
273
- const vaultsToLiquidate = prioritizedVaults.removeVaultsBelow(
274
- collateralizationDetails,
275
- );
276
- /**
277
- * @type {MapStore<
278
- * Vault,
279
- * { collateralAmount: Amount<'nat'>; debtAmount: Amount<'nat'> }
280
- * >}
281
- */
282
- const vaultData = makeScalarMapStore();
283
-
284
- const { zcfSeat: liqSeat } = zcf.makeEmptySeatKit();
285
- let totalDebt = AmountMath.makeEmpty(debtBrand);
286
- let totalCollateral = AmountMath.makeEmpty(collateralBrand);
287
- /** @type {TransferPart[]} */
288
- const transfers = [];
289
-
290
- for (const vault of vaultsToLiquidate.values()) {
291
- vault.liquidating();
292
- liquidatingVaults.add(vault);
293
-
294
- const collateralAmount = vault.getCollateralAmount();
295
- totalCollateral = AmountMath.add(totalCollateral, collateralAmount);
296
-
297
- const debtAmount = vault.getCurrentDebt();
298
- totalDebt = AmountMath.add(totalDebt, debtAmount);
299
- transfers.push([
300
- vault.getVaultSeat(),
301
- liqSeat,
302
- { Collateral: collateralAmount },
303
- ]);
304
- vaultData.init(vault, { collateralAmount, debtAmount });
305
- }
306
-
307
- if (transfers.length > 0) {
308
- zcf.atomicRearrange(harden(transfers));
309
- }
310
-
311
- return { vaultData, totalDebt, totalCollateral, liqSeat };
312
- };
313
- harden(getLiquidatableVaults);
@@ -1,36 +0,0 @@
1
- export function calculateDistributionPlan({ proceeds, totalDebt, totalCollateral, oraclePriceAtStart, vaultsBalances, penaltyRate, }: {
2
- proceeds: AmountKeywordRecord;
3
- totalDebt: Amount<"nat">;
4
- totalCollateral: Amount<"nat">;
5
- oraclePriceAtStart: PriceDescription;
6
- vaultsBalances: VaultBalances[];
7
- penaltyRate: Ratio;
8
- }): DistributionPlan;
9
- /**
10
- * The plan to execute for distributing proceeds of a liquidation.
11
- *
12
- * Vaults are referenced by index in the list sent to the calculator.
13
- */
14
- export type DistributionPlan = {
15
- overage: Amount<"nat">;
16
- shortfallToReserve: Amount<"nat">;
17
- collateralForReserve: Amount<"nat">;
18
- actualCollateralSold: Amount<"nat">;
19
- collateralSold: Amount<"nat">;
20
- collatRemaining: Amount<"nat">;
21
- debtToBurn: Amount<"nat">;
22
- mintedForReserve: Amount<"nat">;
23
- mintedProceeds: Amount<"nat">;
24
- phantomDebt: Amount<"nat">;
25
- totalPenalty: Amount<"nat">;
26
- transfersToVault: [number, AmountKeywordRecord][];
27
- vaultsToReinstate: number[];
28
- };
29
- export type VaultBalances = {
30
- collateral: Amount<"nat">;
31
- presaleDebt: Amount<"nat">;
32
- currentDebt: Amount<"nat">;
33
- };
34
- import type { PriceDescription } from '@agoric/zoe/tools/types.js';
35
- import type { Ratio } from '@agoric/ertp';
36
- //# sourceMappingURL=proceeds.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"proceeds.d.ts","sourceRoot":"","sources":["proceeds.js"],"names":[],"mappings":"AA+DO,sIATJ;IAAoC,QAAQ,EAApC,mBAAmB;IACG,SAAS,EAA/B,MAAM,CAAC,KAAK,CAAC;IACS,eAAe,EAArC,MAAM,CAAC,KAAK,CAAC;IACY,kBAAkB,EAA3C,gBAAgB;IACQ,cAAc,EAAtC,aAAa,EAAE;IAED,WAAW,EAAzB,KAAK;CACb,GAAU,gBAAgB,CA8N5B;;;;;;+BA1QY;IACR,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,oBAAoB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,CAAC;IAClD,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;4BAOS;IACR,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;CAC5B;sCA9ByF,4BAA4B;2BACjG,cAAc"}
@@ -1,285 +0,0 @@
1
- import { AmountMath } from '@agoric/ertp';
2
- import {
3
- ceilDivideBy,
4
- ceilMultiplyBy,
5
- floorMultiplyBy,
6
- makeRatioFromAmounts,
7
- multiplyRatios,
8
- } from '@agoric/zoe/src/contractSupport/index.js';
9
- import { quoteAsRatio, subtractToEmpty } from '../contractSupport.js';
10
- import { liquidationResults } from './liquidation.js';
11
-
12
- /**
13
- * @import {PriceAuthority, PriceDescription, PriceQuote, PriceQuoteValue, PriceQuery,} from '@agoric/zoe/tools/types.js';
14
- * @import {Ratio} from '@agoric/ertp';
15
- */
16
-
17
- /**
18
- * @typedef {{
19
- * overage: Amount<'nat'>;
20
- * shortfallToReserve: Amount<'nat'>;
21
- * collateralForReserve: Amount<'nat'>;
22
- * actualCollateralSold: Amount<'nat'>;
23
- * collateralSold: Amount<'nat'>;
24
- * collatRemaining: Amount<'nat'>;
25
- * debtToBurn: Amount<'nat'>;
26
- * mintedForReserve: Amount<'nat'>;
27
- * mintedProceeds: Amount<'nat'>;
28
- * phantomDebt: Amount<'nat'>;
29
- * totalPenalty: Amount<'nat'>;
30
- * transfersToVault: [number, AmountKeywordRecord][];
31
- * vaultsToReinstate: number[];
32
- * }} DistributionPlan
33
- * The plan to execute for distributing proceeds of a liquidation.
34
- *
35
- * Vaults are referenced by index in the list sent to the calculator.
36
- */
37
-
38
- /**
39
- * @typedef {{
40
- * collateral: Amount<'nat'>;
41
- * presaleDebt: Amount<'nat'>;
42
- * currentDebt: Amount<'nat'>;
43
- * }} VaultBalances
44
- */
45
-
46
- /**
47
- * Liquidation.md describes how to process liquidation proceeds.
48
- *
49
- * This function is complex and may fail. To defend against this possibility, it
50
- * starts with a base plan and updates it. The updating is the complex portion
51
- * and is wrapped in a try/catch. If at any point the plan revising fails, the
52
- * plan is returned as is.
53
- *
54
- * @param {object} inputs
55
- * @param {AmountKeywordRecord} inputs.proceeds
56
- * @param {Amount<'nat'>} inputs.totalDebt
57
- * @param {Amount<'nat'>} inputs.totalCollateral
58
- * @param {PriceDescription} inputs.oraclePriceAtStart
59
- * @param {VaultBalances[]} inputs.vaultsBalances ordered best to worst
60
- * collateralized
61
- * @param {Ratio} inputs.penaltyRate
62
- * @returns {DistributionPlan}
63
- */
64
- export const calculateDistributionPlan = ({
65
- proceeds,
66
- totalDebt,
67
- totalCollateral,
68
- oraclePriceAtStart,
69
- vaultsBalances,
70
- penaltyRate,
71
- }) => {
72
- const emptyCollateral = AmountMath.makeEmptyFromAmount(totalCollateral);
73
- const emptyMinted = AmountMath.makeEmptyFromAmount(totalDebt);
74
-
75
- const { Collateral: collateralProceeds } = proceeds;
76
- /** @type {Amount<'nat'>} */
77
- const collateralSold = AmountMath.subtract(
78
- totalCollateral,
79
- collateralProceeds,
80
- );
81
-
82
- const mintedProceeds = proceeds.Minted || emptyMinted;
83
- const accounting = liquidationResults(totalDebt, mintedProceeds);
84
-
85
- // charged in collateral
86
- const totalPenalty = ceilMultiplyBy(
87
- totalDebt,
88
- multiplyRatios(penaltyRate, quoteAsRatio(oraclePriceAtStart)),
89
- );
90
-
91
- const debtPortion = makeRatioFromAmounts(totalPenalty, totalDebt);
92
-
93
- // We mutate the plan so that at any point if there's an error the plan can be returned and executed.
94
- // IOW the plan must always be in a valid state, though perhaps incomplete.
95
- /** @type {DistributionPlan} */
96
- const plan = {
97
- overage: accounting.overage,
98
- shortfallToReserve: accounting.shortfall,
99
- collateralForReserve: emptyCollateral,
100
- actualCollateralSold: emptyCollateral,
101
- collateralSold,
102
- collatRemaining: emptyCollateral,
103
- debtToBurn: emptyMinted,
104
- mintedForReserve: emptyMinted,
105
- mintedProceeds,
106
- phantomDebt: emptyMinted,
107
- transfersToVault: [],
108
- vaultsToReinstate: [],
109
- totalPenalty,
110
- };
111
-
112
- /**
113
- * If interest was charged between liquidating and liquidated, erase it.
114
- *
115
- * @param {VaultBalances} balances
116
- */
117
- const updatePhantomDebt = ({ presaleDebt, currentDebt }) => {
118
- if (AmountMath.isEqual(presaleDebt, currentDebt)) {
119
- return;
120
- }
121
- const accrued = AmountMath.subtract(currentDebt, presaleDebt);
122
- plan.phantomDebt = AmountMath.add(plan.phantomDebt, accrued);
123
- };
124
-
125
- const runFlow1 = () => {
126
- // Flow #1: no shortfall
127
-
128
- const distributableCollateral = subtractToEmpty(
129
- collateralProceeds,
130
- totalPenalty,
131
- );
132
-
133
- plan.debtToBurn = totalDebt;
134
- plan.mintedForReserve = accounting.overage;
135
-
136
- // return remaining funds to vaults before closing
137
-
138
- let leftToStage = distributableCollateral;
139
- const price = makeRatioFromAmounts(mintedProceeds, collateralSold);
140
-
141
- // iterate from best to worst, returning collateral until it has
142
- // been exhausted. Vaults after that get nothing.
143
- for (const [vaultIndex, balances] of vaultsBalances.entries()) {
144
- const { collateral: vCollat, presaleDebt } = balances;
145
- updatePhantomDebt(balances);
146
-
147
- // max return is vault value reduced by debt and penalty value
148
- const debtCollat = ceilDivideBy(presaleDebt, price);
149
- const penaltyCollat = ceilMultiplyBy(presaleDebt, debtPortion);
150
- const lessCollat = AmountMath.add(debtCollat, penaltyCollat);
151
-
152
- const maxCollat = subtractToEmpty(vCollat, lessCollat);
153
- if (!AmountMath.isEmpty(leftToStage)) {
154
- const collatReturn = AmountMath.min(leftToStage, maxCollat);
155
- leftToStage = AmountMath.subtract(leftToStage, collatReturn);
156
- plan.transfersToVault.push([vaultIndex, { Collateral: collatReturn }]);
157
- }
158
- }
159
-
160
- const hasCollateralToDistribute = !AmountMath.isEmpty(
161
- distributableCollateral,
162
- );
163
- plan.collateralForReserve = hasCollateralToDistribute
164
- ? AmountMath.add(leftToStage, totalPenalty)
165
- : collateralProceeds;
166
- };
167
-
168
- // Flow 2a: all collateral was sold but debt was not covered
169
- const runFlow2a = () => {
170
- // charge penalty if proceeds are sufficient
171
- const penaltyInMinted = ceilMultiplyBy(totalDebt, penaltyRate);
172
- const recoveredDebt = AmountMath.min(
173
- AmountMath.add(totalDebt, penaltyInMinted),
174
- mintedProceeds,
175
- );
176
-
177
- plan.debtToBurn = recoveredDebt;
178
-
179
- const distributable = subtractToEmpty(mintedProceeds, recoveredDebt);
180
- let mintedRemaining = distributable;
181
-
182
- const vaultPortion = makeRatioFromAmounts(distributable, totalCollateral);
183
-
184
- // iterate from best to worst returning remaining funds to vaults
185
- for (const [vaultIndex, balances] of vaultsBalances.entries()) {
186
- // from best to worst, return minted above penalty if any remains
187
- const vaultShare = floorMultiplyBy(balances.collateral, vaultPortion);
188
- updatePhantomDebt(balances);
189
-
190
- if (!AmountMath.isEmpty(mintedRemaining)) {
191
- const mintedToReturn = AmountMath.min(mintedRemaining, vaultShare);
192
- mintedRemaining = AmountMath.subtract(mintedRemaining, mintedToReturn);
193
- plan.transfersToVault.push([vaultIndex, { Minted: mintedToReturn }]);
194
- }
195
- }
196
- };
197
-
198
- // Flow 2b: collateral remains but debt was not covered
199
- const runFlow2b = () => {
200
- plan.debtToBurn = totalDebt;
201
- plan.mintedForReserve = accounting.overage;
202
-
203
- // reconstitute vaults until collateral is insufficient
204
- let reconstituteVaults = AmountMath.isGTE(collateralProceeds, totalPenalty);
205
-
206
- // charge penalty if proceeds are sufficient
207
- const distributableCollateral = subtractToEmpty(
208
- collateralProceeds,
209
- totalPenalty,
210
- );
211
-
212
- plan.collatRemaining = distributableCollateral;
213
-
214
- let shortfallToReserve = accounting.shortfall;
215
- const reduceCollateral = amount =>
216
- (plan.actualCollateralSold = AmountMath.add(
217
- plan.actualCollateralSold,
218
- amount,
219
- ));
220
-
221
- // iterate from best to worst attempting to reconstitute, by
222
- // returning remaining funds to vaults
223
- for (const [vaultIndex, balances] of vaultsBalances.entries()) {
224
- const { collateral: vCollat, presaleDebt } = balances;
225
-
226
- // according to #7123, Collateral for penalty =
227
- // total liquidation penalty * vault debt / total debt
228
- const vaultPenalty = ceilMultiplyBy(presaleDebt, debtPortion);
229
- const collatPostPenalty = subtractToEmpty(vCollat, vaultPenalty);
230
- const vaultDebt = floorMultiplyBy(presaleDebt, debtPortion);
231
-
232
- // Should we continue reconstituting vaults?
233
- reconstituteVaults =
234
- reconstituteVaults &&
235
- !AmountMath.isEmpty(collatPostPenalty) &&
236
- AmountMath.isGTE(plan.collatRemaining, collatPostPenalty) &&
237
- AmountMath.isGTE(totalDebt, presaleDebt);
238
-
239
- if (reconstituteVaults) {
240
- plan.collatRemaining = AmountMath.subtract(
241
- plan.collatRemaining,
242
- collatPostPenalty,
243
- );
244
- shortfallToReserve = subtractToEmpty(shortfallToReserve, presaleDebt);
245
- // must reinstate after atomicRearrange(), so we record them.
246
- plan.vaultsToReinstate.push(vaultIndex);
247
- reduceCollateral(vaultDebt);
248
- plan.transfersToVault.push([
249
- vaultIndex,
250
- { Collateral: collatPostPenalty },
251
- ]);
252
- } else {
253
- updatePhantomDebt(balances);
254
-
255
- reduceCollateral(vCollat);
256
- }
257
- }
258
-
259
- plan.collateralForReserve = AmountMath.add(
260
- plan.collatRemaining,
261
- totalPenalty,
262
- );
263
-
264
- plan.shortfallToReserve = shortfallToReserve;
265
- };
266
-
267
- try {
268
- if (AmountMath.isEmpty(accounting.shortfall)) {
269
- // Flow #1: no shortfall
270
- runFlow1();
271
- } else if (AmountMath.isEmpty(collateralProceeds)) {
272
- // Flow 2a: all collateral was sold but debt was not covered
273
- runFlow2a();
274
- } else {
275
- // Flow #2b: There's unsold collateral; some vaults may be reinstated.
276
- runFlow2b();
277
- }
278
- } catch (err) {
279
- console.error('🚨 Failure running distribution flow:', err);
280
- // continue to return `plan` in the state that was reached
281
- }
282
-
283
- return harden(plan);
284
- };
285
- harden(calculateDistributionPlan);