@morpho-org/blue-sdk 1.0.1 → 1.0.2

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 (151) hide show
  1. package/package.json +10 -6
  2. package/src/addresses.ts +261 -0
  3. package/src/chain/chain.constants.ts +235 -0
  4. package/src/chain/chain.test.ts +51 -0
  5. package/src/chain/chain.types.ts +42 -0
  6. package/src/chain/chain.utils.ts +44 -0
  7. package/src/constants.ts +18 -0
  8. package/src/errors.ts +75 -0
  9. package/src/ethers/ethers.test.ts +17 -0
  10. package/src/ethers/safeGetAddress.ts +4 -0
  11. package/src/ethers/safeParseUnits.ts +29 -0
  12. package/src/evm.ts +172 -0
  13. package/src/helpers/format/format.test.ts +340 -0
  14. package/src/helpers/format/format.ts +416 -0
  15. package/src/helpers/getChecksumedAddress.ts +15 -0
  16. package/{lib/helpers/isZeroAddressOrUnset.d.ts → src/helpers/isZeroAddressOrUnset.ts} +7 -1
  17. package/src/helpers/locale.ts +108 -0
  18. package/src/holding/Holding.ts +109 -0
  19. package/src/market/Market.ts +479 -0
  20. package/src/market/MarketConfig.ts +108 -0
  21. package/src/market/MarketUtils.test.ts +25 -0
  22. package/src/market/MarketUtils.ts +467 -0
  23. package/src/maths/AdaptiveCurveIrmLib.ts +143 -0
  24. package/src/maths/MathLib.ts +208 -0
  25. package/src/maths/MathUtils.ts +31 -0
  26. package/src/maths/SharesMath.ts +40 -0
  27. package/src/notifications.ts +167 -0
  28. package/src/position/Position.ts +251 -0
  29. package/src/signatures/index.ts +18 -0
  30. package/src/signatures/manager.ts +50 -0
  31. package/src/signatures/permit.ts +126 -0
  32. package/src/signatures/permit2.ts +120 -0
  33. package/src/signatures/types.ts +18 -0
  34. package/src/signatures/utils.ts +83 -0
  35. package/src/tests/mocks/markets.ts +110 -0
  36. package/src/token/ERC20Metadata.ts +124 -0
  37. package/src/token/Token.ts +83 -0
  38. package/src/token/TokenNamespace.ts +76 -0
  39. package/src/token/WrappedToken.ts +142 -0
  40. package/src/types.ts +37 -0
  41. package/src/user/User.ts +32 -0
  42. package/src/user/user.types.ts +23 -0
  43. package/src/vault/Vault.ts +370 -0
  44. package/src/vault/VaultAllocation.ts +58 -0
  45. package/src/vault/VaultConfig.ts +55 -0
  46. package/src/vault/VaultUtils.ts +47 -0
  47. package/lib/addresses.d.ts +0 -168
  48. package/lib/addresses.js +0 -169
  49. package/lib/chain/chain.constants.d.ts +0 -3
  50. package/lib/chain/chain.constants.js +0 -232
  51. package/lib/chain/chain.types.d.ts +0 -20
  52. package/lib/chain/chain.types.js +0 -30
  53. package/lib/chain/chain.utils.d.ts +0 -14
  54. package/lib/chain/chain.utils.js +0 -30
  55. package/lib/chain/index.js +0 -18
  56. package/lib/constants.d.ts +0 -8
  57. package/lib/constants.js +0 -13
  58. package/lib/errors.d.ts +0 -37
  59. package/lib/errors.js +0 -71
  60. package/lib/ethers/index.js +0 -18
  61. package/lib/ethers/safeGetAddress.d.ts +0 -1
  62. package/lib/ethers/safeGetAddress.js +0 -6
  63. package/lib/ethers/safeParseUnits.d.ts +0 -2
  64. package/lib/ethers/safeParseUnits.js +0 -25
  65. package/lib/evm.d.ts +0 -36
  66. package/lib/evm.js +0 -113
  67. package/lib/helpers/format/format.d.ts +0 -98
  68. package/lib/helpers/format/format.js +0 -301
  69. package/lib/helpers/format/index.js +0 -17
  70. package/lib/helpers/getChecksumedAddress.d.ts +0 -7
  71. package/lib/helpers/getChecksumedAddress.js +0 -17
  72. package/lib/helpers/index.js +0 -20
  73. package/lib/helpers/isZeroAddressOrUnset.js +0 -14
  74. package/lib/helpers/locale.d.ts +0 -36
  75. package/lib/helpers/locale.js +0 -86
  76. package/lib/holding/Holding.d.ts +0 -60
  77. package/lib/holding/Holding.js +0 -31
  78. package/lib/holding/index.js +0 -17
  79. package/lib/index.d.ts +0 -33
  80. package/lib/index.js +0 -62
  81. package/lib/market/Market.d.ts +0 -159
  82. package/lib/market/Market.js +0 -240
  83. package/lib/market/MarketConfig.d.ts +0 -44
  84. package/lib/market/MarketConfig.js +0 -56
  85. package/lib/market/MarketUtils.d.ts +0 -165
  86. package/lib/market/MarketUtils.js +0 -182
  87. package/lib/market/index.js +0 -19
  88. package/lib/maths/AdaptiveCurveIrmLib.d.ts +0 -37
  89. package/lib/maths/AdaptiveCurveIrmLib.js +0 -116
  90. package/lib/maths/MathLib.d.ts +0 -94
  91. package/lib/maths/MathLib.js +0 -153
  92. package/lib/maths/MathUtils.d.ts +0 -15
  93. package/lib/maths/MathUtils.js +0 -33
  94. package/lib/maths/SharesMath.d.ts +0 -12
  95. package/lib/maths/SharesMath.js +0 -22
  96. package/lib/maths/index.js +0 -20
  97. package/lib/notifications.d.ts +0 -98
  98. package/lib/notifications.js +0 -52
  99. package/lib/position/Position.d.ts +0 -118
  100. package/lib/position/Position.js +0 -145
  101. package/lib/position/index.js +0 -17
  102. package/lib/signatures/index.d.ts +0 -12
  103. package/lib/signatures/index.js +0 -39
  104. package/lib/signatures/manager.d.ts +0 -10
  105. package/lib/signatures/manager.js +0 -37
  106. package/lib/signatures/permit.d.ts +0 -21
  107. package/lib/signatures/permit.js +0 -101
  108. package/lib/signatures/permit2.d.ts +0 -20
  109. package/lib/signatures/permit2.js +0 -91
  110. package/lib/signatures/types.d.ts +0 -13
  111. package/lib/signatures/types.js +0 -2
  112. package/lib/signatures/utils.d.ts +0 -6
  113. package/lib/signatures/utils.js +0 -44
  114. package/lib/tests/mocks/markets.d.ts +0 -17
  115. package/lib/tests/mocks/markets.js +0 -108
  116. package/lib/token/ERC20Metadata.d.ts +0 -249
  117. package/lib/token/ERC20Metadata.js +0 -81
  118. package/lib/token/Token.d.ts +0 -45
  119. package/lib/token/Token.js +0 -39
  120. package/lib/token/TokenNamespace.d.ts +0 -18
  121. package/lib/token/TokenNamespace.js +0 -55
  122. package/lib/token/WrappedToken.d.ts +0 -42
  123. package/lib/token/WrappedToken.js +0 -87
  124. package/lib/token/index.js +0 -18
  125. package/lib/types.d.ts +0 -29
  126. package/lib/types.js +0 -23
  127. package/lib/user/User.d.ts +0 -20
  128. package/lib/user/User.js +0 -11
  129. package/lib/user/index.js +0 -18
  130. package/lib/user/user.types.d.ts +0 -18
  131. package/lib/user/user.types.js +0 -2
  132. package/lib/vault/Vault.d.ts +0 -167
  133. package/lib/vault/Vault.js +0 -156
  134. package/lib/vault/VaultAllocation.d.ts +0 -38
  135. package/lib/vault/VaultAllocation.js +0 -18
  136. package/lib/vault/VaultConfig.d.ts +0 -23
  137. package/lib/vault/VaultConfig.js +0 -26
  138. package/lib/vault/VaultUtils.d.ts +0 -17
  139. package/lib/vault/VaultUtils.js +0 -17
  140. package/lib/vault/index.js +0 -20
  141. /package/{lib/chain/index.d.ts → src/chain/index.ts} +0 -0
  142. /package/{lib/ethers/index.d.ts → src/ethers/index.ts} +0 -0
  143. /package/{lib/helpers/format/index.d.ts → src/helpers/format/index.ts} +0 -0
  144. /package/{lib/helpers/index.d.ts → src/helpers/index.ts} +0 -0
  145. /package/{lib/holding/index.d.ts → src/holding/index.ts} +0 -0
  146. /package/{lib/market/index.d.ts → src/market/index.ts} +0 -0
  147. /package/{lib/maths/index.d.ts → src/maths/index.ts} +0 -0
  148. /package/{lib/position/index.d.ts → src/position/index.ts} +0 -0
  149. /package/{lib/token/index.d.ts → src/token/index.ts} +0 -0
  150. /package/{lib/user/index.d.ts → src/user/index.ts} +0 -0
  151. /package/{lib/vault/index.d.ts → src/vault/index.ts} +0 -0
@@ -0,0 +1,479 @@
1
+ import { BigNumberish, Provider, ZeroAddress, toBigInt } from "ethers";
2
+ import {
3
+ AdaptiveCurveIrm__factory,
4
+ BlueOracle__factory,
5
+ MorphoBlue__factory,
6
+ } from "ethers-types";
7
+ import { ViewOverrides } from "ethers-types/dist/common";
8
+
9
+ import { getChainAddresses } from "../addresses";
10
+ import { ChainId, ChainUtils } from "../chain";
11
+ import { InvalidInterestAccrualError } from "../errors";
12
+ import { AdaptiveCurveIrmLib, RoundingDirection } from "../maths";
13
+ import { MarketId } from "../types";
14
+
15
+ import { MarketConfig } from "./MarketConfig";
16
+ import { MarketUtils } from "./MarketUtils";
17
+
18
+ export enum CapacityLimitReason {
19
+ liquidity = "Liquidity",
20
+ balance = "Balance",
21
+ position = "Position",
22
+ collateral = "Collateral",
23
+ cap = "Cap",
24
+ }
25
+
26
+ export interface CapacityLimit {
27
+ value: bigint;
28
+ limiter: CapacityLimitReason;
29
+ }
30
+
31
+ export interface MaxPositionCapacities {
32
+ supply: CapacityLimit;
33
+ withdraw: CapacityLimit;
34
+ borrow: CapacityLimit;
35
+ repay: CapacityLimit;
36
+ supplyCollateral: CapacityLimit;
37
+ withdrawCollateral: CapacityLimit;
38
+ }
39
+
40
+ export interface InputMarket {
41
+ config: MarketConfig;
42
+ totalSupplyAssets: BigNumberish;
43
+ totalBorrowAssets: BigNumberish;
44
+ totalSupplyShares: BigNumberish;
45
+ totalBorrowShares: BigNumberish;
46
+ lastUpdate: BigNumberish;
47
+ fee: BigNumberish;
48
+ price: BigNumberish;
49
+ rateAtTarget?: BigNumberish;
50
+ }
51
+
52
+ export class Market implements InputMarket {
53
+ static async fetchFromId(
54
+ id: MarketId,
55
+ runner: { provider: Provider },
56
+ {
57
+ chainId,
58
+ overrides = {},
59
+ }: { chainId?: ChainId; overrides?: ViewOverrides } = {}
60
+ ) {
61
+ chainId ??= ChainUtils.parseSupportedChainId(
62
+ (await runner.provider.getNetwork()).chainId
63
+ );
64
+ const config = await MarketConfig.fetch(id, runner, chainId);
65
+
66
+ return Market.fetchFromConfig(config, runner, { chainId, overrides });
67
+ }
68
+
69
+ static async fetchFromConfig(
70
+ config: MarketConfig,
71
+ runner: { provider: Provider },
72
+ {
73
+ chainId,
74
+ overrides = {},
75
+ }: { chainId?: ChainId; overrides?: ViewOverrides } = {}
76
+ ) {
77
+ chainId ??= ChainUtils.parseSupportedChainId(
78
+ (await runner.provider.getNetwork()).chainId
79
+ );
80
+ const { morpho, adaptiveCurveIrm } = getChainAddresses(chainId);
81
+
82
+ const [
83
+ {
84
+ totalSupplyAssets,
85
+ totalSupplyShares,
86
+ totalBorrowShares,
87
+ totalBorrowAssets,
88
+ lastUpdate,
89
+ fee,
90
+ },
91
+ price,
92
+ rateAtTarget,
93
+ ] = await Promise.all([
94
+ MorphoBlue__factory.connect(morpho, runner).market(config.id, overrides),
95
+ config.oracle !== ZeroAddress
96
+ ? BlueOracle__factory.connect(config.oracle, runner).price(overrides)
97
+ : 0n,
98
+ config.irm === adaptiveCurveIrm
99
+ ? await AdaptiveCurveIrm__factory.connect(
100
+ config.irm,
101
+ runner
102
+ ).rateAtTarget(config.id, overrides)
103
+ : undefined,
104
+ ]);
105
+
106
+ return new Market({
107
+ config,
108
+ totalSupplyAssets,
109
+ totalBorrowAssets,
110
+ totalSupplyShares,
111
+ totalBorrowShares,
112
+ lastUpdate,
113
+ fee,
114
+ price,
115
+ rateAtTarget,
116
+ });
117
+ }
118
+
119
+ /**
120
+ * The market's config.
121
+ */
122
+ public readonly config: MarketConfig;
123
+
124
+ /**
125
+ * The amount of loan assets supplied in total on the market.
126
+ */
127
+ public totalSupplyAssets: bigint;
128
+ /**
129
+ * The amount of loan assets supplied in total on the market.
130
+ */
131
+ public totalBorrowAssets: bigint;
132
+ /**
133
+ * The amount of loan assets supplied in total on the market.
134
+ */
135
+ public totalSupplyShares: bigint;
136
+ /**
137
+ * The amount of loan assets supplied in total on the market.
138
+ */
139
+ public totalBorrowShares: bigint;
140
+
141
+ /**
142
+ * The block timestamp (in __seconds__) when the interest was last accrued.
143
+ */
144
+ public lastUpdate: bigint;
145
+ /**
146
+ * The fee percentage of the market, scaled by WAD.
147
+ */
148
+ public fee: bigint;
149
+
150
+ /**
151
+ * The price as returned by the market's oracle.
152
+ */
153
+ public price: bigint;
154
+
155
+ /**
156
+ * If the market uses the Adaptive Curve IRM, the rate at target utilization.
157
+ * Undefined otherwise.
158
+ */
159
+ public rateAtTarget?: bigint;
160
+
161
+ constructor({
162
+ config,
163
+ totalSupplyAssets,
164
+ totalBorrowAssets,
165
+ totalSupplyShares,
166
+ totalBorrowShares,
167
+ lastUpdate,
168
+ fee,
169
+ price,
170
+ rateAtTarget,
171
+ }: InputMarket) {
172
+ this.config = config;
173
+ this.totalSupplyAssets = toBigInt(totalSupplyAssets);
174
+ this.totalBorrowAssets = toBigInt(totalBorrowAssets);
175
+ this.totalSupplyShares = toBigInt(totalSupplyShares);
176
+ this.totalBorrowShares = toBigInt(totalBorrowShares);
177
+ this.lastUpdate = toBigInt(lastUpdate);
178
+ this.fee = toBigInt(fee);
179
+ this.price = toBigInt(price);
180
+
181
+ if (rateAtTarget != null) this.rateAtTarget = toBigInt(rateAtTarget);
182
+ }
183
+
184
+ get id() {
185
+ return this.config.id;
186
+ }
187
+
188
+ get isIdle() {
189
+ return this.config.collateralToken === ZeroAddress;
190
+ }
191
+
192
+ /**
193
+ * @warning Cannot be used to calculate the liquidity available inside a callback,
194
+ * because the balance of Blue may be lower than the market's liquidity due to assets being transferred out prior to the callback.
195
+ */
196
+ get liquidity() {
197
+ return this.totalSupplyAssets - this.totalBorrowAssets;
198
+ }
199
+
200
+ get utilization() {
201
+ return MarketUtils.getUtilization(this);
202
+ }
203
+
204
+ get apyAtTarget() {
205
+ if (this.rateAtTarget == null) return;
206
+
207
+ return MarketUtils.getApy(this.rateAtTarget);
208
+ }
209
+
210
+ get supplyRate() {
211
+ return MarketUtils.getSupplyRate(this.borrowRate, this);
212
+ }
213
+
214
+ get borrowRate() {
215
+ if (this.rateAtTarget == null) return 0n;
216
+
217
+ return AdaptiveCurveIrmLib.getBorrowRate(
218
+ this.utilization,
219
+ this.rateAtTarget,
220
+ 0n
221
+ ).avgBorrowRate;
222
+ }
223
+
224
+ get supplyApy() {
225
+ return MarketUtils.getApy(this.supplyRate);
226
+ }
227
+
228
+ get borrowApy() {
229
+ return MarketUtils.getApy(this.borrowRate);
230
+ }
231
+
232
+ public accrueInterest(timestamp: BigNumberish) {
233
+ timestamp = toBigInt(timestamp);
234
+
235
+ const elapsed = timestamp - this.lastUpdate;
236
+ if (elapsed < 0n)
237
+ throw new InvalidInterestAccrualError(
238
+ this.id,
239
+ timestamp,
240
+ this.lastUpdate
241
+ );
242
+
243
+ let borrowRate = 0n;
244
+ let { rateAtTarget } = this;
245
+ if (this.rateAtTarget != null) {
246
+ const { avgBorrowRate, endRateAtTarget } =
247
+ AdaptiveCurveIrmLib.getBorrowRate(
248
+ this.utilization,
249
+ this.rateAtTarget,
250
+ elapsed
251
+ );
252
+
253
+ borrowRate = avgBorrowRate;
254
+ rateAtTarget = endRateAtTarget;
255
+ }
256
+
257
+ const { interest, feeShares } = MarketUtils.getAccruedInterest(
258
+ borrowRate,
259
+ this,
260
+ elapsed
261
+ );
262
+
263
+ return new Market({
264
+ ...this,
265
+ totalSupplyAssets: this.totalSupplyAssets + interest,
266
+ totalBorrowAssets: this.totalBorrowAssets + interest,
267
+ totalSupplyShares: this.totalSupplyShares + feeShares,
268
+ lastUpdate: timestamp,
269
+ rateAtTarget,
270
+ });
271
+ }
272
+
273
+ public getLiquidityToUtilization(utilization: BigNumberish) {
274
+ return MarketUtils.getLiquidityToUtilization(this, utilization);
275
+ }
276
+
277
+ public getCollateralValue(collateral: BigNumberish) {
278
+ return MarketUtils.getCollateralValue(collateral, this);
279
+ }
280
+
281
+ public getMaxBorrowAssets(collateral: BigNumberish) {
282
+ return MarketUtils.getMaxBorrowAssets(collateral, this, this.config);
283
+ }
284
+
285
+ public getMaxBorrowableAssets(position: {
286
+ collateral: BigNumberish;
287
+ borrowShares: BigNumberish;
288
+ }) {
289
+ return MarketUtils.getMaxBorrowableAssets(position, this, this.config);
290
+ }
291
+
292
+ public getLiquidationSeizedAssets(repaidShares: BigNumberish) {
293
+ return MarketUtils.getLiquidationSeizedAssets(
294
+ repaidShares,
295
+ this,
296
+ this.config
297
+ );
298
+ }
299
+
300
+ public getLiquidationRepaidShares(seizedAssets: BigNumberish) {
301
+ return MarketUtils.getLiquidationRepaidShares(
302
+ seizedAssets,
303
+ this,
304
+ this.config
305
+ );
306
+ }
307
+
308
+ public getSeizableCollateral(position: {
309
+ collateral: BigNumberish;
310
+ borrowShares: BigNumberish;
311
+ }) {
312
+ return MarketUtils.getSeizableCollateral(position, this, this.config);
313
+ }
314
+
315
+ public getWithdrawableCollateral(position: {
316
+ collateral: BigNumberish;
317
+ borrowShares: BigNumberish;
318
+ }) {
319
+ return MarketUtils.getWithdrawableCollateral(position, this, this.config);
320
+ }
321
+
322
+ public isHealthy(position: {
323
+ collateral: BigNumberish;
324
+ borrowShares: BigNumberish;
325
+ }) {
326
+ return MarketUtils.isHealthy(position, this, this.config);
327
+ }
328
+
329
+ public getLiquidationPrice(position: {
330
+ collateral: BigNumberish;
331
+ borrowShares: BigNumberish;
332
+ }) {
333
+ return MarketUtils.getLiquidationPrice(position, this, this.config);
334
+ }
335
+
336
+ public getPriceVariationToLiquidation(position: {
337
+ collateral: BigNumberish;
338
+ borrowShares: BigNumberish;
339
+ }) {
340
+ return MarketUtils.getPriceVariationToLiquidation(
341
+ position,
342
+ this,
343
+ this.config
344
+ );
345
+ }
346
+
347
+ public getHealthFactor(position: {
348
+ collateral: BigNumberish;
349
+ borrowShares: BigNumberish;
350
+ }) {
351
+ return MarketUtils.getHealthFactor(position, this, this.config);
352
+ }
353
+
354
+ public getLtv(position: {
355
+ collateral: BigNumberish;
356
+ borrowShares: BigNumberish;
357
+ }) {
358
+ return MarketUtils.getLtv(position, this);
359
+ }
360
+
361
+ public getBorrowCapacityUsage(position: {
362
+ collateral: BigNumberish;
363
+ borrowShares: BigNumberish;
364
+ }) {
365
+ return MarketUtils.getBorrowCapacityUsage(position, this, this.config);
366
+ }
367
+
368
+ public toSupplyAssets(shares: BigNumberish, rounding?: RoundingDirection) {
369
+ return MarketUtils.toSupplyAssets(shares, this, rounding);
370
+ }
371
+
372
+ public toSupplyShares(assets: BigNumberish, rounding?: RoundingDirection) {
373
+ return MarketUtils.toSupplyShares(assets, this, rounding);
374
+ }
375
+
376
+ public toBorrowAssets(shares: BigNumberish, rounding?: RoundingDirection) {
377
+ return MarketUtils.toBorrowAssets(shares, this, rounding);
378
+ }
379
+
380
+ public toBorrowShares(assets: BigNumberish, rounding?: RoundingDirection) {
381
+ return MarketUtils.toBorrowShares(assets, this, rounding);
382
+ }
383
+
384
+ public getBorrowCapacityLimit(collateral: bigint): CapacityLimit {
385
+ const maxBorrowableAssets = this.getMaxBorrowAssets(collateral);
386
+ const { liquidity } = this;
387
+
388
+ if (maxBorrowableAssets > liquidity)
389
+ return {
390
+ value: liquidity,
391
+ limiter: CapacityLimitReason.liquidity,
392
+ };
393
+
394
+ return {
395
+ value: maxBorrowableAssets,
396
+ limiter: CapacityLimitReason.collateral,
397
+ };
398
+ }
399
+
400
+ public getRepayCapacityLimit(
401
+ borrowShares: bigint,
402
+ loanTokenBalance: bigint
403
+ ): CapacityLimit {
404
+ const borrowAssets = this.toBorrowAssets(borrowShares);
405
+
406
+ if (borrowAssets > loanTokenBalance)
407
+ return {
408
+ value: loanTokenBalance,
409
+ limiter: CapacityLimitReason.balance,
410
+ };
411
+
412
+ return {
413
+ value: borrowAssets,
414
+ limiter: CapacityLimitReason.position,
415
+ };
416
+ }
417
+
418
+ public getWithdrawCapacityLimit(supplyShares: bigint): CapacityLimit {
419
+ const supplyAssets = this.toSupplyAssets(supplyShares);
420
+ const { liquidity } = this;
421
+
422
+ if (supplyAssets > liquidity)
423
+ return {
424
+ value: liquidity,
425
+ limiter: CapacityLimitReason.liquidity,
426
+ };
427
+
428
+ return {
429
+ value: supplyAssets,
430
+ limiter: CapacityLimitReason.position,
431
+ };
432
+ }
433
+
434
+ public getWithdrawCollateralCapacityLimit(position: {
435
+ collateral: bigint;
436
+ borrowShares: bigint;
437
+ }): CapacityLimit {
438
+ const withdrawableCollateral = this.getWithdrawableCollateral(position);
439
+
440
+ if (position.collateral > withdrawableCollateral)
441
+ return {
442
+ value: withdrawableCollateral,
443
+ limiter: CapacityLimitReason.collateral,
444
+ };
445
+
446
+ return {
447
+ value: position.collateral,
448
+ limiter: CapacityLimitReason.position,
449
+ };
450
+ }
451
+
452
+ public getMaxCapacities(
453
+ position: {
454
+ collateral: bigint;
455
+ supplyShares: bigint;
456
+ borrowShares: bigint;
457
+ },
458
+ loanTokenBalance: bigint,
459
+ collateralTokenBalance: bigint
460
+ ): MaxPositionCapacities {
461
+ return {
462
+ supply: {
463
+ value: loanTokenBalance,
464
+ limiter: CapacityLimitReason.balance,
465
+ },
466
+ withdraw: this.getWithdrawCapacityLimit(position.supplyShares),
467
+ borrow: this.getBorrowCapacityLimit(position.collateral),
468
+ repay: this.getRepayCapacityLimit(
469
+ position.borrowShares,
470
+ loanTokenBalance
471
+ ),
472
+ supplyCollateral: {
473
+ value: collateralTokenBalance,
474
+ limiter: CapacityLimitReason.balance,
475
+ },
476
+ withdrawCollateral: this.getWithdrawCollateralCapacityLimit(position),
477
+ };
478
+ }
479
+ }
@@ -0,0 +1,108 @@
1
+ import { Provider, ZeroAddress, toBigInt } from "ethers";
2
+ import { MorphoBlue__factory } from "ethers-types";
3
+ import { MarketParamsStruct } from "ethers-types/dist/protocols/morpho/blue/MorphoBlue";
4
+
5
+ import { getChainAddresses } from "../addresses";
6
+ import { ChainId, ChainUtils } from "../chain";
7
+ import { UnknownMarketConfigError, _try } from "../errors";
8
+ import { Address, MarketId } from "../types";
9
+
10
+ import { MarketUtils } from "./MarketUtils";
11
+
12
+ export interface MarketParams extends MarketParamsStruct {
13
+ loanToken: Address;
14
+ collateralToken: Address;
15
+ oracle: Address;
16
+ irm: Address;
17
+ }
18
+
19
+ export class MarketConfig implements MarketParams {
20
+ private static readonly _CACHE: Record<MarketId, MarketConfig> = {};
21
+
22
+ static get(id: MarketId) {
23
+ const marketConfig = MarketConfig._CACHE[id];
24
+
25
+ if (!marketConfig) throw new UnknownMarketConfigError(id);
26
+
27
+ return marketConfig;
28
+ }
29
+
30
+ static async fetch(
31
+ id: MarketId,
32
+ runner: { provider: Provider },
33
+ chainId?: ChainId
34
+ ) {
35
+ let config = _try(() => MarketConfig.get(id), UnknownMarketConfigError);
36
+
37
+ if (!config) {
38
+ chainId ??= ChainUtils.parseSupportedChainId(
39
+ (await runner.provider.getNetwork()).chainId
40
+ );
41
+
42
+ const { morpho } = getChainAddresses(chainId);
43
+
44
+ config = new MarketConfig(
45
+ // Always fetch at latest block because config is immutable.
46
+ await MorphoBlue__factory.connect(morpho, runner).idToMarketParams(id)
47
+ );
48
+ }
49
+
50
+ return config;
51
+ }
52
+
53
+ static idle(token: Address) {
54
+ return new MarketConfig({
55
+ collateralToken: ZeroAddress,
56
+ loanToken: token,
57
+ oracle: ZeroAddress,
58
+ irm: ZeroAddress,
59
+ lltv: 0n,
60
+ });
61
+ }
62
+
63
+ /**
64
+ * The market's collateral token address.
65
+ */
66
+ public readonly collateralToken: Address;
67
+
68
+ /**
69
+ * The market's loan token address.
70
+ */
71
+ public readonly loanToken: Address;
72
+
73
+ /**
74
+ * The market's oracle address.
75
+ */
76
+ public readonly oracle: Address;
77
+
78
+ /**
79
+ * The market's interest rate model address.
80
+ */
81
+ public readonly irm: Address;
82
+
83
+ /**
84
+ * The market's liquidation Loan-To-Value (scaled by WAD).
85
+ */
86
+ public readonly lltv: bigint;
87
+
88
+ constructor({ collateralToken, loanToken, oracle, irm, lltv }: MarketParams) {
89
+ this.collateralToken = collateralToken;
90
+ this.loanToken = loanToken;
91
+ this.oracle = oracle;
92
+ this.irm = irm;
93
+ this.lltv = toBigInt(lltv);
94
+
95
+ MarketConfig._CACHE[this.id] = this;
96
+ }
97
+
98
+ /**
99
+ * The market's hex-encoded id, defined as the hash of the market's params.
100
+ */
101
+ get id() {
102
+ return MarketUtils.getMarketId(this);
103
+ }
104
+
105
+ get liquidationIncentiveFactor() {
106
+ return MarketUtils.getLiquidationIncentiveFactor(this);
107
+ }
108
+ }
@@ -0,0 +1,25 @@
1
+ import { parseEther } from "ethers";
2
+
3
+ import { MarketUtils } from "./MarketUtils";
4
+
5
+ const market = {
6
+ loanToken: "0x0000000000000000000000000000000000000001",
7
+ collateralToken: "0x0000000000000000000000000000000000000002",
8
+ oracle: "0x0000000000000000000000000000000000000003",
9
+ irm: "0x0000000000000000000000000000000000000004",
10
+ lltv: parseEther("0.86"),
11
+ };
12
+
13
+ describe("MarketUtils", () => {
14
+ it("should calculate the correct market id", () => {
15
+ expect(MarketUtils.getMarketId(market)).toEqual(
16
+ "0x625e29dff74826b71c1f4c74b208a896109cc8ac9910192ce2927a982b0809e6"
17
+ );
18
+ });
19
+
20
+ it("should calculate the correct liquidation incentive factor", () => {
21
+ expect(MarketUtils.getLiquidationIncentiveFactor(market)).toEqual(
22
+ 1043841336116910229n
23
+ );
24
+ });
25
+ });