@morpho-dev/router 0.5.0 → 0.7.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.
@@ -1413,6 +1413,7 @@ var Obligation_exports = /* @__PURE__ */ __exportAll({
1413
1413
  InvalidObligationError: () => InvalidObligationError,
1414
1414
  ObligationSchema: () => ObligationSchema,
1415
1415
  from: () => from$10,
1416
+ fromOffer: () => fromOffer$1,
1416
1417
  fromSnakeCase: () => fromSnakeCase$2,
1417
1418
  id: () => id,
1418
1419
  random: () => random$2
@@ -1541,6 +1542,21 @@ function random$2() {
1541
1542
  maturity: from$11("end_of_next_quarter")
1542
1543
  });
1543
1544
  }
1545
+ /**
1546
+ * Creates an obligation from an offer.
1547
+ * @constructor
1548
+ *
1549
+ * @param offer - The offer to create the obligation from.
1550
+ * @returns The created obligation. {@link fromOffer.ReturnType}
1551
+ */
1552
+ function fromOffer$1(offer) {
1553
+ return from$10({
1554
+ chainId: offer.chainId,
1555
+ loanToken: offer.loanToken,
1556
+ collaterals: offer.collaterals,
1557
+ maturity: offer.maturity
1558
+ });
1559
+ }
1544
1560
  var InvalidObligationError = class extends BaseError {
1545
1561
  constructor(error) {
1546
1562
  super("Invalid obligation.", { cause: error });
@@ -1554,273 +1570,22 @@ var CollateralsAreNotSortedError = class extends BaseError {
1554
1570
  }
1555
1571
  };
1556
1572
 
1557
- //#endregion
1558
- //#region src/core/Tree.ts
1559
- var Tree_exports = /* @__PURE__ */ __exportAll({
1560
- DecodeError: () => DecodeError,
1561
- EncodeError: () => EncodeError,
1562
- TreeError: () => TreeError,
1563
- VERSION: () => VERSION,
1564
- decode: () => decode$1,
1565
- encode: () => encode$1,
1566
- encodeUnsigned: () => encodeUnsigned,
1567
- from: () => from$9,
1568
- proofs: () => proofs
1569
- });
1570
- const VERSION = 1;
1571
- const normalizeHash = (hash) => hash.toLowerCase();
1572
- /**
1573
- * Builds a Merkle tree from a list of offers.
1574
- *
1575
- * Leaves are the offer `hash` values as `bytes32` and are deterministically
1576
- * ordered following the StandardMerkleTree leaf ordering so that the resulting
1577
- * root is stable regardless of the input order.
1578
- *
1579
- * @param offers - Offers to include in the tree.
1580
- * @returns A `StandardMerkleTree` of `bytes32` leaves representing the offers.
1581
- * @throws {TreeError} If tree building fails due to offer inconsistencies.
1582
- */
1583
- const from$9 = (offers) => {
1584
- const leaves = offers.map((offer) => [hash(offer)]);
1585
- const tree = _openzeppelin_merkle_tree.StandardMerkleTree.of(leaves, ["bytes32"]);
1586
- const orderedOffers = orderOffers(tree, offers);
1587
- return Object.assign(tree, { offers: orderedOffers });
1588
- };
1589
- const orderOffers = (tree, offers) => {
1590
- const offerByHash = /* @__PURE__ */ new Map();
1591
- for (const offer of offers) offerByHash.set(normalizeHash(hash(offer)), offer);
1592
- const entries = tree.dump().values.map((value) => {
1593
- const hash = normalizeHash(value.value[0]);
1594
- const offer = offerByHash.get(hash);
1595
- if (!offer) throw new TreeError(`missing offer for leaf ${hash}`);
1596
- return {
1597
- offer,
1598
- treeIndex: value.treeIndex
1599
- };
1600
- });
1601
- entries.sort((a, b) => b.treeIndex - a.treeIndex);
1602
- return entries.map((item) => item.offer);
1603
- };
1604
- /**
1605
- * Generates merkle proofs for all offers in a tree.
1606
- *
1607
- * Each proof allows independent verification that an offer is included in the tree
1608
- * without requiring the full tree. Proofs are ordered by StandardMerkleTree leaf ordering.
1609
- *
1610
- * @param tree - The {@link Tree} to generate proofs for.
1611
- * @returns Array of proofs - {@link Proof}
1612
- */
1613
- const proofs = (tree) => {
1614
- return tree.offers.map((offer) => {
1615
- return {
1616
- offer,
1617
- path: tree.getProof([hash(offer)])
1618
- };
1619
- });
1620
- };
1621
- const assertHex = (value, expectedBytes, name) => {
1622
- if (typeof value !== "string" || !(0, viem.isHex)(value)) throw new DecodeError(`${name} is not a valid hex string`);
1623
- if ((0, viem.hexToBytes)(value).length !== expectedBytes) throw new DecodeError(`${name}: expected ${expectedBytes} bytes`);
1624
- };
1625
- const verifySignatureAndRecoverAddress = async (params) => {
1626
- const { root, signature } = params;
1627
- assertHex(signature, 65, "signature");
1628
- const hash = (0, viem.hashMessage)({ raw: root });
1629
- try {
1630
- return await (0, viem.recoverAddress)({
1631
- hash,
1632
- signature
1633
- });
1634
- } catch {
1635
- throw new DecodeError("signature recovery failed");
1636
- }
1637
- };
1638
- /**
1639
- * Encodes a merkle tree with signature into hex calldata for onchain broadcast.
1640
- *
1641
- * Layout: `0x{vv}{gzip([...offers])}{root}{signature}` where:
1642
- * - `{vv}`: 1-byte version (currently 0x01)
1643
- * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
1644
- * - `{root}`: 32-byte merkle root
1645
- * - `{signature}`: 65-byte EIP-191 signature over raw root bytes
1646
- *
1647
- * Validates signature authenticity and root integrity before encoding.
1648
- *
1649
- * @example
1650
- * ```typescript
1651
- * const tree = Tree.from(offers);
1652
- * const signature = await wallet.signMessage({ message: { raw: tree.root } });
1653
- * const calldata = await Tree.encode(tree, signature);
1654
- * await broadcast(calldata);
1655
- * ```
1656
- *
1657
- * @example
1658
- * Manual construction (for advanced users):
1659
- * ```typescript
1660
- * const tree = Tree.from(offers);
1661
- * const compressed = gzip(JSON.stringify(tree.offers.map(Offer.serialize)));
1662
- * const partial = `0x01${bytesToHex(compressed)}${tree.root.slice(2)}`;
1663
- * const signature = await wallet.signMessage({ message: { raw: tree.root } });
1664
- * const calldata = `${partial}${signature.slice(2)}`;
1665
- * ```
1666
- *
1667
- * @param tree - Merkle tree of offers
1668
- * @param signature - EIP-191 signature over raw root bytes
1669
- * @returns Hex-encoded calldata ready for onchain broadcast
1670
- * @throws {EncodeError} If signature verification fails or root mismatch
1671
- */
1672
- const encode$1 = async (tree, signature) => {
1673
- validateTreeForEncoding(tree);
1674
- await verifySignatureAndRecoverAddress({
1675
- root: tree.root,
1676
- signature
1677
- });
1678
- const unsigned = encodeUnsignedBytes(tree);
1679
- const sigBytes = (0, viem.hexToBytes)(signature);
1680
- const encoded = new Uint8Array(unsigned.length + sigBytes.length);
1681
- encoded.set(unsigned, 0);
1682
- encoded.set(sigBytes, unsigned.length);
1683
- return (0, viem.bytesToHex)(encoded);
1684
- };
1685
- /**
1686
- * Encodes a merkle tree without a signature into hex payload for client-side signing.
1687
- *
1688
- * Layout: `0x{vv}{gzip([...offers])}{root}` where:
1689
- * - `{vv}`: 1-byte version (currently 0x01)
1690
- * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
1691
- * - `{root}`: 32-byte merkle root
1692
- *
1693
- * Validates root integrity before encoding.
1694
- *
1695
- * @param tree - Merkle tree of offers
1696
- * @returns Hex-encoded unsigned payload
1697
- * @throws {EncodeError} If root mismatch
1698
- */
1699
- const encodeUnsigned = (tree) => {
1700
- validateTreeForEncoding(tree);
1701
- return (0, viem.bytesToHex)(encodeUnsignedBytes(tree));
1702
- };
1703
- const validateTreeForEncoding = (tree) => {
1704
- if (VERSION > 255) throw new EncodeError(`version overflow: ${VERSION} exceeds 255`);
1705
- const computed = from$9(tree.offers);
1706
- if (tree.root !== computed.root) throw new EncodeError(`root mismatch: expected ${computed.root}, got ${tree.root}`);
1707
- };
1708
- const encodeUnsignedBytes = (tree) => {
1709
- const offersPayload = tree.offers.map(serialize);
1710
- const compressed = (0, pako.gzip)(JSON.stringify(offersPayload));
1711
- const rootBytes = (0, viem.hexToBytes)(tree.root);
1712
- const encoded = new Uint8Array(1 + compressed.length + 32);
1713
- encoded[0] = VERSION;
1714
- encoded.set(compressed, 1);
1715
- encoded.set(rootBytes, 1 + compressed.length);
1716
- return encoded;
1717
- };
1718
- /**
1719
- * Decodes hex calldata into a validated merkle tree.
1720
- *
1721
- * Validates signature before decompression for fail-fast rejection of invalid payloads.
1722
- * Returns the tree with separately validated signature and recovered signer address.
1723
- *
1724
- * Validation order:
1725
- * 1. Version check
1726
- * 2. Signature verification (fail-fast, before decompression)
1727
- * 3. Decompression (only if signature valid)
1728
- * 4. Root verification (computed from offers vs embedded root)
1729
- *
1730
- * @example
1731
- * ```typescript
1732
- * const { tree, signature, signer } = await Tree.decode(calldata);
1733
- * console.log(`Tree signed by ${signer} with ${tree.offers.length} offers`);
1734
- * ```
1735
- *
1736
- * @param encoded - Hex calldata in format `0x{vv}{gzip}{root}{signature}`
1737
- * @returns Validated tree, signature, and recovered signer address
1738
- * @throws {DecodeError} If version invalid, signature invalid, or root mismatch
1739
- */
1740
- const decode$1 = async (encoded) => {
1741
- const bytes = (0, viem.hexToBytes)(encoded);
1742
- if (bytes.length < 98) throw new DecodeError("payload too short");
1743
- const version = bytes[0];
1744
- if (version !== (VERSION & 255)) throw new DecodeError(`invalid version: expected ${VERSION}, got ${version ?? 0}`);
1745
- const signature = (0, viem.bytesToHex)(bytes.slice(-65));
1746
- const root = (0, viem.bytesToHex)(bytes.slice(-97, -65));
1747
- assertHex(root, 32, "root");
1748
- assertHex(signature, 65, "signature");
1749
- const signer = await verifySignatureAndRecoverAddress({
1750
- root,
1751
- signature
1752
- });
1753
- const compressed = bytes.slice(1, -97);
1754
- let decoded;
1755
- try {
1756
- decoded = (0, pako.ungzip)(compressed, { to: "string" });
1757
- } catch {
1758
- throw new DecodeError("decompression failed");
1759
- }
1760
- let rawOffers;
1761
- try {
1762
- rawOffers = JSON.parse(decoded);
1763
- } catch {
1764
- throw new DecodeError("JSON parse failed");
1765
- }
1766
- const tree = from$9(rawOffers.map((o) => OfferSchema().parse(o)));
1767
- if (root !== tree.root) throw new DecodeError(`root mismatch: expected ${tree.root}, got ${root}`);
1768
- return {
1769
- tree,
1770
- signature,
1771
- signer
1772
- };
1773
- };
1774
- /**
1775
- * Error thrown during tree building operations.
1776
- * Indicates structural issues with the tree (missing offers, inconsistent state).
1777
- */
1778
- var TreeError = class extends BaseError {
1779
- constructor(reason) {
1780
- super(`Tree error: ${reason}`);
1781
- _defineProperty(this, "name", "Tree.TreeError");
1782
- }
1783
- };
1784
- /**
1785
- * Error thrown during tree encoding.
1786
- * Indicates validation failures (signature, root mismatch, mixed makers).
1787
- */
1788
- var EncodeError = class extends BaseError {
1789
- constructor(reason) {
1790
- super(`Failed to encode tree: ${reason}`);
1791
- _defineProperty(this, "name", "Tree.EncodeError");
1792
- }
1793
- };
1794
- /**
1795
- * Error thrown during tree decoding.
1796
- * Indicates payload corruption, version mismatch, or validation failures.
1797
- */
1798
- var DecodeError = class extends BaseError {
1799
- constructor(reason) {
1800
- super(`Failed to decode tree: ${reason}`);
1801
- _defineProperty(this, "name", "Tree.DecodeError");
1802
- }
1803
- };
1804
-
1805
1573
  //#endregion
1806
1574
  //#region src/core/Offer.ts
1807
1575
  var Offer_exports = /* @__PURE__ */ __exportAll({
1808
- AccountNotSetError: () => AccountNotSetError,
1809
1576
  InvalidOfferError: () => InvalidOfferError,
1810
1577
  OfferSchema: () => OfferSchema,
1811
1578
  Status: () => Status,
1812
1579
  consumedEvent: () => consumedEvent,
1813
- decode: () => decode,
1580
+ decode: () => decode$1,
1814
1581
  domain: () => domain,
1815
- encode: () => encode,
1816
- from: () => from$8,
1582
+ encode: () => encode$1,
1583
+ from: () => from$9,
1817
1584
  fromSnakeCase: () => fromSnakeCase$1,
1818
1585
  hash: () => hash,
1819
1586
  obligationId: () => obligationId,
1820
1587
  random: () => random$1,
1821
1588
  serialize: () => serialize,
1822
- sign: () => sign,
1823
- signatureMsg: () => signatureMsg,
1824
1589
  toSnakeCase: () => toSnakeCase,
1825
1590
  types: () => types
1826
1591
  });
@@ -1873,7 +1638,7 @@ const OfferSchema = () => {
1873
1638
  * @param input - The offer to create.
1874
1639
  * @returns The created offer.
1875
1640
  */
1876
- function from$8(input) {
1641
+ function from$9(input) {
1877
1642
  try {
1878
1643
  return OfferSchema().parse(input);
1879
1644
  } catch (error) {
@@ -1887,7 +1652,7 @@ function from$8(input) {
1887
1652
  * @returns The created offer.
1888
1653
  */
1889
1654
  function fromSnakeCase$1(input) {
1890
- return from$8(fromSnakeCase$3(input));
1655
+ return from$9(fromSnakeCase$3(input));
1891
1656
  }
1892
1657
  /**
1893
1658
  * Converts an offer to a snake case object.
@@ -1981,7 +1746,7 @@ function random$1(config) {
1981
1746
  })
1982
1747
  };
1983
1748
  })();
1984
- return from$8({
1749
+ return from$9({
1985
1750
  maker: config?.maker ?? address(),
1986
1751
  assets: assetsScaled,
1987
1752
  obligationUnits: config?.obligationUnits ?? 0n,
@@ -2109,23 +1874,6 @@ const types = {
2109
1874
  type: "bytes"
2110
1875
  }]
2111
1876
  };
2112
- /**
2113
- * Signs an array of offers.
2114
- * @throws {Error} If the wallet account is not set.
2115
- * @param offers - The offers to sign.
2116
- * @param wallet - The wallet to sign the offers with.
2117
- * @returns The signed offers.
2118
- */
2119
- async function sign(offers, wallet) {
2120
- if (!wallet.account) throw new AccountNotSetError();
2121
- return wallet.signMessage({
2122
- account: wallet.account,
2123
- message: { raw: signatureMsg(offers) }
2124
- });
2125
- }
2126
- function signatureMsg(offers) {
2127
- return from$9(offers).root;
2128
- }
2129
1877
  function hash(offer) {
2130
1878
  const cached = offer[HASH_CACHE];
2131
1879
  if (cached) return cached;
@@ -2252,7 +2000,7 @@ const OfferAbi = [
2252
2000
  }]
2253
2001
  }
2254
2002
  ];
2255
- function encode(offer) {
2003
+ function encode$1(offer) {
2256
2004
  return (0, viem.encodeAbiParameters)(OfferAbi, [
2257
2005
  offer.maker,
2258
2006
  offer.assets,
@@ -2271,14 +2019,14 @@ function encode(offer) {
2271
2019
  offer.callback
2272
2020
  ]);
2273
2021
  }
2274
- function decode(data) {
2022
+ function decode$1(data) {
2275
2023
  let decoded;
2276
2024
  try {
2277
2025
  decoded = (0, viem.decodeAbiParameters)(OfferAbi, data);
2278
2026
  } catch (error) {
2279
2027
  throw new InvalidOfferError(error);
2280
2028
  }
2281
- return from$8({
2029
+ return from$9({
2282
2030
  maker: decoded[0],
2283
2031
  assets: decoded[1],
2284
2032
  obligationUnits: decoded[2],
@@ -2357,25 +2105,22 @@ var InvalidOfferError = class InvalidOfferError extends BaseError {
2357
2105
  return `Invalid offer. ${InvalidOfferError.formatDetails(this.cause)}`;
2358
2106
  }
2359
2107
  };
2360
- var AccountNotSetError = class extends BaseError {
2361
- constructor() {
2362
- super("Account not set.");
2363
- _defineProperty(this, "name", "Offer.AccountNotSetError");
2364
- }
2365
- };
2366
2108
 
2367
2109
  //#endregion
2368
2110
  //#region src/core/Oracle.ts
2369
2111
  var Oracle_exports = /* @__PURE__ */ __exportAll({
2370
2112
  Conversion: () => Conversion,
2371
- from: () => from$7
2113
+ from: () => from$8,
2114
+ fromCollateral: () => fromCollateral,
2115
+ fromOffer: () => fromOffer,
2116
+ fromOffers: () => fromOffers
2372
2117
  });
2373
2118
  /**
2374
2119
  * Create an Oracle from a plain object.
2375
2120
  * @param data - The data to create the oracle from.
2376
2121
  * @returns The created oracle.
2377
2122
  */
2378
- function from$7(data) {
2123
+ function from$8(data) {
2379
2124
  return {
2380
2125
  chainId: data.chainId,
2381
2126
  address: data.address.toLowerCase(),
@@ -2383,8 +2128,61 @@ function from$7(data) {
2383
2128
  blockNumber: data.blockNumber
2384
2129
  };
2385
2130
  }
2386
- let Conversion;
2387
- (function(_Conversion) {
2131
+ /**
2132
+ * Creates an oracle from a collateral.
2133
+ * @constructor
2134
+ *
2135
+ * @param parameters - {@link fromCollateral.Parameters}
2136
+ * @returns The created oracle. {@link fromCollateral.ReturnType}
2137
+ */
2138
+ function fromCollateral(parameters) {
2139
+ const { chainId, collateral, blockNumber, price = null } = parameters;
2140
+ return {
2141
+ chainId,
2142
+ address: collateral.oracle.toLowerCase(),
2143
+ price,
2144
+ blockNumber
2145
+ };
2146
+ }
2147
+ /**
2148
+ * Creates oracles from a single offer.
2149
+ * @constructor
2150
+ *
2151
+ * @param parameters - {@link fromOffer.Parameters}
2152
+ * @returns The created oracles. {@link fromOffer.ReturnType}
2153
+ */
2154
+ function fromOffer(parameters) {
2155
+ const { offer, blockNumber, price = null } = parameters;
2156
+ return fromOffers({
2157
+ offers: [offer],
2158
+ blockNumber,
2159
+ price
2160
+ });
2161
+ }
2162
+ /**
2163
+ * Creates oracles from a list of offers.
2164
+ * @constructor
2165
+ *
2166
+ * @param parameters - {@link fromOffers.Parameters}
2167
+ * @returns The created oracles. {@link fromOffers.ReturnType}
2168
+ */
2169
+ function fromOffers(parameters) {
2170
+ const { offers, blockNumber, price = null } = parameters;
2171
+ const rowsByKey = /* @__PURE__ */ new Map();
2172
+ for (const offer of offers) for (const collateral of offer.collaterals) {
2173
+ const key = `${offer.chainId}-${collateral.oracle}`.toLowerCase();
2174
+ if (rowsByKey.has(key)) continue;
2175
+ rowsByKey.set(key, fromCollateral({
2176
+ chainId: offer.chainId,
2177
+ collateral,
2178
+ blockNumber,
2179
+ price
2180
+ }));
2181
+ }
2182
+ return Array.from(rowsByKey.values());
2183
+ }
2184
+ let Conversion;
2185
+ (function(_Conversion) {
2388
2186
  function collateralToLoan(amount, params) {
2389
2187
  return amount * params.price / 10n ** 36n * params.lltv / 10n ** 18n;
2390
2188
  }
@@ -2400,7 +2198,7 @@ let Conversion;
2400
2198
  //#region src/core/Position.ts
2401
2199
  var Position_exports = /* @__PURE__ */ __exportAll({
2402
2200
  Type: () => Type,
2403
- from: () => from$6
2201
+ from: () => from$7
2404
2202
  });
2405
2203
  let Type = /* @__PURE__ */ function(Type) {
2406
2204
  Type["ERC20"] = "erc20";
@@ -2413,7 +2211,7 @@ let Type = /* @__PURE__ */ function(Type) {
2413
2211
  * @param parameters - {@link from.Parameters}
2414
2212
  * @returns The created Position. {@link from.ReturnType}
2415
2213
  */
2416
- function from$6(parameters) {
2214
+ function from$7(parameters) {
2417
2215
  return {
2418
2216
  chainId: parameters.chainId,
2419
2217
  contract: parameters.contract.toLowerCase(),
@@ -2430,7 +2228,7 @@ function from$6(parameters) {
2430
2228
  var Quote_exports = /* @__PURE__ */ __exportAll({
2431
2229
  InvalidQuoteError: () => InvalidQuoteError,
2432
2230
  QuoteSchema: () => QuoteSchema,
2433
- from: () => from$5,
2231
+ from: () => from$6,
2434
2232
  fromSnakeCase: () => fromSnakeCase,
2435
2233
  random: () => random
2436
2234
  });
@@ -2451,7 +2249,7 @@ const QuoteSchema = zod.object({
2451
2249
  * const quote = Quote.from({ obligationId: "0x123", ask: { price: 100n }, bid: { price: 100n } });
2452
2250
  * ```
2453
2251
  */
2454
- function from$5(parameters) {
2252
+ function from$6(parameters) {
2455
2253
  try {
2456
2254
  const parsedQuote = QuoteSchema.parse(parameters);
2457
2255
  return {
@@ -2470,7 +2268,7 @@ function from$5(parameters) {
2470
2268
  * @returns The created quote. {@link fromSnakeCase.ReturnType}
2471
2269
  */
2472
2270
  function fromSnakeCase(snake) {
2473
- return from$5(fromSnakeCase$3(snake));
2271
+ return from$6(fromSnakeCase$3(snake));
2474
2272
  }
2475
2273
  /**
2476
2274
  * Generates a random quote.
@@ -2482,7 +2280,7 @@ function fromSnakeCase(snake) {
2482
2280
  * ```
2483
2281
  */
2484
2282
  function random() {
2485
- return from$5({
2283
+ return from$6({
2486
2284
  obligationId: id(random$2()),
2487
2285
  ask: { price: BigInt(int(1e6)) },
2488
2286
  bid: { price: BigInt(int(1e6)) }
@@ -2505,7 +2303,7 @@ var TradingFee_exports = /* @__PURE__ */ __exportAll({
2505
2303
  activate: () => activate,
2506
2304
  compute: () => compute,
2507
2305
  deactivate: () => deactivate,
2508
- from: () => from$4,
2306
+ from: () => from$5,
2509
2307
  getFees: () => getFees,
2510
2308
  isActivated: () => isActivated
2511
2309
  });
@@ -2531,7 +2329,7 @@ const WAD = 10n ** 18n;
2531
2329
  * @throws {@link InvalidFeeError} if any fee exceeds WAD (100%).
2532
2330
  * @throws {@link InvalidFeesLengthError} if fees array doesn't have exactly 6 elements.
2533
2331
  */
2534
- function from$4(activated, fees) {
2332
+ function from$5(activated, fees) {
2535
2333
  if (fees.length !== 6) throw new InvalidFeesLengthError(fees.length);
2536
2334
  for (let i = 0; i < 6; i++) {
2537
2335
  const fee = fees[i];
@@ -2646,7 +2444,7 @@ var InvalidFeesLengthError = class extends BaseError {
2646
2444
 
2647
2445
  //#endregion
2648
2446
  //#region src/core/Transfer.ts
2649
- var Transfer_exports = /* @__PURE__ */ __exportAll({ from: () => from$3 });
2447
+ var Transfer_exports = /* @__PURE__ */ __exportAll({ from: () => from$4 });
2650
2448
  /**
2651
2449
  * @constructor
2652
2450
  *
@@ -2659,7 +2457,7 @@ var Transfer_exports = /* @__PURE__ */ __exportAll({ from: () => from$3 });
2659
2457
  * const transfer = Transfer.from({ id: "1", chainId: 1, contract: "0x123", from: "0x456", to: "0x789", value: 100n, blockNumber: 100n });
2660
2458
  * ```
2661
2459
  */
2662
- function from$3(parameters) {
2460
+ function from$4(parameters) {
2663
2461
  return {
2664
2462
  id: parameters.id,
2665
2463
  chainId: parameters.chainId,
@@ -2671,6 +2469,337 @@ function from$3(parameters) {
2671
2469
  };
2672
2470
  }
2673
2471
 
2472
+ //#endregion
2473
+ //#region src/core/Tree.ts
2474
+ var Tree_exports = /* @__PURE__ */ __exportAll({
2475
+ DecodeError: () => DecodeError,
2476
+ EncodeError: () => EncodeError,
2477
+ SignatureDomainError: () => SignatureDomainError,
2478
+ TreeError: () => TreeError,
2479
+ VERSION: () => VERSION,
2480
+ decode: () => decode,
2481
+ encode: () => encode,
2482
+ encodeUnsigned: () => encodeUnsigned,
2483
+ from: () => from$3,
2484
+ proofs: () => proofs,
2485
+ signatureDomain: () => signatureDomain,
2486
+ signatureTypes: () => signatureTypes
2487
+ });
2488
+ const VERSION = 1;
2489
+ /**
2490
+ * EIP-712 types for signing the tree root (Root(bytes32 root)).
2491
+ */
2492
+ const signatureTypes = {
2493
+ EIP712Domain: [{
2494
+ name: "chainId",
2495
+ type: "uint256"
2496
+ }, {
2497
+ name: "verifyingContract",
2498
+ type: "address"
2499
+ }],
2500
+ Root: [{
2501
+ name: "root",
2502
+ type: "bytes32"
2503
+ }]
2504
+ };
2505
+ const normalizeHash = (hash) => hash.toLowerCase();
2506
+ /**
2507
+ * Builds a Merkle tree from a list of offers.
2508
+ *
2509
+ * Leaves are the offer `hash` values as `bytes32` and are deterministically
2510
+ * ordered following the StandardMerkleTree leaf ordering so that the resulting
2511
+ * root is stable regardless of the input order.
2512
+ *
2513
+ * @param offers - Offers to include in the tree.
2514
+ * @returns A `StandardMerkleTree` of `bytes32` leaves representing the offers.
2515
+ * @throws {TreeError} If tree building fails due to offer inconsistencies.
2516
+ */
2517
+ const from$3 = (offers) => {
2518
+ const leaves = offers.map((offer) => [hash(offer)]);
2519
+ const tree = _openzeppelin_merkle_tree.StandardMerkleTree.of(leaves, ["bytes32"]);
2520
+ const orderedOffers = orderOffers(tree, offers);
2521
+ return Object.assign(tree, { offers: orderedOffers });
2522
+ };
2523
+ const orderOffers = (tree, offers) => {
2524
+ const offerByHash = /* @__PURE__ */ new Map();
2525
+ for (const offer of offers) offerByHash.set(normalizeHash(hash(offer)), offer);
2526
+ const entries = tree.dump().values.map((value) => {
2527
+ const hash = normalizeHash(value.value[0]);
2528
+ const offer = offerByHash.get(hash);
2529
+ if (!offer) throw new TreeError(`missing offer for leaf ${hash}`);
2530
+ return {
2531
+ offer,
2532
+ treeIndex: value.treeIndex
2533
+ };
2534
+ });
2535
+ entries.sort((a, b) => b.treeIndex - a.treeIndex);
2536
+ return entries.map((item) => item.offer);
2537
+ };
2538
+ /**
2539
+ * Generates merkle proofs for all offers in a tree.
2540
+ *
2541
+ * Each proof allows independent verification that an offer is included in the tree
2542
+ * without requiring the full tree. Proofs are ordered by StandardMerkleTree leaf ordering.
2543
+ *
2544
+ * @param tree - The {@link Tree} to generate proofs for.
2545
+ * @returns Array of proofs - {@link Proof}
2546
+ */
2547
+ const proofs = (tree) => {
2548
+ return tree.offers.map((offer) => {
2549
+ return {
2550
+ offer,
2551
+ path: tree.getProof([hash(offer)])
2552
+ };
2553
+ });
2554
+ };
2555
+ /**
2556
+ * Normalizes a Root signature domain (BigInt chain id, lowercase address).
2557
+ * @throws {SignatureDomainError} When the domain is invalid.
2558
+ */
2559
+ const signatureDomain = (domain) => {
2560
+ return normalizeSignatureDomain(domain, (reason) => new SignatureDomainError(reason));
2561
+ };
2562
+ const normalizeSignatureDomain = (domain, errorFactory) => {
2563
+ let chainId;
2564
+ try {
2565
+ chainId = typeof domain.chainId === "bigint" ? domain.chainId : BigInt(domain.chainId);
2566
+ } catch {
2567
+ throw errorFactory("invalid chainId");
2568
+ }
2569
+ if (chainId < 0n) throw errorFactory("invalid chainId");
2570
+ if (!(0, viem.isAddress)(domain.verifyingContract)) throw errorFactory("invalid verifyingContract");
2571
+ return {
2572
+ chainId,
2573
+ verifyingContract: domain.verifyingContract.toLowerCase()
2574
+ };
2575
+ };
2576
+ const assertHex = (value, expectedBytes, name, errorFactory = (reason) => new DecodeError(reason)) => {
2577
+ if (typeof value !== "string" || !(0, viem.isHex)(value)) throw errorFactory(`${name} is not a valid hex string`);
2578
+ if ((0, viem.hexToBytes)(value).length !== expectedBytes) throw errorFactory(`${name}: expected ${expectedBytes} bytes`);
2579
+ };
2580
+ const verifySignatureAndRecoverAddress = async (params) => {
2581
+ const { root, signature, domain, errorFactory } = params;
2582
+ assertHex(root, 32, "root", errorFactory);
2583
+ assertHex(signature, 65, "signature", errorFactory);
2584
+ const hash = (0, viem.hashTypedData)({
2585
+ domain,
2586
+ types: signatureTypes,
2587
+ primaryType: "Root",
2588
+ message: { root }
2589
+ });
2590
+ try {
2591
+ return await (0, viem.recoverAddress)({
2592
+ hash,
2593
+ signature
2594
+ });
2595
+ } catch {
2596
+ throw errorFactory("signature recovery failed");
2597
+ }
2598
+ };
2599
+ /**
2600
+ * Encodes a merkle tree with signature into hex calldata for onchain broadcast.
2601
+ *
2602
+ * Layout: `0x{vv}{gzip([...offers])}{root}{signature}` where:
2603
+ * - `{vv}`: 1-byte version (currently 0x01)
2604
+ * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
2605
+ * - `{root}`: 32-byte merkle root
2606
+ * - `{signature}`: 65-byte EIP-712 signature over Root(bytes32 root)
2607
+ *
2608
+ * Validates signature authenticity and root integrity before encoding.
2609
+ *
2610
+ * @example
2611
+ * ```typescript
2612
+ * const tree = Tree.from(offers);
2613
+ * const signature = await wallet.signTypedData({
2614
+ * account: wallet.account,
2615
+ * domain: Tree.signatureDomain({ chainId, verifyingContract }),
2616
+ * types: Tree.signatureTypes,
2617
+ * primaryType: "Root",
2618
+ * message: { root: tree.root },
2619
+ * });
2620
+ * const calldata = await Tree.encode(tree, signature, { chainId, verifyingContract });
2621
+ * await broadcast(calldata);
2622
+ * ```
2623
+ *
2624
+ * @example
2625
+ * Manual construction (for advanced users):
2626
+ * ```typescript
2627
+ * const tree = Tree.from(offers);
2628
+ * const compressed = gzip(JSON.stringify(tree.offers.map(Offer.serialize)));
2629
+ * const partial = `0x01${bytesToHex(compressed)}${tree.root.slice(2)}`;
2630
+ * const signature = await wallet.signTypedData({
2631
+ * account: wallet.account,
2632
+ * domain: Tree.signatureDomain({ chainId, verifyingContract }),
2633
+ * types: Tree.signatureTypes,
2634
+ * primaryType: "Root",
2635
+ * message: { root: tree.root },
2636
+ * });
2637
+ * const calldata = `${partial}${signature.slice(2)}`;
2638
+ * ```
2639
+ *
2640
+ * @param tree - Merkle tree of offers
2641
+ * @param signature - EIP-712 signature over Root(bytes32 root)
2642
+ * @param domain - EIP-712 domain with chain id and verifying contract
2643
+ * @returns Hex-encoded calldata ready for onchain broadcast
2644
+ * @throws {EncodeError} If signature verification fails or root mismatch
2645
+ */
2646
+ const encode = async (tree, signature, domain) => {
2647
+ const errorFactory = (reason) => new EncodeError(reason);
2648
+ const normalizedDomain = normalizeSignatureDomain(domain, errorFactory);
2649
+ validateTreeForEncoding(tree, normalizedDomain);
2650
+ await verifySignatureAndRecoverAddress({
2651
+ root: tree.root,
2652
+ signature,
2653
+ domain: normalizedDomain,
2654
+ errorFactory
2655
+ });
2656
+ const unsigned = encodeUnsignedBytes(tree);
2657
+ const sigBytes = (0, viem.hexToBytes)(signature);
2658
+ const encoded = new Uint8Array(unsigned.length + sigBytes.length);
2659
+ encoded.set(unsigned, 0);
2660
+ encoded.set(sigBytes, unsigned.length);
2661
+ return (0, viem.bytesToHex)(encoded);
2662
+ };
2663
+ /**
2664
+ * Encodes a merkle tree without a signature into hex payload for client-side signing.
2665
+ *
2666
+ * Layout: `0x{vv}{gzip([...offers])}{root}` where:
2667
+ * - `{vv}`: 1-byte version (currently 0x01)
2668
+ * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
2669
+ * - `{root}`: 32-byte merkle root
2670
+ *
2671
+ * Validates root integrity before encoding.
2672
+ *
2673
+ * @param tree - Merkle tree of offers
2674
+ * @returns Hex-encoded unsigned payload
2675
+ * @throws {EncodeError} If root mismatch
2676
+ */
2677
+ const encodeUnsigned = (tree) => {
2678
+ validateTreeForEncoding(tree);
2679
+ return (0, viem.bytesToHex)(encodeUnsignedBytes(tree));
2680
+ };
2681
+ const validateTreeForEncoding = (tree, domain) => {
2682
+ if (VERSION > 255) throw new EncodeError(`version overflow: ${VERSION} exceeds 255`);
2683
+ const computed = from$3(tree.offers);
2684
+ if (tree.root !== computed.root) throw new EncodeError(`root mismatch: expected ${computed.root}, got ${tree.root}`);
2685
+ if (domain) {
2686
+ const mismatched = tree.offers.find((offer) => BigInt(offer.chainId) !== domain.chainId);
2687
+ if (mismatched) throw new EncodeError(`chainId mismatch: expected ${domain.chainId}, got ${mismatched.chainId}`);
2688
+ }
2689
+ };
2690
+ const encodeUnsignedBytes = (tree) => {
2691
+ const offersPayload = tree.offers.map(serialize);
2692
+ const compressed = (0, pako.gzip)(JSON.stringify(offersPayload));
2693
+ const rootBytes = (0, viem.hexToBytes)(tree.root);
2694
+ const encoded = new Uint8Array(1 + compressed.length + 32);
2695
+ encoded[0] = VERSION;
2696
+ encoded.set(compressed, 1);
2697
+ encoded.set(rootBytes, 1 + compressed.length);
2698
+ return encoded;
2699
+ };
2700
+ /**
2701
+ * Decodes hex calldata into a validated merkle tree.
2702
+ *
2703
+ * Validates signature before decompression for fail-fast rejection of invalid payloads.
2704
+ * Returns the tree with separately validated signature and recovered signer address.
2705
+ *
2706
+ * Validation order:
2707
+ * 1. Version check
2708
+ * 2. Signature verification (fail-fast, before decompression)
2709
+ * 3. Decompression (only if signature valid)
2710
+ * 4. Root verification (computed from offers vs embedded root)
2711
+ *
2712
+ * @example
2713
+ * ```typescript
2714
+ * const { tree, signature, signer } = await Tree.decode(calldata, { chainId, verifyingContract });
2715
+ * console.log(`Tree signed by ${signer} with ${tree.offers.length} offers`);
2716
+ * ```
2717
+ *
2718
+ * @param encoded - Hex calldata in format `0x{vv}{gzip}{root}{signature}`
2719
+ * @param domain - EIP-712 domain with chain id and verifying contract
2720
+ * @returns Validated tree, signature, and recovered signer address
2721
+ * @throws {DecodeError} If version invalid, signature invalid, or root mismatch
2722
+ */
2723
+ const decode = async (encoded, domain) => {
2724
+ const errorFactory = (reason) => new DecodeError(reason);
2725
+ const normalizedDomain = normalizeSignatureDomain(domain, errorFactory);
2726
+ const bytes = (0, viem.hexToBytes)(encoded);
2727
+ if (bytes.length < 98) throw new DecodeError("payload too short");
2728
+ const version = bytes[0];
2729
+ if (version !== (VERSION & 255)) throw new DecodeError(`invalid version: expected ${VERSION}, got ${version ?? 0}`);
2730
+ const signature = (0, viem.bytesToHex)(bytes.slice(-65));
2731
+ const root = (0, viem.bytesToHex)(bytes.slice(-97, -65));
2732
+ assertHex(root, 32, "root");
2733
+ assertHex(signature, 65, "signature");
2734
+ const signer = await verifySignatureAndRecoverAddress({
2735
+ root,
2736
+ signature,
2737
+ domain: normalizedDomain,
2738
+ errorFactory
2739
+ });
2740
+ const compressed = bytes.slice(1, -97);
2741
+ let decoded;
2742
+ try {
2743
+ decoded = (0, pako.ungzip)(compressed, { to: "string" });
2744
+ } catch {
2745
+ throw new DecodeError("decompression failed");
2746
+ }
2747
+ let rawOffers;
2748
+ try {
2749
+ rawOffers = JSON.parse(decoded);
2750
+ } catch {
2751
+ throw new DecodeError("JSON parse failed");
2752
+ }
2753
+ const tree = from$3(rawOffers.map((o) => OfferSchema().parse(o)));
2754
+ if (root !== tree.root) throw new DecodeError(`root mismatch: expected ${tree.root}, got ${root}`);
2755
+ const chainIdMismatch = tree.offers.find((offer) => BigInt(offer.chainId) !== normalizedDomain.chainId);
2756
+ if (chainIdMismatch) throw new DecodeError(`chainId mismatch: expected ${normalizedDomain.chainId}, got ${chainIdMismatch.chainId}`);
2757
+ return {
2758
+ tree,
2759
+ signature,
2760
+ signer
2761
+ };
2762
+ };
2763
+ /**
2764
+ * Error thrown during tree building operations.
2765
+ * Indicates structural issues with the tree (missing offers, inconsistent state).
2766
+ */
2767
+ var TreeError = class extends BaseError {
2768
+ constructor(reason) {
2769
+ super(`Tree error: ${reason}`);
2770
+ _defineProperty(this, "name", "Tree.TreeError");
2771
+ }
2772
+ };
2773
+ /**
2774
+ * Error thrown during tree encoding.
2775
+ * Indicates validation failures (signature, root mismatch, mixed makers).
2776
+ */
2777
+ var EncodeError = class extends BaseError {
2778
+ constructor(reason) {
2779
+ super(`Failed to encode tree: ${reason}`);
2780
+ _defineProperty(this, "name", "Tree.EncodeError");
2781
+ }
2782
+ };
2783
+ /**
2784
+ * Error thrown during tree decoding.
2785
+ * Indicates payload corruption, version mismatch, or validation failures.
2786
+ */
2787
+ var DecodeError = class extends BaseError {
2788
+ constructor(reason) {
2789
+ super(`Failed to decode tree: ${reason}`);
2790
+ _defineProperty(this, "name", "Tree.DecodeError");
2791
+ }
2792
+ };
2793
+ /**
2794
+ * Error thrown when an invalid signature domain is supplied.
2795
+ */
2796
+ var SignatureDomainError = class extends BaseError {
2797
+ constructor(reason) {
2798
+ super(`Invalid signature domain: ${reason}`);
2799
+ _defineProperty(this, "name", "Tree.SignatureDomainError");
2800
+ }
2801
+ };
2802
+
2674
2803
  //#endregion
2675
2804
  //#region src/core/types.ts
2676
2805
  const BrandTypeId = Symbol.for("mempool/Brand");
@@ -2836,6 +2965,21 @@ const validateOfferExample = {
2836
2965
  data: "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000034cf890db685fc536e05652fb41f02090c3fb751000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000108e644e3ab01184155270aa92a00000000000"
2837
2966
  }
2838
2967
  };
2968
+ const callbackTypesRequestExample = { callbacks: [{
2969
+ chain_id: 1,
2970
+ addresses: [
2971
+ "0x1111111111111111111111111111111111111111",
2972
+ "0x3333333333333333333333333333333333333333",
2973
+ "0x9999999999999999999999999999999999999999"
2974
+ ]
2975
+ }] };
2976
+ const callbackTypesResponseExample = [{
2977
+ chain_id: 1,
2978
+ sell_erc20_callback: ["0x1111111111111111111111111111111111111111"],
2979
+ buy_erc20: ["0x5555555555555555555555555555555555555555"],
2980
+ buy_vault_v1_callback: ["0x3333333333333333333333333333333333333333"],
2981
+ not_supported: ["0x9999999999999999999999999999999999999999"]
2982
+ }];
2839
2983
  const routerStatusExample = {
2840
2984
  status: "live",
2841
2985
  initialized: true,
@@ -2904,6 +3048,55 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
2904
3048
  type: "string",
2905
3049
  example: validateOfferExample.callback.data
2906
3050
  })], ValidateCallbackRequest.prototype, "data", void 0);
3051
+ var CallbackTypesChainRequest = class {};
3052
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3053
+ type: "number",
3054
+ example: callbackTypesRequestExample.callbacks[0].chain_id
3055
+ })], CallbackTypesChainRequest.prototype, "chain_id", void 0);
3056
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3057
+ type: () => [String],
3058
+ example: callbackTypesRequestExample.callbacks[0].addresses
3059
+ })], CallbackTypesChainRequest.prototype, "addresses", void 0);
3060
+ var CallbackTypesRequest = class {};
3061
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3062
+ type: () => [CallbackTypesChainRequest],
3063
+ example: callbackTypesRequestExample.callbacks
3064
+ })], CallbackTypesRequest.prototype, "callbacks", void 0);
3065
+ var CallbackTypesChainResponse = class {};
3066
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3067
+ type: "number",
3068
+ example: callbackTypesResponseExample[0].chain_id
3069
+ })], CallbackTypesChainResponse.prototype, "chain_id", void 0);
3070
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3071
+ type: () => [String],
3072
+ required: false,
3073
+ example: callbackTypesResponseExample[0].buy_vault_v1_callback
3074
+ })], CallbackTypesChainResponse.prototype, "buy_vault_v1_callback", void 0);
3075
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3076
+ type: () => [String],
3077
+ required: false,
3078
+ example: callbackTypesResponseExample[0].sell_erc20_callback
3079
+ })], CallbackTypesChainResponse.prototype, "sell_erc20_callback", void 0);
3080
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3081
+ type: () => [String],
3082
+ required: false,
3083
+ example: callbackTypesResponseExample[0].buy_erc20
3084
+ })], CallbackTypesChainResponse.prototype, "buy_erc20", void 0);
3085
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3086
+ type: () => [String],
3087
+ example: callbackTypesResponseExample[0].not_supported
3088
+ })], CallbackTypesChainResponse.prototype, "not_supported", void 0);
3089
+ var CallbackTypesSuccessResponse = class extends SuccessResponse {};
3090
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3091
+ type: "string",
3092
+ nullable: true,
3093
+ example: "maturity:1:1730415600:end_of_next_month"
3094
+ })], CallbackTypesSuccessResponse.prototype, "cursor", void 0);
3095
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3096
+ type: () => [CallbackTypesChainResponse],
3097
+ description: "Callback types grouped by chain.",
3098
+ example: callbackTypesResponseExample
3099
+ })], CallbackTypesSuccessResponse.prototype, "data", void 0);
2907
3100
  var AskResponse = class {};
2908
3101
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
2909
3102
  type: "string",
@@ -3068,7 +3261,8 @@ var OfferListResponse = class extends SuccessResponse {};
3068
3261
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3069
3262
  type: "string",
3070
3263
  nullable: true,
3071
- example: offerCursorExample
3264
+ example: offerCursorExample,
3265
+ description: "Pagination cursor. Offer hash (0x...) for maker queries; base64url-encoded cursor for obligation queries."
3072
3266
  })], OfferListResponse.prototype, "cursor", void 0);
3073
3267
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3074
3268
  type: () => [OfferListItemResponse],
@@ -3439,6 +3633,28 @@ ValidateController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"
3439
3633
  description: "Bad Request",
3440
3634
  type: BadRequestResponse
3441
3635
  })], ValidateController);
3636
+ let CallbacksController = class CallbacksController {
3637
+ async resolveCallbackTypes() {}
3638
+ };
3639
+ __decorate([
3640
+ (0, openapi_metadata_decorators.ApiOperation)({
3641
+ methods: ["post"],
3642
+ path: "/v1/callbacks",
3643
+ summary: "Resolve callback types",
3644
+ description: "Returns callback types for callback addresses grouped by chain."
3645
+ }),
3646
+ (0, openapi_metadata_decorators.ApiBody)({ type: CallbackTypesRequest }),
3647
+ (0, openapi_metadata_decorators.ApiResponse)({
3648
+ status: 200,
3649
+ description: "Success",
3650
+ type: CallbackTypesSuccessResponse
3651
+ })
3652
+ ], CallbacksController.prototype, "resolveCallbackTypes", null);
3653
+ CallbacksController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"), (0, openapi_metadata_decorators.ApiResponse)({
3654
+ status: 400,
3655
+ description: "Bad Request",
3656
+ type: BadRequestResponse
3657
+ })], CallbacksController);
3442
3658
  let OffersController = class OffersController {
3443
3659
  async getOffers() {}
3444
3660
  };
@@ -3560,54 +3776,227 @@ __decorate([
3560
3776
  })
3561
3777
  ], HealthController.prototype, "getChainsHealth", null);
3562
3778
  HealthController = __decorate([(0, openapi_metadata_decorators.ApiTags)("System")], HealthController);
3563
- const callbacksExample = [Type$1.BuyWithEmptyCallback];
3564
- const chainConfigExample = {
3779
+ const configContractsExample = {
3565
3780
  chain_id: 505050505,
3566
- contracts: { mempool: "0xD946246695A9259F3B33a78629026F61B3Ab40aF" },
3567
- callbacks: callbacksExample
3781
+ address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
3782
+ name: "mempool"
3783
+ };
3784
+ const configContractsPayloadExample = [
3785
+ {
3786
+ chain_id: 505050505,
3787
+ address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
3788
+ name: "mempool"
3789
+ },
3790
+ {
3791
+ chain_id: 505050505,
3792
+ address: "0x8A409D5D6394fC197c596d4E6E2c35e5d13f8a4d",
3793
+ name: "multicall"
3794
+ },
3795
+ {
3796
+ chain_id: 505050505,
3797
+ address: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
3798
+ name: "v2"
3799
+ }
3800
+ ];
3801
+ const configRulesMaturityExample = {
3802
+ type: "maturity",
3803
+ chain_id: 1,
3804
+ name: "end_of_next_month",
3805
+ timestamp: 1730415600
3806
+ };
3807
+ const configRulesCallbackExample = {
3808
+ type: "callback",
3809
+ chain_id: 1,
3810
+ address: "0x1111111111111111111111111111111111111111",
3811
+ callback_type: "sell_erc20_callback"
3812
+ };
3813
+ const configRulesLoanTokenExample = {
3814
+ type: "loan_token",
3815
+ chain_id: 1,
3816
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3817
+ };
3818
+ const configRulesOracleExample = {
3819
+ type: "oracle",
3820
+ chain_id: 1,
3821
+ address: "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83"
3568
3822
  };
3569
- var ConfigContractsResponse = class {};
3823
+ const configRulesChecksumExample = "f1d2d2f924e986ac86fdf7b36c94bcdf";
3824
+ const configRulesPayloadExample = [
3825
+ configRulesMaturityExample,
3826
+ configRulesCallbackExample,
3827
+ configRulesLoanTokenExample,
3828
+ configRulesOracleExample
3829
+ ];
3830
+ const configContractNames = [
3831
+ "mempool",
3832
+ "multicall",
3833
+ "v2"
3834
+ ];
3835
+ const configContractsCursorExample = "505050505:0xd946246695a9259f3b33a78629026f61b3ab40af";
3836
+ var ConfigContractResponse = class {};
3837
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3838
+ type: "number",
3839
+ example: configContractsExample.chain_id
3840
+ })], ConfigContractResponse.prototype, "chain_id", void 0);
3841
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3842
+ type: "string",
3843
+ example: configContractsExample.address
3844
+ })], ConfigContractResponse.prototype, "address", void 0);
3845
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3846
+ type: "string",
3847
+ enum: configContractNames,
3848
+ example: configContractsExample.name
3849
+ })], ConfigContractResponse.prototype, "name", void 0);
3850
+ var ConfigContractsSuccessResponse = class extends SuccessResponse {};
3570
3851
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3571
3852
  type: "string",
3572
- example: chainConfigExample.contracts.mempool
3573
- })], ConfigContractsResponse.prototype, "mempool", void 0);
3574
- var ConfigDataResponse = class {};
3853
+ nullable: true,
3854
+ example: null
3855
+ })], ConfigContractsSuccessResponse.prototype, "cursor", void 0);
3856
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3857
+ type: () => [ConfigContractResponse],
3858
+ description: "Indexer contract configuration for all indexed chains.",
3859
+ example: configContractsPayloadExample
3860
+ })], ConfigContractsSuccessResponse.prototype, "data", void 0);
3861
+ var ConfigRulesMeta = class {};
3862
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3863
+ type: "string",
3864
+ example: timestampExample
3865
+ })], ConfigRulesMeta.prototype, "timestamp", void 0);
3866
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3867
+ type: "string",
3868
+ example: configRulesChecksumExample
3869
+ })], ConfigRulesMeta.prototype, "checksum", void 0);
3870
+ var ConfigRulesRuleResponse = class {};
3871
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3872
+ type: "string",
3873
+ example: configRulesMaturityExample.type
3874
+ })], ConfigRulesRuleResponse.prototype, "type", void 0);
3575
3875
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3576
3876
  type: "number",
3577
- example: chainConfigExample.chain_id
3578
- })], ConfigDataResponse.prototype, "chain_id", void 0);
3579
- __decorate([(0, openapi_metadata_decorators.ApiProperty)({ type: () => ConfigContractsResponse })], ConfigDataResponse.prototype, "contracts", void 0);
3877
+ example: configRulesMaturityExample.chain_id
3878
+ })], ConfigRulesRuleResponse.prototype, "chain_id", void 0);
3580
3879
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3581
- type: () => [String],
3582
- enum: Object.values(Type$1),
3583
- description: "Supported callback types for this chain.",
3584
- example: callbacksExample
3585
- })], ConfigDataResponse.prototype, "callbacks", void 0);
3586
- var ConfigSuccessResponse = class extends SuccessResponse {};
3880
+ type: "string",
3881
+ example: configRulesMaturityExample.name,
3882
+ required: false
3883
+ })], ConfigRulesRuleResponse.prototype, "name", void 0);
3884
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3885
+ type: "number",
3886
+ example: configRulesMaturityExample.timestamp,
3887
+ required: false
3888
+ })], ConfigRulesRuleResponse.prototype, "timestamp", void 0);
3889
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3890
+ type: "string",
3891
+ example: configRulesCallbackExample.address,
3892
+ required: false
3893
+ })], ConfigRulesRuleResponse.prototype, "address", void 0);
3894
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3895
+ type: "string",
3896
+ example: configRulesCallbackExample.callback_type,
3897
+ required: false
3898
+ })], ConfigRulesRuleResponse.prototype, "callback_type", void 0);
3899
+ var ConfigRulesSuccessResponse = class {};
3900
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({ type: () => ConfigRulesMeta })], ConfigRulesSuccessResponse.prototype, "meta", void 0);
3587
3901
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3588
3902
  type: "string",
3589
3903
  nullable: true,
3590
3904
  example: null
3591
- })], ConfigSuccessResponse.prototype, "cursor", void 0);
3592
- __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3593
- type: () => [ConfigDataResponse],
3594
- description: "Array of chain configurations for all indexed chains.",
3595
- example: [chainConfigExample]
3596
- })], ConfigSuccessResponse.prototype, "data", void 0);
3597
- let ConfigController = class ConfigController {
3598
- async getConfig() {}
3905
+ })], ConfigRulesSuccessResponse.prototype, "cursor", void 0);
3906
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
3907
+ type: () => [ConfigRulesRuleResponse],
3908
+ description: "Configured rules returned by the router API.",
3909
+ example: configRulesPayloadExample
3910
+ })], ConfigRulesSuccessResponse.prototype, "data", void 0);
3911
+ let ConfigContractsController = class ConfigContractsController {
3912
+ async getConfigContracts() {}
3913
+ };
3914
+ __decorate([
3915
+ (0, openapi_metadata_decorators.ApiOperation)({
3916
+ methods: ["get"],
3917
+ path: "/v1/config/contracts",
3918
+ summary: "Get indexer contract configuration",
3919
+ description: "Returns contract addresses used by indexers (mempool, v2) and multicall for indexed chains."
3920
+ }),
3921
+ (0, openapi_metadata_decorators.ApiQuery)({
3922
+ name: "cursor",
3923
+ type: "string",
3924
+ required: false,
3925
+ example: configContractsCursorExample,
3926
+ description: "Pagination cursor in chain_id:address format (lowercase address)."
3927
+ }),
3928
+ (0, openapi_metadata_decorators.ApiQuery)({
3929
+ name: "limit",
3930
+ type: "number",
3931
+ required: false,
3932
+ example: 1e3,
3933
+ description: "Maximum number of contracts to return (max 1000)."
3934
+ }),
3935
+ (0, openapi_metadata_decorators.ApiQuery)({
3936
+ name: "chains",
3937
+ type: ["number"],
3938
+ required: false,
3939
+ example: "1,8453",
3940
+ description: "Filter by chain IDs (comma-separated).",
3941
+ style: "form",
3942
+ explode: false
3943
+ }),
3944
+ (0, openapi_metadata_decorators.ApiResponse)({
3945
+ status: 200,
3946
+ description: "Success",
3947
+ type: ConfigContractsSuccessResponse
3948
+ })
3949
+ ], ConfigContractsController.prototype, "getConfigContracts", null);
3950
+ ConfigContractsController = __decorate([(0, openapi_metadata_decorators.ApiTags)("System")], ConfigContractsController);
3951
+ let ConfigRulesController = class ConfigRulesController {
3952
+ async getConfigRules() {}
3599
3953
  };
3600
- __decorate([(0, openapi_metadata_decorators.ApiOperation)({
3601
- methods: ["get"],
3602
- path: "/v1/config",
3603
- summary: "Get router configuration",
3604
- description: "Returns chain configurations including contract addresses and supported callback types."
3605
- }), (0, openapi_metadata_decorators.ApiResponse)({
3606
- status: 200,
3607
- description: "Success",
3608
- type: ConfigSuccessResponse
3609
- })], ConfigController.prototype, "getConfig", null);
3610
- ConfigController = __decorate([(0, openapi_metadata_decorators.ApiTags)("System")], ConfigController);
3954
+ __decorate([
3955
+ (0, openapi_metadata_decorators.ApiOperation)({
3956
+ methods: ["get"],
3957
+ path: "/v1/config/rules",
3958
+ summary: "Get config rules",
3959
+ description: "Returns configured rules for supported chains."
3960
+ }),
3961
+ (0, openapi_metadata_decorators.ApiQuery)({
3962
+ name: "cursor",
3963
+ type: "string",
3964
+ required: false,
3965
+ example: "maturity:1:1730415600:end_of_next_month",
3966
+ description: "Pagination cursor in type:chain_id:<value> format."
3967
+ }),
3968
+ (0, openapi_metadata_decorators.ApiQuery)({
3969
+ name: "limit",
3970
+ type: "number",
3971
+ required: false,
3972
+ example: 100,
3973
+ description: "Maximum number of rules to return (max 1000)."
3974
+ }),
3975
+ (0, openapi_metadata_decorators.ApiQuery)({
3976
+ name: "types",
3977
+ type: ["string"],
3978
+ required: false,
3979
+ example: "maturity,loan_token,oracle",
3980
+ description: "Filter by rule types (comma-separated).",
3981
+ style: "form",
3982
+ explode: false
3983
+ }),
3984
+ (0, openapi_metadata_decorators.ApiQuery)({
3985
+ name: "chains",
3986
+ type: ["number"],
3987
+ required: false,
3988
+ example: "1,8453",
3989
+ description: "Filter by chain IDs (comma-separated).",
3990
+ style: "form",
3991
+ explode: false
3992
+ }),
3993
+ (0, openapi_metadata_decorators.ApiResponse)({
3994
+ status: 200,
3995
+ description: "Success",
3996
+ type: ConfigRulesSuccessResponse
3997
+ })
3998
+ ], ConfigRulesController.prototype, "getConfigRules", null);
3999
+ ConfigRulesController = __decorate([(0, openapi_metadata_decorators.ApiTags)("System")], ConfigRulesController);
3611
4000
  let ObligationsController = class ObligationsController {
3612
4001
  async getObligations() {}
3613
4002
  async getObligation() {}
@@ -3736,16 +4125,18 @@ UsersController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"),
3736
4125
  description: "Bad Request",
3737
4126
  type: BadRequestResponse
3738
4127
  })], UsersController);
3739
- const OpenApi = async (options = {}) => {
3740
- const document = await (0, openapi_metadata.generateDocument)({
4128
+ const OpenApi = async () => {
4129
+ return await (0, openapi_metadata.generateDocument)({
3741
4130
  controllers: [
3742
4131
  BooksController,
3743
- ConfigController,
4132
+ ConfigContractsController,
4133
+ ConfigRulesController,
3744
4134
  OffersController,
3745
4135
  ObligationsController,
3746
4136
  HealthController,
3747
4137
  UsersController,
3748
- ValidateController
4138
+ ValidateController,
4139
+ CallbacksController
3749
4140
  ],
3750
4141
  document: {
3751
4142
  openapi: "3.1.0",
@@ -3777,12 +4168,6 @@ const OpenApi = async (options = {}) => {
3777
4168
  ]
3778
4169
  }
3779
4170
  });
3780
- if (options.rules && options.rules.length > 0) {
3781
- const rulesDescription = options.rules.map((rule) => `- **${rule.name}**: ${rule.description}`).join("\n");
3782
- const validatePath = document.paths?.["/v1/validate"];
3783
- 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}`;
3784
- }
3785
- return document;
3786
4171
  };
3787
4172
 
3788
4173
  //#endregion
@@ -3807,6 +4192,10 @@ function from$1(position) {
3807
4192
  //#region src/api/Schema/requests.ts
3808
4193
  const MAX_LIMIT = 100;
3809
4194
  const DEFAULT_LIMIT = 20;
4195
+ const CONFIG_RULES_MAX_LIMIT = 1e3;
4196
+ const CONFIG_RULES_DEFAULT_LIMIT = 100;
4197
+ const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
4198
+ const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
3810
4199
  /** Validate cursor is a valid base64url-encoded JSON object.
3811
4200
  * Domain layer handles semantic validation of cursor fields. */
3812
4201
  function isValidBase64urlJson(val) {
@@ -3818,6 +4207,9 @@ function isValidBase64urlJson(val) {
3818
4207
  return false;
3819
4208
  }
3820
4209
  }
4210
+ function isValidOfferHashCursor(val) {
4211
+ return /^0x[a-f0-9]{64}$/i.test(val);
4212
+ }
3821
4213
  const csvArray = (schema) => zod.preprocess((value) => {
3822
4214
  if (value === void 0) return void 0;
3823
4215
  if (Array.isArray(value)) {
@@ -3840,8 +4232,49 @@ const PaginationQueryParams = zod.object({
3840
4232
  example: 10
3841
4233
  })
3842
4234
  });
3843
- const GetOffersQueryParams = zod.object({
3844
- ...PaginationQueryParams.shape,
4235
+ const ConfigRuleTypes = zod.enum([
4236
+ "maturity",
4237
+ "callback",
4238
+ "loan_token",
4239
+ "oracle"
4240
+ ]);
4241
+ const GetConfigRulesQueryParams = zod.object({
4242
+ cursor: zod.string().regex(/^(maturity|callback|loan_token|oracle):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
4243
+ description: "Pagination cursor in type:chain_id:<value> format",
4244
+ example: "maturity:1:1730415600:end_of_next_month"
4245
+ }),
4246
+ limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(CONFIG_RULES_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_RULES_MAX_LIMIT}` })).optional().default(CONFIG_RULES_DEFAULT_LIMIT).meta({
4247
+ description: `Limit maximum: ${CONFIG_RULES_MAX_LIMIT}. Default: ${CONFIG_RULES_DEFAULT_LIMIT}`,
4248
+ example: 100
4249
+ }),
4250
+ types: csvArray(ConfigRuleTypes).meta({
4251
+ description: "Filter by rule types (comma-separated).",
4252
+ example: "maturity,loan_token,oracle"
4253
+ }),
4254
+ chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
4255
+ description: "Filter by chain IDs (comma-separated).",
4256
+ example: "1,8453"
4257
+ })
4258
+ });
4259
+ const GetConfigContractsQueryParams = zod.object({
4260
+ cursor: zod.string().regex(/^[1-9]\d*:0x[a-fA-F0-9]{40}$/, { message: "Cursor must be in the format chain_id:0x..." }).optional().meta({
4261
+ description: "Pagination cursor in chain_id:address format (lowercase address).",
4262
+ example: "1:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
4263
+ }),
4264
+ limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(CONFIG_CONTRACTS_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_CONTRACTS_MAX_LIMIT}` })).optional().default(CONFIG_CONTRACTS_DEFAULT_LIMIT).meta({
4265
+ description: `Limit maximum: ${CONFIG_CONTRACTS_MAX_LIMIT}. Default: ${CONFIG_CONTRACTS_DEFAULT_LIMIT}`,
4266
+ example: 1e3
4267
+ }),
4268
+ chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
4269
+ description: "Filter by chain IDs (comma-separated).",
4270
+ example: "1,8453"
4271
+ })
4272
+ });
4273
+ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend({
4274
+ cursor: zod.string().optional().meta({
4275
+ description: "Pagination cursor. Use offer hash (0x...) for maker queries, base64url for obligation queries.",
4276
+ example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
4277
+ }),
3845
4278
  side: zod.enum(["buy", "sell"]).optional().meta({
3846
4279
  description: "Side of the offer. Required when using obligation_id.",
3847
4280
  example: "buy"
@@ -3865,11 +4298,29 @@ const GetOffersQueryParams = zod.object({
3865
4298
  });
3866
4299
  return;
3867
4300
  }
3868
- if (hasMaker) return;
4301
+ if (hasMaker) {
4302
+ if (val.cursor !== void 0 && !isValidOfferHashCursor(val.cursor)) ctx.addIssue({
4303
+ code: "custom",
4304
+ path: ["cursor"],
4305
+ message: "Cursor must be a 32-byte hex offer hash when filtering by maker"
4306
+ });
4307
+ return;
4308
+ }
3869
4309
  if (!hasObligation || !hasSide) ctx.addIssue({
3870
4310
  code: "custom",
3871
4311
  message: "Must provide either maker or both obligation_id and side"
3872
4312
  });
4313
+ if (val.cursor !== void 0 && !isValidBase64urlJson(val.cursor)) ctx.addIssue({
4314
+ code: "custom",
4315
+ path: ["cursor"],
4316
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
4317
+ });
4318
+ }).transform((val) => {
4319
+ if (val.maker && val.cursor) return {
4320
+ ...val,
4321
+ cursor: val.cursor.toLowerCase()
4322
+ };
4323
+ return val;
3873
4324
  });
3874
4325
  const GetObligationsQueryParams = zod.object({
3875
4326
  ...PaginationQueryParams.shape,
@@ -3942,6 +4393,16 @@ const GetBookParams = zod.object({
3942
4393
  })
3943
4394
  });
3944
4395
  const ValidateOffersBody = zod.object({ offers: zod.array(zod.unknown()).min(1, { message: "'offers' must contain at least 1 offer" }) }).strict();
4396
+ const CallbackTypesBody = zod.object({ callbacks: zod.array(zod.object({
4397
+ chain_id: zod.number().int().positive().meta({
4398
+ description: "Chain id.",
4399
+ example: 1
4400
+ }),
4401
+ addresses: zod.array(zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Callback address must be a valid 20-byte address" }).transform((val) => val.toLowerCase())).meta({
4402
+ description: "Callback contract addresses.",
4403
+ example: ["0x1111111111111111111111111111111111111111", "0x3333333333333333333333333333333333333333"]
4404
+ })
4405
+ }).strict()) }).strict();
3945
4406
  const GetUserPositionsParams = zod.object({
3946
4407
  ...PaginationQueryParams.shape,
3947
4408
  user_address: zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "User address must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).meta({
@@ -3953,11 +4414,14 @@ const schemas = {
3953
4414
  get_health: HealthQueryParams,
3954
4415
  get_health_collectors: HealthQueryParams,
3955
4416
  get_health_chains: HealthQueryParams,
4417
+ get_config_contracts: GetConfigContractsQueryParams,
4418
+ get_config_rules: GetConfigRulesQueryParams,
3956
4419
  get_offers: GetOffersQueryParams,
3957
4420
  get_obligations: GetObligationsQueryParams,
3958
4421
  get_obligation: GetObligationParams,
3959
4422
  get_book: GetBookParams,
3960
4423
  validate_offers: ValidateOffersBody,
4424
+ callback_types: CallbackTypesBody,
3961
4425
  get_user_positions: GetUserPositionsParams
3962
4426
  };
3963
4427
  function parse(action, query) {
@@ -3972,11 +4436,13 @@ function safeParse(action, query, error) {
3972
4436
  var Schema_exports = /* @__PURE__ */ __exportAll({
3973
4437
  BookResponse: () => BookResponse_exports,
3974
4438
  BooksController: () => BooksController,
4439
+ CallbacksController: () => CallbacksController,
3975
4440
  ChainHealth: () => ChainHealth,
3976
4441
  ChainsHealthResponse: () => ChainsHealthResponse,
3977
4442
  CollectorHealth: () => CollectorHealth,
3978
4443
  CollectorsHealthResponse: () => CollectorsHealthResponse,
3979
- ConfigController: () => ConfigController,
4444
+ ConfigContractsController: () => ConfigContractsController,
4445
+ ConfigRulesController: () => ConfigRulesController,
3980
4446
  HealthController: () => HealthController,
3981
4447
  ObligationResponse: () => ObligationResponse_exports,
3982
4448
  ObligationsController: () => ObligationsController,
@@ -4182,12 +4648,14 @@ function createHttpClient(config) {
4182
4648
  const fetchFn = config.fetchFn ?? fetch;
4183
4649
  const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
4184
4650
  const baseUrl = normalizeBaseUrl(config.baseUrl);
4651
+ const baseHeaders = config.originSecret ? { "x-origin-verify": config.originSecret } : void 0;
4185
4652
  const request = async (path, init) => {
4186
4653
  const controller = new AbortController();
4187
4654
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
4188
4655
  try {
4189
4656
  return await fetchFn(`${baseUrl}${path}`, {
4190
4657
  ...init,
4658
+ headers: mergeHeaders(baseHeaders, init.headers),
4191
4659
  signal: controller.signal
4192
4660
  });
4193
4661
  } finally {
@@ -4206,12 +4674,20 @@ function createHttpClient(config) {
4206
4674
  body: json
4207
4675
  };
4208
4676
  };
4209
- const getRules = async () => {
4210
- const response = await request("/v1/rules", { method: "GET" });
4677
+ const getConfigRules = async (query) => {
4678
+ const params = new URLSearchParams();
4679
+ if (query?.cursor) params.set("cursor", query.cursor);
4680
+ if (query?.limit !== void 0) params.set("limit", query.limit.toString());
4681
+ if (query?.types !== void 0) {
4682
+ const typesValue = Array.isArray(query.types) ? query.types.join(",") : query.types;
4683
+ if (typesValue.length > 0) params.set("types", typesValue);
4684
+ }
4685
+ const response = await request(params.size > 0 ? `/v1/config/rules?${params.toString()}` : "/v1/config/rules", { method: "GET" });
4211
4686
  const json = await response.json();
4212
- if (!response.ok) throw new Error(`Gatekeeper rules request failed: ${extractErrorMessage(json) ?? response.statusText}`);
4213
- if (!("data" in json) || !Array.isArray(json.data)) throw new Error("Gatekeeper rules response is invalid.");
4214
- return json.data;
4687
+ return {
4688
+ statusCode: response.status,
4689
+ body: json
4690
+ };
4215
4691
  };
4216
4692
  const isAllowed = async (offers) => {
4217
4693
  const { statusCode, body } = await validate({ offers: offers.map((offer) => toSnakeCase(offer)) });
@@ -4239,13 +4715,31 @@ function createHttpClient(config) {
4239
4715
  issues: []
4240
4716
  };
4241
4717
  };
4718
+ const getCallbackTypes = async (requestPayload) => {
4719
+ const response = await request("/v1/callbacks", {
4720
+ method: "POST",
4721
+ headers: { "content-type": "application/json" },
4722
+ body: JSON.stringify(requestPayload)
4723
+ });
4724
+ const json = await response.json();
4725
+ if (!response.ok) throw new Error(`Gatekeeper callbacks request failed: ${extractErrorMessage(json) ?? response.statusText}`);
4726
+ if (!("data" in json) || !Array.isArray(json.data)) throw new Error("Gatekeeper callbacks response is invalid.");
4727
+ return json.data;
4728
+ };
4242
4729
  return {
4243
4730
  baseUrl,
4244
4731
  validate,
4732
+ getConfigRules,
4245
4733
  isAllowed,
4246
- getRules
4734
+ getCallbackTypes
4247
4735
  };
4248
4736
  }
4737
+ function mergeHeaders(base, extra) {
4738
+ if (!base && !extra) return void 0;
4739
+ const merged = new Headers(base ?? void 0);
4740
+ if (extra) for (const [key, value] of new Headers(extra).entries()) merged.set(key, value);
4741
+ return merged;
4742
+ }
4249
4743
  function normalizeBaseUrl(url) {
4250
4744
  return url.trim().replace(/\/+$/, "");
4251
4745
  }
@@ -4341,16 +4835,26 @@ async function run(parameters) {
4341
4835
  };
4342
4836
  }
4343
4837
 
4838
+ //#endregion
4839
+ //#region src/gatekeeper/Gatekeeper.ts
4840
+ var Gatekeeper_exports = /* @__PURE__ */ __exportAll({ create: () => create });
4841
+ /**
4842
+ * Create a gatekeeper instance with the provided rules.
4843
+ * @param parameters - Gatekeeper parameters. {@link GatekeeperParameters}
4844
+ * @returns Gatekeeper instance. {@link Gatekeeper}
4845
+ */
4846
+ function create(parameters) {
4847
+ const { rules } = parameters;
4848
+ return { isAllowed: async (offers) => {
4849
+ return await run({
4850
+ items: offers,
4851
+ rules
4852
+ });
4853
+ } };
4854
+ }
4855
+
4344
4856
  //#endregion
4345
4857
  //#region src/gatekeeper/GateConfig.ts
4346
- var GateConfig_exports = /* @__PURE__ */ __exportAll({
4347
- assets: () => assets,
4348
- configs: () => configs,
4349
- getCallback: () => getCallback,
4350
- getCallbackAddresses: () => getCallbackAddresses,
4351
- getCallbackType: () => getCallbackType,
4352
- getCallbackTypeAddresses: () => getCallbackTypeAddresses
4353
- });
4354
4858
  /**
4355
4859
  * Returns the callback configuration for a given chain and callback type, if it exists.
4356
4860
  *
@@ -4373,17 +4877,6 @@ function getCallbackType(chain, address) {
4373
4877
  return configs[chain].callbacks?.find((c) => c.type !== Type$1.BuyWithEmptyCallback && c.addresses.includes(address?.toLowerCase()))?.type;
4374
4878
  }
4375
4879
  /**
4376
- * Returns the callback addresses for a given chain and callback type, if it exists.
4377
- * @param chain - Chain name for which to read the validation configuration
4378
- * @param type - Callback type to retrieve
4379
- * @returns The matching callback addresses or an empty array if not configured
4380
- */
4381
- function getCallbackTypeAddresses(chain, type) {
4382
- if (type === Type$1.BuyWithEmptyCallback) return [];
4383
- const match = configs[chain].callbacks?.find((c) => c.type === type);
4384
- return match && "addresses" in match ? match.addresses : [];
4385
- }
4386
- /**
4387
4880
  * Returns the list of allowed non-empty callback addresses for a chain.
4388
4881
  *
4389
4882
  * @param chain - Chain name
@@ -4397,13 +4890,18 @@ const assets = {
4397
4890
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
4398
4891
  "0x6B175474E89094C44Da98b954EedeAC495271d0F",
4399
4892
  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
4400
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
4893
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
4894
+ "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
4895
+ "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
4401
4896
  ],
4402
4897
  [ChainId.BASE.toString()]: [
4403
4898
  "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
4404
4899
  "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
4405
4900
  "0x4200000000000000000000000000000000000006",
4406
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
4901
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
4902
+ "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
4903
+ "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
4904
+ "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
4407
4905
  ],
4408
4906
  [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
4409
4907
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
@@ -4419,6 +4917,43 @@ const assets = {
4419
4917
  "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
4420
4918
  ]
4421
4919
  };
4920
+ const oracles = {
4921
+ [ChainId.ETHEREUM.toString()]: [
4922
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
4923
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
4924
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
4925
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
4926
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
4927
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
4928
+ ],
4929
+ [ChainId.BASE.toString()]: [
4930
+ "0xD09048c8B568Dbf5f189302beA26c9edABFC4858",
4931
+ "0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4",
4932
+ "0x05D2618404668D725B66c0f32B39e4EC15B393dC",
4933
+ "0xE1bb8E5b4930eC9FeC7f7943FCF6227649F14B37",
4934
+ "0x663BECd10daE6C4A3Dcd89F1d76c1174199639B9",
4935
+ "0x10b95702a0ce895972C91e432C4f7E19811D320E",
4936
+ "0x8C87DbD7A0c647A4291592Bc2994dbF95880fE2F",
4937
+ "0x4A11590e5326138B514E08A9B52202D42077Ca65",
4938
+ "0xa54122f0E0766258377Ffe732e454A3248f454F4"
4939
+ ],
4940
+ [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
4941
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
4942
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
4943
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
4944
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
4945
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
4946
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
4947
+ ],
4948
+ [ChainId.ANVIL.toString()]: [
4949
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
4950
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
4951
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
4952
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
4953
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
4954
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
4955
+ ]
4956
+ };
4422
4957
  const configs = {
4423
4958
  ethereum: {
4424
4959
  callbacks: [
@@ -4482,30 +5017,6 @@ const configs = {
4482
5017
  }
4483
5018
  };
4484
5019
 
4485
- //#endregion
4486
- //#region src/gatekeeper/Gatekeeper.ts
4487
- var Gatekeeper_exports = /* @__PURE__ */ __exportAll({ create: () => create });
4488
- /**
4489
- * Create a gatekeeper instance with the provided rules.
4490
- * @param parameters - Gatekeeper parameters. {@link GatekeeperParameters}
4491
- * @returns Gatekeeper instance. {@link Gatekeeper}
4492
- */
4493
- function create(parameters) {
4494
- const { rules } = parameters;
4495
- return {
4496
- isAllowed: async (offers) => {
4497
- return await run({
4498
- items: offers,
4499
- rules
4500
- });
4501
- },
4502
- getRules: async () => rules.map((rule) => ({
4503
- name: rule.name,
4504
- description: rule.description
4505
- }))
4506
- };
4507
- }
4508
-
4509
5020
  //#endregion
4510
5021
  //#region src/gatekeeper/Rules.ts
4511
5022
  var Rules_exports = /* @__PURE__ */ __exportAll({
@@ -4513,6 +5024,7 @@ var Rules_exports = /* @__PURE__ */ __exportAll({
4513
5024
  callback: () => callback,
4514
5025
  chains: () => chains,
4515
5026
  maturity: () => maturity,
5027
+ oracle: () => oracle,
4516
5028
  sameMaker: () => sameMaker,
4517
5029
  token: () => token,
4518
5030
  validity: () => validity
@@ -4651,6 +5163,16 @@ const token = ({ assetsByChainId }) => single("token", "Validates that offer loa
4651
5163
  if (offer.collaterals.some((collateral) => !allowedAssets.includes(collateral.asset.toLowerCase()))) return { message: "Collateral is not allowed" };
4652
5164
  });
4653
5165
  /**
5166
+ * A validation rule that checks if the offer's oracle addresses are allowed for its chain.
5167
+ * @param oraclesByChainId - Allowed oracles indexed by chain id.
5168
+ * @returns The issue that was found. If the offer is valid, this will be undefined.
5169
+ */
5170
+ const oracle = ({ oraclesByChainId }) => single("oracle", "Validates that offer collateral oracles are in the allowed oracle list for the offer chain", (offer) => {
5171
+ const allowedOracles = oraclesByChainId[offer.chainId]?.map((oracle) => oracle.toLowerCase());
5172
+ if (!allowedOracles || allowedOracles.length === 0) return { message: `No allowed oracles for chain ${offer.chainId}` };
5173
+ if (offer.collaterals.some((collateral) => !allowedOracles.includes(collateral.oracle.toLowerCase()))) return { message: "Oracle is not allowed" };
5174
+ });
5175
+ /**
4654
5176
  * A batch validation rule that ensures all offers in a tree have the same maker address.
4655
5177
  * Returns an issue only for the first non-conforming offer.
4656
5178
  * This rule is signing-agnostic; signer verification is handled at the collector level.
@@ -4681,7 +5203,11 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
4681
5203
  //#region src/gatekeeper/morphoRules.ts
4682
5204
  const morphoRules = (chains$2) => {
4683
5205
  const assetsByChainId = {};
4684
- for (const chain of chains$2) assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
5206
+ const oraclesByChainId = {};
5207
+ for (const chain of chains$2) {
5208
+ assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
5209
+ oraclesByChainId[chain.id] = oracles[chain.id.toString()] ?? [];
5210
+ }
4685
5211
  return [
4686
5212
  sameMaker(),
4687
5213
  amountMutualExclusivity(),
@@ -4695,7 +5221,8 @@ const morphoRules = (chains$2) => {
4695
5221
  ],
4696
5222
  allowedAddresses: chains$2.flatMap((c) => getCallbackAddresses(c.name))
4697
5223
  }),
4698
- token({ assetsByChainId })
5224
+ token({ assetsByChainId }),
5225
+ oracle({ oraclesByChainId })
4699
5226
  ];
4700
5227
  };
4701
5228
 
@@ -4706,6 +5233,7 @@ function from(parameters) {
4706
5233
  const config = {
4707
5234
  client: parameters.client,
4708
5235
  mempoolAddress: parameters.mempoolAddress,
5236
+ morphoAddress: parameters.morphoAddress,
4709
5237
  blockWindow: parameters.blockWindow
4710
5238
  };
4711
5239
  return {
@@ -4723,11 +5251,18 @@ function from(parameters) {
4723
5251
  */
4724
5252
  async function add(config, offers) {
4725
5253
  if (!config.client.account) throw new WalletAccountNotSetError();
4726
- const tree = from$9(offers.map((o) => from$8(o)));
5254
+ const tree = from$3(offers.map((o) => from$9(o)));
4727
5255
  const chainId = await getChainId(config.client);
4728
5256
  for (const offer of tree.offers) if (chainId !== offer.chainId) throw new ChainIdMismatchError(offer.chainId, chainId);
4729
- const signature = await sign(tree.offers, config.client);
4730
- const encoded = await encode$1(tree, signature);
5257
+ const signatureDomain$1 = resolveSignatureDomain(config, chainId);
5258
+ const signature = await config.client.signTypedData({
5259
+ account: config.client.account,
5260
+ domain: signatureDomain(signatureDomain$1),
5261
+ types: signatureTypes,
5262
+ primaryType: "Root",
5263
+ message: { root: tree.root }
5264
+ });
5265
+ const encoded = await encode(tree, signature, signatureDomain$1);
4731
5266
  try {
4732
5267
  return await config.client.sendTransaction({
4733
5268
  chain: config.client.chain,
@@ -4766,6 +5301,7 @@ const getChainId = async (client) => {
4766
5301
  };
4767
5302
  async function* streamOffers(config, parameters) {
4768
5303
  const { loanToken, blockNumberGte, blockNumberLte, order = "desc", options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = config.blockWindow } = {} } = parameters;
5304
+ const signatureDomain = resolveSignatureDomain(config, await getChainId(config.client));
4769
5305
  const stream = streamLogs({
4770
5306
  client: config.client.extend(viem.publicActions),
4771
5307
  contractAddress: config.mempoolAddress,
@@ -4797,7 +5333,7 @@ async function* streamOffers(config, parameters) {
4797
5333
  if (!log) continue;
4798
5334
  const [payload] = (0, viem.decodeAbiParameters)([{ type: "bytes" }], log.data);
4799
5335
  try {
4800
- const { tree } = await decode$1(payload);
5336
+ const { tree } = await decode(payload, signatureDomain);
4801
5337
  for (const offer of tree.offers) {
4802
5338
  if (loanToken && offer.loanToken.toLowerCase() !== loanToken.toLowerCase()) continue;
4803
5339
  offers.push(offer);
@@ -4832,6 +5368,21 @@ var ChainIdMismatchError = class extends BaseError {
4832
5368
  _defineProperty(this, "name", "Mempool.ChainIdMismatchError");
4833
5369
  }
4834
5370
  };
5371
+ const resolveSignatureDomain = (config, chainId) => {
5372
+ const chain = config.client.chain;
5373
+ const verifyingContract = config.morphoAddress ?? chain?.custom?.morpho?.address ?? getChain(chainId)?.custom.morpho.address;
5374
+ if (!verifyingContract || verifyingContract.toLowerCase() === viem.zeroAddress) throw new MissingMorphoAddressError();
5375
+ return {
5376
+ chainId,
5377
+ verifyingContract
5378
+ };
5379
+ };
5380
+ var MissingMorphoAddressError = class extends BaseError {
5381
+ constructor() {
5382
+ super("Morpho address is required to verify root signatures (zero address is invalid).");
5383
+ _defineProperty(this, "name", "Mempool.MissingMorphoAddressError");
5384
+ }
5385
+ };
4835
5386
 
4836
5387
  //#endregion
4837
5388
  //#region src/mempool/MempoolClient.ts
@@ -5045,12 +5596,6 @@ Object.defineProperty(exports, 'Format', {
5045
5596
  return Format_exports;
5046
5597
  }
5047
5598
  });
5048
- Object.defineProperty(exports, 'GateConfig', {
5049
- enumerable: true,
5050
- get: function () {
5051
- return GateConfig_exports;
5052
- }
5053
- });
5054
5599
  Object.defineProperty(exports, 'Gatekeeper', {
5055
5600
  enumerable: true,
5056
5601
  get: function () {