@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.
- package/LICENSE +21 -0
- package/README.md +56 -46
- package/lib/cjs/abis.d.ts +1185 -996
- package/lib/cjs/abis.js +1445 -1211
- package/lib/cjs/api/MidnightApi.d.ts +22 -16
- package/lib/cjs/api/MidnightApi.js +22 -16
- package/lib/cjs/api/helpers.js +6 -3
- package/lib/cjs/api/types.d.ts +35 -9
- package/lib/cjs/constants.d.ts +12 -11
- package/lib/cjs/constants.js +14 -12
- package/lib/cjs/errors.d.ts +74 -1
- package/lib/cjs/errors.js +94 -3
- package/lib/cjs/fetch/Market.js +0 -1
- package/lib/cjs/fetch/index.d.ts +0 -1
- package/lib/cjs/fetch/index.js +0 -1
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.js +2 -23
- package/lib/cjs/market/Market.d.ts +99 -33
- package/lib/cjs/market/Market.js +137 -28
- package/lib/cjs/market/MarketUtils.d.ts +61 -41
- package/lib/cjs/market/MarketUtils.js +108 -48
- package/lib/cjs/market/Position.d.ts +16 -11
- package/lib/cjs/market/Position.js +18 -12
- package/lib/cjs/market/PositionUtils.d.ts +6 -4
- package/lib/cjs/market/PositionUtils.js +9 -6
- package/lib/cjs/math/TickLib.d.ts +22 -0
- package/lib/cjs/math/TickLib.js +30 -0
- package/lib/cjs/math/index.d.ts +0 -1
- package/lib/cjs/math/index.js +0 -1
- package/lib/cjs/offers/Offer.d.ts +217 -11
- package/lib/cjs/offers/Offer.js +215 -7
- package/lib/cjs/offers/OfferUtils.d.ts +245 -139
- package/lib/cjs/offers/OfferUtils.js +304 -36
- package/lib/cjs/queries/GetPosition.d.ts +1 -1
- package/lib/cjs/queries/GetPosition.js +1 -1
- package/lib/cjs/signatures/EcrecoverRatifierUtils.d.ts +66 -38
- package/lib/cjs/signatures/EcrecoverRatifierUtils.js +147 -79
- package/lib/cjs/signatures/Group.d.ts +12 -4
- package/lib/cjs/signatures/Group.js +12 -4
- package/lib/cjs/signatures/GroupUtils.d.ts +12 -4
- package/lib/cjs/signatures/GroupUtils.js +6 -2
- package/lib/cjs/signatures/Payload.d.ts +277 -277
- package/lib/cjs/signatures/Payload.js +517 -534
- package/lib/cjs/signatures/RatifierUtils.d.ts +57 -0
- package/lib/cjs/signatures/RatifierUtils.js +94 -0
- package/lib/cjs/signatures/SetterRatifierUtils.d.ts +21 -13
- package/lib/cjs/signatures/SetterRatifierUtils.js +25 -20
- package/lib/cjs/signatures/Tree.d.ts +28 -16
- package/lib/cjs/signatures/Tree.js +25 -13
- package/lib/cjs/signatures/TreeUtils.d.ts +181 -62
- package/lib/cjs/signatures/TreeUtils.js +73 -76
- package/lib/cjs/signatures/index.d.ts +1 -0
- package/lib/cjs/signatures/index.js +3 -0
- package/lib/cjs/signatures/offerStructInternal.d.ts +14 -0
- package/lib/cjs/signatures/offerStructInternal.js +72 -0
- package/lib/esm/abis.d.ts +1185 -996
- package/lib/esm/abis.js +1445 -1211
- package/lib/esm/api/MidnightApi.d.ts +22 -16
- package/lib/esm/api/MidnightApi.js +23 -17
- package/lib/esm/api/helpers.js +6 -3
- package/lib/esm/api/types.d.ts +35 -9
- package/lib/esm/constants.d.ts +12 -11
- package/lib/esm/constants.js +13 -11
- package/lib/esm/errors.d.ts +74 -1
- package/lib/esm/errors.js +89 -2
- package/lib/esm/fetch/Market.js +0 -1
- package/lib/esm/fetch/index.d.ts +0 -1
- package/lib/esm/fetch/index.js +0 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/market/Market.d.ts +99 -33
- package/lib/esm/market/Market.js +137 -28
- package/lib/esm/market/MarketUtils.d.ts +61 -41
- package/lib/esm/market/MarketUtils.js +109 -49
- package/lib/esm/market/Position.d.ts +16 -11
- package/lib/esm/market/Position.js +19 -13
- package/lib/esm/market/PositionUtils.d.ts +6 -4
- package/lib/esm/market/PositionUtils.js +9 -6
- package/lib/esm/math/TickLib.d.ts +22 -0
- package/lib/esm/math/TickLib.js +31 -1
- package/lib/esm/math/index.d.ts +0 -1
- package/lib/esm/math/index.js +0 -1
- package/lib/esm/offers/Offer.d.ts +217 -11
- package/lib/esm/offers/Offer.js +216 -8
- package/lib/esm/offers/OfferUtils.d.ts +245 -139
- package/lib/esm/offers/OfferUtils.js +306 -38
- package/lib/esm/queries/GetPosition.d.ts +1 -1
- package/lib/esm/queries/GetPosition.js +1 -1
- package/lib/esm/signatures/EcrecoverRatifierUtils.d.ts +66 -38
- package/lib/esm/signatures/EcrecoverRatifierUtils.js +149 -81
- package/lib/esm/signatures/Group.d.ts +12 -4
- package/lib/esm/signatures/Group.js +12 -4
- package/lib/esm/signatures/GroupUtils.d.ts +12 -4
- package/lib/esm/signatures/GroupUtils.js +6 -2
- package/lib/esm/signatures/Payload.d.ts +277 -277
- package/lib/esm/signatures/Payload.js +517 -531
- package/lib/esm/signatures/RatifierUtils.d.ts +57 -0
- package/lib/esm/signatures/RatifierUtils.js +94 -0
- package/lib/esm/signatures/SetterRatifierUtils.d.ts +21 -13
- package/lib/esm/signatures/SetterRatifierUtils.js +25 -20
- package/lib/esm/signatures/Tree.d.ts +28 -16
- package/lib/esm/signatures/Tree.js +25 -13
- package/lib/esm/signatures/TreeUtils.d.ts +181 -62
- package/lib/esm/signatures/TreeUtils.js +75 -78
- package/lib/esm/signatures/index.d.ts +1 -0
- package/lib/esm/signatures/index.js +1 -0
- package/lib/esm/signatures/offerStructInternal.d.ts +14 -0
- package/lib/esm/signatures/offerStructInternal.js +67 -0
- package/package.json +28 -32
- package/src/index.ts +1 -1
- package/lib/cjs/fetch/Offer.d.ts +0 -74
- package/lib/cjs/fetch/Offer.js +0 -119
- package/lib/cjs/math/ConsumableUnitsLib.d.ts +0 -57
- package/lib/cjs/math/ConsumableUnitsLib.js +0 -77
- package/lib/esm/fetch/Offer.d.ts +0 -74
- package/lib/esm/fetch/Offer.js +0 -116
- package/lib/esm/math/ConsumableUnitsLib.d.ts +0 -57
- 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,
|
|
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
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
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: "
|
|
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: "
|
|
84
|
-
{ name: "maxAssets", type: "
|
|
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
|
-
*
|
|
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 =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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(
|
|
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
|
-
*
|
|
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 =
|
|
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(
|
|
276
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
|
345
|
-
chainId:
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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:
|
|
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:
|
|
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
|
-
*
|
|
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
|
-
|
|
525
|
-
|
|
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
|
|
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
|
-
*
|
|
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(
|
|
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
|
|
670
|
+
for (const offer of tree.offers) {
|
|
603
671
|
items.push({
|
|
604
672
|
offer,
|
|
605
673
|
ratifierData: ratifierData({
|
|
606
|
-
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
178
|
+
* liquidationCursor: 250000000000000000n,
|
|
171
179
|
* oracle: "0x0000000000000000000000000000000000008000",
|
|
172
180
|
* },
|
|
173
181
|
* ],
|