@morpho-dev/router 0.2.1 → 0.3.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/dist/cli.js +1200 -559
- package/dist/drizzle/migrations/0015_add-lots-table.sql +12 -0
- package/dist/drizzle/migrations/0016_merkle-metadata.sql +26 -0
- package/dist/drizzle/migrations/meta/0015_snapshot.json +1365 -0
- package/dist/drizzle/migrations/meta/0016_snapshot.json +1531 -0
- package/dist/drizzle/migrations/meta/_journal.json +14 -0
- package/dist/index.browser.d.mts +443 -229
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +439 -229
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +533 -202
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +534 -203
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +737 -67
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +737 -67
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +1240 -513
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +1241 -514
- package/dist/index.node.mjs.map +1 -1
- package/package.json +1 -2
package/dist/index.browser.js
CHANGED
|
@@ -41,7 +41,6 @@ let openapi_fetch = require("openapi-fetch");
|
|
|
41
41
|
openapi_fetch = __toESM(openapi_fetch);
|
|
42
42
|
let viem_actions = require("viem/actions");
|
|
43
43
|
let viem_chains = require("viem/chains");
|
|
44
|
-
let viem_accounts = require("viem/accounts");
|
|
45
44
|
let __openzeppelin_merkle_tree = require("@openzeppelin/merkle-tree");
|
|
46
45
|
let pako = require("pako");
|
|
47
46
|
|
|
@@ -149,13 +148,16 @@ var OfferResponse_exports = /* @__PURE__ */ __export({ from: () => from$11 });
|
|
|
149
148
|
* Creates an `OfferResponse` from an `Offer`.
|
|
150
149
|
* @constructor
|
|
151
150
|
* @param offer - {@link Offer}
|
|
151
|
+
* @param attestation - {@link Attestation}
|
|
152
152
|
* @returns The created `OfferResponse`. {@link OfferResponse}
|
|
153
153
|
*/
|
|
154
|
-
function from$11(offer) {
|
|
155
|
-
const
|
|
154
|
+
function from$11(offer, attestation) {
|
|
155
|
+
const { signature: _, ...rest } = toSnakeCase$1(offer);
|
|
156
156
|
return {
|
|
157
|
-
...
|
|
158
|
-
|
|
157
|
+
...rest,
|
|
158
|
+
root: attestation?.root.toLowerCase() ?? null,
|
|
159
|
+
proof: attestation?.proof.map((p) => p.toLowerCase()) ?? null,
|
|
160
|
+
signature: attestation?.signature.toLowerCase() ?? null
|
|
159
161
|
};
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -204,10 +206,12 @@ const offerExample = {
|
|
|
204
206
|
data: "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000034cf890db685fc536e05652fb41f02090c3fb751000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000108e644e3ab01184155270aa92a00000000000",
|
|
205
207
|
gas_limit: "500000"
|
|
206
208
|
},
|
|
207
|
-
signature: "0x1234567890123456789012345678901234567890123456789012345678901234123456789012345678901234567890123456789012345678901234567890123400",
|
|
208
209
|
consumed: "0",
|
|
209
210
|
takeable: "369216000000000000000000",
|
|
210
|
-
block_number: 0xa7495128adfb1
|
|
211
|
+
block_number: 0xa7495128adfb1,
|
|
212
|
+
root: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
|
213
|
+
proof: ["0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", "0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba"],
|
|
214
|
+
signature: "0x1234567890123456789012345678901234567890123456789012345678901234123456789012345678901234567890123456789012345678901234567890123400"
|
|
211
215
|
};
|
|
212
216
|
const collectorsHealthExample = {
|
|
213
217
|
name: "offers",
|
|
@@ -352,6 +356,16 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
|
352
356
|
type: "number",
|
|
353
357
|
example: offerExample.block_number
|
|
354
358
|
})], OfferListItemResponse.prototype, "block_number", void 0);
|
|
359
|
+
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
360
|
+
type: "string",
|
|
361
|
+
nullable: true,
|
|
362
|
+
example: offerExample.root
|
|
363
|
+
})], OfferListItemResponse.prototype, "root", void 0);
|
|
364
|
+
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
365
|
+
type: [String],
|
|
366
|
+
nullable: true,
|
|
367
|
+
example: offerExample.proof
|
|
368
|
+
})], OfferListItemResponse.prototype, "proof", void 0);
|
|
355
369
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
356
370
|
type: "string",
|
|
357
371
|
nullable: true,
|
|
@@ -535,44 +549,61 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
|
535
549
|
var ValidateOffersRequest = class {};
|
|
536
550
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
537
551
|
type: () => [ValidateOfferRequest],
|
|
538
|
-
description: "Array of offers in snake_case format.
|
|
539
|
-
required:
|
|
552
|
+
description: "Array of offers in snake_case format. Required, non-empty.",
|
|
553
|
+
required: true
|
|
540
554
|
})], ValidateOffersRequest.prototype, "offers", void 0);
|
|
555
|
+
var ValidationSuccessDataResponse = class {};
|
|
541
556
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
542
557
|
type: "string",
|
|
543
|
-
description: "
|
|
544
|
-
example: "
|
|
545
|
-
|
|
546
|
-
})], ValidateOffersRequest.prototype, "calldata", void 0);
|
|
547
|
-
var ValidateOfferResultResponse = class {};
|
|
558
|
+
description: "Unsigned payload: version (1B) + gzip(offers) + root (32B).",
|
|
559
|
+
example: "0x01789c..."
|
|
560
|
+
})], ValidationSuccessDataResponse.prototype, "payload", void 0);
|
|
548
561
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
549
562
|
type: "string",
|
|
550
|
-
|
|
551
|
-
|
|
563
|
+
description: "Merkle tree root to sign with EIP-191.",
|
|
564
|
+
example: "0xac4bd8318ec914f89f8af913f162230575b0ac0696a19256bc12138c5cfe1427"
|
|
565
|
+
})], ValidationSuccessDataResponse.prototype, "root", void 0);
|
|
566
|
+
var ValidationSuccessResponse = class extends SuccessResponse {};
|
|
552
567
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
553
|
-
type: "
|
|
554
|
-
|
|
555
|
-
|
|
568
|
+
type: "string",
|
|
569
|
+
nullable: true,
|
|
570
|
+
example: null
|
|
571
|
+
})], ValidationSuccessResponse.prototype, "cursor", void 0);
|
|
572
|
+
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
573
|
+
type: () => ValidationSuccessDataResponse,
|
|
574
|
+
description: "Payload and root for client-side signing."
|
|
575
|
+
})], ValidationSuccessResponse.prototype, "data", void 0);
|
|
576
|
+
var ValidationIssueResponse = class {};
|
|
577
|
+
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
578
|
+
type: "number",
|
|
579
|
+
description: "0-indexed position of the failed offer in the request array.",
|
|
580
|
+
example: 0
|
|
581
|
+
})], ValidationIssueResponse.prototype, "index", void 0);
|
|
556
582
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
557
583
|
type: "string",
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
})],
|
|
584
|
+
description: "Gatekeeper rule name that rejected the offer.",
|
|
585
|
+
example: "no_buy"
|
|
586
|
+
})], ValidationIssueResponse.prototype, "rule", void 0);
|
|
561
587
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
562
588
|
type: "string",
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
})],
|
|
566
|
-
var
|
|
589
|
+
description: "Human-readable rejection reason.",
|
|
590
|
+
example: "Buy offers are not supported"
|
|
591
|
+
})], ValidationIssueResponse.prototype, "message", void 0);
|
|
592
|
+
var ValidationFailureDataResponse = class {};
|
|
593
|
+
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
594
|
+
type: () => [ValidationIssueResponse],
|
|
595
|
+
description: "List of validation issues. Returned when any offer fails validation."
|
|
596
|
+
})], ValidationFailureDataResponse.prototype, "issues", void 0);
|
|
597
|
+
var ValidationFailureResponse = class extends SuccessResponse {};
|
|
567
598
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
568
599
|
type: "string",
|
|
569
600
|
nullable: true,
|
|
570
601
|
example: null
|
|
571
|
-
})],
|
|
602
|
+
})], ValidationFailureResponse.prototype, "cursor", void 0);
|
|
572
603
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
573
|
-
type: () =>
|
|
574
|
-
description: "
|
|
575
|
-
})],
|
|
604
|
+
type: () => ValidationFailureDataResponse,
|
|
605
|
+
description: "List of validation issues. Returned when any offer fails validation."
|
|
606
|
+
})], ValidationFailureResponse.prototype, "data", void 0);
|
|
576
607
|
var BookLevelResponse = class {};
|
|
577
608
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
578
609
|
type: "string",
|
|
@@ -637,13 +668,18 @@ __decorate([
|
|
|
637
668
|
methods: ["post"],
|
|
638
669
|
path: "/v1/validate",
|
|
639
670
|
summary: "Validate offers",
|
|
640
|
-
description: "Validates offers against router validation rules. Returns
|
|
671
|
+
description: "Validates offers against router validation rules. Returns unsigned payload + root on success, or issues only on validation failure."
|
|
641
672
|
}),
|
|
642
673
|
(0, openapi_metadata_decorators.ApiBody)({ type: ValidateOffersRequest }),
|
|
643
674
|
(0, openapi_metadata_decorators.ApiResponse)({
|
|
644
675
|
status: 200,
|
|
645
676
|
description: "Success",
|
|
646
|
-
type:
|
|
677
|
+
type: ValidationSuccessResponse
|
|
678
|
+
}),
|
|
679
|
+
(0, openapi_metadata_decorators.ApiResponse)({
|
|
680
|
+
status: 200,
|
|
681
|
+
description: "Validation issues",
|
|
682
|
+
type: ValidationFailureResponse
|
|
647
683
|
})
|
|
648
684
|
], ValidateController.prototype, "validateOffers", null);
|
|
649
685
|
ValidateController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Validate"), (0, openapi_metadata_decorators.ApiResponse)({
|
|
@@ -812,7 +848,7 @@ const OpenApi = async (options = {}) => {
|
|
|
812
848
|
if (options.rules && options.rules.length > 0) {
|
|
813
849
|
const rulesDescription = options.rules.map((rule) => `- **${rule.name}**: ${rule.description}`).join("\n");
|
|
814
850
|
const validatePath = document.paths?.["/v1/validate"];
|
|
815
|
-
if (validatePath && "post" in validatePath && validatePath.post) validatePath.post.description = `Validates offers against router validation rules. Returns
|
|
851
|
+
if (validatePath && "post" in validatePath && validatePath.post) validatePath.post.description = `Validates offers against router validation rules. Returns unsigned payload + root on success, or issues only on validation failure.\n\n**Available validation rules:**\n${rulesDescription}`;
|
|
816
852
|
}
|
|
817
853
|
return document;
|
|
818
854
|
};
|
|
@@ -824,17 +860,23 @@ var Cursor_exports = /* @__PURE__ */ __export({
|
|
|
824
860
|
encode: () => encode$3,
|
|
825
861
|
validate: () => validate
|
|
826
862
|
});
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const c = cursor;
|
|
830
|
-
if (![
|
|
863
|
+
const isSort = (value) => {
|
|
864
|
+
return [
|
|
831
865
|
"rate",
|
|
832
866
|
"maturity",
|
|
833
867
|
"expiry",
|
|
834
868
|
"amount"
|
|
835
|
-
].includes(
|
|
836
|
-
|
|
837
|
-
|
|
869
|
+
].includes(value);
|
|
870
|
+
};
|
|
871
|
+
function validate(cursor) {
|
|
872
|
+
if (!cursor || typeof cursor !== "object") throw new Error("Cursor must be an object");
|
|
873
|
+
const c = cursor;
|
|
874
|
+
const sort = c.sort;
|
|
875
|
+
const dir = c.dir;
|
|
876
|
+
const hash$1 = c.hash;
|
|
877
|
+
if (typeof sort !== "string" || !isSort(sort)) throw new Error(`Invalid sort field: ${String(sort)}. Must be one of: rate, maturity, expiry, amount`);
|
|
878
|
+
if (typeof dir !== "string" || !["asc", "desc"].includes(dir)) throw new Error(`Invalid direction: ${String(dir)}. Must be one of: asc, desc`);
|
|
879
|
+
if (typeof hash$1 !== "string" || !/^0x[a-fA-F0-9]{64}$/.test(hash$1)) throw new Error(`Invalid hash format: ${String(hash$1)}. Must be a 64-character hex string starting with 0x`);
|
|
838
880
|
const validation = {
|
|
839
881
|
rate: {
|
|
840
882
|
field: "rate",
|
|
@@ -851,24 +893,30 @@ function validate(cursor) {
|
|
|
851
893
|
maturity: {
|
|
852
894
|
field: "maturity",
|
|
853
895
|
type: "number",
|
|
854
|
-
|
|
896
|
+
min: 1,
|
|
855
897
|
error: "positive number"
|
|
856
898
|
},
|
|
857
899
|
expiry: {
|
|
858
900
|
field: "expiry",
|
|
859
901
|
type: "number",
|
|
860
|
-
|
|
902
|
+
min: 1,
|
|
861
903
|
error: "positive number"
|
|
862
904
|
}
|
|
863
|
-
}[
|
|
864
|
-
if (!validation) throw new Error(`Invalid sort field: ${
|
|
905
|
+
}[sort];
|
|
906
|
+
if (!validation) throw new Error(`Invalid sort field: ${sort}`);
|
|
865
907
|
const fieldValue = c[validation.field];
|
|
866
|
-
if (
|
|
867
|
-
if (
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
908
|
+
if (fieldValue === void 0 || fieldValue === null) throw new Error(`${sort} sort requires '${validation.field}' field to be present`);
|
|
909
|
+
if (validation.type === "string") {
|
|
910
|
+
if (typeof fieldValue !== "string") throw new Error(`${sort} sort requires '${validation.field}' field of type ${validation.type}`);
|
|
911
|
+
if (!validation.pattern.test(fieldValue)) throw new Error(`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`);
|
|
912
|
+
}
|
|
913
|
+
if (validation.type === "number") {
|
|
914
|
+
if (typeof fieldValue !== "number") throw new Error(`${sort} sort requires '${validation.field}' field of type ${validation.type}`);
|
|
915
|
+
if (fieldValue < validation.min) throw new Error(`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`);
|
|
916
|
+
}
|
|
917
|
+
const page = c.page;
|
|
918
|
+
if (page !== void 0) {
|
|
919
|
+
if (typeof page !== "number" || !Number.isInteger(page) || page < 1) throw new Error("Invalid page: must be a positive integer");
|
|
872
920
|
}
|
|
873
921
|
return true;
|
|
874
922
|
}
|
|
@@ -984,21 +1032,7 @@ const schemas = {
|
|
|
984
1032
|
get_obligations: GetObligationsQueryParams,
|
|
985
1033
|
get_obligation: GetObligationParams,
|
|
986
1034
|
get_book: GetBookParams,
|
|
987
|
-
validate_offers: zod.object({
|
|
988
|
-
offers: zod.any().refine((val) => val === void 0 || Array.isArray(val), { message: "'offers' must be an array" }),
|
|
989
|
-
calldata: zod.string().regex(/^0x[a-fA-F0-9]*$/, { message: "'calldata' must be a hex string starting with '0x'" }).optional()
|
|
990
|
-
}).superRefine((val, ctx) => {
|
|
991
|
-
const hasOffers = val.offers !== void 0;
|
|
992
|
-
const hasCalldata = val.calldata !== void 0;
|
|
993
|
-
if (hasOffers && hasCalldata) ctx.addIssue({
|
|
994
|
-
code: "custom",
|
|
995
|
-
message: "Request body must contain either 'offers' or 'calldata', not both"
|
|
996
|
-
});
|
|
997
|
-
if (!hasOffers && !hasCalldata) ctx.addIssue({
|
|
998
|
-
code: "custom",
|
|
999
|
-
message: "Request body must contain either 'offers' array or 'calldata' hex string"
|
|
1000
|
-
});
|
|
1001
|
-
})
|
|
1035
|
+
validate_offers: zod.object({ offers: zod.array(zod.unknown()).min(1, { message: "'offers' must contain at least 1 offer" }) }).strict()
|
|
1002
1036
|
};
|
|
1003
1037
|
function parse(action, query) {
|
|
1004
1038
|
return schemas[action].parse(query);
|
|
@@ -1225,8 +1259,12 @@ function decode$2(type, data) {
|
|
|
1225
1259
|
}
|
|
1226
1260
|
function encode$2(type, data) {
|
|
1227
1261
|
switch (type) {
|
|
1228
|
-
case CallbackType.BuyVaultV1Callback:
|
|
1229
|
-
|
|
1262
|
+
case CallbackType.BuyVaultV1Callback:
|
|
1263
|
+
if (!("vaults" in data)) throw new Error("Invalid callback data");
|
|
1264
|
+
return encodeBuyVaultV1Callback(data);
|
|
1265
|
+
case CallbackType.SellERC20Callback:
|
|
1266
|
+
if (!("collaterals" in data)) throw new Error("Invalid callback data");
|
|
1267
|
+
return encodeSellERC20Callback(data);
|
|
1230
1268
|
default: throw new Error("Invalid callback type");
|
|
1231
1269
|
}
|
|
1232
1270
|
}
|
|
@@ -1554,22 +1592,22 @@ const DEFAULT_BATCH_SIZE$1 = 2500;
|
|
|
1554
1592
|
const MAX_BLOCK_WINDOW = 1e4;
|
|
1555
1593
|
const DEFAULT_BLOCK_WINDOW = 8e3;
|
|
1556
1594
|
async function* streamLogs(parameters) {
|
|
1557
|
-
const { client, contractAddress, event, blockNumberGte, blockNumberLte, order
|
|
1595
|
+
const { client, contractAddress, event, blockNumberGte, blockNumberLte, order = "desc", options: { maxBatchSize = DEFAULT_BATCH_SIZE$1, blockWindow = DEFAULT_BLOCK_WINDOW } = {} } = parameters;
|
|
1558
1596
|
if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
|
|
1559
1597
|
if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
|
|
1560
|
-
if (order
|
|
1598
|
+
if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
|
|
1561
1599
|
const latestBlock = (await (0, viem_actions.getBlock)(client, {
|
|
1562
1600
|
blockTag: "latest",
|
|
1563
1601
|
includeTransactions: false
|
|
1564
1602
|
})).number;
|
|
1565
1603
|
let toBlock = 0n;
|
|
1566
|
-
if (order
|
|
1567
|
-
if (order
|
|
1604
|
+
if (order === "asc") toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), blockNumberLte ? BigInt(blockNumberLte) : latestBlock);
|
|
1605
|
+
if (order === "desc") toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
|
|
1568
1606
|
let fromBlock = 0n;
|
|
1569
|
-
if (order
|
|
1570
|
-
if (order
|
|
1571
|
-
if (order
|
|
1572
|
-
if (order
|
|
1607
|
+
if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
|
|
1608
|
+
if (order === "desc") fromBlock = max$1(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
|
|
1609
|
+
if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
|
|
1610
|
+
if (order === "desc") fromBlock = max$1(fromBlock, toBlock - BigInt(blockWindow));
|
|
1573
1611
|
if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
|
|
1574
1612
|
let streaming = true;
|
|
1575
1613
|
while (streaming) {
|
|
@@ -1579,29 +1617,29 @@ async function* streamLogs(parameters) {
|
|
|
1579
1617
|
fromBlock,
|
|
1580
1618
|
toBlock
|
|
1581
1619
|
});
|
|
1582
|
-
streaming = order
|
|
1620
|
+
streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
|
|
1583
1621
|
if (logs.length === 0 && !streaming) break;
|
|
1584
1622
|
if (logs.length === 0 && streaming) yield {
|
|
1585
1623
|
logs: [],
|
|
1586
|
-
blockNumber: order
|
|
1624
|
+
blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock)
|
|
1587
1625
|
};
|
|
1588
1626
|
logs.sort((a, b) => {
|
|
1589
|
-
if (a.blockNumber !== b.blockNumber) return order
|
|
1590
|
-
if (a.transactionIndex !== b.transactionIndex) return order
|
|
1591
|
-
return order
|
|
1627
|
+
if (a.blockNumber !== b.blockNumber) return order === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
|
|
1628
|
+
if (a.transactionIndex !== b.transactionIndex) return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
|
|
1629
|
+
return order === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
|
|
1592
1630
|
});
|
|
1593
1631
|
for (const logBatch of batch(logs, maxBatchSize)) yield {
|
|
1594
1632
|
logs: logBatch,
|
|
1595
|
-
blockNumber: logBatch.length === maxBatchSize ? Number(logBatch[logBatch.length - 1]?.blockNumber) : order
|
|
1633
|
+
blockNumber: logBatch.length === maxBatchSize ? Number(logBatch[logBatch.length - 1]?.blockNumber) : order === "asc" ? Number(toBlock) : Number(fromBlock)
|
|
1596
1634
|
};
|
|
1597
|
-
if (order
|
|
1635
|
+
if (order === "asc") {
|
|
1598
1636
|
const upperBound = BigInt(blockNumberLte || latestBlock);
|
|
1599
1637
|
const nextFromBlock = min(BigInt(toBlock) + 1n, upperBound);
|
|
1600
1638
|
const nextToBlock = min(toBlock + BigInt(blockWindow) + 1n, upperBound);
|
|
1601
1639
|
fromBlock = nextFromBlock;
|
|
1602
1640
|
toBlock = nextToBlock;
|
|
1603
1641
|
}
|
|
1604
|
-
if (order
|
|
1642
|
+
if (order === "desc") {
|
|
1605
1643
|
const lowerBound = BigInt(blockNumberGte || 0);
|
|
1606
1644
|
const nextToBlock = max$1(fromBlock - 1n, lowerBound);
|
|
1607
1645
|
const nextFromBlock = max$1(fromBlock - BigInt(blockWindow) - 1n, lowerBound);
|
|
@@ -1611,7 +1649,7 @@ async function* streamLogs(parameters) {
|
|
|
1611
1649
|
}
|
|
1612
1650
|
yield {
|
|
1613
1651
|
logs: [],
|
|
1614
|
-
blockNumber: order
|
|
1652
|
+
blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock)
|
|
1615
1653
|
};
|
|
1616
1654
|
}
|
|
1617
1655
|
var InvalidBlockRangeError = class extends BaseError {
|
|
@@ -1639,6 +1677,96 @@ var MissingBlockNumberError = class extends BaseError {
|
|
|
1639
1677
|
}
|
|
1640
1678
|
};
|
|
1641
1679
|
|
|
1680
|
+
//#endregion
|
|
1681
|
+
//#region src/utils/Random.ts
|
|
1682
|
+
var Random_exports = /* @__PURE__ */ __export({
|
|
1683
|
+
address: () => address,
|
|
1684
|
+
bool: () => bool,
|
|
1685
|
+
bytes: () => bytes,
|
|
1686
|
+
float: () => float,
|
|
1687
|
+
hex: () => hex,
|
|
1688
|
+
int: () => int,
|
|
1689
|
+
seed: () => seed,
|
|
1690
|
+
withSeed: () => withSeed
|
|
1691
|
+
});
|
|
1692
|
+
let currentRng = Math.random;
|
|
1693
|
+
const FNV_OFFSET_BASIS = 2166136261;
|
|
1694
|
+
const FNV_PRIME = 16777619;
|
|
1695
|
+
const hashSeed = (seed$1) => {
|
|
1696
|
+
let hash$1 = FNV_OFFSET_BASIS;
|
|
1697
|
+
for (let i = 0; i < seed$1.length; i += 1) {
|
|
1698
|
+
hash$1 ^= seed$1.charCodeAt(i);
|
|
1699
|
+
hash$1 = Math.imul(hash$1, FNV_PRIME);
|
|
1700
|
+
}
|
|
1701
|
+
return hash$1 >>> 0;
|
|
1702
|
+
};
|
|
1703
|
+
const createSeededRng = (seed$1) => {
|
|
1704
|
+
let state = hashSeed(seed$1);
|
|
1705
|
+
return () => {
|
|
1706
|
+
state += 1831565813;
|
|
1707
|
+
let t = Math.imul(state ^ state >>> 15, state | 1);
|
|
1708
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
1709
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1710
|
+
};
|
|
1711
|
+
};
|
|
1712
|
+
/**
|
|
1713
|
+
* Runs a function with a deterministic RNG derived from the given seed.
|
|
1714
|
+
*/
|
|
1715
|
+
function withSeed(seed$1, fn) {
|
|
1716
|
+
const previous = currentRng;
|
|
1717
|
+
currentRng = createSeededRng(seed$1);
|
|
1718
|
+
try {
|
|
1719
|
+
return fn();
|
|
1720
|
+
} finally {
|
|
1721
|
+
currentRng = previous;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Seeds the global RNG for deterministic test runs.
|
|
1726
|
+
*/
|
|
1727
|
+
function seed(seed$1) {
|
|
1728
|
+
currentRng = createSeededRng(seed$1);
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Returns a deterministic random float in [0, 1).
|
|
1732
|
+
*/
|
|
1733
|
+
function float() {
|
|
1734
|
+
return currentRng();
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Returns a deterministic random integer in [min, maxExclusive).
|
|
1738
|
+
*/
|
|
1739
|
+
function int(maxExclusive, min$1 = 0) {
|
|
1740
|
+
return Math.floor(float() * (maxExclusive - min$1)) + min$1;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Returns a deterministic random boolean.
|
|
1744
|
+
*/
|
|
1745
|
+
function bool(probability = .5) {
|
|
1746
|
+
return float() < probability;
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Returns deterministic random bytes.
|
|
1750
|
+
*/
|
|
1751
|
+
function bytes(length) {
|
|
1752
|
+
const output = new Uint8Array(length);
|
|
1753
|
+
for (let i = 0; i < length; i += 1) output[i] = int(256);
|
|
1754
|
+
return output;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Returns a deterministic random hex string for the given byte length.
|
|
1758
|
+
*/
|
|
1759
|
+
function hex(byteLength) {
|
|
1760
|
+
const output = bytes(byteLength);
|
|
1761
|
+
return `0x${Array.from(output, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* Returns a deterministic random address.
|
|
1765
|
+
*/
|
|
1766
|
+
function address() {
|
|
1767
|
+
return hex(20);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1642
1770
|
//#endregion
|
|
1643
1771
|
//#region src/utils/zod.ts
|
|
1644
1772
|
const transformHex = (val, ctx) => {
|
|
@@ -1760,8 +1888,8 @@ const from$9 = (parameters) => {
|
|
|
1760
1888
|
*/
|
|
1761
1889
|
function random$3() {
|
|
1762
1890
|
return from$9({
|
|
1763
|
-
asset: (
|
|
1764
|
-
oracle: (
|
|
1891
|
+
asset: address(),
|
|
1892
|
+
oracle: address(),
|
|
1765
1893
|
lltv: .965
|
|
1766
1894
|
});
|
|
1767
1895
|
}
|
|
@@ -2176,12 +2304,8 @@ function id(obligation) {
|
|
|
2176
2304
|
function random$2() {
|
|
2177
2305
|
return from$7({
|
|
2178
2306
|
chainId: 1,
|
|
2179
|
-
loanToken: (
|
|
2180
|
-
collaterals: [
|
|
2181
|
-
asset: (0, viem_accounts.privateKeyToAccount)((0, viem_accounts.generatePrivateKey)()).address,
|
|
2182
|
-
oracle: (0, viem_accounts.privateKeyToAccount)((0, viem_accounts.generatePrivateKey)()).address,
|
|
2183
|
-
lltv: .965
|
|
2184
|
-
})],
|
|
2307
|
+
loanToken: address(),
|
|
2308
|
+
collaterals: [random$3()],
|
|
2185
2309
|
maturity: from$8("end_of_next_quarter")
|
|
2186
2310
|
});
|
|
2187
2311
|
}
|
|
@@ -2201,101 +2325,249 @@ var CollateralsAreNotSortedError = class extends BaseError {
|
|
|
2201
2325
|
//#endregion
|
|
2202
2326
|
//#region src/core/Tree.ts
|
|
2203
2327
|
var Tree_exports = /* @__PURE__ */ __export({
|
|
2328
|
+
DecodeError: () => DecodeError,
|
|
2329
|
+
EncodeError: () => EncodeError,
|
|
2330
|
+
TreeError: () => TreeError,
|
|
2204
2331
|
VERSION: () => VERSION,
|
|
2205
2332
|
decode: () => decode$1,
|
|
2206
2333
|
encode: () => encode$1,
|
|
2207
|
-
|
|
2334
|
+
encodeUnsigned: () => encodeUnsigned,
|
|
2335
|
+
from: () => from$6,
|
|
2336
|
+
proofs: () => proofs
|
|
2208
2337
|
});
|
|
2209
2338
|
const VERSION = 1;
|
|
2339
|
+
const normalizeHash = (hash$1) => hash$1.toLowerCase();
|
|
2210
2340
|
/**
|
|
2211
2341
|
* Builds a Merkle tree from a list of offers.
|
|
2212
2342
|
*
|
|
2213
2343
|
* Leaves are the offer `hash` values as `bytes32` and are deterministically
|
|
2214
|
-
* ordered
|
|
2215
|
-
* regardless of the input order.
|
|
2344
|
+
* ordered following the StandardMerkleTree leaf ordering so that the resulting
|
|
2345
|
+
* root is stable regardless of the input order.
|
|
2216
2346
|
*
|
|
2217
2347
|
* @param offers - Offers to include in the tree.
|
|
2218
2348
|
* @returns A `StandardMerkleTree` of `bytes32` leaves representing the offers.
|
|
2349
|
+
* @throws {TreeError} If tree building fails due to offer inconsistencies.
|
|
2219
2350
|
*/
|
|
2220
2351
|
const from$6 = (offers) => {
|
|
2221
|
-
const leaves =
|
|
2222
|
-
return [offer.hash];
|
|
2223
|
-
});
|
|
2352
|
+
const leaves = offers.map((offer) => [offer.hash]);
|
|
2224
2353
|
const tree = __openzeppelin_merkle_tree.StandardMerkleTree.of(leaves, ["bytes32"]);
|
|
2225
|
-
|
|
2354
|
+
const orderedOffers = orderOffers(tree, offers);
|
|
2355
|
+
return Object.assign(tree, { offers: orderedOffers });
|
|
2226
2356
|
};
|
|
2227
|
-
const
|
|
2228
|
-
const
|
|
2229
|
-
|
|
2357
|
+
const orderOffers = (tree, offers) => {
|
|
2358
|
+
const offerByHash = /* @__PURE__ */ new Map();
|
|
2359
|
+
for (const offer of offers) offerByHash.set(normalizeHash(offer.hash), offer);
|
|
2360
|
+
const entries = tree.dump().values.map((value) => {
|
|
2361
|
+
const hash$1 = normalizeHash(value.value[0]);
|
|
2362
|
+
const offer = offerByHash.get(hash$1);
|
|
2363
|
+
if (!offer) throw new TreeError(`missing offer for leaf ${hash$1}`);
|
|
2364
|
+
return {
|
|
2365
|
+
offer,
|
|
2366
|
+
treeIndex: value.treeIndex
|
|
2367
|
+
};
|
|
2368
|
+
});
|
|
2369
|
+
entries.sort((a, b) => b.treeIndex - a.treeIndex);
|
|
2370
|
+
return entries.map((item) => item.offer);
|
|
2230
2371
|
};
|
|
2231
2372
|
/**
|
|
2232
|
-
*
|
|
2373
|
+
* Generates merkle proofs for all offers in a tree.
|
|
2233
2374
|
*
|
|
2234
|
-
*
|
|
2235
|
-
*
|
|
2375
|
+
* Each proof allows independent verification that an offer is included in the tree
|
|
2376
|
+
* without requiring the full tree. Proofs are ordered by StandardMerkleTree leaf ordering.
|
|
2236
2377
|
*
|
|
2237
|
-
* @param tree - The
|
|
2238
|
-
* @returns
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2378
|
+
* @param tree - The {@link Tree} to generate proofs for.
|
|
2379
|
+
* @returns Array of proofs - {@link Proof}
|
|
2380
|
+
*/
|
|
2381
|
+
const proofs = (tree) => {
|
|
2382
|
+
return tree.offers.map((offer) => {
|
|
2383
|
+
return {
|
|
2384
|
+
offer,
|
|
2385
|
+
path: tree.getProof([offer.hash])
|
|
2386
|
+
};
|
|
2387
|
+
});
|
|
2388
|
+
};
|
|
2389
|
+
const assertHex = (value, expectedBytes, name) => {
|
|
2390
|
+
if (typeof value !== "string" || !(0, viem.isHex)(value)) throw new DecodeError(`${name} is not a valid hex string`);
|
|
2391
|
+
if ((0, viem.hexToBytes)(value).length !== expectedBytes) throw new DecodeError(`${name}: expected ${expectedBytes} bytes`);
|
|
2392
|
+
};
|
|
2393
|
+
const verifySignatureAndRecoverAddress = async (params) => {
|
|
2394
|
+
const { root, signature } = params;
|
|
2395
|
+
assertHex(signature, 65, "signature");
|
|
2396
|
+
const hash$1 = (0, viem.hashMessage)({ raw: root });
|
|
2397
|
+
try {
|
|
2398
|
+
return await (0, viem.recoverAddress)({
|
|
2399
|
+
hash: hash$1,
|
|
2400
|
+
signature
|
|
2401
|
+
});
|
|
2402
|
+
} catch {
|
|
2403
|
+
throw new DecodeError("signature recovery failed");
|
|
2404
|
+
}
|
|
2405
|
+
};
|
|
2406
|
+
/**
|
|
2407
|
+
* Encodes a merkle tree with signature into hex calldata for onchain broadcast.
|
|
2408
|
+
*
|
|
2409
|
+
* Layout: `0x{vv}{gzip([...offers])}{root}{signature}` where:
|
|
2410
|
+
* - `{vv}`: 1-byte version (currently 0x01)
|
|
2411
|
+
* - `{gzip([...offers])}`: gzipped JSON array of serialized offers
|
|
2412
|
+
* - `{root}`: 32-byte merkle root
|
|
2413
|
+
* - `{signature}`: 65-byte EIP-191 signature over raw root bytes
|
|
2414
|
+
*
|
|
2415
|
+
* Validates signature authenticity and root integrity before encoding.
|
|
2416
|
+
*
|
|
2417
|
+
* @example
|
|
2418
|
+
* ```typescript
|
|
2419
|
+
* const tree = Tree.from(offers);
|
|
2420
|
+
* const signature = await wallet.signMessage({ message: { raw: tree.root } });
|
|
2421
|
+
* const calldata = await Tree.encode(tree, signature);
|
|
2422
|
+
* await broadcast(calldata);
|
|
2423
|
+
* ```
|
|
2424
|
+
*
|
|
2425
|
+
* @example
|
|
2426
|
+
* Manual construction (for advanced users):
|
|
2427
|
+
* ```typescript
|
|
2428
|
+
* const tree = Tree.from(offers);
|
|
2429
|
+
* const compressed = gzip(JSON.stringify(tree.offers.map(Offer.serialize)));
|
|
2430
|
+
* const partial = `0x01${bytesToHex(compressed)}${tree.root.slice(2)}`;
|
|
2431
|
+
* const signature = await wallet.signMessage({ message: { raw: tree.root } });
|
|
2432
|
+
* const calldata = `${partial}${signature.slice(2)}`;
|
|
2433
|
+
* ```
|
|
2434
|
+
*
|
|
2435
|
+
* @param tree - Merkle tree of offers
|
|
2436
|
+
* @param signature - EIP-191 signature over raw root bytes
|
|
2437
|
+
* @returns Hex-encoded calldata ready for onchain broadcast
|
|
2438
|
+
* @throws {EncodeError} If signature verification fails or root mismatch
|
|
2439
|
+
*/
|
|
2440
|
+
const encode$1 = async (tree, signature) => {
|
|
2441
|
+
validateTreeForEncoding(tree);
|
|
2442
|
+
await verifySignatureAndRecoverAddress({
|
|
2443
|
+
root: tree.root,
|
|
2444
|
+
signature
|
|
2445
|
+
});
|
|
2446
|
+
const unsigned = encodeUnsignedBytes(tree);
|
|
2447
|
+
const sigBytes = (0, viem.hexToBytes)(signature);
|
|
2448
|
+
const encoded = new Uint8Array(unsigned.length + sigBytes.length);
|
|
2449
|
+
encoded.set(unsigned, 0);
|
|
2450
|
+
encoded.set(sigBytes, unsigned.length);
|
|
2272
2451
|
return (0, viem.bytesToHex)(encoded);
|
|
2273
2452
|
};
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2453
|
+
/**
|
|
2454
|
+
* Encodes a merkle tree without a signature into hex payload for client-side signing.
|
|
2455
|
+
*
|
|
2456
|
+
* Layout: `0x{vv}{gzip([...offers])}{root}` where:
|
|
2457
|
+
* - `{vv}`: 1-byte version (currently 0x01)
|
|
2458
|
+
* - `{gzip([...offers])}`: gzipped JSON array of serialized offers
|
|
2459
|
+
* - `{root}`: 32-byte merkle root
|
|
2460
|
+
*
|
|
2461
|
+
* Validates root integrity before encoding.
|
|
2462
|
+
*
|
|
2463
|
+
* @param tree - Merkle tree of offers
|
|
2464
|
+
* @returns Hex-encoded unsigned payload
|
|
2465
|
+
* @throws {EncodeError} If root mismatch
|
|
2466
|
+
*/
|
|
2467
|
+
const encodeUnsigned = (tree) => {
|
|
2468
|
+
validateTreeForEncoding(tree);
|
|
2469
|
+
return (0, viem.bytesToHex)(encodeUnsignedBytes(tree));
|
|
2470
|
+
};
|
|
2471
|
+
const validateTreeForEncoding = (tree) => {
|
|
2472
|
+
if (VERSION > 255) throw new EncodeError(`version overflow: ${VERSION} exceeds 255`);
|
|
2473
|
+
const computed = from$6(tree.offers);
|
|
2474
|
+
if (tree.root !== computed.root) throw new EncodeError(`root mismatch: expected ${computed.root}, got ${tree.root}`);
|
|
2475
|
+
};
|
|
2476
|
+
const encodeUnsignedBytes = (tree) => {
|
|
2477
|
+
const offersPayload = tree.offers.map(serialize);
|
|
2478
|
+
const compressed = (0, pako.gzip)(JSON.stringify(offersPayload));
|
|
2479
|
+
const rootBytes = (0, viem.hexToBytes)(tree.root);
|
|
2480
|
+
const encoded = new Uint8Array(1 + compressed.length + 32);
|
|
2481
|
+
encoded[0] = VERSION;
|
|
2482
|
+
encoded.set(compressed, 1);
|
|
2483
|
+
encoded.set(rootBytes, 1 + compressed.length);
|
|
2484
|
+
return encoded;
|
|
2277
2485
|
};
|
|
2278
2486
|
/**
|
|
2279
|
-
* Decodes
|
|
2487
|
+
* Decodes hex calldata into a validated merkle tree.
|
|
2488
|
+
*
|
|
2489
|
+
* Validates signature before decompression for fail-fast rejection of invalid payloads.
|
|
2490
|
+
* Returns the tree with separately validated signature and recovered signer address.
|
|
2491
|
+
*
|
|
2492
|
+
* Validation order:
|
|
2493
|
+
* 1. Version check
|
|
2494
|
+
* 2. Signature verification (fail-fast, before decompression)
|
|
2495
|
+
* 3. Decompression (only if signature valid)
|
|
2496
|
+
* 4. Root verification (computed from offers vs embedded root)
|
|
2280
2497
|
*
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2498
|
+
* @example
|
|
2499
|
+
* ```typescript
|
|
2500
|
+
* const { tree, signature, signer } = await Tree.decode(calldata);
|
|
2501
|
+
* console.log(`Tree signed by ${signer} with ${tree.offers.length} offers`);
|
|
2502
|
+
* ```
|
|
2283
2503
|
*
|
|
2284
|
-
* @param encoded - Hex
|
|
2285
|
-
* @returns
|
|
2286
|
-
* @throws
|
|
2287
|
-
*/
|
|
2288
|
-
const decode$1 = (encoded) => {
|
|
2289
|
-
const bytes = (0, viem.hexToBytes)(encoded);
|
|
2290
|
-
if (bytes.length <
|
|
2291
|
-
const version = bytes[0];
|
|
2292
|
-
if (version !== (VERSION & 255)) throw new
|
|
2293
|
-
const
|
|
2294
|
-
const
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2504
|
+
* @param encoded - Hex calldata in format `0x{vv}{gzip}{root}{signature}`
|
|
2505
|
+
* @returns Validated tree, signature, and recovered signer address
|
|
2506
|
+
* @throws {DecodeError} If version invalid, signature invalid, or root mismatch
|
|
2507
|
+
*/
|
|
2508
|
+
const decode$1 = async (encoded) => {
|
|
2509
|
+
const bytes$1 = (0, viem.hexToBytes)(encoded);
|
|
2510
|
+
if (bytes$1.length < 98) throw new DecodeError("payload too short");
|
|
2511
|
+
const version = bytes$1[0];
|
|
2512
|
+
if (version !== (VERSION & 255)) throw new DecodeError(`invalid version: expected ${VERSION}, got ${version ?? 0}`);
|
|
2513
|
+
const signature = (0, viem.bytesToHex)(bytes$1.slice(-65));
|
|
2514
|
+
const root = (0, viem.bytesToHex)(bytes$1.slice(-97, -65));
|
|
2515
|
+
assertHex(root, 32, "root");
|
|
2516
|
+
assertHex(signature, 65, "signature");
|
|
2517
|
+
const signer = await verifySignatureAndRecoverAddress({
|
|
2518
|
+
root,
|
|
2519
|
+
signature
|
|
2520
|
+
});
|
|
2521
|
+
const compressed = bytes$1.slice(1, -97);
|
|
2522
|
+
let decoded;
|
|
2523
|
+
try {
|
|
2524
|
+
decoded = (0, pako.ungzip)(compressed, { to: "string" });
|
|
2525
|
+
} catch {
|
|
2526
|
+
throw new DecodeError("decompression failed");
|
|
2527
|
+
}
|
|
2528
|
+
let rawOffers;
|
|
2529
|
+
try {
|
|
2530
|
+
rawOffers = JSON.parse(decoded);
|
|
2531
|
+
} catch {
|
|
2532
|
+
throw new DecodeError("JSON parse failed");
|
|
2533
|
+
}
|
|
2534
|
+
const tree = from$6(rawOffers.map((o) => OfferSchema().parse(o)));
|
|
2535
|
+
if (root !== tree.root) throw new DecodeError(`root mismatch: expected ${tree.root}, got ${root}`);
|
|
2536
|
+
return {
|
|
2537
|
+
tree,
|
|
2538
|
+
signature,
|
|
2539
|
+
signer
|
|
2540
|
+
};
|
|
2541
|
+
};
|
|
2542
|
+
/**
|
|
2543
|
+
* Error thrown during tree building operations.
|
|
2544
|
+
* Indicates structural issues with the tree (missing offers, inconsistent state).
|
|
2545
|
+
*/
|
|
2546
|
+
var TreeError = class extends BaseError {
|
|
2547
|
+
constructor(reason) {
|
|
2548
|
+
super(`Tree error: ${reason}`);
|
|
2549
|
+
_defineProperty(this, "name", "Tree.TreeError");
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
/**
|
|
2553
|
+
* Error thrown during tree encoding.
|
|
2554
|
+
* Indicates validation failures (signature, root mismatch, mixed makers).
|
|
2555
|
+
*/
|
|
2556
|
+
var EncodeError = class extends BaseError {
|
|
2557
|
+
constructor(reason) {
|
|
2558
|
+
super(`Failed to encode tree: ${reason}`);
|
|
2559
|
+
_defineProperty(this, "name", "Tree.EncodeError");
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
/**
|
|
2563
|
+
* Error thrown during tree decoding.
|
|
2564
|
+
* Indicates payload corruption, version mismatch, or validation failures.
|
|
2565
|
+
*/
|
|
2566
|
+
var DecodeError = class extends BaseError {
|
|
2567
|
+
constructor(reason) {
|
|
2568
|
+
super(`Failed to decode tree: ${reason}`);
|
|
2569
|
+
_defineProperty(this, "name", "Tree.DecodeError");
|
|
2570
|
+
}
|
|
2299
2571
|
};
|
|
2300
2572
|
|
|
2301
2573
|
//#endregion
|
|
@@ -2315,6 +2587,7 @@ var Offer_exports = /* @__PURE__ */ __export({
|
|
|
2315
2587
|
hash: () => hash,
|
|
2316
2588
|
obligationId: () => obligationId,
|
|
2317
2589
|
random: () => random$1,
|
|
2590
|
+
serialize: () => serialize,
|
|
2318
2591
|
sign: () => sign,
|
|
2319
2592
|
signatureMsg: () => signatureMsg,
|
|
2320
2593
|
toSnakeCase: () => toSnakeCase,
|
|
@@ -2395,16 +2668,47 @@ function toSnakeCase(offer) {
|
|
|
2395
2668
|
return toSnakeCase$1(offer);
|
|
2396
2669
|
}
|
|
2397
2670
|
/**
|
|
2671
|
+
* Serializes an offer for merkle tree encoding.
|
|
2672
|
+
* Converts BigInt fields to strings for JSON compatibility.
|
|
2673
|
+
*
|
|
2674
|
+
* @param offer - Offer to serialize
|
|
2675
|
+
* @returns JSON-serializable offer object
|
|
2676
|
+
*/
|
|
2677
|
+
const serialize = (offer) => ({
|
|
2678
|
+
offering: offer.offering,
|
|
2679
|
+
assets: offer.assets.toString(),
|
|
2680
|
+
rate: offer.rate.toString(),
|
|
2681
|
+
maturity: Number(offer.maturity),
|
|
2682
|
+
expiry: Number(offer.expiry),
|
|
2683
|
+
start: Number(offer.start),
|
|
2684
|
+
nonce: offer.nonce.toString(),
|
|
2685
|
+
buy: offer.buy,
|
|
2686
|
+
chainId: offer.chainId,
|
|
2687
|
+
loanToken: offer.loanToken,
|
|
2688
|
+
collaterals: offer.collaterals.map((c) => ({
|
|
2689
|
+
asset: c.asset,
|
|
2690
|
+
oracle: c.oracle,
|
|
2691
|
+
lltv: c.lltv.toString()
|
|
2692
|
+
})),
|
|
2693
|
+
callback: {
|
|
2694
|
+
address: offer.callback.address,
|
|
2695
|
+
data: offer.callback.data,
|
|
2696
|
+
gasLimit: offer.callback.gasLimit.toString()
|
|
2697
|
+
},
|
|
2698
|
+
signature: offer.signature,
|
|
2699
|
+
hash: offer.hash
|
|
2700
|
+
});
|
|
2701
|
+
/**
|
|
2398
2702
|
* Generates a random Offer.
|
|
2399
2703
|
* The returned Offer contains randomly generated values.
|
|
2400
2704
|
* @warning The generated Offer should not be used for production usage.
|
|
2401
2705
|
* @returns {Offer} A randomly generated Offer object.
|
|
2402
2706
|
*/
|
|
2403
2707
|
function random$1(config) {
|
|
2404
|
-
const chain = config?.chains ? config.chains[
|
|
2405
|
-
const loanToken = config?.loanTokens ? config.loanTokens[
|
|
2406
|
-
const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [(
|
|
2407
|
-
const collateralAsset = collateralCandidates[
|
|
2708
|
+
const chain = config?.chains ? config.chains[int(config.chains.length)] : chains$1.ethereum;
|
|
2709
|
+
const loanToken = config?.loanTokens ? config.loanTokens[int(config.loanTokens.length)] : address();
|
|
2710
|
+
const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [address()];
|
|
2711
|
+
const collateralAsset = collateralCandidates[int(collateralCandidates.length)];
|
|
2408
2712
|
const maturityOption = weightedChoice([["end_of_month", 1], ["end_of_next_month", 1]]);
|
|
2409
2713
|
const maturity$1 = config?.maturity ?? from$8(maturityOption);
|
|
2410
2714
|
const lltv = from$10(weightedChoice([
|
|
@@ -2418,7 +2722,7 @@ function random$1(config) {
|
|
|
2418
2722
|
[.965, 4],
|
|
2419
2723
|
[.98, 2]
|
|
2420
2724
|
]));
|
|
2421
|
-
const buy = config?.buy !== void 0 ? config.buy :
|
|
2725
|
+
const buy = config?.buy !== void 0 ? config.buy : bool();
|
|
2422
2726
|
const ONE = 1000000000000000000n;
|
|
2423
2727
|
const qMin = buy ? 16 : 4;
|
|
2424
2728
|
const len = (buy ? 32 : 16) - qMin + 1;
|
|
@@ -2429,9 +2733,9 @@ function random$1(config) {
|
|
|
2429
2733
|
const rate = config?.rate ?? weightedChoice(ratePairs);
|
|
2430
2734
|
const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
|
|
2431
2735
|
const unit = BigInt(10) ** BigInt(loanTokenDecimals);
|
|
2432
|
-
const amountBase = BigInt(100 +
|
|
2736
|
+
const amountBase = BigInt(100 + int(999901));
|
|
2433
2737
|
const assetsScaled = config?.assets ?? amountBase * unit;
|
|
2434
|
-
const consumed = config?.consumed !== void 0 ? config.consumed :
|
|
2738
|
+
const consumed = config?.consumed !== void 0 ? config.consumed : float() < .8 ? 0n : assetsScaled * BigInt(1 + int(900)) / 1000n;
|
|
2435
2739
|
const callbackBySide = (() => {
|
|
2436
2740
|
if (buy) return {
|
|
2437
2741
|
address: viem.zeroAddress,
|
|
@@ -2450,29 +2754,29 @@ function random$1(config) {
|
|
|
2450
2754
|
};
|
|
2451
2755
|
})();
|
|
2452
2756
|
return from$5({
|
|
2453
|
-
offering: config?.offering ?? (
|
|
2757
|
+
offering: config?.offering ?? address(),
|
|
2454
2758
|
assets: assetsScaled,
|
|
2455
2759
|
rate,
|
|
2456
2760
|
maturity: maturity$1,
|
|
2457
2761
|
expiry: config?.expiry ?? maturity$1 - 1,
|
|
2458
2762
|
start: config?.start ?? maturity$1 - 10,
|
|
2459
|
-
nonce: BigInt(
|
|
2763
|
+
nonce: BigInt(int(1e6)),
|
|
2460
2764
|
buy,
|
|
2461
2765
|
chainId: chain.id,
|
|
2462
2766
|
loanToken,
|
|
2463
|
-
collaterals: config?.collaterals ?? Array.from({ length:
|
|
2767
|
+
collaterals: config?.collaterals ?? Array.from({ length: int(3) + 1 }, () => ({
|
|
2464
2768
|
...random$3(),
|
|
2465
2769
|
lltv
|
|
2466
2770
|
})).sort((a, b) => a.asset.localeCompare(b.asset)),
|
|
2467
2771
|
callback: config?.callback ?? callbackBySide,
|
|
2468
2772
|
consumed,
|
|
2469
2773
|
takeable: config?.takeable ?? assetsScaled - consumed,
|
|
2470
|
-
blockNumber: config?.blockNumber ??
|
|
2774
|
+
blockNumber: config?.blockNumber ?? int(Number.MAX_SAFE_INTEGER)
|
|
2471
2775
|
});
|
|
2472
2776
|
}
|
|
2473
2777
|
const weightedChoice = (pairs) => {
|
|
2474
2778
|
const total = pairs.reduce((sum, [, weight]) => sum + weight, 0);
|
|
2475
|
-
let roll =
|
|
2779
|
+
let roll = float() * total;
|
|
2476
2780
|
for (const [value, weight] of pairs) {
|
|
2477
2781
|
roll -= weight;
|
|
2478
2782
|
if (roll < 0) return value;
|
|
@@ -2940,8 +3244,8 @@ function fromSnakeCase(snake) {
|
|
|
2940
3244
|
function random() {
|
|
2941
3245
|
return from$2({
|
|
2942
3246
|
obligationId: id(random$2()),
|
|
2943
|
-
ask: { rate: BigInt(
|
|
2944
|
-
bid: { rate: BigInt(
|
|
3247
|
+
ask: { rate: BigInt(int(1e6)) },
|
|
3248
|
+
bid: { rate: BigInt(int(1e6)) }
|
|
2945
3249
|
});
|
|
2946
3250
|
}
|
|
2947
3251
|
var InvalidQuoteError = class extends BaseError {
|
|
@@ -3041,24 +3345,28 @@ async function getOffers(apiClient, parameters) {
|
|
|
3041
3345
|
throw new HttpGetApiFailedError(`GET request returned ${response.status}`, { details: JSON.stringify(error) });
|
|
3042
3346
|
}
|
|
3043
3347
|
const offers = data?.data.map((item) => {
|
|
3044
|
-
const { signature, ...rest } = item;
|
|
3045
|
-
return
|
|
3046
|
-
...
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3348
|
+
const { root, proof, signature, ...rest } = item;
|
|
3349
|
+
return {
|
|
3350
|
+
...fromSnakeCase$1({
|
|
3351
|
+
...rest,
|
|
3352
|
+
offering: item.offering,
|
|
3353
|
+
maturity: from$8(item.maturity),
|
|
3354
|
+
loan_token: item.loan_token,
|
|
3355
|
+
collaterals: item.collaterals.map((collateral) => ({
|
|
3356
|
+
asset: collateral.asset,
|
|
3357
|
+
oracle: collateral.oracle,
|
|
3358
|
+
lltv: collateral.lltv
|
|
3359
|
+
})),
|
|
3360
|
+
callback: {
|
|
3361
|
+
...item.callback,
|
|
3362
|
+
address: item.callback.address,
|
|
3363
|
+
data: item.callback.data
|
|
3364
|
+
},
|
|
3365
|
+
signature: signature?.toLowerCase()
|
|
3366
|
+
}),
|
|
3367
|
+
root: root?.toLowerCase() || void 0,
|
|
3368
|
+
proof: proof?.map((p) => p.toLowerCase()) || void 0
|
|
3369
|
+
};
|
|
3062
3370
|
}) ?? [];
|
|
3063
3371
|
return {
|
|
3064
3372
|
cursor: data?.cursor ?? null,
|
|
@@ -3249,8 +3557,8 @@ function getCallback(chain, type) {
|
|
|
3249
3557
|
* @param address - Callback contract address
|
|
3250
3558
|
* @returns The callback type when found, otherwise undefined
|
|
3251
3559
|
*/
|
|
3252
|
-
function getCallbackType(chain, address) {
|
|
3253
|
-
return configs[chain].callbacks?.find((c) => c.type !== CallbackType.BuyWithEmptyCallback && c.addresses.includes(address?.toLowerCase()))?.type;
|
|
3560
|
+
function getCallbackType(chain, address$1) {
|
|
3561
|
+
return configs[chain].callbacks?.find((c) => c.type !== CallbackType.BuyWithEmptyCallback && c.addresses.includes(address$1?.toLowerCase()))?.type;
|
|
3254
3562
|
}
|
|
3255
3563
|
/**
|
|
3256
3564
|
* Returns the callback addresses for a given chain and callback type, if it exists.
|
|
@@ -3383,6 +3691,7 @@ var Rules_exports = /* @__PURE__ */ __export({
|
|
|
3383
3691
|
callback: () => callback,
|
|
3384
3692
|
chains: () => chains,
|
|
3385
3693
|
maturity: () => maturity,
|
|
3694
|
+
sameMaker: () => sameMaker,
|
|
3386
3695
|
token: () => token,
|
|
3387
3696
|
validity: () => validity
|
|
3388
3697
|
});
|
|
@@ -3519,10 +3828,29 @@ const token = ({ assets: assets$1 }) => single("token", "Validates that offer lo
|
|
|
3519
3828
|
if (!allowedAssets.includes(offer.loanToken.toLowerCase())) return { message: "Loan token is not allowed" };
|
|
3520
3829
|
if (offer.collaterals.some((collateral) => !allowedAssets.includes(collateral.asset.toLowerCase()))) return { message: "Collateral is not allowed" };
|
|
3521
3830
|
});
|
|
3831
|
+
/**
|
|
3832
|
+
* A batch validation rule that ensures all offers in a tree have the same maker (offering address).
|
|
3833
|
+
* Returns an issue only for the first non-conforming offer.
|
|
3834
|
+
* This rule is signing-agnostic; signer verification is handled at the collector level.
|
|
3835
|
+
*/
|
|
3836
|
+
const sameMaker = () => batch$1("mixed_maker", "Validates that all offers in a batch have the same maker (offering address)", (offers) => {
|
|
3837
|
+
const issues = /* @__PURE__ */ new Map();
|
|
3838
|
+
if (offers.length === 0) return issues;
|
|
3839
|
+
const firstMaker = offers[0].offering.toLowerCase();
|
|
3840
|
+
for (let i = 1; i < offers.length; i++) {
|
|
3841
|
+
const offer = offers[i];
|
|
3842
|
+
if (offer.offering.toLowerCase() !== firstMaker) {
|
|
3843
|
+
issues.set(i, { message: `Offer has different maker ${offer.offering} than first offer ${offers[0].offering}` });
|
|
3844
|
+
return issues;
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
return issues;
|
|
3848
|
+
});
|
|
3522
3849
|
|
|
3523
3850
|
//#endregion
|
|
3524
3851
|
//#region src/gatekeeper/morphoRules.ts
|
|
3525
3852
|
const morphoRules = (chains$2) => [
|
|
3853
|
+
sameMaker(),
|
|
3526
3854
|
chains({ chains: chains$2 }),
|
|
3527
3855
|
maturity({ maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth] }),
|
|
3528
3856
|
callback({
|
|
@@ -3563,22 +3891,24 @@ async function add(config, offers) {
|
|
|
3563
3891
|
const tree = from$6(offers.map((o) => from$5(o)));
|
|
3564
3892
|
const chainId = await getChainId(config.client);
|
|
3565
3893
|
for (const offer of tree.offers) if (chainId !== offer.chainId) throw new ChainIdMismatchError(offer.chainId, chainId);
|
|
3894
|
+
const signature = await sign(tree.offers, config.client);
|
|
3895
|
+
const encoded = await encode$1(tree, signature);
|
|
3566
3896
|
try {
|
|
3567
3897
|
return await config.client.sendTransaction({
|
|
3568
3898
|
chain: config.client.chain,
|
|
3569
3899
|
account: config.client.account,
|
|
3570
3900
|
to: config.mempoolAddress,
|
|
3571
|
-
data:
|
|
3901
|
+
data: encoded
|
|
3572
3902
|
});
|
|
3573
3903
|
} catch (error) {
|
|
3574
3904
|
throw new ViemClientError(error instanceof Error ? error.message : "Unknown error");
|
|
3575
3905
|
}
|
|
3576
3906
|
}
|
|
3577
3907
|
async function* get(config, parameters) {
|
|
3578
|
-
const { loanToken, blockNumberGte, blockNumberLte, order
|
|
3908
|
+
const { loanToken, blockNumberGte, blockNumberLte, order = "desc", options: { maxBatchSize = DEFAULT_BATCH_SIZE } = {} } = parameters || {};
|
|
3579
3909
|
yield* streamOffers(config, {
|
|
3580
3910
|
loanToken,
|
|
3581
|
-
order
|
|
3911
|
+
order,
|
|
3582
3912
|
blockNumberGte,
|
|
3583
3913
|
blockNumberLte,
|
|
3584
3914
|
options: {
|
|
@@ -3600,7 +3930,7 @@ const getChainId = async (client) => {
|
|
|
3600
3930
|
return chainId;
|
|
3601
3931
|
};
|
|
3602
3932
|
async function* streamOffers(config, parameters) {
|
|
3603
|
-
const { loanToken, blockNumberGte, blockNumberLte, order
|
|
3933
|
+
const { loanToken, blockNumberGte, blockNumberLte, order = "desc", options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = config.blockWindow } = {} } = parameters;
|
|
3604
3934
|
const stream = streamLogs({
|
|
3605
3935
|
client: config.client.extend(viem.publicActions),
|
|
3606
3936
|
contractAddress: config.mempoolAddress,
|
|
@@ -3617,13 +3947,13 @@ async function* streamOffers(config, parameters) {
|
|
|
3617
3947
|
},
|
|
3618
3948
|
blockNumberGte,
|
|
3619
3949
|
blockNumberLte,
|
|
3620
|
-
order
|
|
3950
|
+
order,
|
|
3621
3951
|
options: {
|
|
3622
3952
|
maxBatchSize,
|
|
3623
3953
|
blockWindow
|
|
3624
3954
|
}
|
|
3625
3955
|
});
|
|
3626
|
-
let blockNumber = order
|
|
3956
|
+
let blockNumber = order === "asc" ? blockNumberGte : blockNumberLte;
|
|
3627
3957
|
for await (const { logs, blockNumber: newBlockNumber } of stream) {
|
|
3628
3958
|
blockNumber = newBlockNumber;
|
|
3629
3959
|
if (logs.length === 0) continue;
|
|
@@ -3632,7 +3962,7 @@ async function* streamOffers(config, parameters) {
|
|
|
3632
3962
|
if (!log) continue;
|
|
3633
3963
|
const [payload] = (0, viem.decodeAbiParameters)([{ type: "bytes" }], log.data);
|
|
3634
3964
|
try {
|
|
3635
|
-
const tree = decode$1(payload);
|
|
3965
|
+
const { tree } = await decode$1(payload);
|
|
3636
3966
|
for (const offer of tree.offers) {
|
|
3637
3967
|
if (loanToken && offer.loanToken.toLowerCase() !== loanToken.toLowerCase()) continue;
|
|
3638
3968
|
offers.push({
|
|
@@ -3801,6 +4131,7 @@ function max() {
|
|
|
3801
4131
|
//#region src/utils/index.ts
|
|
3802
4132
|
var utils_exports = /* @__PURE__ */ __export({
|
|
3803
4133
|
BaseError: () => BaseError,
|
|
4134
|
+
Random: () => Random_exports,
|
|
3804
4135
|
ReorgError: () => ReorgError,
|
|
3805
4136
|
Time: () => time_exports,
|
|
3806
4137
|
batch: () => batch,
|