@morpho-org/midnight-sdk 1.1.1 → 1.2.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 (41) hide show
  1. package/lib/cjs/fetch/Position.d.ts +1 -1
  2. package/lib/cjs/fetch/Position.js +5 -1
  3. package/lib/cjs/index.d.ts +1 -0
  4. package/lib/cjs/index.js +1 -0
  5. package/lib/cjs/market/index.d.ts +0 -2
  6. package/lib/cjs/market/index.js +0 -2
  7. package/lib/cjs/offers/OfferChainUtils.d.ts +179 -0
  8. package/lib/cjs/offers/OfferChainUtils.js +446 -0
  9. package/lib/cjs/offers/index.d.ts +1 -0
  10. package/lib/cjs/offers/index.js +1 -0
  11. package/lib/cjs/{market → position}/Position.d.ts +83 -6
  12. package/lib/cjs/{market → position}/Position.js +83 -5
  13. package/lib/{esm/market → cjs/position}/PositionUtils.d.ts +26 -1
  14. package/lib/cjs/{market → position}/PositionUtils.js +27 -0
  15. package/lib/cjs/position/index.d.ts +2 -0
  16. package/lib/cjs/position/index.js +18 -0
  17. package/lib/cjs/signatures/RatifierUtils.js +2 -1
  18. package/lib/cjs/signatures/Tree.js +3 -1
  19. package/lib/cjs/signatures/TreeUtils.d.ts +2 -2
  20. package/lib/cjs/signatures/TreeUtils.js +17 -8
  21. package/lib/esm/fetch/Position.d.ts +1 -1
  22. package/lib/esm/fetch/Position.js +5 -1
  23. package/lib/esm/index.d.ts +1 -0
  24. package/lib/esm/index.js +1 -0
  25. package/lib/esm/market/index.d.ts +0 -2
  26. package/lib/esm/market/index.js +0 -2
  27. package/lib/esm/offers/OfferChainUtils.d.ts +179 -0
  28. package/lib/esm/offers/OfferChainUtils.js +443 -0
  29. package/lib/esm/offers/index.d.ts +1 -0
  30. package/lib/esm/offers/index.js +1 -0
  31. package/lib/esm/{market → position}/Position.d.ts +83 -6
  32. package/lib/esm/{market → position}/Position.js +83 -5
  33. package/lib/{cjs/market → esm/position}/PositionUtils.d.ts +26 -1
  34. package/lib/esm/{market → position}/PositionUtils.js +27 -0
  35. package/lib/esm/position/index.d.ts +2 -0
  36. package/lib/esm/position/index.js +2 -0
  37. package/lib/esm/signatures/RatifierUtils.js +2 -1
  38. package/lib/esm/signatures/Tree.js +3 -1
  39. package/lib/esm/signatures/TreeUtils.d.ts +2 -2
  40. package/lib/esm/signatures/TreeUtils.js +17 -8
  41. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { _try, MathLib } from "@morpho-org/morpho-ts";
2
2
  import { UnknownCollateralIndexError } from "../errors.js";
3
- import { Market } from "./Market.js";
3
+ import { Market } from "../market/Market.js";
4
4
  import { PositionUtils } from "./PositionUtils.js";
5
5
  /**
6
6
  * Midnight user position as stored by the core contract.
@@ -10,6 +10,8 @@ import { PositionUtils } from "./PositionUtils.js";
10
10
  * import { Position } from "@morpho-org/midnight-sdk";
11
11
  *
12
12
  * const position = new Position({
13
+ * user: "0x0000000000000000000000000000000000009000",
14
+ * marketId: "0x0000000000000000000000000000000000000000000000000000000000000001",
13
15
  * credit: 0n,
14
16
  * pendingFee: 0n,
15
17
  * lastLossFactor: 0n,
@@ -22,6 +24,10 @@ import { PositionUtils } from "./PositionUtils.js";
22
24
  * ```
23
25
  */
24
26
  export class Position {
27
+ /** User holding this position. */
28
+ user;
29
+ /** Id of the market on which this position is held. */
30
+ marketId;
25
31
  /** User credit. */
26
32
  credit;
27
33
  /** Pending continuous fee. */
@@ -37,6 +43,8 @@ export class Position {
37
43
  /** Collateral balances by index. */
38
44
  collateral;
39
45
  constructor(position) {
46
+ this.user = position.user;
47
+ this.marketId = position.marketId;
40
48
  this.credit = BigInt(position.credit);
41
49
  this.pendingFee = BigInt(position.pendingFee);
42
50
  this.lastLossFactor = BigInt(position.lastLossFactor);
@@ -54,6 +62,8 @@ export class Position {
54
62
  * import { Position } from "@morpho-org/midnight-sdk";
55
63
  *
56
64
  * const position = new Position({
65
+ * user: "0x0000000000000000000000000000000000009000",
66
+ * marketId: "0x0000000000000000000000000000000000000000000000000000000000000001",
57
67
  * credit: 1_000n,
58
68
  * pendingFee: 100n,
59
69
  * lastLossFactor: 0n,
@@ -93,6 +103,7 @@ export class Position {
93
103
  *
94
104
  * const position = new AccrualPosition(
95
105
  * {
106
+ * user: "0x0000000000000000000000000000000000009000",
96
107
  * credit: 1_000n,
97
108
  * pendingFee: 100n,
98
109
  * lastLossFactor: 0n,
@@ -132,11 +143,74 @@ export class Position {
132
143
  * ```
133
144
  */
134
145
  export class AccrualPosition extends Position {
135
- /** Hydrated market for this position. */
136
- market;
146
+ _market;
137
147
  constructor(position, market) {
138
- super(position);
139
- this.market = Market.from(market);
148
+ const _market = Market.from(market);
149
+ super({ ...position, marketId: _market.id });
150
+ this._market = _market;
151
+ }
152
+ /** Hydrated market for this position. */
153
+ get market() {
154
+ return this._market;
155
+ }
156
+ /**
157
+ * Returns the maximum credit units currently withdrawable from this position.
158
+ *
159
+ * The protocol capacity is limited by both the accrued position credit and
160
+ * the market's currently withdrawable liquidity. Default redemption flows may
161
+ * still choose the position face value.
162
+ *
163
+ * @returns Maximum credit units currently withdrawable.
164
+ * @example
165
+ * ```ts
166
+ * import { AccrualPosition } from "@morpho-org/midnight-sdk";
167
+ *
168
+ * const position = new AccrualPosition(
169
+ * {
170
+ * user: "0x0000000000000000000000000000000000009000",
171
+ * credit: 1_000n,
172
+ * pendingFee: 100n,
173
+ * lastLossFactor: 0n,
174
+ * lastAccrual: 1_000n,
175
+ * debt: 0n,
176
+ * collateralBitmap: 1n,
177
+ * collateral: [50n],
178
+ * },
179
+ * {
180
+ * params: {
181
+ * chainId: 8453,
182
+ * midnight: "0x0000000000000000000000000000000000001000",
183
+ * loanToken: "0x0000000000000000000000000000000000006000",
184
+ * collateralParams: [
185
+ * {
186
+ * token: "0x0000000000000000000000000000000000007000",
187
+ * lltv: 770000000000000000n,
188
+ * liquidationCursor: 250000000000000000n,
189
+ * oracle: "0x0000000000000000000000000000000000008000",
190
+ * },
191
+ * ],
192
+ * maturity: 54_000n,
193
+ * rcfThreshold: 0n,
194
+ * enterGate: "0x0000000000000000000000000000000000000000",
195
+ * liquidatorGate: "0x0000000000000000000000000000000000000000",
196
+ * },
197
+ * totalUnits: 1_000n,
198
+ * lossFactor: 0n,
199
+ * withdrawable: 950n,
200
+ * continuousFeeCredit: 0n,
201
+ * settlementFeeCbps: [1, 2, 3, 4, 5, 6, 7],
202
+ * continuousFee: 10,
203
+ * tickSpacing: 4,
204
+ * },
205
+ * );
206
+ * console.log(position.withdrawable); // 950n
207
+ * ```
208
+ */
209
+ get withdrawable() {
210
+ return PositionUtils.getWithdrawable({
211
+ position: this,
212
+ market: this.market,
213
+ });
140
214
  }
141
215
  /**
142
216
  * Returns the position collateral balance at an index configured by the market.
@@ -164,6 +238,7 @@ export class AccrualPosition extends Position {
164
238
  *
165
239
  * const position = new AccrualPosition(
166
240
  * {
241
+ * user: "0x0000000000000000000000000000000000009000",
167
242
  * credit: 1_000n,
168
243
  * pendingFee: 100n,
169
244
  * lastLossFactor: 0n,
@@ -236,6 +311,7 @@ export class AccrualPosition extends Position {
236
311
  * const collateralToken = "0x0000000000000000000000000000000000007000";
237
312
  * const position = new AccrualPosition(
238
313
  * {
314
+ * user: "0x0000000000000000000000000000000000009000",
239
315
  * credit: 1_000n,
240
316
  * pendingFee: 100n,
241
317
  * lastLossFactor: 0n,
@@ -305,6 +381,7 @@ export class AccrualPosition extends Position {
305
381
  *
306
382
  * const position = new AccrualPosition(
307
383
  * {
384
+ * user: "0x0000000000000000000000000000000000009000",
308
385
  * credit: 1_000n,
309
386
  * pendingFee: 100n,
310
387
  * lastLossFactor: 0n,
@@ -376,6 +453,7 @@ export class AccrualPosition extends Position {
376
453
  *
377
454
  * const position = new AccrualPosition(
378
455
  * {
456
+ * user: "0x0000000000000000000000000000000000009000",
379
457
  * credit: 1_000n,
380
458
  * pendingFee: 100n,
381
459
  * lastLossFactor: 0n,
@@ -1,5 +1,5 @@
1
1
  import { type BigIntish } from "@morpho-org/morpho-ts";
2
- import type { IMarket } from "./Market.js";
2
+ import type { IMarket } from "../market/Market.js";
3
3
  import type { IPosition } from "./Position.js";
4
4
  /**
5
5
  * Domain helpers for Midnight positions.
@@ -10,6 +10,8 @@ import type { IPosition } from "./Position.js";
10
10
  *
11
11
  * const accrued = PositionUtils.accrueInterest({
12
12
  * position: {
13
+ * user: "0x0000000000000000000000000000000000009000",
14
+ * marketId: "0x0000000000000000000000000000000000000000000000000000000000000001",
13
15
  * credit: 1_000n,
14
16
  * pendingFee: 100n,
15
17
  * lastLossFactor: 0n,
@@ -50,6 +52,27 @@ import type { IPosition } from "./Position.js";
50
52
  * ```
51
53
  */
52
54
  export declare namespace PositionUtils {
55
+ /**
56
+ * Returns the maximum credit units currently withdrawable by a position.
57
+ *
58
+ * @param params.position - Position whose credit limits the withdrawal.
59
+ * @param params.market - Market whose available liquidity limits the withdrawal.
60
+ * @returns The lower of the position credit and market withdrawable liquidity.
61
+ * @example
62
+ * ```ts
63
+ * import { PositionUtils } from "@morpho-org/midnight-sdk";
64
+ *
65
+ * const withdrawable = PositionUtils.getWithdrawable({
66
+ * position: { credit: 1_000n },
67
+ * market: { withdrawable: 500n },
68
+ * });
69
+ * console.log(withdrawable); // 500n
70
+ * ```
71
+ */
72
+ function getWithdrawable(params: {
73
+ readonly position: Pick<IPosition, "credit">;
74
+ readonly market: Pick<IMarket, "withdrawable">;
75
+ }): bigint;
53
76
  /**
54
77
  * Returns plain Midnight position and market objects accrued like Midnight `updatePositionView`.
55
78
  *
@@ -66,6 +89,8 @@ export declare namespace PositionUtils {
66
89
  *
67
90
  * const { position, market } = PositionUtils.accrueInterest({
68
91
  * position: {
92
+ * user: "0x0000000000000000000000000000000000009000",
93
+ * marketId: "0x0000000000000000000000000000000000000000000000000000000000000001",
69
94
  * credit: 1_000n,
70
95
  * pendingFee: 100n,
71
96
  * lastLossFactor: 0n,
@@ -9,6 +9,8 @@ import { InvalidPositionAccrualStateError, InvalidPositionAccrualTimestampError,
9
9
  *
10
10
  * const accrued = PositionUtils.accrueInterest({
11
11
  * position: {
12
+ * user: "0x0000000000000000000000000000000000009000",
13
+ * marketId: "0x0000000000000000000000000000000000000000000000000000000000000001",
12
14
  * credit: 1_000n,
13
15
  * pendingFee: 100n,
14
16
  * lastLossFactor: 0n,
@@ -50,6 +52,27 @@ import { InvalidPositionAccrualStateError, InvalidPositionAccrualTimestampError,
50
52
  */
51
53
  export var PositionUtils;
52
54
  (function (PositionUtils) {
55
+ /**
56
+ * Returns the maximum credit units currently withdrawable by a position.
57
+ *
58
+ * @param params.position - Position whose credit limits the withdrawal.
59
+ * @param params.market - Market whose available liquidity limits the withdrawal.
60
+ * @returns The lower of the position credit and market withdrawable liquidity.
61
+ * @example
62
+ * ```ts
63
+ * import { PositionUtils } from "@morpho-org/midnight-sdk";
64
+ *
65
+ * const withdrawable = PositionUtils.getWithdrawable({
66
+ * position: { credit: 1_000n },
67
+ * market: { withdrawable: 500n },
68
+ * });
69
+ * console.log(withdrawable); // 500n
70
+ * ```
71
+ */
72
+ function getWithdrawable(params) {
73
+ return MathLib.min(BigInt(params.position.credit), BigInt(params.market.withdrawable));
74
+ }
75
+ PositionUtils.getWithdrawable = getWithdrawable;
53
76
  /**
54
77
  * Returns plain Midnight position and market objects accrued like Midnight `updatePositionView`.
55
78
  *
@@ -66,6 +89,8 @@ export var PositionUtils;
66
89
  *
67
90
  * const { position, market } = PositionUtils.accrueInterest({
68
91
  * position: {
92
+ * user: "0x0000000000000000000000000000000000009000",
93
+ * marketId: "0x0000000000000000000000000000000000000000000000000000000000000001",
69
94
  * credit: 1_000n,
70
95
  * pendingFee: 100n,
71
96
  * lastLossFactor: 0n,
@@ -169,6 +194,8 @@ export var PositionUtils;
169
194
  ];
170
195
  return {
171
196
  position: {
197
+ user: params.position.user,
198
+ marketId: params.position.marketId,
172
199
  credit: postSlashCredit - accruedFee,
173
200
  pendingFee: postSlashPendingFee - accruedFee,
174
201
  lastLossFactor: marketLossFactor,
@@ -0,0 +1,2 @@
1
+ export * from "./Position.js";
2
+ export * from "./PositionUtils.js";
@@ -0,0 +1,2 @@
1
+ export * from "./Position.js";
2
+ export * from "./PositionUtils.js";
@@ -15,7 +15,8 @@ function normalizeTree(tree) {
15
15
  if (isTreeLike(tree)) {
16
16
  return tree;
17
17
  }
18
- const offers = tree.flatMap((entry) => GroupUtils.isGroupInput(entry)
18
+ const entries = Array.isArray(tree) ? tree : [tree];
19
+ const offers = entries.flatMap((entry) => GroupUtils.isGroupInput(entry)
19
20
  ? Group.from(entry).offers
20
21
  : [
21
22
  new Offer({
@@ -115,7 +115,9 @@ export class Tree {
115
115
  * ```
116
116
  */
117
117
  static from(tree) {
118
- return tree instanceof Tree ? tree : Tree.create(tree);
118
+ if (tree instanceof Tree)
119
+ return tree;
120
+ return Tree.create(Array.isArray(tree) ? tree : [tree]);
119
121
  }
120
122
  /**
121
123
  * Creates a tree from groups or standalone offers.
@@ -188,7 +188,7 @@ export type TreeCreateParams = readonly GroupInput[];
188
188
  * console.log(tree);
189
189
  * ```
190
190
  */
191
- export type TreeInput = Tree | TreeCreateParams;
191
+ export type TreeInput = Tree | TreeCreateParams | GroupInput;
192
192
  /**
193
193
  * Tree-shaped data required by ratifier helpers.
194
194
  *
@@ -284,7 +284,7 @@ export interface TreeLike {
284
284
  * console.log(tree);
285
285
  * ```
286
286
  */
287
- export type RatifierTreeInput = TreeLike | TreeCreateParams;
287
+ export type RatifierTreeInput = TreeLike | TreeInput;
288
288
  /**
289
289
  * Optional ratification inputs for {@link Tree.mempoolValidate}.
290
290
  *
@@ -35,6 +35,7 @@ function assertLeafOffers(offers, leafHashes) {
35
35
  }
36
36
  seen.add(leafHash);
37
37
  }
38
+ /* v8 ignore next: buildDescriptor rejects empty struct lists before padding; this protects direct internal misuse. */
38
39
  if (seen.size === 0) {
39
40
  throw new InvalidTreeError("Tree must not be empty.");
40
41
  }
@@ -118,9 +119,17 @@ export var TreeUtils;
118
119
  async function mempoolValidate(params) {
119
120
  let items;
120
121
  if (params.ratification == null) {
121
- const offers = "paddedOffers" in params.tree
122
- ? params.tree.offers
123
- : params.tree.flatMap((entry) => GroupUtils.isGroupInput(entry)
122
+ if ("paddedOffers" in params.tree) {
123
+ items = params.tree.offers.map((offer) => ({
124
+ offer,
125
+ ratifierData: "0x",
126
+ }));
127
+ }
128
+ else {
129
+ const entries = Array.isArray(params.tree)
130
+ ? params.tree
131
+ : [params.tree];
132
+ const offers = entries.flatMap((entry) => GroupUtils.isGroupInput(entry)
124
133
  ? Group.from(entry).offers
125
134
  : [
126
135
  new Offer({
@@ -128,13 +137,12 @@ export var TreeUtils;
128
137
  group: OfferUtils.groupHash(entry),
129
138
  }),
130
139
  ]);
131
- if (!("paddedOffers" in params.tree)) {
132
140
  buildDescriptor(offers, { preserveStandaloneGroups: true });
141
+ items = offers.map((offer) => ({
142
+ offer,
143
+ ratifierData: "0x",
144
+ }));
133
145
  }
134
- items = offers.map((offer) => ({
135
- offer,
136
- ratifierData: "0x",
137
- }));
138
146
  }
139
147
  else if (params.ratification.type === "ecrecover") {
140
148
  if (params.ratification.signature != null) {
@@ -259,6 +267,7 @@ export var TreeUtils;
259
267
  const leaves = offerStructs.map(OfferUtils.hashStruct);
260
268
  assertLeafOffers(offerStructs, leaves);
261
269
  const height = Math.log2(offerStructs.length);
270
+ /* v8 ignore next: exercising this branch would require allocating more than 2^20 offer structs. */
262
271
  if (height > 20)
263
272
  throw new InvalidTreeHeightError(height);
264
273
  let level = [...leaves];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@morpho-org/midnight-sdk",
3
3
  "description": "Viem-based package for Morpho Midnight that exports protocol utilities, fetch helpers, and Midnight API utilities under a dedicated API subpath.",
4
- "version": "1.1.1",
4
+ "version": "1.2.0",
5
5
  "author": "Morpho Association <contact@morpho.org>",
6
6
  "contributors": [
7
7
  "Rubilmax <rmilon@gmail.com>"
@@ -52,7 +52,7 @@
52
52
  "viem": "^2.54.2",
53
53
  "vitest": "^4.1.9",
54
54
  "@morpho-org/morpho-ts": "^2.8.0",
55
- "@morpho-org/test": "^2.8.2"
55
+ "@morpho-org/test": "^2.8.3"
56
56
  },
57
57
  "scripts": {
58
58
  "prepublish": "$npm_execpath build",