@hadron-fi/sdk 0.2.0 → 0.3.1

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/index.js CHANGED
@@ -309,6 +309,24 @@ function decodeFeeConfig(data) {
309
309
  feeRecipient: new (0, _web3js.PublicKey)(buf.subarray(40, 72))
310
310
  };
311
311
  }
312
+ var SPREAD_TRIGGER_LEN = 40;
313
+ function decodeSpreadConfig(data) {
314
+ const buf = Buffer.from(data);
315
+ const initialized = buf.readUInt8(0) !== 0;
316
+ const bump = buf.readUInt8(1);
317
+ const numTriggers = buf.readUInt8(2);
318
+ const admin = new (0, _web3js.PublicKey)(buf.subarray(8, 40));
319
+ const config = new (0, _web3js.PublicKey)(buf.subarray(40, 72));
320
+ const triggers = [];
321
+ const triggersStart = 72;
322
+ for (let i = 0; i < numTriggers; i++) {
323
+ const off = triggersStart + i * SPREAD_TRIGGER_LEN;
324
+ const account = new (0, _web3js.PublicKey)(buf.subarray(off, off + 32));
325
+ const spreadBps = buf.readUInt16LE(off + 32);
326
+ triggers.push({ account, spreadBps });
327
+ }
328
+ return { initialized, bump, numTriggers, admin, config, triggers };
329
+ }
312
330
  function decodeCurveUpdates(data) {
313
331
  const buf = Buffer.from(data);
314
332
  let offset = 0;
@@ -1519,6 +1537,66 @@ var Hadron = class _Hadron {
1519
1537
  this.programId
1520
1538
  );
1521
1539
  }
1540
+ /** Build initialize spread config instruction. */
1541
+ initializeSpreadConfig(payer, authority, params) {
1542
+ return buildInitializeSpreadConfig(
1543
+ payer,
1544
+ authority,
1545
+ this.poolAddress,
1546
+ params,
1547
+ this.programId
1548
+ );
1549
+ }
1550
+ /** Build update spread config instruction (full replacement). */
1551
+ updateSpreadConfig(admin, params) {
1552
+ return buildUpdateSpreadConfig(
1553
+ admin,
1554
+ this.poolAddress,
1555
+ params,
1556
+ this.programId
1557
+ );
1558
+ }
1559
+ /**
1560
+ * Fetch current spread triggers, append new ones, and return the update ix.
1561
+ * If a trigger for the same account already exists, its spreadBps is updated.
1562
+ */
1563
+ async addSpreadTriggers(admin, triggers) {
1564
+ const current = await this.fetchSpreadTriggers();
1565
+ const merged = [...current];
1566
+ for (const t of triggers) {
1567
+ const existing = merged.findIndex(
1568
+ (e) => e.account.equals(t.account)
1569
+ );
1570
+ if (existing >= 0) {
1571
+ merged[existing] = t;
1572
+ } else {
1573
+ merged.push(t);
1574
+ }
1575
+ }
1576
+ return this.updateSpreadConfig(admin, { triggers: merged });
1577
+ }
1578
+ /**
1579
+ * Fetch current spread triggers, remove the given accounts, and return the update ix.
1580
+ */
1581
+ async removeSpreadTriggers(admin, accounts) {
1582
+ const current = await this.fetchSpreadTriggers();
1583
+ const removeSet = new Set(accounts.map((a) => a.toBase58()));
1584
+ const filtered = current.filter(
1585
+ (t) => !removeSet.has(t.account.toBase58())
1586
+ );
1587
+ return this.updateSpreadConfig(admin, { triggers: filtered });
1588
+ }
1589
+ /** Fetch and decode the current spread config triggers from chain. */
1590
+ async fetchSpreadTriggers() {
1591
+ const [spreadConfigPda] = getSpreadConfigAddress(
1592
+ this.poolAddress,
1593
+ this.programId
1594
+ );
1595
+ const info = await this.connection.getAccountInfo(spreadConfigPda);
1596
+ if (!info) return [];
1597
+ const decoded = decodeSpreadConfig(info.data);
1598
+ return decoded.triggers;
1599
+ }
1522
1600
  /** Build close pool instruction. */
1523
1601
  closePool(authority) {
1524
1602
  return buildClosePool(
@@ -1538,6 +1616,565 @@ var Hadron = class _Hadron {
1538
1616
  }
1539
1617
  };
1540
1618
 
1619
+ // src/orderbook.ts
1620
+ var MAX_ORDERS_PER_SIDE = MAX_SETCURVE_POINTS - 1;
1621
+ function tokensToAtoms(tokens, decimals) {
1622
+ return BigInt(Math.round(tokens * 10 ** decimals));
1623
+ }
1624
+ function atomsToTokens(atoms, decimals) {
1625
+ return Number(atoms) / 10 ** decimals;
1626
+ }
1627
+ var HadronOrderbook = class _HadronOrderbook {
1628
+ constructor(pool, decimalsX, decimalsY, initialVaultX, initialVaultY) {
1629
+ this.bidOrders = [];
1630
+ this.askOrders = [];
1631
+ this.bidsDirty = false;
1632
+ this.asksDirty = false;
1633
+ this.priceCurvesSet = false;
1634
+ this.riskBidInitialized = false;
1635
+ this.riskAskInitialized = false;
1636
+ this.committedBidPoints = [];
1637
+ this.committedAskPoints = [];
1638
+ this.pool = pool;
1639
+ this.decimalsX = decimalsX;
1640
+ this.decimalsY = decimalsY;
1641
+ this.initialVaultX = initialVaultX;
1642
+ this.initialVaultY = initialVaultY;
1643
+ }
1644
+ // ==========================================================================
1645
+ // Static Factories
1646
+ // ==========================================================================
1647
+ /**
1648
+ * Load an orderbook from an existing on-chain pool.
1649
+ * Fetches decimals and vault balances, and reconstructs staged orders
1650
+ * from the active risk curves.
1651
+ */
1652
+ static async load(params) {
1653
+ const { connection, pool: poolAddress } = params;
1654
+ const hadron = await Hadron.load(connection, poolAddress);
1655
+ const [mintXInfo, mintYInfo, vaultXInfo, vaultYInfo] = await Promise.all([
1656
+ connection.getAccountInfo(hadron.config.mintX),
1657
+ connection.getAccountInfo(hadron.config.mintY),
1658
+ connection.getAccountInfo(hadron.addresses.vaultX),
1659
+ connection.getAccountInfo(hadron.addresses.vaultY)
1660
+ ]);
1661
+ if (!mintXInfo || !mintYInfo) throw new Error("Mint account(s) not found");
1662
+ if (!vaultXInfo || !vaultYInfo) throw new Error("Vault account(s) not found");
1663
+ const decimalsX = mintXInfo.data[44];
1664
+ const decimalsY = mintYInfo.data[44];
1665
+ const vaultXBalance = new DataView(
1666
+ vaultXInfo.data.buffer,
1667
+ vaultXInfo.data.byteOffset
1668
+ ).getBigUint64(64, true);
1669
+ const vaultYBalance = new DataView(
1670
+ vaultYInfo.data.buffer,
1671
+ vaultYInfo.data.byteOffset
1672
+ ).getBigUint64(64, true);
1673
+ const book = new _HadronOrderbook(
1674
+ hadron,
1675
+ decimalsX,
1676
+ decimalsY,
1677
+ vaultXBalance,
1678
+ vaultYBalance
1679
+ );
1680
+ book.reconstructFromCurves();
1681
+ return book;
1682
+ }
1683
+ /**
1684
+ * Wrap an existing Hadron instance as an orderbook.
1685
+ * Useful for tests or when you already have the pool loaded.
1686
+ */
1687
+ static fromPool(pool, decimalsX, decimalsY, initialVaultX, initialVaultY) {
1688
+ return new _HadronOrderbook(
1689
+ pool,
1690
+ decimalsX,
1691
+ decimalsY,
1692
+ initialVaultX,
1693
+ initialVaultY
1694
+ );
1695
+ }
1696
+ // ==========================================================================
1697
+ // Order Management
1698
+ // ==========================================================================
1699
+ /**
1700
+ * Place a new order. Orders are staged locally and pushed on-chain via `push()`.
1701
+ * Orders on each side are sorted by spreadBps ascending (tightest first).
1702
+ * @throws If more than 31 orders on a side.
1703
+ */
1704
+ placeOrder(params) {
1705
+ const orders = params.side === "bid" ? this.bidOrders : this.askOrders;
1706
+ if (orders.length >= MAX_ORDERS_PER_SIDE) {
1707
+ throw new Error(
1708
+ `Cannot place more than ${MAX_ORDERS_PER_SIDE} orders per side`
1709
+ );
1710
+ }
1711
+ orders.push({ size: params.size, spreadBps: params.spreadBps });
1712
+ orders.sort((a, b) => a.spreadBps - b.spreadBps);
1713
+ if (params.side === "bid") this.bidsDirty = true;
1714
+ else this.asksDirty = true;
1715
+ }
1716
+ /**
1717
+ * Amend an existing order at the given level index.
1718
+ * Re-sorts if spread changes.
1719
+ * @throws On invalid index.
1720
+ */
1721
+ amendOrder(params) {
1722
+ const orders = params.side === "bid" ? this.bidOrders : this.askOrders;
1723
+ if (params.level < 0 || params.level >= orders.length) {
1724
+ throw new Error(
1725
+ `Invalid level ${params.level} for ${params.side} (have ${orders.length} orders)`
1726
+ );
1727
+ }
1728
+ const order = orders[params.level];
1729
+ if (params.spreadBps !== void 0) order.spreadBps = params.spreadBps;
1730
+ if (params.size !== void 0) order.size = params.size;
1731
+ orders.sort((a, b) => a.spreadBps - b.spreadBps);
1732
+ if (params.side === "bid") this.bidsDirty = true;
1733
+ else this.asksDirty = true;
1734
+ }
1735
+ /**
1736
+ * Cancel the order at the given level index.
1737
+ * @throws On invalid index.
1738
+ */
1739
+ cancelOrder(params) {
1740
+ const orders = params.side === "bid" ? this.bidOrders : this.askOrders;
1741
+ if (params.level < 0 || params.level >= orders.length) {
1742
+ throw new Error(
1743
+ `Invalid level ${params.level} for ${params.side} (have ${orders.length} orders)`
1744
+ );
1745
+ }
1746
+ orders.splice(params.level, 1);
1747
+ if (params.side === "bid") this.bidsDirty = true;
1748
+ else this.asksDirty = true;
1749
+ }
1750
+ /** Cancel all orders on one or both sides. */
1751
+ cancelAll(side) {
1752
+ if (!side || side === "bid") {
1753
+ this.bidOrders = [];
1754
+ this.bidsDirty = true;
1755
+ }
1756
+ if (!side || side === "ask") {
1757
+ this.askOrders = [];
1758
+ this.asksDirty = true;
1759
+ }
1760
+ }
1761
+ /** Read-only access to staged bid orders. */
1762
+ getBids() {
1763
+ return this.bidOrders;
1764
+ }
1765
+ /** Read-only access to staged ask orders. */
1766
+ getAsks() {
1767
+ return this.askOrders;
1768
+ }
1769
+ // ==========================================================================
1770
+ // Push (orders → on-chain risk curves)
1771
+ // ==========================================================================
1772
+ /**
1773
+ * Build transaction instructions to push the current staged orders on-chain.
1774
+ *
1775
+ * 1. On first push, sets flat price curves (factor=1.0, Step interpolation).
1776
+ * 2. If risk curves not yet initialized, uses setRiskCurveAbsolute (sets headers).
1777
+ * 3. On subsequent pushes, computes a minimal diff and uses curve updates
1778
+ * (submit + apply) when possible, falling back to full rewrite for large diffs.
1779
+ */
1780
+ push(authority) {
1781
+ const ixs = [];
1782
+ if (!this.priceCurvesSet) {
1783
+ const flatPoints = [
1784
+ { amountIn: 0n, priceFactor: 1, interpolation: 0 /* Step */ },
1785
+ {
1786
+ amountIn: BigInt("1000000000000000000"),
1787
+ priceFactor: 1,
1788
+ interpolation: 0 /* Step */
1789
+ }
1790
+ ];
1791
+ const [bidPriceIx, askPriceIx] = this.pool.setCurveBoth(authority, {
1792
+ bid: { defaultInterpolation: 0 /* Step */, points: flatPoints },
1793
+ ask: { defaultInterpolation: 0 /* Step */, points: flatPoints }
1794
+ });
1795
+ ixs.push(bidPriceIx, askPriceIx);
1796
+ this.priceCurvesSet = true;
1797
+ }
1798
+ if (!this.riskBidInitialized || !this.riskAskInitialized) {
1799
+ ixs.push(...this.buildFullRiskCurveIxs(authority));
1800
+ this.bidsDirty = false;
1801
+ this.asksDirty = false;
1802
+ return ixs;
1803
+ }
1804
+ const updateIxs = this.buildCurveUpdateIxs(authority);
1805
+ ixs.push(...updateIxs);
1806
+ this.bidsDirty = false;
1807
+ this.asksDirty = false;
1808
+ return ixs;
1809
+ }
1810
+ // ==========================================================================
1811
+ // Delegated Methods
1812
+ // ==========================================================================
1813
+ /** Build an updateMidprice instruction. */
1814
+ updateMidprice(authority, midprice, sequence) {
1815
+ const params = {
1816
+ midpriceQ32: BigInt(Math.floor(midprice * 2 ** 32)),
1817
+ sequence
1818
+ };
1819
+ return this.pool.updateMidprice(authority, params);
1820
+ }
1821
+ /** Build a deposit instruction. */
1822
+ deposit(user, params) {
1823
+ return this.pool.deposit(user, params);
1824
+ }
1825
+ /** Build a withdraw instruction. */
1826
+ withdraw(user, params) {
1827
+ return this.pool.withdraw(user, params);
1828
+ }
1829
+ /** Build a setPoolState instruction. */
1830
+ setPoolState(authority, params) {
1831
+ return this.pool.setPoolState(authority, params);
1832
+ }
1833
+ /**
1834
+ * Get the current book state by reading on-chain risk curves + midprice.
1835
+ * Reconstructs levels from curve points.
1836
+ */
1837
+ getBookState() {
1838
+ const midprice = this.pool.getMidprice();
1839
+ const curves = this.pool.getActiveCurves();
1840
+ const bids = this.curveSideToLevels(
1841
+ curves.riskBid,
1842
+ midprice,
1843
+ "bid",
1844
+ this.decimalsX
1845
+ );
1846
+ const asks = this.curveSideToLevels(
1847
+ curves.riskAsk,
1848
+ midprice,
1849
+ "ask",
1850
+ this.decimalsY
1851
+ );
1852
+ const base = atomsToTokens(this.initialVaultX, this.decimalsX);
1853
+ const quote = atomsToTokens(this.initialVaultY, this.decimalsY);
1854
+ return { midprice, bids, asks, inventory: { base, quote } };
1855
+ }
1856
+ // ==========================================================================
1857
+ // Curve Update Diff Algorithm
1858
+ // ==========================================================================
1859
+ /**
1860
+ * Compute the minimal set of curve update ops to transform `committed` into `desired`.
1861
+ *
1862
+ * Algorithm:
1863
+ * 1. Scan left-to-right for factor-only diffs (amountIn unchanged) → Edit ops.
1864
+ * 2. At the first structural diff (amountIn changed or length mismatch),
1865
+ * truncate-and-rebuild: remove all from that index to end, then add desired.
1866
+ * 3. Returns the ops array.
1867
+ */
1868
+ static computeCurveOps(committed, desired, curveType) {
1869
+ const ops = [];
1870
+ const minLen = Math.min(committed.length, desired.length);
1871
+ let structuralDiffIndex = minLen;
1872
+ for (let i = 0; i < minLen; i++) {
1873
+ const c = committed[i];
1874
+ const d = desired[i];
1875
+ if (c.amountIn !== d.amountIn) {
1876
+ structuralDiffIndex = i;
1877
+ break;
1878
+ }
1879
+ if (c.priceFactorQ32 !== d.priceFactorQ32 || c.interpolation !== d.interpolation) {
1880
+ ops.push({
1881
+ curveType,
1882
+ opKind: 0 /* Edit */,
1883
+ pointIndex: i,
1884
+ interpolation: d.interpolation,
1885
+ amountIn: d.amountIn,
1886
+ priceFactorQ32: d.priceFactorQ32,
1887
+ params: d.params
1888
+ });
1889
+ }
1890
+ }
1891
+ if (structuralDiffIndex === minLen && committed.length !== desired.length) {
1892
+ structuralDiffIndex = minLen;
1893
+ }
1894
+ if (structuralDiffIndex === minLen && committed.length === desired.length) {
1895
+ return ops;
1896
+ }
1897
+ const numToRemove = committed.length - structuralDiffIndex;
1898
+ for (let i = 0; i < numToRemove; i++) {
1899
+ ops.push({
1900
+ curveType,
1901
+ opKind: 2 /* Remove */,
1902
+ pointIndex: structuralDiffIndex,
1903
+ interpolation: 0 /* Step */,
1904
+ amountIn: 0n,
1905
+ priceFactorQ32: 0n,
1906
+ params: new Uint8Array(4)
1907
+ });
1908
+ }
1909
+ for (let i = structuralDiffIndex; i < desired.length; i++) {
1910
+ const d = desired[i];
1911
+ ops.push({
1912
+ curveType,
1913
+ opKind: 1 /* Add */,
1914
+ pointIndex: i,
1915
+ interpolation: d.interpolation,
1916
+ amountIn: d.amountIn,
1917
+ priceFactorQ32: d.priceFactorQ32,
1918
+ params: d.params
1919
+ });
1920
+ }
1921
+ return ops;
1922
+ }
1923
+ // ==========================================================================
1924
+ // Internal Helpers
1925
+ // ==========================================================================
1926
+ /** Convert SetRiskCurveAbsolutePointInput[] to CurvePoint[] for diffing. */
1927
+ static toCommittedPoints(points) {
1928
+ return points.map((p) => ({
1929
+ amountIn: p.vaultBalance,
1930
+ priceFactorQ32: toQ32(p.priceFactor),
1931
+ interpolation: _nullishCoalesce(p.interpolation, () => ( 0)) /* Step */,
1932
+ params: _nullishCoalesce(p.params, () => ( new Uint8Array(4)))
1933
+ }));
1934
+ }
1935
+ /**
1936
+ * Build full setRiskCurveAbsolute instructions for sides that need initialization.
1937
+ * After sending, snapshots committed points and marks sides as initialized.
1938
+ */
1939
+ buildFullRiskCurveIxs(authority) {
1940
+ const ixs = [];
1941
+ const hasBids = this.bidOrders.length > 0;
1942
+ const hasAsks = this.askOrders.length > 0;
1943
+ const bidPoints = hasBids ? this.buildBidRiskPoints() : void 0;
1944
+ const askPoints = hasAsks ? this.buildAskRiskPoints() : void 0;
1945
+ if (bidPoints && askPoints && !this.riskBidInitialized && !this.riskAskInitialized) {
1946
+ const [bidIx, askIx] = this.pool.setRiskCurveAbsoluteBoth(authority, {
1947
+ bid: {
1948
+ defaultInterpolation: 0 /* Step */,
1949
+ points: bidPoints,
1950
+ riskMode: 1 /* Integrated */
1951
+ },
1952
+ ask: {
1953
+ defaultInterpolation: 0 /* Step */,
1954
+ points: askPoints,
1955
+ riskMode: 1 /* Integrated */
1956
+ }
1957
+ });
1958
+ ixs.push(bidIx, askIx);
1959
+ this.committedBidPoints = _HadronOrderbook.toCommittedPoints(bidPoints);
1960
+ this.committedAskPoints = _HadronOrderbook.toCommittedPoints(askPoints);
1961
+ this.riskBidInitialized = true;
1962
+ this.riskAskInitialized = true;
1963
+ } else {
1964
+ if (bidPoints && !this.riskBidInitialized) {
1965
+ ixs.push(
1966
+ this.pool.setRiskCurveAbsolute(authority, {
1967
+ side: 0 /* Bid */,
1968
+ defaultInterpolation: 0 /* Step */,
1969
+ points: bidPoints,
1970
+ riskMode: 1 /* Integrated */
1971
+ })
1972
+ );
1973
+ this.committedBidPoints = _HadronOrderbook.toCommittedPoints(bidPoints);
1974
+ this.riskBidInitialized = true;
1975
+ }
1976
+ if (askPoints && !this.riskAskInitialized) {
1977
+ ixs.push(
1978
+ this.pool.setRiskCurveAbsolute(authority, {
1979
+ side: 1 /* Ask */,
1980
+ defaultInterpolation: 0 /* Step */,
1981
+ points: askPoints,
1982
+ riskMode: 1 /* Integrated */
1983
+ })
1984
+ );
1985
+ this.committedAskPoints = _HadronOrderbook.toCommittedPoints(askPoints);
1986
+ this.riskAskInitialized = true;
1987
+ }
1988
+ }
1989
+ return ixs;
1990
+ }
1991
+ /**
1992
+ * Build curve update instructions (submit + apply pairs) for both sides.
1993
+ * Falls back to full setRiskCurve rewrite if diff is too large.
1994
+ */
1995
+ buildCurveUpdateIxs(authority) {
1996
+ const bidPoints = this.buildBidRiskPoints();
1997
+ const askPoints = this.buildAskRiskPoints();
1998
+ const desiredBid = _HadronOrderbook.toCommittedPoints(bidPoints);
1999
+ const desiredAsk = _HadronOrderbook.toCommittedPoints(askPoints);
2000
+ const bidOps = _HadronOrderbook.computeCurveOps(
2001
+ this.committedBidPoints,
2002
+ desiredBid,
2003
+ 2 /* RiskBid */
2004
+ );
2005
+ const askOps = _HadronOrderbook.computeCurveOps(
2006
+ this.committedAskPoints,
2007
+ desiredAsk,
2008
+ 3 /* RiskAsk */
2009
+ );
2010
+ const allOps = [...bidOps, ...askOps];
2011
+ if (allOps.length === 0) {
2012
+ return [];
2013
+ }
2014
+ if (allOps.length > MAX_CURVE_UPDATE_OPS * 4) {
2015
+ return this.buildFallbackRewrite(authority, bidPoints, askPoints);
2016
+ }
2017
+ const ixs = [];
2018
+ for (let i = 0; i < allOps.length; i += MAX_CURVE_UPDATE_OPS) {
2019
+ const batch = allOps.slice(i, i + MAX_CURVE_UPDATE_OPS);
2020
+ ixs.push(this.pool.submitCurveUpdates(authority, batch));
2021
+ ixs.push(this.pool.applyCurveUpdates(authority));
2022
+ }
2023
+ this.committedBidPoints = desiredBid;
2024
+ this.committedAskPoints = desiredAsk;
2025
+ return ixs;
2026
+ }
2027
+ /** Fallback: full setRiskCurve rewrite for both sides. */
2028
+ buildFallbackRewrite(authority, bidPoints, askPoints) {
2029
+ const [bidIx, askIx] = this.pool.setRiskCurveAbsoluteBoth(authority, {
2030
+ bid: {
2031
+ defaultInterpolation: 0 /* Step */,
2032
+ points: bidPoints,
2033
+ riskMode: 1 /* Integrated */
2034
+ },
2035
+ ask: {
2036
+ defaultInterpolation: 0 /* Step */,
2037
+ points: askPoints,
2038
+ riskMode: 1 /* Integrated */
2039
+ }
2040
+ });
2041
+ this.committedBidPoints = _HadronOrderbook.toCommittedPoints(bidPoints);
2042
+ this.committedAskPoints = _HadronOrderbook.toCommittedPoints(askPoints);
2043
+ return [bidIx, askIx];
2044
+ }
2045
+ buildBidRiskPoints() {
2046
+ const points = [];
2047
+ let cumulativeAtoms = this.initialVaultX;
2048
+ for (const order of this.bidOrders) {
2049
+ const priceFactor = 1 - order.spreadBps / 1e4;
2050
+ points.push({
2051
+ vaultBalance: cumulativeAtoms,
2052
+ priceFactor,
2053
+ interpolation: 0 /* Step */
2054
+ });
2055
+ cumulativeAtoms += tokensToAtoms(order.size, this.decimalsX);
2056
+ }
2057
+ const lastFactor = this.bidOrders.length > 0 ? 1 - this.bidOrders[this.bidOrders.length - 1].spreadBps / 1e4 : 1;
2058
+ points.push({
2059
+ vaultBalance: cumulativeAtoms,
2060
+ priceFactor: lastFactor,
2061
+ interpolation: 0 /* Step */
2062
+ });
2063
+ return points;
2064
+ }
2065
+ buildAskRiskPoints() {
2066
+ const points = [];
2067
+ let cumulativeAtoms = this.initialVaultY;
2068
+ const midprice = this.pool.getMidprice();
2069
+ for (const order of this.askOrders) {
2070
+ const priceFactor = 1 + order.spreadBps / 1e4;
2071
+ points.push({
2072
+ vaultBalance: cumulativeAtoms,
2073
+ priceFactor,
2074
+ interpolation: 0 /* Step */
2075
+ });
2076
+ const quoteSizeTokens = order.size * midprice;
2077
+ cumulativeAtoms += tokensToAtoms(quoteSizeTokens, this.decimalsY);
2078
+ }
2079
+ const lastFactor = this.askOrders.length > 0 ? 1 + this.askOrders[this.askOrders.length - 1].spreadBps / 1e4 : 1;
2080
+ points.push({
2081
+ vaultBalance: cumulativeAtoms,
2082
+ priceFactor: lastFactor,
2083
+ interpolation: 0 /* Step */
2084
+ });
2085
+ return points;
2086
+ }
2087
+ /**
2088
+ * Reconstruct staged orders from on-chain risk curves (after load).
2089
+ * Assumes curves were set via this class (CurveXMode.Alternate, RiskMode.Integrated, Step).
2090
+ */
2091
+ reconstructFromCurves() {
2092
+ const curves = this.pool.getActiveCurves();
2093
+ if (curves.riskBid.xMode === 1 /* Alternate */ && curves.riskBid.riskMode === 1 /* Integrated */ && curves.riskBid.numPoints >= 2) {
2094
+ this.riskBidInitialized = true;
2095
+ this.priceCurvesSet = true;
2096
+ this.committedBidPoints = curves.riskBid.points.map((p) => ({
2097
+ amountIn: p.amountIn,
2098
+ priceFactorQ32: p.priceFactorQ32,
2099
+ interpolation: p.interpolation,
2100
+ params: new Uint8Array(p.params)
2101
+ }));
2102
+ }
2103
+ if (curves.riskAsk.xMode === 1 /* Alternate */ && curves.riskAsk.riskMode === 1 /* Integrated */ && curves.riskAsk.numPoints >= 2) {
2104
+ this.riskAskInitialized = true;
2105
+ this.priceCurvesSet = true;
2106
+ this.committedAskPoints = curves.riskAsk.points.map((p) => ({
2107
+ amountIn: p.amountIn,
2108
+ priceFactorQ32: p.priceFactorQ32,
2109
+ interpolation: p.interpolation,
2110
+ params: new Uint8Array(p.params)
2111
+ }));
2112
+ }
2113
+ this.bidOrders = this.reconstructSide(
2114
+ curves.riskBid,
2115
+ "bid",
2116
+ this.initialVaultX,
2117
+ this.decimalsX
2118
+ );
2119
+ this.askOrders = this.reconstructSide(
2120
+ curves.riskAsk,
2121
+ "ask",
2122
+ this.initialVaultY,
2123
+ this.decimalsY
2124
+ );
2125
+ }
2126
+ reconstructSide(curve, side, initialVault, decimals) {
2127
+ if (curve.xMode !== 1 /* Alternate */ || curve.riskMode !== 1 /* Integrated */) {
2128
+ return [];
2129
+ }
2130
+ const orders = [];
2131
+ const points = curve.points;
2132
+ for (let i = 0; i < points.length - 1; i++) {
2133
+ const pt = points[i];
2134
+ const nextPt = points[i + 1];
2135
+ const priceFactor = fromQ32(pt.priceFactorQ32);
2136
+ let spreadBps;
2137
+ if (side === "bid") {
2138
+ spreadBps = Math.round((1 - priceFactor) * 1e4);
2139
+ } else {
2140
+ spreadBps = Math.round((priceFactor - 1) * 1e4);
2141
+ }
2142
+ const sizeAtoms = nextPt.amountIn - pt.amountIn;
2143
+ let sizeTokens;
2144
+ if (side === "ask") {
2145
+ const midprice = this.pool.getMidprice();
2146
+ sizeTokens = atomsToTokens(sizeAtoms, decimals) / midprice;
2147
+ } else {
2148
+ sizeTokens = atomsToTokens(sizeAtoms, decimals);
2149
+ }
2150
+ orders.push({ size: sizeTokens, spreadBps });
2151
+ }
2152
+ return orders;
2153
+ }
2154
+ curveSideToLevels(curve, midprice, side, decimals) {
2155
+ if (curve.numPoints < 2) return [];
2156
+ const levels = [];
2157
+ let cumulative = 0;
2158
+ const points = curve.points;
2159
+ for (let i = 0; i < points.length - 1; i++) {
2160
+ const pt = points[i];
2161
+ const nextPt = points[i + 1];
2162
+ const priceFactor = fromQ32(pt.priceFactorQ32);
2163
+ const price = midprice * priceFactor;
2164
+ const sizeAtoms = nextPt.amountIn - pt.amountIn;
2165
+ let sizeTokens;
2166
+ if (side === "ask") {
2167
+ sizeTokens = atomsToTokens(sizeAtoms, decimals) / midprice;
2168
+ } else {
2169
+ sizeTokens = atomsToTokens(sizeAtoms, decimals);
2170
+ }
2171
+ cumulative += sizeTokens;
2172
+ levels.push({ price, size: sizeTokens, cumulative });
2173
+ }
2174
+ return levels;
2175
+ }
2176
+ };
2177
+
1541
2178
  // src/helpers/token.ts
1542
2179
 
1543
2180
 
@@ -1649,5 +2286,7 @@ async function getOrCreateAta(connection, mint, owner, payer, tokenProgram = _sp
1649
2286
 
1650
2287
 
1651
2288
 
1652
- exports.ABSOLUTE_MAX_CURVE_POINTS = ABSOLUTE_MAX_CURVE_POINTS; exports.ABSOLUTE_MAX_PREFAB_SLOTS = ABSOLUTE_MAX_PREFAB_SLOTS; exports.CONFIG_SEED = CONFIG_SEED; exports.CONFIG_SIZE = CONFIG_SIZE; exports.CURVE_META_SEED = CURVE_META_SEED; exports.CURVE_META_SIZE = CURVE_META_SIZE; exports.CURVE_POINT_LEN = CURVE_POINT_LEN; exports.CURVE_PREFABS_SEED = CURVE_PREFABS_SEED; exports.CURVE_SIDE_HEADER = CURVE_SIDE_HEADER; exports.CURVE_UPDATES_SEED = CURVE_UPDATES_SEED; exports.CURVE_UPDATES_SIZE = CURVE_UPDATES_SIZE; exports.CURVE_UPDATE_OP_SIZE = CURVE_UPDATE_OP_SIZE; exports.CurveType = CurveType; exports.CurveUpdateOpKind = CurveUpdateOpKind; exports.CurveXMode = CurveXMode; exports.DEFAULT_MAX_CURVE_POINTS = DEFAULT_MAX_CURVE_POINTS; exports.DEFAULT_MAX_PREFAB_SLOTS = DEFAULT_MAX_PREFAB_SLOTS; exports.Discriminator = Discriminator; exports.FEE_CONFIG_SEED = FEE_CONFIG_SEED; exports.FEE_CONFIG_SIZE = FEE_CONFIG_SIZE; exports.HADRON_PROGRAM_ID = HADRON_PROGRAM_ID; exports.Hadron = Hadron; exports.Interpolation = Interpolation; exports.MAX_CURVE_UPDATE_OPS = MAX_CURVE_UPDATE_OPS; exports.MAX_SETCURVE_POINTS = MAX_SETCURVE_POINTS; exports.MIDPRICE_ORACLE_SEED = MIDPRICE_ORACLE_SEED; exports.MIDPRICE_ORACLE_SIZE = MIDPRICE_ORACLE_SIZE; exports.OracleMode = OracleMode; exports.POINT_DATA_SIZE = POINT_DATA_SIZE; exports.PoolState = PoolState; exports.Q32_ONE = Q32_ONE; exports.RiskMode = RiskMode; exports.SPREAD_CONFIG_SEED = SPREAD_CONFIG_SEED; exports.Side = Side; exports.buildAcceptAuthority = buildAcceptAuthority; exports.buildAllocateCurvePrefabs = buildAllocateCurvePrefabs; exports.buildApplyCurveUpdates = buildApplyCurveUpdates; exports.buildClosePool = buildClosePool; exports.buildDeposit = buildDeposit; exports.buildInitialize = buildInitialize; exports.buildInitializeFeeConfig = buildInitializeFeeConfig; exports.buildInitializeSpreadConfig = buildInitializeSpreadConfig; exports.buildNominateAuthority = buildNominateAuthority; exports.buildRotateFeeAdmin = buildRotateFeeAdmin; exports.buildSetCurve = buildSetCurve; exports.buildSetCurveBoth = buildSetCurveBoth; exports.buildSetPoolState = buildSetPoolState; exports.buildSetQuotingAuthority = buildSetQuotingAuthority; exports.buildSetRiskCurve = buildSetRiskCurve; exports.buildSetRiskCurveAbsolute = buildSetRiskCurveAbsolute; exports.buildSetRiskCurveAbsoluteBoth = buildSetRiskCurveAbsoluteBoth; exports.buildSetRiskCurveBoth = buildSetRiskCurveBoth; exports.buildSubmitCurveUpdates = buildSubmitCurveUpdates; exports.buildSwapExactIn = buildSwapExactIn; exports.buildSwitchPriceCurve = buildSwitchPriceCurve; exports.buildSwitchRiskCurve = buildSwitchRiskCurve; exports.buildUpdateBaseSpread = buildUpdateBaseSpread; exports.buildUpdateDeltaStaleness = buildUpdateDeltaStaleness; exports.buildUpdateFeeConfig = buildUpdateFeeConfig; exports.buildUpdateMidprice = buildUpdateMidprice; exports.buildUpdateMidpriceAndBaseSpread = buildUpdateMidpriceAndBaseSpread; exports.buildUpdateSpreadConfig = buildUpdateSpreadConfig; exports.buildWithdraw = buildWithdraw; exports.curvePrefabsSize = curvePrefabsSize; exports.decodeActiveCurves = decodeActiveCurves; exports.decodeConfig = decodeConfig; exports.decodeCurveMeta = decodeCurveMeta; exports.decodeCurveSide = decodeCurveSide; exports.decodeCurveUpdates = decodeCurveUpdates; exports.decodeFeeConfig = decodeFeeConfig; exports.decodeMidpriceOracle = decodeMidpriceOracle; exports.derivePoolAddresses = derivePoolAddresses; exports.fromQ32 = fromQ32; exports.getConfigAddress = getConfigAddress; exports.getCurveMetaAddress = getCurveMetaAddress; exports.getCurvePrefabsAddress = getCurvePrefabsAddress; exports.getCurveUpdatesAddress = getCurveUpdatesAddress; exports.getFeeConfigAddress = getFeeConfigAddress; exports.getMidpriceOracleAddress = getMidpriceOracleAddress; exports.getOrCreateAta = getOrCreateAta; exports.getSpreadConfigAddress = getSpreadConfigAddress; exports.isSlotInitialized = isSlotInitialized; exports.pctToQ32 = pctToQ32; exports.toQ32 = toQ32;
2289
+
2290
+
2291
+ exports.ABSOLUTE_MAX_CURVE_POINTS = ABSOLUTE_MAX_CURVE_POINTS; exports.ABSOLUTE_MAX_PREFAB_SLOTS = ABSOLUTE_MAX_PREFAB_SLOTS; exports.CONFIG_SEED = CONFIG_SEED; exports.CONFIG_SIZE = CONFIG_SIZE; exports.CURVE_META_SEED = CURVE_META_SEED; exports.CURVE_META_SIZE = CURVE_META_SIZE; exports.CURVE_POINT_LEN = CURVE_POINT_LEN; exports.CURVE_PREFABS_SEED = CURVE_PREFABS_SEED; exports.CURVE_SIDE_HEADER = CURVE_SIDE_HEADER; exports.CURVE_UPDATES_SEED = CURVE_UPDATES_SEED; exports.CURVE_UPDATES_SIZE = CURVE_UPDATES_SIZE; exports.CURVE_UPDATE_OP_SIZE = CURVE_UPDATE_OP_SIZE; exports.CurveType = CurveType; exports.CurveUpdateOpKind = CurveUpdateOpKind; exports.CurveXMode = CurveXMode; exports.DEFAULT_MAX_CURVE_POINTS = DEFAULT_MAX_CURVE_POINTS; exports.DEFAULT_MAX_PREFAB_SLOTS = DEFAULT_MAX_PREFAB_SLOTS; exports.Discriminator = Discriminator; exports.FEE_CONFIG_SEED = FEE_CONFIG_SEED; exports.FEE_CONFIG_SIZE = FEE_CONFIG_SIZE; exports.HADRON_PROGRAM_ID = HADRON_PROGRAM_ID; exports.Hadron = Hadron; exports.HadronOrderbook = HadronOrderbook; exports.Interpolation = Interpolation; exports.MAX_CURVE_UPDATE_OPS = MAX_CURVE_UPDATE_OPS; exports.MAX_SETCURVE_POINTS = MAX_SETCURVE_POINTS; exports.MIDPRICE_ORACLE_SEED = MIDPRICE_ORACLE_SEED; exports.MIDPRICE_ORACLE_SIZE = MIDPRICE_ORACLE_SIZE; exports.OracleMode = OracleMode; exports.POINT_DATA_SIZE = POINT_DATA_SIZE; exports.PoolState = PoolState; exports.Q32_ONE = Q32_ONE; exports.RiskMode = RiskMode; exports.SPREAD_CONFIG_SEED = SPREAD_CONFIG_SEED; exports.Side = Side; exports.buildAcceptAuthority = buildAcceptAuthority; exports.buildAllocateCurvePrefabs = buildAllocateCurvePrefabs; exports.buildApplyCurveUpdates = buildApplyCurveUpdates; exports.buildClosePool = buildClosePool; exports.buildDeposit = buildDeposit; exports.buildInitialize = buildInitialize; exports.buildInitializeFeeConfig = buildInitializeFeeConfig; exports.buildInitializeSpreadConfig = buildInitializeSpreadConfig; exports.buildNominateAuthority = buildNominateAuthority; exports.buildRotateFeeAdmin = buildRotateFeeAdmin; exports.buildSetCurve = buildSetCurve; exports.buildSetCurveBoth = buildSetCurveBoth; exports.buildSetPoolState = buildSetPoolState; exports.buildSetQuotingAuthority = buildSetQuotingAuthority; exports.buildSetRiskCurve = buildSetRiskCurve; exports.buildSetRiskCurveAbsolute = buildSetRiskCurveAbsolute; exports.buildSetRiskCurveAbsoluteBoth = buildSetRiskCurveAbsoluteBoth; exports.buildSetRiskCurveBoth = buildSetRiskCurveBoth; exports.buildSubmitCurveUpdates = buildSubmitCurveUpdates; exports.buildSwapExactIn = buildSwapExactIn; exports.buildSwitchPriceCurve = buildSwitchPriceCurve; exports.buildSwitchRiskCurve = buildSwitchRiskCurve; exports.buildUpdateBaseSpread = buildUpdateBaseSpread; exports.buildUpdateDeltaStaleness = buildUpdateDeltaStaleness; exports.buildUpdateFeeConfig = buildUpdateFeeConfig; exports.buildUpdateMidprice = buildUpdateMidprice; exports.buildUpdateMidpriceAndBaseSpread = buildUpdateMidpriceAndBaseSpread; exports.buildUpdateSpreadConfig = buildUpdateSpreadConfig; exports.buildWithdraw = buildWithdraw; exports.curvePrefabsSize = curvePrefabsSize; exports.decodeActiveCurves = decodeActiveCurves; exports.decodeConfig = decodeConfig; exports.decodeCurveMeta = decodeCurveMeta; exports.decodeCurveSide = decodeCurveSide; exports.decodeCurveUpdates = decodeCurveUpdates; exports.decodeFeeConfig = decodeFeeConfig; exports.decodeMidpriceOracle = decodeMidpriceOracle; exports.decodeSpreadConfig = decodeSpreadConfig; exports.derivePoolAddresses = derivePoolAddresses; exports.fromQ32 = fromQ32; exports.getConfigAddress = getConfigAddress; exports.getCurveMetaAddress = getCurveMetaAddress; exports.getCurvePrefabsAddress = getCurvePrefabsAddress; exports.getCurveUpdatesAddress = getCurveUpdatesAddress; exports.getFeeConfigAddress = getFeeConfigAddress; exports.getMidpriceOracleAddress = getMidpriceOracleAddress; exports.getOrCreateAta = getOrCreateAta; exports.getSpreadConfigAddress = getSpreadConfigAddress; exports.isSlotInitialized = isSlotInitialized; exports.pctToQ32 = pctToQ32; exports.toQ32 = toQ32;
1653
2292
  //# sourceMappingURL=index.js.map