@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.
- package/dist/cli.js +2353 -1043
- package/dist/drizzle/migrations/0023_remove-block-number-for-collaterals.sql +1 -0
- package/dist/drizzle/migrations/meta/0023_snapshot.json +1436 -0
- package/dist/drizzle/migrations/meta/_journal.json +7 -0
- package/dist/index.browser.d.mts +540 -169
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +540 -169
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +956 -411
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +958 -407
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +628 -187
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +627 -186
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +6776 -5605
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +6767 -5602
- package/dist/index.node.mjs.map +1 -1
- package/docs/integrator.md +7 -1
- package/package.json +2 -2
package/dist/index.browser.mjs
CHANGED
|
@@ -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,
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
2346
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
3523
|
-
const chainConfigExample = {
|
|
3738
|
+
const configContractsExample = {
|
|
3524
3739
|
chain_id: 505050505,
|
|
3525
|
-
|
|
3526
|
-
|
|
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
|
-
|
|
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:
|
|
3532
|
-
})],
|
|
3533
|
-
|
|
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:
|
|
3537
|
-
})],
|
|
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:
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
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
|
-
})],
|
|
3551
|
-
__decorate([ApiProperty({
|
|
3552
|
-
type: () => [
|
|
3553
|
-
description: "
|
|
3554
|
-
example:
|
|
3555
|
-
})],
|
|
3556
|
-
let
|
|
3557
|
-
async
|
|
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([
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
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 (
|
|
3699
|
-
|
|
4087
|
+
const OpenApi = async () => {
|
|
4088
|
+
return await generateDocument({
|
|
3700
4089
|
controllers: [
|
|
3701
4090
|
BooksController,
|
|
3702
|
-
|
|
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
|
|
3803
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
|
4169
|
-
const
|
|
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
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
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
|
|
4689
|
-
const
|
|
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
|
|
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,
|
|
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
|