@sovryn-zero/lib-base 0.1.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 (84) hide show
  1. package/.eslintrc.json +17 -0
  2. package/.mocharc.yml +1 -0
  3. package/LICENSE +905 -0
  4. package/README.md +23 -0
  5. package/api-extractor.json +4 -0
  6. package/dist/index.d.ts +14 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +26 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/src/Decimal.d.ts +89 -0
  11. package/dist/src/Decimal.d.ts.map +1 -0
  12. package/dist/src/Decimal.js +361 -0
  13. package/dist/src/Decimal.js.map +1 -0
  14. package/dist/src/Fees.d.ts +82 -0
  15. package/dist/src/Fees.d.ts.map +1 -0
  16. package/dist/src/Fees.js +123 -0
  17. package/dist/src/Fees.js.map +1 -0
  18. package/dist/src/LiquityStore.d.ts +209 -0
  19. package/dist/src/LiquityStore.d.ts.map +1 -0
  20. package/dist/src/LiquityStore.js +209 -0
  21. package/dist/src/LiquityStore.js.map +1 -0
  22. package/dist/src/ObservableLiquity.d.ts +15 -0
  23. package/dist/src/ObservableLiquity.d.ts.map +1 -0
  24. package/dist/src/ObservableLiquity.js +3 -0
  25. package/dist/src/ObservableLiquity.js.map +1 -0
  26. package/dist/src/PopulatableLiquity.d.ts +125 -0
  27. package/dist/src/PopulatableLiquity.d.ts.map +1 -0
  28. package/dist/src/PopulatableLiquity.js +3 -0
  29. package/dist/src/PopulatableLiquity.js.map +1 -0
  30. package/dist/src/ReadableLiquity.d.ts +156 -0
  31. package/dist/src/ReadableLiquity.d.ts.map +1 -0
  32. package/dist/src/ReadableLiquity.js +3 -0
  33. package/dist/src/ReadableLiquity.js.map +1 -0
  34. package/dist/src/SendableLiquity.d.ts +156 -0
  35. package/dist/src/SendableLiquity.d.ts.map +1 -0
  36. package/dist/src/SendableLiquity.js +20 -0
  37. package/dist/src/SendableLiquity.js.map +1 -0
  38. package/dist/src/StabilityDeposit.d.ts +59 -0
  39. package/dist/src/StabilityDeposit.d.ts.map +1 -0
  40. package/dist/src/StabilityDeposit.js +80 -0
  41. package/dist/src/StabilityDeposit.js.map +1 -0
  42. package/dist/src/TransactableLiquity.d.ts +414 -0
  43. package/dist/src/TransactableLiquity.d.ts.map +1 -0
  44. package/dist/src/TransactableLiquity.js +18 -0
  45. package/dist/src/TransactableLiquity.js.map +1 -0
  46. package/dist/src/Trove.d.ts +367 -0
  47. package/dist/src/Trove.d.ts.map +1 -0
  48. package/dist/src/Trove.js +423 -0
  49. package/dist/src/Trove.js.map +1 -0
  50. package/dist/src/ZEROStake.d.ts +52 -0
  51. package/dist/src/ZEROStake.d.ts.map +1 -0
  52. package/dist/src/ZEROStake.js +74 -0
  53. package/dist/src/ZEROStake.js.map +1 -0
  54. package/dist/src/_CachedReadableLiquity.d.ts +55 -0
  55. package/dist/src/_CachedReadableLiquity.d.ts.map +1 -0
  56. package/dist/src/_CachedReadableLiquity.js +93 -0
  57. package/dist/src/_CachedReadableLiquity.js.map +1 -0
  58. package/dist/src/constants.d.ts +61 -0
  59. package/dist/src/constants.d.ts.map +1 -0
  60. package/dist/src/constants.js +64 -0
  61. package/dist/src/constants.js.map +1 -0
  62. package/dist/tsdoc-metadata.json +11 -0
  63. package/etc/lib-base.api.md +788 -0
  64. package/index.ts +13 -0
  65. package/package.json +52 -0
  66. package/src/Decimal.ts +456 -0
  67. package/src/Fees.ts +160 -0
  68. package/src/LiquityStore.ts +563 -0
  69. package/src/ObservableLiquity.ts +32 -0
  70. package/src/PopulatableLiquity.ts +280 -0
  71. package/src/ReadableLiquity.ts +175 -0
  72. package/src/SendableLiquity.ts +251 -0
  73. package/src/StabilityDeposit.ts +126 -0
  74. package/src/TransactableLiquity.ts +471 -0
  75. package/src/Trove.ts +824 -0
  76. package/src/ZEROStake.ts +99 -0
  77. package/src/_CachedReadableLiquity.ts +186 -0
  78. package/src/constants.ts +68 -0
  79. package/test/Decimal.test.ts +212 -0
  80. package/test/StabilityDeposit.test.ts +30 -0
  81. package/test/Trove.test.ts +143 -0
  82. package/test/ZEROStake.test.ts +24 -0
  83. package/tsconfig.dist.json +8 -0
  84. package/tsconfig.json +5 -0
@@ -0,0 +1,563 @@
1
+ import assert from "assert";
2
+
3
+ import { Decimal } from "./Decimal";
4
+ import { StabilityDeposit } from "./StabilityDeposit";
5
+ import { Trove, TroveWithPendingRedistribution, UserTrove } from "./Trove";
6
+ import { Fees } from "./Fees";
7
+ import { ZEROStake } from "./ZEROStake";
8
+ import { FrontendStatus } from "./ReadableLiquity";
9
+
10
+ /**
11
+ * State variables read from the blockchain.
12
+ *
13
+ * @public
14
+ */
15
+ export interface LiquityStoreBaseState {
16
+ /** Status of currently used frontend. */
17
+ frontend: FrontendStatus;
18
+
19
+ /** Status of user's own frontend. */
20
+ ownFrontend: FrontendStatus;
21
+
22
+ /** Number of Troves that are currently open. */
23
+ numberOfTroves: number;
24
+
25
+ /** User's native currency balance (e.g. Ether). */
26
+ accountBalance: Decimal;
27
+
28
+ /** User's ZUSD token balance. */
29
+ zusdBalance: Decimal;
30
+
31
+ /** User's NUE token balance. */
32
+ nueBalance: Decimal;
33
+
34
+ /** User's ZERO token balance. */
35
+ zeroBalance: Decimal;
36
+
37
+ /**
38
+ * Amount of leftover collateral available for withdrawal to the user.
39
+ *
40
+ * @remarks
41
+ * See {@link ReadableLiquity.getCollateralSurplusBalance | getCollateralSurplusBalance()} for
42
+ * more information.
43
+ */
44
+ collateralSurplusBalance: Decimal;
45
+
46
+ /** Current price of the native currency (e.g. Ether) in USD. */
47
+ price: Decimal;
48
+
49
+ /** Total amount of ZUSD currently deposited in the Stability Pool. */
50
+ zusdInStabilityPool: Decimal;
51
+
52
+ /** Total collateral and debt in the Zero system. */
53
+ total: Trove;
54
+
55
+ /**
56
+ * Total collateral and debt per stake that has been liquidated through redistribution.
57
+ *
58
+ * @remarks
59
+ * Needed when dealing with instances of {@link TroveWithPendingRedistribution}.
60
+ */
61
+ totalRedistributed: Trove;
62
+
63
+ /**
64
+ * User's Trove in its state after the last direct modification.
65
+ *
66
+ * @remarks
67
+ * The current state of the user's Trove can be found as
68
+ * {@link LiquityStoreDerivedState.trove | trove}.
69
+ */
70
+ troveBeforeRedistribution: TroveWithPendingRedistribution;
71
+
72
+ /** User's stability deposit. */
73
+ stabilityDeposit: StabilityDeposit;
74
+
75
+ /** Remaining ZERO that will be collectively rewarded to stability depositors. */
76
+ remainingStabilityPoolZEROReward: Decimal;
77
+
78
+ /** @internal */
79
+ _feesInNormalMode: Fees;
80
+
81
+ /** User's ZERO stake. */
82
+ zeroStake: ZEROStake;
83
+
84
+ /** Total amount of ZERO currently staked. */
85
+ totalStakedZERO: Decimal;
86
+
87
+ /** @internal */
88
+ _riskiestTroveBeforeRedistribution: TroveWithPendingRedistribution;
89
+ }
90
+
91
+ /**
92
+ * State variables derived from {@link LiquityStoreBaseState}.
93
+ *
94
+ * @public
95
+ */
96
+ export interface LiquityStoreDerivedState {
97
+ /** Current state of user's Trove */
98
+ trove: UserTrove;
99
+
100
+ /** Calculator for current fees. */
101
+ fees: Fees;
102
+
103
+ /**
104
+ * Current borrowing rate.
105
+ *
106
+ * @remarks
107
+ * A value between 0 and 1.
108
+ *
109
+ * @example
110
+ * For example a value of 0.01 amounts to a borrowing fee of 1% of the borrowed amount.
111
+ */
112
+ borrowingRate: Decimal;
113
+
114
+ /**
115
+ * Current redemption rate.
116
+ *
117
+ * @remarks
118
+ * Note that the actual rate paid by a redemption transaction will depend on the amount of ZUSD
119
+ * being redeemed.
120
+ *
121
+ * Use {@link Fees.redemptionRate} to calculate a precise redemption rate.
122
+ */
123
+ redemptionRate: Decimal;
124
+
125
+ /**
126
+ * Whether there are any Troves with collateral ratio below the
127
+ * {@link MINIMUM_COLLATERAL_RATIO | minimum}.
128
+ */
129
+ haveUndercollateralizedTroves: boolean;
130
+ }
131
+
132
+ /**
133
+ * Type of {@link LiquityStore}'s {@link LiquityStore.state | state}.
134
+ *
135
+ * @remarks
136
+ * It combines all properties of {@link LiquityStoreBaseState} and {@link LiquityStoreDerivedState}
137
+ * with optional extra state added by the particular `LiquityStore` implementation.
138
+ *
139
+ * The type parameter `T` may be used to type the extra state.
140
+ *
141
+ * @public
142
+ */
143
+ export type LiquityStoreState<T = unknown> = LiquityStoreBaseState & LiquityStoreDerivedState & T;
144
+
145
+ /**
146
+ * Parameters passed to {@link LiquityStore} listeners.
147
+ *
148
+ * @remarks
149
+ * Use the {@link LiquityStore.subscribe | subscribe()} function to register a listener.
150
+
151
+ * @public
152
+ */
153
+ export interface LiquityStoreListenerParams<T = unknown> {
154
+ /** The entire previous state. */
155
+ newState: LiquityStoreState<T>;
156
+
157
+ /** The entire new state. */
158
+ oldState: LiquityStoreState<T>;
159
+
160
+ /** Only the state variables that have changed. */
161
+ stateChange: Partial<LiquityStoreState<T>>;
162
+ }
163
+
164
+ const strictEquals = <T>(a: T, b: T) => a === b;
165
+ const eq = <T extends { eq(that: T): boolean }>(a: T, b: T) => a.eq(b);
166
+ const equals = <T extends { equals(that: T): boolean }>(a: T, b: T) => a.equals(b);
167
+
168
+ const frontendStatusEquals = (a: FrontendStatus, b: FrontendStatus) =>
169
+ a.status === "unregistered"
170
+ ? b.status === "unregistered"
171
+ : b.status === "registered" && a.kickbackRate.eq(b.kickbackRate);
172
+
173
+ const showFrontendStatus = (x: FrontendStatus) =>
174
+ x.status === "unregistered"
175
+ ? '{ status: "unregistered" }'
176
+ : `{ status: "registered", kickbackRate: ${x.kickbackRate} }`;
177
+
178
+ const wrap = <A extends unknown[], R>(f: (...args: A) => R) => (...args: A) => f(...args);
179
+
180
+ const difference = <T>(a: T, b: T) =>
181
+ Object.fromEntries(
182
+ Object.entries(a).filter(([key, value]) => value !== (b as Record<string, unknown>)[key])
183
+ ) as Partial<T>;
184
+
185
+ /**
186
+ * Abstract base class of Zero data store implementations.
187
+ *
188
+ * @remarks
189
+ * The type parameter `T` may be used to type extra state added to {@link LiquityStoreState} by the
190
+ * subclass.
191
+ *
192
+ * Implemented by {@link @sovryn-zero/lib-ethers#BlockPolledLiquityStore}.
193
+ *
194
+ * @public
195
+ */
196
+ export abstract class LiquityStore<T = unknown> {
197
+ /** Turn console logging on/off. */
198
+ logging = false;
199
+
200
+ /**
201
+ * Called after the state is fetched for the first time.
202
+ *
203
+ * @remarks
204
+ * See {@link LiquityStore.start | start()}.
205
+ */
206
+ onLoaded?: () => void;
207
+
208
+ /** @internal */
209
+ protected _loaded = false;
210
+
211
+ private _baseState?: LiquityStoreBaseState;
212
+ private _derivedState?: LiquityStoreDerivedState;
213
+ private _extraState?: T;
214
+
215
+ private _updateTimeoutId: ReturnType<typeof setTimeout> | undefined;
216
+ private _listeners = new Set<(params: LiquityStoreListenerParams<T>) => void>();
217
+
218
+ /**
219
+ * The current store state.
220
+ *
221
+ * @remarks
222
+ * Should not be accessed before the store is loaded. Assign a function to
223
+ * {@link LiquityStore.onLoaded | onLoaded} to get a callback when this happens.
224
+ *
225
+ * See {@link LiquityStoreState} for the list of properties returned.
226
+ */
227
+ get state(): LiquityStoreState<T> {
228
+ return Object.assign({}, this._baseState, this._derivedState, this._extraState);
229
+ }
230
+
231
+ /** @internal */
232
+ protected abstract _doStart(): () => void;
233
+
234
+ /**
235
+ * Start monitoring the blockchain for Zero state changes.
236
+ *
237
+ * @remarks
238
+ * The {@link LiquityStore.onLoaded | onLoaded} callback will be called after the state is fetched
239
+ * for the first time.
240
+ *
241
+ * Use the {@link LiquityStore.subscribe | subscribe()} function to register listeners.
242
+ *
243
+ * @returns Function to stop the monitoring.
244
+ */
245
+ start(): () => void {
246
+ const doStop = this._doStart();
247
+
248
+ return () => {
249
+ doStop();
250
+
251
+ this._cancelUpdateIfScheduled();
252
+ };
253
+ }
254
+
255
+ private _cancelUpdateIfScheduled() {
256
+ if (this._updateTimeoutId !== undefined) {
257
+ clearTimeout(this._updateTimeoutId);
258
+ }
259
+ }
260
+
261
+ private _scheduleUpdate() {
262
+ this._cancelUpdateIfScheduled();
263
+
264
+ this._updateTimeoutId = setTimeout(() => {
265
+ this._updateTimeoutId = undefined;
266
+ this._update();
267
+ }, 30000);
268
+ }
269
+
270
+ private _logUpdate<U>(name: string, next: U, show?: (next: U) => string): U {
271
+ if (this.logging) {
272
+ console.log(`${name} updated to ${show ? show(next) : next}`);
273
+ }
274
+
275
+ return next;
276
+ }
277
+
278
+ private _updateIfChanged<U>(
279
+ equals: (a: U, b: U) => boolean,
280
+ name: string,
281
+ prev: U,
282
+ next?: U,
283
+ show?: (next: U) => string
284
+ ): U {
285
+ return next !== undefined && !equals(prev, next) ? this._logUpdate(name, next, show) : prev;
286
+ }
287
+
288
+ private _silentlyUpdateIfChanged<U>(equals: (a: U, b: U) => boolean, prev: U, next?: U): U {
289
+ return next !== undefined && !equals(prev, next) ? next : prev;
290
+ }
291
+
292
+ private _updateFees(name: string, prev: Fees, next?: Fees): Fees {
293
+ if (next && !next.equals(prev)) {
294
+ // Filter out fee update spam that happens on every new block by only logging when string
295
+ // representation changes.
296
+ if (`${next}` !== `${prev}`) {
297
+ this._logUpdate(name, next);
298
+ }
299
+ return next;
300
+ } else {
301
+ return prev;
302
+ }
303
+ }
304
+
305
+ private _reduce(
306
+ baseState: LiquityStoreBaseState,
307
+ baseStateUpdate: Partial<LiquityStoreBaseState>
308
+ ): LiquityStoreBaseState {
309
+ return {
310
+ frontend: this._updateIfChanged(
311
+ frontendStatusEquals,
312
+ "frontend",
313
+ baseState.frontend,
314
+ baseStateUpdate.frontend,
315
+ showFrontendStatus
316
+ ),
317
+
318
+ ownFrontend: this._updateIfChanged(
319
+ frontendStatusEquals,
320
+ "ownFrontend",
321
+ baseState.ownFrontend,
322
+ baseStateUpdate.ownFrontend,
323
+ showFrontendStatus
324
+ ),
325
+
326
+ numberOfTroves: this._updateIfChanged(
327
+ strictEquals,
328
+ "numberOfTroves",
329
+ baseState.numberOfTroves,
330
+ baseStateUpdate.numberOfTroves
331
+ ),
332
+
333
+ accountBalance: this._updateIfChanged(
334
+ eq,
335
+ "accountBalance",
336
+ baseState.accountBalance,
337
+ baseStateUpdate.accountBalance
338
+ ),
339
+
340
+ zusdBalance: this._updateIfChanged(
341
+ eq,
342
+ "zusdBalance",
343
+ baseState.zusdBalance,
344
+ baseStateUpdate.zusdBalance
345
+ ),
346
+
347
+ nueBalance: this._updateIfChanged(
348
+ eq,
349
+ "nueBalance",
350
+ baseState.nueBalance,
351
+ baseStateUpdate.nueBalance
352
+ ),
353
+
354
+ zeroBalance: this._updateIfChanged(
355
+ eq,
356
+ "zeroBalance",
357
+ baseState.zeroBalance,
358
+ baseStateUpdate.zeroBalance
359
+ ),
360
+
361
+ collateralSurplusBalance: this._updateIfChanged(
362
+ eq,
363
+ "collateralSurplusBalance",
364
+ baseState.collateralSurplusBalance,
365
+ baseStateUpdate.collateralSurplusBalance
366
+ ),
367
+
368
+ price: this._updateIfChanged(eq, "price", baseState.price, baseStateUpdate.price),
369
+
370
+ zusdInStabilityPool: this._updateIfChanged(
371
+ eq,
372
+ "zusdInStabilityPool",
373
+ baseState.zusdInStabilityPool,
374
+ baseStateUpdate.zusdInStabilityPool
375
+ ),
376
+
377
+ total: this._updateIfChanged(equals, "total", baseState.total, baseStateUpdate.total),
378
+
379
+ totalRedistributed: this._updateIfChanged(
380
+ equals,
381
+ "totalRedistributed",
382
+ baseState.totalRedistributed,
383
+ baseStateUpdate.totalRedistributed
384
+ ),
385
+
386
+ troveBeforeRedistribution: this._updateIfChanged(
387
+ equals,
388
+ "troveBeforeRedistribution",
389
+ baseState.troveBeforeRedistribution,
390
+ baseStateUpdate.troveBeforeRedistribution
391
+ ),
392
+
393
+ stabilityDeposit: this._updateIfChanged(
394
+ equals,
395
+ "stabilityDeposit",
396
+ baseState.stabilityDeposit,
397
+ baseStateUpdate.stabilityDeposit
398
+ ),
399
+
400
+ remainingStabilityPoolZEROReward: this._silentlyUpdateIfChanged(
401
+ eq,
402
+ baseState.remainingStabilityPoolZEROReward,
403
+ baseStateUpdate.remainingStabilityPoolZEROReward
404
+ ),
405
+
406
+ _feesInNormalMode: this._silentlyUpdateIfChanged(
407
+ equals,
408
+ baseState._feesInNormalMode,
409
+ baseStateUpdate._feesInNormalMode
410
+ ),
411
+
412
+ zeroStake: this._updateIfChanged(
413
+ equals,
414
+ "zeroStake",
415
+ baseState.zeroStake,
416
+ baseStateUpdate.zeroStake
417
+ ),
418
+
419
+ totalStakedZERO: this._updateIfChanged(
420
+ eq,
421
+ "totalStakedZERO",
422
+ baseState.totalStakedZERO,
423
+ baseStateUpdate.totalStakedZERO
424
+ ),
425
+
426
+ _riskiestTroveBeforeRedistribution: this._silentlyUpdateIfChanged(
427
+ equals,
428
+ baseState._riskiestTroveBeforeRedistribution,
429
+ baseStateUpdate._riskiestTroveBeforeRedistribution
430
+ )
431
+ };
432
+ }
433
+
434
+ private _derive({
435
+ troveBeforeRedistribution,
436
+ totalRedistributed,
437
+ _feesInNormalMode,
438
+ total,
439
+ price,
440
+ _riskiestTroveBeforeRedistribution
441
+ }: LiquityStoreBaseState): LiquityStoreDerivedState {
442
+ const fees = _feesInNormalMode._setRecoveryMode(total.collateralRatioIsBelowCritical(price));
443
+
444
+ return {
445
+ trove: troveBeforeRedistribution.applyRedistribution(totalRedistributed),
446
+ fees,
447
+ borrowingRate: fees.borrowingRate(),
448
+ redemptionRate: fees.redemptionRate(),
449
+ haveUndercollateralizedTroves: _riskiestTroveBeforeRedistribution
450
+ .applyRedistribution(totalRedistributed)
451
+ .collateralRatioIsBelowMinimum(price)
452
+ };
453
+ }
454
+
455
+ private _reduceDerived(
456
+ derivedState: LiquityStoreDerivedState,
457
+ derivedStateUpdate: LiquityStoreDerivedState
458
+ ): LiquityStoreDerivedState {
459
+ return {
460
+ fees: this._updateFees("fees", derivedState.fees, derivedStateUpdate.fees),
461
+
462
+ trove: this._updateIfChanged(equals, "trove", derivedState.trove, derivedStateUpdate.trove),
463
+
464
+ borrowingRate: this._silentlyUpdateIfChanged(
465
+ eq,
466
+ derivedState.borrowingRate,
467
+ derivedStateUpdate.borrowingRate
468
+ ),
469
+
470
+ redemptionRate: this._silentlyUpdateIfChanged(
471
+ eq,
472
+ derivedState.redemptionRate,
473
+ derivedStateUpdate.redemptionRate
474
+ ),
475
+
476
+ haveUndercollateralizedTroves: this._updateIfChanged(
477
+ strictEquals,
478
+ "haveUndercollateralizedTroves",
479
+ derivedState.haveUndercollateralizedTroves,
480
+ derivedStateUpdate.haveUndercollateralizedTroves
481
+ )
482
+ };
483
+ }
484
+
485
+ /** @internal */
486
+ protected abstract _reduceExtra(extraState: T, extraStateUpdate: Partial<T>): T;
487
+
488
+ private _notify(params: LiquityStoreListenerParams<T>) {
489
+ // Iterate on a copy of `_listeners`, to avoid notifying any new listeners subscribed by
490
+ // existing listeners, as that could result in infinite loops.
491
+ //
492
+ // Before calling a listener from our copy of `_listeners`, check if it has been removed from
493
+ // the original set. This way we avoid calling listeners that have already been unsubscribed
494
+ // by an earlier listener callback.
495
+ [...this._listeners].forEach(listener => {
496
+ if (this._listeners.has(listener)) {
497
+ listener(params);
498
+ }
499
+ });
500
+ }
501
+
502
+ /**
503
+ * Register a state change listener.
504
+ *
505
+ * @param listener - Function that will be called whenever state changes.
506
+ * @returns Function to unregister this listener.
507
+ */
508
+ subscribe(listener: (params: LiquityStoreListenerParams<T>) => void): () => void {
509
+ const uniqueListener = wrap(listener);
510
+
511
+ this._listeners.add(uniqueListener);
512
+
513
+ return () => {
514
+ this._listeners.delete(uniqueListener);
515
+ };
516
+ }
517
+
518
+ /** @internal */
519
+ protected _load(baseState: LiquityStoreBaseState, extraState?: T): void {
520
+ assert(!this._loaded);
521
+
522
+ this._baseState = baseState;
523
+ this._derivedState = this._derive(baseState);
524
+ this._extraState = extraState;
525
+ this._loaded = true;
526
+
527
+ this._scheduleUpdate();
528
+
529
+ if (this.onLoaded) {
530
+ this.onLoaded();
531
+ }
532
+ }
533
+
534
+ /** @internal */
535
+ protected _update(
536
+ baseStateUpdate?: Partial<LiquityStoreBaseState>,
537
+ extraStateUpdate?: Partial<T>
538
+ ): void {
539
+ assert(this._baseState && this._derivedState);
540
+
541
+ const oldState = this.state;
542
+
543
+ if (baseStateUpdate) {
544
+ this._baseState = this._reduce(this._baseState, baseStateUpdate);
545
+ }
546
+
547
+ // Always running this lets us derive state based on passage of time, like baseRate decay
548
+ this._derivedState = this._reduceDerived(this._derivedState, this._derive(this._baseState));
549
+
550
+ if (extraStateUpdate) {
551
+ assert(this._extraState);
552
+ this._extraState = this._reduceExtra(this._extraState, extraStateUpdate);
553
+ }
554
+
555
+ this._scheduleUpdate();
556
+
557
+ this._notify({
558
+ newState: this.state,
559
+ oldState,
560
+ stateChange: difference(this.state, oldState)
561
+ });
562
+ }
563
+ }
@@ -0,0 +1,32 @@
1
+ import { Decimal } from "./Decimal";
2
+ import { Trove, TroveWithPendingRedistribution } from "./Trove";
3
+ import { StabilityDeposit } from "./StabilityDeposit";
4
+
5
+ /** @alpha */
6
+ export interface ObservableLiquity {
7
+ watchTotalRedistributed(
8
+ onTotalRedistributedChanged: (totalRedistributed: Trove) => void
9
+ ): () => void;
10
+
11
+ watchTroveWithoutRewards(
12
+ onTroveChanged: (trove: TroveWithPendingRedistribution) => void,
13
+ address?: string
14
+ ): () => void;
15
+
16
+ watchNumberOfTroves(onNumberOfTrovesChanged: (numberOfTroves: number) => void): () => void;
17
+
18
+ watchPrice(onPriceChanged: (price: Decimal) => void): () => void;
19
+
20
+ watchTotal(onTotalChanged: (total: Trove) => void): () => void;
21
+
22
+ watchStabilityDeposit(
23
+ onStabilityDepositChanged: (stabilityDeposit: StabilityDeposit) => void,
24
+ address?: string
25
+ ): () => void;
26
+
27
+ watchZUSDInStabilityPool(
28
+ onZUSDInStabilityPoolChanged: (zusdInStabilityPool: Decimal) => void
29
+ ): () => void;
30
+
31
+ watchZUSDBalance(onZUSDBalanceChanged: (balance: Decimal) => void, address?: string): () => void;
32
+ }