@agoric/inter-protocol 0.13.3 → 0.13.4-dev-d32a66a.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.
Files changed (140) hide show
  1. package/README.md +26 -20
  2. package/bundles/bundle-auctioneer-js-meta.json +722 -0
  3. package/bundles/bundle-auctioneer.js +1 -0
  4. package/bundles/bundle-econCommitteeCharter-js-meta.json +514 -0
  5. package/bundles/bundle-econCommitteeCharter.js +1 -0
  6. package/bundles/bundle-feeDistributor-js-meta.json +458 -0
  7. package/bundles/bundle-feeDistributor.js +1 -1
  8. package/bundles/bundle-fluxAggregatorKit-js-meta.json +674 -0
  9. package/bundles/bundle-fluxAggregatorKit.js +1 -0
  10. package/bundles/bundle-psm-js-meta.json +666 -0
  11. package/bundles/bundle-psm.js +1 -1
  12. package/bundles/bundle-reserve-js-meta.json +662 -0
  13. package/bundles/bundle-reserve.js +1 -1
  14. package/bundles/bundle-scaledPriceAuthority-js-meta.json +542 -0
  15. package/bundles/bundle-scaledPriceAuthority.js +1 -1
  16. package/bundles/bundle-vaultFactory-js-meta.json +786 -0
  17. package/bundles/bundle-vaultFactory.js +1 -1
  18. package/exported.js +0 -2
  19. package/package.json +34 -31
  20. package/scripts/add-collateral-core.js +14 -4
  21. package/scripts/build-bundles.js +9 -2
  22. package/scripts/deploy-contracts.js +1 -4
  23. package/scripts/init-core.js +20 -38
  24. package/scripts/invite-committee-core.js +1 -1
  25. package/scripts/manual-price-feed.js +2 -2
  26. package/scripts/price-feed-core.js +7 -6
  27. package/scripts/start-local-chain.sh +84 -0
  28. package/src/auction/auctionBook.js +755 -0
  29. package/src/auction/auctioneer.js +720 -0
  30. package/src/auction/offerBook.js +221 -0
  31. package/src/auction/params.js +174 -0
  32. package/src/auction/scheduleMath.js +167 -0
  33. package/src/auction/scheduler.js +370 -0
  34. package/src/auction/sortedOffers.js +136 -0
  35. package/src/auction/util.js +55 -0
  36. package/src/clientSupport.js +324 -0
  37. package/src/collectFees.js +8 -14
  38. package/src/contractSupport.js +24 -68
  39. package/src/econCommitteeCharter.js +161 -77
  40. package/src/feeDistributor.js +60 -20
  41. package/src/index.js +2 -0
  42. package/src/interest-math.js +1 -4
  43. package/src/interest.js +20 -13
  44. package/src/price/README.md +57 -0
  45. package/src/price/fluxAggregatorContract.js +190 -0
  46. package/src/price/fluxAggregatorKit.js +375 -0
  47. package/src/price/priceOracleKit.js +127 -0
  48. package/src/price/roundsManager.js +726 -0
  49. package/src/proposals/addAssetToVault.js +89 -94
  50. package/src/proposals/committee-proposal.js +141 -40
  51. package/src/proposals/core-proposal.js +100 -167
  52. package/src/proposals/econ-behaviors.js +320 -564
  53. package/src/proposals/price-feed-proposal.js +175 -78
  54. package/src/proposals/startEconCommittee.js +25 -12
  55. package/src/proposals/startPSM.js +231 -211
  56. package/src/proposals/utils.js +31 -17
  57. package/src/psm/README.md +71 -0
  58. package/src/psm/psm.js +272 -160
  59. package/src/psm/types.js +2 -0
  60. package/src/reserve/assetReserve.js +56 -413
  61. package/src/reserve/assetReserveKit.js +303 -0
  62. package/src/reserve/doc.md +1 -3
  63. package/src/reserve/params.js +2 -5
  64. package/src/typeGuards.js +13 -0
  65. package/src/vaultFactory/Liquidation.md +100 -0
  66. package/src/vaultFactory/burn.js +2 -1
  67. package/src/vaultFactory/liquidation.js +270 -108
  68. package/src/vaultFactory/math.js +95 -0
  69. package/src/vaultFactory/orderedVaultStore.js +0 -1
  70. package/src/vaultFactory/params.js +133 -112
  71. package/src/vaultFactory/prioritizedVaults.js +36 -79
  72. package/src/vaultFactory/storeUtils.js +67 -9
  73. package/src/vaultFactory/type-imports.js +2 -0
  74. package/src/vaultFactory/types.js +22 -26
  75. package/src/vaultFactory/vault.js +772 -709
  76. package/src/vaultFactory/vaultDirector.js +480 -483
  77. package/src/vaultFactory/vaultFactory.js +99 -60
  78. package/src/vaultFactory/vaultHolder.js +121 -95
  79. package/src/vaultFactory/vaultKit.js +39 -31
  80. package/src/vaultFactory/vaultManager.js +1249 -868
  81. package/bundles/bundle-amm-meta.js +0 -618
  82. package/bundles/bundle-amm.js +0 -1
  83. package/bundles/bundle-feeDistributor-meta.js +0 -346
  84. package/bundles/bundle-interchainPool-meta.js +0 -534
  85. package/bundles/bundle-interchainPool.js +0 -1
  86. package/bundles/bundle-liquidateIncrementally-meta.js +0 -442
  87. package/bundles/bundle-liquidateIncrementally.js +0 -1
  88. package/bundles/bundle-liquidateMinimum-meta.js +0 -442
  89. package/bundles/bundle-liquidateMinimum.js +0 -1
  90. package/bundles/bundle-psm-meta.js +0 -538
  91. package/bundles/bundle-psmCharter-meta.js +0 -418
  92. package/bundles/bundle-psmCharter.js +0 -1
  93. package/bundles/bundle-reserve-meta.js +0 -542
  94. package/bundles/bundle-scaledPriceAuthority-meta.js +0 -442
  95. package/bundles/bundle-stakeFactory-meta.js +0 -574
  96. package/bundles/bundle-stakeFactory.js +0 -1
  97. package/bundles/bundle-vaultFactory-meta.js +0 -598
  98. package/scripts/add-initial-liquidity.js +0 -180
  99. package/src/collect.js +0 -13
  100. package/src/interchainPool.js +0 -135
  101. package/src/makeTracer.js +0 -36
  102. package/src/my-lien.js +0 -85
  103. package/src/proposals/demoIssuers.js +0 -685
  104. package/src/proposals/sim-behaviors.js +0 -40
  105. package/src/psm/psmCharter.js +0 -139
  106. package/src/stakeFactory/README.md +0 -62
  107. package/src/stakeFactory/attestation.js +0 -267
  108. package/src/stakeFactory/attestationTool.js +0 -66
  109. package/src/stakeFactory/constants.js +0 -16
  110. package/src/stakeFactory/params.js +0 -31
  111. package/src/stakeFactory/stakeFactory.js +0 -236
  112. package/src/stakeFactory/stakeFactoryKit.js +0 -455
  113. package/src/stakeFactory/stakeFactoryManager.js +0 -296
  114. package/src/stakeFactory/types.js +0 -42
  115. package/src/vaultFactory/liquidateIncrementally.js +0 -350
  116. package/src/vaultFactory/liquidateMinimum.js +0 -100
  117. package/src/vpool-xyk-amm/AttackersGuide.md +0 -89
  118. package/src/vpool-xyk-amm/addLiquidity.js +0 -222
  119. package/src/vpool-xyk-amm/addPool.js +0 -197
  120. package/src/vpool-xyk-amm/constantProduct/AMM-Trade.jpeg +0 -0
  121. package/src/vpool-xyk-amm/constantProduct/README.md +0 -204
  122. package/src/vpool-xyk-amm/constantProduct/calcFees.js +0 -99
  123. package/src/vpool-xyk-amm/constantProduct/calcSwapPrices.js +0 -85
  124. package/src/vpool-xyk-amm/constantProduct/core.js +0 -153
  125. package/src/vpool-xyk-amm/constantProduct/defaults.js +0 -5
  126. package/src/vpool-xyk-amm/constantProduct/getXY.js +0 -52
  127. package/src/vpool-xyk-amm/constantProduct/internal-types.js +0 -116
  128. package/src/vpool-xyk-amm/constantProduct/invariants.js +0 -43
  129. package/src/vpool-xyk-amm/constantProduct/swap.js +0 -272
  130. package/src/vpool-xyk-amm/constantProduct/types.js +0 -32
  131. package/src/vpool-xyk-amm/doublePool.js +0 -194
  132. package/src/vpool-xyk-amm/estimate.js +0 -155
  133. package/src/vpool-xyk-amm/multipoolMarketMaker.js +0 -520
  134. package/src/vpool-xyk-amm/params.js +0 -61
  135. package/src/vpool-xyk-amm/pool.js +0 -396
  136. package/src/vpool-xyk-amm/priceAuthority.js +0 -94
  137. package/src/vpool-xyk-amm/removeLiquidity.js +0 -31
  138. package/src/vpool-xyk-amm/singlePool.js +0 -73
  139. package/src/vpool-xyk-amm/swap.js +0 -69
  140. package/src/vpool-xyk-amm/types.js +0 -154
@@ -1,296 +0,0 @@
1
- // @ts-check
2
- // @jessie-check
3
- import { AmountMath } from '@agoric/ertp';
4
- import { makePublishKit, observeNotifier } from '@agoric/notifier';
5
- import { fit, getCopyBagEntries, M } from '@agoric/store';
6
- import { defineKindMulti } from '@agoric/vat-data';
7
- import { floorMultiplyBy } from '@agoric/zoe/src/contractSupport/index.js';
8
- import { makeRatio } from '@agoric/zoe/src/contractSupport/ratio.js';
9
- import { E } from '@endo/far';
10
- import { checkDebtLimit } from '../contractSupport.js';
11
- import { chargeInterest } from '../interest.js';
12
- import { makeTracer } from '../makeTracer.js';
13
- import { ManagerKW as KW } from './constants.js';
14
-
15
- const { details: X } = assert;
16
-
17
- const trace = makeTracer('RSM', false);
18
-
19
- /**
20
- * @typedef {{
21
- * getDebtLimit: () => Amount<'nat'>,
22
- * getInterestRate: () => Ratio,
23
- * getMintingRatio: () => Ratio,
24
- * getLoanFee: () => Ratio,
25
- * }} ParamManager
26
- * @typedef {{
27
- * compoundedInterest: Ratio,
28
- * latestInterestUpdate: Timestamp,
29
- * totalDebt: Amount<'nat'>,
30
- * }} AssetState
31
- * @typedef {Readonly<{
32
- * assetPublisher: Publisher<AssetState>,
33
- * assetSubscriber: Subscriber<AssetState>,
34
- * brands: { debt: Brand<'nat'>, Attestation: Brand<'copyBag'>, Stake: Brand<'nat'> },
35
- * mintPowers: { burnDebt: BurnDebt, getGovernedParams: () => ParamManager, mintAndReallocate: MintAndReallocate },
36
- * chargingPeriod: RelativeTime,
37
- * debtMint: ZCFMint<'nat'>,
38
- * poolIncrementSeat: ZCFSeat,
39
- * recordingPeriod: RelativeTime,
40
- * startTimeStamp: Timestamp,
41
- * timerService: ERef<TimerService>,
42
- * zcf: ZCF,
43
- * }>} ImmutableState
44
- * @typedef {AssetState & {
45
- * }} MutableState
46
- * @typedef {ImmutableState & MutableState} State
47
- * @typedef {Readonly<{
48
- * state: State,
49
- * facets: import('@agoric/vat-data/src/types').KindFacets<typeof behavior>,
50
- * }>} MethodContext
51
- */
52
-
53
- /**
54
- * @param {ZCF} zcf
55
- * @param {ZCFMint<'nat'>} debtMint
56
- * @param {{ debt: Brand<'nat'>, Attestation: Brand<'copyBag'>, Stake: Brand<'nat'> }} brands
57
- * @param {{ burnDebt: BurnDebt, getGovernedParams: () => ParamManager, mintAndReallocate: MintAndReallocate }} mintPowers
58
- * @param {object} timing
59
- * @param {ERef<TimerService>} timing.timerService
60
- * @param {RelativeTime} timing.chargingPeriod
61
- * @param {RelativeTime} timing.recordingPeriod
62
- * @param {Timestamp} timing.startTimeStamp
63
- *
64
- * @returns {State}
65
- */
66
- const initState = (
67
- zcf,
68
- debtMint,
69
- brands,
70
- mintPowers,
71
- { chargingPeriod, recordingPeriod, startTimeStamp, timerService },
72
- ) => {
73
- const totalDebt = AmountMath.makeEmpty(brands.debt, 'nat');
74
- const compoundedInterest = makeRatio(100n, brands.debt); // starts at 1.0, no interest
75
- const latestInterestUpdate = startTimeStamp;
76
-
77
- const { publisher: assetPublisher, subscriber: assetSubscriber } =
78
- makePublishKit();
79
- assetPublisher.publish(
80
- harden({
81
- compoundedInterest,
82
- interestRate: mintPowers.getGovernedParams().getInterestRate(),
83
- latestInterestUpdate,
84
- totalDebt,
85
- }),
86
- );
87
-
88
- const { zcfSeat: poolIncrementSeat } = zcf.makeEmptySeatKit();
89
-
90
- return {
91
- assetSubscriber,
92
- assetPublisher,
93
- brands,
94
- chargingPeriod,
95
- compoundedInterest,
96
- debtMint,
97
- latestInterestUpdate,
98
- mintPowers,
99
- poolIncrementSeat,
100
- recordingPeriod,
101
- startTimeStamp,
102
- timerService,
103
- totalDebt,
104
- zcf,
105
- };
106
- };
107
-
108
- /**
109
- *
110
- * @param {MethodContext} context
111
- */
112
- const finish = ({ state, facets }) => {
113
- const { recordingPeriod, timerService, zcf } = state;
114
- const { helper } = facets;
115
-
116
- const periodNotifier = E(timerService).makeNotifier(0n, recordingPeriod);
117
-
118
- void observeNotifier(periodNotifier, {
119
- updateState: updateTime =>
120
- helper
121
- .chargeAllVaults(updateTime)
122
- .catch(e =>
123
- console.error('🚨 stakeFactoryManager failed to charge interest', e),
124
- ),
125
- fail: reason => {
126
- zcf.shutdownWithFailure(
127
- assert.error(X`Unable to continue without a timer: ${reason}`),
128
- );
129
- },
130
- finish: done => {
131
- zcf.shutdownWithFailure(
132
- assert.error(X`Unable to continue without a timer: ${done}`),
133
- );
134
- },
135
- });
136
- };
137
-
138
- const helper = {
139
- /**
140
- * @param {MethodContext} context
141
- * @param {Timestamp} updateTime
142
- */
143
- chargeAllVaults: async ({ state }, updateTime) => {
144
- const { debtMint, mintPowers, poolIncrementSeat } = state;
145
- trace('chargeAllVaults', { updateTime });
146
- const interestRate = mintPowers.getGovernedParams().getInterestRate();
147
-
148
- const changes = chargeInterest(
149
- {
150
- mint: debtMint,
151
- mintAndReallocateWithFee: mintPowers.mintAndReallocate,
152
- poolIncrementSeat,
153
- seatAllocationKeyword: KW.Debt,
154
- },
155
- {
156
- interestRate,
157
- chargingPeriod: state.chargingPeriod,
158
- recordingPeriod: state.recordingPeriod,
159
- },
160
- {
161
- latestInterestUpdate: state.latestInterestUpdate,
162
- compoundedInterest: state.compoundedInterest,
163
- totalDebt: state.totalDebt,
164
- },
165
- updateTime,
166
- );
167
- state.compoundedInterest = changes.compoundedInterest;
168
- state.latestInterestUpdate = changes.latestInterestUpdate;
169
- state.totalDebt = changes.totalDebt;
170
-
171
- const payload = harden({
172
- compoundedInterest: state.compoundedInterest,
173
- interestRate,
174
- latestInterestUpdate: state.latestInterestUpdate,
175
- totalDebt: state.totalDebt,
176
- });
177
- const { assetPublisher: assetUpdater } = state;
178
- assetUpdater.publish(payload);
179
-
180
- trace('chargeAllVaults complete', payload);
181
- },
182
- };
183
-
184
- const manager = {
185
- /**
186
- * @param {MethodContext} context
187
- * @param { Amount<'copyBag'>} attestationGiven
188
- * */
189
- maxDebtForLien: ({ state }, attestationGiven) => {
190
- const { brands, mintPowers } = state;
191
- const mintingRatio = mintPowers.getGovernedParams().getMintingRatio();
192
- assert.equal(
193
- mintingRatio.numerator.brand,
194
- brands.debt,
195
- X`${mintingRatio} not in Debt / Stake`,
196
- );
197
- assert.equal(
198
- mintingRatio.denominator.brand,
199
- brands.Stake,
200
- X`${mintingRatio} not in Debt / Stake`,
201
- );
202
- assert.equal(
203
- attestationGiven.brand,
204
- brands.Attestation,
205
- X`Invalid Attestation ${attestationGiven}. Expected brand ${brands.Attestation}`,
206
- );
207
- fit(attestationGiven.value, M.bagOf(M.string()), 'attestationGiven');
208
- const [[_addr, valueLiened]] = getCopyBagEntries(attestationGiven.value);
209
- const amountLiened = AmountMath.make(brands.Stake, valueLiened);
210
- const maxDebt = floorMultiplyBy(amountLiened, mintingRatio);
211
- return { maxDebt, amountLiened };
212
- },
213
-
214
- /**
215
- * Update total debt of this manager given the change in debt on a vault
216
- *
217
- * @param {MethodContext} context
218
- * @param {Amount<'nat'>} oldDebtOnVault
219
- * @param {Amount<'nat'>} newDebtOnVault
220
- */
221
- // TODO: Add limits for amounts between vault and vault manager
222
- // https://github.com/Agoric/agoric-sdk/issues/4599
223
- applyDebtDelta: ({ state }, oldDebtOnVault, newDebtOnVault) => {
224
- // This does not use AmountMath because it could be validly negative
225
- const delta = newDebtOnVault.value - oldDebtOnVault.value;
226
- trace(`updating total debt ${state.totalDebt} by ${delta}`);
227
- if (delta === 0n) {
228
- // nothing to do
229
- return;
230
- }
231
-
232
- const { brands } = state;
233
- // totalDebt += delta (Amount type ensures natural value)
234
- state.totalDebt = AmountMath.make(
235
- brands.debt,
236
- state.totalDebt.value + delta,
237
- );
238
- },
239
-
240
- /**
241
- * @param {MethodContext} context
242
- * @param {Amount} toMint
243
- * @param {Amount} fee
244
- * @param {ZCFSeat} seat
245
- * @param {...ZCFSeat} otherSeats
246
- * @returns {void}
247
- */
248
- mintAndReallocate: ({ state }, toMint, fee, seat, ...otherSeats) => {
249
- const { mintPowers } = state;
250
- checkDebtLimit(
251
- mintPowers.getGovernedParams().getDebtLimit(),
252
- state.totalDebt,
253
- toMint,
254
- );
255
- mintPowers.mintAndReallocate(toMint, fee, seat, ...otherSeats);
256
- state.totalDebt = AmountMath.add(state.totalDebt, toMint);
257
- },
258
-
259
- /** @param {MethodContext} context */
260
- getMintingRatio: ({ state }) =>
261
- state.mintPowers.getGovernedParams().getMintingRatio(),
262
- /** @param {MethodContext} context */
263
- getInterestRate: ({ state }) =>
264
- state.mintPowers.getGovernedParams().getInterestRate(),
265
- /** @param {MethodContext} context */
266
- getLoanFee: ({ state }) => state.mintPowers.getGovernedParams().getLoanFee(),
267
- /**
268
- * @param {MethodContext} context
269
- * @param {Amount} toBurn
270
- * @param {ZCFSeat} seat
271
- */
272
- burnDebt: ({ state }, toBurn, seat) =>
273
- state.mintPowers.burnDebt(toBurn, seat),
274
-
275
- /** @param {MethodContext} context */
276
- getDebtBrand: ({ state }) => state.brands.debt,
277
- /** @param {MethodContext} context */
278
- getCollateralBrand: ({ state }) => state.brands.Attestation,
279
-
280
- /** @param {MethodContext} context */
281
- getCompoundedInterest: ({ state }) => state.compoundedInterest,
282
- /** @param {MethodContext} context */
283
- getAssetSubscriber: ({ state }) => state.assetSubscriber,
284
- };
285
-
286
- const behavior = { helper, manager };
287
-
288
- export const makeStakeFactoryManager = defineKindMulti(
289
- 'StakeFactoryManager',
290
- initState,
291
- behavior,
292
- { finish },
293
- );
294
- /**
295
- * @typedef {ReturnType<typeof makeStakeFactoryManager>['manager']} StakeFactoryManager
296
- */
@@ -1,42 +0,0 @@
1
- /**
2
- * For example, an agoric1... Cosmos address
3
- *
4
- * @typedef {string} Address
5
- */
6
-
7
- /**
8
- * @typedef AttestationTool
9
- * @property {(amountToLien: Amount<'nat'>) => Promise<Payment>} makeAttestation
10
- * @property {() => Promise<AccountState>} getAccountState
11
- * @property {(lienedAmount: Amount<'nat'>) => Amount<'copyBag'>} wrapLienedAmount
12
- * @property {(attAmount: Amount<'copyBag'>) => Amount<'nat'>} unwrapLienedAmount
13
- * @property {() => Promise<Invitation>} makeReturnAttInvitation Make an invitation for returning an attestation.
14
- */
15
-
16
- /**
17
- * @typedef {CopyBag<Address>} ReturnableAttValue
18
- */
19
-
20
- /**
21
- * @typedef {{getTime: () => Timestamp, updateTime: (currentTime:
22
- * Timestamp) => void}} StoredTime
23
- */
24
-
25
- /**
26
- * @typedef {object} StakingAuthority
27
- * @property {(address: Address, increase: Amount<'nat'>) => Promise<Amount<'nat'>>} increaseLiened
28
- * @property {(address: Address, decrease: Amount<'nat'>) => Promise<Amount<'nat'>>} decreaseLiened
29
- * @property {(address: Address, brand: Brand<'nat'>) => ERef<AccountState> } getAccountState
30
- * @typedef {object} AccountState
31
- * @property {Amount<'nat'>} bonded
32
- * @property {Amount<'nat'>} liened
33
- * @property {Amount<'nat'>} locked
34
- * @property {Amount<'nat'>} total
35
- * @property {Amount<'nat'>} unbonding
36
- * @property {bigint} currentTime
37
- *
38
- * Performance note: getAccountState is expensive, as it has to iterate over all
39
- * delegations.
40
- *
41
- * @typedef {(address: Address, brand: Brand<'nat'>) => Amount} GetLiened
42
- */
@@ -1,350 +0,0 @@
1
- /* eslint-disable no-await-in-loop */
2
- // @ts-check
3
-
4
- import { E } from '@endo/eventual-send';
5
- import {
6
- getAmountOut,
7
- assertProposalShape,
8
- offerTo,
9
- natSafeMath as NatMath,
10
- ceilMultiplyBy,
11
- oneMinus,
12
- } from '@agoric/zoe/src/contractSupport/index.js';
13
- import { AmountMath } from '@agoric/ertp';
14
- import { Far } from '@endo/marshal';
15
- import { makeTracer } from '../makeTracer.js';
16
-
17
- const { details: X } = assert;
18
- const trace = makeTracer('LiqI', false);
19
-
20
- /**
21
- * @file
22
- * This contract liquidates a payment in multiple tranches to limit the
23
- * price impact on the AMM of any one sale. Each block it will compute
24
- * a tranche of collateral to sell, where the size is a function of
25
- * the amount of that collateral in the AMM pool and the desired price impact.
26
- * It presently consults the AMM and Oracle for whether to sell.
27
- *
28
- * The next revision of this will work as follows...
29
- *
30
- * It then gets 3 prices for the current tranche:
31
- * - AMM quote - compute XYK locally based on the pool sizes
32
- * - Reserve quote - based on a low price at which the Reserve will purchase
33
- * - Oracle quote - provided by the oracle that triggered liquidation
34
- * - Oracle limit - a limit that is DIVERGENCE_TOLERANCE below the
35
- * Oracle quote.
36
- * Then sell based on the following:
37
- * - If the AMM quote is *below* the oracle limit, skip this block
38
- * to wait for oracle and AMM prices to converge.
39
- * - If the Reserve quote is higher than the AMM quote, sell to the
40
- * Reserve with a `want` of the Oracle limit
41
- * - Otherwise, sell to the AMM with a `want` of the Oracle limit
42
- *
43
- * Selling uses the `oracleLimit as the `want` as the limit to allowed
44
- * slippage, and provides the remaining `debt` as the `stopAfter` so
45
- * that we sell no more than is needed to pay off the debt.
46
- *
47
- * TODO integrate the reserve, including the above Reserve strategies.
48
- */
49
-
50
- /**
51
- * @typedef {{
52
- * amm: XYKAMMPublicFacet,
53
- * priceAuthority: PriceAuthority,
54
- * reservePublicFacet: AssetReservePublicFacet,
55
- * timerService: TimerService,
56
- * debtBrand: Brand,
57
- * MaxImpactBP: NatValue,
58
- * OracleTolerance: Ratio,
59
- * AMMMaxSlippage: Ratio,
60
- * }} LiquidationContractTerms
61
- * @param {ZCF<LiquidationContractTerms>} zcf
62
- */
63
- const start = async zcf => {
64
- const {
65
- amm,
66
- priceAuthority,
67
- reservePublicFacet,
68
- timerService,
69
- debtBrand,
70
- MaxImpactBP,
71
- OracleTolerance,
72
- AMMMaxSlippage,
73
- } = zcf.getTerms();
74
-
75
- const SCALE = 1_000_000n;
76
- const BASIS_POINTS = 10_000n * SCALE;
77
- const BP2 = BASIS_POINTS * BASIS_POINTS;
78
- const ONE = BASIS_POINTS;
79
-
80
- const asFloat = (numerator, denominator) =>
81
- Number(numerator) / Number(denominator);
82
-
83
- // #region penalty distribution
84
- const { zcfSeat: penaltyPoolSeat } = zcf.makeEmptySeatKit();
85
- const drainPenaltyPool = async () => {
86
- const addCollateral = await E(
87
- reservePublicFacet,
88
- ).makeAddCollateralInvitation();
89
- const proposal = harden({
90
- give: { Collateral: penaltyPoolSeat.getCurrentAllocation().Out },
91
- });
92
- const { deposited, userSeatPromise } = await offerTo(
93
- zcf,
94
- addCollateral,
95
- harden({ Out: 'Collateral' }),
96
- proposal,
97
- penaltyPoolSeat,
98
- );
99
- const [deposits] = await Promise.all([deposited, userSeatPromise]);
100
- trace('drainPenaltyPool deposited', deposits.Out);
101
- };
102
- // #endregion
103
-
104
- /**
105
- * Compute the tranche size whose sale on the AMM would have
106
- * a price impact of MAX_IMPACT_BP.
107
- * This doesn't use ratios so that it is usable for any brand
108
- *
109
- * @param {Amount} poolSize
110
- * @param {bigint} maxImpactBP
111
- * @param {bigint} feeBP
112
- * @returns {Amount}
113
- */
114
- const maxTrancheWithFees = (poolSize, maxImpactBP, feeBP) => {
115
- trace('maxTrancheWithFees', poolSize, maxImpactBP, feeBP);
116
- const maxImpactScaled = maxImpactBP * SCALE;
117
- const ammFeeScaled = feeBP * SCALE;
118
- // sqrt(impact + 1) - 1
119
- // Since we take SQRT but want to be BP, multiply in BP
120
- const impactFactor =
121
- BigInt(
122
- Math.ceil(Math.sqrt(Number((maxImpactScaled + ONE) * BASIS_POINTS))),
123
- ) - ONE;
124
- const impactWithFee = impactFactor * (ONE - ammFeeScaled);
125
- return AmountMath.make(
126
- poolSize.brand,
127
- NatMath.ceilDivide(NatMath.multiply(poolSize.value, impactWithFee), BP2),
128
- );
129
- };
130
-
131
- const computeOracleLimit = (oracleQuote, oracleTolerance) => {
132
- return ceilMultiplyBy(getAmountOut(oracleQuote), oneMinus(oracleTolerance));
133
- };
134
-
135
- const getAMMFeeBP = async () => {
136
- const zoe = zcf.getZoeService();
137
- const instance = await E(zoe).getInstance(E(amm).makeSwapInvitation());
138
- const terms = await E(zoe).getTerms(instance);
139
- // trace('amm terms', terms);
140
- const { poolFeeBP, protocolFeeBP } = terms;
141
- return poolFeeBP + protocolFeeBP;
142
- };
143
-
144
- const AMMFeeBP = await getAMMFeeBP();
145
-
146
- const estimateAMMProceeds = (
147
- poolCentral,
148
- poolCollateral,
149
- tranche,
150
- debt,
151
- maxSlip,
152
- ) => {
153
- const k = NatMath.multiply(poolCentral.value, poolCollateral.value);
154
- const postSaleCollateral = AmountMath.add(tranche, poolCollateral);
155
- const estimateCentral = NatMath.subtract(
156
- poolCentral.value,
157
- NatMath.floorDivide(k, postSaleCollateral.value),
158
- );
159
- const estimateAmount = AmountMath.make(debt.brand, estimateCentral);
160
- const minAmmProceeds = ceilMultiplyBy(estimateAmount, oneMinus(maxSlip));
161
- trace('AMM estimate', {
162
- tranche,
163
- minAmmProceeds,
164
- poolCentral,
165
- poolCollateral,
166
- });
167
- return minAmmProceeds;
168
- };
169
-
170
- /**
171
- * A generator that yields once per block, starting in the current block.
172
- *
173
- * @yields {void}
174
- */
175
- async function* oncePerBlock() {
176
- yield;
177
- while (true) {
178
- yield E(timerService).delay(1n);
179
- }
180
- }
181
-
182
- /**
183
- * Generate tranches to sell until the debt is paid off.
184
- *
185
- * @param {ZCFSeat} seat
186
- * @param {Amount} originalDebt
187
- * @yields {LiquidationStep}
188
- */
189
- async function* processTranches(seat, originalDebt) {
190
- while (true) {
191
- const proceedsSoFar = seat.getAmountAllocated('Out');
192
- const toSell = seat.getAmountAllocated('In');
193
- if (
194
- AmountMath.isGTE(proceedsSoFar, originalDebt) ||
195
- AmountMath.isEmpty(toSell)
196
- ) {
197
- trace('exiting processTranches loop');
198
- return;
199
- }
200
- await oncePerBlock();
201
-
202
- // Determine the max allowed tranche size
203
- const { Secondary: poolCollateral, Central: poolCentral } = await E(
204
- amm,
205
- ).getPoolAllocation(toSell.brand);
206
- const maxAllowedTranche = maxTrancheWithFees(
207
- poolCollateral,
208
- MaxImpactBP,
209
- AMMFeeBP,
210
- );
211
- trace('Pool', { poolCentral, poolCollateral });
212
- trace('Max tranche', asFloat(maxAllowedTranche.value, 100000n));
213
- const tranche = AmountMath.min(maxAllowedTranche, toSell);
214
-
215
- // compute the expected proceeds from the AMM for tranche
216
- const debt = AmountMath.subtract(originalDebt, proceedsSoFar);
217
- const minAmmProceeds = estimateAMMProceeds(
218
- poolCentral,
219
- poolCollateral,
220
- tranche,
221
- debt,
222
- AMMMaxSlippage,
223
- );
224
-
225
- // this could use a unit rather than the tranche size to run concurrently
226
- /** @type PriceQuote */
227
- const oracleQuote = await E(priceAuthority).quoteGiven(
228
- tranche,
229
- debtBrand,
230
- );
231
- const oracleLimit = computeOracleLimit(oracleQuote, OracleTolerance);
232
-
233
- trace('Tranche', { debt, tranche, minAmmProceeds, oracleLimit });
234
- if (AmountMath.isGTE(minAmmProceeds, oracleLimit)) {
235
- // our prices are within the same range; go ahead and try to sell
236
- yield {
237
- collateral: tranche,
238
- ammProceeds: minAmmProceeds,
239
- debt,
240
- oracleLimit,
241
- };
242
- } else {
243
- // prices are too far apart; don't sell this block
244
- trace('SKIP');
245
- }
246
- }
247
- }
248
-
249
- async function sellTranche(debtorSeat, collateral, debt, oracleLimit) {
250
- const swapInvitation = E(amm).makeSwapInvitation();
251
- const want = AmountMath.min(debt, oracleLimit);
252
- // TODO if the debt shouldn't require all the collatearal to cover it,
253
- // this may not provide relevant offer safety
254
- const proposal = {
255
- give: { In: collateral },
256
- want: { Out: want },
257
- };
258
- const { deposited, userSeatPromise: liqSeat } = await offerTo(
259
- zcf,
260
- swapInvitation,
261
- undefined, // The keywords were mapped already
262
- proposal,
263
- debtorSeat,
264
- debtorSeat,
265
- { stopAfter: debt },
266
- );
267
- await Promise.all([E(liqSeat).getOfferResult(), deposited]);
268
-
269
- // This uses getCurrentAllocationJig only to support testing, so is ok
270
- const amounts = await E(liqSeat).getCurrentAllocationJig();
271
- trace('offerResult', { amounts });
272
- }
273
-
274
- /**
275
- * @param {ZCFSeat} debtorSeat
276
- * @param {object} options
277
- * @param {Amount<'nat'>} options.debt Debt before penalties
278
- * @param {Ratio} options.penaltyRate
279
- */
280
- const handleLiquidateOffer = async (
281
- debtorSeat,
282
- { debt: originalDebt, penaltyRate },
283
- ) => {
284
- assertProposalShape(debtorSeat, {
285
- give: { In: null },
286
- });
287
- originalDebt.brand === debtBrand ||
288
- assert.fail(X`Cannot liquidate to ${originalDebt.brand}`);
289
- const penalty = ceilMultiplyBy(originalDebt, penaltyRate);
290
- const debtWithPenalty = AmountMath.add(originalDebt, penalty);
291
- trace('LIQ', { originalDebt, debtWithPenalty });
292
-
293
- for await (const t of processTranches(debtorSeat, debtWithPenalty)) {
294
- const { collateral, oracleLimit, ammProceeds, debt } = t;
295
- trace(`OFFER TO DEBT: `, {
296
- collateral,
297
- ammProceeds,
298
- oracleLimit,
299
- debt,
300
- });
301
- const collateralBefore = debtorSeat.getAmountAllocated('In');
302
- const proceedsBefore = debtorSeat.getAmountAllocated('Out');
303
-
304
- await sellTranche(debtorSeat, collateral, debt, oracleLimit);
305
-
306
- const proceedsAfter = debtorSeat.getAmountAllocated('Out');
307
- if (AmountMath.isEqual(proceedsBefore, proceedsAfter)) {
308
- // nothing got sold
309
- const collateralAfter = debtorSeat.getAmountAllocated('In');
310
- trace('NOSELL', {
311
- proceedsBefore,
312
- proceedsAfter,
313
- collateralBefore,
314
- collateralAfter,
315
- });
316
- }
317
- }
318
-
319
- // Now we need to know how much was sold so we can pay off the debt.
320
- // We can use this seat because only liquidation adds debt brand to it..
321
- const debtPaid = debtorSeat.getAmountAllocated('Out', debtBrand);
322
- const penaltyPaid = AmountMath.min(penalty, debtPaid);
323
-
324
- // Allocate penalty portion of proceeds to a seat that will hold it for transfer to reserve
325
- penaltyPoolSeat.incrementBy(
326
- debtorSeat.decrementBy(harden({ Out: penaltyPaid })),
327
- );
328
- zcf.reallocate(penaltyPoolSeat, debtorSeat);
329
-
330
- debtorSeat.exit();
331
- trace('exit seat');
332
- // trigger but don't await so it doesn't make liquidation itself fail
333
- drainPenaltyPool().catch(e =>
334
- console.error('🚨 error draining penalty pool', e),
335
- );
336
- };
337
-
338
- /**
339
- * @type {ERef<Liquidator>}
340
- */
341
- const creatorFacet = Far('debtorInvitationCreator (incrementally)', {
342
- makeLiquidateInvitation: () =>
343
- zcf.makeInvitation(handleLiquidateOffer, 'Liquidate'),
344
- });
345
-
346
- return harden({ creatorFacet });
347
- };
348
-
349
- harden(start);
350
- export { start };