@morpho-org/midnight-sdk 0.1.0 → 1.0.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 (118) 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 +22 -16
  7. package/lib/cjs/api/helpers.js +6 -3
  8. package/lib/cjs/api/types.d.ts +35 -9
  9. package/lib/cjs/constants.d.ts +12 -11
  10. package/lib/cjs/constants.js +14 -12
  11. package/lib/cjs/errors.d.ts +74 -1
  12. package/lib/cjs/errors.js +94 -3
  13. package/lib/cjs/fetch/Market.js +0 -1
  14. package/lib/cjs/fetch/index.d.ts +0 -1
  15. package/lib/cjs/fetch/index.js +0 -1
  16. package/lib/cjs/index.d.ts +1 -1
  17. package/lib/cjs/index.js +2 -23
  18. package/lib/cjs/market/Market.d.ts +99 -33
  19. package/lib/cjs/market/Market.js +137 -28
  20. package/lib/cjs/market/MarketUtils.d.ts +61 -41
  21. package/lib/cjs/market/MarketUtils.js +108 -48
  22. package/lib/cjs/market/Position.d.ts +16 -11
  23. package/lib/cjs/market/Position.js +18 -12
  24. package/lib/cjs/market/PositionUtils.d.ts +6 -4
  25. package/lib/cjs/market/PositionUtils.js +9 -6
  26. package/lib/cjs/math/TickLib.d.ts +22 -0
  27. package/lib/cjs/math/TickLib.js +30 -0
  28. package/lib/cjs/math/index.d.ts +0 -1
  29. package/lib/cjs/math/index.js +0 -1
  30. package/lib/cjs/offers/Offer.d.ts +217 -11
  31. package/lib/cjs/offers/Offer.js +215 -7
  32. package/lib/cjs/offers/OfferUtils.d.ts +245 -139
  33. package/lib/cjs/offers/OfferUtils.js +304 -36
  34. package/lib/cjs/queries/GetPosition.d.ts +1 -1
  35. package/lib/cjs/queries/GetPosition.js +1 -1
  36. package/lib/cjs/signatures/EcrecoverRatifierUtils.d.ts +66 -38
  37. package/lib/cjs/signatures/EcrecoverRatifierUtils.js +147 -79
  38. package/lib/cjs/signatures/Group.d.ts +12 -4
  39. package/lib/cjs/signatures/Group.js +12 -4
  40. package/lib/cjs/signatures/GroupUtils.d.ts +12 -4
  41. package/lib/cjs/signatures/GroupUtils.js +6 -2
  42. package/lib/cjs/signatures/Payload.d.ts +277 -277
  43. package/lib/cjs/signatures/Payload.js +517 -534
  44. package/lib/cjs/signatures/RatifierUtils.d.ts +57 -0
  45. package/lib/cjs/signatures/RatifierUtils.js +94 -0
  46. package/lib/cjs/signatures/SetterRatifierUtils.d.ts +21 -13
  47. package/lib/cjs/signatures/SetterRatifierUtils.js +25 -20
  48. package/lib/cjs/signatures/Tree.d.ts +28 -16
  49. package/lib/cjs/signatures/Tree.js +25 -13
  50. package/lib/cjs/signatures/TreeUtils.d.ts +181 -62
  51. package/lib/cjs/signatures/TreeUtils.js +73 -76
  52. package/lib/cjs/signatures/index.d.ts +1 -0
  53. package/lib/cjs/signatures/index.js +3 -0
  54. package/lib/cjs/signatures/offerStructInternal.d.ts +14 -0
  55. package/lib/cjs/signatures/offerStructInternal.js +72 -0
  56. package/lib/esm/abis.d.ts +1185 -996
  57. package/lib/esm/abis.js +1445 -1211
  58. package/lib/esm/api/MidnightApi.d.ts +22 -16
  59. package/lib/esm/api/MidnightApi.js +23 -17
  60. package/lib/esm/api/helpers.js +6 -3
  61. package/lib/esm/api/types.d.ts +35 -9
  62. package/lib/esm/constants.d.ts +12 -11
  63. package/lib/esm/constants.js +13 -11
  64. package/lib/esm/errors.d.ts +74 -1
  65. package/lib/esm/errors.js +89 -2
  66. package/lib/esm/fetch/Market.js +0 -1
  67. package/lib/esm/fetch/index.d.ts +0 -1
  68. package/lib/esm/fetch/index.js +0 -1
  69. package/lib/esm/index.d.ts +1 -1
  70. package/lib/esm/index.js +1 -1
  71. package/lib/esm/market/Market.d.ts +99 -33
  72. package/lib/esm/market/Market.js +137 -28
  73. package/lib/esm/market/MarketUtils.d.ts +61 -41
  74. package/lib/esm/market/MarketUtils.js +109 -49
  75. package/lib/esm/market/Position.d.ts +16 -11
  76. package/lib/esm/market/Position.js +19 -13
  77. package/lib/esm/market/PositionUtils.d.ts +6 -4
  78. package/lib/esm/market/PositionUtils.js +9 -6
  79. package/lib/esm/math/TickLib.d.ts +22 -0
  80. package/lib/esm/math/TickLib.js +31 -1
  81. package/lib/esm/math/index.d.ts +0 -1
  82. package/lib/esm/math/index.js +0 -1
  83. package/lib/esm/offers/Offer.d.ts +217 -11
  84. package/lib/esm/offers/Offer.js +216 -8
  85. package/lib/esm/offers/OfferUtils.d.ts +245 -139
  86. package/lib/esm/offers/OfferUtils.js +306 -38
  87. package/lib/esm/queries/GetPosition.d.ts +1 -1
  88. package/lib/esm/queries/GetPosition.js +1 -1
  89. package/lib/esm/signatures/EcrecoverRatifierUtils.d.ts +66 -38
  90. package/lib/esm/signatures/EcrecoverRatifierUtils.js +149 -81
  91. package/lib/esm/signatures/Group.d.ts +12 -4
  92. package/lib/esm/signatures/Group.js +12 -4
  93. package/lib/esm/signatures/GroupUtils.d.ts +12 -4
  94. package/lib/esm/signatures/GroupUtils.js +6 -2
  95. package/lib/esm/signatures/Payload.d.ts +277 -277
  96. package/lib/esm/signatures/Payload.js +517 -531
  97. package/lib/esm/signatures/RatifierUtils.d.ts +57 -0
  98. package/lib/esm/signatures/RatifierUtils.js +94 -0
  99. package/lib/esm/signatures/SetterRatifierUtils.d.ts +21 -13
  100. package/lib/esm/signatures/SetterRatifierUtils.js +25 -20
  101. package/lib/esm/signatures/Tree.d.ts +28 -16
  102. package/lib/esm/signatures/Tree.js +25 -13
  103. package/lib/esm/signatures/TreeUtils.d.ts +181 -62
  104. package/lib/esm/signatures/TreeUtils.js +75 -78
  105. package/lib/esm/signatures/index.d.ts +1 -0
  106. package/lib/esm/signatures/index.js +1 -0
  107. package/lib/esm/signatures/offerStructInternal.d.ts +14 -0
  108. package/lib/esm/signatures/offerStructInternal.js +67 -0
  109. package/package.json +28 -32
  110. package/src/index.ts +1 -1
  111. package/lib/cjs/fetch/Offer.d.ts +0 -74
  112. package/lib/cjs/fetch/Offer.js +0 -119
  113. package/lib/cjs/math/ConsumableUnitsLib.d.ts +0 -57
  114. package/lib/cjs/math/ConsumableUnitsLib.js +0 -77
  115. package/lib/esm/fetch/Offer.d.ts +0 -74
  116. package/lib/esm/fetch/Offer.js +0 -116
  117. package/lib/esm/math/ConsumableUnitsLib.d.ts +0 -57
  118. package/lib/esm/math/ConsumableUnitsLib.js +0 -74
@@ -1,30 +1,33 @@
1
1
  import { deepFreeze } from "@morpho-org/morpho-ts";
2
- import { concat, decodeAbiParameters, encodeAbiParameters, keccak256, parseSignature, verifyTypedData, } from "viem";
2
+ import { concat, decodeAbiParameters, encodeAbiParameters, keccak256, parseSignature, serializeSignature, verifyHash, } from "viem";
3
3
  import { signTypedData } from "viem/actions";
4
4
  import { EIP712_DOMAIN_TYPEHASH } from "../constants.js";
5
- import { InvalidTreeError, InvalidTreeHeightError, InvalidTypedDataSignatureError, } from "../errors.js";
5
+ import { ChainIdMismatchError, InvalidTreeError, InvalidTreeHeightError, InvalidTypedDataSignatureError, } from "../errors.js";
6
+ import { MarketParams } from "../market/index.js";
7
+ import { RatifierUtils } from "./RatifierUtils.js";
8
+ import { TreeUtils, } from "./TreeUtils.js";
6
9
  const treeTypeHashes = [
7
- "0xc27c38e446b48c820ab9c4373dc63a4a750a08165cb4bb488206ebabe045d650",
8
- "0x4e15d8736f4406e07bf9844b1653474472a827130c61e899bf1f574a88b8d987",
9
- "0x46d107447b480c38ef5b7f54603dba0cb23b887f302b01a998b9d8a80320dd53",
10
- "0xd1f3607a8e81454bb3baf5f898274ab47541fffc690278a74f13e174e116be72",
11
- "0xb2d98adca9d116c9bc02ce59ec599ac3c2d33db1c0d1217c7e411d9198d427be",
12
- "0x5931e0597fcf986027f3118b2495a9ac22139d133f9ad2c2198e6738dc3886c5",
13
- "0x3967d37928614a085b47e8758fbc3869a8aed63bdf60ccee8536ff2b5064da06",
14
- "0xd6b9f5f45915a260f6e521d9b40f86c385730b6bc330590fcde212e2fea64263",
15
- "0x080caa519dbd5328c119d9907e0fa3d9a50dc2ae4bf6dd42c93c100dbc89b51a",
16
- "0x45da471048924165ea2ad1855ba940e454b486e71dcf1666c71a928c8844c419",
17
- "0xa49a9434fc1836bd08097368325b31039b6a0fd44919f53e4d8f4bf814084cb0",
18
- "0xd3e93e4525132f0187a6964dc01fef33fde414538ffd212e9f2f478c3263e0a0",
19
- "0x25990db2d26547f92c711988300df317af57bad5cd5d9d8e787a82f95c929474",
20
- "0x8e0c648afa977572ead40a1d10a6db2c425b8099545006d834a7b849c6166643",
21
- "0x4b635250efa6243e277fdd0cf6df993c2943b64f10f3a0756ceb1f47ef8f9b18",
22
- "0xbde1c927f6222c07c8df264e68b42b8382c7c2b85f4729e0df94297cfeebfa91",
23
- "0x4d58aea1a67f94be21ab1415bf3b602592430eb9112268fd0fc4e141b1a35e76",
24
- "0x14c03281bce13010b158e5a4a3378be394ac9e16118aedb17d82ced51e66836c",
25
- "0x99fd3e76f43b2cc221cb9860bc6c96cda95af3fa07ef5f04e071b54aa9386d06",
26
- "0x1b1c2f1a04968094d8d0453d49838f7a809d1202ae04a1c2e0964e442ff7988b",
27
- "0xc8ccd3cb3267dd76f563584920ac60f2283b719917481b85fe5e10b754932455",
10
+ "0x270da1ebafc0f24637af3612fb8c3a1d828fcb56d3637c24e86dd006b12ca7f9",
11
+ "0x828b9cdf8326a1cf234328e4d5229546a98fb72ef73624f5b6b31538e555b96c",
12
+ "0xfcb7a3ca4094246b8185620c4cf025c93032b6f0384805aa3f22afe04290e982",
13
+ "0xcc97cb1955496a5269b5a7afca62ba694edcab26ba838a1adbd257931249de92",
14
+ "0xda3feb08db360ad9e09540132ff04d2b6a596fdaa4747892217aaa4c7c9bcc31",
15
+ "0x15bd6e2aa1a7a61614187ac16d2cbf8610c8f2f3c3d9eaa380ae7a501ee3cf06",
16
+ "0xb726cb7fab1a24c28213cbd482fa5a301f127fb25feb01da341919983a72711a",
17
+ "0xcea9cd557c6f821868ea287304199d0e0554af630bfa8fe36c64eb3bbacca418",
18
+ "0xf7dbde8234e8e345cec8fc0a8ac5909ee336b214882751ecd51e7b37df4f6cdd",
19
+ "0x5400a5d43d39e6bfe910af8cb84ac77bf501d310413769dffd62ccecda8b00c6",
20
+ "0x0754209b60d99d0822b3ecd5a970f9db09df9c8998a8441e24b81f06d6c76fee",
21
+ "0xf5d561d88647c3b38ed6636709d3166819fc66f8ed52a0daf4ae186387b4646c",
22
+ "0x5801c07a6c7df039ce00a7a2b8bd92aa1cf333c30b0bc3d78768590b6063d09e",
23
+ "0xc9da7190eaf4b14c7cb1c14f9898256c0adb6b1dc303afe79594dea64fe199c0",
24
+ "0xa47534c85ac57c583568465d40fd46683d2d558d8129fe1aca01e93023afca92",
25
+ "0xb1e841691fb54f4ef85e2ed9de45d610e57f49e1e6eb2510ceead16e447dd519",
26
+ "0x4fa4f16f09f0c36c7670449a4032073380d28a60071e12ee8874bb3e5a8318fc",
27
+ "0x817bbaac8bb863670f488b454cdd5d0990d9d81871a68e9df381c3c13d3f2ba2",
28
+ "0xc447f06079bddf4b011523c4bce119e9e90fdf937de4ee88f48010406560e9c1",
29
+ "0x1608d5eb56943c667c34b413f9f8a1c24a84ddfe1301a9c25487e638de1f5822",
30
+ "0x3a677100d2e855c24a62d1e9c365bff90d02287f066a07064843ca1ee70ea113",
28
31
  ];
29
32
  const signatureAbi = [
30
33
  {
@@ -56,10 +59,12 @@ const typedDataTypes = {
56
59
  CollateralParams: [
57
60
  { name: "token", type: "address" },
58
61
  { name: "lltv", type: "uint256" },
59
- { name: "maxLif", type: "uint256" },
62
+ { name: "liquidationCursor", type: "uint256" },
60
63
  { name: "oracle", type: "address" },
61
64
  ],
62
65
  Market: [
66
+ { name: "chainId", type: "uint256" },
67
+ { name: "midnight", type: "address" },
63
68
  { name: "loanToken", type: "address" },
64
69
  { name: "collateralParams", type: "CollateralParams[]" },
65
70
  { name: "maturity", type: "uint256" },
@@ -80,8 +85,8 @@ const typedDataTypes = {
80
85
  { name: "receiverIfMakerIsSeller", type: "address" },
81
86
  { name: "ratifier", type: "address" },
82
87
  { name: "reduceOnly", type: "bool" },
83
- { name: "maxUnits", type: "uint256" },
84
- { name: "maxAssets", type: "uint256" },
88
+ { name: "maxUnits", type: "uint128" },
89
+ { name: "maxAssets", type: "uint128" },
85
90
  { name: "continuousFeeCap", type: "uint256" },
86
91
  ],
87
92
  };
@@ -94,21 +99,6 @@ const buildTreeValue = (offers) => {
94
99
  buildTreeValue(offers.slice(mid)),
95
100
  ];
96
101
  };
97
- const getTreeRatifier = (tree) => {
98
- const offers = tree.offers;
99
- const firstOffer = offers[0];
100
- if (firstOffer == null) {
101
- throw new InvalidTreeError("Tree must contain at least one offer.");
102
- }
103
- const ratifier = firstOffer.ratifier;
104
- const comparableRatifier = ratifier.toLowerCase();
105
- for (const offer of offers.slice(1)) {
106
- if (offer.ratifier.toLowerCase() !== comparableRatifier) {
107
- throw new InvalidTreeError(`All offers in an Ecrecover tree must use one ratifier; expected "${ratifier}", got "${offer.ratifier}". Build separate trees per ratifier.`);
108
- }
109
- }
110
- return ratifier;
111
- };
112
102
  /**
113
103
  * EcrecoverRatifier-specific pure utilities.
114
104
  *
@@ -117,7 +107,10 @@ const getTreeRatifier = (tree) => {
117
107
  * validate the tree, sign the typed data, call `ratify`, then pass the returned
118
108
  * items to `Payload.encode`. Ecrecover trees must contain one ratifier; split
119
109
  * trees by ratifier before signing. The signer may be the maker or an address
120
- * authorized by every maker in the tree.
110
+ * authorized by every maker in the tree. Ratifier helpers accept tree-like
111
+ * inputs rather than requiring the `Tree` class. Passing an existing `Tree`
112
+ * remains the optimal path because its cached offers, leaves, root, and height
113
+ * are reused for typed data and per-leaf proofs.
121
114
  *
122
115
  * @example
123
116
  * ```ts
@@ -149,14 +142,14 @@ export var EcrecoverRatifierUtils;
149
142
  }
150
143
  EcrecoverRatifierUtils.treeTypeHash = treeTypeHash;
151
144
  /**
152
- * Builds EcrecoverRatifier typed data for a tree.
145
+ * Builds EcrecoverRatifier typed data for a tree-like input.
153
146
  *
154
147
  * Use after the tree is built and validated, before requesting the signer
155
148
  * signature. The EIP-712 verifier is derived from the shared ratifier address
156
149
  * on the tree offers. `ratify` calls this for you when given a client and
157
150
  * account.
158
151
  *
159
- * @param params.tree - Ecrecover-ratified offer tree to sign.
152
+ * @param params.tree - Ecrecover-ratified offer tree-like input to sign.
160
153
  * @param params.chainId - EIP-155 chain id included in the EIP-712 domain.
161
154
  * @returns EIP-712 typed-data descriptor.
162
155
  * @throws {InvalidTreeError} when the tree is invalid or contains multiple ratifiers.
@@ -169,12 +162,14 @@ export var EcrecoverRatifierUtils;
169
162
  *
170
163
  * const offer = Offer.create({
171
164
  * market: {
165
+ * chainId: 8453,
166
+ * midnight: "0x0000000000000000000000000000000000001000",
172
167
  * loanToken: "0x0000000000000000000000000000000000006000",
173
168
  * collateralParams: [
174
169
  * {
175
170
  * token: "0x0000000000000000000000000000000000007000",
176
171
  * lltv: 770000000000000000n,
177
- * maxLif: 1061007957559681697n,
172
+ * liquidationCursor: 250000000000000000n,
178
173
  * oracle: "0x0000000000000000000000000000000000008000",
179
174
  * },
180
175
  * ],
@@ -199,10 +194,11 @@ export var EcrecoverRatifierUtils;
199
194
  * ```
200
195
  */
201
196
  function typedData(params) {
202
- const verifyingContract = getTreeRatifier(params.tree);
203
- const treeType = params.tree.height === 0
204
- ? "Offer"
205
- : `Offer${"[2]".repeat(params.tree.height)}`;
197
+ const { tree, ratifier: verifyingContract } = RatifierUtils.normalizeRatifierTree({
198
+ tree: params.tree,
199
+ label: "Ecrecover",
200
+ });
201
+ const treeType = tree.height === 0 ? "Offer" : `Offer${"[2]".repeat(tree.height)}`;
206
202
  return deepFreeze({
207
203
  domain: {
208
204
  chainId: BigInt(params.chainId),
@@ -214,7 +210,7 @@ export var EcrecoverRatifierUtils;
214
210
  },
215
211
  primaryType: "OfferTree",
216
212
  message: {
217
- offerTree: buildTreeValue(params.tree.paddedOffers),
213
+ offerTree: buildTreeValue(tree.paddedOffers),
218
214
  },
219
215
  });
220
216
  }
@@ -222,7 +218,7 @@ export var EcrecoverRatifierUtils;
222
218
  /**
223
219
  * Builds the EcrecoverRatifier digest used by the Solidity ratifier.
224
220
  *
225
- * @param params.tree - Ecrecover-ratified offer tree to hash.
221
+ * @param params.tree - Ecrecover-ratified offer tree-like input to hash.
226
222
  * @param params.chainId - EIP-155 chain id included in the EIP-712 domain.
227
223
  * @returns EIP-712 digest.
228
224
  * @throws {InvalidTreeError} when the tree contains multiple ratifiers.
@@ -235,12 +231,14 @@ export var EcrecoverRatifierUtils;
235
231
  *
236
232
  * const offer = Offer.create({
237
233
  * market: {
234
+ * chainId: 8453,
235
+ * midnight: "0x0000000000000000000000000000000000001000",
238
236
  * loanToken: "0x0000000000000000000000000000000000006000",
239
237
  * collateralParams: [
240
238
  * {
241
239
  * token: "0x0000000000000000000000000000000000007000",
242
240
  * lltv: 770000000000000000n,
243
- * maxLif: 1061007957559681697n,
241
+ * liquidationCursor: 250000000000000000n,
244
242
  * oracle: "0x0000000000000000000000000000000000008000",
245
243
  * },
246
244
  * ],
@@ -265,15 +263,18 @@ export var EcrecoverRatifierUtils;
265
263
  * ```
266
264
  */
267
265
  function digest(params) {
268
- const verifyingContract = getTreeRatifier(params.tree);
266
+ const { tree, ratifier: verifyingContract } = RatifierUtils.normalizeRatifierTree({
267
+ tree: params.tree,
268
+ label: "Ecrecover",
269
+ });
269
270
  const domainSeparator = keccak256(encodeAbiParameters(domainSeparatorAbi, [
270
271
  EIP712_DOMAIN_TYPEHASH,
271
272
  BigInt(params.chainId),
272
273
  verifyingContract,
273
274
  ]));
274
275
  const structHash = keccak256(encodeAbiParameters(treeStructHashAbi, [
275
- treeTypeHash(params.tree.height),
276
- params.tree.root,
276
+ treeTypeHash(tree.height),
277
+ tree.root,
277
278
  ]));
278
279
  return keccak256(concat(["0x1901", domainSeparator, structHash]));
279
280
  }
@@ -286,12 +287,13 @@ export var EcrecoverRatifierUtils;
286
287
  * client and account. The account may be the maker or an address authorized
287
288
  * by every maker in the tree; the protocol checks that authorization onchain.
288
289
  *
289
- * @param params.tree - Ecrecover-ratified offer tree to sign.
290
+ * @param params.tree - Ecrecover-ratified offer tree-like input to sign.
290
291
  * @param params.client - Viem client whose transport signs the tree typed data.
291
292
  * @param params.account - Account used to sign the tree typed data.
292
293
  * @returns Signature returned by the client.
293
- * @throws {InvalidTreeError} when the tree is invalid or contains multiple ratifiers.
294
+ * @throws {InvalidTreeError} when the tree is invalid, contains multiple ratifiers, or spans multiple chain ids.
294
295
  * @throws {InvalidTreeHeightError} when the tree height is unsupported.
296
+ * @throws {ChainIdMismatchError} when `params.client.chain?.id` does not match the tree offer chain id.
295
297
  * @throws {InvalidTypedDataSignatureError} when the returned signature does not recover to `params.account`.
296
298
  * @example
297
299
  * ```ts
@@ -302,12 +304,14 @@ export var EcrecoverRatifierUtils;
302
304
  *
303
305
  * const offer = Offer.create({
304
306
  * market: {
307
+ * chainId: 8453,
308
+ * midnight: "0x0000000000000000000000000000000000001000",
305
309
  * loanToken: "0x0000000000000000000000000000000000006000",
306
310
  * collateralParams: [
307
311
  * {
308
312
  * token: "0x0000000000000000000000000000000000007000",
309
313
  * lltv: 770000000000000000n,
310
- * maxLif: 1061007957559681697n,
314
+ * liquidationCursor: 250000000000000000n,
311
315
  * oracle: "0x0000000000000000000000000000000000008000",
312
316
  * },
313
317
  * ],
@@ -337,24 +341,46 @@ export var EcrecoverRatifierUtils;
337
341
  * ```
338
342
  */
339
343
  async function sign(params) {
344
+ const { tree } = RatifierUtils.normalizeRatifierTree({
345
+ tree: params.tree,
346
+ label: "Ecrecover",
347
+ });
348
+ const expectedChainId = MarketParams.from(tree.offers[0].market).chainId;
349
+ for (const offer of tree.offers.slice(1)) {
350
+ const offerChainId = MarketParams.from(offer.market).chainId;
351
+ if (offerChainId !== expectedChainId) {
352
+ throw new InvalidTreeError(`All offers in an Ecrecover tree must use one chain id; expected "${expectedChainId}", got "${offerChainId}". Build separate trees per chain.`);
353
+ }
354
+ }
355
+ const clientChainId = params.client.chain?.id;
356
+ if (clientChainId == null || BigInt(clientChainId) !== expectedChainId) {
357
+ throw new ChainIdMismatchError(clientChainId, expectedChainId);
358
+ }
340
359
  const signer = typeof params.account === "string"
341
360
  ? params.account
342
361
  : params.account.address;
343
362
  const data = typedData({
344
- tree: params.tree,
345
- chainId: params.client.chain.id,
363
+ tree,
364
+ chainId: expectedChainId,
346
365
  });
347
366
  const signature = await signTypedData(params.client, {
348
367
  account: params.account,
349
368
  ...data,
350
369
  });
351
- const isValidSignature = await verifyTypedData({
352
- ...data,
353
- address: signer,
354
- signature,
355
- });
356
- if (!isValidSignature)
370
+ let isValidSignature = false;
371
+ try {
372
+ isValidSignature = await verifyHash({
373
+ address: signer,
374
+ hash: digest({ tree, chainId: expectedChainId }),
375
+ signature,
376
+ });
377
+ }
378
+ catch (cause) {
379
+ throw new InvalidTypedDataSignatureError(signer, cause);
380
+ }
381
+ if (!isValidSignature) {
357
382
  throw new InvalidTypedDataSignatureError(signer);
383
+ }
358
384
  return signature;
359
385
  }
360
386
  EcrecoverRatifierUtils.sign = sign;
@@ -378,17 +404,17 @@ export var EcrecoverRatifierUtils;
378
404
  */
379
405
  function toSignature(signature) {
380
406
  if (typeof signature !== "string") {
407
+ const v = signature.v != null ? Number(signature.v) : Number(signature.yParity);
381
408
  return {
382
- v: signature.v != null
383
- ? Number(signature.v)
384
- : Number(signature.yParity) + 27,
409
+ v: v < 27 ? v + 27 : v,
385
410
  r: signature.r,
386
411
  s: signature.s,
387
412
  };
388
413
  }
389
414
  const parsed = parseSignature(signature);
415
+ const v = Number(parsed.v ?? BigInt(parsed.yParity));
390
416
  return {
391
- v: Number(parsed.v ?? BigInt(parsed.yParity + 27)),
417
+ v: v < 27 ? v + 27 : v,
392
418
  r: parsed.r,
393
419
  s: parsed.s,
394
420
  };
@@ -479,7 +505,7 @@ export var EcrecoverRatifierUtils;
479
505
  * Use after a tree has been signed when a caller needs data for one offer
480
506
  * leaf. Use `ratify` to produce payload-ready items for the whole tree.
481
507
  *
482
- * @param params.tree - Ecrecover-ratified offer tree that produced the proof.
508
+ * @param params.tree - Ecrecover-ratified offer tree-like input that produced the proof.
483
509
  * @param params.leafIndex - Leaf index to prove.
484
510
  * @param params.signature - Ecrecover signature for the tree root.
485
511
  * @returns ABI-encoded EcrecoverRatifier data.
@@ -491,12 +517,14 @@ export var EcrecoverRatifierUtils;
491
517
  *
492
518
  * const offer = Offer.create({
493
519
  * market: {
520
+ * chainId: 8453,
521
+ * midnight: "0x0000000000000000000000000000000000001000",
494
522
  * loanToken: "0x0000000000000000000000000000000000006000",
495
523
  * collateralParams: [
496
524
  * {
497
525
  * token: "0x0000000000000000000000000000000000007000",
498
526
  * lltv: 770000000000000000n,
499
- * maxLif: 1061007957559681697n,
527
+ * liquidationCursor: 250000000000000000n,
500
528
  * oracle: "0x0000000000000000000000000000000000008000",
501
529
  * },
502
530
  * ],
@@ -521,8 +549,11 @@ export var EcrecoverRatifierUtils;
521
549
  * ```
522
550
  */
523
551
  function ratifierData(params) {
524
- getTreeRatifier(params.tree);
525
- const proof = params.tree.proof(params.leafIndex);
552
+ const { tree } = RatifierUtils.normalizeRatifierTree({
553
+ tree: params.tree,
554
+ label: "Ecrecover",
555
+ });
556
+ const proof = TreeUtils.buildProof({ tree, leafIndex: params.leafIndex });
526
557
  return encodeRatifierData({
527
558
  signature: toSignature(params.signature),
528
559
  root: proof.root,
@@ -538,14 +569,14 @@ export var EcrecoverRatifierUtils;
538
569
  * The returned items preserve tree leaf order and include ratifier data
539
570
  * required by takers. The group id is stored on each inline offer.
540
571
  *
541
- * @param params.tree - Ecrecover-ratified offer tree to ratify.
572
+ * @param params.tree - Ecrecover-ratified offer tree-like input to ratify.
542
573
  * @param params.client - Optional viem client whose transport signs typed data built from `params.tree`.
543
574
  * @param params.account - Optional account used to sign typed data built from `params.tree`.
544
575
  * @param params.signature - Optional precomputed signature for `params.tree`.
545
576
  * @returns Items containing each offer and its ratifier data.
546
577
  * @throws {InvalidTreeError} when the tree is invalid or contains multiple ratifiers.
547
578
  * @throws {InvalidTreeHeightError} when the tree height is unsupported.
548
- * @throws {InvalidTypedDataSignatureError} when client signing returns a signature that does not recover to `params.account`.
579
+ * @throws {InvalidTypedDataSignatureError} when client signing or a precomputed signature does not recover to `params.account`.
549
580
  * @example
550
581
  * ```ts
551
582
  * import { EcrecoverRatifierUtils, Tree } from "@morpho-org/midnight-sdk";
@@ -555,12 +586,14 @@ export var EcrecoverRatifierUtils;
555
586
  *
556
587
  * const offer = Offer.create({
557
588
  * market: {
589
+ * chainId: 8453,
590
+ * midnight: "0x0000000000000000000000000000000000001000",
558
591
  * loanToken: "0x0000000000000000000000000000000000006000",
559
592
  * collateralParams: [
560
593
  * {
561
594
  * token: "0x0000000000000000000000000000000000007000",
562
595
  * lltv: 770000000000000000n,
563
- * maxLif: 1061007957559681697n,
596
+ * liquidationCursor: 250000000000000000n,
564
597
  * oracle: "0x0000000000000000000000000000000000008000",
565
598
  * },
566
599
  * ],
@@ -590,20 +623,55 @@ export var EcrecoverRatifierUtils;
590
623
  * ```
591
624
  */
592
625
  async function ratify(params) {
626
+ const { tree } = RatifierUtils.normalizeRatifierTree({
627
+ tree: params.tree,
628
+ label: "Ecrecover",
629
+ });
593
630
  let signature;
594
631
  if (params.signature != null) {
595
- getTreeRatifier(params.tree);
596
632
  signature = toSignature(params.signature);
633
+ const expectedChainId = MarketParams.from(tree.offers[0].market).chainId;
634
+ for (const offer of tree.offers.slice(1)) {
635
+ const offerChainId = MarketParams.from(offer.market).chainId;
636
+ if (offerChainId !== expectedChainId) {
637
+ throw new InvalidTreeError(`All offers in an Ecrecover tree must use one chain id; expected "${expectedChainId}", got "${offerChainId}". Build separate trees per chain.`);
638
+ }
639
+ }
640
+ const signer = typeof params.account === "string"
641
+ ? params.account
642
+ : params.account.address;
643
+ let isValidSignature = false;
644
+ try {
645
+ isValidSignature = await verifyHash({
646
+ address: signer,
647
+ hash: digest({ tree, chainId: expectedChainId }),
648
+ signature: serializeSignature({
649
+ r: signature.r,
650
+ s: signature.s,
651
+ yParity: signature.v - 27,
652
+ }),
653
+ });
654
+ }
655
+ catch (cause) {
656
+ throw new InvalidTypedDataSignatureError(signer, cause);
657
+ }
658
+ if (!isValidSignature) {
659
+ throw new InvalidTypedDataSignatureError(signer);
660
+ }
597
661
  }
598
662
  else {
599
- signature = toSignature(await sign(params));
663
+ signature = toSignature(await sign({
664
+ tree,
665
+ client: params.client,
666
+ account: params.account,
667
+ }));
600
668
  }
601
669
  const items = [];
602
- for (const offer of params.tree.offers) {
670
+ for (const offer of tree.offers) {
603
671
  items.push({
604
672
  offer,
605
673
  ratifierData: ratifierData({
606
- tree: params.tree,
674
+ tree,
607
675
  leafIndex: items.length,
608
676
  signature,
609
677
  }),
@@ -18,12 +18,14 @@ import { type GroupInput, type IGroup } from "./GroupUtils.js";
18
18
  *
19
19
  * const offer = Offer.create({
20
20
  * market: {
21
+ * chainId: 8453,
22
+ * midnight: "0x0000000000000000000000000000000000001000",
21
23
  * loanToken: "0x0000000000000000000000000000000000006000",
22
24
  * collateralParams: [
23
25
  * {
24
26
  * token: "0x0000000000000000000000000000000000007000",
25
27
  * lltv: 770000000000000000n,
26
- * maxLif: 1061007957559681697n,
28
+ * liquidationCursor: 250000000000000000n,
27
29
  * oracle: "0x0000000000000000000000000000000000008000",
28
30
  * },
29
31
  * ],
@@ -61,12 +63,14 @@ export declare class Group implements IGroup {
61
63
  *
62
64
  * const offer = Offer.create({
63
65
  * market: {
66
+ * chainId: 8453,
67
+ * midnight: "0x0000000000000000000000000000000000001000",
64
68
  * loanToken: "0x0000000000000000000000000000000000006000",
65
69
  * collateralParams: [
66
70
  * {
67
71
  * token: "0x0000000000000000000000000000000000007000",
68
72
  * lltv: 770000000000000000n,
69
- * maxLif: 1061007957559681697n,
73
+ * liquidationCursor: 250000000000000000n,
70
74
  * oracle: "0x0000000000000000000000000000000000008000",
71
75
  * },
72
76
  * ],
@@ -104,12 +108,14 @@ export declare class Group implements IGroup {
104
108
  *
105
109
  * const offer = Offer.create({
106
110
  * market: {
111
+ * chainId: 8453,
112
+ * midnight: "0x0000000000000000000000000000000000001000",
107
113
  * loanToken: "0x0000000000000000000000000000000000006000",
108
114
  * collateralParams: [
109
115
  * {
110
116
  * token: "0x0000000000000000000000000000000000007000",
111
117
  * lltv: 770000000000000000n,
112
- * maxLif: 1061007957559681697n,
118
+ * liquidationCursor: 250000000000000000n,
113
119
  * oracle: "0x0000000000000000000000000000000000008000",
114
120
  * },
115
121
  * ],
@@ -149,12 +155,14 @@ export declare class Group implements IGroup {
149
155
  *
150
156
  * const offer = Offer.create({
151
157
  * market: {
158
+ * chainId: 8453,
159
+ * midnight: "0x0000000000000000000000000000000000001000",
152
160
  * loanToken: "0x0000000000000000000000000000000000006000",
153
161
  * collateralParams: [
154
162
  * {
155
163
  * token: "0x0000000000000000000000000000000000007000",
156
164
  * lltv: 770000000000000000n,
157
- * maxLif: 1061007957559681697n,
165
+ * liquidationCursor: 250000000000000000n,
158
166
  * oracle: "0x0000000000000000000000000000000000008000",
159
167
  * },
160
168
  * ],
@@ -17,12 +17,14 @@ import { GroupUtils } from "./GroupUtils.js";
17
17
  *
18
18
  * const offer = Offer.create({
19
19
  * market: {
20
+ * chainId: 8453,
21
+ * midnight: "0x0000000000000000000000000000000000001000",
20
22
  * loanToken: "0x0000000000000000000000000000000000006000",
21
23
  * collateralParams: [
22
24
  * {
23
25
  * token: "0x0000000000000000000000000000000000007000",
24
26
  * lltv: 770000000000000000n,
25
- * maxLif: 1061007957559681697n,
27
+ * liquidationCursor: 250000000000000000n,
26
28
  * oracle: "0x0000000000000000000000000000000000008000",
27
29
  * },
28
30
  * ],
@@ -64,12 +66,14 @@ export class Group {
64
66
  *
65
67
  * const offer = Offer.create({
66
68
  * market: {
69
+ * chainId: 8453,
70
+ * midnight: "0x0000000000000000000000000000000000001000",
67
71
  * loanToken: "0x0000000000000000000000000000000000006000",
68
72
  * collateralParams: [
69
73
  * {
70
74
  * token: "0x0000000000000000000000000000000000007000",
71
75
  * lltv: 770000000000000000n,
72
- * maxLif: 1061007957559681697n,
76
+ * liquidationCursor: 250000000000000000n,
73
77
  * oracle: "0x0000000000000000000000000000000000008000",
74
78
  * },
75
79
  * ],
@@ -109,12 +113,14 @@ export class Group {
109
113
  *
110
114
  * const offer = Offer.create({
111
115
  * market: {
116
+ * chainId: 8453,
117
+ * midnight: "0x0000000000000000000000000000000000001000",
112
118
  * loanToken: "0x0000000000000000000000000000000000006000",
113
119
  * collateralParams: [
114
120
  * {
115
121
  * token: "0x0000000000000000000000000000000000007000",
116
122
  * lltv: 770000000000000000n,
117
- * maxLif: 1061007957559681697n,
123
+ * liquidationCursor: 250000000000000000n,
118
124
  * oracle: "0x0000000000000000000000000000000000008000",
119
125
  * },
120
126
  * ],
@@ -158,12 +164,14 @@ export class Group {
158
164
  *
159
165
  * const offer = Offer.create({
160
166
  * market: {
167
+ * chainId: 8453,
168
+ * midnight: "0x0000000000000000000000000000000000001000",
161
169
  * loanToken: "0x0000000000000000000000000000000000006000",
162
170
  * collateralParams: [
163
171
  * {
164
172
  * token: "0x0000000000000000000000000000000000007000",
165
173
  * lltv: 770000000000000000n,
166
- * maxLif: 1061007957559681697n,
174
+ * liquidationCursor: 250000000000000000n,
167
175
  * oracle: "0x0000000000000000000000000000000000008000",
168
176
  * },
169
177
  * ],
@@ -12,12 +12,14 @@ import { type IOffer, type OfferStruct } from "../offers/index.js";
12
12
  * offers: [
13
13
  * Offer.create({
14
14
  * market: {
15
+ * chainId: 8453,
16
+ * midnight: "0x0000000000000000000000000000000000001000",
15
17
  * loanToken: "0x0000000000000000000000000000000000006000",
16
18
  * collateralParams: [
17
19
  * {
18
20
  * token: "0x0000000000000000000000000000000000007000",
19
21
  * lltv: 770000000000000000n,
20
- * maxLif: 1061007957559681697n,
22
+ * liquidationCursor: 250000000000000000n,
21
23
  * oracle: "0x0000000000000000000000000000000000008000",
22
24
  * },
23
25
  * ],
@@ -57,12 +59,14 @@ export interface IGroup {
57
59
  *
58
60
  * const input: GroupInput = Offer.create({
59
61
  * market: {
62
+ * chainId: 8453,
63
+ * midnight: "0x0000000000000000000000000000000000001000",
60
64
  * loanToken: "0x0000000000000000000000000000000000006000",
61
65
  * collateralParams: [
62
66
  * {
63
67
  * token: "0x0000000000000000000000000000000000007000",
64
68
  * lltv: 770000000000000000n,
65
- * maxLif: 1061007957559681697n,
69
+ * liquidationCursor: 250000000000000000n,
66
70
  * oracle: "0x0000000000000000000000000000000000008000",
67
71
  * },
68
72
  * ],
@@ -115,12 +119,14 @@ export declare namespace GroupUtils {
115
119
  *
116
120
  * const offer = Offer.create({
117
121
  * market: {
122
+ * chainId: 8453,
123
+ * midnight: "0x0000000000000000000000000000000000001000",
118
124
  * loanToken: "0x0000000000000000000000000000000000006000",
119
125
  * collateralParams: [
120
126
  * {
121
127
  * token: "0x0000000000000000000000000000000000007000",
122
128
  * lltv: 770000000000000000n,
123
- * maxLif: 1061007957559681697n,
129
+ * liquidationCursor: 250000000000000000n,
124
130
  * oracle: "0x0000000000000000000000000000000000008000",
125
131
  * },
126
132
  * ],
@@ -162,12 +168,14 @@ export declare namespace GroupUtils {
162
168
  *
163
169
  * const offer = Offer.create({
164
170
  * market: {
171
+ * chainId: 8453,
172
+ * midnight: "0x0000000000000000000000000000000000001000",
165
173
  * loanToken: "0x0000000000000000000000000000000000006000",
166
174
  * collateralParams: [
167
175
  * {
168
176
  * token: "0x0000000000000000000000000000000000007000",
169
177
  * lltv: 770000000000000000n,
170
- * maxLif: 1061007957559681697n,
178
+ * liquidationCursor: 250000000000000000n,
171
179
  * oracle: "0x0000000000000000000000000000000000008000",
172
180
  * },
173
181
  * ],