@agoric/inter-protocol 0.14.1-dev-13c169c.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-fluxAggregatorKit.js +1 -1
- package/bundles/bundle-psm-js-meta.json +145 -145
- package/bundles/bundle-psm.js +1 -1
- package/bundles/bundle-reserve-js-meta.json +108 -108
- package/bundles/bundle-reserve.js +1 -1
- 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 -45
- package/src/price/fluxAggregatorKit.js +1 -15
- package/src/proposals/econ-behaviors.js +4 -4
- package/src/reserve/assetReserveKit.js +1 -7
- package/src/reserve/doc.md +4 -4
- package/src/vaultFactory/Liquidation.md +1 -1
- package/src/vaultFactory/liquidation.js +8 -23
- package/src/vaultFactory/proceeds.js +274 -0
- package/src/vaultFactory/types.js +1 -10
- package/src/vaultFactory/vaultDirector.js +3 -42
- package/src/vaultFactory/vaultFactory.js +3 -3
- package/src/vaultFactory/vaultHolder.js +1 -9
- package/src/vaultFactory/vaultManager.js +163 -350
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
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// @jessie-check
|
|
2
2
|
|
|
3
3
|
import { AmountMath } from '@agoric/ertp';
|
|
4
|
-
import { makeStoredPublisherKit, makeStoredPublishKit } from '@agoric/notifier';
|
|
5
4
|
import { M } from '@agoric/store';
|
|
6
|
-
import {
|
|
5
|
+
import { makeRatioFromAmounts } from '@agoric/zoe/src/contractSupport/index.js';
|
|
7
6
|
|
|
8
7
|
const { Fail, quote: q } = assert;
|
|
9
8
|
|
|
@@ -29,6 +28,17 @@ export const ratioPattern = harden({
|
|
|
29
28
|
export const addSubtract = (base, gain, loss) =>
|
|
30
29
|
AmountMath.subtract(AmountMath.add(base, gain), loss);
|
|
31
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
|
+
|
|
32
42
|
/**
|
|
33
43
|
* Verifies that every key in the proposal is in the provided list
|
|
34
44
|
*
|
|
@@ -65,27 +75,6 @@ export const checkDebtLimit = (debtLimit, totalDebt, toMint) => {
|
|
|
65
75
|
)} would hit total debt limit ${q(debtLimit)}`;
|
|
66
76
|
};
|
|
67
77
|
|
|
68
|
-
/**
|
|
69
|
-
* @deprecated incompatible with durability; instead handle vstorage ephemerally on a durable PublishKit
|
|
70
|
-
* @template T
|
|
71
|
-
* @param {ERef<StorageNode>} storageNode
|
|
72
|
-
* @param {ERef<Marshaller>} marshaller
|
|
73
|
-
* @returns {MetricsPublisherKit<T>}
|
|
74
|
-
*/
|
|
75
|
-
export const makeMetricsPublisherKit = (storageNode, marshaller) => {
|
|
76
|
-
assert(
|
|
77
|
-
storageNode && marshaller,
|
|
78
|
-
'makeMetricsPublisherKit missing storageNode or marshaller',
|
|
79
|
-
);
|
|
80
|
-
/** @type {import('@agoric/notifier').StoredPublisherKit<T>} */
|
|
81
|
-
const kit = makeStoredPublisherKit(storageNode, marshaller, 'metrics');
|
|
82
|
-
return {
|
|
83
|
-
metricsPublication: kit.publisher,
|
|
84
|
-
metricsSubscription: kit.subscriber,
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
harden(makeMetricsPublisherKit);
|
|
88
|
-
|
|
89
78
|
/**
|
|
90
79
|
* @template T
|
|
91
80
|
* @typedef {object} MetricsPublisherKit<T>
|
|
@@ -100,31 +89,13 @@ harden(makeMetricsPublisherKit);
|
|
|
100
89
|
* @property {StoredSubscriber<T>} metricsSubscriber
|
|
101
90
|
*/
|
|
102
91
|
|
|
103
|
-
/**
|
|
104
|
-
* @deprecated incompatible with durability; instead handle vstorage ephemerally on a durable PublishKit
|
|
105
|
-
* @template T
|
|
106
|
-
* @param {ERef<StorageNode>} storageNode
|
|
107
|
-
* @param {ERef<Marshaller>} marshaller
|
|
108
|
-
* @returns {MetricsPublishKit<T>}
|
|
109
|
-
*/
|
|
110
|
-
export const makeMetricsPublishKit = (storageNode, marshaller) => {
|
|
111
|
-
assert(
|
|
112
|
-
storageNode && marshaller,
|
|
113
|
-
'makeMetricsPublisherKit missing storageNode or marshaller',
|
|
114
|
-
);
|
|
115
|
-
const metricsNode = E(storageNode).makeChildNode('metrics');
|
|
116
|
-
/** @type {StoredPublishKit<T>} */
|
|
117
|
-
const kit = makeStoredPublishKit(metricsNode, marshaller);
|
|
118
|
-
return {
|
|
119
|
-
metricsPublisher: kit.publisher,
|
|
120
|
-
metricsSubscriber: kit.subscriber,
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
harden(makeMetricsPublishKit);
|
|
124
|
-
|
|
125
92
|
/**
|
|
126
93
|
* @param {Brand} brand must be a 'nat' brand, not checked
|
|
127
94
|
* @param {NatValue} [min]
|
|
128
95
|
*/
|
|
129
96
|
export const makeNatAmountShape = (brand, min) =>
|
|
130
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);
|
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { AmountMath } from '@agoric/ertp';
|
|
7
7
|
import { assertAllDefined, makeTracer } from '@agoric/internal';
|
|
8
|
-
import {
|
|
9
|
-
makeNotifierFromSubscriber,
|
|
10
|
-
observeNotifier,
|
|
11
|
-
SubscriberShape,
|
|
12
|
-
} from '@agoric/notifier';
|
|
8
|
+
import { makeNotifierFromSubscriber, observeNotifier } from '@agoric/notifier';
|
|
13
9
|
import { M, makeScalarBigMapStore, prepareExoClassKit } from '@agoric/vat-data';
|
|
14
10
|
import {
|
|
15
11
|
defineRecorderKit,
|
|
@@ -208,8 +204,6 @@ export const prepareFluxAggregatorKit = async (
|
|
|
208
204
|
creator: M.interface('fluxAggregator creatorFacet', {}, { sloppy: true }),
|
|
209
205
|
public: M.interface('fluxAggregator publicFacet', {
|
|
210
206
|
getPriceAuthority: M.call().returns(M.any()),
|
|
211
|
-
getSubscriber: M.call().returns(SubscriberShape),
|
|
212
|
-
getRoundStartNotifier: M.call().returns(SubscriberShape),
|
|
213
207
|
getPublicTopics: M.call().returns({
|
|
214
208
|
quotes: M.any(),
|
|
215
209
|
latestRound: M.any(),
|
|
@@ -346,14 +340,6 @@ export const prepareFluxAggregatorKit = async (
|
|
|
346
340
|
getPriceAuthority() {
|
|
347
341
|
return priceAuthority;
|
|
348
342
|
},
|
|
349
|
-
/** @deprecated use getPublicTopics */
|
|
350
|
-
getSubscriber: () => {
|
|
351
|
-
return priceKit.subscriber;
|
|
352
|
-
},
|
|
353
|
-
/** @deprecated use getPublicTopics */
|
|
354
|
-
getRoundStartNotifier() {
|
|
355
|
-
return latestRoundKit.subscriber;
|
|
356
|
-
},
|
|
357
343
|
getPublicTopics() {
|
|
358
344
|
return {
|
|
359
345
|
quotes: makeRecorderTopic(
|
|
@@ -519,7 +519,7 @@ export const startAuctioneer = async (
|
|
|
519
519
|
},
|
|
520
520
|
},
|
|
521
521
|
issuer: {
|
|
522
|
-
consume: { [Stable.symbol]:
|
|
522
|
+
consume: { [Stable.symbol]: stableIssuerP },
|
|
523
523
|
},
|
|
524
524
|
},
|
|
525
525
|
{
|
|
@@ -539,11 +539,11 @@ export const startAuctioneer = async (
|
|
|
539
539
|
|
|
540
540
|
const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation();
|
|
541
541
|
|
|
542
|
-
const [initialPoserInvitation, electorateInvitationAmount,
|
|
542
|
+
const [initialPoserInvitation, electorateInvitationAmount, stableIssuer] =
|
|
543
543
|
await Promise.all([
|
|
544
544
|
poserInvitationP,
|
|
545
545
|
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP),
|
|
546
|
-
|
|
546
|
+
stableIssuerP,
|
|
547
547
|
]);
|
|
548
548
|
|
|
549
549
|
const timerBrand = await E(chainTimerService).getTimerBrand();
|
|
@@ -571,7 +571,7 @@ export const startAuctioneer = async (
|
|
|
571
571
|
governedContractInstallation: auctionInstallation,
|
|
572
572
|
governed: {
|
|
573
573
|
terms: auctionTerms,
|
|
574
|
-
issuerKeywordRecord: { Bid:
|
|
574
|
+
issuerKeywordRecord: { Bid: stableIssuer },
|
|
575
575
|
storageNode,
|
|
576
576
|
marshaller,
|
|
577
577
|
label: 'auctioneer',
|
|
@@ -5,7 +5,6 @@ import { M, makeScalarBigMapStore, prepareExoClassKit } from '@agoric/vat-data';
|
|
|
5
5
|
import { atomicTransfer } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
|
|
6
6
|
import {
|
|
7
7
|
makeRecorderTopic,
|
|
8
|
-
SubscriberShape,
|
|
9
8
|
TopicsRecordShape,
|
|
10
9
|
} from '@agoric/zoe/src/contractSupport/topics.js';
|
|
11
10
|
import { AmountKeywordRecordShape } from '@agoric/zoe/src/typeGuards.js';
|
|
@@ -57,7 +56,6 @@ export const prepareAssetReserveKit = async (
|
|
|
57
56
|
}),
|
|
58
57
|
public: M.interface('AssetReserve public', {
|
|
59
58
|
makeAddCollateralInvitation: M.call().returns(M.promise()),
|
|
60
|
-
getMetrics: M.call().returns(SubscriberShape),
|
|
61
59
|
getPublicTopics: M.call().returns(TopicsRecordShape),
|
|
62
60
|
}),
|
|
63
61
|
shortfallReportingFacet: M.interface('AssetReserve shortfall reporter', {
|
|
@@ -189,7 +187,7 @@ export const prepareAssetReserveKit = async (
|
|
|
189
187
|
await zcf.saveIssuer(issuer, keyword);
|
|
190
188
|
},
|
|
191
189
|
|
|
192
|
-
/**
|
|
190
|
+
/** XXX redundant with getPublicTopics metrics `allocation` */
|
|
193
191
|
getAllocations() {
|
|
194
192
|
return this.state.collateralSeat.getCurrentAllocation();
|
|
195
193
|
},
|
|
@@ -239,10 +237,6 @@ export const prepareAssetReserveKit = async (
|
|
|
239
237
|
};
|
|
240
238
|
return zcf.makeInvitation(handler, 'Add Collateral');
|
|
241
239
|
},
|
|
242
|
-
/** @deprecated */
|
|
243
|
-
getMetrics() {
|
|
244
|
-
return this.state.metricsKit.subscriber;
|
|
245
|
-
},
|
|
246
240
|
getPublicTopics() {
|
|
247
241
|
return {
|
|
248
242
|
metrics: makeRecorderTopic(
|
package/src/reserve/doc.md
CHANGED
|
@@ -90,20 +90,20 @@ sequenceDiagram
|
|
|
90
90
|
participant caller
|
|
91
91
|
participant reserveContract
|
|
92
92
|
participant collateralSeat
|
|
93
|
-
participant
|
|
93
|
+
participant stableMint
|
|
94
94
|
participant runSeat
|
|
95
95
|
participant zcf
|
|
96
96
|
participant ammPublicFacet
|
|
97
97
|
participant liquidityInvite
|
|
98
98
|
|
|
99
|
-
caller ->> reserveContract: addLiquidityToAmmPool(collateralAmount,
|
|
99
|
+
caller ->> reserveContract: addLiquidityToAmmPool(collateralAmount, stableAmount)
|
|
100
100
|
reserveContract ->> reserveContract: get collateralKeyword from collateralAmount.brand
|
|
101
101
|
reserveContract ->> collateralSeat: get current allocation for collateralKeyword
|
|
102
102
|
reserveContract ->> reserveContract: ensure current allocation for collateralKeyword is >= collateralAmount
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
note right of reserveContract: create the IST
|
|
106
|
-
reserveContract ->>
|
|
106
|
+
reserveContract ->> stableMint: mintGains of stableAmount, and get runSeat
|
|
107
107
|
reserveContract ->> runSeat: increment collateralKeyword: collateralAmount
|
|
108
108
|
reserveContract ->> collateralSeat: decrement collateralKeyword: collateralAmount
|
|
109
109
|
reserveContract ->> zcf: reallocate(runSeat, collateralSeat)
|
|
@@ -112,7 +112,7 @@ note right of reserveContract: Add IST and collateral to the AMM
|
|
|
112
112
|
reserveContract ->> ammPublicFacet: get invite for makeAddLiquidityAtRateInvitation
|
|
113
113
|
reserveContract ->> reserveContract: create mapping, IST = central, collateralKeyword: secondary
|
|
114
114
|
reserveContract ->> reserveContract: get liquidity brand for collateralKeyword
|
|
115
|
-
reserveContract ->> reserveContract: terms mapping, give Central:
|
|
115
|
+
reserveContract ->> reserveContract: terms mapping, give Central: stableAmount, Secondary collateralAmount, want liquidity
|
|
116
116
|
reserveContract ->> zcf: offer
|
|
117
117
|
|
|
118
118
|
|
|
@@ -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);
|
|
@@ -21,15 +21,6 @@
|
|
|
21
21
|
* @property {() => Invitation} makeSwapInvitation
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {object} Collateral
|
|
26
|
-
* @property {Ratio} liquidationMargin
|
|
27
|
-
* @property {Ratio} stabilityFee
|
|
28
|
-
* @property {Ratio} marketPrice
|
|
29
|
-
* @property {Ratio} interestRate
|
|
30
|
-
* @property {Brand} brand
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
24
|
/**
|
|
34
25
|
* @typedef {object} VaultManagerParamValues
|
|
35
26
|
* @property {Ratio} liquidationMargin - margin below which collateral will be
|
|
@@ -108,7 +99,7 @@
|
|
|
108
99
|
* @typedef {object} LiquidationStrategy
|
|
109
100
|
* @property {() => KeywordKeywordRecord} keywordMapping
|
|
110
101
|
* @property {(collateral: Amount, run: Amount) => Proposal} makeProposal
|
|
111
|
-
* @property {(
|
|
102
|
+
* @property {(debt: Amount) => Promise<Invitation>} makeInvitation
|
|
112
103
|
*/
|
|
113
104
|
|
|
114
105
|
/**
|