@morpho-org/midnight-sdk 1.7.0-next.0 → 1.8.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 (92) hide show
  1. package/README.md +61 -61
  2. package/lib/cjs/abis.d.ts +794 -0
  3. package/lib/cjs/abis.js +502 -1
  4. package/lib/cjs/api/version.generated.d.ts +1 -1
  5. package/lib/cjs/api/version.generated.js +2 -2
  6. package/lib/cjs/constants.d.ts +34 -0
  7. package/lib/cjs/constants.js +35 -1
  8. package/lib/cjs/errors.d.ts +107 -0
  9. package/lib/cjs/errors.js +139 -1
  10. package/lib/cjs/fetch/Ratifier.d.ts +4 -3
  11. package/lib/cjs/fetch/Ratifier.js +5 -4
  12. package/lib/cjs/index.d.ts +7 -0
  13. package/lib/cjs/index.js +16 -1
  14. package/lib/cjs/offers/Offer.d.ts +10 -0
  15. package/lib/cjs/offers/Offer.js +15 -1
  16. package/lib/cjs/offers/OfferUtils.d.ts +3 -0
  17. package/lib/cjs/offers/OfferUtils.js +3 -0
  18. package/lib/cjs/signatures/{EcrecoverRatifierUtils.d.ts → EcrecoverRatifier.d.ts} +118 -2
  19. package/lib/cjs/signatures/{EcrecoverRatifierUtils.js → EcrecoverRatifier.js} +146 -9
  20. package/lib/cjs/signatures/GroupUtils.d.ts +55 -2
  21. package/lib/cjs/signatures/GroupUtils.js +64 -5
  22. package/lib/cjs/signatures/Payload.d.ts +3 -3
  23. package/lib/cjs/signatures/PriceRatifierV1.d.ts +472 -0
  24. package/lib/cjs/signatures/PriceRatifierV1.js +521 -0
  25. package/lib/cjs/signatures/RateRatifierV1.d.ts +581 -0
  26. package/lib/cjs/signatures/RateRatifierV1.js +696 -0
  27. package/lib/{esm/signatures/RatifierUtils.d.ts → cjs/signatures/Ratifier.d.ts} +14 -12
  28. package/lib/cjs/signatures/{RatifierUtils.js → Ratifier.js} +29 -29
  29. package/lib/cjs/signatures/{SetterRatifierUtils.d.ts → SetterRatifier.d.ts} +50 -3
  30. package/lib/cjs/signatures/{SetterRatifierUtils.js → SetterRatifier.js} +65 -7
  31. package/lib/cjs/signatures/Tree.d.ts +94 -17
  32. package/lib/cjs/signatures/Tree.js +146 -111
  33. package/lib/cjs/signatures/TreeUtils.d.ts +191 -21
  34. package/lib/cjs/signatures/TreeUtils.js +320 -134
  35. package/lib/cjs/signatures/index.d.ts +7 -3
  36. package/lib/cjs/signatures/index.js +5 -3
  37. package/lib/cjs/signatures/ratifierRequests.d.ts +55 -0
  38. package/lib/cjs/signatures/ratifierRequests.js +2 -0
  39. package/lib/cjs/signatures/ratifierV1Internal.d.ts +57 -0
  40. package/lib/cjs/signatures/ratifierV1Internal.js +183 -0
  41. package/lib/cjs/signatures/standardRatifierInternal.d.ts +4 -0
  42. package/lib/cjs/signatures/standardRatifierInternal.js +12 -0
  43. package/lib/cjs/signatures/treeMathInternal.d.ts +4 -0
  44. package/lib/cjs/signatures/treeMathInternal.js +12 -0
  45. package/lib/cjs/signatures/treeTypes.d.ts +109 -0
  46. package/lib/cjs/signatures/treeTypes.js +2 -0
  47. package/lib/esm/abis.d.ts +794 -0
  48. package/lib/esm/abis.js +501 -0
  49. package/lib/esm/api/version.generated.d.ts +1 -1
  50. package/lib/esm/api/version.generated.js +2 -2
  51. package/lib/esm/constants.d.ts +34 -0
  52. package/lib/esm/constants.js +34 -0
  53. package/lib/esm/errors.d.ts +107 -0
  54. package/lib/esm/errors.js +132 -0
  55. package/lib/esm/fetch/Ratifier.d.ts +4 -3
  56. package/lib/esm/fetch/Ratifier.js +5 -4
  57. package/lib/esm/index.d.ts +7 -0
  58. package/lib/esm/index.js +5 -0
  59. package/lib/esm/offers/Offer.d.ts +10 -0
  60. package/lib/esm/offers/Offer.js +15 -1
  61. package/lib/esm/offers/OfferUtils.d.ts +3 -0
  62. package/lib/esm/offers/OfferUtils.js +3 -0
  63. package/lib/esm/signatures/{EcrecoverRatifierUtils.d.ts → EcrecoverRatifier.d.ts} +118 -2
  64. package/lib/esm/signatures/{EcrecoverRatifierUtils.js → EcrecoverRatifier.js} +146 -9
  65. package/lib/esm/signatures/GroupUtils.d.ts +55 -2
  66. package/lib/esm/signatures/GroupUtils.js +64 -5
  67. package/lib/esm/signatures/Payload.d.ts +3 -3
  68. package/lib/esm/signatures/PriceRatifierV1.d.ts +472 -0
  69. package/lib/esm/signatures/PriceRatifierV1.js +520 -0
  70. package/lib/esm/signatures/RateRatifierV1.d.ts +581 -0
  71. package/lib/esm/signatures/RateRatifierV1.js +695 -0
  72. package/lib/{cjs/signatures/RatifierUtils.d.ts → esm/signatures/Ratifier.d.ts} +14 -12
  73. package/lib/esm/signatures/{RatifierUtils.js → Ratifier.js} +31 -29
  74. package/lib/esm/signatures/{SetterRatifierUtils.d.ts → SetterRatifier.d.ts} +50 -3
  75. package/lib/esm/signatures/{SetterRatifierUtils.js → SetterRatifier.js} +65 -7
  76. package/lib/esm/signatures/Tree.d.ts +94 -17
  77. package/lib/esm/signatures/Tree.js +147 -112
  78. package/lib/esm/signatures/TreeUtils.d.ts +191 -21
  79. package/lib/esm/signatures/TreeUtils.js +318 -132
  80. package/lib/esm/signatures/index.d.ts +7 -3
  81. package/lib/esm/signatures/index.js +5 -3
  82. package/lib/esm/signatures/ratifierRequests.d.ts +55 -0
  83. package/lib/esm/signatures/ratifierRequests.js +1 -0
  84. package/lib/esm/signatures/ratifierV1Internal.d.ts +57 -0
  85. package/lib/esm/signatures/ratifierV1Internal.js +176 -0
  86. package/lib/esm/signatures/standardRatifierInternal.d.ts +4 -0
  87. package/lib/esm/signatures/standardRatifierInternal.js +9 -0
  88. package/lib/esm/signatures/treeMathInternal.d.ts +4 -0
  89. package/lib/esm/signatures/treeMathInternal.js +8 -0
  90. package/lib/esm/signatures/treeTypes.d.ts +109 -0
  91. package/lib/esm/signatures/treeTypes.js +1 -0
  92. package/package.json +4 -4
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EcrecoverRatifierUtils = void 0;
3
+ exports.EcrecoverRatifier = exports.EcrecoverRatifierUtils = void 0;
4
4
  const morpho_ts_1 = require("@morpho-org/morpho-ts");
5
5
  const viem_1 = require("viem");
6
6
  const actions_1 = require("viem/actions");
@@ -9,7 +9,8 @@ const errors_js_1 = require("../errors.js");
9
9
  const index_js_1 = require("../market/index.js");
10
10
  const index_js_2 = require("../offers/index.js");
11
11
  const eip712_js_1 = require("./eip712.js");
12
- const RatifierUtils_js_1 = require("./RatifierUtils.js");
12
+ const Ratifier_js_1 = require("./Ratifier.js");
13
+ const standardRatifierInternal_js_1 = require("./standardRatifierInternal.js");
13
14
  const TreeUtils_js_1 = require("./TreeUtils.js");
14
15
  const treeTypeHashes = [
15
16
  "0x270da1ebafc0f24637af3612fb8c3a1d828fcb56d3637c24e86dd006b12ca7f9",
@@ -130,6 +131,7 @@ function toCanonicalYParity(v) {
130
131
  *
131
132
  * console.log(EcrecoverRatifierUtils.treeTypeHash(0));
132
133
  * ```
134
+ * @deprecated Use {@link EcrecoverRatifier} with a tagged tree.
133
135
  */
134
136
  var EcrecoverRatifierUtils;
135
137
  (function (EcrecoverRatifierUtils) {
@@ -206,7 +208,7 @@ var EcrecoverRatifierUtils;
206
208
  * ```
207
209
  */
208
210
  function typedData(params) {
209
- const { tree, ratifier: verifyingContract } = RatifierUtils_js_1.RatifierUtils.normalizeRatifierTree({
211
+ const { tree, ratifier: verifyingContract } = Ratifier_js_1.Ratifier.normalizeRatifierTree({
210
212
  tree: params.tree,
211
213
  label: "Ecrecover",
212
214
  });
@@ -275,7 +277,7 @@ var EcrecoverRatifierUtils;
275
277
  * ```
276
278
  */
277
279
  function digest(params) {
278
- const { tree, ratifier: verifyingContract } = RatifierUtils_js_1.RatifierUtils.normalizeRatifierTree({
280
+ const { tree, ratifier: verifyingContract } = Ratifier_js_1.Ratifier.normalizeRatifierTree({
279
281
  tree: params.tree,
280
282
  label: "Ecrecover",
281
283
  });
@@ -375,7 +377,7 @@ var EcrecoverRatifierUtils;
375
377
  * Verifies an Ecrecover ratifier-data proof and recovers its signer.
376
378
  *
377
379
  * This helper intentionally does not check `Midnight.isAuthorized` or
378
- * `EcrecoverRatifier.isRootCanceled` state. Consumers must query both at the
380
+ * `EcrecoverRatifierUtils.isRootCanceled` state. Consumers must query both at the
379
381
  * observed block before treating a recovered item as executable: the returned
380
382
  * signer must be the maker or authorized by the maker, the offer ratifier must
381
383
  * be authorized by the maker, and `isRootCanceled(maker, root)` must be false.
@@ -492,7 +494,7 @@ var EcrecoverRatifierUtils;
492
494
  * ```
493
495
  */
494
496
  async function sign(params) {
495
- const { tree } = RatifierUtils_js_1.RatifierUtils.normalizeRatifierTree({
497
+ const { tree } = Ratifier_js_1.Ratifier.normalizeRatifierTree({
496
498
  tree: params.tree,
497
499
  label: "Ecrecover",
498
500
  });
@@ -700,7 +702,7 @@ var EcrecoverRatifierUtils;
700
702
  * ```
701
703
  */
702
704
  function ratifierData(params) {
703
- const { tree } = RatifierUtils_js_1.RatifierUtils.normalizeRatifierTree({
705
+ const { tree } = Ratifier_js_1.Ratifier.normalizeRatifierTree({
704
706
  tree: params.tree,
705
707
  label: "Ecrecover",
706
708
  });
@@ -774,7 +776,7 @@ var EcrecoverRatifierUtils;
774
776
  * ```
775
777
  */
776
778
  async function ratify(params) {
777
- const { tree } = RatifierUtils_js_1.RatifierUtils.normalizeRatifierTree({
779
+ const { tree } = Ratifier_js_1.Ratifier.normalizeRatifierTree({
778
780
  tree: params.tree,
779
781
  label: "Ecrecover",
780
782
  });
@@ -817,13 +819,148 @@ var EcrecoverRatifierUtils;
817
819
  account: params.account,
818
820
  }));
819
821
  }
822
+ const proofs = TreeUtils_js_1.TreeUtils.buildProofs({
823
+ tree,
824
+ count: tree.offers.length,
825
+ });
820
826
  return tree.offers.map((offer, leafIndex) => ({
821
827
  offer,
822
828
  ratifierData: encodeRatifierData({
823
829
  signature,
824
- ...TreeUtils_js_1.TreeUtils.buildProof({ tree, leafIndex }),
830
+ ...proofs[leafIndex],
825
831
  }),
826
832
  }));
827
833
  }
828
834
  EcrecoverRatifierUtils.ratify = ratify;
829
835
  })(EcrecoverRatifierUtils || (exports.EcrecoverRatifierUtils = EcrecoverRatifierUtils = {}));
836
+ /**
837
+ * Tagged-tree API for EcrecoverRatifier with validated portable leaf commitments.
838
+ * Legacy untagged inputs remain available through {@link EcrecoverRatifierUtils}.
839
+ */
840
+ var EcrecoverRatifier;
841
+ (function (EcrecoverRatifier) {
842
+ /**
843
+ * Builds Ecrecover signing data from a tagged tree.
844
+ * @param request.tree - Matching tagged tree or portable snapshot; commitments are validated before use.
845
+ * @param request.chainId - Chain defining the EIP-712 domain.
846
+ * @returns EIP-712 domain, type definitions, and offer-tree message.
847
+ * @throws {InvalidTreeError} When the tag, commitments, or root are invalid.
848
+ * @throws {InvalidTreeHeightError} When the descriptor height is unsupported.
849
+ * @example
850
+ * ```ts
851
+ * import { EcrecoverRatifier, type EcrecoverRatifierTypedDataRequest } from "@morpho-org/midnight-sdk";
852
+ * function execute(request: EcrecoverRatifierTypedDataRequest) {
853
+ * return EcrecoverRatifier.typedData(request);
854
+ * }
855
+ * ```
856
+ */
857
+ function typedData(request) {
858
+ const tree = (0, standardRatifierInternal_js_1.parseStandardRatifierTree)(request.tree, "ecrecover");
859
+ return EcrecoverRatifierUtils.typedData({ ...request, tree });
860
+ }
861
+ EcrecoverRatifier.typedData = typedData;
862
+ /**
863
+ * Computes the signing digest of a tagged Ecrecover tree.
864
+ * @param request.tree - Matching tagged tree or portable snapshot; commitments are validated before use.
865
+ * @param request.chainId - Chain defining the EIP-712 domain.
866
+ * @returns The EIP-712 signing digest.
867
+ * @throws {InvalidTreeError} When the tag, commitments, or root are invalid.
868
+ * @throws {InvalidTreeHeightError} When the descriptor height is unsupported.
869
+ * @example
870
+ * ```ts
871
+ * import { EcrecoverRatifier, type EcrecoverRatifierTypedDataRequest } from "@morpho-org/midnight-sdk";
872
+ * function execute(request: EcrecoverRatifierTypedDataRequest) {
873
+ * return EcrecoverRatifier.digest(request);
874
+ * }
875
+ * ```
876
+ */
877
+ function digest(request) {
878
+ const tree = (0, standardRatifierInternal_js_1.parseStandardRatifierTree)(request.tree, "ecrecover");
879
+ return EcrecoverRatifierUtils.digest({ ...request, tree });
880
+ }
881
+ EcrecoverRatifier.digest = digest;
882
+ /**
883
+ * Signs a tagged Ecrecover tree using the supplied client and account.
884
+ * @param request.tree - Matching tagged tree or portable snapshot; commitments are validated before use.
885
+ * @param request.account - Maker or authorized signer.
886
+ * @param request.client - Signing client; omit when ratify receives a precomputed signature.
887
+ * @returns A verified hex signature over the tree.
888
+ * @throws {InvalidTreeError} When the tag, commitments, or root are invalid.
889
+ * @throws {InvalidTreeHeightError} When the descriptor height is unsupported.
890
+ * @throws {ChainIdMismatchError} When the signing client's chain differs from the offers.
891
+ * @throws {InvalidTypedDataSignatureError} When the signature does not recover to the supplied account.
892
+ * @example
893
+ * ```ts
894
+ * import { EcrecoverRatifier, type EcrecoverRatifierSignRequest } from "@morpho-org/midnight-sdk";
895
+ * function execute(request: EcrecoverRatifierSignRequest) {
896
+ * return EcrecoverRatifier.sign(request);
897
+ * }
898
+ * ```
899
+ */
900
+ async function sign(request) {
901
+ const tree = (0, standardRatifierInternal_js_1.parseStandardRatifierTree)(request.tree, "ecrecover");
902
+ return EcrecoverRatifierUtils.sign({ ...request, tree });
903
+ }
904
+ EcrecoverRatifier.sign = sign;
905
+ /**
906
+ * Encodes the signature and proof for one tagged Ecrecover leaf.
907
+ * @param request.tree - Matching tagged tree or portable snapshot; commitments are validated before use.
908
+ * @param request.signature - Tree signature; ratify may instead request one through the supplied client.
909
+ * @param request.leafIndex - Leaf to prove.
910
+ * @returns ABI-encoded ratifier data for the requested leaf.
911
+ * @throws {InvalidTreeError} When the tag, commitments, or root are invalid.
912
+ * @throws {InvalidTreeHeightError} When the descriptor height is unsupported.
913
+ * @throws {InvalidEcrecoverSignatureVError} When a supplied signature has an unsupported recovery value.
914
+ * @example
915
+ * ```ts
916
+ * import { EcrecoverRatifier, type EcrecoverRatifierDataRequest } from "@morpho-org/midnight-sdk";
917
+ * function execute(request: EcrecoverRatifierDataRequest) {
918
+ * return EcrecoverRatifier.ratifierData(request);
919
+ * }
920
+ * ```
921
+ */
922
+ function ratifierData(request) {
923
+ const tree = (0, standardRatifierInternal_js_1.parseStandardRatifierTree)(request.tree, "ecrecover");
924
+ return EcrecoverRatifierUtils.ratifierData({ ...request, tree });
925
+ }
926
+ EcrecoverRatifier.ratifierData = ratifierData;
927
+ /**
928
+ * Builds payload items from a tagged tree and a verified signature.
929
+ * @param request.tree - Matching tagged tree or portable snapshot; commitments are validated before use.
930
+ * @param request.account - Maker or authorized signer.
931
+ * @param request.client - Signing client; omit when ratify receives a precomputed signature.
932
+ * @param request.signature - Tree signature; ratify may instead request one through the supplied client.
933
+ * @returns One payload item per non-padding offer, including its ratifier data.
934
+ * @throws {InvalidTreeError} When the tag, commitments, or root are invalid.
935
+ * @throws {InvalidTreeHeightError} When the descriptor height is unsupported.
936
+ * @throws {ChainIdMismatchError} When the signing client's chain differs from the offers.
937
+ * @throws {InvalidTypedDataSignatureError} When the signature does not recover to the supplied account.
938
+ * @throws {InvalidEcrecoverSignatureVError} When a supplied signature has an unsupported recovery value.
939
+ * @example
940
+ * ```ts
941
+ * import { EcrecoverRatifier, type EcrecoverRatifierRatifyRequest } from "@morpho-org/midnight-sdk";
942
+ * function execute(request: EcrecoverRatifierRatifyRequest) {
943
+ * return EcrecoverRatifier.ratify(request);
944
+ * }
945
+ * ```
946
+ */
947
+ async function ratify(request) {
948
+ const tree = (0, standardRatifierInternal_js_1.parseStandardRatifierTree)(request.tree, "ecrecover");
949
+ return EcrecoverRatifierUtils.ratify({ ...request, tree });
950
+ }
951
+ EcrecoverRatifier.ratify = ratify;
952
+ /** {@inheritDoc EcrecoverRatifierUtils.treeTypeHash} */
953
+ EcrecoverRatifier.treeTypeHash = EcrecoverRatifierUtils.treeTypeHash;
954
+ /** {@inheritDoc EcrecoverRatifierUtils.digestForRoot} */
955
+ EcrecoverRatifier.digestForRoot = EcrecoverRatifierUtils.digestForRoot;
956
+ /** {@inheritDoc EcrecoverRatifierUtils.digestRatifierData} */
957
+ EcrecoverRatifier.digestRatifierData = EcrecoverRatifierUtils.digestRatifierData;
958
+ /** {@inheritDoc EcrecoverRatifierUtils.verifyRatifierData} */
959
+ EcrecoverRatifier.verifyRatifierData = EcrecoverRatifierUtils.verifyRatifierData;
960
+ /** {@inheritDoc EcrecoverRatifierUtils.toSignature} */
961
+ EcrecoverRatifier.toSignature = EcrecoverRatifierUtils.toSignature;
962
+ /** {@inheritDoc EcrecoverRatifierUtils.encodeRatifierData} */
963
+ EcrecoverRatifier.encodeRatifierData = EcrecoverRatifierUtils.encodeRatifierData;
964
+ /** {@inheritDoc EcrecoverRatifierUtils.decodeRatifierData} */
965
+ EcrecoverRatifier.decodeRatifierData = EcrecoverRatifierUtils.decodeRatifierData;
966
+ })(EcrecoverRatifier || (exports.EcrecoverRatifier = EcrecoverRatifier = {}));
@@ -154,8 +154,11 @@ export declare namespace GroupUtils {
154
154
  /**
155
155
  * Derives the deterministic content-addressed id for a group of offers.
156
156
  *
157
- * This mirrors the router implementation: hash each offer with `group = 0`,
158
- * sort those hashes, concatenate them, then keccak the result.
157
+ * Hashes each offer with `group = 0` using the protocol offer hash, sorts
158
+ * those hashes, concatenates them, then keccaks the result. This matches the
159
+ * router for Ecrecover and Setter offers only; RateRatifierV1 and
160
+ * PriceRatifierV1 commit to scheme leaf hashes, so use
161
+ * `RateRatifierV1.groupId` / `PriceRatifierV1.groupId` for those.
159
162
  *
160
163
  * @param offers - Offers to hash as one group.
161
164
  * @returns Content-addressed group id.
@@ -195,6 +198,56 @@ export declare namespace GroupUtils {
195
198
  * ```
196
199
  */
197
200
  function hash(offers: Iterable<IOffer>): Hash;
201
+ /**
202
+ * Derives a content-addressed group id from ratifier-specific member hashes.
203
+ *
204
+ * This is the ratifier-agnostic core of {@link hash}: each member hash must
205
+ * commit the offer with `group = 0` under the scheme the offer is ratified
206
+ * with (the protocol offer hash for Ecrecover/Setter, the zero-group leaf
207
+ * hash for `RateRatifierV1`/`PriceRatifierV1`). Hashes are lowercased,
208
+ * sorted, concatenated, then keccak'd, so neither member order nor hex
209
+ * casing affects the id.
210
+ *
211
+ * @param memberHashes - Zero-group member hashes of one consumption group.
212
+ * @returns Content-addressed group id.
213
+ * @throws {InvalidOfferGroupError} when `memberHashes` is empty.
214
+ * @example
215
+ * ```ts
216
+ * import { GroupUtils, Offer, RateRatifierV1 } from "@morpho-org/midnight-sdk";
217
+ * import { zeroAddress } from "viem";
218
+ *
219
+ * const offer = Offer.create({
220
+ * market: {
221
+ * chainId: 8453,
222
+ * midnight: "0x0000000000000000000000000000000000001000",
223
+ * loanToken: "0x0000000000000000000000000000000000006000",
224
+ * collateralParams: [
225
+ * {
226
+ * token: "0x0000000000000000000000000000000000007000",
227
+ * lltv: 770000000000000000n,
228
+ * liquidationCursor: 250000000000000000n,
229
+ * oracle: "0x0000000000000000000000000000000000008000",
230
+ * },
231
+ * ],
232
+ * maturity: 54_000n,
233
+ * rcfThreshold: 0n,
234
+ * enterGate: zeroAddress,
235
+ * liquidatorGate: zeroAddress,
236
+ * },
237
+ * buy: true,
238
+ * maker: "0x0000000000000000000000000000000000009000",
239
+ * tick: 5_000n,
240
+ * expiry: 3_600n,
241
+ * ratifier: "0x000000000000000000000000000000000000a111",
242
+ * maxUnits: 100n,
243
+ * });
244
+ * const id = GroupUtils.hashMembers([
245
+ * RateRatifierV1.memberHash({ offer, rate: 50_000_000_000_000_000n }),
246
+ * ]);
247
+ * console.log(id);
248
+ * ```
249
+ */
250
+ function hashMembers(memberHashes: Iterable<Hash>): Hash;
198
251
  /**
199
252
  * Converts a group into ABI-compatible offers carrying the derived group id.
200
253
  *
@@ -76,8 +76,11 @@ var GroupUtils;
76
76
  /**
77
77
  * Derives the deterministic content-addressed id for a group of offers.
78
78
  *
79
- * This mirrors the router implementation: hash each offer with `group = 0`,
80
- * sort those hashes, concatenate them, then keccak the result.
79
+ * Hashes each offer with `group = 0` using the protocol offer hash, sorts
80
+ * those hashes, concatenates them, then keccaks the result. This matches the
81
+ * router for Ecrecover and Setter offers only; RateRatifierV1 and
82
+ * PriceRatifierV1 commit to scheme leaf hashes, so use
83
+ * `RateRatifierV1.groupId` / `PriceRatifierV1.groupId` for those.
81
84
  *
82
85
  * @param offers - Offers to hash as one group.
83
86
  * @returns Content-addressed group id.
@@ -121,11 +124,67 @@ var GroupUtils;
121
124
  if (offerInputs.length === 0) {
122
125
  throw new errors_js_1.InvalidOfferGroupError("Provide at least one offer in the group.");
123
126
  }
124
- const offerHashes = offerInputs.map((offer) => index_js_1.OfferUtils.groupHash(offer));
125
- const sorted = offerHashes.length > 1 ? [...offerHashes].sort() : offerHashes;
126
- return (0, viem_1.keccak256)((0, viem_1.concat)(sorted));
127
+ return hashMembers(offerInputs.map((offer) => index_js_1.OfferUtils.groupHash(offer)));
127
128
  }
128
129
  GroupUtils.hash = hash;
130
+ /**
131
+ * Derives a content-addressed group id from ratifier-specific member hashes.
132
+ *
133
+ * This is the ratifier-agnostic core of {@link hash}: each member hash must
134
+ * commit the offer with `group = 0` under the scheme the offer is ratified
135
+ * with (the protocol offer hash for Ecrecover/Setter, the zero-group leaf
136
+ * hash for `RateRatifierV1`/`PriceRatifierV1`). Hashes are lowercased,
137
+ * sorted, concatenated, then keccak'd, so neither member order nor hex
138
+ * casing affects the id.
139
+ *
140
+ * @param memberHashes - Zero-group member hashes of one consumption group.
141
+ * @returns Content-addressed group id.
142
+ * @throws {InvalidOfferGroupError} when `memberHashes` is empty.
143
+ * @example
144
+ * ```ts
145
+ * import { GroupUtils, Offer, RateRatifierV1 } from "@morpho-org/midnight-sdk";
146
+ * import { zeroAddress } from "viem";
147
+ *
148
+ * const offer = Offer.create({
149
+ * market: {
150
+ * chainId: 8453,
151
+ * midnight: "0x0000000000000000000000000000000000001000",
152
+ * loanToken: "0x0000000000000000000000000000000000006000",
153
+ * collateralParams: [
154
+ * {
155
+ * token: "0x0000000000000000000000000000000000007000",
156
+ * lltv: 770000000000000000n,
157
+ * liquidationCursor: 250000000000000000n,
158
+ * oracle: "0x0000000000000000000000000000000000008000",
159
+ * },
160
+ * ],
161
+ * maturity: 54_000n,
162
+ * rcfThreshold: 0n,
163
+ * enterGate: zeroAddress,
164
+ * liquidatorGate: zeroAddress,
165
+ * },
166
+ * buy: true,
167
+ * maker: "0x0000000000000000000000000000000000009000",
168
+ * tick: 5_000n,
169
+ * expiry: 3_600n,
170
+ * ratifier: "0x000000000000000000000000000000000000a111",
171
+ * maxUnits: 100n,
172
+ * });
173
+ * const id = GroupUtils.hashMembers([
174
+ * RateRatifierV1.memberHash({ offer, rate: 50_000_000_000_000_000n }),
175
+ * ]);
176
+ * console.log(id);
177
+ * ```
178
+ */
179
+ function hashMembers(memberHashes) {
180
+ const hashes = Array.from(memberHashes, (h) => h.toLowerCase());
181
+ if (hashes.length === 0) {
182
+ throw new errors_js_1.InvalidOfferGroupError("Provide at least one member hash in the group.");
183
+ }
184
+ const sorted = hashes.length > 1 ? [...hashes].sort() : hashes;
185
+ return (0, viem_1.keccak256)((0, viem_1.concat)(sorted));
186
+ }
187
+ GroupUtils.hashMembers = hashMembers;
129
188
  /**
130
189
  * Converts a group into ABI-compatible offers carrying the derived group id.
131
190
  *
@@ -13,12 +13,12 @@ export declare namespace Payload {
13
13
  * `ratifierData` blob a taker hands to `Midnight.take(..., ratifierData)`.
14
14
  *
15
15
  * `ratifierData` is owned by the ratifier scheme the maker used (e.g.
16
- * `EcrecoverRatifierUtils.ratifierData`). The payload codec treats it as
16
+ * `EcrecoverRatifier.ratifierData`). The payload codec treats it as
17
17
  * opaque bytes — simulators that only need to forward `ratifierData` can
18
18
  * stay completely ratifier-agnostic.
19
19
  *
20
- * Build items with `EcrecoverRatifierUtils.ratify` or
21
- * `SetterRatifierUtils.ratify` after the tree is signed or approved, then pass
20
+ * Build items with `EcrecoverRatifier.ratify` or
21
+ * `SetterRatifier.ratify` after the tree is signed or approved, then pass
22
22
  * the items to `Payload.encode` for publication.
23
23
  *
24
24
  * @example