@agoric/inter-protocol 0.14.1-dev-effbf7f.0 → 0.14.1-dev-72b0aa9.0
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.
- package/bundles/bundle-auctioneer-js-meta.json +116 -116
- package/bundles/bundle-auctioneer.js +1 -1
- package/bundles/bundle-econCommitteeCharter-js-meta.json +71 -71
- package/bundles/bundle-feeDistributor-js-meta.json +57 -57
- package/bundles/bundle-fluxAggregatorKit-js-meta.json +111 -111
- package/bundles/bundle-psm-js-meta.json +109 -109
- package/bundles/bundle-psm.js +1 -1
- package/bundles/bundle-reserve-js-meta.json +108 -108
- package/bundles/bundle-scaledPriceAuthority-js-meta.json +78 -78
- package/bundles/bundle-vaultFactory-js-meta.json +136 -132
- package/bundles/bundle-vaultFactory.js +1 -1
- package/package.json +17 -17
- package/src/contractSupport.js +16 -0
- package/src/vaultFactory/Liquidation.md +1 -1
- package/src/vaultFactory/liquidation.js +8 -23
- package/src/vaultFactory/proceeds.js +274 -0
- package/src/vaultFactory/vaultDirector.js +1 -1
- package/src/vaultFactory/vaultFactory.js +3 -3
- package/src/vaultFactory/vaultManager.js +163 -340
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/inter-protocol",
|
|
3
|
-
"version": "0.14.1-dev-
|
|
3
|
+
"version": "0.14.1-dev-72b0aa9.0+72b0aa9",
|
|
4
4
|
"description": "Core cryptoeconomy contracts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -29,29 +29,29 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@agoric/assert": "0.6.1-dev-
|
|
33
|
-
"@agoric/ertp": "0.16.1-dev-
|
|
34
|
-
"@agoric/governance": "0.10.1-dev-
|
|
35
|
-
"@agoric/internal": "0.3.1-dev-
|
|
36
|
-
"@agoric/notifier": "0.6.1-dev-
|
|
37
|
-
"@agoric/store": "0.9.1-dev-
|
|
38
|
-
"@agoric/time": "0.3.1-dev-
|
|
39
|
-
"@agoric/vat-data": "0.5.1-dev-
|
|
40
|
-
"@agoric/vats": "0.14.1-dev-
|
|
41
|
-
"@agoric/zoe": "0.26.1-dev-
|
|
32
|
+
"@agoric/assert": "0.6.1-dev-72b0aa9.0+72b0aa9",
|
|
33
|
+
"@agoric/ertp": "0.16.1-dev-72b0aa9.0+72b0aa9",
|
|
34
|
+
"@agoric/governance": "0.10.1-dev-72b0aa9.0+72b0aa9",
|
|
35
|
+
"@agoric/internal": "0.3.1-dev-72b0aa9.0+72b0aa9",
|
|
36
|
+
"@agoric/notifier": "0.6.1-dev-72b0aa9.0+72b0aa9",
|
|
37
|
+
"@agoric/store": "0.9.1-dev-72b0aa9.0+72b0aa9",
|
|
38
|
+
"@agoric/time": "0.3.1-dev-72b0aa9.0+72b0aa9",
|
|
39
|
+
"@agoric/vat-data": "0.5.1-dev-72b0aa9.0+72b0aa9",
|
|
40
|
+
"@agoric/vats": "0.14.1-dev-72b0aa9.0+72b0aa9",
|
|
41
|
+
"@agoric/zoe": "0.26.1-dev-72b0aa9.0+72b0aa9",
|
|
42
42
|
"@endo/captp": "^3.1.1",
|
|
43
43
|
"@endo/eventual-send": "^0.17.2",
|
|
44
44
|
"@endo/far": "^0.2.18",
|
|
45
45
|
"@endo/marshal": "^0.8.5",
|
|
46
46
|
"@endo/nat": "^4.1.27",
|
|
47
|
-
"agoric": "0.19.1-dev-
|
|
47
|
+
"agoric": "0.19.1-dev-72b0aa9.0+72b0aa9",
|
|
48
48
|
"jessie.js": "^0.3.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@agoric/deploy-script-support": "0.10.1-dev-
|
|
52
|
-
"@agoric/smart-wallet": "0.5.1-dev-
|
|
53
|
-
"@agoric/swingset-liveslots": "0.10.1-dev-
|
|
54
|
-
"@agoric/swingset-vat": "0.32.1-dev-
|
|
51
|
+
"@agoric/deploy-script-support": "0.10.1-dev-72b0aa9.0+72b0aa9",
|
|
52
|
+
"@agoric/smart-wallet": "0.5.1-dev-72b0aa9.0+72b0aa9",
|
|
53
|
+
"@agoric/swingset-liveslots": "0.10.1-dev-72b0aa9.0+72b0aa9",
|
|
54
|
+
"@agoric/swingset-vat": "0.32.1-dev-72b0aa9.0+72b0aa9",
|
|
55
55
|
"@endo/bundle-source": "^2.5.1",
|
|
56
56
|
"@endo/init": "^0.5.56",
|
|
57
57
|
"@endo/promise-kit": "^0.2.56",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"publishConfig": {
|
|
80
80
|
"access": "public"
|
|
81
81
|
},
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "72b0aa9ce03797b76d8d671b010d1a1615b5ae45"
|
|
83
83
|
}
|
package/src/contractSupport.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { AmountMath } from '@agoric/ertp';
|
|
4
4
|
import { M } from '@agoric/store';
|
|
5
|
+
import { makeRatioFromAmounts } from '@agoric/zoe/src/contractSupport/index.js';
|
|
5
6
|
|
|
6
7
|
const { Fail, quote: q } = assert;
|
|
7
8
|
|
|
@@ -27,6 +28,17 @@ export const ratioPattern = harden({
|
|
|
27
28
|
export const addSubtract = (base, gain, loss) =>
|
|
28
29
|
AmountMath.subtract(AmountMath.add(base, gain), loss);
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @template {Amount} T
|
|
33
|
+
* @param {T} left
|
|
34
|
+
* @param {T} right
|
|
35
|
+
* @returns {T}
|
|
36
|
+
*/
|
|
37
|
+
export const subtractToEmpty = (left, right) =>
|
|
38
|
+
AmountMath.isGTE(right, left)
|
|
39
|
+
? /** @type {T} */ (AmountMath.makeEmptyFromAmount(left))
|
|
40
|
+
: /** @type {T} */ (AmountMath.subtract(left, right));
|
|
41
|
+
|
|
30
42
|
/**
|
|
31
43
|
* Verifies that every key in the proposal is in the provided list
|
|
32
44
|
*
|
|
@@ -83,3 +95,7 @@ export const checkDebtLimit = (debtLimit, totalDebt, toMint) => {
|
|
|
83
95
|
*/
|
|
84
96
|
export const makeNatAmountShape = (brand, min) =>
|
|
85
97
|
harden({ brand, value: min ? M.gte(min) : M.nat() });
|
|
98
|
+
|
|
99
|
+
/** @param {Pick<PriceDescription, 'amountIn' | 'amountOut'>} quoteAmount */
|
|
100
|
+
export const quoteAsRatio = quoteAmount =>
|
|
101
|
+
makeRatioFromAmounts(quoteAmount.amountIn, quoteAmount.amountOut);
|
|
@@ -13,7 +13,7 @@ There are two interesting exception cases. All the collateral might be sold
|
|
|
13
13
|
without covering the debt (flow 2a), or there might not be enough bids to
|
|
14
14
|
purchase all the collateral (flow 2b). In the first case, vault-holders don't
|
|
15
15
|
get anything back, we burn the currency we received to cover a portion of the
|
|
16
|
-
debt,
|
|
16
|
+
debt, and we report a shortfall.
|
|
17
17
|
|
|
18
18
|
In the second case, we can't tell whether it's due to a downturn in the
|
|
19
19
|
market or a DoS attack on the exchange. In hopes that it's not the latter, we
|
|
@@ -144,7 +144,7 @@ const setWakeups = ({
|
|
|
144
144
|
* @param {TimerWaker} reschedulerWaker
|
|
145
145
|
* @returns {Promise<void>}
|
|
146
146
|
*/
|
|
147
|
-
const setWakeupsForNextAuction = async (
|
|
147
|
+
export const setWakeupsForNextAuction = async (
|
|
148
148
|
auctioneerPublicFacet,
|
|
149
149
|
timer,
|
|
150
150
|
priceLockWaker,
|
|
@@ -180,35 +180,25 @@ const setWakeupsForNextAuction = async (
|
|
|
180
180
|
params,
|
|
181
181
|
});
|
|
182
182
|
};
|
|
183
|
+
harden(setWakeupsForNextAuction);
|
|
183
184
|
|
|
184
185
|
/**
|
|
185
186
|
* @param {Amount<'nat'>} debt
|
|
186
187
|
* @param {Amount<'nat'>} minted
|
|
187
|
-
* @returns {{ overage: Amount<'nat'>, shortfall: Amount<'nat'
|
|
188
|
+
* @returns {{ overage: Amount<'nat'>, shortfall: Amount<'nat'>}}
|
|
188
189
|
*/
|
|
189
|
-
const liquidationResults = (debt, minted) => {
|
|
190
|
+
export const liquidationResults = (debt, minted) => {
|
|
190
191
|
if (AmountMath.isEmpty(minted)) {
|
|
191
|
-
return {
|
|
192
|
-
overage: minted,
|
|
193
|
-
toBurn: minted,
|
|
194
|
-
shortfall: debt,
|
|
195
|
-
};
|
|
192
|
+
return { overage: minted, shortfall: debt };
|
|
196
193
|
}
|
|
197
194
|
|
|
198
195
|
const [overage, shortfall] = AmountMath.isGTE(debt, minted)
|
|
199
196
|
? [AmountMath.makeEmptyFromAmount(debt), AmountMath.subtract(debt, minted)]
|
|
200
197
|
: [AmountMath.subtract(minted, debt), AmountMath.makeEmptyFromAmount(debt)];
|
|
201
198
|
|
|
202
|
-
|
|
203
|
-
// debt is fully accounted for, with toBurn and shortfall
|
|
204
|
-
assert(AmountMath.isEqual(debt, AmountMath.add(toBurn, shortfall)));
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
overage,
|
|
208
|
-
toBurn,
|
|
209
|
-
shortfall,
|
|
210
|
-
};
|
|
199
|
+
return { overage, shortfall };
|
|
211
200
|
};
|
|
201
|
+
harden(liquidationResults);
|
|
212
202
|
|
|
213
203
|
/**
|
|
214
204
|
* Watch governed params for change
|
|
@@ -260,7 +250,7 @@ export const watchForGovernanceChange = (
|
|
|
260
250
|
* totalCollateral: Amount<'nat'>,
|
|
261
251
|
* liqSeat: ZCFSeat}}
|
|
262
252
|
*/
|
|
263
|
-
const getLiquidatableVaults = (
|
|
253
|
+
export const getLiquidatableVaults = (
|
|
264
254
|
zcf,
|
|
265
255
|
collateralizationDetails,
|
|
266
256
|
prioritizedVaults,
|
|
@@ -303,9 +293,4 @@ const getLiquidatableVaults = (
|
|
|
303
293
|
|
|
304
294
|
return { vaultData, totalDebt, totalCollateral, liqSeat };
|
|
305
295
|
};
|
|
306
|
-
|
|
307
|
-
harden(setWakeupsForNextAuction);
|
|
308
|
-
harden(liquidationResults);
|
|
309
296
|
harden(getLiquidatableVaults);
|
|
310
|
-
|
|
311
|
-
export { setWakeupsForNextAuction, liquidationResults, getLiquidatableVaults };
|
|
@@ -0,0 +1,274 @@
|
|
|
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
|
+
* @typedef {{
|
|
14
|
+
* overage: Amount<'nat'>,
|
|
15
|
+
* shortfallToReserve: Amount<'nat'>,
|
|
16
|
+
* collateralForReserve: Amount<'nat'>,
|
|
17
|
+
* actualCollateralSold: Amount<'nat'>,
|
|
18
|
+
* collateralSold: Amount<'nat'>,
|
|
19
|
+
* collatRemaining: Amount<'nat'>,
|
|
20
|
+
* debtToBurn: Amount<'nat'>,
|
|
21
|
+
* mintedForReserve: Amount<'nat'>,
|
|
22
|
+
* mintedProceeds: Amount<'nat'>,
|
|
23
|
+
* phantomDebt: Amount<'nat'>,
|
|
24
|
+
* totalPenalty: Amount<'nat'>,
|
|
25
|
+
* transfersToVault: Array<[number, AmountKeywordRecord]>,
|
|
26
|
+
* vaultsToReinstate: Array<number>
|
|
27
|
+
* }} DistributionPlan
|
|
28
|
+
*
|
|
29
|
+
* The plan to execute for distributing proceeds of a liquidation.
|
|
30
|
+
*
|
|
31
|
+
* Vaults are referenced by index in the list sent to the calculator.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** @typedef {{ collateral: Amount<'nat'>, presaleDebt: Amount<'nat'>, currentDebt: Amount<'nat'> }} VaultBalances */
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Liquidation.md describes how to process liquidation proceeds.
|
|
38
|
+
*
|
|
39
|
+
* This function is complex and may fail. To defend against this possibility, it
|
|
40
|
+
* starts with a base plan and updates it. The updating is the complex portion
|
|
41
|
+
* and is wrapped in a try/catch. If at any point the plan revising fails, the
|
|
42
|
+
* plan is returned as is.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} inputs
|
|
45
|
+
* @param {AmountKeywordRecord} inputs.proceeds
|
|
46
|
+
* @param {Amount<'nat'>} inputs.totalDebt
|
|
47
|
+
* @param {Amount<'nat'>} inputs.totalCollateral
|
|
48
|
+
* @param {PriceDescription} inputs.oraclePriceAtStart
|
|
49
|
+
* @param {Array<VaultBalances>} inputs.vaultsBalances ordered best to worst collateralized
|
|
50
|
+
* @param {Ratio} inputs.penaltyRate
|
|
51
|
+
* @returns {DistributionPlan}
|
|
52
|
+
*/
|
|
53
|
+
export const calculateDistributionPlan = ({
|
|
54
|
+
proceeds,
|
|
55
|
+
totalDebt,
|
|
56
|
+
totalCollateral,
|
|
57
|
+
oraclePriceAtStart,
|
|
58
|
+
vaultsBalances,
|
|
59
|
+
penaltyRate,
|
|
60
|
+
}) => {
|
|
61
|
+
const emptyCollateral = AmountMath.makeEmptyFromAmount(totalCollateral);
|
|
62
|
+
const emptyMinted = AmountMath.makeEmptyFromAmount(totalDebt);
|
|
63
|
+
|
|
64
|
+
const { Collateral: collateralProceeds } = proceeds;
|
|
65
|
+
/** @type {Amount<'nat'>} */
|
|
66
|
+
const collateralSold = AmountMath.subtract(
|
|
67
|
+
totalCollateral,
|
|
68
|
+
collateralProceeds,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const mintedProceeds = proceeds.Minted || emptyMinted;
|
|
72
|
+
const accounting = liquidationResults(totalDebt, mintedProceeds);
|
|
73
|
+
|
|
74
|
+
// charged in collateral
|
|
75
|
+
const totalPenalty = ceilMultiplyBy(
|
|
76
|
+
totalDebt,
|
|
77
|
+
multiplyRatios(penaltyRate, quoteAsRatio(oraclePriceAtStart)),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const debtPortion = makeRatioFromAmounts(totalPenalty, totalDebt);
|
|
81
|
+
|
|
82
|
+
// We mutate the plan so that at any point if there's an error the plan can be returned and executed.
|
|
83
|
+
// IOW the plan must always be in a valid state, though perhaps incomplete.
|
|
84
|
+
/** @type {DistributionPlan} */
|
|
85
|
+
const plan = {
|
|
86
|
+
overage: accounting.overage,
|
|
87
|
+
shortfallToReserve: accounting.shortfall,
|
|
88
|
+
collateralForReserve: emptyCollateral,
|
|
89
|
+
actualCollateralSold: emptyCollateral,
|
|
90
|
+
collateralSold,
|
|
91
|
+
collatRemaining: emptyCollateral,
|
|
92
|
+
debtToBurn: emptyMinted,
|
|
93
|
+
mintedForReserve: emptyMinted,
|
|
94
|
+
mintedProceeds,
|
|
95
|
+
phantomDebt: emptyMinted,
|
|
96
|
+
transfersToVault: [],
|
|
97
|
+
vaultsToReinstate: [],
|
|
98
|
+
totalPenalty,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* If interest was charged between liquidating and liquidated, erase it.
|
|
103
|
+
*
|
|
104
|
+
* @param {VaultBalances} balances
|
|
105
|
+
*/
|
|
106
|
+
const updatePhantomDebt = ({ presaleDebt, currentDebt }) => {
|
|
107
|
+
if (AmountMath.isEqual(presaleDebt, currentDebt)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const accrued = AmountMath.subtract(currentDebt, presaleDebt);
|
|
111
|
+
plan.phantomDebt = AmountMath.add(plan.phantomDebt, accrued);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const runFlow1 = () => {
|
|
115
|
+
// Flow #1: no shortfall
|
|
116
|
+
|
|
117
|
+
const distributableCollateral = subtractToEmpty(
|
|
118
|
+
collateralProceeds,
|
|
119
|
+
totalPenalty,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
plan.debtToBurn = totalDebt;
|
|
123
|
+
plan.mintedForReserve = accounting.overage;
|
|
124
|
+
|
|
125
|
+
// return remaining funds to vaults before closing
|
|
126
|
+
|
|
127
|
+
let leftToStage = distributableCollateral;
|
|
128
|
+
const price = makeRatioFromAmounts(mintedProceeds, collateralSold);
|
|
129
|
+
|
|
130
|
+
// iterate from best to worst, returning collateral until it has
|
|
131
|
+
// been exhausted. Vaults after that get nothing.
|
|
132
|
+
for (const [vaultIndex, balances] of vaultsBalances.entries()) {
|
|
133
|
+
const { collateral: vCollat, presaleDebt } = balances;
|
|
134
|
+
updatePhantomDebt(balances);
|
|
135
|
+
|
|
136
|
+
// max return is vault value reduced by debt and penalty value
|
|
137
|
+
const debtCollat = ceilDivideBy(presaleDebt, price);
|
|
138
|
+
const penaltyCollat = ceilMultiplyBy(presaleDebt, debtPortion);
|
|
139
|
+
const lessCollat = AmountMath.add(debtCollat, penaltyCollat);
|
|
140
|
+
|
|
141
|
+
const maxCollat = subtractToEmpty(vCollat, lessCollat);
|
|
142
|
+
if (!AmountMath.isEmpty(leftToStage)) {
|
|
143
|
+
const collatReturn = AmountMath.min(leftToStage, maxCollat);
|
|
144
|
+
leftToStage = AmountMath.subtract(leftToStage, collatReturn);
|
|
145
|
+
plan.transfersToVault.push([vaultIndex, { Collateral: collatReturn }]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const hasCollateralToDistribute = !AmountMath.isEmpty(
|
|
150
|
+
distributableCollateral,
|
|
151
|
+
);
|
|
152
|
+
plan.collateralForReserve = hasCollateralToDistribute
|
|
153
|
+
? AmountMath.add(leftToStage, totalPenalty)
|
|
154
|
+
: collateralProceeds;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Flow 2a: all collateral was sold but debt was not covered
|
|
158
|
+
const runFlow2a = () => {
|
|
159
|
+
// charge penalty if proceeds are sufficient
|
|
160
|
+
const penaltyInMinted = ceilMultiplyBy(totalDebt, penaltyRate);
|
|
161
|
+
const recoveredDebt = AmountMath.min(
|
|
162
|
+
AmountMath.add(totalDebt, penaltyInMinted),
|
|
163
|
+
mintedProceeds,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
plan.debtToBurn = recoveredDebt;
|
|
167
|
+
|
|
168
|
+
const distributable = subtractToEmpty(mintedProceeds, recoveredDebt);
|
|
169
|
+
let mintedRemaining = distributable;
|
|
170
|
+
|
|
171
|
+
const vaultPortion = makeRatioFromAmounts(distributable, totalCollateral);
|
|
172
|
+
|
|
173
|
+
// iterate from best to worst returning remaining funds to vaults
|
|
174
|
+
for (const [vaultIndex, balances] of vaultsBalances.entries()) {
|
|
175
|
+
// from best to worst, return minted above penalty if any remains
|
|
176
|
+
const vaultShare = floorMultiplyBy(balances.collateral, vaultPortion);
|
|
177
|
+
updatePhantomDebt(balances);
|
|
178
|
+
|
|
179
|
+
if (!AmountMath.isEmpty(mintedRemaining)) {
|
|
180
|
+
const mintedToReturn = AmountMath.min(mintedRemaining, vaultShare);
|
|
181
|
+
mintedRemaining = AmountMath.subtract(mintedRemaining, mintedToReturn);
|
|
182
|
+
plan.transfersToVault.push([vaultIndex, { Minted: mintedToReturn }]);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Flow 2b: collateral remains but debt was not covered
|
|
188
|
+
const runFlow2b = () => {
|
|
189
|
+
plan.debtToBurn = totalDebt;
|
|
190
|
+
plan.mintedForReserve = accounting.overage;
|
|
191
|
+
|
|
192
|
+
// reconstitute vaults until collateral is insufficient
|
|
193
|
+
let reconstituteVaults = AmountMath.isGTE(collateralProceeds, totalPenalty);
|
|
194
|
+
|
|
195
|
+
// charge penalty if proceeds are sufficient
|
|
196
|
+
const distributableCollateral = subtractToEmpty(
|
|
197
|
+
collateralProceeds,
|
|
198
|
+
totalPenalty,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
plan.collatRemaining = distributableCollateral;
|
|
202
|
+
|
|
203
|
+
let shortfallToReserve = accounting.shortfall;
|
|
204
|
+
const reduceCollateral = amount =>
|
|
205
|
+
(plan.actualCollateralSold = AmountMath.add(
|
|
206
|
+
plan.actualCollateralSold,
|
|
207
|
+
amount,
|
|
208
|
+
));
|
|
209
|
+
|
|
210
|
+
// iterate from best to worst attempting to reconstitute, by
|
|
211
|
+
// returning remaining funds to vaults
|
|
212
|
+
for (const [vaultIndex, balances] of vaultsBalances.entries()) {
|
|
213
|
+
const { collateral: vCollat, presaleDebt } = balances;
|
|
214
|
+
|
|
215
|
+
// according to #7123, Collateral for penalty =
|
|
216
|
+
// total liquidation penalty * vault debt / total debt
|
|
217
|
+
const vaultPenalty = ceilMultiplyBy(presaleDebt, debtPortion);
|
|
218
|
+
const collatPostPenalty = subtractToEmpty(vCollat, vaultPenalty);
|
|
219
|
+
const vaultDebt = floorMultiplyBy(presaleDebt, debtPortion);
|
|
220
|
+
|
|
221
|
+
// Should we continue reconstituting vaults?
|
|
222
|
+
reconstituteVaults =
|
|
223
|
+
reconstituteVaults &&
|
|
224
|
+
!AmountMath.isEmpty(collatPostPenalty) &&
|
|
225
|
+
AmountMath.isGTE(plan.collatRemaining, collatPostPenalty) &&
|
|
226
|
+
AmountMath.isGTE(totalDebt, presaleDebt);
|
|
227
|
+
|
|
228
|
+
if (reconstituteVaults) {
|
|
229
|
+
plan.collatRemaining = AmountMath.subtract(
|
|
230
|
+
plan.collatRemaining,
|
|
231
|
+
collatPostPenalty,
|
|
232
|
+
);
|
|
233
|
+
shortfallToReserve = subtractToEmpty(shortfallToReserve, presaleDebt);
|
|
234
|
+
// must reinstate after atomicRearrange(), so we record them.
|
|
235
|
+
plan.vaultsToReinstate.push(vaultIndex);
|
|
236
|
+
reduceCollateral(vaultDebt);
|
|
237
|
+
plan.transfersToVault.push([
|
|
238
|
+
vaultIndex,
|
|
239
|
+
{ Collateral: collatPostPenalty },
|
|
240
|
+
]);
|
|
241
|
+
} else {
|
|
242
|
+
updatePhantomDebt(balances);
|
|
243
|
+
|
|
244
|
+
reduceCollateral(vCollat);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
plan.collateralForReserve = AmountMath.add(
|
|
249
|
+
plan.collatRemaining,
|
|
250
|
+
totalPenalty,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
plan.shortfallToReserve = shortfallToReserve;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
if (AmountMath.isEmpty(accounting.shortfall)) {
|
|
258
|
+
// Flow #1: no shortfall
|
|
259
|
+
runFlow1();
|
|
260
|
+
} else if (AmountMath.isEmpty(collateralProceeds)) {
|
|
261
|
+
// Flow 2a: all collateral was sold but debt was not covered
|
|
262
|
+
runFlow2a();
|
|
263
|
+
} else {
|
|
264
|
+
// Flow #2b: There's unsold collateral; some vaults may be reinstated.
|
|
265
|
+
runFlow2b();
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
console.error('🚨 Failure running distribution flow:', err);
|
|
269
|
+
// continue to return `plan` in the state that was reached
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return harden(plan);
|
|
273
|
+
};
|
|
274
|
+
harden(calculateDistributionPlan);
|
|
@@ -84,7 +84,7 @@ const shortfallInvitationKey = 'shortfallInvitation';
|
|
|
84
84
|
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorderKit} makeRecorderKit
|
|
85
85
|
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeERecorderKit} makeERecorderKit
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
const prepareVaultDirector = (
|
|
88
88
|
baggage,
|
|
89
89
|
zcf,
|
|
90
90
|
directorParamManager,
|
|
@@ -13,9 +13,9 @@ import '@agoric/zoe/src/contracts/exported.js';
|
|
|
13
13
|
|
|
14
14
|
// This contract wants to be managed by a contractGovernor, but it isn't
|
|
15
15
|
// compatible with contractGovernor, since it has a separate paramManager for
|
|
16
|
-
// each
|
|
17
|
-
// to satisfy contractGovernor. It needs to return a creatorFacet
|
|
18
|
-
// { getParamMgrRetriever, getInvitation, getLimitedCreatorFacet }.
|
|
16
|
+
// each VaultManager. This requires it to manually replicate the API of
|
|
17
|
+
// contractHelper to satisfy contractGovernor. It needs to return a creatorFacet
|
|
18
|
+
// with { getParamMgrRetriever, getInvitation, getLimitedCreatorFacet }.
|
|
19
19
|
|
|
20
20
|
import { CONTRACT_ELECTORATE } from '@agoric/governance';
|
|
21
21
|
import { makeParamManagerFromTerms } from '@agoric/governance/src/contractGovernance/typedParamManager.js';
|