@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,99 @@
1
+ import { Decimal, Decimalish } from "./Decimal";
2
+
3
+ /**
4
+ * Represents the change between two states of an ZERO Stake.
5
+ *
6
+ * @public
7
+ */
8
+ export type ZEROStakeChange<T> =
9
+ | { stakeZERO: T; unstakeZERO?: undefined }
10
+ | { stakeZERO?: undefined; unstakeZERO: T; unstakeAllZERO: boolean };
11
+
12
+ /**
13
+ * Represents a user's ZERO stake and accrued gains.
14
+ *
15
+ * @remarks
16
+ * Returned by the {@link ReadableLiquity.getZEROStake | getZEROStake()} function.
17
+
18
+ * @public
19
+ */
20
+ export class ZEROStake {
21
+ /** The amount of ZERO that's staked. */
22
+ readonly stakedZERO: Decimal;
23
+
24
+ /** Collateral gain available to withdraw. */
25
+ readonly collateralGain: Decimal;
26
+
27
+ /** ZUSD gain available to withdraw. */
28
+ readonly zusdGain: Decimal;
29
+
30
+ /** @internal */
31
+ constructor(stakedZERO = Decimal.ZERO, collateralGain = Decimal.ZERO, zusdGain = Decimal.ZERO) {
32
+ this.stakedZERO = stakedZERO;
33
+ this.collateralGain = collateralGain;
34
+ this.zusdGain = zusdGain;
35
+ }
36
+
37
+ get isEmpty(): boolean {
38
+ return this.stakedZERO.isZero && this.collateralGain.isZero && this.zusdGain.isZero;
39
+ }
40
+
41
+ /** @internal */
42
+ toString(): string {
43
+ return (
44
+ `{ stakedZERO: ${this.stakedZERO}` +
45
+ `, collateralGain: ${this.collateralGain}` +
46
+ `, zusdGain: ${this.zusdGain} }`
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Compare to another instance of `ZEROStake`.
52
+ */
53
+ equals(that: ZEROStake): boolean {
54
+ return (
55
+ this.stakedZERO.eq(that.stakedZERO) &&
56
+ this.collateralGain.eq(that.collateralGain) &&
57
+ this.zusdGain.eq(that.zusdGain)
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Calculate the difference between this `ZEROStake` and `thatStakedZERO`.
63
+ *
64
+ * @returns An object representing the change, or `undefined` if the staked amounts are equal.
65
+ */
66
+ whatChanged(thatStakedZERO: Decimalish): ZEROStakeChange<Decimal> | undefined {
67
+ thatStakedZERO = Decimal.from(thatStakedZERO);
68
+
69
+ if (thatStakedZERO.lt(this.stakedZERO)) {
70
+ return {
71
+ unstakeZERO: this.stakedZERO.sub(thatStakedZERO),
72
+ unstakeAllZERO: thatStakedZERO.isZero
73
+ };
74
+ }
75
+
76
+ if (thatStakedZERO.gt(this.stakedZERO)) {
77
+ return { stakeZERO: thatStakedZERO.sub(this.stakedZERO) };
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Apply a {@link ZEROStakeChange} to this `ZEROStake`.
83
+ *
84
+ * @returns The new staked ZERO amount.
85
+ */
86
+ apply(change: ZEROStakeChange<Decimalish> | undefined): Decimal {
87
+ if (!change) {
88
+ return this.stakedZERO;
89
+ }
90
+
91
+ if (change.unstakeZERO !== undefined) {
92
+ return change.unstakeAllZERO || this.stakedZERO.lte(change.unstakeZERO)
93
+ ? Decimal.ZERO
94
+ : this.stakedZERO.sub(change.unstakeZERO);
95
+ } else {
96
+ return this.stakedZERO.add(change.stakeZERO);
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,186 @@
1
+ import { Decimal } from "./Decimal";
2
+ import { Fees } from "./Fees";
3
+ import { ZEROStake } from "./ZEROStake";
4
+ import { StabilityDeposit } from "./StabilityDeposit";
5
+ import { Trove, TroveWithPendingRedistribution, UserTrove } from "./Trove";
6
+ import { FrontendStatus, ReadableLiquity, TroveListingParams } from "./ReadableLiquity";
7
+
8
+ /** @internal */
9
+ export type _ReadableLiquityWithExtraParamsBase<T extends unknown[]> = {
10
+ [P in keyof ReadableLiquity]: ReadableLiquity[P] extends (...params: infer A) => infer R
11
+ ? (...params: [...originalParams: A, ...extraParams: T]) => R
12
+ : never;
13
+ };
14
+
15
+ /** @internal */
16
+ export type _LiquityReadCacheBase<T extends unknown[]> = {
17
+ [P in keyof ReadableLiquity]: ReadableLiquity[P] extends (...args: infer A) => Promise<infer R>
18
+ ? (...params: [...originalParams: A, ...extraParams: T]) => R | undefined
19
+ : never;
20
+ };
21
+
22
+ // Overloads get lost in the mapping, so we need to define them again...
23
+
24
+ /** @internal */
25
+ export interface _ReadableLiquityWithExtraParams<T extends unknown[]>
26
+ extends _ReadableLiquityWithExtraParamsBase<T> {
27
+ getTroves(
28
+ params: TroveListingParams & { beforeRedistribution: true },
29
+ ...extraParams: T
30
+ ): Promise<TroveWithPendingRedistribution[]>;
31
+
32
+ getTroves(params: TroveListingParams, ...extraParams: T): Promise<UserTrove[]>;
33
+ }
34
+
35
+ /** @internal */
36
+ export interface _LiquityReadCache<T extends unknown[]> extends _LiquityReadCacheBase<T> {
37
+ getTroves(
38
+ params: TroveListingParams & { beforeRedistribution: true },
39
+ ...extraParams: T
40
+ ): TroveWithPendingRedistribution[] | undefined;
41
+
42
+ getTroves(params: TroveListingParams, ...extraParams: T): UserTrove[] | undefined;
43
+ }
44
+
45
+ /** @internal */
46
+ export class _CachedReadableLiquity<T extends unknown[]>
47
+ implements _ReadableLiquityWithExtraParams<T> {
48
+ private _readable: _ReadableLiquityWithExtraParams<T>;
49
+ private _cache: _LiquityReadCache<T>;
50
+
51
+ constructor(readable: _ReadableLiquityWithExtraParams<T>, cache: _LiquityReadCache<T>) {
52
+ this._readable = readable;
53
+ this._cache = cache;
54
+ }
55
+
56
+ async getTotalRedistributed(...extraParams: T): Promise<Trove> {
57
+ return (
58
+ this._cache.getTotalRedistributed(...extraParams) ??
59
+ this._readable.getTotalRedistributed(...extraParams)
60
+ );
61
+ }
62
+
63
+ async getTroveBeforeRedistribution(
64
+ address?: string,
65
+ ...extraParams: T
66
+ ): Promise<TroveWithPendingRedistribution> {
67
+ return (
68
+ this._cache.getTroveBeforeRedistribution(address, ...extraParams) ??
69
+ this._readable.getTroveBeforeRedistribution(address, ...extraParams)
70
+ );
71
+ }
72
+
73
+ async getTrove(address?: string, ...extraParams: T): Promise<UserTrove> {
74
+ const [troveBeforeRedistribution, totalRedistributed] = await Promise.all([
75
+ this.getTroveBeforeRedistribution(address, ...extraParams),
76
+ this.getTotalRedistributed(...extraParams)
77
+ ]);
78
+
79
+ return troveBeforeRedistribution.applyRedistribution(totalRedistributed);
80
+ }
81
+
82
+ async getNumberOfTroves(...extraParams: T): Promise<number> {
83
+ return (
84
+ this._cache.getNumberOfTroves(...extraParams) ??
85
+ this._readable.getNumberOfTroves(...extraParams)
86
+ );
87
+ }
88
+
89
+ async getPrice(...extraParams: T): Promise<Decimal> {
90
+ return this._cache.getPrice(...extraParams) ?? this._readable.getPrice(...extraParams);
91
+ }
92
+
93
+ async getTotal(...extraParams: T): Promise<Trove> {
94
+ return this._cache.getTotal(...extraParams) ?? this._readable.getTotal(...extraParams);
95
+ }
96
+
97
+ async getStabilityDeposit(address?: string, ...extraParams: T): Promise<StabilityDeposit> {
98
+ return (
99
+ this._cache.getStabilityDeposit(address, ...extraParams) ??
100
+ this._readable.getStabilityDeposit(address, ...extraParams)
101
+ );
102
+ }
103
+
104
+ async getRemainingStabilityPoolZEROReward(...extraParams: T): Promise<Decimal> {
105
+ return (
106
+ this._cache.getRemainingStabilityPoolZEROReward(...extraParams) ??
107
+ this._readable.getRemainingStabilityPoolZEROReward(...extraParams)
108
+ );
109
+ }
110
+
111
+ async getZUSDInStabilityPool(...extraParams: T): Promise<Decimal> {
112
+ return (
113
+ this._cache.getZUSDInStabilityPool(...extraParams) ??
114
+ this._readable.getZUSDInStabilityPool(...extraParams)
115
+ );
116
+ }
117
+
118
+ async getZUSDBalance(address?: string, ...extraParams: T): Promise<Decimal> {
119
+ return (
120
+ this._cache.getZUSDBalance(address, ...extraParams) ??
121
+ this._readable.getZUSDBalance(address, ...extraParams)
122
+ );
123
+ }
124
+
125
+ async getZEROBalance(address?: string, ...extraParams: T): Promise<Decimal> {
126
+ return (
127
+ this._cache.getZEROBalance(address, ...extraParams) ??
128
+ this._readable.getZEROBalance(address, ...extraParams)
129
+ );
130
+ }
131
+
132
+ async getCollateralSurplusBalance(address?: string, ...extraParams: T): Promise<Decimal> {
133
+ return (
134
+ this._cache.getCollateralSurplusBalance(address, ...extraParams) ??
135
+ this._readable.getCollateralSurplusBalance(address, ...extraParams)
136
+ );
137
+ }
138
+
139
+ getTroves(
140
+ params: TroveListingParams & { beforeRedistribution: true },
141
+ ...extraParams: T
142
+ ): Promise<TroveWithPendingRedistribution[]>;
143
+
144
+ getTroves(params: TroveListingParams, ...extraParams: T): Promise<UserTrove[]>;
145
+
146
+ async getTroves(params: TroveListingParams, ...extraParams: T): Promise<UserTrove[]> {
147
+ const { beforeRedistribution, ...restOfParams } = params;
148
+
149
+ const [totalRedistributed, troves] = await Promise.all([
150
+ beforeRedistribution ? undefined : this.getTotalRedistributed(...extraParams),
151
+ this._cache.getTroves({ beforeRedistribution: true, ...restOfParams }, ...extraParams) ??
152
+ this._readable.getTroves({ beforeRedistribution: true, ...restOfParams }, ...extraParams)
153
+ ]);
154
+
155
+ if (totalRedistributed) {
156
+ return troves.map(trove => trove.applyRedistribution(totalRedistributed));
157
+ } else {
158
+ return troves;
159
+ }
160
+ }
161
+
162
+ async getFees(...extraParams: T): Promise<Fees> {
163
+ return this._cache.getFees(...extraParams) ?? this._readable.getFees(...extraParams);
164
+ }
165
+
166
+ async getZEROStake(address?: string, ...extraParams: T): Promise<ZEROStake> {
167
+ return (
168
+ this._cache.getZEROStake(address, ...extraParams) ??
169
+ this._readable.getZEROStake(address, ...extraParams)
170
+ );
171
+ }
172
+
173
+ async getTotalStakedZERO(...extraParams: T): Promise<Decimal> {
174
+ return (
175
+ this._cache.getTotalStakedZERO(...extraParams) ??
176
+ this._readable.getTotalStakedZERO(...extraParams)
177
+ );
178
+ }
179
+
180
+ async getFrontendStatus(address?: string, ...extraParams: T): Promise<FrontendStatus> {
181
+ return (
182
+ this._cache.getFrontendStatus(address, ...extraParams) ??
183
+ this._readable.getFrontendStatus(address, ...extraParams)
184
+ );
185
+ }
186
+ }
@@ -0,0 +1,68 @@
1
+ import { Decimal } from "./Decimal";
2
+
3
+ /**
4
+ * Total collateral ratio below which recovery mode is triggered.
5
+ *
6
+ * @public
7
+ */
8
+ export const CRITICAL_COLLATERAL_RATIO = Decimal.from(1.5);
9
+
10
+ /**
11
+ * Collateral ratio below which a Trove can be liquidated in normal mode.
12
+ *
13
+ * @public
14
+ */
15
+ export const MINIMUM_COLLATERAL_RATIO = Decimal.from(1.1);
16
+
17
+ /**
18
+ * Amount of ZUSD that's reserved for compensating the liquidator of a Trove.
19
+ *
20
+ * @public
21
+ */
22
+ export const ZUSD_LIQUIDATION_RESERVE = Decimal.from(20);
23
+
24
+ /**
25
+ * A Trove must always have at least this much debt on top of the
26
+ * {@link ZUSD_LIQUIDATION_RESERVE | liquidation reserve}.
27
+ *
28
+ * @remarks
29
+ * Any transaction that would result in a Trove with less net debt than this will be reverted.
30
+ *
31
+ * @public
32
+ */
33
+ export const ZUSD_MINIMUM_NET_DEBT = Decimal.from(180);
34
+
35
+ /**
36
+ * A Trove must always have at least this much debt.
37
+ *
38
+ * @remarks
39
+ * Any transaction that would result in a Trove with less debt than this will be reverted.
40
+ *
41
+ * @public
42
+ */
43
+ export const ZUSD_MINIMUM_DEBT = ZUSD_LIQUIDATION_RESERVE.add(ZUSD_MINIMUM_NET_DEBT);
44
+
45
+ /**
46
+ * Value that the {@link Fees.borrowingRate | borrowing rate} will never decay below.
47
+ *
48
+ * @remarks
49
+ * Note that the borrowing rate can still be lower than this during recovery mode, when it's
50
+ * overridden by zero.
51
+ *
52
+ * @public
53
+ */
54
+ export const MINIMUM_BORROWING_RATE = Decimal.from(0.025);
55
+
56
+ /**
57
+ * Value that the {@link Fees.borrowingRate | borrowing rate} will never exceed.
58
+ *
59
+ * @public
60
+ */
61
+ export const MAXIMUM_BORROWING_RATE = Decimal.from(0.05);
62
+
63
+ /**
64
+ * Value that the {@link Fees.redemptionRate | redemption rate} will never decay below.
65
+ *
66
+ * @public
67
+ */
68
+ export const MINIMUM_REDEMPTION_RATE = Decimal.from(0.025);
@@ -0,0 +1,212 @@
1
+ import { describe, it } from "mocha";
2
+ import { expect } from "chai";
3
+ import fc from "fast-check";
4
+
5
+ import { Decimal, Difference } from "../src/Decimal";
6
+
7
+ describe("Decimal", () => {
8
+ describe(".from()", () => {
9
+ describe("when passing a string", () => {
10
+ it("should throw if it's empty", () => {
11
+ expect(() => Decimal.from("")).to.throw("bad decimal format");
12
+ });
13
+
14
+ it("should throw if it's non-numeric", () => {
15
+ expect(() => Decimal.from("12O")).to.throw("bad decimal format");
16
+ });
17
+
18
+ it("should convert it if it has no decimal point", () => {
19
+ expect(Decimal.from("123456789123456789123456789").bigNumber)
20
+ // |<----- 18 ----->|
21
+ /********/ .to.equal("123456789123456789123456789000000000000000000");
22
+ });
23
+
24
+ it("should convert it if it has decimal point", () => {
25
+ expect(Decimal.from("123456789123456789.123456789").bigNumber)
26
+ // |<----- 18 ----->|
27
+ /*********/ .to.equal("123456789123456789123456789000000000");
28
+ });
29
+
30
+ it("should convert it if characteristic is missing", () => {
31
+ expect(Decimal.from(".123456789").bigNumber)
32
+ // |<----- 18 ----->|
33
+ /*********/ .to.equal("123456789000000000");
34
+ });
35
+
36
+ it("should convert it if mantissa is missing", () => {
37
+ expect(Decimal.from("123456789.").bigNumber)
38
+ // |<----- 18 ----->|
39
+ /********/ .to.equal("123456789000000000000000000");
40
+ });
41
+
42
+ it("should truncate if mantissa is too long", () => {
43
+ expect(Decimal.from("1.123456789123456789123456789").bigNumber)
44
+ // |<----- 18 ----->|
45
+ /*********/ .to.equal("1123456789123456789");
46
+ });
47
+
48
+ describe("in scientific notation", () => {
49
+ it("should convert it if exponent has no sign", () => {
50
+ expect(Decimal.from("1.23456789e7").bigNumber)
51
+ // |<-7->||<----- 18 ----->|
52
+ /*********/ .to.equal("12345678900000000000000000");
53
+ });
54
+
55
+ it("should convert it if exponent has '+' sign", () => {
56
+ expect(Decimal.from("1.23456789e+9").bigNumber)
57
+ // |<- 9 ->||<----- 18 ----->|
58
+ /*********/ .to.equal("1234567890000000000000000000");
59
+ });
60
+
61
+ it("should convert it if exponent has '-' sign", () => {
62
+ expect(Decimal.from("123456789.123456789e-10").bigNumber)
63
+ // |<- 8->|
64
+ /*********/ .to.equal("12345678912345678");
65
+ });
66
+ });
67
+ });
68
+
69
+ describe("when passing a number", () => {
70
+ it("should preserve fractional part", () => {
71
+ expect(Decimal.from(1.23456789).bigNumber)
72
+ // |<----- 18 ----->|
73
+ /********/ .to.equal("1234567890000000000");
74
+ });
75
+
76
+ it("should convert it even if it's very small", () => {
77
+ expect(Decimal.from(1e-15).bigNumber).to.equal("1000");
78
+ });
79
+
80
+ it("should convert it even if it's very large", () => {
81
+ expect(Decimal.from(1e25).bigNumber)
82
+ // |<--------- 25 -------->||<----- 18 ----->|
83
+ .to.equal("10000000000000000000000000000000000000000000");
84
+ });
85
+ });
86
+
87
+ describe("when passing a Decimal", () => {
88
+ it("should return the same Decimal", () => {
89
+ const decimal = Decimal.from("123456789");
90
+ expect(Decimal.from(decimal)).to.equal(decimal);
91
+ });
92
+ });
93
+ });
94
+
95
+ describe(".toString()", () => {
96
+ describe("when not passing a parameter", () => {
97
+ it("should not include '.' for integers", () => {
98
+ expect(Decimal.fromBigNumberString("123456789000000000000000000").toString())
99
+ // |<----- 18 ----->|
100
+ /*****************/ .to.equal("123456789");
101
+ });
102
+
103
+ it("should include '.' for fractions", () => {
104
+ expect(Decimal.fromBigNumberString("123456789000000000123456789").toString())
105
+ // |<----- 18 ----->|
106
+ /****************/ .to.equal("123456789.000000000123456789");
107
+ });
108
+
109
+ it("should trim trailing zeros from fractions", () => {
110
+ expect(Decimal.fromBigNumberString("123456789123456789000000000").toString())
111
+ // |<----- 18 ----->|
112
+ /****************/ .to.equal("123456789.123456789");
113
+ });
114
+ });
115
+
116
+ describe("when passing a precision parameter", () => {
117
+ it("should round to the nearest decimal", () => {
118
+ expect(Decimal.fromBigNumberString("123456789000499999999999999").toString(3))
119
+ // |<----- 18 ----->|
120
+ /****************/ .to.equal("123456789.000");
121
+
122
+ expect(Decimal.fromBigNumberString("123456789000500000000000000").toString(3))
123
+ // |<----- 18 ----->|
124
+ /****************/ .to.equal("123456789.001");
125
+ });
126
+
127
+ it("should include '.' and decimal zeros for integers if precision is >0", () => {
128
+ expect(Decimal.fromBigNumberString("123456789000000000000000000").toString(2))
129
+ // |<----- 18 ----->|
130
+ /****************/ .to.equal("123456789.00");
131
+ });
132
+
133
+ it("should not include '.' if precision is 0", () => {
134
+ expect(Decimal.fromBigNumberString("123456789123456789123456789").toString(0))
135
+ // |<----- 18 ----->|
136
+ /*****************/ .to.equal("123456789");
137
+ });
138
+ });
139
+ });
140
+
141
+ describe(".shorten()", () => {
142
+ const cases = [
143
+ ["0.123", "0.12"],
144
+ ["1.234", "1.23"],
145
+ ["12.345", "12.3"],
146
+ ["123.456", "123"],
147
+ ["1234.567", "1.23K"],
148
+ ["12345.678", "12.3K"],
149
+ ["123456.789", "123K"],
150
+ ["1234567.891", "1.23M"],
151
+ ["12345678.912", "12.3M"],
152
+ ["123456789.123", "123M"],
153
+ ["1234567891.234", "1.23B"],
154
+ ["12345678912.345", "12.3B"],
155
+ ["123456789123.456", "123B"],
156
+ ["1234567891234.567", "1.23T"],
157
+ ["12345678912345.678", "12.3T"],
158
+ ["123456789123456.789", "123T"],
159
+ ["1234567891234567.891", "1,235T"],
160
+ ["12345678912345678.912", "12,346T"],
161
+ ["123456789123456789.123", "123,457T"],
162
+ ["1234567891234567891.234", "1,234,568T"]
163
+ ];
164
+
165
+ for (const [input, expectedOutput] of cases) {
166
+ it(`should turn '${input}' into '${expectedOutput}'`, () => {
167
+ expect(Decimal.from(input).shorten()).to.equal(expectedOutput);
168
+ });
169
+ }
170
+ });
171
+
172
+ describe(".mul()", () => {
173
+ it("should multiply", () => {
174
+ expect(Decimal.from(2).mul(3).toString()).to.equal("6");
175
+ });
176
+ });
177
+
178
+ describe(".div()", () => {
179
+ it("should divide", () => {
180
+ expect(Decimal.from(3).div(2).toString()).to.equal("1.5");
181
+ });
182
+ });
183
+
184
+ describe(".mulDiv()", () => {
185
+ it("should multiply then divide", () => {
186
+ expect(Decimal.from(2).mulDiv(3, 2).toString()).to.equal("3");
187
+ });
188
+ });
189
+
190
+ describe(".pow()", () => {
191
+ it("should be roughly the same as Math.pow()", () => {
192
+ fc.assert(
193
+ fc.property(
194
+ fc.float(),
195
+ fc.integer({ min: 0, max: 0xffffffff }),
196
+ (x, y) =>
197
+ !!Difference.between(Math.pow(x, y), Decimal.from(x).pow(y)).absoluteValue?.lt(1e-9)
198
+ )
199
+ );
200
+ });
201
+ });
202
+
203
+ describe(".isZero", () => {
204
+ it("should be true if Decimal is zero", () => {
205
+ expect(Decimal.from("0.0").isZero).to.be.true;
206
+ });
207
+
208
+ it("should be false if Decimal is non-zero", () => {
209
+ expect(Decimal.from("0.1").isZero).to.be.false;
210
+ });
211
+ });
212
+ });
@@ -0,0 +1,30 @@
1
+ import { describe, it } from "mocha";
2
+ import fc from "fast-check";
3
+
4
+ import { Decimal } from "../src/Decimal";
5
+ import { StabilityDeposit } from "../src/StabilityDeposit";
6
+
7
+ const arbitraryDeposit = () =>
8
+ fc
9
+ .tuple(fc.float(), fc.float(), fc.float(), fc.float())
10
+ .filter(([initialZUSD, currentZUSD]) => initialZUSD >= currentZUSD)
11
+ .map(
12
+ ([a, b, c, d]) =>
13
+ new StabilityDeposit(Decimal.from(a), Decimal.from(b), Decimal.from(c), Decimal.from(d), "")
14
+ );
15
+
16
+ const nonZeroDeposit = () => arbitraryDeposit().filter(({ currentZUSD }) => !currentZUSD.isZero);
17
+
18
+ describe("StabilityDeposit", () => {
19
+ it("applying diff of `b` from `a` to `a` should always yield `b`", () => {
20
+ fc.assert(
21
+ fc.property(arbitraryDeposit(), fc.float(), (a, b) => a.apply(a.whatChanged(b)).eq(b))
22
+ );
23
+ });
24
+
25
+ it("applying what changed should preserve zeroing", () => {
26
+ fc.assert(
27
+ fc.property(arbitraryDeposit(), nonZeroDeposit(), (a, b) => a.apply(b.whatChanged(0)).eq(0))
28
+ );
29
+ });
30
+ });