@morpho-org/midnight-sdk 1.7.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.
- package/README.md +61 -61
- package/lib/cjs/abis.d.ts +794 -0
- package/lib/cjs/abis.js +502 -1
- package/lib/cjs/api/version.generated.d.ts +1 -1
- package/lib/cjs/api/version.generated.js +2 -2
- package/lib/cjs/constants.d.ts +34 -0
- package/lib/cjs/constants.js +35 -1
- package/lib/cjs/errors.d.ts +107 -0
- package/lib/cjs/errors.js +139 -1
- package/lib/cjs/fetch/Ratifier.d.ts +4 -3
- package/lib/cjs/fetch/Ratifier.js +5 -4
- package/lib/cjs/index.d.ts +7 -0
- package/lib/cjs/index.js +16 -1
- package/lib/cjs/offers/Offer.d.ts +10 -0
- package/lib/cjs/offers/Offer.js +15 -1
- package/lib/cjs/offers/OfferUtils.d.ts +3 -0
- package/lib/cjs/offers/OfferUtils.js +3 -0
- package/lib/cjs/signatures/{EcrecoverRatifierUtils.d.ts → EcrecoverRatifier.d.ts} +118 -2
- package/lib/cjs/signatures/{EcrecoverRatifierUtils.js → EcrecoverRatifier.js} +146 -9
- package/lib/cjs/signatures/GroupUtils.d.ts +55 -2
- package/lib/cjs/signatures/GroupUtils.js +64 -5
- package/lib/cjs/signatures/Payload.d.ts +3 -3
- package/lib/cjs/signatures/PriceRatifierV1.d.ts +472 -0
- package/lib/cjs/signatures/PriceRatifierV1.js +521 -0
- package/lib/cjs/signatures/RateRatifierV1.d.ts +581 -0
- package/lib/cjs/signatures/RateRatifierV1.js +696 -0
- package/lib/{esm/signatures/RatifierUtils.d.ts → cjs/signatures/Ratifier.d.ts} +14 -12
- package/lib/cjs/signatures/{RatifierUtils.js → Ratifier.js} +29 -29
- package/lib/cjs/signatures/{SetterRatifierUtils.d.ts → SetterRatifier.d.ts} +50 -3
- package/lib/cjs/signatures/{SetterRatifierUtils.js → SetterRatifier.js} +65 -7
- package/lib/cjs/signatures/Tree.d.ts +94 -17
- package/lib/cjs/signatures/Tree.js +146 -111
- package/lib/cjs/signatures/TreeUtils.d.ts +191 -21
- package/lib/cjs/signatures/TreeUtils.js +320 -134
- package/lib/cjs/signatures/index.d.ts +7 -3
- package/lib/cjs/signatures/index.js +5 -3
- package/lib/cjs/signatures/ratifierRequests.d.ts +55 -0
- package/lib/cjs/signatures/ratifierRequests.js +2 -0
- package/lib/cjs/signatures/ratifierV1Internal.d.ts +57 -0
- package/lib/cjs/signatures/ratifierV1Internal.js +183 -0
- package/lib/cjs/signatures/standardRatifierInternal.d.ts +4 -0
- package/lib/cjs/signatures/standardRatifierInternal.js +12 -0
- package/lib/cjs/signatures/treeMathInternal.d.ts +4 -0
- package/lib/cjs/signatures/treeMathInternal.js +12 -0
- package/lib/cjs/signatures/treeTypes.d.ts +109 -0
- package/lib/cjs/signatures/treeTypes.js +2 -0
- package/lib/esm/abis.d.ts +794 -0
- package/lib/esm/abis.js +501 -0
- package/lib/esm/api/version.generated.d.ts +1 -1
- package/lib/esm/api/version.generated.js +2 -2
- package/lib/esm/constants.d.ts +34 -0
- package/lib/esm/constants.js +34 -0
- package/lib/esm/errors.d.ts +107 -0
- package/lib/esm/errors.js +132 -0
- package/lib/esm/fetch/Ratifier.d.ts +4 -3
- package/lib/esm/fetch/Ratifier.js +5 -4
- package/lib/esm/index.d.ts +7 -0
- package/lib/esm/index.js +5 -0
- package/lib/esm/offers/Offer.d.ts +10 -0
- package/lib/esm/offers/Offer.js +15 -1
- package/lib/esm/offers/OfferUtils.d.ts +3 -0
- package/lib/esm/offers/OfferUtils.js +3 -0
- package/lib/esm/signatures/{EcrecoverRatifierUtils.d.ts → EcrecoverRatifier.d.ts} +118 -2
- package/lib/esm/signatures/{EcrecoverRatifierUtils.js → EcrecoverRatifier.js} +146 -9
- package/lib/esm/signatures/GroupUtils.d.ts +55 -2
- package/lib/esm/signatures/GroupUtils.js +64 -5
- package/lib/esm/signatures/Payload.d.ts +3 -3
- package/lib/esm/signatures/PriceRatifierV1.d.ts +472 -0
- package/lib/esm/signatures/PriceRatifierV1.js +520 -0
- package/lib/esm/signatures/RateRatifierV1.d.ts +581 -0
- package/lib/esm/signatures/RateRatifierV1.js +695 -0
- package/lib/{cjs/signatures/RatifierUtils.d.ts → esm/signatures/Ratifier.d.ts} +14 -12
- package/lib/esm/signatures/{RatifierUtils.js → Ratifier.js} +31 -29
- package/lib/esm/signatures/{SetterRatifierUtils.d.ts → SetterRatifier.d.ts} +50 -3
- package/lib/esm/signatures/{SetterRatifierUtils.js → SetterRatifier.js} +65 -7
- package/lib/esm/signatures/Tree.d.ts +94 -17
- package/lib/esm/signatures/Tree.js +147 -112
- package/lib/esm/signatures/TreeUtils.d.ts +191 -21
- package/lib/esm/signatures/TreeUtils.js +318 -132
- package/lib/esm/signatures/index.d.ts +7 -3
- package/lib/esm/signatures/index.js +5 -3
- package/lib/esm/signatures/ratifierRequests.d.ts +55 -0
- package/lib/esm/signatures/ratifierRequests.js +1 -0
- package/lib/esm/signatures/ratifierV1Internal.d.ts +57 -0
- package/lib/esm/signatures/ratifierV1Internal.js +176 -0
- package/lib/esm/signatures/standardRatifierInternal.d.ts +4 -0
- package/lib/esm/signatures/standardRatifierInternal.js +9 -0
- package/lib/esm/signatures/treeMathInternal.d.ts +4 -0
- package/lib/esm/signatures/treeMathInternal.js +8 -0
- package/lib/esm/signatures/treeTypes.d.ts +109 -0
- package/lib/esm/signatures/treeTypes.js +1 -0
- package/package.json +4 -4
|
@@ -4,24 +4,24 @@ exports.TreeUtils = void 0;
|
|
|
4
4
|
const morpho_ts_1 = require("@morpho-org/morpho-ts");
|
|
5
5
|
const viem_1 = require("viem");
|
|
6
6
|
const MidnightApi_js_1 = require("../api/MidnightApi.js");
|
|
7
|
+
const constants_js_1 = require("../constants.js");
|
|
7
8
|
const errors_js_1 = require("../errors.js");
|
|
8
9
|
const index_js_1 = require("../offers/index.js");
|
|
9
|
-
const
|
|
10
|
+
const EcrecoverRatifier_js_1 = require("./EcrecoverRatifier.js");
|
|
10
11
|
const Group_js_1 = require("./Group.js");
|
|
11
12
|
const GroupUtils_js_1 = require("./GroupUtils.js");
|
|
12
13
|
const offerStructInternal_js_1 = require("./offerStructInternal.js");
|
|
13
14
|
const Payload_js_1 = require("./Payload.js");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
15
|
+
const PriceRatifierV1_js_1 = require("./PriceRatifierV1.js");
|
|
16
|
+
const RateRatifierV1_js_1 = require("./RateRatifierV1.js");
|
|
17
|
+
const Ratifier_js_1 = require("./Ratifier.js");
|
|
18
|
+
const SetterRatifier_js_1 = require("./SetterRatifier.js");
|
|
19
|
+
const Tree_js_1 = require("./Tree.js");
|
|
20
|
+
const treeMathInternal_js_1 = require("./treeMathInternal.js");
|
|
21
21
|
function padOfferStructs(offers) {
|
|
22
|
-
if (isPowerOfTwo(offers.length))
|
|
22
|
+
if ((0, treeMathInternal_js_1.isPowerOfTwo)(offers.length))
|
|
23
23
|
return [...offers];
|
|
24
|
-
const paddedLength = nextPowerOfTwo(offers.length);
|
|
24
|
+
const paddedLength = (0, treeMathInternal_js_1.nextPowerOfTwo)(offers.length);
|
|
25
25
|
return [
|
|
26
26
|
...offers,
|
|
27
27
|
...Array.from({ length: paddedLength - offers.length }, () => offerStructInternal_js_1.EMPTY_OFFER_STRUCT),
|
|
@@ -43,6 +43,33 @@ function assertLeafOffers(offers, leafHashes) {
|
|
|
43
43
|
throw new errors_js_1.InvalidTreeError("Tree must not be empty.");
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
function buildLayers(leaves) {
|
|
47
|
+
const layers = [[...leaves]];
|
|
48
|
+
let level = layers[0];
|
|
49
|
+
while (level.length > 1) {
|
|
50
|
+
const next = [];
|
|
51
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
52
|
+
next.push(TreeUtils.hashNode(level[i], level[i + 1]));
|
|
53
|
+
}
|
|
54
|
+
layers.push(next);
|
|
55
|
+
level = next;
|
|
56
|
+
}
|
|
57
|
+
return layers;
|
|
58
|
+
}
|
|
59
|
+
function assertProvableLeaves(leaves) {
|
|
60
|
+
if (!(0, treeMathInternal_js_1.isPowerOfTwo)(leaves.length)) {
|
|
61
|
+
throw new errors_js_1.InvalidTreeError(`Tree leaf count "${leaves.length}" is not a power of two.`);
|
|
62
|
+
}
|
|
63
|
+
const height = Math.log2(leaves.length);
|
|
64
|
+
if (height > constants_js_1.MAX_TREE_HEIGHT)
|
|
65
|
+
throw new errors_js_1.InvalidTreeHeightError(height);
|
|
66
|
+
return height;
|
|
67
|
+
}
|
|
68
|
+
function computeRootFromLeaves(leaves) {
|
|
69
|
+
const height = assertProvableLeaves(leaves);
|
|
70
|
+
const layers = buildLayers(leaves);
|
|
71
|
+
return { root: layers[height][0], height };
|
|
72
|
+
}
|
|
46
73
|
/**
|
|
47
74
|
* Object-compatible tree hashing, root, proof, and verification helpers.
|
|
48
75
|
*
|
|
@@ -60,6 +87,37 @@ function assertLeafOffers(offers, leafHashes) {
|
|
|
60
87
|
*/
|
|
61
88
|
var TreeUtils;
|
|
62
89
|
(function (TreeUtils) {
|
|
90
|
+
/**
|
|
91
|
+
* Normalizes legacy standard tree entries into grouped offers.
|
|
92
|
+
*
|
|
93
|
+
* Explicit groups are flattened into their member offers, and every
|
|
94
|
+
* standalone offer is normalized as a singleton group using the
|
|
95
|
+
* router-compatible `GroupUtils.hash` group id. Use this when you need the
|
|
96
|
+
* exact offer list a standard Ecrecover/Setter tree commits to.
|
|
97
|
+
*
|
|
98
|
+
* @param entries - Groups or standalone offers in leaf order.
|
|
99
|
+
* @returns Offers in leaf order with their committed group ids.
|
|
100
|
+
* @throws {InvalidOfferGroupError} when an explicit group entry violates group mechanics.
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* import { TreeUtils, type TreeCreateParams } from "@morpho-org/midnight-sdk";
|
|
104
|
+
*
|
|
105
|
+
* function normalize(entries: TreeCreateParams) {
|
|
106
|
+
* return TreeUtils.normalizeEntries(entries);
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
function normalizeEntries(entries) {
|
|
111
|
+
return entries.flatMap((entry) => GroupUtils_js_1.GroupUtils.isGroupInput(entry)
|
|
112
|
+
? Group_js_1.Group.from(entry).offers
|
|
113
|
+
: [
|
|
114
|
+
new index_js_1.Offer({
|
|
115
|
+
...index_js_1.Offer.from(entry),
|
|
116
|
+
group: GroupUtils_js_1.GroupUtils.hash([entry]),
|
|
117
|
+
}),
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
TreeUtils.normalizeEntries = normalizeEntries;
|
|
63
121
|
/**
|
|
64
122
|
* Validates a tree against Midnight mempool API policy.
|
|
65
123
|
*
|
|
@@ -67,17 +125,20 @@ var TreeUtils;
|
|
|
67
125
|
* with empty `ratifierData`, then sends the temporary payload to the Midnight
|
|
68
126
|
* API `POST /mempool/validate` endpoint. Pass `ratification` after signing or
|
|
69
127
|
* Setter root preparation to validate final payload bytes with real
|
|
70
|
-
* `ratifierData`.
|
|
128
|
+
* `ratifierData`. Price/Rate V1 tree snapshots are supported directly and
|
|
129
|
+
* always post the corresponding V1 `ratify` output — there is no empty
|
|
130
|
+
* ratifier-data shape because the router decodes the leaf fields to identify
|
|
131
|
+
* each offer. A V1 `ratification` only asserts the matching route.
|
|
71
132
|
*
|
|
72
133
|
* @param params.chainId - Chain id whose API policy should validate the tree.
|
|
73
|
-
* @param params.tree - Offer tree to validate.
|
|
134
|
+
* @param params.tree - Offer tree or route-typed tree snapshot to validate. Serialized standard snapshots resume without rewriting committed groups.
|
|
74
135
|
* @param params.apiUrl - Optional Midnight API URL override used for the validation HTTP request.
|
|
75
136
|
* @param params.timestamp - Optional ISO-8601 timestamp or `Date` selecting the API policy snapshot.
|
|
76
137
|
* @param params.fetch - Optional fetch implementation override used for the API call.
|
|
77
138
|
* @param params.request - Optional fetch options forwarded to the API request.
|
|
78
|
-
* @param params.ratification - Optional ratification inputs used to validate final payload bytes with real ratifier data.
|
|
139
|
+
* @param params.ratification - Optional route-matching ratification inputs used to validate final payload bytes with real ratifier data.
|
|
79
140
|
* @returns Successful API validation result with `valid: true`.
|
|
80
|
-
* @throws {InvalidTreeError} when the tree is empty, all padding, or duplicated.
|
|
141
|
+
* @throws {InvalidTreeError} when the tree is empty, all padding, or duplicated, or when `ratification` does not match the tree route.
|
|
81
142
|
* @throws {InvalidTreeHeightError} when the resulting height is unsupported.
|
|
82
143
|
* @throws {PayloadDecodeError} when validation payload encoding fails.
|
|
83
144
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
@@ -121,51 +182,72 @@ var TreeUtils;
|
|
|
121
182
|
*/
|
|
122
183
|
async function mempoolValidate(params) {
|
|
123
184
|
let items;
|
|
124
|
-
if (params.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
else {
|
|
132
|
-
const entries = Array.isArray(params.tree)
|
|
133
|
-
? params.tree
|
|
134
|
-
: [params.tree];
|
|
135
|
-
const offers = entries.flatMap((entry) => GroupUtils_js_1.GroupUtils.isGroupInput(entry)
|
|
136
|
-
? Group_js_1.Group.from(entry).offers
|
|
137
|
-
: [
|
|
138
|
-
new index_js_1.Offer({
|
|
139
|
-
...index_js_1.Offer.from(entry),
|
|
140
|
-
group: GroupUtils_js_1.GroupUtils.hash([entry]),
|
|
141
|
-
}),
|
|
142
|
-
]);
|
|
143
|
-
buildDescriptor(entries);
|
|
144
|
-
items = offers.map((offer) => ({
|
|
145
|
-
offer,
|
|
146
|
-
ratifierData: "0x",
|
|
147
|
-
}));
|
|
185
|
+
if (!Array.isArray(params.tree) &&
|
|
186
|
+
"type" in params.tree &&
|
|
187
|
+
(params.tree.type === "priceV1" || params.tree.type === "rateV1")) {
|
|
188
|
+
const tree = params.tree;
|
|
189
|
+
if (params.ratification != null &&
|
|
190
|
+
params.ratification.type !== tree.type) {
|
|
191
|
+
throw new errors_js_1.InvalidTreeError("Ratification route does not match the tree route.");
|
|
148
192
|
}
|
|
193
|
+
items =
|
|
194
|
+
tree.type === "priceV1"
|
|
195
|
+
? PriceRatifierV1_js_1.PriceRatifierV1.ratify({ tree })
|
|
196
|
+
: RateRatifierV1_js_1.RateRatifierV1.ratify({ tree });
|
|
149
197
|
}
|
|
150
|
-
else
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
198
|
+
else {
|
|
199
|
+
// Only standard Ecrecover/Setter inputs remain beyond this point.
|
|
200
|
+
// Serialized standard snapshots resume through descriptor validation so committed groups are kept.
|
|
201
|
+
const rawTree = params.tree;
|
|
202
|
+
const tree = !(rawTree instanceof Tree_js_1.Tree) &&
|
|
203
|
+
!Array.isArray(rawTree) &&
|
|
204
|
+
"entries" in rawTree &&
|
|
205
|
+
"offers" in rawTree &&
|
|
206
|
+
"leaves" in rawTree &&
|
|
207
|
+
"root" in rawTree &&
|
|
208
|
+
"height" in rawTree
|
|
209
|
+
? Tree_js_1.Tree.fromDescriptor(rawTree)
|
|
210
|
+
: rawTree;
|
|
211
|
+
if (params.ratification == null) {
|
|
212
|
+
if (Array.isArray(tree) || !("paddedOffers" in tree)) {
|
|
213
|
+
const entries = Array.isArray(tree) ? tree : [tree];
|
|
214
|
+
const offers = normalizeEntries(entries);
|
|
215
|
+
buildDescriptor(entries);
|
|
216
|
+
items = offers.map((offer) => ({
|
|
217
|
+
offer,
|
|
218
|
+
ratifierData: "0x",
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
items = tree.offers.map((offer) => ({
|
|
223
|
+
offer,
|
|
224
|
+
ratifierData: "0x",
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (params.ratification.type === "ecrecover") {
|
|
229
|
+
if (params.ratification.signature != null) {
|
|
230
|
+
items = await EcrecoverRatifier_js_1.EcrecoverRatifierUtils.ratify({
|
|
231
|
+
tree,
|
|
232
|
+
signature: params.ratification.signature,
|
|
233
|
+
account: params.ratification.account,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
items = await EcrecoverRatifier_js_1.EcrecoverRatifierUtils.ratify({
|
|
238
|
+
tree,
|
|
239
|
+
client: params.ratification.client,
|
|
240
|
+
account: params.ratification.account,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else if (params.ratification.type === "setter") {
|
|
245
|
+
items = SetterRatifier_js_1.SetterRatifierUtils.ratify({ tree });
|
|
157
246
|
}
|
|
158
247
|
else {
|
|
159
|
-
|
|
160
|
-
tree: params.tree,
|
|
161
|
-
client: params.ratification.client,
|
|
162
|
-
account: params.ratification.account,
|
|
163
|
-
});
|
|
248
|
+
throw new errors_js_1.InvalidTreeError("Ratification route does not match the tree route.");
|
|
164
249
|
}
|
|
165
250
|
}
|
|
166
|
-
else {
|
|
167
|
-
items = SetterRatifierUtils_js_1.SetterRatifierUtils.ratify({ tree: params.tree });
|
|
168
|
-
}
|
|
169
251
|
const payload = await Payload_js_1.Payload.encode(items);
|
|
170
252
|
const validation = await MidnightApi_js_1.MidnightApi.validateMempoolPayload({
|
|
171
253
|
baseUrl: params.apiUrl,
|
|
@@ -202,56 +284,48 @@ var TreeUtils;
|
|
|
202
284
|
return (0, viem_1.keccak256)((0, viem_1.concat)([left, right]));
|
|
203
285
|
}
|
|
204
286
|
TreeUtils.hashNode = hashNode;
|
|
205
|
-
/**
|
|
206
|
-
* Builds a tree descriptor and Merkle root. Non-power-of-two batches are
|
|
207
|
-
* padded with protocol-zero offers at the highest leaf indices.
|
|
208
|
-
*
|
|
209
|
-
* Use after `Group.create` or standalone `Offer.create` when you need the
|
|
210
|
-
* padded leaves for custom signing, root approval, or proof construction.
|
|
211
|
-
* Groups are encoded with their derived shared group id, and standalone
|
|
212
|
-
* offers are encoded as singleton groups with the same algorithm.
|
|
213
|
-
* `Tree.create` calls this internally and is the simpler API for most
|
|
214
|
-
* make-side code.
|
|
215
|
-
*
|
|
216
|
-
* @param entries - Groups or standalone offers in leaf order.
|
|
217
|
-
* @returns Tree descriptor.
|
|
218
|
-
* @throws {InvalidTreeError} when the offer count is empty, all padding, or duplicated.
|
|
219
|
-
* @throws {InvalidTreeHeightError} when the padded tree exceeds supported ratifier typehashes.
|
|
220
|
-
* @example
|
|
221
|
-
* ```ts
|
|
222
|
-
* import { Offer, TreeUtils } from "@morpho-org/midnight-sdk";
|
|
223
|
-
* import { zeroAddress } from "viem";
|
|
224
|
-
*
|
|
225
|
-
* const offer = Offer.create({
|
|
226
|
-
* market: {
|
|
227
|
-
* chainId: 8453,
|
|
228
|
-
* midnight: "0x0000000000000000000000000000000000001000",
|
|
229
|
-
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
230
|
-
* collateralParams: [
|
|
231
|
-
* {
|
|
232
|
-
* token: "0x0000000000000000000000000000000000007000",
|
|
233
|
-
* lltv: 770000000000000000n,
|
|
234
|
-
* liquidationCursor: 250000000000000000n,
|
|
235
|
-
* oracle: "0x0000000000000000000000000000000000008000",
|
|
236
|
-
* },
|
|
237
|
-
* ],
|
|
238
|
-
* maturity: 54_000n,
|
|
239
|
-
* rcfThreshold: 0n,
|
|
240
|
-
* enterGate: zeroAddress,
|
|
241
|
-
* liquidatorGate: zeroAddress,
|
|
242
|
-
* },
|
|
243
|
-
* buy: true,
|
|
244
|
-
* maker: "0x0000000000000000000000000000000000009000",
|
|
245
|
-
* tick: 5_000n,
|
|
246
|
-
* expiry: 3_600n,
|
|
247
|
-
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
248
|
-
* maxUnits: 100n,
|
|
249
|
-
* });
|
|
250
|
-
* const tree = TreeUtils.buildDescriptor([offer]);
|
|
251
|
-
* console.log(tree.root);
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
287
|
function buildDescriptor(entries) {
|
|
288
|
+
if ("type" in entries) {
|
|
289
|
+
switch (entries.type) {
|
|
290
|
+
case "priceV1": {
|
|
291
|
+
const descriptor = PriceRatifierV1_js_1.PriceRatifierV1.buildDescriptor(entries.entries);
|
|
292
|
+
return (0, morpho_ts_1.deepFreeze)({
|
|
293
|
+
...descriptor,
|
|
294
|
+
type: "priceV1",
|
|
295
|
+
offers: descriptor.entries
|
|
296
|
+
.slice(0, descriptor.offers.length)
|
|
297
|
+
.map((entry) => entry.offer),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
case "rateV1": {
|
|
301
|
+
const descriptor = RateRatifierV1_js_1.RateRatifierV1.buildDescriptor(entries.entries);
|
|
302
|
+
return (0, morpho_ts_1.deepFreeze)({
|
|
303
|
+
...descriptor,
|
|
304
|
+
type: "rateV1",
|
|
305
|
+
offers: descriptor.entries
|
|
306
|
+
.slice(0, descriptor.offers.length)
|
|
307
|
+
.map((entry) => entry.offer),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
case "ecrecover":
|
|
311
|
+
case "setter": {
|
|
312
|
+
const { tree } = Ratifier_js_1.Ratifier.normalizeRatifierTree({
|
|
313
|
+
tree: entries.entries,
|
|
314
|
+
label: entries.type === "ecrecover" ? "Ecrecover" : "Setter",
|
|
315
|
+
});
|
|
316
|
+
return (0, morpho_ts_1.deepFreeze)({
|
|
317
|
+
type: entries.type,
|
|
318
|
+
entries: tree.paddedOffers,
|
|
319
|
+
offers: tree.paddedOffers.slice(0, tree.offers.length),
|
|
320
|
+
leaves: tree.leaves,
|
|
321
|
+
root: tree.root,
|
|
322
|
+
height: tree.height,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
default:
|
|
326
|
+
throw new errors_js_1.InvalidTreeError("Unsupported tree ratifier route.");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
255
329
|
const structs = entries.flatMap((entry) => GroupUtils_js_1.GroupUtils.isGroupInput(entry)
|
|
256
330
|
? GroupUtils_js_1.GroupUtils.toStructs(entry)
|
|
257
331
|
: [
|
|
@@ -266,22 +340,11 @@ var TreeUtils;
|
|
|
266
340
|
const offerStructs = padOfferStructs(structs);
|
|
267
341
|
const leaves = offerStructs.map(index_js_1.OfferUtils.hashStruct);
|
|
268
342
|
assertLeafOffers(offerStructs, leaves);
|
|
269
|
-
const height =
|
|
270
|
-
/* v8 ignore next: exercising this branch would require allocating more than 2^20 offer structs. */
|
|
271
|
-
if (height > 20)
|
|
272
|
-
throw new errors_js_1.InvalidTreeHeightError(height);
|
|
273
|
-
let level = [...leaves];
|
|
274
|
-
while (level.length > 1) {
|
|
275
|
-
const next = [];
|
|
276
|
-
for (let i = 0; i < level.length; i += 2) {
|
|
277
|
-
next.push(hashNode(level[i], level[i + 1]));
|
|
278
|
-
}
|
|
279
|
-
level = next;
|
|
280
|
-
}
|
|
343
|
+
const { root, height } = computeRootFromLeaves(leaves);
|
|
281
344
|
return (0, morpho_ts_1.deepFreeze)({
|
|
282
345
|
offers: offerStructs,
|
|
283
346
|
leaves,
|
|
284
|
-
root
|
|
347
|
+
root,
|
|
285
348
|
height,
|
|
286
349
|
});
|
|
287
350
|
}
|
|
@@ -335,17 +398,42 @@ var TreeUtils;
|
|
|
335
398
|
return buildDescriptor(entries).root;
|
|
336
399
|
}
|
|
337
400
|
TreeUtils.buildRoot = buildRoot;
|
|
401
|
+
/**
|
|
402
|
+
* Builds a Merkle root from already-hashed leaves.
|
|
403
|
+
*
|
|
404
|
+
* Use this leaf-hash-agnostic form when a ratifier's leaves are not plain
|
|
405
|
+
* offer hashes, for example `RateRatifierV1.hashLeaf` outputs. The leaf
|
|
406
|
+
* count must be a power of two; pad non-power-of-two lists beforehand.
|
|
407
|
+
*
|
|
408
|
+
* @param leaves - Leaf hashes in leaf order; length must be a power of two.
|
|
409
|
+
* @returns Merkle root and tree height.
|
|
410
|
+
* @throws {InvalidTreeError} when the leaf count is not a power of two.
|
|
411
|
+
* @throws {InvalidTreeHeightError} when the tree exceeds supported ratifier typehashes.
|
|
412
|
+
* @example
|
|
413
|
+
* ```ts
|
|
414
|
+
* import { TreeUtils } from "@morpho-org/midnight-sdk";
|
|
415
|
+
* import { zeroHash } from "viem";
|
|
416
|
+
*
|
|
417
|
+
* const { root, height } = TreeUtils.buildRootFromLeaves([zeroHash]);
|
|
418
|
+
* console.log(height);
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
421
|
+
function buildRootFromLeaves(leaves) {
|
|
422
|
+
return computeRootFromLeaves(leaves);
|
|
423
|
+
}
|
|
424
|
+
TreeUtils.buildRootFromLeaves = buildRootFromLeaves;
|
|
338
425
|
/**
|
|
339
426
|
* Builds a Merkle proof for one offer.
|
|
340
427
|
*
|
|
341
|
-
* Use after tree construction when a custom ratifier needs one proof
|
|
342
|
-
* built-in
|
|
343
|
-
* `ratifierData
|
|
428
|
+
* Use after tree construction when a custom ratifier needs one proof; the
|
|
429
|
+
* built-in ratify helpers use {@link TreeUtils.buildProofs} to generate
|
|
430
|
+
* `ratifierData` in bulk.
|
|
344
431
|
*
|
|
345
432
|
* @param params.tree - Tree-like data with the root and leaves that contain the leaf.
|
|
346
433
|
* @param params.leafIndex - Leaf index to prove.
|
|
347
434
|
* @returns Proof descriptor.
|
|
348
|
-
* @throws {InvalidTreeError} when leaf index is out of range.
|
|
435
|
+
* @throws {InvalidTreeError} when leaf index is out of range or when the leaf count is not a power of two.
|
|
436
|
+
* @throws {InvalidTreeHeightError} when the tree height (log2 of the leaf count) exceeds 20.
|
|
349
437
|
* @example
|
|
350
438
|
* ```ts
|
|
351
439
|
* import { Offer, Tree, TreeUtils } from "@morpho-org/midnight-sdk";
|
|
@@ -388,21 +476,84 @@ var TreeUtils;
|
|
|
388
476
|
if (leafIndex < 0n || leafIndex >= BigInt(params.tree.leaves.length)) {
|
|
389
477
|
throw new errors_js_1.InvalidTreeError(`Leaf index "${leafIndex}" is outside the tree.`);
|
|
390
478
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
next.push(hashNode(level[i], level[i + 1]));
|
|
399
|
-
}
|
|
400
|
-
index = Math.floor(index / 2);
|
|
401
|
-
level = next;
|
|
402
|
-
}
|
|
479
|
+
// Height is unused; the call only validates the leaf set is provable.
|
|
480
|
+
assertProvableLeaves(params.tree.leaves);
|
|
481
|
+
const index = Number(leafIndex);
|
|
482
|
+
const layers = buildLayers(params.tree.leaves);
|
|
483
|
+
const proof = layers
|
|
484
|
+
.slice(0, -1)
|
|
485
|
+
.map((level, layerIndex) => level[(index >> layerIndex) ^ 1]);
|
|
403
486
|
return (0, morpho_ts_1.deepFreeze)({ root: params.tree.root, leafIndex, proof });
|
|
404
487
|
}
|
|
405
488
|
TreeUtils.buildProof = buildProof;
|
|
489
|
+
/**
|
|
490
|
+
* Builds Merkle proofs for a range of leaves from one tree-layer pass.
|
|
491
|
+
*
|
|
492
|
+
* Use instead of calling {@link TreeUtils.buildProof} per leaf when many
|
|
493
|
+
* proofs are needed at once, for example inside `ratify` helpers: the
|
|
494
|
+
* parent layers are computed once instead of once per leaf. Each returned
|
|
495
|
+
* proof is byte-identical to `buildProof` on the same tree and leaf index.
|
|
496
|
+
*
|
|
497
|
+
* @param params.tree - Tree leaves and root to prove against.
|
|
498
|
+
* @param params.count - Number of leading leaf indices to prove; defaults to `tree.leaves.length`.
|
|
499
|
+
* @returns Frozen proofs for leaf indices `0` through `count - 1`.
|
|
500
|
+
* @throws {InvalidTreeError} when `count` is negative, non-integer, or exceeds the leaf count, or when the leaf count is not a power of two.
|
|
501
|
+
* @throws {InvalidTreeHeightError} when the tree height (log2 of the leaf count) exceeds 20.
|
|
502
|
+
* @example
|
|
503
|
+
* ```ts
|
|
504
|
+
* import { Offer, Tree, TreeUtils } from "@morpho-org/midnight-sdk";
|
|
505
|
+
* import { zeroAddress } from "viem";
|
|
506
|
+
*
|
|
507
|
+
* const offer = Offer.create({
|
|
508
|
+
* market: {
|
|
509
|
+
* chainId: 8453,
|
|
510
|
+
* midnight: "0x0000000000000000000000000000000000001000",
|
|
511
|
+
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
512
|
+
* collateralParams: [
|
|
513
|
+
* {
|
|
514
|
+
* token: "0x0000000000000000000000000000000000007000",
|
|
515
|
+
* lltv: 770000000000000000n,
|
|
516
|
+
* liquidationCursor: 250000000000000000n,
|
|
517
|
+
* oracle: "0x0000000000000000000000000000000000008000",
|
|
518
|
+
* },
|
|
519
|
+
* ],
|
|
520
|
+
* maturity: 54_000n,
|
|
521
|
+
* rcfThreshold: 0n,
|
|
522
|
+
* enterGate: zeroAddress,
|
|
523
|
+
* liquidatorGate: zeroAddress,
|
|
524
|
+
* },
|
|
525
|
+
* buy: true,
|
|
526
|
+
* maker: "0x0000000000000000000000000000000000009000",
|
|
527
|
+
* tick: 5_000n,
|
|
528
|
+
* expiry: 3_600n,
|
|
529
|
+
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
530
|
+
* maxUnits: 100n,
|
|
531
|
+
* });
|
|
532
|
+
* const proofs = TreeUtils.buildProofs({
|
|
533
|
+
* tree: Tree.create([offer]),
|
|
534
|
+
* count: 1,
|
|
535
|
+
* });
|
|
536
|
+
* console.log(proofs[0]!.proof.length);
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
function buildProofs(params) {
|
|
540
|
+
const count = params.count ?? params.tree.leaves.length;
|
|
541
|
+
if (!Number.isInteger(count) ||
|
|
542
|
+
count < 0 ||
|
|
543
|
+
count > params.tree.leaves.length) {
|
|
544
|
+
throw new errors_js_1.InvalidTreeError(`Proof count "${count}" is outside the tree.`);
|
|
545
|
+
}
|
|
546
|
+
// Height is unused; the call only validates the leaf set is provable.
|
|
547
|
+
assertProvableLeaves(params.tree.leaves);
|
|
548
|
+
const layers = buildLayers(params.tree.leaves);
|
|
549
|
+
const siblingLayers = layers.slice(0, -1);
|
|
550
|
+
return (0, morpho_ts_1.deepFreeze)(Array.from({ length: count }, (_, index) => ({
|
|
551
|
+
root: params.tree.root,
|
|
552
|
+
leafIndex: BigInt(index),
|
|
553
|
+
proof: siblingLayers.map((level, layerIndex) => level[(index >> layerIndex) ^ 1]),
|
|
554
|
+
})));
|
|
555
|
+
}
|
|
556
|
+
TreeUtils.buildProofs = buildProofs;
|
|
406
557
|
/**
|
|
407
558
|
* Verifies a local Merkle proof against a root.
|
|
408
559
|
*
|
|
@@ -457,8 +608,43 @@ var TreeUtils;
|
|
|
457
608
|
* ```
|
|
458
609
|
*/
|
|
459
610
|
function verifyProof(params) {
|
|
460
|
-
|
|
461
|
-
|
|
611
|
+
return verifyLeafProof({
|
|
612
|
+
leaf: index_js_1.OfferUtils.hash(index_js_1.Offer.from(params.offer)),
|
|
613
|
+
root: params.root,
|
|
614
|
+
leafIndex: params.leafIndex,
|
|
615
|
+
proof: params.proof,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
TreeUtils.verifyProof = verifyProof;
|
|
619
|
+
/**
|
|
620
|
+
* Verifies a local Merkle proof for an already-hashed leaf against a root.
|
|
621
|
+
*
|
|
622
|
+
* Use this leaf-hash-agnostic form when a ratifier's leaves are not plain
|
|
623
|
+
* offer hashes, for example `RateRatifierV1.hashLeaf` outputs. The leaf
|
|
624
|
+
* index determines each sibling's left/right position, matching
|
|
625
|
+
* `HashLib.isLeaf` onchain.
|
|
626
|
+
*
|
|
627
|
+
* @param params.leaf - Leaf hash that starts proof reconstruction.
|
|
628
|
+
* @param params.root - Expected Merkle root.
|
|
629
|
+
* @param params.leafIndex - Leaf index proven by `params.proof`.
|
|
630
|
+
* @param params.proof - Merkle proof siblings for the leaf.
|
|
631
|
+
* @returns Whether the proof reconstructs the supplied root.
|
|
632
|
+
* @example
|
|
633
|
+
* ```ts
|
|
634
|
+
* import { TreeUtils } from "@morpho-org/midnight-sdk";
|
|
635
|
+
* import { zeroHash } from "viem";
|
|
636
|
+
*
|
|
637
|
+
* const valid = TreeUtils.verifyLeafProof({
|
|
638
|
+
* leaf: zeroHash,
|
|
639
|
+
* root: zeroHash,
|
|
640
|
+
* leafIndex: 0n,
|
|
641
|
+
* proof: [],
|
|
642
|
+
* });
|
|
643
|
+
* console.log(valid);
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
646
|
+
function verifyLeafProof(params) {
|
|
647
|
+
let node = params.leaf;
|
|
462
648
|
const leafIndex = BigInt(params.leafIndex);
|
|
463
649
|
if (leafIndex < 0n || leafIndex >> BigInt(params.proof.length) !== 0n) {
|
|
464
650
|
return false;
|
|
@@ -473,5 +659,5 @@ var TreeUtils;
|
|
|
473
659
|
}
|
|
474
660
|
return node === params.root;
|
|
475
661
|
}
|
|
476
|
-
TreeUtils.
|
|
662
|
+
TreeUtils.verifyLeafProof = verifyLeafProof;
|
|
477
663
|
})(TreeUtils || (exports.TreeUtils = TreeUtils = {}));
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./EcrecoverRatifier.js";
|
|
2
2
|
export * from "./eip712.js";
|
|
3
3
|
export * from "./Group.js";
|
|
4
4
|
export * from "./GroupUtils.js";
|
|
5
5
|
export { Payload } from "./Payload.js";
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./
|
|
6
|
+
export * from "./PriceRatifierV1.js";
|
|
7
|
+
export * from "./RateRatifierV1.js";
|
|
8
|
+
export * from "./Ratifier.js";
|
|
9
|
+
export type { EcrecoverRatifierDataRequest, EcrecoverRatifierRatifyRequest, EcrecoverRatifierSignRequest, EcrecoverRatifierTypedDataRequest, SetterRatifierDataRequest, SetterRatifierRatifyRequest, } from "./ratifierRequests.js";
|
|
10
|
+
export * from "./SetterRatifier.js";
|
|
8
11
|
export * from "./Tree.js";
|
|
9
12
|
export * from "./TreeUtils.js";
|
|
13
|
+
export type { AnyTree, AnyTreeSnapshot, EcrecoverTreeCreateRequest, PriceRatifierV1TreeCreateRequest, RateRatifierV1TreeCreateRequest, RatifierKind, RatifierTypes, SetterTreeCreateRequest, TreeCreateRequest, TreeData, TreeEntry, TreeSnapshot, TypedRatifierTreeInput, TypedTreeMempoolValidateParams, } from "./treeTypes.js";
|
|
@@ -15,13 +15,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.Payload = void 0;
|
|
18
|
-
__exportStar(require("./
|
|
18
|
+
__exportStar(require("./EcrecoverRatifier.js"), exports);
|
|
19
19
|
__exportStar(require("./eip712.js"), exports);
|
|
20
20
|
__exportStar(require("./Group.js"), exports);
|
|
21
21
|
__exportStar(require("./GroupUtils.js"), exports);
|
|
22
22
|
var Payload_js_1 = require("./Payload.js");
|
|
23
23
|
Object.defineProperty(exports, "Payload", { enumerable: true, get: function () { return Payload_js_1.Payload; } });
|
|
24
|
-
__exportStar(require("./
|
|
25
|
-
__exportStar(require("./
|
|
24
|
+
__exportStar(require("./PriceRatifierV1.js"), exports);
|
|
25
|
+
__exportStar(require("./RateRatifierV1.js"), exports);
|
|
26
|
+
__exportStar(require("./Ratifier.js"), exports);
|
|
27
|
+
__exportStar(require("./SetterRatifier.js"), exports);
|
|
26
28
|
__exportStar(require("./Tree.js"), exports);
|
|
27
29
|
__exportStar(require("./TreeUtils.js"), exports);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { BigIntish } from "@morpho-org/morpho-ts";
|
|
2
|
+
import type { Account, Address, Chain, Client, Transport } from "viem";
|
|
3
|
+
import type { EcrecoverSignatureInput } from "./EcrecoverRatifier.js";
|
|
4
|
+
import type { TreeSnapshot } from "./treeTypes.js";
|
|
5
|
+
/** Tagged Ecrecover tree and chain used to construct signing data or a digest. */
|
|
6
|
+
export interface EcrecoverRatifierTypedDataRequest {
|
|
7
|
+
/** Complete Ecrecover leaf commitments, from a tree instance or portable snapshot. */
|
|
8
|
+
readonly tree: TreeSnapshot<"ecrecover">;
|
|
9
|
+
/** Chain defining the signing domain. */
|
|
10
|
+
readonly chainId: BigIntish;
|
|
11
|
+
}
|
|
12
|
+
/** Tagged Ecrecover tree and signer used to request a signature. */
|
|
13
|
+
export interface EcrecoverRatifierSignRequest {
|
|
14
|
+
/** Complete Ecrecover leaf commitments. */
|
|
15
|
+
readonly tree: TreeSnapshot<"ecrecover">;
|
|
16
|
+
/** Client used at the signing boundary. */
|
|
17
|
+
readonly client: Client<Transport, Chain, Account | undefined>;
|
|
18
|
+
/** Maker or authorized signer. */
|
|
19
|
+
readonly account: Account | Address;
|
|
20
|
+
}
|
|
21
|
+
/** Tagged Ecrecover tree with either a signing client or a precomputed signature. */
|
|
22
|
+
export type EcrecoverRatifierRatifyRequest = (EcrecoverRatifierSignRequest & {
|
|
23
|
+
/** Omit when requesting a signature through the client. */
|
|
24
|
+
readonly signature?: undefined;
|
|
25
|
+
}) | {
|
|
26
|
+
/** Complete Ecrecover leaf commitments. */
|
|
27
|
+
readonly tree: TreeSnapshot<"ecrecover">;
|
|
28
|
+
/** Maker or authorized signer that produced the signature. */
|
|
29
|
+
readonly account: Account | Address;
|
|
30
|
+
/** Signature over this tree's signing digest. */
|
|
31
|
+
readonly signature: EcrecoverSignatureInput;
|
|
32
|
+
/** Omit when supplying a precomputed signature. */
|
|
33
|
+
readonly client?: undefined;
|
|
34
|
+
/** The chain is derived from the offers when supplying a signature. */
|
|
35
|
+
readonly chainId?: undefined;
|
|
36
|
+
};
|
|
37
|
+
/** Tagged Ecrecover tree, signature and leaf index used to encode one proof. */
|
|
38
|
+
export interface EcrecoverRatifierDataRequest {
|
|
39
|
+
/** Complete Ecrecover leaf commitments. */
|
|
40
|
+
readonly tree: TreeSnapshot<"ecrecover">;
|
|
41
|
+
/** Leaf to prove. */
|
|
42
|
+
readonly leafIndex: BigIntish;
|
|
43
|
+
/** Signature over the tree's signing digest. */
|
|
44
|
+
readonly signature: EcrecoverSignatureInput;
|
|
45
|
+
}
|
|
46
|
+
/** Tagged Setter tree whose root has been approved onchain. */
|
|
47
|
+
export interface SetterRatifierRatifyRequest {
|
|
48
|
+
/** Complete Setter leaf commitments; the tag does not prove onchain approval. */
|
|
49
|
+
readonly tree: TreeSnapshot<"setter">;
|
|
50
|
+
}
|
|
51
|
+
/** Tagged Setter tree and leaf index used to encode one proof. */
|
|
52
|
+
export interface SetterRatifierDataRequest extends SetterRatifierRatifyRequest {
|
|
53
|
+
/** Leaf to prove. */
|
|
54
|
+
readonly leafIndex: BigIntish;
|
|
55
|
+
}
|