@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.
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-Bo1DHCg-.mjs";
2
2
  import { z } from "zod/v4";
3
- import { bytesToHex, decodeAbiParameters, encodeAbiParameters, getAddress, hashMessage, hashTypedData, hexToBytes, isAddress, isHex, keccak256, maxUint256, numberToHex, pad, parseAbi, publicActions, recoverAddress, zeroAddress } from "viem";
3
+ import { bytesToHex, decodeAbiParameters, encodeAbiParameters, getAddress, hashTypedData, hexToBytes, isAddress, isHex, keccak256, maxUint256, numberToHex, pad, parseAbi, publicActions, recoverAddress, zeroAddress } from "viem";
4
4
  import { getBlock, getLogs, multicall } from "viem/actions";
5
5
  import { anvil, base, mainnet } from "viem/chains";
6
6
  import * as z$1 from "zod";
@@ -1372,6 +1372,7 @@ var Obligation_exports = /* @__PURE__ */ __exportAll({
1372
1372
  InvalidObligationError: () => InvalidObligationError,
1373
1373
  ObligationSchema: () => ObligationSchema,
1374
1374
  from: () => from$10,
1375
+ fromOffer: () => fromOffer$1,
1375
1376
  fromSnakeCase: () => fromSnakeCase$2,
1376
1377
  id: () => id,
1377
1378
  random: () => random$2
@@ -1500,6 +1501,21 @@ function random$2() {
1500
1501
  maturity: from$11("end_of_next_quarter")
1501
1502
  });
1502
1503
  }
1504
+ /**
1505
+ * Creates an obligation from an offer.
1506
+ * @constructor
1507
+ *
1508
+ * @param offer - The offer to create the obligation from.
1509
+ * @returns The created obligation. {@link fromOffer.ReturnType}
1510
+ */
1511
+ function fromOffer$1(offer) {
1512
+ return from$10({
1513
+ chainId: offer.chainId,
1514
+ loanToken: offer.loanToken,
1515
+ collaterals: offer.collaterals,
1516
+ maturity: offer.maturity
1517
+ });
1518
+ }
1503
1519
  var InvalidObligationError = class extends BaseError {
1504
1520
  constructor(error) {
1505
1521
  super("Invalid obligation.", { cause: error });
@@ -1513,273 +1529,22 @@ var CollateralsAreNotSortedError = class extends BaseError {
1513
1529
  }
1514
1530
  };
1515
1531
 
1516
- //#endregion
1517
- //#region src/core/Tree.ts
1518
- var Tree_exports = /* @__PURE__ */ __exportAll({
1519
- DecodeError: () => DecodeError,
1520
- EncodeError: () => EncodeError,
1521
- TreeError: () => TreeError,
1522
- VERSION: () => VERSION,
1523
- decode: () => decode$1,
1524
- encode: () => encode$1,
1525
- encodeUnsigned: () => encodeUnsigned,
1526
- from: () => from$9,
1527
- proofs: () => proofs
1528
- });
1529
- const VERSION = 1;
1530
- const normalizeHash = (hash) => hash.toLowerCase();
1531
- /**
1532
- * Builds a Merkle tree from a list of offers.
1533
- *
1534
- * Leaves are the offer `hash` values as `bytes32` and are deterministically
1535
- * ordered following the StandardMerkleTree leaf ordering so that the resulting
1536
- * root is stable regardless of the input order.
1537
- *
1538
- * @param offers - Offers to include in the tree.
1539
- * @returns A `StandardMerkleTree` of `bytes32` leaves representing the offers.
1540
- * @throws {TreeError} If tree building fails due to offer inconsistencies.
1541
- */
1542
- const from$9 = (offers) => {
1543
- const leaves = offers.map((offer) => [hash(offer)]);
1544
- const tree = StandardMerkleTree.of(leaves, ["bytes32"]);
1545
- const orderedOffers = orderOffers(tree, offers);
1546
- return Object.assign(tree, { offers: orderedOffers });
1547
- };
1548
- const orderOffers = (tree, offers) => {
1549
- const offerByHash = /* @__PURE__ */ new Map();
1550
- for (const offer of offers) offerByHash.set(normalizeHash(hash(offer)), offer);
1551
- const entries = tree.dump().values.map((value) => {
1552
- const hash = normalizeHash(value.value[0]);
1553
- const offer = offerByHash.get(hash);
1554
- if (!offer) throw new TreeError(`missing offer for leaf ${hash}`);
1555
- return {
1556
- offer,
1557
- treeIndex: value.treeIndex
1558
- };
1559
- });
1560
- entries.sort((a, b) => b.treeIndex - a.treeIndex);
1561
- return entries.map((item) => item.offer);
1562
- };
1563
- /**
1564
- * Generates merkle proofs for all offers in a tree.
1565
- *
1566
- * Each proof allows independent verification that an offer is included in the tree
1567
- * without requiring the full tree. Proofs are ordered by StandardMerkleTree leaf ordering.
1568
- *
1569
- * @param tree - The {@link Tree} to generate proofs for.
1570
- * @returns Array of proofs - {@link Proof}
1571
- */
1572
- const proofs = (tree) => {
1573
- return tree.offers.map((offer) => {
1574
- return {
1575
- offer,
1576
- path: tree.getProof([hash(offer)])
1577
- };
1578
- });
1579
- };
1580
- const assertHex = (value, expectedBytes, name) => {
1581
- if (typeof value !== "string" || !isHex(value)) throw new DecodeError(`${name} is not a valid hex string`);
1582
- if (hexToBytes(value).length !== expectedBytes) throw new DecodeError(`${name}: expected ${expectedBytes} bytes`);
1583
- };
1584
- const verifySignatureAndRecoverAddress = async (params) => {
1585
- const { root, signature } = params;
1586
- assertHex(signature, 65, "signature");
1587
- const hash = hashMessage({ raw: root });
1588
- try {
1589
- return await recoverAddress({
1590
- hash,
1591
- signature
1592
- });
1593
- } catch {
1594
- throw new DecodeError("signature recovery failed");
1595
- }
1596
- };
1597
- /**
1598
- * Encodes a merkle tree with signature into hex calldata for onchain broadcast.
1599
- *
1600
- * Layout: `0x{vv}{gzip([...offers])}{root}{signature}` where:
1601
- * - `{vv}`: 1-byte version (currently 0x01)
1602
- * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
1603
- * - `{root}`: 32-byte merkle root
1604
- * - `{signature}`: 65-byte EIP-191 signature over raw root bytes
1605
- *
1606
- * Validates signature authenticity and root integrity before encoding.
1607
- *
1608
- * @example
1609
- * ```typescript
1610
- * const tree = Tree.from(offers);
1611
- * const signature = await wallet.signMessage({ message: { raw: tree.root } });
1612
- * const calldata = await Tree.encode(tree, signature);
1613
- * await broadcast(calldata);
1614
- * ```
1615
- *
1616
- * @example
1617
- * Manual construction (for advanced users):
1618
- * ```typescript
1619
- * const tree = Tree.from(offers);
1620
- * const compressed = gzip(JSON.stringify(tree.offers.map(Offer.serialize)));
1621
- * const partial = `0x01${bytesToHex(compressed)}${tree.root.slice(2)}`;
1622
- * const signature = await wallet.signMessage({ message: { raw: tree.root } });
1623
- * const calldata = `${partial}${signature.slice(2)}`;
1624
- * ```
1625
- *
1626
- * @param tree - Merkle tree of offers
1627
- * @param signature - EIP-191 signature over raw root bytes
1628
- * @returns Hex-encoded calldata ready for onchain broadcast
1629
- * @throws {EncodeError} If signature verification fails or root mismatch
1630
- */
1631
- const encode$1 = async (tree, signature) => {
1632
- validateTreeForEncoding(tree);
1633
- await verifySignatureAndRecoverAddress({
1634
- root: tree.root,
1635
- signature
1636
- });
1637
- const unsigned = encodeUnsignedBytes(tree);
1638
- const sigBytes = hexToBytes(signature);
1639
- const encoded = new Uint8Array(unsigned.length + sigBytes.length);
1640
- encoded.set(unsigned, 0);
1641
- encoded.set(sigBytes, unsigned.length);
1642
- return bytesToHex(encoded);
1643
- };
1644
- /**
1645
- * Encodes a merkle tree without a signature into hex payload for client-side signing.
1646
- *
1647
- * Layout: `0x{vv}{gzip([...offers])}{root}` where:
1648
- * - `{vv}`: 1-byte version (currently 0x01)
1649
- * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
1650
- * - `{root}`: 32-byte merkle root
1651
- *
1652
- * Validates root integrity before encoding.
1653
- *
1654
- * @param tree - Merkle tree of offers
1655
- * @returns Hex-encoded unsigned payload
1656
- * @throws {EncodeError} If root mismatch
1657
- */
1658
- const encodeUnsigned = (tree) => {
1659
- validateTreeForEncoding(tree);
1660
- return bytesToHex(encodeUnsignedBytes(tree));
1661
- };
1662
- const validateTreeForEncoding = (tree) => {
1663
- if (VERSION > 255) throw new EncodeError(`version overflow: ${VERSION} exceeds 255`);
1664
- const computed = from$9(tree.offers);
1665
- if (tree.root !== computed.root) throw new EncodeError(`root mismatch: expected ${computed.root}, got ${tree.root}`);
1666
- };
1667
- const encodeUnsignedBytes = (tree) => {
1668
- const offersPayload = tree.offers.map(serialize);
1669
- const compressed = gzip(JSON.stringify(offersPayload));
1670
- const rootBytes = hexToBytes(tree.root);
1671
- const encoded = new Uint8Array(1 + compressed.length + 32);
1672
- encoded[0] = VERSION;
1673
- encoded.set(compressed, 1);
1674
- encoded.set(rootBytes, 1 + compressed.length);
1675
- return encoded;
1676
- };
1677
- /**
1678
- * Decodes hex calldata into a validated merkle tree.
1679
- *
1680
- * Validates signature before decompression for fail-fast rejection of invalid payloads.
1681
- * Returns the tree with separately validated signature and recovered signer address.
1682
- *
1683
- * Validation order:
1684
- * 1. Version check
1685
- * 2. Signature verification (fail-fast, before decompression)
1686
- * 3. Decompression (only if signature valid)
1687
- * 4. Root verification (computed from offers vs embedded root)
1688
- *
1689
- * @example
1690
- * ```typescript
1691
- * const { tree, signature, signer } = await Tree.decode(calldata);
1692
- * console.log(`Tree signed by ${signer} with ${tree.offers.length} offers`);
1693
- * ```
1694
- *
1695
- * @param encoded - Hex calldata in format `0x{vv}{gzip}{root}{signature}`
1696
- * @returns Validated tree, signature, and recovered signer address
1697
- * @throws {DecodeError} If version invalid, signature invalid, or root mismatch
1698
- */
1699
- const decode$1 = async (encoded) => {
1700
- const bytes = hexToBytes(encoded);
1701
- if (bytes.length < 98) throw new DecodeError("payload too short");
1702
- const version = bytes[0];
1703
- if (version !== (VERSION & 255)) throw new DecodeError(`invalid version: expected ${VERSION}, got ${version ?? 0}`);
1704
- const signature = bytesToHex(bytes.slice(-65));
1705
- const root = bytesToHex(bytes.slice(-97, -65));
1706
- assertHex(root, 32, "root");
1707
- assertHex(signature, 65, "signature");
1708
- const signer = await verifySignatureAndRecoverAddress({
1709
- root,
1710
- signature
1711
- });
1712
- const compressed = bytes.slice(1, -97);
1713
- let decoded;
1714
- try {
1715
- decoded = ungzip(compressed, { to: "string" });
1716
- } catch {
1717
- throw new DecodeError("decompression failed");
1718
- }
1719
- let rawOffers;
1720
- try {
1721
- rawOffers = JSON.parse(decoded);
1722
- } catch {
1723
- throw new DecodeError("JSON parse failed");
1724
- }
1725
- const tree = from$9(rawOffers.map((o) => OfferSchema().parse(o)));
1726
- if (root !== tree.root) throw new DecodeError(`root mismatch: expected ${tree.root}, got ${root}`);
1727
- return {
1728
- tree,
1729
- signature,
1730
- signer
1731
- };
1732
- };
1733
- /**
1734
- * Error thrown during tree building operations.
1735
- * Indicates structural issues with the tree (missing offers, inconsistent state).
1736
- */
1737
- var TreeError = class extends BaseError {
1738
- constructor(reason) {
1739
- super(`Tree error: ${reason}`);
1740
- _defineProperty(this, "name", "Tree.TreeError");
1741
- }
1742
- };
1743
- /**
1744
- * Error thrown during tree encoding.
1745
- * Indicates validation failures (signature, root mismatch, mixed makers).
1746
- */
1747
- var EncodeError = class extends BaseError {
1748
- constructor(reason) {
1749
- super(`Failed to encode tree: ${reason}`);
1750
- _defineProperty(this, "name", "Tree.EncodeError");
1751
- }
1752
- };
1753
- /**
1754
- * Error thrown during tree decoding.
1755
- * Indicates payload corruption, version mismatch, or validation failures.
1756
- */
1757
- var DecodeError = class extends BaseError {
1758
- constructor(reason) {
1759
- super(`Failed to decode tree: ${reason}`);
1760
- _defineProperty(this, "name", "Tree.DecodeError");
1761
- }
1762
- };
1763
-
1764
1532
  //#endregion
1765
1533
  //#region src/core/Offer.ts
1766
1534
  var Offer_exports = /* @__PURE__ */ __exportAll({
1767
- AccountNotSetError: () => AccountNotSetError,
1768
1535
  InvalidOfferError: () => InvalidOfferError,
1769
1536
  OfferSchema: () => OfferSchema,
1770
1537
  Status: () => Status,
1771
1538
  consumedEvent: () => consumedEvent,
1772
- decode: () => decode,
1539
+ decode: () => decode$1,
1773
1540
  domain: () => domain,
1774
- encode: () => encode,
1775
- from: () => from$8,
1541
+ encode: () => encode$1,
1542
+ from: () => from$9,
1776
1543
  fromSnakeCase: () => fromSnakeCase$1,
1777
1544
  hash: () => hash,
1778
1545
  obligationId: () => obligationId,
1779
1546
  random: () => random$1,
1780
1547
  serialize: () => serialize,
1781
- sign: () => sign,
1782
- signatureMsg: () => signatureMsg,
1783
1548
  toSnakeCase: () => toSnakeCase,
1784
1549
  types: () => types
1785
1550
  });
@@ -1832,7 +1597,7 @@ const OfferSchema = () => {
1832
1597
  * @param input - The offer to create.
1833
1598
  * @returns The created offer.
1834
1599
  */
1835
- function from$8(input) {
1600
+ function from$9(input) {
1836
1601
  try {
1837
1602
  return OfferSchema().parse(input);
1838
1603
  } catch (error) {
@@ -1846,7 +1611,7 @@ function from$8(input) {
1846
1611
  * @returns The created offer.
1847
1612
  */
1848
1613
  function fromSnakeCase$1(input) {
1849
- return from$8(fromSnakeCase$3(input));
1614
+ return from$9(fromSnakeCase$3(input));
1850
1615
  }
1851
1616
  /**
1852
1617
  * Converts an offer to a snake case object.
@@ -1940,7 +1705,7 @@ function random$1(config) {
1940
1705
  })
1941
1706
  };
1942
1707
  })();
1943
- return from$8({
1708
+ return from$9({
1944
1709
  maker: config?.maker ?? address(),
1945
1710
  assets: assetsScaled,
1946
1711
  obligationUnits: config?.obligationUnits ?? 0n,
@@ -2068,23 +1833,6 @@ const types = {
2068
1833
  type: "bytes"
2069
1834
  }]
2070
1835
  };
2071
- /**
2072
- * Signs an array of offers.
2073
- * @throws {Error} If the wallet account is not set.
2074
- * @param offers - The offers to sign.
2075
- * @param wallet - The wallet to sign the offers with.
2076
- * @returns The signed offers.
2077
- */
2078
- async function sign(offers, wallet) {
2079
- if (!wallet.account) throw new AccountNotSetError();
2080
- return wallet.signMessage({
2081
- account: wallet.account,
2082
- message: { raw: signatureMsg(offers) }
2083
- });
2084
- }
2085
- function signatureMsg(offers) {
2086
- return from$9(offers).root;
2087
- }
2088
1836
  function hash(offer) {
2089
1837
  const cached = offer[HASH_CACHE];
2090
1838
  if (cached) return cached;
@@ -2211,7 +1959,7 @@ const OfferAbi = [
2211
1959
  }]
2212
1960
  }
2213
1961
  ];
2214
- function encode(offer) {
1962
+ function encode$1(offer) {
2215
1963
  return encodeAbiParameters(OfferAbi, [
2216
1964
  offer.maker,
2217
1965
  offer.assets,
@@ -2230,14 +1978,14 @@ function encode(offer) {
2230
1978
  offer.callback
2231
1979
  ]);
2232
1980
  }
2233
- function decode(data) {
1981
+ function decode$1(data) {
2234
1982
  let decoded;
2235
1983
  try {
2236
1984
  decoded = decodeAbiParameters(OfferAbi, data);
2237
1985
  } catch (error) {
2238
1986
  throw new InvalidOfferError(error);
2239
1987
  }
2240
- return from$8({
1988
+ return from$9({
2241
1989
  maker: decoded[0],
2242
1990
  assets: decoded[1],
2243
1991
  obligationUnits: decoded[2],
@@ -2316,25 +2064,22 @@ var InvalidOfferError = class InvalidOfferError extends BaseError {
2316
2064
  return `Invalid offer. ${InvalidOfferError.formatDetails(this.cause)}`;
2317
2065
  }
2318
2066
  };
2319
- var AccountNotSetError = class extends BaseError {
2320
- constructor() {
2321
- super("Account not set.");
2322
- _defineProperty(this, "name", "Offer.AccountNotSetError");
2323
- }
2324
- };
2325
2067
 
2326
2068
  //#endregion
2327
2069
  //#region src/core/Oracle.ts
2328
2070
  var Oracle_exports = /* @__PURE__ */ __exportAll({
2329
2071
  Conversion: () => Conversion,
2330
- from: () => from$7
2072
+ from: () => from$8,
2073
+ fromCollateral: () => fromCollateral,
2074
+ fromOffer: () => fromOffer,
2075
+ fromOffers: () => fromOffers
2331
2076
  });
2332
2077
  /**
2333
2078
  * Create an Oracle from a plain object.
2334
2079
  * @param data - The data to create the oracle from.
2335
2080
  * @returns The created oracle.
2336
2081
  */
2337
- function from$7(data) {
2082
+ function from$8(data) {
2338
2083
  return {
2339
2084
  chainId: data.chainId,
2340
2085
  address: data.address.toLowerCase(),
@@ -2342,8 +2087,61 @@ function from$7(data) {
2342
2087
  blockNumber: data.blockNumber
2343
2088
  };
2344
2089
  }
2345
- let Conversion;
2346
- (function(_Conversion) {
2090
+ /**
2091
+ * Creates an oracle from a collateral.
2092
+ * @constructor
2093
+ *
2094
+ * @param parameters - {@link fromCollateral.Parameters}
2095
+ * @returns The created oracle. {@link fromCollateral.ReturnType}
2096
+ */
2097
+ function fromCollateral(parameters) {
2098
+ const { chainId, collateral, blockNumber, price = null } = parameters;
2099
+ return {
2100
+ chainId,
2101
+ address: collateral.oracle.toLowerCase(),
2102
+ price,
2103
+ blockNumber
2104
+ };
2105
+ }
2106
+ /**
2107
+ * Creates oracles from a single offer.
2108
+ * @constructor
2109
+ *
2110
+ * @param parameters - {@link fromOffer.Parameters}
2111
+ * @returns The created oracles. {@link fromOffer.ReturnType}
2112
+ */
2113
+ function fromOffer(parameters) {
2114
+ const { offer, blockNumber, price = null } = parameters;
2115
+ return fromOffers({
2116
+ offers: [offer],
2117
+ blockNumber,
2118
+ price
2119
+ });
2120
+ }
2121
+ /**
2122
+ * Creates oracles from a list of offers.
2123
+ * @constructor
2124
+ *
2125
+ * @param parameters - {@link fromOffers.Parameters}
2126
+ * @returns The created oracles. {@link fromOffers.ReturnType}
2127
+ */
2128
+ function fromOffers(parameters) {
2129
+ const { offers, blockNumber, price = null } = parameters;
2130
+ const rowsByKey = /* @__PURE__ */ new Map();
2131
+ for (const offer of offers) for (const collateral of offer.collaterals) {
2132
+ const key = `${offer.chainId}-${collateral.oracle}`.toLowerCase();
2133
+ if (rowsByKey.has(key)) continue;
2134
+ rowsByKey.set(key, fromCollateral({
2135
+ chainId: offer.chainId,
2136
+ collateral,
2137
+ blockNumber,
2138
+ price
2139
+ }));
2140
+ }
2141
+ return Array.from(rowsByKey.values());
2142
+ }
2143
+ let Conversion;
2144
+ (function(_Conversion) {
2347
2145
  function collateralToLoan(amount, params) {
2348
2146
  return amount * params.price / 10n ** 36n * params.lltv / 10n ** 18n;
2349
2147
  }
@@ -2359,7 +2157,7 @@ let Conversion;
2359
2157
  //#region src/core/Position.ts
2360
2158
  var Position_exports = /* @__PURE__ */ __exportAll({
2361
2159
  Type: () => Type,
2362
- from: () => from$6
2160
+ from: () => from$7
2363
2161
  });
2364
2162
  let Type = /* @__PURE__ */ function(Type) {
2365
2163
  Type["ERC20"] = "erc20";
@@ -2372,7 +2170,7 @@ let Type = /* @__PURE__ */ function(Type) {
2372
2170
  * @param parameters - {@link from.Parameters}
2373
2171
  * @returns The created Position. {@link from.ReturnType}
2374
2172
  */
2375
- function from$6(parameters) {
2173
+ function from$7(parameters) {
2376
2174
  return {
2377
2175
  chainId: parameters.chainId,
2378
2176
  contract: parameters.contract.toLowerCase(),
@@ -2389,7 +2187,7 @@ function from$6(parameters) {
2389
2187
  var Quote_exports = /* @__PURE__ */ __exportAll({
2390
2188
  InvalidQuoteError: () => InvalidQuoteError,
2391
2189
  QuoteSchema: () => QuoteSchema,
2392
- from: () => from$5,
2190
+ from: () => from$6,
2393
2191
  fromSnakeCase: () => fromSnakeCase,
2394
2192
  random: () => random
2395
2193
  });
@@ -2410,7 +2208,7 @@ const QuoteSchema = z$1.object({
2410
2208
  * const quote = Quote.from({ obligationId: "0x123", ask: { price: 100n }, bid: { price: 100n } });
2411
2209
  * ```
2412
2210
  */
2413
- function from$5(parameters) {
2211
+ function from$6(parameters) {
2414
2212
  try {
2415
2213
  const parsedQuote = QuoteSchema.parse(parameters);
2416
2214
  return {
@@ -2429,7 +2227,7 @@ function from$5(parameters) {
2429
2227
  * @returns The created quote. {@link fromSnakeCase.ReturnType}
2430
2228
  */
2431
2229
  function fromSnakeCase(snake) {
2432
- return from$5(fromSnakeCase$3(snake));
2230
+ return from$6(fromSnakeCase$3(snake));
2433
2231
  }
2434
2232
  /**
2435
2233
  * Generates a random quote.
@@ -2441,7 +2239,7 @@ function fromSnakeCase(snake) {
2441
2239
  * ```
2442
2240
  */
2443
2241
  function random() {
2444
- return from$5({
2242
+ return from$6({
2445
2243
  obligationId: id(random$2()),
2446
2244
  ask: { price: BigInt(int(1e6)) },
2447
2245
  bid: { price: BigInt(int(1e6)) }
@@ -2464,7 +2262,7 @@ var TradingFee_exports = /* @__PURE__ */ __exportAll({
2464
2262
  activate: () => activate,
2465
2263
  compute: () => compute,
2466
2264
  deactivate: () => deactivate,
2467
- from: () => from$4,
2265
+ from: () => from$5,
2468
2266
  getFees: () => getFees,
2469
2267
  isActivated: () => isActivated
2470
2268
  });
@@ -2490,7 +2288,7 @@ const WAD = 10n ** 18n;
2490
2288
  * @throws {@link InvalidFeeError} if any fee exceeds WAD (100%).
2491
2289
  * @throws {@link InvalidFeesLengthError} if fees array doesn't have exactly 6 elements.
2492
2290
  */
2493
- function from$4(activated, fees) {
2291
+ function from$5(activated, fees) {
2494
2292
  if (fees.length !== 6) throw new InvalidFeesLengthError(fees.length);
2495
2293
  for (let i = 0; i < 6; i++) {
2496
2294
  const fee = fees[i];
@@ -2605,7 +2403,7 @@ var InvalidFeesLengthError = class extends BaseError {
2605
2403
 
2606
2404
  //#endregion
2607
2405
  //#region src/core/Transfer.ts
2608
- var Transfer_exports = /* @__PURE__ */ __exportAll({ from: () => from$3 });
2406
+ var Transfer_exports = /* @__PURE__ */ __exportAll({ from: () => from$4 });
2609
2407
  /**
2610
2408
  * @constructor
2611
2409
  *
@@ -2618,7 +2416,7 @@ var Transfer_exports = /* @__PURE__ */ __exportAll({ from: () => from$3 });
2618
2416
  * const transfer = Transfer.from({ id: "1", chainId: 1, contract: "0x123", from: "0x456", to: "0x789", value: 100n, blockNumber: 100n });
2619
2417
  * ```
2620
2418
  */
2621
- function from$3(parameters) {
2419
+ function from$4(parameters) {
2622
2420
  return {
2623
2421
  id: parameters.id,
2624
2422
  chainId: parameters.chainId,
@@ -2630,6 +2428,337 @@ function from$3(parameters) {
2630
2428
  };
2631
2429
  }
2632
2430
 
2431
+ //#endregion
2432
+ //#region src/core/Tree.ts
2433
+ var Tree_exports = /* @__PURE__ */ __exportAll({
2434
+ DecodeError: () => DecodeError,
2435
+ EncodeError: () => EncodeError,
2436
+ SignatureDomainError: () => SignatureDomainError,
2437
+ TreeError: () => TreeError,
2438
+ VERSION: () => VERSION,
2439
+ decode: () => decode,
2440
+ encode: () => encode,
2441
+ encodeUnsigned: () => encodeUnsigned,
2442
+ from: () => from$3,
2443
+ proofs: () => proofs,
2444
+ signatureDomain: () => signatureDomain,
2445
+ signatureTypes: () => signatureTypes
2446
+ });
2447
+ const VERSION = 1;
2448
+ /**
2449
+ * EIP-712 types for signing the tree root (Root(bytes32 root)).
2450
+ */
2451
+ const signatureTypes = {
2452
+ EIP712Domain: [{
2453
+ name: "chainId",
2454
+ type: "uint256"
2455
+ }, {
2456
+ name: "verifyingContract",
2457
+ type: "address"
2458
+ }],
2459
+ Root: [{
2460
+ name: "root",
2461
+ type: "bytes32"
2462
+ }]
2463
+ };
2464
+ const normalizeHash = (hash) => hash.toLowerCase();
2465
+ /**
2466
+ * Builds a Merkle tree from a list of offers.
2467
+ *
2468
+ * Leaves are the offer `hash` values as `bytes32` and are deterministically
2469
+ * ordered following the StandardMerkleTree leaf ordering so that the resulting
2470
+ * root is stable regardless of the input order.
2471
+ *
2472
+ * @param offers - Offers to include in the tree.
2473
+ * @returns A `StandardMerkleTree` of `bytes32` leaves representing the offers.
2474
+ * @throws {TreeError} If tree building fails due to offer inconsistencies.
2475
+ */
2476
+ const from$3 = (offers) => {
2477
+ const leaves = offers.map((offer) => [hash(offer)]);
2478
+ const tree = StandardMerkleTree.of(leaves, ["bytes32"]);
2479
+ const orderedOffers = orderOffers(tree, offers);
2480
+ return Object.assign(tree, { offers: orderedOffers });
2481
+ };
2482
+ const orderOffers = (tree, offers) => {
2483
+ const offerByHash = /* @__PURE__ */ new Map();
2484
+ for (const offer of offers) offerByHash.set(normalizeHash(hash(offer)), offer);
2485
+ const entries = tree.dump().values.map((value) => {
2486
+ const hash = normalizeHash(value.value[0]);
2487
+ const offer = offerByHash.get(hash);
2488
+ if (!offer) throw new TreeError(`missing offer for leaf ${hash}`);
2489
+ return {
2490
+ offer,
2491
+ treeIndex: value.treeIndex
2492
+ };
2493
+ });
2494
+ entries.sort((a, b) => b.treeIndex - a.treeIndex);
2495
+ return entries.map((item) => item.offer);
2496
+ };
2497
+ /**
2498
+ * Generates merkle proofs for all offers in a tree.
2499
+ *
2500
+ * Each proof allows independent verification that an offer is included in the tree
2501
+ * without requiring the full tree. Proofs are ordered by StandardMerkleTree leaf ordering.
2502
+ *
2503
+ * @param tree - The {@link Tree} to generate proofs for.
2504
+ * @returns Array of proofs - {@link Proof}
2505
+ */
2506
+ const proofs = (tree) => {
2507
+ return tree.offers.map((offer) => {
2508
+ return {
2509
+ offer,
2510
+ path: tree.getProof([hash(offer)])
2511
+ };
2512
+ });
2513
+ };
2514
+ /**
2515
+ * Normalizes a Root signature domain (BigInt chain id, lowercase address).
2516
+ * @throws {SignatureDomainError} When the domain is invalid.
2517
+ */
2518
+ const signatureDomain = (domain) => {
2519
+ return normalizeSignatureDomain(domain, (reason) => new SignatureDomainError(reason));
2520
+ };
2521
+ const normalizeSignatureDomain = (domain, errorFactory) => {
2522
+ let chainId;
2523
+ try {
2524
+ chainId = typeof domain.chainId === "bigint" ? domain.chainId : BigInt(domain.chainId);
2525
+ } catch {
2526
+ throw errorFactory("invalid chainId");
2527
+ }
2528
+ if (chainId < 0n) throw errorFactory("invalid chainId");
2529
+ if (!isAddress(domain.verifyingContract)) throw errorFactory("invalid verifyingContract");
2530
+ return {
2531
+ chainId,
2532
+ verifyingContract: domain.verifyingContract.toLowerCase()
2533
+ };
2534
+ };
2535
+ const assertHex = (value, expectedBytes, name, errorFactory = (reason) => new DecodeError(reason)) => {
2536
+ if (typeof value !== "string" || !isHex(value)) throw errorFactory(`${name} is not a valid hex string`);
2537
+ if (hexToBytes(value).length !== expectedBytes) throw errorFactory(`${name}: expected ${expectedBytes} bytes`);
2538
+ };
2539
+ const verifySignatureAndRecoverAddress = async (params) => {
2540
+ const { root, signature, domain, errorFactory } = params;
2541
+ assertHex(root, 32, "root", errorFactory);
2542
+ assertHex(signature, 65, "signature", errorFactory);
2543
+ const hash = hashTypedData({
2544
+ domain,
2545
+ types: signatureTypes,
2546
+ primaryType: "Root",
2547
+ message: { root }
2548
+ });
2549
+ try {
2550
+ return await recoverAddress({
2551
+ hash,
2552
+ signature
2553
+ });
2554
+ } catch {
2555
+ throw errorFactory("signature recovery failed");
2556
+ }
2557
+ };
2558
+ /**
2559
+ * Encodes a merkle tree with signature into hex calldata for onchain broadcast.
2560
+ *
2561
+ * Layout: `0x{vv}{gzip([...offers])}{root}{signature}` where:
2562
+ * - `{vv}`: 1-byte version (currently 0x01)
2563
+ * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
2564
+ * - `{root}`: 32-byte merkle root
2565
+ * - `{signature}`: 65-byte EIP-712 signature over Root(bytes32 root)
2566
+ *
2567
+ * Validates signature authenticity and root integrity before encoding.
2568
+ *
2569
+ * @example
2570
+ * ```typescript
2571
+ * const tree = Tree.from(offers);
2572
+ * const signature = await wallet.signTypedData({
2573
+ * account: wallet.account,
2574
+ * domain: Tree.signatureDomain({ chainId, verifyingContract }),
2575
+ * types: Tree.signatureTypes,
2576
+ * primaryType: "Root",
2577
+ * message: { root: tree.root },
2578
+ * });
2579
+ * const calldata = await Tree.encode(tree, signature, { chainId, verifyingContract });
2580
+ * await broadcast(calldata);
2581
+ * ```
2582
+ *
2583
+ * @example
2584
+ * Manual construction (for advanced users):
2585
+ * ```typescript
2586
+ * const tree = Tree.from(offers);
2587
+ * const compressed = gzip(JSON.stringify(tree.offers.map(Offer.serialize)));
2588
+ * const partial = `0x01${bytesToHex(compressed)}${tree.root.slice(2)}`;
2589
+ * const signature = await wallet.signTypedData({
2590
+ * account: wallet.account,
2591
+ * domain: Tree.signatureDomain({ chainId, verifyingContract }),
2592
+ * types: Tree.signatureTypes,
2593
+ * primaryType: "Root",
2594
+ * message: { root: tree.root },
2595
+ * });
2596
+ * const calldata = `${partial}${signature.slice(2)}`;
2597
+ * ```
2598
+ *
2599
+ * @param tree - Merkle tree of offers
2600
+ * @param signature - EIP-712 signature over Root(bytes32 root)
2601
+ * @param domain - EIP-712 domain with chain id and verifying contract
2602
+ * @returns Hex-encoded calldata ready for onchain broadcast
2603
+ * @throws {EncodeError} If signature verification fails or root mismatch
2604
+ */
2605
+ const encode = async (tree, signature, domain) => {
2606
+ const errorFactory = (reason) => new EncodeError(reason);
2607
+ const normalizedDomain = normalizeSignatureDomain(domain, errorFactory);
2608
+ validateTreeForEncoding(tree, normalizedDomain);
2609
+ await verifySignatureAndRecoverAddress({
2610
+ root: tree.root,
2611
+ signature,
2612
+ domain: normalizedDomain,
2613
+ errorFactory
2614
+ });
2615
+ const unsigned = encodeUnsignedBytes(tree);
2616
+ const sigBytes = hexToBytes(signature);
2617
+ const encoded = new Uint8Array(unsigned.length + sigBytes.length);
2618
+ encoded.set(unsigned, 0);
2619
+ encoded.set(sigBytes, unsigned.length);
2620
+ return bytesToHex(encoded);
2621
+ };
2622
+ /**
2623
+ * Encodes a merkle tree without a signature into hex payload for client-side signing.
2624
+ *
2625
+ * Layout: `0x{vv}{gzip([...offers])}{root}` where:
2626
+ * - `{vv}`: 1-byte version (currently 0x01)
2627
+ * - `{gzip([...offers])}`: gzipped JSON array of serialized offers
2628
+ * - `{root}`: 32-byte merkle root
2629
+ *
2630
+ * Validates root integrity before encoding.
2631
+ *
2632
+ * @param tree - Merkle tree of offers
2633
+ * @returns Hex-encoded unsigned payload
2634
+ * @throws {EncodeError} If root mismatch
2635
+ */
2636
+ const encodeUnsigned = (tree) => {
2637
+ validateTreeForEncoding(tree);
2638
+ return bytesToHex(encodeUnsignedBytes(tree));
2639
+ };
2640
+ const validateTreeForEncoding = (tree, domain) => {
2641
+ if (VERSION > 255) throw new EncodeError(`version overflow: ${VERSION} exceeds 255`);
2642
+ const computed = from$3(tree.offers);
2643
+ if (tree.root !== computed.root) throw new EncodeError(`root mismatch: expected ${computed.root}, got ${tree.root}`);
2644
+ if (domain) {
2645
+ const mismatched = tree.offers.find((offer) => BigInt(offer.chainId) !== domain.chainId);
2646
+ if (mismatched) throw new EncodeError(`chainId mismatch: expected ${domain.chainId}, got ${mismatched.chainId}`);
2647
+ }
2648
+ };
2649
+ const encodeUnsignedBytes = (tree) => {
2650
+ const offersPayload = tree.offers.map(serialize);
2651
+ const compressed = gzip(JSON.stringify(offersPayload));
2652
+ const rootBytes = hexToBytes(tree.root);
2653
+ const encoded = new Uint8Array(1 + compressed.length + 32);
2654
+ encoded[0] = VERSION;
2655
+ encoded.set(compressed, 1);
2656
+ encoded.set(rootBytes, 1 + compressed.length);
2657
+ return encoded;
2658
+ };
2659
+ /**
2660
+ * Decodes hex calldata into a validated merkle tree.
2661
+ *
2662
+ * Validates signature before decompression for fail-fast rejection of invalid payloads.
2663
+ * Returns the tree with separately validated signature and recovered signer address.
2664
+ *
2665
+ * Validation order:
2666
+ * 1. Version check
2667
+ * 2. Signature verification (fail-fast, before decompression)
2668
+ * 3. Decompression (only if signature valid)
2669
+ * 4. Root verification (computed from offers vs embedded root)
2670
+ *
2671
+ * @example
2672
+ * ```typescript
2673
+ * const { tree, signature, signer } = await Tree.decode(calldata, { chainId, verifyingContract });
2674
+ * console.log(`Tree signed by ${signer} with ${tree.offers.length} offers`);
2675
+ * ```
2676
+ *
2677
+ * @param encoded - Hex calldata in format `0x{vv}{gzip}{root}{signature}`
2678
+ * @param domain - EIP-712 domain with chain id and verifying contract
2679
+ * @returns Validated tree, signature, and recovered signer address
2680
+ * @throws {DecodeError} If version invalid, signature invalid, or root mismatch
2681
+ */
2682
+ const decode = async (encoded, domain) => {
2683
+ const errorFactory = (reason) => new DecodeError(reason);
2684
+ const normalizedDomain = normalizeSignatureDomain(domain, errorFactory);
2685
+ const bytes = hexToBytes(encoded);
2686
+ if (bytes.length < 98) throw new DecodeError("payload too short");
2687
+ const version = bytes[0];
2688
+ if (version !== (VERSION & 255)) throw new DecodeError(`invalid version: expected ${VERSION}, got ${version ?? 0}`);
2689
+ const signature = bytesToHex(bytes.slice(-65));
2690
+ const root = bytesToHex(bytes.slice(-97, -65));
2691
+ assertHex(root, 32, "root");
2692
+ assertHex(signature, 65, "signature");
2693
+ const signer = await verifySignatureAndRecoverAddress({
2694
+ root,
2695
+ signature,
2696
+ domain: normalizedDomain,
2697
+ errorFactory
2698
+ });
2699
+ const compressed = bytes.slice(1, -97);
2700
+ let decoded;
2701
+ try {
2702
+ decoded = ungzip(compressed, { to: "string" });
2703
+ } catch {
2704
+ throw new DecodeError("decompression failed");
2705
+ }
2706
+ let rawOffers;
2707
+ try {
2708
+ rawOffers = JSON.parse(decoded);
2709
+ } catch {
2710
+ throw new DecodeError("JSON parse failed");
2711
+ }
2712
+ const tree = from$3(rawOffers.map((o) => OfferSchema().parse(o)));
2713
+ if (root !== tree.root) throw new DecodeError(`root mismatch: expected ${tree.root}, got ${root}`);
2714
+ const chainIdMismatch = tree.offers.find((offer) => BigInt(offer.chainId) !== normalizedDomain.chainId);
2715
+ if (chainIdMismatch) throw new DecodeError(`chainId mismatch: expected ${normalizedDomain.chainId}, got ${chainIdMismatch.chainId}`);
2716
+ return {
2717
+ tree,
2718
+ signature,
2719
+ signer
2720
+ };
2721
+ };
2722
+ /**
2723
+ * Error thrown during tree building operations.
2724
+ * Indicates structural issues with the tree (missing offers, inconsistent state).
2725
+ */
2726
+ var TreeError = class extends BaseError {
2727
+ constructor(reason) {
2728
+ super(`Tree error: ${reason}`);
2729
+ _defineProperty(this, "name", "Tree.TreeError");
2730
+ }
2731
+ };
2732
+ /**
2733
+ * Error thrown during tree encoding.
2734
+ * Indicates validation failures (signature, root mismatch, mixed makers).
2735
+ */
2736
+ var EncodeError = class extends BaseError {
2737
+ constructor(reason) {
2738
+ super(`Failed to encode tree: ${reason}`);
2739
+ _defineProperty(this, "name", "Tree.EncodeError");
2740
+ }
2741
+ };
2742
+ /**
2743
+ * Error thrown during tree decoding.
2744
+ * Indicates payload corruption, version mismatch, or validation failures.
2745
+ */
2746
+ var DecodeError = class extends BaseError {
2747
+ constructor(reason) {
2748
+ super(`Failed to decode tree: ${reason}`);
2749
+ _defineProperty(this, "name", "Tree.DecodeError");
2750
+ }
2751
+ };
2752
+ /**
2753
+ * Error thrown when an invalid signature domain is supplied.
2754
+ */
2755
+ var SignatureDomainError = class extends BaseError {
2756
+ constructor(reason) {
2757
+ super(`Invalid signature domain: ${reason}`);
2758
+ _defineProperty(this, "name", "Tree.SignatureDomainError");
2759
+ }
2760
+ };
2761
+
2633
2762
  //#endregion
2634
2763
  //#region src/core/types.ts
2635
2764
  const BrandTypeId = Symbol.for("mempool/Brand");
@@ -2795,6 +2924,21 @@ const validateOfferExample = {
2795
2924
  data: "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000034cf890db685fc536e05652fb41f02090c3fb751000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000108e644e3ab01184155270aa92a00000000000"
2796
2925
  }
2797
2926
  };
2927
+ const callbackTypesRequestExample = { callbacks: [{
2928
+ chain_id: 1,
2929
+ addresses: [
2930
+ "0x1111111111111111111111111111111111111111",
2931
+ "0x3333333333333333333333333333333333333333",
2932
+ "0x9999999999999999999999999999999999999999"
2933
+ ]
2934
+ }] };
2935
+ const callbackTypesResponseExample = [{
2936
+ chain_id: 1,
2937
+ sell_erc20_callback: ["0x1111111111111111111111111111111111111111"],
2938
+ buy_erc20: ["0x5555555555555555555555555555555555555555"],
2939
+ buy_vault_v1_callback: ["0x3333333333333333333333333333333333333333"],
2940
+ not_supported: ["0x9999999999999999999999999999999999999999"]
2941
+ }];
2798
2942
  const routerStatusExample = {
2799
2943
  status: "live",
2800
2944
  initialized: true,
@@ -2863,6 +3007,55 @@ __decorate([ApiProperty({
2863
3007
  type: "string",
2864
3008
  example: validateOfferExample.callback.data
2865
3009
  })], ValidateCallbackRequest.prototype, "data", void 0);
3010
+ var CallbackTypesChainRequest = class {};
3011
+ __decorate([ApiProperty({
3012
+ type: "number",
3013
+ example: callbackTypesRequestExample.callbacks[0].chain_id
3014
+ })], CallbackTypesChainRequest.prototype, "chain_id", void 0);
3015
+ __decorate([ApiProperty({
3016
+ type: () => [String],
3017
+ example: callbackTypesRequestExample.callbacks[0].addresses
3018
+ })], CallbackTypesChainRequest.prototype, "addresses", void 0);
3019
+ var CallbackTypesRequest = class {};
3020
+ __decorate([ApiProperty({
3021
+ type: () => [CallbackTypesChainRequest],
3022
+ example: callbackTypesRequestExample.callbacks
3023
+ })], CallbackTypesRequest.prototype, "callbacks", void 0);
3024
+ var CallbackTypesChainResponse = class {};
3025
+ __decorate([ApiProperty({
3026
+ type: "number",
3027
+ example: callbackTypesResponseExample[0].chain_id
3028
+ })], CallbackTypesChainResponse.prototype, "chain_id", void 0);
3029
+ __decorate([ApiProperty({
3030
+ type: () => [String],
3031
+ required: false,
3032
+ example: callbackTypesResponseExample[0].buy_vault_v1_callback
3033
+ })], CallbackTypesChainResponse.prototype, "buy_vault_v1_callback", void 0);
3034
+ __decorate([ApiProperty({
3035
+ type: () => [String],
3036
+ required: false,
3037
+ example: callbackTypesResponseExample[0].sell_erc20_callback
3038
+ })], CallbackTypesChainResponse.prototype, "sell_erc20_callback", void 0);
3039
+ __decorate([ApiProperty({
3040
+ type: () => [String],
3041
+ required: false,
3042
+ example: callbackTypesResponseExample[0].buy_erc20
3043
+ })], CallbackTypesChainResponse.prototype, "buy_erc20", void 0);
3044
+ __decorate([ApiProperty({
3045
+ type: () => [String],
3046
+ example: callbackTypesResponseExample[0].not_supported
3047
+ })], CallbackTypesChainResponse.prototype, "not_supported", void 0);
3048
+ var CallbackTypesSuccessResponse = class extends SuccessResponse {};
3049
+ __decorate([ApiProperty({
3050
+ type: "string",
3051
+ nullable: true,
3052
+ example: "maturity:1:1730415600:end_of_next_month"
3053
+ })], CallbackTypesSuccessResponse.prototype, "cursor", void 0);
3054
+ __decorate([ApiProperty({
3055
+ type: () => [CallbackTypesChainResponse],
3056
+ description: "Callback types grouped by chain.",
3057
+ example: callbackTypesResponseExample
3058
+ })], CallbackTypesSuccessResponse.prototype, "data", void 0);
2866
3059
  var AskResponse = class {};
2867
3060
  __decorate([ApiProperty({
2868
3061
  type: "string",
@@ -3027,7 +3220,8 @@ var OfferListResponse = class extends SuccessResponse {};
3027
3220
  __decorate([ApiProperty({
3028
3221
  type: "string",
3029
3222
  nullable: true,
3030
- example: offerCursorExample
3223
+ example: offerCursorExample,
3224
+ description: "Pagination cursor. Offer hash (0x...) for maker queries; base64url-encoded cursor for obligation queries."
3031
3225
  })], OfferListResponse.prototype, "cursor", void 0);
3032
3226
  __decorate([ApiProperty({
3033
3227
  type: () => [OfferListItemResponse],
@@ -3398,6 +3592,28 @@ ValidateController = __decorate([ApiTags("Make"), ApiResponse({
3398
3592
  description: "Bad Request",
3399
3593
  type: BadRequestResponse
3400
3594
  })], ValidateController);
3595
+ let CallbacksController = class CallbacksController {
3596
+ async resolveCallbackTypes() {}
3597
+ };
3598
+ __decorate([
3599
+ ApiOperation({
3600
+ methods: ["post"],
3601
+ path: "/v1/callbacks",
3602
+ summary: "Resolve callback types",
3603
+ description: "Returns callback types for callback addresses grouped by chain."
3604
+ }),
3605
+ ApiBody({ type: CallbackTypesRequest }),
3606
+ ApiResponse({
3607
+ status: 200,
3608
+ description: "Success",
3609
+ type: CallbackTypesSuccessResponse
3610
+ })
3611
+ ], CallbacksController.prototype, "resolveCallbackTypes", null);
3612
+ CallbacksController = __decorate([ApiTags("Make"), ApiResponse({
3613
+ status: 400,
3614
+ description: "Bad Request",
3615
+ type: BadRequestResponse
3616
+ })], CallbacksController);
3401
3617
  let OffersController = class OffersController {
3402
3618
  async getOffers() {}
3403
3619
  };
@@ -3519,54 +3735,227 @@ __decorate([
3519
3735
  })
3520
3736
  ], HealthController.prototype, "getChainsHealth", null);
3521
3737
  HealthController = __decorate([ApiTags("System")], HealthController);
3522
- const callbacksExample = [Type$1.BuyWithEmptyCallback];
3523
- const chainConfigExample = {
3738
+ const configContractsExample = {
3524
3739
  chain_id: 505050505,
3525
- contracts: { mempool: "0xD946246695A9259F3B33a78629026F61B3Ab40aF" },
3526
- callbacks: callbacksExample
3740
+ address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
3741
+ name: "mempool"
3742
+ };
3743
+ const configContractsPayloadExample = [
3744
+ {
3745
+ chain_id: 505050505,
3746
+ address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
3747
+ name: "mempool"
3748
+ },
3749
+ {
3750
+ chain_id: 505050505,
3751
+ address: "0x8A409D5D6394fC197c596d4E6E2c35e5d13f8a4d",
3752
+ name: "multicall"
3753
+ },
3754
+ {
3755
+ chain_id: 505050505,
3756
+ address: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
3757
+ name: "v2"
3758
+ }
3759
+ ];
3760
+ const configRulesMaturityExample = {
3761
+ type: "maturity",
3762
+ chain_id: 1,
3763
+ name: "end_of_next_month",
3764
+ timestamp: 1730415600
3765
+ };
3766
+ const configRulesCallbackExample = {
3767
+ type: "callback",
3768
+ chain_id: 1,
3769
+ address: "0x1111111111111111111111111111111111111111",
3770
+ callback_type: "sell_erc20_callback"
3527
3771
  };
3528
- var ConfigContractsResponse = class {};
3772
+ const configRulesLoanTokenExample = {
3773
+ type: "loan_token",
3774
+ chain_id: 1,
3775
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3776
+ };
3777
+ const configRulesOracleExample = {
3778
+ type: "oracle",
3779
+ chain_id: 1,
3780
+ address: "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83"
3781
+ };
3782
+ const configRulesChecksumExample = "f1d2d2f924e986ac86fdf7b36c94bcdf";
3783
+ const configRulesPayloadExample = [
3784
+ configRulesMaturityExample,
3785
+ configRulesCallbackExample,
3786
+ configRulesLoanTokenExample,
3787
+ configRulesOracleExample
3788
+ ];
3789
+ const configContractNames = [
3790
+ "mempool",
3791
+ "multicall",
3792
+ "v2"
3793
+ ];
3794
+ const configContractsCursorExample = "505050505:0xd946246695a9259f3b33a78629026f61b3ab40af";
3795
+ var ConfigContractResponse = class {};
3796
+ __decorate([ApiProperty({
3797
+ type: "number",
3798
+ example: configContractsExample.chain_id
3799
+ })], ConfigContractResponse.prototype, "chain_id", void 0);
3529
3800
  __decorate([ApiProperty({
3530
3801
  type: "string",
3531
- example: chainConfigExample.contracts.mempool
3532
- })], ConfigContractsResponse.prototype, "mempool", void 0);
3533
- var ConfigDataResponse = class {};
3802
+ example: configContractsExample.address
3803
+ })], ConfigContractResponse.prototype, "address", void 0);
3804
+ __decorate([ApiProperty({
3805
+ type: "string",
3806
+ enum: configContractNames,
3807
+ example: configContractsExample.name
3808
+ })], ConfigContractResponse.prototype, "name", void 0);
3809
+ var ConfigContractsSuccessResponse = class extends SuccessResponse {};
3810
+ __decorate([ApiProperty({
3811
+ type: "string",
3812
+ nullable: true,
3813
+ example: null
3814
+ })], ConfigContractsSuccessResponse.prototype, "cursor", void 0);
3815
+ __decorate([ApiProperty({
3816
+ type: () => [ConfigContractResponse],
3817
+ description: "Indexer contract configuration for all indexed chains.",
3818
+ example: configContractsPayloadExample
3819
+ })], ConfigContractsSuccessResponse.prototype, "data", void 0);
3820
+ var ConfigRulesMeta = class {};
3821
+ __decorate([ApiProperty({
3822
+ type: "string",
3823
+ example: timestampExample
3824
+ })], ConfigRulesMeta.prototype, "timestamp", void 0);
3825
+ __decorate([ApiProperty({
3826
+ type: "string",
3827
+ example: configRulesChecksumExample
3828
+ })], ConfigRulesMeta.prototype, "checksum", void 0);
3829
+ var ConfigRulesRuleResponse = class {};
3830
+ __decorate([ApiProperty({
3831
+ type: "string",
3832
+ example: configRulesMaturityExample.type
3833
+ })], ConfigRulesRuleResponse.prototype, "type", void 0);
3534
3834
  __decorate([ApiProperty({
3535
3835
  type: "number",
3536
- example: chainConfigExample.chain_id
3537
- })], ConfigDataResponse.prototype, "chain_id", void 0);
3538
- __decorate([ApiProperty({ type: () => ConfigContractsResponse })], ConfigDataResponse.prototype, "contracts", void 0);
3836
+ example: configRulesMaturityExample.chain_id
3837
+ })], ConfigRulesRuleResponse.prototype, "chain_id", void 0);
3539
3838
  __decorate([ApiProperty({
3540
- type: () => [String],
3541
- enum: Object.values(Type$1),
3542
- description: "Supported callback types for this chain.",
3543
- example: callbacksExample
3544
- })], ConfigDataResponse.prototype, "callbacks", void 0);
3545
- var ConfigSuccessResponse = class extends SuccessResponse {};
3839
+ type: "string",
3840
+ example: configRulesMaturityExample.name,
3841
+ required: false
3842
+ })], ConfigRulesRuleResponse.prototype, "name", void 0);
3843
+ __decorate([ApiProperty({
3844
+ type: "number",
3845
+ example: configRulesMaturityExample.timestamp,
3846
+ required: false
3847
+ })], ConfigRulesRuleResponse.prototype, "timestamp", void 0);
3848
+ __decorate([ApiProperty({
3849
+ type: "string",
3850
+ example: configRulesCallbackExample.address,
3851
+ required: false
3852
+ })], ConfigRulesRuleResponse.prototype, "address", void 0);
3853
+ __decorate([ApiProperty({
3854
+ type: "string",
3855
+ example: configRulesCallbackExample.callback_type,
3856
+ required: false
3857
+ })], ConfigRulesRuleResponse.prototype, "callback_type", void 0);
3858
+ var ConfigRulesSuccessResponse = class {};
3859
+ __decorate([ApiProperty({ type: () => ConfigRulesMeta })], ConfigRulesSuccessResponse.prototype, "meta", void 0);
3546
3860
  __decorate([ApiProperty({
3547
3861
  type: "string",
3548
3862
  nullable: true,
3549
3863
  example: null
3550
- })], ConfigSuccessResponse.prototype, "cursor", void 0);
3551
- __decorate([ApiProperty({
3552
- type: () => [ConfigDataResponse],
3553
- description: "Array of chain configurations for all indexed chains.",
3554
- example: [chainConfigExample]
3555
- })], ConfigSuccessResponse.prototype, "data", void 0);
3556
- let ConfigController = class ConfigController {
3557
- async getConfig() {}
3864
+ })], ConfigRulesSuccessResponse.prototype, "cursor", void 0);
3865
+ __decorate([ApiProperty({
3866
+ type: () => [ConfigRulesRuleResponse],
3867
+ description: "Configured rules returned by the router API.",
3868
+ example: configRulesPayloadExample
3869
+ })], ConfigRulesSuccessResponse.prototype, "data", void 0);
3870
+ let ConfigContractsController = class ConfigContractsController {
3871
+ async getConfigContracts() {}
3872
+ };
3873
+ __decorate([
3874
+ ApiOperation({
3875
+ methods: ["get"],
3876
+ path: "/v1/config/contracts",
3877
+ summary: "Get indexer contract configuration",
3878
+ description: "Returns contract addresses used by indexers (mempool, v2) and multicall for indexed chains."
3879
+ }),
3880
+ ApiQuery({
3881
+ name: "cursor",
3882
+ type: "string",
3883
+ required: false,
3884
+ example: configContractsCursorExample,
3885
+ description: "Pagination cursor in chain_id:address format (lowercase address)."
3886
+ }),
3887
+ ApiQuery({
3888
+ name: "limit",
3889
+ type: "number",
3890
+ required: false,
3891
+ example: 1e3,
3892
+ description: "Maximum number of contracts to return (max 1000)."
3893
+ }),
3894
+ ApiQuery({
3895
+ name: "chains",
3896
+ type: ["number"],
3897
+ required: false,
3898
+ example: "1,8453",
3899
+ description: "Filter by chain IDs (comma-separated).",
3900
+ style: "form",
3901
+ explode: false
3902
+ }),
3903
+ ApiResponse({
3904
+ status: 200,
3905
+ description: "Success",
3906
+ type: ConfigContractsSuccessResponse
3907
+ })
3908
+ ], ConfigContractsController.prototype, "getConfigContracts", null);
3909
+ ConfigContractsController = __decorate([ApiTags("System")], ConfigContractsController);
3910
+ let ConfigRulesController = class ConfigRulesController {
3911
+ async getConfigRules() {}
3558
3912
  };
3559
- __decorate([ApiOperation({
3560
- methods: ["get"],
3561
- path: "/v1/config",
3562
- summary: "Get router configuration",
3563
- description: "Returns chain configurations including contract addresses and supported callback types."
3564
- }), ApiResponse({
3565
- status: 200,
3566
- description: "Success",
3567
- type: ConfigSuccessResponse
3568
- })], ConfigController.prototype, "getConfig", null);
3569
- ConfigController = __decorate([ApiTags("System")], ConfigController);
3913
+ __decorate([
3914
+ ApiOperation({
3915
+ methods: ["get"],
3916
+ path: "/v1/config/rules",
3917
+ summary: "Get config rules",
3918
+ description: "Returns configured rules for supported chains."
3919
+ }),
3920
+ ApiQuery({
3921
+ name: "cursor",
3922
+ type: "string",
3923
+ required: false,
3924
+ example: "maturity:1:1730415600:end_of_next_month",
3925
+ description: "Pagination cursor in type:chain_id:<value> format."
3926
+ }),
3927
+ ApiQuery({
3928
+ name: "limit",
3929
+ type: "number",
3930
+ required: false,
3931
+ example: 100,
3932
+ description: "Maximum number of rules to return (max 1000)."
3933
+ }),
3934
+ ApiQuery({
3935
+ name: "types",
3936
+ type: ["string"],
3937
+ required: false,
3938
+ example: "maturity,loan_token,oracle",
3939
+ description: "Filter by rule types (comma-separated).",
3940
+ style: "form",
3941
+ explode: false
3942
+ }),
3943
+ ApiQuery({
3944
+ name: "chains",
3945
+ type: ["number"],
3946
+ required: false,
3947
+ example: "1,8453",
3948
+ description: "Filter by chain IDs (comma-separated).",
3949
+ style: "form",
3950
+ explode: false
3951
+ }),
3952
+ ApiResponse({
3953
+ status: 200,
3954
+ description: "Success",
3955
+ type: ConfigRulesSuccessResponse
3956
+ })
3957
+ ], ConfigRulesController.prototype, "getConfigRules", null);
3958
+ ConfigRulesController = __decorate([ApiTags("System")], ConfigRulesController);
3570
3959
  let ObligationsController = class ObligationsController {
3571
3960
  async getObligations() {}
3572
3961
  async getObligation() {}
@@ -3695,16 +4084,18 @@ UsersController = __decorate([ApiTags("Make"), ApiResponse({
3695
4084
  description: "Bad Request",
3696
4085
  type: BadRequestResponse
3697
4086
  })], UsersController);
3698
- const OpenApi = async (options = {}) => {
3699
- const document = await generateDocument({
4087
+ const OpenApi = async () => {
4088
+ return await generateDocument({
3700
4089
  controllers: [
3701
4090
  BooksController,
3702
- ConfigController,
4091
+ ConfigContractsController,
4092
+ ConfigRulesController,
3703
4093
  OffersController,
3704
4094
  ObligationsController,
3705
4095
  HealthController,
3706
4096
  UsersController,
3707
- ValidateController
4097
+ ValidateController,
4098
+ CallbacksController
3708
4099
  ],
3709
4100
  document: {
3710
4101
  openapi: "3.1.0",
@@ -3736,12 +4127,6 @@ const OpenApi = async (options = {}) => {
3736
4127
  ]
3737
4128
  }
3738
4129
  });
3739
- if (options.rules && options.rules.length > 0) {
3740
- const rulesDescription = options.rules.map((rule) => `- **${rule.name}**: ${rule.description}`).join("\n");
3741
- const validatePath = document.paths?.["/v1/validate"];
3742
- 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}`;
3743
- }
3744
- return document;
3745
4130
  };
3746
4131
 
3747
4132
  //#endregion
@@ -3766,6 +4151,10 @@ function from$1(position) {
3766
4151
  //#region src/api/Schema/requests.ts
3767
4152
  const MAX_LIMIT = 100;
3768
4153
  const DEFAULT_LIMIT = 20;
4154
+ const CONFIG_RULES_MAX_LIMIT = 1e3;
4155
+ const CONFIG_RULES_DEFAULT_LIMIT = 100;
4156
+ const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
4157
+ const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
3769
4158
  /** Validate cursor is a valid base64url-encoded JSON object.
3770
4159
  * Domain layer handles semantic validation of cursor fields. */
3771
4160
  function isValidBase64urlJson(val) {
@@ -3777,6 +4166,9 @@ function isValidBase64urlJson(val) {
3777
4166
  return false;
3778
4167
  }
3779
4168
  }
4169
+ function isValidOfferHashCursor(val) {
4170
+ return /^0x[a-f0-9]{64}$/i.test(val);
4171
+ }
3780
4172
  const csvArray = (schema) => z$1.preprocess((value) => {
3781
4173
  if (value === void 0) return void 0;
3782
4174
  if (Array.isArray(value)) {
@@ -3799,8 +4191,49 @@ const PaginationQueryParams = z$1.object({
3799
4191
  example: 10
3800
4192
  })
3801
4193
  });
3802
- const GetOffersQueryParams = z$1.object({
3803
- ...PaginationQueryParams.shape,
4194
+ const ConfigRuleTypes = z$1.enum([
4195
+ "maturity",
4196
+ "callback",
4197
+ "loan_token",
4198
+ "oracle"
4199
+ ]);
4200
+ const GetConfigRulesQueryParams = z$1.object({
4201
+ cursor: z$1.string().regex(/^(maturity|callback|loan_token|oracle):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
4202
+ description: "Pagination cursor in type:chain_id:<value> format",
4203
+ example: "maturity:1:1730415600:end_of_next_month"
4204
+ }),
4205
+ limit: z$1.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$1.number().max(CONFIG_RULES_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_RULES_MAX_LIMIT}` })).optional().default(CONFIG_RULES_DEFAULT_LIMIT).meta({
4206
+ description: `Limit maximum: ${CONFIG_RULES_MAX_LIMIT}. Default: ${CONFIG_RULES_DEFAULT_LIMIT}`,
4207
+ example: 100
4208
+ }),
4209
+ types: csvArray(ConfigRuleTypes).meta({
4210
+ description: "Filter by rule types (comma-separated).",
4211
+ example: "maturity,loan_token,oracle"
4212
+ }),
4213
+ chains: csvArray(z$1.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
4214
+ description: "Filter by chain IDs (comma-separated).",
4215
+ example: "1,8453"
4216
+ })
4217
+ });
4218
+ const GetConfigContractsQueryParams = z$1.object({
4219
+ cursor: z$1.string().regex(/^[1-9]\d*:0x[a-fA-F0-9]{40}$/, { message: "Cursor must be in the format chain_id:0x..." }).optional().meta({
4220
+ description: "Pagination cursor in chain_id:address format (lowercase address).",
4221
+ example: "1:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
4222
+ }),
4223
+ limit: z$1.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$1.number().max(CONFIG_CONTRACTS_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_CONTRACTS_MAX_LIMIT}` })).optional().default(CONFIG_CONTRACTS_DEFAULT_LIMIT).meta({
4224
+ description: `Limit maximum: ${CONFIG_CONTRACTS_MAX_LIMIT}. Default: ${CONFIG_CONTRACTS_DEFAULT_LIMIT}`,
4225
+ example: 1e3
4226
+ }),
4227
+ chains: csvArray(z$1.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
4228
+ description: "Filter by chain IDs (comma-separated).",
4229
+ example: "1,8453"
4230
+ })
4231
+ });
4232
+ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend({
4233
+ cursor: z$1.string().optional().meta({
4234
+ description: "Pagination cursor. Use offer hash (0x...) for maker queries, base64url for obligation queries.",
4235
+ example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
4236
+ }),
3804
4237
  side: z$1.enum(["buy", "sell"]).optional().meta({
3805
4238
  description: "Side of the offer. Required when using obligation_id.",
3806
4239
  example: "buy"
@@ -3824,11 +4257,29 @@ const GetOffersQueryParams = z$1.object({
3824
4257
  });
3825
4258
  return;
3826
4259
  }
3827
- if (hasMaker) return;
4260
+ if (hasMaker) {
4261
+ if (val.cursor !== void 0 && !isValidOfferHashCursor(val.cursor)) ctx.addIssue({
4262
+ code: "custom",
4263
+ path: ["cursor"],
4264
+ message: "Cursor must be a 32-byte hex offer hash when filtering by maker"
4265
+ });
4266
+ return;
4267
+ }
3828
4268
  if (!hasObligation || !hasSide) ctx.addIssue({
3829
4269
  code: "custom",
3830
4270
  message: "Must provide either maker or both obligation_id and side"
3831
4271
  });
4272
+ if (val.cursor !== void 0 && !isValidBase64urlJson(val.cursor)) ctx.addIssue({
4273
+ code: "custom",
4274
+ path: ["cursor"],
4275
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
4276
+ });
4277
+ }).transform((val) => {
4278
+ if (val.maker && val.cursor) return {
4279
+ ...val,
4280
+ cursor: val.cursor.toLowerCase()
4281
+ };
4282
+ return val;
3832
4283
  });
3833
4284
  const GetObligationsQueryParams = z$1.object({
3834
4285
  ...PaginationQueryParams.shape,
@@ -3901,6 +4352,16 @@ const GetBookParams = z$1.object({
3901
4352
  })
3902
4353
  });
3903
4354
  const ValidateOffersBody = z$1.object({ offers: z$1.array(z$1.unknown()).min(1, { message: "'offers' must contain at least 1 offer" }) }).strict();
4355
+ const CallbackTypesBody = z$1.object({ callbacks: z$1.array(z$1.object({
4356
+ chain_id: z$1.number().int().positive().meta({
4357
+ description: "Chain id.",
4358
+ example: 1
4359
+ }),
4360
+ addresses: z$1.array(z$1.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Callback address must be a valid 20-byte address" }).transform((val) => val.toLowerCase())).meta({
4361
+ description: "Callback contract addresses.",
4362
+ example: ["0x1111111111111111111111111111111111111111", "0x3333333333333333333333333333333333333333"]
4363
+ })
4364
+ }).strict()) }).strict();
3904
4365
  const GetUserPositionsParams = z$1.object({
3905
4366
  ...PaginationQueryParams.shape,
3906
4367
  user_address: z$1.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "User address must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).meta({
@@ -3912,11 +4373,14 @@ const schemas = {
3912
4373
  get_health: HealthQueryParams,
3913
4374
  get_health_collectors: HealthQueryParams,
3914
4375
  get_health_chains: HealthQueryParams,
4376
+ get_config_contracts: GetConfigContractsQueryParams,
4377
+ get_config_rules: GetConfigRulesQueryParams,
3915
4378
  get_offers: GetOffersQueryParams,
3916
4379
  get_obligations: GetObligationsQueryParams,
3917
4380
  get_obligation: GetObligationParams,
3918
4381
  get_book: GetBookParams,
3919
4382
  validate_offers: ValidateOffersBody,
4383
+ callback_types: CallbackTypesBody,
3920
4384
  get_user_positions: GetUserPositionsParams
3921
4385
  };
3922
4386
  function parse(action, query) {
@@ -3931,11 +4395,13 @@ function safeParse(action, query, error) {
3931
4395
  var Schema_exports = /* @__PURE__ */ __exportAll({
3932
4396
  BookResponse: () => BookResponse_exports,
3933
4397
  BooksController: () => BooksController,
4398
+ CallbacksController: () => CallbacksController,
3934
4399
  ChainHealth: () => ChainHealth,
3935
4400
  ChainsHealthResponse: () => ChainsHealthResponse,
3936
4401
  CollectorHealth: () => CollectorHealth,
3937
4402
  CollectorsHealthResponse: () => CollectorsHealthResponse,
3938
- ConfigController: () => ConfigController,
4403
+ ConfigContractsController: () => ConfigContractsController,
4404
+ ConfigRulesController: () => ConfigRulesController,
3939
4405
  HealthController: () => HealthController,
3940
4406
  ObligationResponse: () => ObligationResponse_exports,
3941
4407
  ObligationsController: () => ObligationsController,
@@ -4141,12 +4607,14 @@ function createHttpClient(config) {
4141
4607
  const fetchFn = config.fetchFn ?? fetch;
4142
4608
  const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
4143
4609
  const baseUrl = normalizeBaseUrl(config.baseUrl);
4610
+ const baseHeaders = config.originSecret ? { "x-origin-verify": config.originSecret } : void 0;
4144
4611
  const request = async (path, init) => {
4145
4612
  const controller = new AbortController();
4146
4613
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
4147
4614
  try {
4148
4615
  return await fetchFn(`${baseUrl}${path}`, {
4149
4616
  ...init,
4617
+ headers: mergeHeaders(baseHeaders, init.headers),
4150
4618
  signal: controller.signal
4151
4619
  });
4152
4620
  } finally {
@@ -4165,12 +4633,20 @@ function createHttpClient(config) {
4165
4633
  body: json
4166
4634
  };
4167
4635
  };
4168
- const getRules = async () => {
4169
- const response = await request("/v1/rules", { method: "GET" });
4636
+ const getConfigRules = async (query) => {
4637
+ const params = new URLSearchParams();
4638
+ if (query?.cursor) params.set("cursor", query.cursor);
4639
+ if (query?.limit !== void 0) params.set("limit", query.limit.toString());
4640
+ if (query?.types !== void 0) {
4641
+ const typesValue = Array.isArray(query.types) ? query.types.join(",") : query.types;
4642
+ if (typesValue.length > 0) params.set("types", typesValue);
4643
+ }
4644
+ const response = await request(params.size > 0 ? `/v1/config/rules?${params.toString()}` : "/v1/config/rules", { method: "GET" });
4170
4645
  const json = await response.json();
4171
- if (!response.ok) throw new Error(`Gatekeeper rules request failed: ${extractErrorMessage(json) ?? response.statusText}`);
4172
- if (!("data" in json) || !Array.isArray(json.data)) throw new Error("Gatekeeper rules response is invalid.");
4173
- return json.data;
4646
+ return {
4647
+ statusCode: response.status,
4648
+ body: json
4649
+ };
4174
4650
  };
4175
4651
  const isAllowed = async (offers) => {
4176
4652
  const { statusCode, body } = await validate({ offers: offers.map((offer) => toSnakeCase(offer)) });
@@ -4198,13 +4674,31 @@ function createHttpClient(config) {
4198
4674
  issues: []
4199
4675
  };
4200
4676
  };
4677
+ const getCallbackTypes = async (requestPayload) => {
4678
+ const response = await request("/v1/callbacks", {
4679
+ method: "POST",
4680
+ headers: { "content-type": "application/json" },
4681
+ body: JSON.stringify(requestPayload)
4682
+ });
4683
+ const json = await response.json();
4684
+ if (!response.ok) throw new Error(`Gatekeeper callbacks request failed: ${extractErrorMessage(json) ?? response.statusText}`);
4685
+ if (!("data" in json) || !Array.isArray(json.data)) throw new Error("Gatekeeper callbacks response is invalid.");
4686
+ return json.data;
4687
+ };
4201
4688
  return {
4202
4689
  baseUrl,
4203
4690
  validate,
4691
+ getConfigRules,
4204
4692
  isAllowed,
4205
- getRules
4693
+ getCallbackTypes
4206
4694
  };
4207
4695
  }
4696
+ function mergeHeaders(base, extra) {
4697
+ if (!base && !extra) return void 0;
4698
+ const merged = new Headers(base ?? void 0);
4699
+ if (extra) for (const [key, value] of new Headers(extra).entries()) merged.set(key, value);
4700
+ return merged;
4701
+ }
4208
4702
  function normalizeBaseUrl(url) {
4209
4703
  return url.trim().replace(/\/+$/, "");
4210
4704
  }
@@ -4300,16 +4794,26 @@ async function run(parameters) {
4300
4794
  };
4301
4795
  }
4302
4796
 
4797
+ //#endregion
4798
+ //#region src/gatekeeper/Gatekeeper.ts
4799
+ var Gatekeeper_exports = /* @__PURE__ */ __exportAll({ create: () => create });
4800
+ /**
4801
+ * Create a gatekeeper instance with the provided rules.
4802
+ * @param parameters - Gatekeeper parameters. {@link GatekeeperParameters}
4803
+ * @returns Gatekeeper instance. {@link Gatekeeper}
4804
+ */
4805
+ function create(parameters) {
4806
+ const { rules } = parameters;
4807
+ return { isAllowed: async (offers) => {
4808
+ return await run({
4809
+ items: offers,
4810
+ rules
4811
+ });
4812
+ } };
4813
+ }
4814
+
4303
4815
  //#endregion
4304
4816
  //#region src/gatekeeper/GateConfig.ts
4305
- var GateConfig_exports = /* @__PURE__ */ __exportAll({
4306
- assets: () => assets,
4307
- configs: () => configs,
4308
- getCallback: () => getCallback,
4309
- getCallbackAddresses: () => getCallbackAddresses,
4310
- getCallbackType: () => getCallbackType,
4311
- getCallbackTypeAddresses: () => getCallbackTypeAddresses
4312
- });
4313
4817
  /**
4314
4818
  * Returns the callback configuration for a given chain and callback type, if it exists.
4315
4819
  *
@@ -4332,17 +4836,6 @@ function getCallbackType(chain, address) {
4332
4836
  return configs[chain].callbacks?.find((c) => c.type !== Type$1.BuyWithEmptyCallback && c.addresses.includes(address?.toLowerCase()))?.type;
4333
4837
  }
4334
4838
  /**
4335
- * Returns the callback addresses for a given chain and callback type, if it exists.
4336
- * @param chain - Chain name for which to read the validation configuration
4337
- * @param type - Callback type to retrieve
4338
- * @returns The matching callback addresses or an empty array if not configured
4339
- */
4340
- function getCallbackTypeAddresses(chain, type) {
4341
- if (type === Type$1.BuyWithEmptyCallback) return [];
4342
- const match = configs[chain].callbacks?.find((c) => c.type === type);
4343
- return match && "addresses" in match ? match.addresses : [];
4344
- }
4345
- /**
4346
4839
  * Returns the list of allowed non-empty callback addresses for a chain.
4347
4840
  *
4348
4841
  * @param chain - Chain name
@@ -4356,13 +4849,18 @@ const assets = {
4356
4849
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
4357
4850
  "0x6B175474E89094C44Da98b954EedeAC495271d0F",
4358
4851
  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
4359
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
4852
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
4853
+ "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
4854
+ "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
4360
4855
  ],
4361
4856
  [ChainId.BASE.toString()]: [
4362
4857
  "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
4363
4858
  "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
4364
4859
  "0x4200000000000000000000000000000000000006",
4365
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
4860
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
4861
+ "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
4862
+ "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
4863
+ "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
4366
4864
  ],
4367
4865
  [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
4368
4866
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
@@ -4378,6 +4876,43 @@ const assets = {
4378
4876
  "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
4379
4877
  ]
4380
4878
  };
4879
+ const oracles = {
4880
+ [ChainId.ETHEREUM.toString()]: [
4881
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
4882
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
4883
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
4884
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
4885
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
4886
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
4887
+ ],
4888
+ [ChainId.BASE.toString()]: [
4889
+ "0xD09048c8B568Dbf5f189302beA26c9edABFC4858",
4890
+ "0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4",
4891
+ "0x05D2618404668D725B66c0f32B39e4EC15B393dC",
4892
+ "0xE1bb8E5b4930eC9FeC7f7943FCF6227649F14B37",
4893
+ "0x663BECd10daE6C4A3Dcd89F1d76c1174199639B9",
4894
+ "0x10b95702a0ce895972C91e432C4f7E19811D320E",
4895
+ "0x8C87DbD7A0c647A4291592Bc2994dbF95880fE2F",
4896
+ "0x4A11590e5326138B514E08A9B52202D42077Ca65",
4897
+ "0xa54122f0E0766258377Ffe732e454A3248f454F4"
4898
+ ],
4899
+ [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
4900
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
4901
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
4902
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
4903
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
4904
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
4905
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
4906
+ ],
4907
+ [ChainId.ANVIL.toString()]: [
4908
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
4909
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
4910
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
4911
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
4912
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
4913
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
4914
+ ]
4915
+ };
4381
4916
  const configs = {
4382
4917
  ethereum: {
4383
4918
  callbacks: [
@@ -4441,30 +4976,6 @@ const configs = {
4441
4976
  }
4442
4977
  };
4443
4978
 
4444
- //#endregion
4445
- //#region src/gatekeeper/Gatekeeper.ts
4446
- var Gatekeeper_exports = /* @__PURE__ */ __exportAll({ create: () => create });
4447
- /**
4448
- * Create a gatekeeper instance with the provided rules.
4449
- * @param parameters - Gatekeeper parameters. {@link GatekeeperParameters}
4450
- * @returns Gatekeeper instance. {@link Gatekeeper}
4451
- */
4452
- function create(parameters) {
4453
- const { rules } = parameters;
4454
- return {
4455
- isAllowed: async (offers) => {
4456
- return await run({
4457
- items: offers,
4458
- rules
4459
- });
4460
- },
4461
- getRules: async () => rules.map((rule) => ({
4462
- name: rule.name,
4463
- description: rule.description
4464
- }))
4465
- };
4466
- }
4467
-
4468
4979
  //#endregion
4469
4980
  //#region src/gatekeeper/Rules.ts
4470
4981
  var Rules_exports = /* @__PURE__ */ __exportAll({
@@ -4472,6 +4983,7 @@ var Rules_exports = /* @__PURE__ */ __exportAll({
4472
4983
  callback: () => callback,
4473
4984
  chains: () => chains,
4474
4985
  maturity: () => maturity,
4986
+ oracle: () => oracle,
4475
4987
  sameMaker: () => sameMaker,
4476
4988
  token: () => token,
4477
4989
  validity: () => validity
@@ -4610,6 +5122,16 @@ const token = ({ assetsByChainId }) => single("token", "Validates that offer loa
4610
5122
  if (offer.collaterals.some((collateral) => !allowedAssets.includes(collateral.asset.toLowerCase()))) return { message: "Collateral is not allowed" };
4611
5123
  });
4612
5124
  /**
5125
+ * A validation rule that checks if the offer's oracle addresses are allowed for its chain.
5126
+ * @param oraclesByChainId - Allowed oracles indexed by chain id.
5127
+ * @returns The issue that was found. If the offer is valid, this will be undefined.
5128
+ */
5129
+ const oracle = ({ oraclesByChainId }) => single("oracle", "Validates that offer collateral oracles are in the allowed oracle list for the offer chain", (offer) => {
5130
+ const allowedOracles = oraclesByChainId[offer.chainId]?.map((oracle) => oracle.toLowerCase());
5131
+ if (!allowedOracles || allowedOracles.length === 0) return { message: `No allowed oracles for chain ${offer.chainId}` };
5132
+ if (offer.collaterals.some((collateral) => !allowedOracles.includes(collateral.oracle.toLowerCase()))) return { message: "Oracle is not allowed" };
5133
+ });
5134
+ /**
4613
5135
  * A batch validation rule that ensures all offers in a tree have the same maker address.
4614
5136
  * Returns an issue only for the first non-conforming offer.
4615
5137
  * This rule is signing-agnostic; signer verification is handled at the collector level.
@@ -4640,7 +5162,11 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
4640
5162
  //#region src/gatekeeper/morphoRules.ts
4641
5163
  const morphoRules = (chains$2) => {
4642
5164
  const assetsByChainId = {};
4643
- for (const chain of chains$2) assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
5165
+ const oraclesByChainId = {};
5166
+ for (const chain of chains$2) {
5167
+ assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
5168
+ oraclesByChainId[chain.id] = oracles[chain.id.toString()] ?? [];
5169
+ }
4644
5170
  return [
4645
5171
  sameMaker(),
4646
5172
  amountMutualExclusivity(),
@@ -4654,7 +5180,8 @@ const morphoRules = (chains$2) => {
4654
5180
  ],
4655
5181
  allowedAddresses: chains$2.flatMap((c) => getCallbackAddresses(c.name))
4656
5182
  }),
4657
- token({ assetsByChainId })
5183
+ token({ assetsByChainId }),
5184
+ oracle({ oraclesByChainId })
4658
5185
  ];
4659
5186
  };
4660
5187
 
@@ -4665,6 +5192,7 @@ function from(parameters) {
4665
5192
  const config = {
4666
5193
  client: parameters.client,
4667
5194
  mempoolAddress: parameters.mempoolAddress,
5195
+ morphoAddress: parameters.morphoAddress,
4668
5196
  blockWindow: parameters.blockWindow
4669
5197
  };
4670
5198
  return {
@@ -4682,11 +5210,18 @@ function from(parameters) {
4682
5210
  */
4683
5211
  async function add(config, offers) {
4684
5212
  if (!config.client.account) throw new WalletAccountNotSetError();
4685
- const tree = from$9(offers.map((o) => from$8(o)));
5213
+ const tree = from$3(offers.map((o) => from$9(o)));
4686
5214
  const chainId = await getChainId(config.client);
4687
5215
  for (const offer of tree.offers) if (chainId !== offer.chainId) throw new ChainIdMismatchError(offer.chainId, chainId);
4688
- const signature = await sign(tree.offers, config.client);
4689
- const encoded = await encode$1(tree, signature);
5216
+ const signatureDomain$1 = resolveSignatureDomain(config, chainId);
5217
+ const signature = await config.client.signTypedData({
5218
+ account: config.client.account,
5219
+ domain: signatureDomain(signatureDomain$1),
5220
+ types: signatureTypes,
5221
+ primaryType: "Root",
5222
+ message: { root: tree.root }
5223
+ });
5224
+ const encoded = await encode(tree, signature, signatureDomain$1);
4690
5225
  try {
4691
5226
  return await config.client.sendTransaction({
4692
5227
  chain: config.client.chain,
@@ -4725,6 +5260,7 @@ const getChainId = async (client) => {
4725
5260
  };
4726
5261
  async function* streamOffers(config, parameters) {
4727
5262
  const { loanToken, blockNumberGte, blockNumberLte, order = "desc", options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = config.blockWindow } = {} } = parameters;
5263
+ const signatureDomain = resolveSignatureDomain(config, await getChainId(config.client));
4728
5264
  const stream = streamLogs({
4729
5265
  client: config.client.extend(publicActions),
4730
5266
  contractAddress: config.mempoolAddress,
@@ -4756,7 +5292,7 @@ async function* streamOffers(config, parameters) {
4756
5292
  if (!log) continue;
4757
5293
  const [payload] = decodeAbiParameters([{ type: "bytes" }], log.data);
4758
5294
  try {
4759
- const { tree } = await decode$1(payload);
5295
+ const { tree } = await decode(payload, signatureDomain);
4760
5296
  for (const offer of tree.offers) {
4761
5297
  if (loanToken && offer.loanToken.toLowerCase() !== loanToken.toLowerCase()) continue;
4762
5298
  offers.push(offer);
@@ -4791,6 +5327,21 @@ var ChainIdMismatchError = class extends BaseError {
4791
5327
  _defineProperty(this, "name", "Mempool.ChainIdMismatchError");
4792
5328
  }
4793
5329
  };
5330
+ const resolveSignatureDomain = (config, chainId) => {
5331
+ const chain = config.client.chain;
5332
+ const verifyingContract = config.morphoAddress ?? chain?.custom?.morpho?.address ?? getChain(chainId)?.custom.morpho.address;
5333
+ if (!verifyingContract || verifyingContract.toLowerCase() === zeroAddress) throw new MissingMorphoAddressError();
5334
+ return {
5335
+ chainId,
5336
+ verifyingContract
5337
+ };
5338
+ };
5339
+ var MissingMorphoAddressError = class extends BaseError {
5340
+ constructor() {
5341
+ super("Morpho address is required to verify root signatures (zero address is invalid).");
5342
+ _defineProperty(this, "name", "Mempool.MissingMorphoAddressError");
5343
+ }
5344
+ };
4794
5345
 
4795
5346
  //#endregion
4796
5347
  //#region src/mempool/MempoolClient.ts
@@ -4955,5 +5506,5 @@ var utils_exports = /* @__PURE__ */ __exportAll({
4955
5506
  });
4956
5507
 
4957
5508
  //#endregion
4958
- export { Abi_exports as Abi, BrandTypeId, Callback_exports as Callback, Chain_exports as Chain, ChainRegistry_exports as ChainRegistry, Collateral_exports as Collateral, ERC4626_exports as ERC4626, Errors_exports as Errors, Format_exports as Format, GateConfig_exports as GateConfig, Gatekeeper_exports as Gatekeeper, Client_exports as GatekeeperClient, LLTV_exports as LLTV, Liquidity_exports as Liquidity, Maturity_exports as Maturity, MempoolClient_exports as Mempool, Obligation_exports as Obligation, Offer_exports as Offer, Oracle_exports as Oracle, Position_exports as Position, Quote_exports as Quote, Schema_exports as RouterApi, Client_exports$1 as RouterClient, Rules_exports as Rules, time_exports as Time, TradingFee_exports as TradingFee, Transfer_exports as Transfer, Tree_exports as Tree, utils_exports as Utils, Gate_exports as Validation, morphoRules };
5509
+ export { Abi_exports as Abi, BrandTypeId, Callback_exports as Callback, Chain_exports as Chain, ChainRegistry_exports as ChainRegistry, Collateral_exports as Collateral, ERC4626_exports as ERC4626, Errors_exports as Errors, Format_exports as Format, Gatekeeper_exports as Gatekeeper, Client_exports as GatekeeperClient, LLTV_exports as LLTV, Liquidity_exports as Liquidity, Maturity_exports as Maturity, MempoolClient_exports as Mempool, Obligation_exports as Obligation, Offer_exports as Offer, Oracle_exports as Oracle, Position_exports as Position, Quote_exports as Quote, Schema_exports as RouterApi, Client_exports$1 as RouterClient, Rules_exports as Rules, time_exports as Time, TradingFee_exports as TradingFee, Transfer_exports as Transfer, Tree_exports as Tree, utils_exports as Utils, Gate_exports as Validation, morphoRules };
4959
5510
  //# sourceMappingURL=index.browser.mjs.map