@morpho-org/midnight-sdk 1.3.1 → 1.4.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/lib/cjs/api/MidnightApi.d.ts +8 -2
- package/lib/cjs/api/MidnightApi.js +33 -16
- package/lib/cjs/api/types.d.ts +2 -0
- package/lib/cjs/api/version.generated.d.ts +1 -1
- package/lib/cjs/api/version.generated.js +1 -1
- package/lib/cjs/offers/OfferUtils.js +6 -9
- package/lib/cjs/queries/GetPosition.d.ts +1 -1
- package/lib/cjs/queries/GetPosition.js +1 -1
- package/lib/cjs/signatures/EcrecoverRatifierUtils.js +7 -12
- package/lib/cjs/signatures/RatifierUtils.d.ts +2 -2
- package/lib/cjs/signatures/RatifierUtils.js +46 -2
- package/lib/cjs/signatures/SetterRatifierUtils.js +4 -11
- package/lib/cjs/signatures/offerStructInternal.js +4 -4
- package/lib/esm/api/MidnightApi.d.ts +8 -2
- package/lib/esm/api/MidnightApi.js +35 -18
- package/lib/esm/api/types.d.ts +2 -0
- package/lib/esm/api/version.generated.d.ts +1 -1
- package/lib/esm/api/version.generated.js +1 -1
- package/lib/esm/offers/OfferUtils.js +6 -9
- package/lib/esm/queries/GetPosition.d.ts +1 -1
- package/lib/esm/queries/GetPosition.js +1 -1
- package/lib/esm/signatures/EcrecoverRatifierUtils.js +7 -12
- package/lib/esm/signatures/RatifierUtils.d.ts +2 -2
- package/lib/esm/signatures/RatifierUtils.js +48 -4
- package/lib/esm/signatures/SetterRatifierUtils.js +4 -11
- package/lib/esm/signatures/offerStructInternal.js +5 -5
- package/package.json +5 -5
|
@@ -166,12 +166,15 @@ export declare class MidnightApi {
|
|
|
166
166
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
167
167
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
168
168
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
169
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
169
170
|
* @param params.baseUrl - Optional Midnight API base URL override.
|
|
170
171
|
* @param params.fetch - Optional fetch implementation override.
|
|
171
172
|
* @param params.request - Optional fetch options forwarded to this request.
|
|
172
173
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
174
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
173
175
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
174
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
176
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
177
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
175
178
|
* @example
|
|
176
179
|
* ```ts
|
|
177
180
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
|
@@ -411,9 +414,12 @@ export declare class MidnightApi {
|
|
|
411
414
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
412
415
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
413
416
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
417
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
414
418
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
419
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
415
420
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
416
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
421
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
422
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
417
423
|
* @example
|
|
418
424
|
* ```ts
|
|
419
425
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
|
@@ -242,12 +242,15 @@ class MidnightApi {
|
|
|
242
242
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
243
243
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
244
244
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
245
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
245
246
|
* @param params.baseUrl - Optional Midnight API base URL override.
|
|
246
247
|
* @param params.fetch - Optional fetch implementation override.
|
|
247
248
|
* @param params.request - Optional fetch options forwarded to this request.
|
|
248
249
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
250
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
249
251
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
250
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
252
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
253
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
251
254
|
* @example
|
|
252
255
|
* ```ts
|
|
253
256
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
|
@@ -263,6 +266,8 @@ class MidnightApi {
|
|
|
263
266
|
*/
|
|
264
267
|
static async fetchBookQuote(params) {
|
|
265
268
|
const input = params;
|
|
269
|
+
const settlementFee = BigInt(input.settlementFee ?? 0n);
|
|
270
|
+
(0, morpho_ts_1.assertNonNegative)("settlementFee", settlementFee);
|
|
266
271
|
const response = await (0, helpers_js_1.requestMidnightApi)({
|
|
267
272
|
...input,
|
|
268
273
|
method: "GET",
|
|
@@ -290,16 +295,20 @@ class MidnightApi {
|
|
|
290
295
|
if (averageWorstPrice != null) {
|
|
291
296
|
const guard = BigInt(averageWorstPrice);
|
|
292
297
|
let filledUnits = 0n;
|
|
293
|
-
let
|
|
298
|
+
let filledAssets = 0n;
|
|
294
299
|
if ("units" in input && input.units != null) {
|
|
295
300
|
let remainingUnits = BigInt(input.units);
|
|
296
301
|
for (const take of takeableOffers) {
|
|
297
302
|
if (remainingUnits === 0n)
|
|
298
303
|
break;
|
|
299
|
-
const
|
|
304
|
+
const { buyerPrice, sellerPrice } = index_js_1.TakeAmountsLib.prices({
|
|
305
|
+
offer: take.offer,
|
|
306
|
+
settlementFee,
|
|
307
|
+
});
|
|
308
|
+
const price = input.side === "asks" ? buyerPrice : sellerPrice;
|
|
300
309
|
const filled = take.units < remainingUnits ? take.units : remainingUnits;
|
|
301
310
|
filledUnits += filled;
|
|
302
|
-
|
|
311
|
+
filledAssets += morpho_ts_1.MathLib.mulDiv(filled, price, morpho_ts_1.MathLib.WAD, input.side === "asks" ? "Up" : "Down");
|
|
303
312
|
remainingUnits -= filled;
|
|
304
313
|
}
|
|
305
314
|
}
|
|
@@ -308,25 +317,30 @@ class MidnightApi {
|
|
|
308
317
|
for (const take of takeableOffers) {
|
|
309
318
|
if (remainingAssets === 0n)
|
|
310
319
|
break;
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const
|
|
320
|
+
const { buyerPrice, sellerPrice } = index_js_1.TakeAmountsLib.prices({
|
|
321
|
+
offer: take.offer,
|
|
322
|
+
settlementFee,
|
|
323
|
+
});
|
|
324
|
+
const makerPrice = input.side === "asks" ? sellerPrice : buyerPrice;
|
|
325
|
+
const settlementPrice = input.side === "asks" ? buyerPrice : sellerPrice;
|
|
326
|
+
const takeMakerAssets = input.side === "asks"
|
|
327
|
+
? morpho_ts_1.MathLib.mulDivUp(take.units, makerPrice, morpho_ts_1.MathLib.WAD)
|
|
328
|
+
: morpho_ts_1.MathLib.mulDivDown(take.units, makerPrice, morpho_ts_1.MathLib.WAD);
|
|
329
|
+
const fillsEntireTake = takeMakerAssets <= remainingAssets;
|
|
318
330
|
const filled = fillsEntireTake
|
|
319
331
|
? take.units
|
|
320
|
-
: morpho_ts_1.MathLib.mulDiv(remainingAssets, morpho_ts_1.MathLib.WAD,
|
|
332
|
+
: morpho_ts_1.MathLib.mulDiv(remainingAssets, morpho_ts_1.MathLib.WAD, makerPrice, input.side === "asks" ? "Down" : "Up");
|
|
321
333
|
if (filled === 0n)
|
|
322
334
|
continue;
|
|
323
335
|
filledUnits += filled;
|
|
324
|
-
|
|
325
|
-
remainingAssets = fillsEntireTake
|
|
336
|
+
filledAssets += morpho_ts_1.MathLib.mulDiv(filled, settlementPrice, morpho_ts_1.MathLib.WAD, input.side === "asks" ? "Up" : "Down");
|
|
337
|
+
remainingAssets = fillsEntireTake
|
|
338
|
+
? remainingAssets - takeMakerAssets
|
|
339
|
+
: 0n;
|
|
326
340
|
}
|
|
327
341
|
}
|
|
328
342
|
if (filledUnits > 0n) {
|
|
329
|
-
const averagePrice = morpho_ts_1.MathLib.
|
|
343
|
+
const averagePrice = morpho_ts_1.MathLib.mulDiv(filledAssets, morpho_ts_1.MathLib.WAD, filledUnits, input.side === "asks" ? "Up" : "Down");
|
|
330
344
|
const violatesGuard = input.side === "asks" ? averagePrice > guard : averagePrice < guard;
|
|
331
345
|
if (violatesGuard) {
|
|
332
346
|
throw new errors_js_1.InvalidMidnightApiResponseError(`Midnight API quote takeable offers imply average price "${averagePrice}" outside average_worst_price guard "${guard}".`);
|
|
@@ -636,9 +650,12 @@ class MidnightApi {
|
|
|
636
650
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
637
651
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
638
652
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
653
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
639
654
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
655
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
640
656
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
641
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
657
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
658
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
642
659
|
* @example
|
|
643
660
|
* ```ts
|
|
644
661
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
package/lib/cjs/api/types.d.ts
CHANGED
|
@@ -177,6 +177,8 @@ export type FetchBookQuoteParams = MidnightApiConfig & {
|
|
|
177
177
|
readonly marketId: Hash;
|
|
178
178
|
/** Book side to quote. */
|
|
179
179
|
readonly side: MidnightApiBookSide;
|
|
180
|
+
/** Current WAD-scaled settlement fee used for local guard validation. Defaults to zero. */
|
|
181
|
+
readonly settlementFee?: BigIntish;
|
|
180
182
|
} & (MidnightApiQuoteAssetsTarget | MidnightApiQuoteUnitsTarget) & (MidnightApiQuoteAverageWorstPriceGuard | MidnightApiQuoteSlippageGuard | MidnightApiQuoteWithoutGuard);
|
|
181
183
|
/**
|
|
182
184
|
* Parameters for {@link MidnightApi.fetchTakeableOffers}.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal Installed Midnight SDK package version. */
|
|
2
|
-
export declare const MIDNIGHT_SDK_VERSION = "1.
|
|
2
|
+
export declare const MIDNIGHT_SDK_VERSION = "1.4.0";
|
|
@@ -868,23 +868,20 @@ var OfferUtils;
|
|
|
868
868
|
return 0n;
|
|
869
869
|
if (continuousFeeCap < BigInt(market.continuousFee))
|
|
870
870
|
return 0n;
|
|
871
|
+
const settlementFee = market.getSettlementFee(market.timeToMaturity(timestamp));
|
|
872
|
+
// Validates the settlement fee against the offer price for both cap modes.
|
|
873
|
+
const { sellerPrice, buyerPrice } = index_js_2.TakeAmountsLib.prices({
|
|
874
|
+
offer,
|
|
875
|
+
settlementFee,
|
|
876
|
+
});
|
|
871
877
|
if (maxUnits > 0n)
|
|
872
878
|
return morpho_ts_1.MathLib.zeroFloorSub(maxUnits, consumed);
|
|
873
879
|
const remainingAssets = morpho_ts_1.MathLib.zeroFloorSub(maxAssets, consumed);
|
|
874
|
-
const settlementFee = market.getSettlementFee(market.timeToMaturity(timestamp));
|
|
875
880
|
if (!offer.buy) {
|
|
876
|
-
const { sellerPrice } = index_js_2.TakeAmountsLib.prices({
|
|
877
|
-
offer,
|
|
878
|
-
settlementFee,
|
|
879
|
-
});
|
|
880
881
|
if (sellerPrice === 0n)
|
|
881
882
|
return morpho_ts_1.MathLib.MAX_UINT_256;
|
|
882
883
|
return morpho_ts_1.MathLib.mulDivDown(remainingAssets, morpho_ts_1.MathLib.WAD, sellerPrice);
|
|
883
884
|
}
|
|
884
|
-
const { buyerPrice } = index_js_2.TakeAmountsLib.prices({
|
|
885
|
-
offer,
|
|
886
|
-
settlementFee,
|
|
887
|
-
});
|
|
888
885
|
if (buyerPrice === 0n)
|
|
889
886
|
return morpho_ts_1.MathLib.MAX_UINT_256;
|
|
890
887
|
return ((remainingAssets + 1n) * morpho_ts_1.MathLib.WAD - 1n) / buyerPrice;
|
|
@@ -52,4 +52,4 @@ export declare const abi: readonly [{
|
|
|
52
52
|
readonly type: "function";
|
|
53
53
|
}];
|
|
54
54
|
/** @internal Deployless `GetPosition` query bytecode. */
|
|
55
|
-
export declare const code = "
|
|
55
|
+
export declare const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea26469706673582212200eb5ee2c25180818e6f54eaf74e6f626bff5ff8c68ec38bf86f24bc97a4fe77264736f6c63430008240033";
|
|
@@ -71,4 +71,4 @@ exports.abi = [
|
|
|
71
71
|
},
|
|
72
72
|
];
|
|
73
73
|
/** @internal Deployless `GetPosition` query bytecode. */
|
|
74
|
-
exports.code = "
|
|
74
|
+
exports.code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea26469706673582212200eb5ee2c25180818e6f54eaf74e6f626bff5ff8c68ec38bf86f24bc97a4fe77264736f6c63430008240033";
|
|
@@ -817,18 +817,13 @@ var EcrecoverRatifierUtils;
|
|
|
817
817
|
account: params.account,
|
|
818
818
|
}));
|
|
819
819
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
signature,
|
|
828
|
-
}),
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
return items;
|
|
820
|
+
return tree.offers.map((offer, leafIndex) => ({
|
|
821
|
+
offer,
|
|
822
|
+
ratifierData: encodeRatifierData({
|
|
823
|
+
signature,
|
|
824
|
+
...TreeUtils_js_1.TreeUtils.buildProof({ tree, leafIndex }),
|
|
825
|
+
}),
|
|
826
|
+
}));
|
|
832
827
|
}
|
|
833
828
|
EcrecoverRatifierUtils.ratify = ratify;
|
|
834
829
|
})(EcrecoverRatifierUtils || (exports.EcrecoverRatifierUtils = EcrecoverRatifierUtils = {}));
|
|
@@ -76,8 +76,8 @@ export declare namespace RatifierUtils {
|
|
|
76
76
|
* @param params.tree - Tree-like object or raw offer/group input.
|
|
77
77
|
* @param params.label - Ratifier label used in validation errors.
|
|
78
78
|
* @returns Normalized tree-like data and its shared ratifier.
|
|
79
|
-
* @throws {InvalidTreeError} when the tree is empty or contains multiple ratifiers.
|
|
80
|
-
* @throws {InvalidTreeHeightError} when
|
|
79
|
+
* @throws {InvalidTreeError} when the tree fields disagree, the tree is empty, or it contains multiple ratifiers.
|
|
80
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
81
81
|
* @example
|
|
82
82
|
* ```ts
|
|
83
83
|
* import { Offer, RatifierUtils } from "@morpho-org/midnight-sdk";
|
|
@@ -5,6 +5,7 @@ const errors_js_1 = require("../errors.js");
|
|
|
5
5
|
const index_js_1 = require("../offers/index.js");
|
|
6
6
|
const Group_js_1 = require("./Group.js");
|
|
7
7
|
const GroupUtils_js_1 = require("./GroupUtils.js");
|
|
8
|
+
const offerStructInternal_js_1 = require("./offerStructInternal.js");
|
|
8
9
|
const TreeUtils_js_1 = require("./TreeUtils.js");
|
|
9
10
|
function isTreeLike(tree) {
|
|
10
11
|
return (!Array.isArray(tree) &&
|
|
@@ -16,6 +17,49 @@ function isTreeLike(tree) {
|
|
|
16
17
|
}
|
|
17
18
|
function normalizeTree(tree) {
|
|
18
19
|
if (isTreeLike(tree)) {
|
|
20
|
+
if (!Number.isInteger(tree.height) || tree.height < 0 || tree.height > 20) {
|
|
21
|
+
throw new errors_js_1.InvalidTreeHeightError(tree.height);
|
|
22
|
+
}
|
|
23
|
+
const expectedLength = 2 ** tree.height;
|
|
24
|
+
if (tree.paddedOffers.length !== expectedLength ||
|
|
25
|
+
tree.leaves.length !== expectedLength ||
|
|
26
|
+
tree.offers.length > expectedLength) {
|
|
27
|
+
throw new errors_js_1.InvalidTreeError("Tree offers, leaves, and height describe different trees.");
|
|
28
|
+
}
|
|
29
|
+
const computedLeaves = tree.paddedOffers.map(index_js_1.OfferUtils.hashStruct);
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
for (const [index, offer] of tree.offers.entries()) {
|
|
32
|
+
if ((0, offerStructInternal_js_1.isEmptyOfferStruct)(tree.paddedOffers[index], {
|
|
33
|
+
allowDefaultGroup: true,
|
|
34
|
+
})) {
|
|
35
|
+
throw new errors_js_1.InvalidTreeError("Visible offers must not contain tree padding.");
|
|
36
|
+
}
|
|
37
|
+
const hash = index_js_1.OfferUtils.hashStruct(index_js_1.OfferUtils.toStruct({ offer }));
|
|
38
|
+
if (hash.toLowerCase() !== computedLeaves[index].toLowerCase() ||
|
|
39
|
+
seen.has(hash.toLowerCase())) {
|
|
40
|
+
throw new errors_js_1.InvalidTreeError("Visible offers do not match the tree leaves.");
|
|
41
|
+
}
|
|
42
|
+
seen.add(hash.toLowerCase());
|
|
43
|
+
}
|
|
44
|
+
if (tree.paddedOffers
|
|
45
|
+
.slice(tree.offers.length)
|
|
46
|
+
.some((offer) => !(0, offerStructInternal_js_1.isEmptyOfferStruct)(offer, { allowDefaultGroup: true }))) {
|
|
47
|
+
throw new errors_js_1.InvalidTreeError("Tree padding contains offers hidden from the visible offer list.");
|
|
48
|
+
}
|
|
49
|
+
if (computedLeaves.some((leaf, index) => leaf.toLowerCase() !== tree.leaves[index].toLowerCase())) {
|
|
50
|
+
throw new errors_js_1.InvalidTreeError("Tree leaves do not match its padded offers.");
|
|
51
|
+
}
|
|
52
|
+
let level = computedLeaves;
|
|
53
|
+
while (level.length > 1) {
|
|
54
|
+
const next = [];
|
|
55
|
+
for (let index = 0; index < level.length; index += 2) {
|
|
56
|
+
next.push(TreeUtils_js_1.TreeUtils.hashNode(level[index], level[index + 1]));
|
|
57
|
+
}
|
|
58
|
+
level = next;
|
|
59
|
+
}
|
|
60
|
+
if (level[0]?.toLowerCase() !== tree.root.toLowerCase()) {
|
|
61
|
+
throw new errors_js_1.InvalidTreeError("Tree root does not match its leaves.");
|
|
62
|
+
}
|
|
19
63
|
return tree;
|
|
20
64
|
}
|
|
21
65
|
const entries = Array.isArray(tree) ? tree : [tree];
|
|
@@ -77,8 +121,8 @@ var RatifierUtils;
|
|
|
77
121
|
* @param params.tree - Tree-like object or raw offer/group input.
|
|
78
122
|
* @param params.label - Ratifier label used in validation errors.
|
|
79
123
|
* @returns Normalized tree-like data and its shared ratifier.
|
|
80
|
-
* @throws {InvalidTreeError} when the tree is empty or contains multiple ratifiers.
|
|
81
|
-
* @throws {InvalidTreeHeightError} when
|
|
124
|
+
* @throws {InvalidTreeError} when the tree fields disagree, the tree is empty, or it contains multiple ratifiers.
|
|
125
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
82
126
|
* @example
|
|
83
127
|
* ```ts
|
|
84
128
|
* import { Offer, RatifierUtils } from "@morpho-org/midnight-sdk";
|
|
@@ -244,17 +244,10 @@ var SetterRatifierUtils;
|
|
|
244
244
|
tree: params.tree,
|
|
245
245
|
label: "Setter",
|
|
246
246
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
ratifierData: ratifierData({
|
|
252
|
-
tree,
|
|
253
|
-
leafIndex: items.length,
|
|
254
|
-
}),
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
return items;
|
|
247
|
+
return tree.offers.map((offer, leafIndex) => ({
|
|
248
|
+
offer,
|
|
249
|
+
ratifierData: encodeRatifierData(TreeUtils_js_1.TreeUtils.buildProof({ tree, leafIndex })),
|
|
250
|
+
}));
|
|
258
251
|
}
|
|
259
252
|
SetterRatifierUtils.ratify = ratify;
|
|
260
253
|
})(SetterRatifierUtils || (exports.SetterRatifierUtils = SetterRatifierUtils = {}));
|
|
@@ -32,7 +32,7 @@ exports.EMPTY_OFFER_STRUCT = {
|
|
|
32
32
|
maxAssets: 0n,
|
|
33
33
|
continuousFeeCap: 0n,
|
|
34
34
|
};
|
|
35
|
-
const EMPTY_OFFER_DEFAULT_GROUP = index_js_1.OfferUtils.hashStruct(exports.EMPTY_OFFER_STRUCT);
|
|
35
|
+
const EMPTY_OFFER_DEFAULT_GROUP = (0, viem_1.keccak256)(index_js_1.OfferUtils.hashStruct(exports.EMPTY_OFFER_STRUCT));
|
|
36
36
|
/** @internal Returns whether an address value is the zero address. */
|
|
37
37
|
function isZeroAddress(value) {
|
|
38
38
|
return value.toLowerCase() === viem_1.zeroAddress;
|
|
@@ -44,9 +44,9 @@ function isZeroAddress(value) {
|
|
|
44
44
|
* decoding only treats the canonical zero-group empty struct as padding.
|
|
45
45
|
*/
|
|
46
46
|
function isEmptyOfferStruct(offer, options = {}) {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const group = offer.group.toLowerCase();
|
|
48
|
+
const hasEmptyGroup = group === viem_1.zeroHash ||
|
|
49
|
+
(options.allowDefaultGroup === true && group === EMPTY_OFFER_DEFAULT_GROUP);
|
|
50
50
|
return (offer.market.chainId === 0n &&
|
|
51
51
|
isZeroAddress(offer.market.midnight) &&
|
|
52
52
|
isZeroAddress(offer.market.loanToken) &&
|
|
@@ -166,12 +166,15 @@ export declare class MidnightApi {
|
|
|
166
166
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
167
167
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
168
168
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
169
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
169
170
|
* @param params.baseUrl - Optional Midnight API base URL override.
|
|
170
171
|
* @param params.fetch - Optional fetch implementation override.
|
|
171
172
|
* @param params.request - Optional fetch options forwarded to this request.
|
|
172
173
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
174
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
173
175
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
174
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
176
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
177
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
175
178
|
* @example
|
|
176
179
|
* ```ts
|
|
177
180
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
|
@@ -411,9 +414,12 @@ export declare class MidnightApi {
|
|
|
411
414
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
412
415
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
413
416
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
417
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
414
418
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
419
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
415
420
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
416
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
421
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
422
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
417
423
|
* @example
|
|
418
424
|
* ```ts
|
|
419
425
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { MathLib } from "@morpho-org/morpho-ts";
|
|
1
|
+
import { assertNonNegative, MathLib } from "@morpho-org/morpho-ts";
|
|
2
2
|
import { InvalidMidnightApiResponseError } from "../errors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { TakeAmountsLib } from "../math/index.js";
|
|
4
4
|
import { Payload } from "../signatures/Payload.js";
|
|
5
5
|
import { buildBookPath, mapBoundBookMarket, mapBoundBooks, mapBoundTakeableOffers, mapPriceLevel, parseValidationResponse, requestMidnightApi, } from "./helpers.js";
|
|
6
6
|
export { MempoolPayloadValidationRule } from "./types.js";
|
|
@@ -238,12 +238,15 @@ export class MidnightApi {
|
|
|
238
238
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
239
239
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
240
240
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
241
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
241
242
|
* @param params.baseUrl - Optional Midnight API base URL override.
|
|
242
243
|
* @param params.fetch - Optional fetch implementation override.
|
|
243
244
|
* @param params.request - Optional fetch options forwarded to this request.
|
|
244
245
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
246
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
245
247
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
246
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
248
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
249
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
247
250
|
* @example
|
|
248
251
|
* ```ts
|
|
249
252
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
|
@@ -259,6 +262,8 @@ export class MidnightApi {
|
|
|
259
262
|
*/
|
|
260
263
|
static async fetchBookQuote(params) {
|
|
261
264
|
const input = params;
|
|
265
|
+
const settlementFee = BigInt(input.settlementFee ?? 0n);
|
|
266
|
+
assertNonNegative("settlementFee", settlementFee);
|
|
262
267
|
const response = await requestMidnightApi({
|
|
263
268
|
...input,
|
|
264
269
|
method: "GET",
|
|
@@ -286,16 +291,20 @@ export class MidnightApi {
|
|
|
286
291
|
if (averageWorstPrice != null) {
|
|
287
292
|
const guard = BigInt(averageWorstPrice);
|
|
288
293
|
let filledUnits = 0n;
|
|
289
|
-
let
|
|
294
|
+
let filledAssets = 0n;
|
|
290
295
|
if ("units" in input && input.units != null) {
|
|
291
296
|
let remainingUnits = BigInt(input.units);
|
|
292
297
|
for (const take of takeableOffers) {
|
|
293
298
|
if (remainingUnits === 0n)
|
|
294
299
|
break;
|
|
295
|
-
const
|
|
300
|
+
const { buyerPrice, sellerPrice } = TakeAmountsLib.prices({
|
|
301
|
+
offer: take.offer,
|
|
302
|
+
settlementFee,
|
|
303
|
+
});
|
|
304
|
+
const price = input.side === "asks" ? buyerPrice : sellerPrice;
|
|
296
305
|
const filled = take.units < remainingUnits ? take.units : remainingUnits;
|
|
297
306
|
filledUnits += filled;
|
|
298
|
-
|
|
307
|
+
filledAssets += MathLib.mulDiv(filled, price, MathLib.WAD, input.side === "asks" ? "Up" : "Down");
|
|
299
308
|
remainingUnits -= filled;
|
|
300
309
|
}
|
|
301
310
|
}
|
|
@@ -304,25 +313,30 @@ export class MidnightApi {
|
|
|
304
313
|
for (const take of takeableOffers) {
|
|
305
314
|
if (remainingAssets === 0n)
|
|
306
315
|
break;
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
316
|
+
const { buyerPrice, sellerPrice } = TakeAmountsLib.prices({
|
|
317
|
+
offer: take.offer,
|
|
318
|
+
settlementFee,
|
|
319
|
+
});
|
|
320
|
+
const makerPrice = input.side === "asks" ? sellerPrice : buyerPrice;
|
|
321
|
+
const settlementPrice = input.side === "asks" ? buyerPrice : sellerPrice;
|
|
322
|
+
const takeMakerAssets = input.side === "asks"
|
|
323
|
+
? MathLib.mulDivUp(take.units, makerPrice, MathLib.WAD)
|
|
324
|
+
: MathLib.mulDivDown(take.units, makerPrice, MathLib.WAD);
|
|
325
|
+
const fillsEntireTake = takeMakerAssets <= remainingAssets;
|
|
314
326
|
const filled = fillsEntireTake
|
|
315
327
|
? take.units
|
|
316
|
-
: MathLib.mulDiv(remainingAssets, MathLib.WAD,
|
|
328
|
+
: MathLib.mulDiv(remainingAssets, MathLib.WAD, makerPrice, input.side === "asks" ? "Down" : "Up");
|
|
317
329
|
if (filled === 0n)
|
|
318
330
|
continue;
|
|
319
331
|
filledUnits += filled;
|
|
320
|
-
|
|
321
|
-
remainingAssets = fillsEntireTake
|
|
332
|
+
filledAssets += MathLib.mulDiv(filled, settlementPrice, MathLib.WAD, input.side === "asks" ? "Up" : "Down");
|
|
333
|
+
remainingAssets = fillsEntireTake
|
|
334
|
+
? remainingAssets - takeMakerAssets
|
|
335
|
+
: 0n;
|
|
322
336
|
}
|
|
323
337
|
}
|
|
324
338
|
if (filledUnits > 0n) {
|
|
325
|
-
const averagePrice = MathLib.
|
|
339
|
+
const averagePrice = MathLib.mulDiv(filledAssets, MathLib.WAD, filledUnits, input.side === "asks" ? "Up" : "Down");
|
|
326
340
|
const violatesGuard = input.side === "asks" ? averagePrice > guard : averagePrice < guard;
|
|
327
341
|
if (violatesGuard) {
|
|
328
342
|
throw new InvalidMidnightApiResponseError(`Midnight API quote takeable offers imply average price "${averagePrice}" outside average_worst_price guard "${guard}".`);
|
|
@@ -632,9 +646,12 @@ export class MidnightApi {
|
|
|
632
646
|
* @param params.units - Optional target unit amount. Mutually exclusive with `params.assets`.
|
|
633
647
|
* @param params.averageWorstPrice - Optional WAD-scaled average worst price guard. Mutually exclusive with `params.slippage`.
|
|
634
648
|
* @param params.slippage - Optional slippage percentage used to derive the guard. Mutually exclusive with `params.averageWorstPrice`.
|
|
649
|
+
* @param params.settlementFee - Optional current WAD-scaled settlement fee used for local guard validation. Defaults to zero.
|
|
635
650
|
* @returns Quote and signed ABI-ready take caps mapped from the API response.
|
|
651
|
+
* @throws {NegativeValueError} when `settlementFee` is negative.
|
|
636
652
|
* @throws {MidnightApiError} when the API returns a non-2xx response.
|
|
637
|
-
* @throws {InvalidMidnightApiResponseError} when the API success response
|
|
653
|
+
* @throws {InvalidMidnightApiResponseError} when the API returns a malformed success response or the returned offers imply a rounded aggregate settlement price outside the effective average-worst-price guard.
|
|
654
|
+
* @throws {SettlementFeeExceedsPriceError} when a bid's settlement fee exceeds its offer price.
|
|
638
655
|
* @example
|
|
639
656
|
* ```ts
|
|
640
657
|
* import { MidnightApi } from "@morpho-org/midnight-sdk/api";
|
package/lib/esm/api/types.d.ts
CHANGED
|
@@ -177,6 +177,8 @@ export type FetchBookQuoteParams = MidnightApiConfig & {
|
|
|
177
177
|
readonly marketId: Hash;
|
|
178
178
|
/** Book side to quote. */
|
|
179
179
|
readonly side: MidnightApiBookSide;
|
|
180
|
+
/** Current WAD-scaled settlement fee used for local guard validation. Defaults to zero. */
|
|
181
|
+
readonly settlementFee?: BigIntish;
|
|
180
182
|
} & (MidnightApiQuoteAssetsTarget | MidnightApiQuoteUnitsTarget) & (MidnightApiQuoteAverageWorstPriceGuard | MidnightApiQuoteSlippageGuard | MidnightApiQuoteWithoutGuard);
|
|
181
183
|
/**
|
|
182
184
|
* Parameters for {@link MidnightApi.fetchTakeableOffers}.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal Installed Midnight SDK package version. */
|
|
2
|
-
export declare const MIDNIGHT_SDK_VERSION = "1.
|
|
2
|
+
export declare const MIDNIGHT_SDK_VERSION = "1.4.0";
|
|
@@ -865,23 +865,20 @@ export var OfferUtils;
|
|
|
865
865
|
return 0n;
|
|
866
866
|
if (continuousFeeCap < BigInt(market.continuousFee))
|
|
867
867
|
return 0n;
|
|
868
|
+
const settlementFee = market.getSettlementFee(market.timeToMaturity(timestamp));
|
|
869
|
+
// Validates the settlement fee against the offer price for both cap modes.
|
|
870
|
+
const { sellerPrice, buyerPrice } = TakeAmountsLib.prices({
|
|
871
|
+
offer,
|
|
872
|
+
settlementFee,
|
|
873
|
+
});
|
|
868
874
|
if (maxUnits > 0n)
|
|
869
875
|
return MathLib.zeroFloorSub(maxUnits, consumed);
|
|
870
876
|
const remainingAssets = MathLib.zeroFloorSub(maxAssets, consumed);
|
|
871
|
-
const settlementFee = market.getSettlementFee(market.timeToMaturity(timestamp));
|
|
872
877
|
if (!offer.buy) {
|
|
873
|
-
const { sellerPrice } = TakeAmountsLib.prices({
|
|
874
|
-
offer,
|
|
875
|
-
settlementFee,
|
|
876
|
-
});
|
|
877
878
|
if (sellerPrice === 0n)
|
|
878
879
|
return MathLib.MAX_UINT_256;
|
|
879
880
|
return MathLib.mulDivDown(remainingAssets, MathLib.WAD, sellerPrice);
|
|
880
881
|
}
|
|
881
|
-
const { buyerPrice } = TakeAmountsLib.prices({
|
|
882
|
-
offer,
|
|
883
|
-
settlementFee,
|
|
884
|
-
});
|
|
885
882
|
if (buyerPrice === 0n)
|
|
886
883
|
return MathLib.MAX_UINT_256;
|
|
887
884
|
return ((remainingAssets + 1n) * MathLib.WAD - 1n) / buyerPrice;
|
|
@@ -52,4 +52,4 @@ export declare const abi: readonly [{
|
|
|
52
52
|
readonly type: "function";
|
|
53
53
|
}];
|
|
54
54
|
/** @internal Deployless `GetPosition` query bytecode. */
|
|
55
|
-
export declare const code = "
|
|
55
|
+
export declare const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea26469706673582212200eb5ee2c25180818e6f54eaf74e6f626bff5ff8c68ec38bf86f24bc97a4fe77264736f6c63430008240033";
|
|
@@ -68,4 +68,4 @@ export const abi = [
|
|
|
68
68
|
},
|
|
69
69
|
];
|
|
70
70
|
/** @internal Deployless `GetPosition` query bytecode. */
|
|
71
|
-
export const code = "
|
|
71
|
+
export const code = "0x60808060405234601557610362908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b346101b85760603660031901126101b8576004356001600160a01b03811691908290036101b857602435906044356001600160a01b038116908190036101b85760e0820182811067ffffffffffffffff8211176102e2576040525f825260208201925f845260408301925f8452606081015f8152608082015f815260a08301915f835260c0840196611000604051906100be81836102f6565b36823788526040516349e2903160e11b8152600481018790526024810188905260c0816044818e5afa9081156101c4575f905f925f91885f925f925f91610243575b506001600160801b039081169091529081168852908116865290811684529182168b521685525f5b60808110156101cf5760405163ecdcc72d60e01b8152600481018890526024810189905260448101829052906020826064818f5afa80156101c4575f90610187575b600192506001600160801b038b519116908260051b015201610128565b506020823d82116101bc575b816101a0602093836102f6565b810103126101b8576101b3600192610318565b61016a565b5f80fd5b3d9150610193565b6040513d5f823e3d90fd5b506001600160801b039283899593818094818e816040519c51168c52511660208b0152511660408901525116606087015251166080850152511660a08301525160c082015f905b60808210610224576110c084f35b6020806001926001600160801b03865116815201930191019091610216565b965050505050505060c0813d60c0116102da575b8161026460c093836102f6565b810103126101b857806001600160801b0361027f8193610318565b918161028d60208301610318565b938161029b60408501610318565b93816102a960608301610318565b938d826102c460a06102bd60808801610318565b9601610318565b929a989694929550509497509450509396610100565b3d9150610257565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176102e257604052565b51906001600160801b03821682036101b85756fea26469706673582212200eb5ee2c25180818e6f54eaf74e6f626bff5ff8c68ec38bf86f24bc97a4fe77264736f6c63430008240033";
|
|
@@ -814,18 +814,13 @@ export var EcrecoverRatifierUtils;
|
|
|
814
814
|
account: params.account,
|
|
815
815
|
}));
|
|
816
816
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
signature,
|
|
825
|
-
}),
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
return items;
|
|
817
|
+
return tree.offers.map((offer, leafIndex) => ({
|
|
818
|
+
offer,
|
|
819
|
+
ratifierData: encodeRatifierData({
|
|
820
|
+
signature,
|
|
821
|
+
...TreeUtils.buildProof({ tree, leafIndex }),
|
|
822
|
+
}),
|
|
823
|
+
}));
|
|
829
824
|
}
|
|
830
825
|
EcrecoverRatifierUtils.ratify = ratify;
|
|
831
826
|
})(EcrecoverRatifierUtils || (EcrecoverRatifierUtils = {}));
|
|
@@ -76,8 +76,8 @@ export declare namespace RatifierUtils {
|
|
|
76
76
|
* @param params.tree - Tree-like object or raw offer/group input.
|
|
77
77
|
* @param params.label - Ratifier label used in validation errors.
|
|
78
78
|
* @returns Normalized tree-like data and its shared ratifier.
|
|
79
|
-
* @throws {InvalidTreeError} when the tree is empty or contains multiple ratifiers.
|
|
80
|
-
* @throws {InvalidTreeHeightError} when
|
|
79
|
+
* @throws {InvalidTreeError} when the tree fields disagree, the tree is empty, or it contains multiple ratifiers.
|
|
80
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
81
81
|
* @example
|
|
82
82
|
* ```ts
|
|
83
83
|
* import { Offer, RatifierUtils } from "@morpho-org/midnight-sdk";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { InvalidTreeError } from "../errors.js";
|
|
2
|
-
import { Offer } from "../offers/index.js";
|
|
1
|
+
import { InvalidTreeError, InvalidTreeHeightError } from "../errors.js";
|
|
2
|
+
import { Offer, OfferUtils } from "../offers/index.js";
|
|
3
3
|
import { Group } from "./Group.js";
|
|
4
4
|
import { GroupUtils } from "./GroupUtils.js";
|
|
5
|
+
import { isEmptyOfferStruct } from "./offerStructInternal.js";
|
|
5
6
|
import { TreeUtils } from "./TreeUtils.js";
|
|
6
7
|
function isTreeLike(tree) {
|
|
7
8
|
return (!Array.isArray(tree) &&
|
|
@@ -13,6 +14,49 @@ function isTreeLike(tree) {
|
|
|
13
14
|
}
|
|
14
15
|
function normalizeTree(tree) {
|
|
15
16
|
if (isTreeLike(tree)) {
|
|
17
|
+
if (!Number.isInteger(tree.height) || tree.height < 0 || tree.height > 20) {
|
|
18
|
+
throw new InvalidTreeHeightError(tree.height);
|
|
19
|
+
}
|
|
20
|
+
const expectedLength = 2 ** tree.height;
|
|
21
|
+
if (tree.paddedOffers.length !== expectedLength ||
|
|
22
|
+
tree.leaves.length !== expectedLength ||
|
|
23
|
+
tree.offers.length > expectedLength) {
|
|
24
|
+
throw new InvalidTreeError("Tree offers, leaves, and height describe different trees.");
|
|
25
|
+
}
|
|
26
|
+
const computedLeaves = tree.paddedOffers.map(OfferUtils.hashStruct);
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
for (const [index, offer] of tree.offers.entries()) {
|
|
29
|
+
if (isEmptyOfferStruct(tree.paddedOffers[index], {
|
|
30
|
+
allowDefaultGroup: true,
|
|
31
|
+
})) {
|
|
32
|
+
throw new InvalidTreeError("Visible offers must not contain tree padding.");
|
|
33
|
+
}
|
|
34
|
+
const hash = OfferUtils.hashStruct(OfferUtils.toStruct({ offer }));
|
|
35
|
+
if (hash.toLowerCase() !== computedLeaves[index].toLowerCase() ||
|
|
36
|
+
seen.has(hash.toLowerCase())) {
|
|
37
|
+
throw new InvalidTreeError("Visible offers do not match the tree leaves.");
|
|
38
|
+
}
|
|
39
|
+
seen.add(hash.toLowerCase());
|
|
40
|
+
}
|
|
41
|
+
if (tree.paddedOffers
|
|
42
|
+
.slice(tree.offers.length)
|
|
43
|
+
.some((offer) => !isEmptyOfferStruct(offer, { allowDefaultGroup: true }))) {
|
|
44
|
+
throw new InvalidTreeError("Tree padding contains offers hidden from the visible offer list.");
|
|
45
|
+
}
|
|
46
|
+
if (computedLeaves.some((leaf, index) => leaf.toLowerCase() !== tree.leaves[index].toLowerCase())) {
|
|
47
|
+
throw new InvalidTreeError("Tree leaves do not match its padded offers.");
|
|
48
|
+
}
|
|
49
|
+
let level = computedLeaves;
|
|
50
|
+
while (level.length > 1) {
|
|
51
|
+
const next = [];
|
|
52
|
+
for (let index = 0; index < level.length; index += 2) {
|
|
53
|
+
next.push(TreeUtils.hashNode(level[index], level[index + 1]));
|
|
54
|
+
}
|
|
55
|
+
level = next;
|
|
56
|
+
}
|
|
57
|
+
if (level[0]?.toLowerCase() !== tree.root.toLowerCase()) {
|
|
58
|
+
throw new InvalidTreeError("Tree root does not match its leaves.");
|
|
59
|
+
}
|
|
16
60
|
return tree;
|
|
17
61
|
}
|
|
18
62
|
const entries = Array.isArray(tree) ? tree : [tree];
|
|
@@ -74,8 +118,8 @@ export var RatifierUtils;
|
|
|
74
118
|
* @param params.tree - Tree-like object or raw offer/group input.
|
|
75
119
|
* @param params.label - Ratifier label used in validation errors.
|
|
76
120
|
* @returns Normalized tree-like data and its shared ratifier.
|
|
77
|
-
* @throws {InvalidTreeError} when the tree is empty or contains multiple ratifiers.
|
|
78
|
-
* @throws {InvalidTreeHeightError} when
|
|
121
|
+
* @throws {InvalidTreeError} when the tree fields disagree, the tree is empty, or it contains multiple ratifiers.
|
|
122
|
+
* @throws {InvalidTreeHeightError} when the tree height is unsupported.
|
|
79
123
|
* @example
|
|
80
124
|
* ```ts
|
|
81
125
|
* import { Offer, RatifierUtils } from "@morpho-org/midnight-sdk";
|
|
@@ -241,17 +241,10 @@ export var SetterRatifierUtils;
|
|
|
241
241
|
tree: params.tree,
|
|
242
242
|
label: "Setter",
|
|
243
243
|
});
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
ratifierData: ratifierData({
|
|
249
|
-
tree,
|
|
250
|
-
leafIndex: items.length,
|
|
251
|
-
}),
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
return items;
|
|
244
|
+
return tree.offers.map((offer, leafIndex) => ({
|
|
245
|
+
offer,
|
|
246
|
+
ratifierData: encodeRatifierData(TreeUtils.buildProof({ tree, leafIndex })),
|
|
247
|
+
}));
|
|
255
248
|
}
|
|
256
249
|
SetterRatifierUtils.ratify = ratify;
|
|
257
250
|
})(SetterRatifierUtils || (SetterRatifierUtils = {}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { zeroAddress, zeroHash } from "viem";
|
|
1
|
+
import { keccak256, zeroAddress, zeroHash } from "viem";
|
|
2
2
|
import { OfferUtils } from "../offers/index.js";
|
|
3
3
|
/** @internal Empty protocol offer struct used for Merkle padding. */
|
|
4
4
|
export const EMPTY_OFFER_STRUCT = {
|
|
@@ -27,7 +27,7 @@ export const EMPTY_OFFER_STRUCT = {
|
|
|
27
27
|
maxAssets: 0n,
|
|
28
28
|
continuousFeeCap: 0n,
|
|
29
29
|
};
|
|
30
|
-
const EMPTY_OFFER_DEFAULT_GROUP = OfferUtils.hashStruct(EMPTY_OFFER_STRUCT);
|
|
30
|
+
const EMPTY_OFFER_DEFAULT_GROUP = keccak256(OfferUtils.hashStruct(EMPTY_OFFER_STRUCT));
|
|
31
31
|
/** @internal Returns whether an address value is the zero address. */
|
|
32
32
|
export function isZeroAddress(value) {
|
|
33
33
|
return value.toLowerCase() === zeroAddress;
|
|
@@ -39,9 +39,9 @@ export function isZeroAddress(value) {
|
|
|
39
39
|
* decoding only treats the canonical zero-group empty struct as padding.
|
|
40
40
|
*/
|
|
41
41
|
export function isEmptyOfferStruct(offer, options = {}) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const group = offer.group.toLowerCase();
|
|
43
|
+
const hasEmptyGroup = group === zeroHash ||
|
|
44
|
+
(options.allowDefaultGroup === true && group === EMPTY_OFFER_DEFAULT_GROUP);
|
|
45
45
|
return (offer.market.chainId === 0n &&
|
|
46
46
|
isZeroAddress(offer.market.midnight) &&
|
|
47
47
|
isZeroAddress(offer.market.loanToken) &&
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morpho-org/midnight-sdk",
|
|
3
3
|
"description": "Viem-based package for Morpho Midnight that exports protocol utilities, fetch helpers, and Midnight API utilities under a dedicated API subpath.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"author": "Morpho Association <contact@morpho.org>",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Rubilmax <rmilon@gmail.com>"
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"fast-check": "^4.9.0",
|
|
51
|
-
"typescript": "
|
|
51
|
+
"typescript": "7.0.2",
|
|
52
52
|
"viem": "^2.55.10",
|
|
53
53
|
"vitest": "^4.1.10",
|
|
54
|
-
"@morpho-org/morpho-ts": "^2.
|
|
54
|
+
"@morpho-org/morpho-ts": "^2.12.0",
|
|
55
55
|
"@morpho-org/test": "^2.10.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
|
-
"prepublish": "
|
|
58
|
+
"prepublish": "pnpm build",
|
|
59
59
|
"compile": "node ../../scripts/compile-solidity.js midnight-sdk",
|
|
60
|
-
"build": "
|
|
60
|
+
"build": "pnpm generate:version && tsc --noEmit && pnpm build:cjs && pnpm build:esm",
|
|
61
61
|
"build:cjs": "tsc --build tsconfig.build.cjs.json && echo '{\"type\":\"commonjs\"}' > lib/cjs/package.json",
|
|
62
62
|
"build:esm": "tsc --build tsconfig.build.esm.json && echo '{\"type\":\"module\"}' > lib/esm/package.json",
|
|
63
63
|
"generate:version": "node ../../scripts/release/generate-midnight-package-version.mjs",
|