@morpho-org/midnight-sdk 0.1.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 +252 -0
- package/lib/cjs/abis.d.ts +3170 -0
- package/lib/cjs/abis.js +4073 -0
- package/lib/cjs/api/MidnightApi.d.ts +532 -0
- package/lib/cjs/api/MidnightApi.js +713 -0
- package/lib/cjs/api/helpers.d.ts +20 -0
- package/lib/cjs/api/helpers.js +189 -0
- package/lib/cjs/api/index.d.ts +2 -0
- package/lib/cjs/api/index.js +21 -0
- package/lib/cjs/api/types.d.ts +453 -0
- package/lib/cjs/api/types.js +2 -0
- package/lib/cjs/constants.d.ts +168 -0
- package/lib/cjs/constants.js +187 -0
- package/lib/cjs/errors.d.ts +253 -0
- package/lib/cjs/errors.js +309 -0
- package/lib/cjs/fetch/Market.d.ts +65 -0
- package/lib/cjs/fetch/Market.js +117 -0
- package/lib/cjs/fetch/Offer.d.ts +74 -0
- package/lib/cjs/fetch/Offer.js +119 -0
- package/lib/cjs/fetch/Position.d.ts +85 -0
- package/lib/cjs/fetch/Position.js +148 -0
- package/lib/cjs/fetch/Ratifier.d.ts +38 -0
- package/lib/cjs/fetch/Ratifier.js +57 -0
- package/lib/cjs/fetch/index.d.ts +5 -0
- package/lib/cjs/fetch/index.js +21 -0
- package/lib/cjs/fetch/types.d.ts +57 -0
- package/lib/cjs/fetch/types.js +2 -0
- package/lib/cjs/fetch/utils.d.ts +8 -0
- package/lib/cjs/fetch/utils.js +18 -0
- package/lib/cjs/index.d.ts +18 -0
- package/lib/cjs/index.js +57 -0
- package/lib/cjs/market/Market.d.ts +613 -0
- package/lib/cjs/market/Market.js +503 -0
- package/lib/cjs/market/MarketUtils.d.ts +189 -0
- package/lib/cjs/market/MarketUtils.js +303 -0
- package/lib/cjs/market/Position.d.ts +424 -0
- package/lib/cjs/market/Position.js +425 -0
- package/lib/cjs/market/PositionUtils.d.ts +115 -0
- package/lib/cjs/market/PositionUtils.js +194 -0
- package/lib/cjs/market/index.d.ts +4 -0
- package/lib/cjs/market/index.js +20 -0
- package/lib/cjs/math/ConsumableUnitsLib.d.ts +57 -0
- package/lib/cjs/math/ConsumableUnitsLib.js +77 -0
- package/lib/cjs/math/TakeAmountsLib.d.ts +175 -0
- package/lib/cjs/math/TakeAmountsLib.js +199 -0
- package/lib/cjs/math/TickLib.d.ts +200 -0
- package/lib/cjs/math/TickLib.js +272 -0
- package/lib/cjs/math/index.d.ts +3 -0
- package/lib/cjs/math/index.js +19 -0
- package/lib/cjs/offers/Offer.d.ts +534 -0
- package/lib/cjs/offers/Offer.js +343 -0
- package/lib/cjs/offers/OfferUtils.d.ts +565 -0
- package/lib/cjs/offers/OfferUtils.js +659 -0
- package/lib/cjs/offers/index.d.ts +2 -0
- package/lib/cjs/offers/index.js +18 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/queries/GetPosition.d.ts +55 -0
- package/lib/cjs/queries/GetPosition.js +74 -0
- package/lib/cjs/queries/index.d.ts +1 -0
- package/lib/cjs/queries/index.js +37 -0
- package/lib/cjs/signatures/EcrecoverRatifierUtils.d.ts +733 -0
- package/lib/cjs/signatures/EcrecoverRatifierUtils.js +618 -0
- package/lib/cjs/signatures/Group.d.ts +178 -0
- package/lib/cjs/signatures/Group.js +193 -0
- package/lib/cjs/signatures/GroupUtils.d.ts +191 -0
- package/lib/cjs/signatures/GroupUtils.js +127 -0
- package/lib/cjs/signatures/Payload.d.ts +283 -0
- package/lib/cjs/signatures/Payload.js +570 -0
- package/lib/cjs/signatures/RatifierUtils.d.ts +109 -0
- package/lib/cjs/signatures/RatifierUtils.js +76 -0
- package/lib/cjs/signatures/SetterRatifierUtils.d.ts +243 -0
- package/lib/cjs/signatures/SetterRatifierUtils.js +217 -0
- package/lib/cjs/signatures/Tree.d.ts +241 -0
- package/lib/cjs/signatures/Tree.js +263 -0
- package/lib/cjs/signatures/TreeUtils.d.ts +573 -0
- package/lib/cjs/signatures/TreeUtils.js +461 -0
- package/lib/cjs/signatures/index.d.ts +7 -0
- package/lib/cjs/signatures/index.js +23 -0
- package/lib/esm/abis.d.ts +3170 -0
- package/lib/esm/abis.js +4070 -0
- package/lib/esm/api/MidnightApi.d.ts +532 -0
- package/lib/esm/api/MidnightApi.js +709 -0
- package/lib/esm/api/helpers.d.ts +20 -0
- package/lib/esm/api/helpers.js +180 -0
- package/lib/esm/api/index.d.ts +2 -0
- package/lib/esm/api/index.js +2 -0
- package/lib/esm/api/types.d.ts +453 -0
- package/lib/esm/api/types.js +1 -0
- package/lib/esm/constants.d.ts +168 -0
- package/lib/esm/constants.js +184 -0
- package/lib/esm/errors.d.ts +253 -0
- package/lib/esm/errors.js +291 -0
- package/lib/esm/fetch/Market.d.ts +65 -0
- package/lib/esm/fetch/Market.js +113 -0
- package/lib/esm/fetch/Offer.d.ts +74 -0
- package/lib/esm/fetch/Offer.js +116 -0
- package/lib/esm/fetch/Position.d.ts +85 -0
- package/lib/esm/fetch/Position.js +144 -0
- package/lib/esm/fetch/Ratifier.d.ts +38 -0
- package/lib/esm/fetch/Ratifier.js +54 -0
- package/lib/esm/fetch/index.d.ts +5 -0
- package/lib/esm/fetch/index.js +5 -0
- package/lib/esm/fetch/types.d.ts +57 -0
- package/lib/esm/fetch/types.js +1 -0
- package/lib/esm/fetch/utils.d.ts +8 -0
- package/lib/esm/fetch/utils.js +12 -0
- package/lib/esm/index.d.ts +18 -0
- package/lib/esm/index.js +18 -0
- package/lib/esm/market/Market.d.ts +613 -0
- package/lib/esm/market/Market.js +498 -0
- package/lib/esm/market/MarketUtils.d.ts +189 -0
- package/lib/esm/market/MarketUtils.js +300 -0
- package/lib/esm/market/Position.d.ts +424 -0
- package/lib/esm/market/Position.js +420 -0
- package/lib/esm/market/PositionUtils.d.ts +115 -0
- package/lib/esm/market/PositionUtils.js +191 -0
- package/lib/esm/market/index.d.ts +4 -0
- package/lib/esm/market/index.js +4 -0
- package/lib/esm/math/ConsumableUnitsLib.d.ts +57 -0
- package/lib/esm/math/ConsumableUnitsLib.js +74 -0
- package/lib/esm/math/TakeAmountsLib.d.ts +175 -0
- package/lib/esm/math/TakeAmountsLib.js +196 -0
- package/lib/esm/math/TickLib.d.ts +200 -0
- package/lib/esm/math/TickLib.js +269 -0
- package/lib/esm/math/index.d.ts +3 -0
- package/lib/esm/math/index.js +3 -0
- package/lib/esm/offers/Offer.d.ts +534 -0
- package/lib/esm/offers/Offer.js +339 -0
- package/lib/esm/offers/OfferUtils.d.ts +565 -0
- package/lib/esm/offers/OfferUtils.js +656 -0
- package/lib/esm/offers/index.d.ts +2 -0
- package/lib/esm/offers/index.js +2 -0
- package/lib/esm/package.json +1 -0
- package/lib/esm/queries/GetPosition.d.ts +55 -0
- package/lib/esm/queries/GetPosition.js +71 -0
- package/lib/esm/queries/index.d.ts +1 -0
- package/lib/esm/queries/index.js +1 -0
- package/lib/esm/signatures/EcrecoverRatifierUtils.d.ts +733 -0
- package/lib/esm/signatures/EcrecoverRatifierUtils.js +615 -0
- package/lib/esm/signatures/Group.d.ts +178 -0
- package/lib/esm/signatures/Group.js +189 -0
- package/lib/esm/signatures/GroupUtils.d.ts +191 -0
- package/lib/esm/signatures/GroupUtils.js +124 -0
- package/lib/esm/signatures/Payload.d.ts +283 -0
- package/lib/esm/signatures/Payload.js +564 -0
- package/lib/esm/signatures/RatifierUtils.d.ts +109 -0
- package/lib/esm/signatures/RatifierUtils.js +73 -0
- package/lib/esm/signatures/SetterRatifierUtils.d.ts +243 -0
- package/lib/esm/signatures/SetterRatifierUtils.js +214 -0
- package/lib/esm/signatures/Tree.d.ts +241 -0
- package/lib/esm/signatures/Tree.js +259 -0
- package/lib/esm/signatures/TreeUtils.d.ts +573 -0
- package/lib/esm/signatures/TreeUtils.js +458 -0
- package/lib/esm/signatures/index.d.ts +7 -0
- package/lib/esm/signatures/index.js +7 -0
- package/package.json +69 -0
- package/src/index.ts +19 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import { deepFreeze } from "@morpho-org/morpho-ts";
|
|
2
|
+
import { concat, decodeAbiParameters, encodeAbiParameters, keccak256, parseSignature, verifyTypedData, } from "viem";
|
|
3
|
+
import { signTypedData } from "viem/actions";
|
|
4
|
+
import { EIP712_DOMAIN_TYPEHASH } from "../constants.js";
|
|
5
|
+
import { InvalidTreeError, InvalidTreeHeightError, InvalidTypedDataSignatureError, } from "../errors.js";
|
|
6
|
+
const treeTypeHashes = [
|
|
7
|
+
"0xc27c38e446b48c820ab9c4373dc63a4a750a08165cb4bb488206ebabe045d650",
|
|
8
|
+
"0x4e15d8736f4406e07bf9844b1653474472a827130c61e899bf1f574a88b8d987",
|
|
9
|
+
"0x46d107447b480c38ef5b7f54603dba0cb23b887f302b01a998b9d8a80320dd53",
|
|
10
|
+
"0xd1f3607a8e81454bb3baf5f898274ab47541fffc690278a74f13e174e116be72",
|
|
11
|
+
"0xb2d98adca9d116c9bc02ce59ec599ac3c2d33db1c0d1217c7e411d9198d427be",
|
|
12
|
+
"0x5931e0597fcf986027f3118b2495a9ac22139d133f9ad2c2198e6738dc3886c5",
|
|
13
|
+
"0x3967d37928614a085b47e8758fbc3869a8aed63bdf60ccee8536ff2b5064da06",
|
|
14
|
+
"0xd6b9f5f45915a260f6e521d9b40f86c385730b6bc330590fcde212e2fea64263",
|
|
15
|
+
"0x080caa519dbd5328c119d9907e0fa3d9a50dc2ae4bf6dd42c93c100dbc89b51a",
|
|
16
|
+
"0x45da471048924165ea2ad1855ba940e454b486e71dcf1666c71a928c8844c419",
|
|
17
|
+
"0xa49a9434fc1836bd08097368325b31039b6a0fd44919f53e4d8f4bf814084cb0",
|
|
18
|
+
"0xd3e93e4525132f0187a6964dc01fef33fde414538ffd212e9f2f478c3263e0a0",
|
|
19
|
+
"0x25990db2d26547f92c711988300df317af57bad5cd5d9d8e787a82f95c929474",
|
|
20
|
+
"0x8e0c648afa977572ead40a1d10a6db2c425b8099545006d834a7b849c6166643",
|
|
21
|
+
"0x4b635250efa6243e277fdd0cf6df993c2943b64f10f3a0756ceb1f47ef8f9b18",
|
|
22
|
+
"0xbde1c927f6222c07c8df264e68b42b8382c7c2b85f4729e0df94297cfeebfa91",
|
|
23
|
+
"0x4d58aea1a67f94be21ab1415bf3b602592430eb9112268fd0fc4e141b1a35e76",
|
|
24
|
+
"0x14c03281bce13010b158e5a4a3378be394ac9e16118aedb17d82ced51e66836c",
|
|
25
|
+
"0x99fd3e76f43b2cc221cb9860bc6c96cda95af3fa07ef5f04e071b54aa9386d06",
|
|
26
|
+
"0x1b1c2f1a04968094d8d0453d49838f7a809d1202ae04a1c2e0964e442ff7988b",
|
|
27
|
+
"0xc8ccd3cb3267dd76f563584920ac60f2283b719917481b85fe5e10b754932455",
|
|
28
|
+
];
|
|
29
|
+
const signatureAbi = [
|
|
30
|
+
{
|
|
31
|
+
type: "tuple",
|
|
32
|
+
components: [
|
|
33
|
+
{ name: "v", type: "uint8" },
|
|
34
|
+
{ name: "r", type: "bytes32" },
|
|
35
|
+
{ name: "s", type: "bytes32" },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{ name: "root", type: "bytes32" },
|
|
39
|
+
{ name: "leafIndex", type: "uint256" },
|
|
40
|
+
{ name: "proof", type: "bytes32[]" },
|
|
41
|
+
];
|
|
42
|
+
const domainSeparatorAbi = [
|
|
43
|
+
{ name: "typehash", type: "bytes32" },
|
|
44
|
+
{ name: "chainId", type: "uint256" },
|
|
45
|
+
{ name: "verifyingContract", type: "address" },
|
|
46
|
+
];
|
|
47
|
+
const treeStructHashAbi = [
|
|
48
|
+
{ name: "typehash", type: "bytes32" },
|
|
49
|
+
{ name: "root", type: "bytes32" },
|
|
50
|
+
];
|
|
51
|
+
const typedDataTypes = {
|
|
52
|
+
EIP712Domain: [
|
|
53
|
+
{ name: "chainId", type: "uint256" },
|
|
54
|
+
{ name: "verifyingContract", type: "address" },
|
|
55
|
+
],
|
|
56
|
+
CollateralParams: [
|
|
57
|
+
{ name: "token", type: "address" },
|
|
58
|
+
{ name: "lltv", type: "uint256" },
|
|
59
|
+
{ name: "maxLif", type: "uint256" },
|
|
60
|
+
{ name: "oracle", type: "address" },
|
|
61
|
+
],
|
|
62
|
+
Market: [
|
|
63
|
+
{ name: "loanToken", type: "address" },
|
|
64
|
+
{ name: "collateralParams", type: "CollateralParams[]" },
|
|
65
|
+
{ name: "maturity", type: "uint256" },
|
|
66
|
+
{ name: "rcfThreshold", type: "uint256" },
|
|
67
|
+
{ name: "enterGate", type: "address" },
|
|
68
|
+
{ name: "liquidatorGate", type: "address" },
|
|
69
|
+
],
|
|
70
|
+
Offer: [
|
|
71
|
+
{ name: "market", type: "Market" },
|
|
72
|
+
{ name: "buy", type: "bool" },
|
|
73
|
+
{ name: "maker", type: "address" },
|
|
74
|
+
{ name: "start", type: "uint256" },
|
|
75
|
+
{ name: "expiry", type: "uint256" },
|
|
76
|
+
{ name: "tick", type: "uint256" },
|
|
77
|
+
{ name: "group", type: "bytes32" },
|
|
78
|
+
{ name: "callback", type: "address" },
|
|
79
|
+
{ name: "callbackData", type: "bytes" },
|
|
80
|
+
{ name: "receiverIfMakerIsSeller", type: "address" },
|
|
81
|
+
{ name: "ratifier", type: "address" },
|
|
82
|
+
{ name: "reduceOnly", type: "bool" },
|
|
83
|
+
{ name: "maxUnits", type: "uint256" },
|
|
84
|
+
{ name: "maxAssets", type: "uint256" },
|
|
85
|
+
{ name: "continuousFeeCap", type: "uint256" },
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
const buildTreeValue = (offers) => {
|
|
89
|
+
if (offers.length === 1)
|
|
90
|
+
return offers[0];
|
|
91
|
+
const mid = offers.length / 2;
|
|
92
|
+
return [
|
|
93
|
+
buildTreeValue(offers.slice(0, mid)),
|
|
94
|
+
buildTreeValue(offers.slice(mid)),
|
|
95
|
+
];
|
|
96
|
+
};
|
|
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
|
+
/**
|
|
113
|
+
* EcrecoverRatifier-specific pure utilities.
|
|
114
|
+
*
|
|
115
|
+
* Use this route for EOA and EIP-7702 makers. The make-side sequence is:
|
|
116
|
+
* create offers with the Ecrecover ratifier address, build the group/tree,
|
|
117
|
+
* validate the tree, sign the typed data, call `ratify`, then pass the returned
|
|
118
|
+
* items to `Payload.encode`. Ecrecover trees must contain one ratifier; split
|
|
119
|
+
* trees by ratifier before signing. The signer may be the maker or an address
|
|
120
|
+
* authorized by every maker in the tree.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { EcrecoverRatifierUtils } from "@morpho-org/midnight-sdk";
|
|
125
|
+
*
|
|
126
|
+
* console.log(EcrecoverRatifierUtils.treeTypeHash(0));
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export var EcrecoverRatifierUtils;
|
|
130
|
+
(function (EcrecoverRatifierUtils) {
|
|
131
|
+
/**
|
|
132
|
+
* Returns the Solidity HashLib tree typehash for a tree height.
|
|
133
|
+
*
|
|
134
|
+
* @param height - Tree height.
|
|
135
|
+
* @returns Tree typehash.
|
|
136
|
+
* @throws {InvalidTreeHeightError} when height exceeds 20.
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* import { EcrecoverRatifierUtils } from "@morpho-org/midnight-sdk";
|
|
140
|
+
*
|
|
141
|
+
* console.log(EcrecoverRatifierUtils.treeTypeHash(0));
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
function treeTypeHash(height) {
|
|
145
|
+
const typehash = treeTypeHashes[height];
|
|
146
|
+
if (typehash == null)
|
|
147
|
+
throw new InvalidTreeHeightError(height);
|
|
148
|
+
return typehash;
|
|
149
|
+
}
|
|
150
|
+
EcrecoverRatifierUtils.treeTypeHash = treeTypeHash;
|
|
151
|
+
/**
|
|
152
|
+
* Builds EcrecoverRatifier typed data for a tree.
|
|
153
|
+
*
|
|
154
|
+
* Use after the tree is built and validated, before requesting the signer
|
|
155
|
+
* signature. The EIP-712 verifier is derived from the shared ratifier address
|
|
156
|
+
* on the tree offers. `ratify` calls this for you when given a client and
|
|
157
|
+
* account.
|
|
158
|
+
*
|
|
159
|
+
* @param params.tree - Ecrecover-ratified offer tree to sign.
|
|
160
|
+
* @param params.chainId - EIP-155 chain id included in the EIP-712 domain.
|
|
161
|
+
* @returns EIP-712 typed-data descriptor.
|
|
162
|
+
* @throws {InvalidTreeError} when the tree is invalid or contains multiple ratifiers.
|
|
163
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* import { EcrecoverRatifierUtils, Tree } from "@morpho-org/midnight-sdk";
|
|
167
|
+
* import { Offer } from "@morpho-org/midnight-sdk";
|
|
168
|
+
* import { zeroAddress } from "viem";
|
|
169
|
+
*
|
|
170
|
+
* const offer = Offer.create({
|
|
171
|
+
* market: {
|
|
172
|
+
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
173
|
+
* collateralParams: [
|
|
174
|
+
* {
|
|
175
|
+
* token: "0x0000000000000000000000000000000000007000",
|
|
176
|
+
* lltv: 770000000000000000n,
|
|
177
|
+
* maxLif: 1061007957559681697n,
|
|
178
|
+
* oracle: "0x0000000000000000000000000000000000008000",
|
|
179
|
+
* },
|
|
180
|
+
* ],
|
|
181
|
+
* maturity: 54_000n,
|
|
182
|
+
* rcfThreshold: 0n,
|
|
183
|
+
* enterGate: zeroAddress,
|
|
184
|
+
* liquidatorGate: zeroAddress,
|
|
185
|
+
* },
|
|
186
|
+
* buy: true,
|
|
187
|
+
* maker: "0x0000000000000000000000000000000000009000",
|
|
188
|
+
* tick: 5_000n,
|
|
189
|
+
* expiry: 3_600n,
|
|
190
|
+
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
191
|
+
* maxUnits: 100n,
|
|
192
|
+
* });
|
|
193
|
+
*
|
|
194
|
+
* const typedData = EcrecoverRatifierUtils.typedData({
|
|
195
|
+
* tree: Tree.create([offer]),
|
|
196
|
+
* chainId: 8453n,
|
|
197
|
+
* });
|
|
198
|
+
* console.log(typedData.primaryType);
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
function typedData(params) {
|
|
202
|
+
const verifyingContract = getTreeRatifier(params.tree);
|
|
203
|
+
const treeType = params.tree.height === 0
|
|
204
|
+
? "Offer"
|
|
205
|
+
: `Offer${"[2]".repeat(params.tree.height)}`;
|
|
206
|
+
return deepFreeze({
|
|
207
|
+
domain: {
|
|
208
|
+
chainId: BigInt(params.chainId),
|
|
209
|
+
verifyingContract,
|
|
210
|
+
},
|
|
211
|
+
types: {
|
|
212
|
+
...typedDataTypes,
|
|
213
|
+
OfferTree: [{ name: "offerTree", type: treeType }],
|
|
214
|
+
},
|
|
215
|
+
primaryType: "OfferTree",
|
|
216
|
+
message: {
|
|
217
|
+
offerTree: buildTreeValue(params.tree.paddedOffers),
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
EcrecoverRatifierUtils.typedData = typedData;
|
|
222
|
+
/**
|
|
223
|
+
* Builds the EcrecoverRatifier digest used by the Solidity ratifier.
|
|
224
|
+
*
|
|
225
|
+
* @param params.tree - Ecrecover-ratified offer tree to hash.
|
|
226
|
+
* @param params.chainId - EIP-155 chain id included in the EIP-712 domain.
|
|
227
|
+
* @returns EIP-712 digest.
|
|
228
|
+
* @throws {InvalidTreeError} when the tree contains multiple ratifiers.
|
|
229
|
+
* @throws {InvalidTreeHeightError} when height exceeds 20.
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* import { EcrecoverRatifierUtils, Tree } from "@morpho-org/midnight-sdk";
|
|
233
|
+
* import { Offer } from "@morpho-org/midnight-sdk";
|
|
234
|
+
* import { zeroAddress } from "viem";
|
|
235
|
+
*
|
|
236
|
+
* const offer = Offer.create({
|
|
237
|
+
* market: {
|
|
238
|
+
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
239
|
+
* collateralParams: [
|
|
240
|
+
* {
|
|
241
|
+
* token: "0x0000000000000000000000000000000000007000",
|
|
242
|
+
* lltv: 770000000000000000n,
|
|
243
|
+
* maxLif: 1061007957559681697n,
|
|
244
|
+
* oracle: "0x0000000000000000000000000000000000008000",
|
|
245
|
+
* },
|
|
246
|
+
* ],
|
|
247
|
+
* maturity: 54_000n,
|
|
248
|
+
* rcfThreshold: 0n,
|
|
249
|
+
* enterGate: zeroAddress,
|
|
250
|
+
* liquidatorGate: zeroAddress,
|
|
251
|
+
* },
|
|
252
|
+
* buy: true,
|
|
253
|
+
* maker: "0x0000000000000000000000000000000000009000",
|
|
254
|
+
* tick: 5_000n,
|
|
255
|
+
* expiry: 3_600n,
|
|
256
|
+
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
257
|
+
* maxUnits: 100n,
|
|
258
|
+
* });
|
|
259
|
+
*
|
|
260
|
+
* const digest = EcrecoverRatifierUtils.digest({
|
|
261
|
+
* tree: Tree.create([offer]),
|
|
262
|
+
* chainId: 8453n,
|
|
263
|
+
* });
|
|
264
|
+
* console.log(digest);
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
function digest(params) {
|
|
268
|
+
const verifyingContract = getTreeRatifier(params.tree);
|
|
269
|
+
const domainSeparator = keccak256(encodeAbiParameters(domainSeparatorAbi, [
|
|
270
|
+
EIP712_DOMAIN_TYPEHASH,
|
|
271
|
+
BigInt(params.chainId),
|
|
272
|
+
verifyingContract,
|
|
273
|
+
]));
|
|
274
|
+
const structHash = keccak256(encodeAbiParameters(treeStructHashAbi, [
|
|
275
|
+
treeTypeHash(params.tree.height),
|
|
276
|
+
params.tree.root,
|
|
277
|
+
]));
|
|
278
|
+
return keccak256(concat(["0x1901", domainSeparator, structHash]));
|
|
279
|
+
}
|
|
280
|
+
EcrecoverRatifierUtils.digest = digest;
|
|
281
|
+
/**
|
|
282
|
+
* Signs EcrecoverRatifier typed data through a viem client.
|
|
283
|
+
*
|
|
284
|
+
* Use when app code wants the signature separately from payload item
|
|
285
|
+
* construction. If you only need payload items, call `ratify` with the same
|
|
286
|
+
* client and account. The account may be the maker or an address authorized
|
|
287
|
+
* by every maker in the tree; the protocol checks that authorization onchain.
|
|
288
|
+
*
|
|
289
|
+
* @param params.tree - Ecrecover-ratified offer tree to sign.
|
|
290
|
+
* @param params.client - Viem client whose transport signs the tree typed data.
|
|
291
|
+
* @param params.account - Account used to sign the tree typed data.
|
|
292
|
+
* @returns Signature returned by the client.
|
|
293
|
+
* @throws {InvalidTreeError} when the tree is invalid or contains multiple ratifiers.
|
|
294
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
295
|
+
* @throws {InvalidTypedDataSignatureError} when the returned signature does not recover to `params.account`.
|
|
296
|
+
* @example
|
|
297
|
+
* ```ts
|
|
298
|
+
* import { EcrecoverRatifierUtils, Tree } from "@morpho-org/midnight-sdk";
|
|
299
|
+
* import { Offer } from "@morpho-org/midnight-sdk";
|
|
300
|
+
* import { createWalletClient, http, zeroAddress } from "viem";
|
|
301
|
+
* import { base } from "viem/chains";
|
|
302
|
+
*
|
|
303
|
+
* const offer = Offer.create({
|
|
304
|
+
* market: {
|
|
305
|
+
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
306
|
+
* collateralParams: [
|
|
307
|
+
* {
|
|
308
|
+
* token: "0x0000000000000000000000000000000000007000",
|
|
309
|
+
* lltv: 770000000000000000n,
|
|
310
|
+
* maxLif: 1061007957559681697n,
|
|
311
|
+
* oracle: "0x0000000000000000000000000000000000008000",
|
|
312
|
+
* },
|
|
313
|
+
* ],
|
|
314
|
+
* maturity: 54_000n,
|
|
315
|
+
* rcfThreshold: 0n,
|
|
316
|
+
* enterGate: zeroAddress,
|
|
317
|
+
* liquidatorGate: zeroAddress,
|
|
318
|
+
* },
|
|
319
|
+
* buy: true,
|
|
320
|
+
* maker: "0x0000000000000000000000000000000000009000",
|
|
321
|
+
* tick: 5_000n,
|
|
322
|
+
* expiry: 3_600n,
|
|
323
|
+
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
324
|
+
* maxUnits: 100n,
|
|
325
|
+
* });
|
|
326
|
+
* const client = createWalletClient({
|
|
327
|
+
* chain: base,
|
|
328
|
+
* transport: http(),
|
|
329
|
+
* });
|
|
330
|
+
*
|
|
331
|
+
* const signature = await EcrecoverRatifierUtils.sign({
|
|
332
|
+
* tree: Tree.create([offer]),
|
|
333
|
+
* client,
|
|
334
|
+
* account: offer.maker,
|
|
335
|
+
* });
|
|
336
|
+
* console.log(signature);
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
async function sign(params) {
|
|
340
|
+
const signer = typeof params.account === "string"
|
|
341
|
+
? params.account
|
|
342
|
+
: params.account.address;
|
|
343
|
+
const data = typedData({
|
|
344
|
+
tree: params.tree,
|
|
345
|
+
chainId: params.client.chain.id,
|
|
346
|
+
});
|
|
347
|
+
const signature = await signTypedData(params.client, {
|
|
348
|
+
account: params.account,
|
|
349
|
+
...data,
|
|
350
|
+
});
|
|
351
|
+
const isValidSignature = await verifyTypedData({
|
|
352
|
+
...data,
|
|
353
|
+
address: signer,
|
|
354
|
+
signature,
|
|
355
|
+
});
|
|
356
|
+
if (!isValidSignature)
|
|
357
|
+
throw new InvalidTypedDataSignatureError(signer);
|
|
358
|
+
return signature;
|
|
359
|
+
}
|
|
360
|
+
EcrecoverRatifierUtils.sign = sign;
|
|
361
|
+
/**
|
|
362
|
+
* Converts a hex ECDSA signature into the Solidity tuple shape.
|
|
363
|
+
*
|
|
364
|
+
* @param signature - Hex string or tuple signature.
|
|
365
|
+
* @returns Signature tuple.
|
|
366
|
+
* @example
|
|
367
|
+
* ```ts
|
|
368
|
+
* import { EcrecoverRatifierUtils } from "@morpho-org/midnight-sdk";
|
|
369
|
+
* import { zeroHash } from "viem";
|
|
370
|
+
*
|
|
371
|
+
* const signature = EcrecoverRatifierUtils.toSignature({
|
|
372
|
+
* v: 27,
|
|
373
|
+
* r: zeroHash,
|
|
374
|
+
* s: zeroHash,
|
|
375
|
+
* });
|
|
376
|
+
* console.log(signature.v);
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
function toSignature(signature) {
|
|
380
|
+
if (typeof signature !== "string") {
|
|
381
|
+
return {
|
|
382
|
+
v: signature.v != null
|
|
383
|
+
? Number(signature.v)
|
|
384
|
+
: Number(signature.yParity) + 27,
|
|
385
|
+
r: signature.r,
|
|
386
|
+
s: signature.s,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
const parsed = parseSignature(signature);
|
|
390
|
+
return {
|
|
391
|
+
v: Number(parsed.v ?? BigInt(parsed.yParity + 27)),
|
|
392
|
+
r: parsed.r,
|
|
393
|
+
s: parsed.s,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
EcrecoverRatifierUtils.toSignature = toSignature;
|
|
397
|
+
/**
|
|
398
|
+
* Encodes EcrecoverRatifier ratifier data.
|
|
399
|
+
*
|
|
400
|
+
* Use only when you already have a signature and proof. Most maker flows call
|
|
401
|
+
* `ratifierData` for one leaf or `ratify` for every leaf in the tree.
|
|
402
|
+
*
|
|
403
|
+
* @param params.signature - Signature tuple encoded into the ratifier data.
|
|
404
|
+
* @param params.root - Merkle root approved by the signature.
|
|
405
|
+
* @param params.leafIndex - Leaf index proven by `params.proof`.
|
|
406
|
+
* @param params.proof - Merkle proof siblings for the leaf.
|
|
407
|
+
* @returns ABI-encoded ratifier data.
|
|
408
|
+
* @example
|
|
409
|
+
* ```ts
|
|
410
|
+
* import { EcrecoverRatifierUtils } from "@morpho-org/midnight-sdk";
|
|
411
|
+
*
|
|
412
|
+
* const data = EcrecoverRatifierUtils.encodeRatifierData({
|
|
413
|
+
* signature: {
|
|
414
|
+
* v: 27,
|
|
415
|
+
* r: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
416
|
+
* s: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
417
|
+
* },
|
|
418
|
+
* root: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
419
|
+
* leafIndex: 0n,
|
|
420
|
+
* proof: [],
|
|
421
|
+
* });
|
|
422
|
+
* console.log(data);
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
function encodeRatifierData(params) {
|
|
426
|
+
const signature = toSignature(params.signature);
|
|
427
|
+
return encodeAbiParameters(signatureAbi, [
|
|
428
|
+
{
|
|
429
|
+
v: signature.v,
|
|
430
|
+
r: signature.r,
|
|
431
|
+
s: signature.s,
|
|
432
|
+
},
|
|
433
|
+
params.root,
|
|
434
|
+
BigInt(params.leafIndex),
|
|
435
|
+
params.proof,
|
|
436
|
+
]);
|
|
437
|
+
}
|
|
438
|
+
EcrecoverRatifierUtils.encodeRatifierData = encodeRatifierData;
|
|
439
|
+
/**
|
|
440
|
+
* Decodes EcrecoverRatifier ratifier data.
|
|
441
|
+
*
|
|
442
|
+
* Use on the take-side or in tests after `Payload.decode` to inspect the
|
|
443
|
+
* proof/signature attached to a published offer.
|
|
444
|
+
*
|
|
445
|
+
* @param data - ABI-encoded ratifier data.
|
|
446
|
+
* @returns Decoded Ecrecover ratifier data.
|
|
447
|
+
* @example
|
|
448
|
+
* ```ts
|
|
449
|
+
* import { EcrecoverRatifierUtils } from "@morpho-org/midnight-sdk";
|
|
450
|
+
* import { zeroHash } from "viem";
|
|
451
|
+
*
|
|
452
|
+
* const data = EcrecoverRatifierUtils.encodeRatifierData({
|
|
453
|
+
* signature: { v: 27, r: zeroHash, s: zeroHash },
|
|
454
|
+
* root: zeroHash,
|
|
455
|
+
* leafIndex: 0n,
|
|
456
|
+
* proof: [],
|
|
457
|
+
* });
|
|
458
|
+
* const decoded = EcrecoverRatifierUtils.decodeRatifierData(data);
|
|
459
|
+
* console.log(decoded.leafIndex);
|
|
460
|
+
* ```
|
|
461
|
+
*/
|
|
462
|
+
function decodeRatifierData(data) {
|
|
463
|
+
const [signature, root, leafIndex, proof] = decodeAbiParameters(signatureAbi, data);
|
|
464
|
+
return deepFreeze({
|
|
465
|
+
signature: {
|
|
466
|
+
v: signature.v,
|
|
467
|
+
r: signature.r,
|
|
468
|
+
s: signature.s,
|
|
469
|
+
},
|
|
470
|
+
root,
|
|
471
|
+
leafIndex,
|
|
472
|
+
proof: [...proof],
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
EcrecoverRatifierUtils.decodeRatifierData = decodeRatifierData;
|
|
476
|
+
/**
|
|
477
|
+
* Builds one ratifier-data value for a tree leaf.
|
|
478
|
+
*
|
|
479
|
+
* Use after a tree has been signed when a caller needs data for one offer
|
|
480
|
+
* leaf. Use `ratify` to produce payload-ready items for the whole tree.
|
|
481
|
+
*
|
|
482
|
+
* @param params.tree - Ecrecover-ratified offer tree that produced the proof.
|
|
483
|
+
* @param params.leafIndex - Leaf index to prove.
|
|
484
|
+
* @param params.signature - Ecrecover signature for the tree root.
|
|
485
|
+
* @returns ABI-encoded EcrecoverRatifier data.
|
|
486
|
+
* @throws {InvalidTreeError} when the leaf index is outside the tree or the tree contains multiple ratifiers.
|
|
487
|
+
* @example
|
|
488
|
+
* ```ts
|
|
489
|
+
* import { EcrecoverRatifierUtils, Offer, Tree } from "@morpho-org/midnight-sdk";
|
|
490
|
+
* import { zeroAddress, zeroHash } from "viem";
|
|
491
|
+
*
|
|
492
|
+
* const offer = Offer.create({
|
|
493
|
+
* market: {
|
|
494
|
+
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
495
|
+
* collateralParams: [
|
|
496
|
+
* {
|
|
497
|
+
* token: "0x0000000000000000000000000000000000007000",
|
|
498
|
+
* lltv: 770000000000000000n,
|
|
499
|
+
* maxLif: 1061007957559681697n,
|
|
500
|
+
* oracle: "0x0000000000000000000000000000000000008000",
|
|
501
|
+
* },
|
|
502
|
+
* ],
|
|
503
|
+
* maturity: 54_000n,
|
|
504
|
+
* rcfThreshold: 0n,
|
|
505
|
+
* enterGate: zeroAddress,
|
|
506
|
+
* liquidatorGate: zeroAddress,
|
|
507
|
+
* },
|
|
508
|
+
* buy: true,
|
|
509
|
+
* maker: "0x0000000000000000000000000000000000009000",
|
|
510
|
+
* tick: 5_000n,
|
|
511
|
+
* expiry: 3_600n,
|
|
512
|
+
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
513
|
+
* maxUnits: 100n,
|
|
514
|
+
* });
|
|
515
|
+
* const data = EcrecoverRatifierUtils.ratifierData({
|
|
516
|
+
* tree: Tree.create([offer]),
|
|
517
|
+
* leafIndex: 0n,
|
|
518
|
+
* signature: { v: 27, r: zeroHash, s: zeroHash },
|
|
519
|
+
* });
|
|
520
|
+
* console.log(data);
|
|
521
|
+
* ```
|
|
522
|
+
*/
|
|
523
|
+
function ratifierData(params) {
|
|
524
|
+
getTreeRatifier(params.tree);
|
|
525
|
+
const proof = params.tree.proof(params.leafIndex);
|
|
526
|
+
return encodeRatifierData({
|
|
527
|
+
signature: toSignature(params.signature),
|
|
528
|
+
root: proof.root,
|
|
529
|
+
leafIndex: proof.leafIndex,
|
|
530
|
+
proof: proof.proof,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
EcrecoverRatifierUtils.ratifierData = ratifierData;
|
|
534
|
+
/**
|
|
535
|
+
* Signs or consumes a tree signature and returns payload-ready items.
|
|
536
|
+
*
|
|
537
|
+
* Use after `Tree.mempoolValidate` and before `Payload.encode`.
|
|
538
|
+
* The returned items preserve tree leaf order and include ratifier data
|
|
539
|
+
* required by takers. The group id is stored on each inline offer.
|
|
540
|
+
*
|
|
541
|
+
* @param params.tree - Ecrecover-ratified offer tree to ratify.
|
|
542
|
+
* @param params.client - Optional viem client whose transport signs typed data built from `params.tree`.
|
|
543
|
+
* @param params.account - Optional account used to sign typed data built from `params.tree`.
|
|
544
|
+
* @param params.signature - Optional precomputed signature for `params.tree`.
|
|
545
|
+
* @returns Items containing each offer and its ratifier data.
|
|
546
|
+
* @throws {InvalidTreeError} when the tree is invalid or contains multiple ratifiers.
|
|
547
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
548
|
+
* @throws {InvalidTypedDataSignatureError} when client signing returns a signature that does not recover to `params.account`.
|
|
549
|
+
* @example
|
|
550
|
+
* ```ts
|
|
551
|
+
* import { EcrecoverRatifierUtils, Tree } from "@morpho-org/midnight-sdk";
|
|
552
|
+
* import { Offer } from "@morpho-org/midnight-sdk";
|
|
553
|
+
* import { createWalletClient, http, zeroAddress } from "viem";
|
|
554
|
+
* import { base } from "viem/chains";
|
|
555
|
+
*
|
|
556
|
+
* const offer = Offer.create({
|
|
557
|
+
* market: {
|
|
558
|
+
* loanToken: "0x0000000000000000000000000000000000006000",
|
|
559
|
+
* collateralParams: [
|
|
560
|
+
* {
|
|
561
|
+
* token: "0x0000000000000000000000000000000000007000",
|
|
562
|
+
* lltv: 770000000000000000n,
|
|
563
|
+
* maxLif: 1061007957559681697n,
|
|
564
|
+
* oracle: "0x0000000000000000000000000000000000008000",
|
|
565
|
+
* },
|
|
566
|
+
* ],
|
|
567
|
+
* maturity: 54_000n,
|
|
568
|
+
* rcfThreshold: 0n,
|
|
569
|
+
* enterGate: zeroAddress,
|
|
570
|
+
* liquidatorGate: zeroAddress,
|
|
571
|
+
* },
|
|
572
|
+
* buy: true,
|
|
573
|
+
* maker: "0x0000000000000000000000000000000000009000",
|
|
574
|
+
* tick: 5_000n,
|
|
575
|
+
* expiry: 3_600n,
|
|
576
|
+
* ratifier: "0x0000000000000000000000000000000000004000",
|
|
577
|
+
* maxUnits: 100n,
|
|
578
|
+
* });
|
|
579
|
+
* const client = createWalletClient({
|
|
580
|
+
* chain: base,
|
|
581
|
+
* transport: http(),
|
|
582
|
+
* });
|
|
583
|
+
*
|
|
584
|
+
* const items = await EcrecoverRatifierUtils.ratify({
|
|
585
|
+
* tree: Tree.create([offer]),
|
|
586
|
+
* client,
|
|
587
|
+
* account: offer.maker,
|
|
588
|
+
* });
|
|
589
|
+
* console.log(items.length);
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
async function ratify(params) {
|
|
593
|
+
let signature;
|
|
594
|
+
if (params.signature != null) {
|
|
595
|
+
getTreeRatifier(params.tree);
|
|
596
|
+
signature = toSignature(params.signature);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
signature = toSignature(await sign(params));
|
|
600
|
+
}
|
|
601
|
+
const items = [];
|
|
602
|
+
for (const offer of params.tree.offers) {
|
|
603
|
+
items.push({
|
|
604
|
+
offer,
|
|
605
|
+
ratifierData: ratifierData({
|
|
606
|
+
tree: params.tree,
|
|
607
|
+
leafIndex: items.length,
|
|
608
|
+
signature,
|
|
609
|
+
}),
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
return items;
|
|
613
|
+
}
|
|
614
|
+
EcrecoverRatifierUtils.ratify = ratify;
|
|
615
|
+
})(EcrecoverRatifierUtils || (EcrecoverRatifierUtils = {}));
|