@@ -15,12 +15,25 @@ import { makeNotifierKit } from '@agoric/notifier';
15
15
import { makeRatio } from '@agoric/zoe/src/contractSupport/ratio.js' ;
16
16
import { AmountMath } from '@agoric/ertp' ;
17
17
import { Far } from '@agoric/marshal' ;
18
- import { makeTracer } from './makeTracer.js ' ;
18
+ import { makePromiseKit } from '@agoric/promise-kit ' ;
19
19
import { makeInterestCalculator } from './interest.js' ;
20
20
21
21
// a Vault is an individual loan, using some collateralType as the
22
22
// collateral, and lending RUN to the borrower
23
23
24
+ /**
25
+ * Constants for vault state.
26
+ *
27
+ * @typedef {'active' | 'liquidating' | 'closed' } VAULT_STATE
28
+ *
29
+ * @type {{ ACTIVE: 'active', LIQUIDATING: 'liquidating', CLOSED: 'closed' } }
30
+ */
31
+ export const VaultState = {
32
+ ACTIVE : 'active' ,
33
+ LIQUIDATING : 'liquidating' ,
34
+ CLOSED : 'closed' ,
35
+ } ;
36
+
24
37
/** @type {MakeVaultKit } */
25
38
export function makeVaultKit (
26
39
zcf ,
@@ -31,13 +44,18 @@ export function makeVaultKit(
31
44
loanParams ,
32
45
startTimeStamp ,
33
46
) {
34
- const trace = makeTracer ( 'VV' ) ;
35
47
const { updater : uiUpdater , notifier } = makeNotifierKit ( ) ;
48
+ const {
49
+ zcfSeat : liquidationZcfSeat ,
50
+ userSeat : liquidationSeat ,
51
+ } = zcf . makeEmptySeatKit ( undefined ) ;
52
+ const liquidationPromiseKit = makePromiseKit ( ) ;
36
53
37
- let active = true ; // liquidation halts all user actions
54
+ /** @type {VAULT_STATE } */
55
+ let vaultState = VaultState . ACTIVE ;
38
56
39
57
function assertVaultIsOpen ( ) {
40
- assert ( active , 'vault must still be active' ) ;
58
+ assert ( vaultState === VaultState . ACTIVE , 'vault must still be active' ) ;
41
59
}
42
60
43
61
const collateralBrand = manager . getCollateralBrand ( ) ;
@@ -49,9 +67,7 @@ export function makeVaultKit(
49
67
// (because the StableCoinMachine vat died), they'll get all their
50
68
// collateral back. If that happens, the issuer for the RUN will be dead,
51
69
// so their loan will be worthless.
52
- const { zcfSeat : vaultSeat , userSeat } = zcf . makeEmptySeatKit ( ) ;
53
-
54
- trace ( 'vaultSeat proposal' , vaultSeat . getProposal ( ) ) ;
70
+ const { zcfSeat : vaultSeat } = zcf . makeEmptySeatKit ( ) ;
55
71
56
72
const { brand : runBrand } = runMint . getIssuerRecord ( ) ;
57
73
let runDebt = AmountMath . makeEmpty ( runBrand ) ;
@@ -130,19 +146,31 @@ export function makeVaultKit(
130
146
locked : getCollateralAmount ( ) ,
131
147
debt : runDebt ,
132
148
collateralizationRatio,
133
- liquidated : ! active ,
149
+ liquidated : vaultState === VaultState . CLOSED ,
150
+ vaultState,
134
151
} ) ;
135
152
136
- if ( active ) {
137
- uiUpdater . updateState ( uiState ) ;
138
- } else {
139
- uiUpdater . finish ( uiState ) ;
153
+ switch ( vaultState ) {
154
+ case VaultState . ACTIVE :
155
+ case VaultState . LIQUIDATING :
156
+ uiUpdater . updateState ( uiState ) ;
157
+ break ;
158
+ case VaultState . CLOSED :
159
+ uiUpdater . finish ( uiState ) ;
160
+ break ;
161
+ default :
162
+ throw Error ( `unreachable vaultState: ${ vaultState } ` ) ;
140
163
}
141
164
}
142
165
143
166
function liquidated ( newDebt ) {
144
167
runDebt = newDebt ;
145
- active = false ;
168
+ vaultState = VaultState . CLOSED ;
169
+ updateUiState ( ) ;
170
+ }
171
+
172
+ function liquidating ( ) {
173
+ vaultState = VaultState . LIQUIDATING ;
146
174
updateUiState ( ) ;
147
175
}
148
176
@@ -167,20 +195,24 @@ export function makeVaultKit(
167
195
assert ( AmountMath . isGTE ( runReturned , runDebt ) ) ;
168
196
169
197
// Return any overpayment
170
- vaultSeat . incrementBy ( seat . decrementBy ( { RUN : runDebt } ) ) ;
198
+
199
+ const { zcfSeat : burnSeat } = zcf . makeEmptySeatKit ( ) ;
200
+ burnSeat . incrementBy ( seat . decrementBy ( { RUN : runDebt } ) ) ;
171
201
seat . incrementBy (
172
202
vaultSeat . decrementBy ( { Collateral : getCollateralAllocated ( vaultSeat ) } ) ,
173
203
) ;
174
- zcf . reallocate ( seat , vaultSeat ) ;
175
-
204
+ zcf . reallocate ( seat , vaultSeat , burnSeat ) ;
205
+ runMint . burnLosses ( { RUN : runDebt } , burnSeat ) ;
176
206
seat . exit ( ) ;
177
- active = false ;
207
+ burnSeat . exit ( ) ;
208
+ vaultState = VaultState . CLOSED ;
178
209
updateUiState ( ) ;
179
210
180
- runMint . burnLosses ( { RUN : runDebt } , vaultSeat ) ;
181
211
runDebt = AmountMath . makeEmpty ( runBrand ) ;
182
212
assertVaultHoldsNoRun ( ) ;
183
213
vaultSeat . exit ( ) ;
214
+ liquidationZcfSeat . exit ( ) ;
215
+ liquidationPromiseKit . resolve ( 'Closed' ) ;
184
216
185
217
return 'your loan is closed, thank you for your business' ;
186
218
}
@@ -397,8 +429,6 @@ export function makeVaultKit(
397
429
want : { RUN : wantedRun } ,
398
430
} = seat . getProposal ( ) ;
399
431
400
- const collateralPayoutP = E ( userSeat ) . getPayouts ( ) ;
401
-
402
432
// todo trigger process() check right away, in case the price dropped while we ran
403
433
404
434
const fee = multiplyBy ( wantedRun , manager . getLoanFee ( ) ) ;
@@ -419,7 +449,7 @@ export function makeVaultKit(
419
449
420
450
updateUiState ( ) ;
421
451
422
- return { notifier, collateralPayoutP } ;
452
+ return { notifier } ;
423
453
}
424
454
425
455
function accrueInterestAndAddToPool ( currentTime ) {
@@ -453,13 +483,18 @@ export function makeVaultKit(
453
483
// for status/debugging
454
484
getCollateralAmount,
455
485
getDebtAmount,
486
+ getLiquidationSeat : ( ) => liquidationSeat ,
487
+ getLiquidationPromise : ( ) => liquidationPromiseKit . promise ,
456
488
} ) ;
457
489
458
490
return harden ( {
459
491
vault,
460
492
openLoan,
461
493
accrueInterestAndAddToPool,
462
494
vaultSeat,
495
+ liquidating,
463
496
liquidated,
497
+ liquidationPromiseKit,
498
+ liquidationZcfSeat,
464
499
} ) ;
465
500
}
0 commit comments