@morpho-org/midnight-sdk 0.1.0 → 1.1.1

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 (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -46
  3. package/lib/cjs/abis.d.ts +1185 -996
  4. package/lib/cjs/abis.js +1445 -1211
  5. package/lib/cjs/api/MidnightApi.d.ts +22 -16
  6. package/lib/cjs/api/MidnightApi.js +89 -19
  7. package/lib/cjs/api/helpers.d.ts +11 -1
  8. package/lib/cjs/api/helpers.js +54 -4
  9. package/lib/cjs/api/types.d.ts +35 -9
  10. package/lib/cjs/constants.d.ts +12 -11
  11. package/lib/cjs/constants.js +14 -12
  12. package/lib/cjs/errors.d.ts +89 -1
  13. package/lib/cjs/errors.js +114 -3
  14. package/lib/cjs/fetch/Market.js +37 -3
  15. package/lib/cjs/fetch/index.d.ts +0 -1
  16. package/lib/cjs/fetch/index.js +0 -1
  17. package/lib/cjs/fetch/utils.d.ts +1 -1
  18. package/lib/cjs/fetch/utils.js +9 -2
  19. package/lib/cjs/index.d.ts +1 -1
  20. package/lib/cjs/index.js +2 -23
  21. package/lib/cjs/market/Market.d.ts +99 -33
  22. package/lib/cjs/market/Market.js +137 -28
  23. package/lib/cjs/market/MarketUtils.d.ts +61 -41
  24. package/lib/cjs/market/MarketUtils.js +108 -48
  25. package/lib/cjs/market/Position.d.ts +16 -11
  26. package/lib/cjs/market/Position.js +18 -12
  27. package/lib/cjs/market/PositionUtils.d.ts +6 -4
  28. package/lib/cjs/market/PositionUtils.js +9 -6
  29. package/lib/cjs/math/TickLib.d.ts +22 -0
  30. package/lib/cjs/math/TickLib.js +30 -0
  31. package/lib/cjs/math/index.d.ts +0 -1
  32. package/lib/cjs/math/index.js +0 -1
  33. package/lib/cjs/offers/Offer.d.ts +218 -12
  34. package/lib/cjs/offers/Offer.js +215 -7
  35. package/lib/cjs/offers/OfferUtils.d.ts +249 -143
  36. package/lib/cjs/offers/OfferUtils.js +326 -45
  37. package/lib/cjs/queries/GetPosition.d.ts +1 -1
  38. package/lib/cjs/queries/GetPosition.js +1 -1
  39. package/lib/cjs/signatures/EcrecoverRatifierUtils.d.ts +184 -38
  40. package/lib/cjs/signatures/EcrecoverRatifierUtils.js +296 -80
  41. package/lib/cjs/signatures/Group.d.ts +12 -4
  42. package/lib/cjs/signatures/Group.js +17 -7
  43. package/lib/cjs/signatures/GroupUtils.d.ts +60 -4
  44. package/lib/cjs/signatures/GroupUtils.js +57 -2
  45. package/lib/cjs/signatures/Payload.d.ts +277 -277
  46. package/lib/cjs/signatures/Payload.js +517 -534
  47. package/lib/cjs/signatures/RatifierUtils.d.ts +57 -0
  48. package/lib/cjs/signatures/RatifierUtils.js +105 -0
  49. package/lib/cjs/signatures/SetterRatifierUtils.d.ts +52 -13
  50. package/lib/cjs/signatures/SetterRatifierUtils.js +62 -19
  51. package/lib/cjs/signatures/Tree.d.ts +28 -16
  52. package/lib/cjs/signatures/Tree.js +35 -15
  53. package/lib/cjs/signatures/TreeUtils.d.ts +185 -63
  54. package/lib/cjs/signatures/TreeUtils.js +89 -79
  55. package/lib/cjs/signatures/eip712.d.ts +22 -0
  56. package/lib/cjs/signatures/eip712.js +26 -0
  57. package/lib/cjs/signatures/index.d.ts +2 -0
  58. package/lib/cjs/signatures/index.js +4 -0
  59. package/lib/cjs/signatures/offerStructInternal.d.ts +14 -0
  60. package/lib/cjs/signatures/offerStructInternal.js +72 -0
  61. package/lib/esm/abis.d.ts +1185 -996
  62. package/lib/esm/abis.js +1445 -1211
  63. package/lib/esm/api/MidnightApi.d.ts +22 -16
  64. package/lib/esm/api/MidnightApi.js +91 -21
  65. package/lib/esm/api/helpers.d.ts +11 -1
  66. package/lib/esm/api/helpers.js +54 -5
  67. package/lib/esm/api/types.d.ts +35 -9
  68. package/lib/esm/constants.d.ts +12 -11
  69. package/lib/esm/constants.js +13 -11
  70. package/lib/esm/errors.d.ts +89 -1
  71. package/lib/esm/errors.js +108 -2
  72. package/lib/esm/fetch/Market.js +38 -4
  73. package/lib/esm/fetch/index.d.ts +0 -1
  74. package/lib/esm/fetch/index.js +0 -1
  75. package/lib/esm/fetch/utils.d.ts +1 -1
  76. package/lib/esm/fetch/utils.js +9 -2
  77. package/lib/esm/index.d.ts +1 -1
  78. package/lib/esm/index.js +1 -1
  79. package/lib/esm/market/Market.d.ts +99 -33
  80. package/lib/esm/market/Market.js +137 -28
  81. package/lib/esm/market/MarketUtils.d.ts +61 -41
  82. package/lib/esm/market/MarketUtils.js +109 -49
  83. package/lib/esm/market/Position.d.ts +16 -11
  84. package/lib/esm/market/Position.js +19 -13
  85. package/lib/esm/market/PositionUtils.d.ts +6 -4
  86. package/lib/esm/market/PositionUtils.js +9 -6
  87. package/lib/esm/math/TickLib.d.ts +22 -0
  88. package/lib/esm/math/TickLib.js +31 -1
  89. package/lib/esm/math/index.d.ts +0 -1
  90. package/lib/esm/math/index.js +0 -1
  91. package/lib/esm/offers/Offer.d.ts +218 -12
  92. package/lib/esm/offers/Offer.js +216 -8
  93. package/lib/esm/offers/OfferUtils.d.ts +249 -143
  94. package/lib/esm/offers/OfferUtils.js +329 -48
  95. package/lib/esm/queries/GetPosition.d.ts +1 -1
  96. package/lib/esm/queries/GetPosition.js +1 -1
  97. package/lib/esm/signatures/EcrecoverRatifierUtils.d.ts +184 -38
  98. package/lib/esm/signatures/EcrecoverRatifierUtils.js +298 -82
  99. package/lib/esm/signatures/Group.d.ts +12 -4
  100. package/lib/esm/signatures/Group.js +17 -7
  101. package/lib/esm/signatures/GroupUtils.d.ts +60 -4
  102. package/lib/esm/signatures/GroupUtils.js +57 -2
  103. package/lib/esm/signatures/Payload.d.ts +277 -277
  104. package/lib/esm/signatures/Payload.js +517 -531
  105. package/lib/esm/signatures/RatifierUtils.d.ts +57 -0
  106. package/lib/esm/signatures/RatifierUtils.js +105 -0
  107. package/lib/esm/signatures/SetterRatifierUtils.d.ts +52 -13
  108. package/lib/esm/signatures/SetterRatifierUtils.js +62 -19
  109. package/lib/esm/signatures/Tree.d.ts +28 -16
  110. package/lib/esm/signatures/Tree.js +36 -16
  111. package/lib/esm/signatures/TreeUtils.d.ts +185 -63
  112. package/lib/esm/signatures/TreeUtils.js +91 -81
  113. package/lib/esm/signatures/eip712.d.ts +22 -0
  114. package/lib/esm/signatures/eip712.js +22 -0
  115. package/lib/esm/signatures/index.d.ts +2 -0
  116. package/lib/esm/signatures/index.js +2 -0
  117. package/lib/esm/signatures/offerStructInternal.d.ts +14 -0
  118. package/lib/esm/signatures/offerStructInternal.js +67 -0
  119. package/package.json +31 -34
  120. package/lib/cjs/fetch/Offer.d.ts +0 -74
  121. package/lib/cjs/fetch/Offer.js +0 -119
  122. package/lib/cjs/math/ConsumableUnitsLib.d.ts +0 -57
  123. package/lib/cjs/math/ConsumableUnitsLib.js +0 -77
  124. package/lib/esm/fetch/Offer.d.ts +0 -74
  125. package/lib/esm/fetch/Offer.js +0 -116
  126. package/lib/esm/math/ConsumableUnitsLib.d.ts +0 -57
  127. package/lib/esm/math/ConsumableUnitsLib.js +0 -74
  128. package/src/index.ts +0 -19
@@ -1,9 +1,10 @@
1
- import { encodeAbiParameters, keccak256, zeroAddress, zeroHash } from "viem";
2
- import { DEFAULT_TICK_SPACING, MAX_CONTINUOUS_FEE, MAX_TICK, OFFER_TYPEHASH, } from "../constants.js";
1
+ import { assertNonNegative, DivisionByZeroError, MathLib, } from "@morpho-org/morpho-ts";
2
+ import { encodeAbiParameters, isAddressEqual, keccak256, zeroAddress, zeroHash, } from "viem";
3
+ import { DEFAULT_TICK_SPACING, MAX_OFFER_CAP, MAX_TICK, OFFER_TYPEHASH, } from "../constants.js";
3
4
  import { InvalidOfferGroupError, InvalidOfferParameterError, } from "../errors.js";
4
- import { MarketUtils } from "../market/index.js";
5
+ import { Market, MarketParams, MarketUtils } from "../market/index.js";
6
+ import { TakeAmountsLib, TickLib } from "../math/index.js";
5
7
  import { Offer, } from "./Offer.js";
6
- const comparableHex = (value) => value.toLowerCase();
7
8
  const offerHashParams = [
8
9
  { name: "typehash", type: "bytes32" },
9
10
  { name: "marketHash", type: "bytes32" },
@@ -18,8 +19,8 @@ const offerHashParams = [
18
19
  { name: "receiverIfMakerIsSeller", type: "address" },
19
20
  { name: "ratifier", type: "address" },
20
21
  { name: "reduceOnly", type: "bool" },
21
- { name: "maxUnits", type: "uint256" },
22
- { name: "maxAssets", type: "uint256" },
22
+ { name: "maxUnits", type: "uint128" },
23
+ { name: "maxAssets", type: "uint128" },
23
24
  { name: "continuousFeeCap", type: "uint256" },
24
25
  ];
25
26
  /**
@@ -56,12 +57,14 @@ export var OfferUtils;
56
57
  *
57
58
  * const offer = Offer.create({
58
59
  * market: {
60
+ * chainId: 8453,
61
+ * midnight: "0x0000000000000000000000000000000000001000",
59
62
  * loanToken: "0x0000000000000000000000000000000000006000",
60
63
  * collateralParams: [
61
64
  * {
62
65
  * token: "0x0000000000000000000000000000000000007000",
63
66
  * lltv: 770000000000000000n,
64
- * maxLif: 1061007957559681697n,
67
+ * liquidationCursor: 250000000000000000n,
65
68
  * oracle: "0x0000000000000000000000000000000000008000",
66
69
  * },
67
70
  * ],
@@ -120,12 +123,14 @@ export var OfferUtils;
120
123
  *
121
124
  * const offer = Offer.create({
122
125
  * market: {
126
+ * chainId: 8453,
127
+ * midnight: "0x0000000000000000000000000000000000001000",
123
128
  * loanToken: "0x0000000000000000000000000000000000006000",
124
129
  * collateralParams: [
125
130
  * {
126
131
  * token: "0x0000000000000000000000000000000000007000",
127
132
  * lltv: 770000000000000000n,
128
- * maxLif: 1061007957559681697n,
133
+ * liquidationCursor: 250000000000000000n,
129
134
  * oracle: "0x0000000000000000000000000000000000008000",
130
135
  * },
131
136
  * ],
@@ -183,12 +188,14 @@ export var OfferUtils;
183
188
  *
184
189
  * const offer = Offer.create({
185
190
  * market: {
191
+ * chainId: 8453,
192
+ * midnight: "0x0000000000000000000000000000000000001000",
186
193
  * loanToken: "0x0000000000000000000000000000000000006000",
187
194
  * collateralParams: [
188
195
  * {
189
196
  * token: "0x0000000000000000000000000000000000007000",
190
197
  * lltv: 770000000000000000n,
191
- * maxLif: 1061007957559681697n,
198
+ * liquidationCursor: 250000000000000000n,
192
199
  * oracle: "0x0000000000000000000000000000000000008000",
193
200
  * },
194
201
  * ],
@@ -229,12 +236,14 @@ export var OfferUtils;
229
236
  *
230
237
  * const offer = Offer.create({
231
238
  * market: {
239
+ * chainId: 8453,
240
+ * midnight: "0x0000000000000000000000000000000000001000",
232
241
  * loanToken: "0x0000000000000000000000000000000000006000",
233
242
  * collateralParams: [
234
243
  * {
235
244
  * token: "0x0000000000000000000000000000000000007000",
236
245
  * lltv: 770000000000000000n,
237
- * maxLif: 1061007957559681697n,
246
+ * liquidationCursor: 250000000000000000n,
238
247
  * oracle: "0x0000000000000000000000000000000000008000",
239
248
  * },
240
249
  * ],
@@ -312,7 +321,7 @@ export var OfferUtils;
312
321
  * @param params.start - Optional offer start timestamp; defaults to zero.
313
322
  * @param params.expiry - Offer expiry timestamp.
314
323
  * @returns Normalized start and expiry timestamps.
315
- * @throws {InvalidOfferParameterError} when the range is negative or not strictly increasing.
324
+ * @throws {InvalidOfferParameterError} when the range is negative or expiry is before start.
316
325
  * @example
317
326
  * ```ts
318
327
  * import { OfferUtils } from "@morpho-org/midnight-sdk";
@@ -331,11 +340,11 @@ export var OfferUtils;
331
340
  instruction: "Use a non-negative timestamp.",
332
341
  });
333
342
  }
334
- if (expiry < 0n || expiry <= start) {
343
+ if (expiry < 0n || expiry < start) {
335
344
  throw new InvalidOfferParameterError({
336
345
  parameter: "expiry",
337
346
  value: expiry,
338
- instruction: `Use an expiry greater than start "${start}".`,
347
+ instruction: `Use an expiry greater than or equal to start "${start}".`,
339
348
  });
340
349
  }
341
350
  return { start, expiry };
@@ -376,6 +385,20 @@ export var OfferUtils;
376
385
  instruction: "Use a non-negative cap.",
377
386
  });
378
387
  }
388
+ if (maxUnits > MAX_OFFER_CAP) {
389
+ throw new InvalidOfferParameterError({
390
+ parameter: "maxUnits",
391
+ value: maxUnits,
392
+ instruction: `Use a cap less than or equal to "${MAX_OFFER_CAP}".`,
393
+ });
394
+ }
395
+ if (maxAssets > MAX_OFFER_CAP) {
396
+ throw new InvalidOfferParameterError({
397
+ parameter: "maxAssets",
398
+ value: maxAssets,
399
+ instruction: `Use a cap less than or equal to "${MAX_OFFER_CAP}".`,
400
+ });
401
+ }
379
402
  if ((maxAssets === 0n) === (maxUnits === 0n)) {
380
403
  throw new InvalidOfferParameterError({
381
404
  parameter: "maxUnits/maxAssets",
@@ -390,8 +413,8 @@ export var OfferUtils;
390
413
  * Validates and normalizes the maximum continuous fee an offer accepts.
391
414
  *
392
415
  * Use this before `Offer.create` when a maker UI exposes fee tolerance.
393
- * `Offer.create` calls it internally and defaults omitted values to
394
- * {@link MAX_CONTINUOUS_FEE}.
416
+ * `Offer.create` calls it internally and defaults omitted values to zero so
417
+ * fresh offers fail closed unless the maker explicitly accepts a fee cap.
395
418
  *
396
419
  * @param params.continuousFeeCap - Optional maximum market continuous fee accepted by this offer.
397
420
  * @returns Normalized continuous fee cap.
@@ -405,7 +428,7 @@ export var OfferUtils;
405
428
  * ```
406
429
  */
407
430
  function validateContinuousFeeCap(params) {
408
- const continuousFeeCap = BigInt(params.continuousFeeCap ?? MAX_CONTINUOUS_FEE);
431
+ const continuousFeeCap = BigInt(params.continuousFeeCap ?? 0n);
409
432
  if (continuousFeeCap < 0n) {
410
433
  throw new InvalidOfferParameterError({
411
434
  parameter: "continuousFeeCap",
@@ -458,22 +481,16 @@ export var OfferUtils;
458
481
  * instantiating an `Offer`. `Offer.create` uses the same validation and then
459
482
  * constructs the class instance for grouping and trees.
460
483
  *
461
- * @param params.market - Market params or hydrated market this offer trades.
462
484
  * @param params.buy - Whether the maker buys loan assets.
463
485
  * @param params.maker - Maker address.
464
486
  * @param params.tick - Offer tick.
465
- * @param params.group - Optional consumption group id for already-grouped offers.
466
487
  * @param params.tickSpacing - Optional market tick spacing; defaults to `DEFAULT_TICK_SPACING`.
467
488
  * @param params.maxUnits - Optional unit cap; defaults to zero.
468
489
  * @param params.maxAssets - Optional buyer or seller asset cap; defaults to zero.
469
- * @param params.continuousFeeCap - Optional maximum market continuous fee accepted by this offer; defaults to `MAX_CONTINUOUS_FEE`.
490
+ * @param params.continuousFeeCap - Optional maximum market continuous fee accepted by this offer; defaults to zero.
470
491
  * @param params.start - Optional offer start timestamp; defaults to zero.
471
492
  * @param params.expiry - Offer expiry timestamp.
472
- * @param params.callback - Optional callback address; defaults to the zero address.
473
- * @param params.callbackData - Optional callback payload; defaults to `0x`.
474
493
  * @param params.receiverIfMakerIsSeller - Optional receiver used when maker is seller.
475
- * @param params.ratifier - Ratifier contract address.
476
- * @param params.reduceOnly - Optional flag restricting the offer to exposure-reducing execution.
477
494
  * @returns Normalized deterministic offer parameters.
478
495
  * @throws {InvalidOfferParameterError} when a deterministic offer parameter cannot satisfy protocol rules.
479
496
  * @example
@@ -481,26 +498,10 @@ export var OfferUtils;
481
498
  * import { OfferUtils } from "@morpho-org/midnight-sdk";
482
499
  *
483
500
  * const params = OfferUtils.validateOfferParams({
484
- * market: {
485
- * loanToken: "0x0000000000000000000000000000000000006000",
486
- * collateralParams: [
487
- * {
488
- * token: "0x0000000000000000000000000000000000007000",
489
- * lltv: 770000000000000000n,
490
- * maxLif: 1061007957559681697n,
491
- * oracle: "0x0000000000000000000000000000000000008000",
492
- * },
493
- * ],
494
- * maturity: 54_000n,
495
- * rcfThreshold: 0n,
496
- * enterGate: "0x0000000000000000000000000000000000000000",
497
- * liquidatorGate: "0x0000000000000000000000000000000000000000",
498
- * },
499
501
  * buy: true,
500
502
  * maker: "0x0000000000000000000000000000000000009000",
501
503
  * tick: 5_000n,
502
504
  * expiry: 3_600n,
503
- * ratifier: "0x0000000000000000000000000000000000004000",
504
505
  * maxUnits: 100n,
505
506
  * });
506
507
  * console.log(params.maxAssets);
@@ -543,12 +544,14 @@ export var OfferUtils;
543
544
  *
544
545
  * const offer = Offer.create({
545
546
  * market: {
547
+ * chainId: 8453,
548
+ * midnight: "0x0000000000000000000000000000000000001000",
546
549
  * loanToken: "0x0000000000000000000000000000000000006000",
547
550
  * collateralParams: [
548
551
  * {
549
552
  * token: "0x0000000000000000000000000000000000007000",
550
553
  * lltv: 770000000000000000n,
551
- * maxLif: 1061007957559681697n,
554
+ * liquidationCursor: 250000000000000000n,
552
555
  * oracle: "0x0000000000000000000000000000000000008000",
553
556
  * },
554
557
  * ],
@@ -575,39 +578,315 @@ export var OfferUtils;
575
578
  }
576
579
  const offers = offerInputs.map((offer) => Offer.from(offer));
577
580
  const first = offers[0];
578
- const expectedMaker = comparableHex(first.maker);
581
+ const expectedMaker = first.maker;
579
582
  const expectedBuy = first.buy;
580
- const expectedLoanToken = comparableHex(first.market.loanToken);
583
+ const expectedLoanToken = MarketParams.from(first.market).loanToken;
584
+ const expectedChainId = MarketParams.from(first.market).chainId;
585
+ const expectedMidnight = MarketParams.from(first.market).midnight;
581
586
  const expectedMaxUnits = first.maxUnits;
582
587
  const expectedMaxAssets = first.maxAssets;
583
588
  for (const offer of offers) {
584
- if (comparableHex(offer.maker) !== expectedMaker) {
589
+ if (!isAddressEqual(offer.maker, expectedMaker)) {
585
590
  throw new InvalidOfferGroupError("All offers in a group must use the same maker.");
586
591
  }
587
592
  if (offer.buy !== expectedBuy) {
588
593
  throw new InvalidOfferGroupError("All offers in a group must use the same maker side.");
589
594
  }
590
595
  if (offer.maxUnits < 0n ||
596
+ offer.maxUnits > MAX_OFFER_CAP ||
591
597
  offer.maxAssets < 0n ||
598
+ offer.maxAssets > MAX_OFFER_CAP ||
592
599
  (offer.maxAssets === 0n) === (offer.maxUnits === 0n)) {
593
- throw new InvalidOfferGroupError("Every offer must set exactly one non-zero non-negative cap.");
600
+ throw new InvalidOfferGroupError("Every offer must set exactly one non-zero uint128 cap.");
594
601
  }
595
- if (comparableHex(offer.market.loanToken) !== expectedLoanToken) {
602
+ if (!isAddressEqual(MarketParams.from(offer.market).loanToken, expectedLoanToken)) {
596
603
  throw new InvalidOfferGroupError("All offers in a group must use the same loan token.");
597
604
  }
605
+ if (MarketParams.from(offer.market).chainId !== expectedChainId) {
606
+ throw new InvalidOfferGroupError("All offers in a group must use the same chain id.");
607
+ }
608
+ if (!isAddressEqual(MarketParams.from(offer.market).midnight, expectedMidnight)) {
609
+ throw new InvalidOfferGroupError("All offers in a group must use the same Midnight contract.");
610
+ }
598
611
  if (offer.maxUnits !== expectedMaxUnits ||
599
612
  offer.maxAssets !== expectedMaxAssets) {
600
613
  throw new InvalidOfferGroupError("All offers in a group must use the same cap mode and value.");
601
614
  }
602
615
  if (offer.buy &&
603
- comparableHex(offer.receiverIfMakerIsSeller) !==
604
- comparableHex(zeroAddress)) {
616
+ !isAddressEqual(offer.receiverIfMakerIsSeller, zeroAddress)) {
605
617
  throw new InvalidOfferGroupError("Buy offers must use the zero address as receiverIfMakerIsSeller.");
606
618
  }
607
619
  }
608
620
  return [...offers];
609
621
  }
610
622
  OfferUtils.validateOfferGroup = validateOfferGroup;
623
+ /**
624
+ * Converts an offer tick into its WAD zero-coupon price.
625
+ *
626
+ * Use for display or local quote checks when accepting any plain object
627
+ * matching `IOffer` or an `Offer` instance.
628
+ *
629
+ * @param offer - Offer to inspect.
630
+ * @returns WAD price rounded to the protocol price quantum.
631
+ * @throws {NegativeValueError} when `offer.tick` is negative.
632
+ * @throws {TickOutOfRangeError} when `offer.tick` exceeds `MAX_TICK`.
633
+ * @example
634
+ * ```ts
635
+ * import { OfferUtils } from "@morpho-org/midnight-sdk";
636
+ *
637
+ * const price = OfferUtils.getPrice({ tick: 5_000n });
638
+ * console.log(price);
639
+ * ```
640
+ */
641
+ function getPrice(offer) {
642
+ return TickLib.tickToPrice(offer.tick);
643
+ }
644
+ OfferUtils.getPrice = getPrice;
645
+ /**
646
+ * Converts an offer tick into a WAD per-second simple rate at a timestamp.
647
+ *
648
+ * The rate is the offer's fixed period rate divided by the market's remaining
649
+ * time to maturity at `timestamp`, rounded up.
650
+ *
651
+ * @param params.offer - Offer to inspect.
652
+ * @param params.timestamp - Timestamp at which the rate is calculated.
653
+ * @returns WAD per-second simple rate rounded up.
654
+ * @throws {NegativeValueError} when `market.maturity`, `timestamp`, or `tick` is negative.
655
+ * @throws {TickOutOfRangeError} when `tick` exceeds `MAX_TICK`.
656
+ * @throws {DivisionByZeroError} when the tick price is zero or `timestamp` is at or after maturity.
657
+ * @example
658
+ * ```ts
659
+ * import { Offer, OfferUtils } from "@morpho-org/midnight-sdk";
660
+ *
661
+ * const offer = Offer.from({
662
+ * market: {
663
+ * loanToken: "0x0000000000000000000000000000000000006000",
664
+ * collateralParams: [
665
+ * {
666
+ * token: "0x0000000000000000000000000000000000007000",
667
+ * lltv: 770000000000000000n,
668
+ * liquidationCursor: 250000000000000000n,
669
+ * oracle: "0x0000000000000000000000000000000000008000",
670
+ * },
671
+ * ],
672
+ * maturity: 54_000n,
673
+ * rcfThreshold: 0n,
674
+ * enterGate: "0x0000000000000000000000000000000000000000",
675
+ * liquidatorGate: "0x0000000000000000000000000000000000000000",
676
+ * },
677
+ * buy: true,
678
+ * maker: "0x0000000000000000000000000000000000009000",
679
+ * start: 0n,
680
+ * expiry: 3_600n,
681
+ * tick: 5_000n,
682
+ * callback: "0x0000000000000000000000000000000000000000",
683
+ * callbackData: "0x",
684
+ * receiverIfMakerIsSeller: "0x0000000000000000000000000000000000000000",
685
+ * ratifier: "0x0000000000000000000000000000000000004000",
686
+ * reduceOnly: false,
687
+ * maxUnits: 100n,
688
+ * maxAssets: 0n,
689
+ * continuousFeeCap: 317097919n,
690
+ * });
691
+ * const rate = OfferUtils.getRate({ offer, timestamp: 1_000n });
692
+ * console.log(rate);
693
+ * ```
694
+ */
695
+ function getRate(params) {
696
+ const market = MarketParams.from(params.offer.market);
697
+ const timestamp = BigInt(params.timestamp);
698
+ assertNonNegative("market.maturity", market.maturity);
699
+ assertNonNegative("timestamp", timestamp);
700
+ const timeToMaturity = MathLib.zeroFloorSub(market.maturity, timestamp);
701
+ if (timeToMaturity === 0n) {
702
+ throw new DivisionByZeroError("timeToMaturity");
703
+ }
704
+ return MathLib.mulDiv(TickLib.tickToRate(params.offer.tick), 1n, timeToMaturity, "Up");
705
+ }
706
+ OfferUtils.getRate = getRate;
707
+ /**
708
+ * Converts an offer tick into a WAD simple annual percentage rate at a timestamp.
709
+ *
710
+ * It computes time to maturity from the offer market and delegates the final
711
+ * tick annualization to `TickLib.tickToApr`.
712
+ *
713
+ * @param params.offer - Offer to inspect.
714
+ * @param params.timestamp - Timestamp at which the APR is calculated.
715
+ * @returns WAD simple APR rounded up.
716
+ * @throws {NegativeValueError} when `market.maturity`, `timestamp`, or `tick` is negative.
717
+ * @throws {TickOutOfRangeError} when `tick` exceeds `MAX_TICK`.
718
+ * @throws {DivisionByZeroError} when the tick price is zero or `timestamp` is at or after maturity.
719
+ * @example
720
+ * ```ts
721
+ * import { Offer, OfferUtils } from "@morpho-org/midnight-sdk";
722
+ *
723
+ * const offer = Offer.from({
724
+ * market: {
725
+ * loanToken: "0x0000000000000000000000000000000000006000",
726
+ * collateralParams: [
727
+ * {
728
+ * token: "0x0000000000000000000000000000000000007000",
729
+ * lltv: 770000000000000000n,
730
+ * liquidationCursor: 250000000000000000n,
731
+ * oracle: "0x0000000000000000000000000000000000008000",
732
+ * },
733
+ * ],
734
+ * maturity: 54_000n,
735
+ * rcfThreshold: 0n,
736
+ * enterGate: "0x0000000000000000000000000000000000000000",
737
+ * liquidatorGate: "0x0000000000000000000000000000000000000000",
738
+ * },
739
+ * buy: true,
740
+ * maker: "0x0000000000000000000000000000000000009000",
741
+ * start: 0n,
742
+ * expiry: 3_600n,
743
+ * tick: 5_000n,
744
+ * callback: "0x0000000000000000000000000000000000000000",
745
+ * callbackData: "0x",
746
+ * receiverIfMakerIsSeller: "0x0000000000000000000000000000000000000000",
747
+ * ratifier: "0x0000000000000000000000000000000000004000",
748
+ * reduceOnly: false,
749
+ * maxUnits: 100n,
750
+ * maxAssets: 0n,
751
+ * continuousFeeCap: 317097919n,
752
+ * });
753
+ * const apr = OfferUtils.getApr({ offer, timestamp: 1_000n });
754
+ * console.log(apr);
755
+ * ```
756
+ */
757
+ function getApr(params) {
758
+ const market = MarketParams.from(params.offer.market);
759
+ const timestamp = BigInt(params.timestamp);
760
+ assertNonNegative("market.maturity", market.maturity);
761
+ assertNonNegative("timestamp", timestamp);
762
+ return TickLib.tickToApr(params.offer.tick, MathLib.zeroFloorSub(market.maturity, timestamp));
763
+ }
764
+ OfferUtils.getApr = getApr;
765
+ /**
766
+ * Returns remaining units accepted by an offer's cap at a timestamp.
767
+ *
768
+ * The offer must carry a hydrated {@link Market} so the current market
769
+ * continuous fee and settlement-fee buckets are available locally. Fetch the
770
+ * current `consumed(maker, group)` value separately and pass it as input.
771
+ *
772
+ * @param params.offer - Offer to inspect. Its market must be hydrated.
773
+ * @param params.consumed - Amount already consumed from the offer group.
774
+ * @param params.timestamp - Timestamp used to compute market time to maturity.
775
+ * @returns Remaining consumable units accepted by Midnight `take`.
776
+ * @throws {InvalidOfferParameterError} when the offer does not carry hydrated market state or has invalid caps.
777
+ * @throws {NegativeValueError} when `consumed`, `timestamp`, offer limits, or delegated math inputs are negative.
778
+ * @throws {TickOutOfRangeError} when `tick` exceeds `MAX_TICK`.
779
+ * @throws {SettlementFeeExceedsPriceError} when settlement fee exceeds a buy-offer price.
780
+ * @example
781
+ * ```ts
782
+ * import { OfferUtils, midnightAbi } from "@morpho-org/midnight-sdk";
783
+ * import { createPublicClient, http } from "viem";
784
+ * import { base } from "viem/chains";
785
+ * import { readContract } from "viem/actions";
786
+ *
787
+ * const client = createPublicClient({ chain: base, transport: http() });
788
+ * const offer = {
789
+ * market: {
790
+ * params: {
791
+ * chainId: 8453,
792
+ * midnight: "0x0000000000000000000000000000000000001000",
793
+ * loanToken: "0x0000000000000000000000000000000000006000",
794
+ * collateralParams: [
795
+ * {
796
+ * token: "0x0000000000000000000000000000000000007000",
797
+ * lltv: 770000000000000000n,
798
+ * liquidationCursor: 250000000000000000n,
799
+ * oracle: "0x0000000000000000000000000000000000008000",
800
+ * },
801
+ * ],
802
+ * maturity: 54_000n,
803
+ * rcfThreshold: 0n,
804
+ * enterGate: "0x0000000000000000000000000000000000000000",
805
+ * liquidatorGate: "0x0000000000000000000000000000000000000000",
806
+ * },
807
+ * totalUnits: 1_000n,
808
+ * lossFactor: 0n,
809
+ * withdrawable: 500n,
810
+ * continuousFeeCredit: 0n,
811
+ * settlementFeeCbps: [1, 2, 3, 4, 5, 6, 7],
812
+ * continuousFee: 10,
813
+ * tickSpacing: 4,
814
+ * },
815
+ * buy: true,
816
+ * maker: "0x0000000000000000000000000000000000009000",
817
+ * start: 0n,
818
+ * expiry: 3_600n,
819
+ * tick: 5_000n,
820
+ * callback: "0x0000000000000000000000000000000000000000",
821
+ * callbackData: "0x",
822
+ * receiverIfMakerIsSeller: "0x0000000000000000000000000000000000000000",
823
+ * ratifier: "0x0000000000000000000000000000000000004000",
824
+ * reduceOnly: false,
825
+ * maxUnits: 100n,
826
+ * maxAssets: 0n,
827
+ * continuousFeeCap: 317097919n,
828
+ * } as const;
829
+ * const consumed = await readContract(client, {
830
+ * address: "0x0000000000000000000000000000000000001000",
831
+ * abi: midnightAbi,
832
+ * functionName: "consumed",
833
+ * args: [offer.maker, "0x1111111111111111111111111111111111111111111111111111111111111111"],
834
+ * });
835
+ * const units = OfferUtils.getConsumableUnits({ offer, consumed, timestamp: 1_000n });
836
+ * console.log(units);
837
+ * ```
838
+ */
839
+ function getConsumableUnits(params) {
840
+ const offer = params.offer;
841
+ const consumed = BigInt(params.consumed);
842
+ const timestamp = BigInt(params.timestamp);
843
+ const start = BigInt(offer.start);
844
+ const expiry = BigInt(offer.expiry);
845
+ const maxUnits = BigInt(offer.maxUnits);
846
+ const maxAssets = BigInt(offer.maxAssets);
847
+ const continuousFeeCap = BigInt(offer.continuousFeeCap);
848
+ assertNonNegative("consumed", consumed);
849
+ assertNonNegative("timestamp", timestamp);
850
+ assertNonNegative("offer.start", start);
851
+ assertNonNegative("offer.expiry", expiry);
852
+ assertNonNegative("offer.maxUnits", maxUnits);
853
+ assertNonNegative("offer.maxAssets", maxAssets);
854
+ assertNonNegative("offer.continuousFeeCap", continuousFeeCap);
855
+ validateOfferCaps({ maxUnits, maxAssets });
856
+ if (!("params" in offer.market)) {
857
+ throw new InvalidOfferParameterError({
858
+ parameter: "market",
859
+ value: offer.market,
860
+ instruction: "Provide a hydrated Market with continuous fee and settlement fee buckets.",
861
+ });
862
+ }
863
+ const market = Market.from(offer.market);
864
+ if (timestamp < start || timestamp > expiry)
865
+ return 0n;
866
+ if (continuousFeeCap < BigInt(market.continuousFee))
867
+ return 0n;
868
+ if (maxUnits > 0n)
869
+ return MathLib.zeroFloorSub(maxUnits, consumed);
870
+ const remainingAssets = MathLib.zeroFloorSub(maxAssets, consumed);
871
+ const settlementFee = market.getSettlementFee(market.timeToMaturity(timestamp));
872
+ if (!offer.buy) {
873
+ const { sellerPrice } = TakeAmountsLib.prices({
874
+ offer,
875
+ settlementFee,
876
+ });
877
+ if (sellerPrice === 0n)
878
+ return MathLib.MAX_UINT_256;
879
+ return MathLib.mulDivDown(remainingAssets, MathLib.WAD, sellerPrice);
880
+ }
881
+ const { buyerPrice } = TakeAmountsLib.prices({
882
+ offer,
883
+ settlementFee,
884
+ });
885
+ if (buyerPrice === 0n)
886
+ return MathLib.MAX_UINT_256;
887
+ return ((remainingAssets + 1n) * MathLib.WAD - 1n) / buyerPrice;
888
+ }
889
+ OfferUtils.getConsumableUnits = getConsumableUnits;
611
890
  /**
612
891
  * Returns an offer expiry timestamp.
613
892
  *
@@ -624,12 +903,14 @@ export var OfferUtils;
624
903
  *
625
904
  * const offer = Offer.create({
626
905
  * market: {
906
+ * chainId: 8453,
907
+ * midnight: "0x0000000000000000000000000000000000001000",
627
908
  * loanToken: "0x0000000000000000000000000000000000006000",
628
909
  * collateralParams: [
629
910
  * {
630
911
  * token: "0x0000000000000000000000000000000000007000",
631
912
  * lltv: 770000000000000000n,
632
- * maxLif: 1061007957559681697n,
913
+ * liquidationCursor: 250000000000000000n,
633
914
  * oracle: "0x0000000000000000000000000000000000008000",
634
915
  * },
635
916
  * ],
@@ -52,4 +52,4 @@ export declare const abi: readonly [{
52
52
  readonly type: "function";
53
53
  }];
54
54
  /** @internal Deployless `GetPosition` query bytecode. */
55
- export declare const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea264697066735822122047b31034ba6d0d5a88fb953996f2d97fa501664a716d124ff2e42e0782119d9164736f6c63430008230033";
55
+ export declare const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea264697066735822122048ab3e1a986178f7e3c65d73416611318a31ef34d18800cb6a1f1ba4dad746b364736f6c63430008230033";
@@ -68,4 +68,4 @@ export const abi = [
68
68
  },
69
69
  ];
70
70
  /** @internal Deployless `GetPosition` query bytecode. */
71
- export const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea264697066735822122047b31034ba6d0d5a88fb953996f2d97fa501664a716d124ff2e42e0782119d9164736f6c63430008230033";
71
+ export const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea264697066735822122048ab3e1a986178f7e3c65d73416611318a31ef34d18800cb6a1f1ba4dad746b364736f6c63430008230033";