@hadron-fi/sdk 0.3.0 → 0.3.2

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;
@@ -578,7 +596,8 @@ function buildWithdraw(user, configPda, mintX, mintY, tokenProgramX, tokenProgra
578
596
 
579
597
 
580
598
 
581
- function buildSwapExactIn(user, poolAddresses, mintX, mintY, tokenProgramX, tokenProgramY, params, programId = HADRON_PROGRAM_ID) {
599
+
600
+ function buildSwapExactIn(user, poolAddresses, mintX, mintY, tokenProgramX, tokenProgramY, params, programId = HADRON_PROGRAM_ID, spreadConfigInitialized = false) {
582
601
  const data = Buffer.alloc(1 + 1 + 8 + 8 + 8);
583
602
  let offset = 0;
584
603
  data.writeUInt8(Discriminator.SwapExactIn, offset);
@@ -603,39 +622,50 @@ function buildSwapExactIn(user, poolAddresses, mintX, mintY, tokenProgramX, toke
603
622
  false,
604
623
  inputMintProgram
605
624
  );
625
+ const keys = [
626
+ { pubkey: tokenProgramX, isSigner: false, isWritable: false },
627
+ { pubkey: tokenProgramY, isSigner: false, isWritable: false },
628
+ { pubkey: poolAddresses.config, isSigner: false, isWritable: false },
629
+ {
630
+ pubkey: poolAddresses.midpriceOracle,
631
+ isSigner: false,
632
+ isWritable: false
633
+ },
634
+ { pubkey: poolAddresses.curveMeta, isSigner: false, isWritable: false },
635
+ {
636
+ pubkey: poolAddresses.curvePrefabs,
637
+ isSigner: false,
638
+ isWritable: true
639
+ },
640
+ { pubkey: poolAddresses.config, isSigner: false, isWritable: false },
641
+ // authority = pool address PDA
642
+ { pubkey: user, isSigner: true, isWritable: false },
643
+ { pubkey: userSource, isSigner: false, isWritable: true },
644
+ { pubkey: vaultSource, isSigner: false, isWritable: true },
645
+ { pubkey: vaultDest, isSigner: false, isWritable: true },
646
+ { pubkey: userDest, isSigner: false, isWritable: true },
647
+ { pubkey: feeConfigPda, isSigner: false, isWritable: false },
648
+ { pubkey: feeRecipientAta, isSigner: false, isWritable: true },
649
+ { pubkey: _web3js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
650
+ {
651
+ pubkey: poolAddresses.curveUpdates,
652
+ isSigner: false,
653
+ isWritable: true
654
+ }
655
+ ];
656
+ if (spreadConfigInitialized) {
657
+ const [spreadConfigPda] = getSpreadConfigAddress(
658
+ poolAddresses.config,
659
+ programId
660
+ );
661
+ keys.push(
662
+ { pubkey: spreadConfigPda, isSigner: false, isWritable: false },
663
+ { pubkey: _web3js.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false }
664
+ );
665
+ }
606
666
  return new (0, _web3js.TransactionInstruction)({
607
667
  programId,
608
- keys: [
609
- { pubkey: tokenProgramX, isSigner: false, isWritable: false },
610
- { pubkey: tokenProgramY, isSigner: false, isWritable: false },
611
- { pubkey: poolAddresses.config, isSigner: false, isWritable: false },
612
- {
613
- pubkey: poolAddresses.midpriceOracle,
614
- isSigner: false,
615
- isWritable: false
616
- },
617
- { pubkey: poolAddresses.curveMeta, isSigner: false, isWritable: false },
618
- {
619
- pubkey: poolAddresses.curvePrefabs,
620
- isSigner: false,
621
- isWritable: true
622
- },
623
- { pubkey: poolAddresses.config, isSigner: false, isWritable: false },
624
- // authority = pool address PDA
625
- { pubkey: user, isSigner: true, isWritable: false },
626
- { pubkey: userSource, isSigner: false, isWritable: true },
627
- { pubkey: vaultSource, isSigner: false, isWritable: true },
628
- { pubkey: vaultDest, isSigner: false, isWritable: true },
629
- { pubkey: userDest, isSigner: false, isWritable: true },
630
- { pubkey: feeConfigPda, isSigner: false, isWritable: false },
631
- { pubkey: feeRecipientAta, isSigner: false, isWritable: true },
632
- { pubkey: _web3js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
633
- {
634
- pubkey: poolAddresses.curveUpdates,
635
- isSigner: false,
636
- isWritable: true
637
- }
638
- ],
668
+ keys,
639
669
  data
640
670
  });
641
671
  }
@@ -1357,7 +1387,8 @@ var Hadron = class _Hadron {
1357
1387
  this.config.tokenProgramX,
1358
1388
  this.config.tokenProgramY,
1359
1389
  params,
1360
- this.programId
1390
+ this.programId,
1391
+ this.config.spreadConfigInitialized
1361
1392
  );
1362
1393
  }
1363
1394
  /** Build set curve instruction. */
@@ -1519,6 +1550,66 @@ var Hadron = class _Hadron {
1519
1550
  this.programId
1520
1551
  );
1521
1552
  }
1553
+ /** Build initialize spread config instruction. */
1554
+ initializeSpreadConfig(payer, authority, params) {
1555
+ return buildInitializeSpreadConfig(
1556
+ payer,
1557
+ authority,
1558
+ this.poolAddress,
1559
+ params,
1560
+ this.programId
1561
+ );
1562
+ }
1563
+ /** Build update spread config instruction (full replacement). */
1564
+ updateSpreadConfig(admin, params) {
1565
+ return buildUpdateSpreadConfig(
1566
+ admin,
1567
+ this.poolAddress,
1568
+ params,
1569
+ this.programId
1570
+ );
1571
+ }
1572
+ /**
1573
+ * Fetch current spread triggers, append new ones, and return the update ix.
1574
+ * If a trigger for the same account already exists, its spreadBps is updated.
1575
+ */
1576
+ async addSpreadTriggers(admin, triggers) {
1577
+ const current = await this.fetchSpreadTriggers();
1578
+ const merged = [...current];
1579
+ for (const t of triggers) {
1580
+ const existing = merged.findIndex(
1581
+ (e) => e.account.equals(t.account)
1582
+ );
1583
+ if (existing >= 0) {
1584
+ merged[existing] = t;
1585
+ } else {
1586
+ merged.push(t);
1587
+ }
1588
+ }
1589
+ return this.updateSpreadConfig(admin, { triggers: merged });
1590
+ }
1591
+ /**
1592
+ * Fetch current spread triggers, remove the given accounts, and return the update ix.
1593
+ */
1594
+ async removeSpreadTriggers(admin, accounts) {
1595
+ const current = await this.fetchSpreadTriggers();
1596
+ const removeSet = new Set(accounts.map((a) => a.toBase58()));
1597
+ const filtered = current.filter(
1598
+ (t) => !removeSet.has(t.account.toBase58())
1599
+ );
1600
+ return this.updateSpreadConfig(admin, { triggers: filtered });
1601
+ }
1602
+ /** Fetch and decode the current spread config triggers from chain. */
1603
+ async fetchSpreadTriggers() {
1604
+ const [spreadConfigPda] = getSpreadConfigAddress(
1605
+ this.poolAddress,
1606
+ this.programId
1607
+ );
1608
+ const info = await this.connection.getAccountInfo(spreadConfigPda);
1609
+ if (!info) return [];
1610
+ const decoded = decodeSpreadConfig(info.data);
1611
+ return decoded.triggers;
1612
+ }
1522
1613
  /** Build close pool instruction. */
1523
1614
  closePool(authority) {
1524
1615
  return buildClosePool(
@@ -1538,6 +1629,565 @@ var Hadron = class _Hadron {
1538
1629
  }
1539
1630
  };
1540
1631
 
1632
+ // src/orderbook.ts
1633
+ var MAX_ORDERS_PER_SIDE = MAX_SETCURVE_POINTS - 1;
1634
+ function tokensToAtoms(tokens, decimals) {
1635
+ return BigInt(Math.round(tokens * 10 ** decimals));
1636
+ }
1637
+ function atomsToTokens(atoms, decimals) {
1638
+ return Number(atoms) / 10 ** decimals;
1639
+ }
1640
+ var HadronOrderbook = class _HadronOrderbook {
1641
+ constructor(pool, decimalsX, decimalsY, initialVaultX, initialVaultY) {
1642
+ this.bidOrders = [];
1643
+ this.askOrders = [];
1644
+ this.bidsDirty = false;
1645
+ this.asksDirty = false;
1646
+ this.priceCurvesSet = false;
1647
+ this.riskBidInitialized = false;
1648
+ this.riskAskInitialized = false;
1649
+ this.committedBidPoints = [];
1650
+ this.committedAskPoints = [];
1651
+ this.pool = pool;
1652
+ this.decimalsX = decimalsX;
1653
+ this.decimalsY = decimalsY;
1654
+ this.initialVaultX = initialVaultX;
1655
+ this.initialVaultY = initialVaultY;
1656
+ }
1657
+ // ==========================================================================
1658
+ // Static Factories
1659
+ // ==========================================================================
1660
+ /**
1661
+ * Load an orderbook from an existing on-chain pool.
1662
+ * Fetches decimals and vault balances, and reconstructs staged orders
1663
+ * from the active risk curves.
1664
+ */
1665
+ static async load(params) {
1666
+ const { connection, pool: poolAddress } = params;
1667
+ const hadron = await Hadron.load(connection, poolAddress);
1668
+ const [mintXInfo, mintYInfo, vaultXInfo, vaultYInfo] = await Promise.all([
1669
+ connection.getAccountInfo(hadron.config.mintX),
1670
+ connection.getAccountInfo(hadron.config.mintY),
1671
+ connection.getAccountInfo(hadron.addresses.vaultX),
1672
+ connection.getAccountInfo(hadron.addresses.vaultY)
1673
+ ]);
1674
+ if (!mintXInfo || !mintYInfo) throw new Error("Mint account(s) not found");
1675
+ if (!vaultXInfo || !vaultYInfo) throw new Error("Vault account(s) not found");
1676
+ const decimalsX = mintXInfo.data[44];
1677
+ const decimalsY = mintYInfo.data[44];
1678
+ const vaultXBalance = new DataView(
1679
+ vaultXInfo.data.buffer,
1680
+ vaultXInfo.data.byteOffset
1681
+ ).getBigUint64(64, true);
1682
+ const vaultYBalance = new DataView(
1683
+ vaultYInfo.data.buffer,
1684
+ vaultYInfo.data.byteOffset
1685
+ ).getBigUint64(64, true);
1686
+ const book = new _HadronOrderbook(
1687
+ hadron,
1688
+ decimalsX,
1689
+ decimalsY,
1690
+ vaultXBalance,
1691
+ vaultYBalance
1692
+ );
1693
+ book.reconstructFromCurves();
1694
+ return book;
1695
+ }
1696
+ /**
1697
+ * Wrap an existing Hadron instance as an orderbook.
1698
+ * Useful for tests or when you already have the pool loaded.
1699
+ */
1700
+ static fromPool(pool, decimalsX, decimalsY, initialVaultX, initialVaultY) {
1701
+ return new _HadronOrderbook(
1702
+ pool,
1703
+ decimalsX,
1704
+ decimalsY,
1705
+ initialVaultX,
1706
+ initialVaultY
1707
+ );
1708
+ }
1709
+ // ==========================================================================
1710
+ // Order Management
1711
+ // ==========================================================================
1712
+ /**
1713
+ * Place a new order. Orders are staged locally and pushed on-chain via `push()`.
1714
+ * Orders on each side are sorted by spreadBps ascending (tightest first).
1715
+ * @throws If more than 31 orders on a side.
1716
+ */
1717
+ placeOrder(params) {
1718
+ const orders = params.side === "bid" ? this.bidOrders : this.askOrders;
1719
+ if (orders.length >= MAX_ORDERS_PER_SIDE) {
1720
+ throw new Error(
1721
+ `Cannot place more than ${MAX_ORDERS_PER_SIDE} orders per side`
1722
+ );
1723
+ }
1724
+ orders.push({ size: params.size, spreadBps: params.spreadBps });
1725
+ orders.sort((a, b) => a.spreadBps - b.spreadBps);
1726
+ if (params.side === "bid") this.bidsDirty = true;
1727
+ else this.asksDirty = true;
1728
+ }
1729
+ /**
1730
+ * Amend an existing order at the given level index.
1731
+ * Re-sorts if spread changes.
1732
+ * @throws On invalid index.
1733
+ */
1734
+ amendOrder(params) {
1735
+ const orders = params.side === "bid" ? this.bidOrders : this.askOrders;
1736
+ if (params.level < 0 || params.level >= orders.length) {
1737
+ throw new Error(
1738
+ `Invalid level ${params.level} for ${params.side} (have ${orders.length} orders)`
1739
+ );
1740
+ }
1741
+ const order = orders[params.level];
1742
+ if (params.spreadBps !== void 0) order.spreadBps = params.spreadBps;
1743
+ if (params.size !== void 0) order.size = params.size;
1744
+ orders.sort((a, b) => a.spreadBps - b.spreadBps);
1745
+ if (params.side === "bid") this.bidsDirty = true;
1746
+ else this.asksDirty = true;
1747
+ }
1748
+ /**
1749
+ * Cancel the order at the given level index.
1750
+ * @throws On invalid index.
1751
+ */
1752
+ cancelOrder(params) {
1753
+ const orders = params.side === "bid" ? this.bidOrders : this.askOrders;
1754
+ if (params.level < 0 || params.level >= orders.length) {
1755
+ throw new Error(
1756
+ `Invalid level ${params.level} for ${params.side} (have ${orders.length} orders)`
1757
+ );
1758
+ }
1759
+ orders.splice(params.level, 1);
1760
+ if (params.side === "bid") this.bidsDirty = true;
1761
+ else this.asksDirty = true;
1762
+ }
1763
+ /** Cancel all orders on one or both sides. */
1764
+ cancelAll(side) {
1765
+ if (!side || side === "bid") {
1766
+ this.bidOrders = [];
1767
+ this.bidsDirty = true;
1768
+ }
1769
+ if (!side || side === "ask") {
1770
+ this.askOrders = [];
1771
+ this.asksDirty = true;
1772
+ }
1773
+ }
1774
+ /** Read-only access to staged bid orders. */
1775
+ getBids() {
1776
+ return this.bidOrders;
1777
+ }
1778
+ /** Read-only access to staged ask orders. */
1779
+ getAsks() {
1780
+ return this.askOrders;
1781
+ }
1782
+ // ==========================================================================
1783
+ // Push (orders → on-chain risk curves)
1784
+ // ==========================================================================
1785
+ /**
1786
+ * Build transaction instructions to push the current staged orders on-chain.
1787
+ *
1788
+ * 1. On first push, sets flat price curves (factor=1.0, Step interpolation).
1789
+ * 2. If risk curves not yet initialized, uses setRiskCurveAbsolute (sets headers).
1790
+ * 3. On subsequent pushes, computes a minimal diff and uses curve updates
1791
+ * (submit + apply) when possible, falling back to full rewrite for large diffs.
1792
+ */
1793
+ push(authority) {
1794
+ const ixs = [];
1795
+ if (!this.priceCurvesSet) {
1796
+ const flatPoints = [
1797
+ { amountIn: 0n, priceFactor: 1, interpolation: 0 /* Step */ },
1798
+ {
1799
+ amountIn: BigInt("1000000000000000000"),
1800
+ priceFactor: 1,
1801
+ interpolation: 0 /* Step */
1802
+ }
1803
+ ];
1804
+ const [bidPriceIx, askPriceIx] = this.pool.setCurveBoth(authority, {
1805
+ bid: { defaultInterpolation: 0 /* Step */, points: flatPoints },
1806
+ ask: { defaultInterpolation: 0 /* Step */, points: flatPoints }
1807
+ });
1808
+ ixs.push(bidPriceIx, askPriceIx);
1809
+ this.priceCurvesSet = true;
1810
+ }
1811
+ if (!this.riskBidInitialized || !this.riskAskInitialized) {
1812
+ ixs.push(...this.buildFullRiskCurveIxs(authority));
1813
+ this.bidsDirty = false;
1814
+ this.asksDirty = false;
1815
+ return ixs;
1816
+ }
1817
+ const updateIxs = this.buildCurveUpdateIxs(authority);
1818
+ ixs.push(...updateIxs);
1819
+ this.bidsDirty = false;
1820
+ this.asksDirty = false;
1821
+ return ixs;
1822
+ }
1823
+ // ==========================================================================
1824
+ // Delegated Methods
1825
+ // ==========================================================================
1826
+ /** Build an updateMidprice instruction. */
1827
+ updateMidprice(authority, midprice, sequence) {
1828
+ const params = {
1829
+ midpriceQ32: BigInt(Math.floor(midprice * 2 ** 32)),
1830
+ sequence
1831
+ };
1832
+ return this.pool.updateMidprice(authority, params);
1833
+ }
1834
+ /** Build a deposit instruction. */
1835
+ deposit(user, params) {
1836
+ return this.pool.deposit(user, params);
1837
+ }
1838
+ /** Build a withdraw instruction. */
1839
+ withdraw(user, params) {
1840
+ return this.pool.withdraw(user, params);
1841
+ }
1842
+ /** Build a setPoolState instruction. */
1843
+ setPoolState(authority, params) {
1844
+ return this.pool.setPoolState(authority, params);
1845
+ }
1846
+ /**
1847
+ * Get the current book state by reading on-chain risk curves + midprice.
1848
+ * Reconstructs levels from curve points.
1849
+ */
1850
+ getBookState() {
1851
+ const midprice = this.pool.getMidprice();
1852
+ const curves = this.pool.getActiveCurves();
1853
+ const bids = this.curveSideToLevels(
1854
+ curves.riskBid,
1855
+ midprice,
1856
+ "bid",
1857
+ this.decimalsX
1858
+ );
1859
+ const asks = this.curveSideToLevels(
1860
+ curves.riskAsk,
1861
+ midprice,
1862
+ "ask",
1863
+ this.decimalsY
1864
+ );
1865
+ const base = atomsToTokens(this.initialVaultX, this.decimalsX);
1866
+ const quote = atomsToTokens(this.initialVaultY, this.decimalsY);
1867
+ return { midprice, bids, asks, inventory: { base, quote } };
1868
+ }
1869
+ // ==========================================================================
1870
+ // Curve Update Diff Algorithm
1871
+ // ==========================================================================
1872
+ /**
1873
+ * Compute the minimal set of curve update ops to transform `committed` into `desired`.
1874
+ *
1875
+ * Algorithm:
1876
+ * 1. Scan left-to-right for factor-only diffs (amountIn unchanged) → Edit ops.
1877
+ * 2. At the first structural diff (amountIn changed or length mismatch),
1878
+ * truncate-and-rebuild: remove all from that index to end, then add desired.
1879
+ * 3. Returns the ops array.
1880
+ */
1881
+ static computeCurveOps(committed, desired, curveType) {
1882
+ const ops = [];
1883
+ const minLen = Math.min(committed.length, desired.length);
1884
+ let structuralDiffIndex = minLen;
1885
+ for (let i = 0; i < minLen; i++) {
1886
+ const c = committed[i];
1887
+ const d = desired[i];
1888
+ if (c.amountIn !== d.amountIn) {
1889
+ structuralDiffIndex = i;
1890
+ break;
1891
+ }
1892
+ if (c.priceFactorQ32 !== d.priceFactorQ32 || c.interpolation !== d.interpolation) {
1893
+ ops.push({
1894
+ curveType,
1895
+ opKind: 0 /* Edit */,
1896
+ pointIndex: i,
1897
+ interpolation: d.interpolation,
1898
+ amountIn: d.amountIn,
1899
+ priceFactorQ32: d.priceFactorQ32,
1900
+ params: d.params
1901
+ });
1902
+ }
1903
+ }
1904
+ if (structuralDiffIndex === minLen && committed.length !== desired.length) {
1905
+ structuralDiffIndex = minLen;
1906
+ }
1907
+ if (structuralDiffIndex === minLen && committed.length === desired.length) {
1908
+ return ops;
1909
+ }
1910
+ const numToRemove = committed.length - structuralDiffIndex;
1911
+ for (let i = 0; i < numToRemove; i++) {
1912
+ ops.push({
1913
+ curveType,
1914
+ opKind: 2 /* Remove */,
1915
+ pointIndex: structuralDiffIndex,
1916
+ interpolation: 0 /* Step */,
1917
+ amountIn: 0n,
1918
+ priceFactorQ32: 0n,
1919
+ params: new Uint8Array(4)
1920
+ });
1921
+ }
1922
+ for (let i = structuralDiffIndex; i < desired.length; i++) {
1923
+ const d = desired[i];
1924
+ ops.push({
1925
+ curveType,
1926
+ opKind: 1 /* Add */,
1927
+ pointIndex: i,
1928
+ interpolation: d.interpolation,
1929
+ amountIn: d.amountIn,
1930
+ priceFactorQ32: d.priceFactorQ32,
1931
+ params: d.params
1932
+ });
1933
+ }
1934
+ return ops;
1935
+ }
1936
+ // ==========================================================================
1937
+ // Internal Helpers
1938
+ // ==========================================================================
1939
+ /** Convert SetRiskCurveAbsolutePointInput[] to CurvePoint[] for diffing. */
1940
+ static toCommittedPoints(points) {
1941
+ return points.map((p) => ({
1942
+ amountIn: p.vaultBalance,
1943
+ priceFactorQ32: toQ32(p.priceFactor),
1944
+ interpolation: _nullishCoalesce(p.interpolation, () => ( 0)) /* Step */,
1945
+ params: _nullishCoalesce(p.params, () => ( new Uint8Array(4)))
1946
+ }));
1947
+ }
1948
+ /**
1949
+ * Build full setRiskCurveAbsolute instructions for sides that need initialization.
1950
+ * After sending, snapshots committed points and marks sides as initialized.
1951
+ */
1952
+ buildFullRiskCurveIxs(authority) {
1953
+ const ixs = [];
1954
+ const hasBids = this.bidOrders.length > 0;
1955
+ const hasAsks = this.askOrders.length > 0;
1956
+ const bidPoints = hasBids ? this.buildBidRiskPoints() : void 0;
1957
+ const askPoints = hasAsks ? this.buildAskRiskPoints() : void 0;
1958
+ if (bidPoints && askPoints && !this.riskBidInitialized && !this.riskAskInitialized) {
1959
+ const [bidIx, askIx] = this.pool.setRiskCurveAbsoluteBoth(authority, {
1960
+ bid: {
1961
+ defaultInterpolation: 0 /* Step */,
1962
+ points: bidPoints,
1963
+ riskMode: 1 /* Integrated */
1964
+ },
1965
+ ask: {
1966
+ defaultInterpolation: 0 /* Step */,
1967
+ points: askPoints,
1968
+ riskMode: 1 /* Integrated */
1969
+ }
1970
+ });
1971
+ ixs.push(bidIx, askIx);
1972
+ this.committedBidPoints = _HadronOrderbook.toCommittedPoints(bidPoints);
1973
+ this.committedAskPoints = _HadronOrderbook.toCommittedPoints(askPoints);
1974
+ this.riskBidInitialized = true;
1975
+ this.riskAskInitialized = true;
1976
+ } else {
1977
+ if (bidPoints && !this.riskBidInitialized) {
1978
+ ixs.push(
1979
+ this.pool.setRiskCurveAbsolute(authority, {
1980
+ side: 0 /* Bid */,
1981
+ defaultInterpolation: 0 /* Step */,
1982
+ points: bidPoints,
1983
+ riskMode: 1 /* Integrated */
1984
+ })
1985
+ );
1986
+ this.committedBidPoints = _HadronOrderbook.toCommittedPoints(bidPoints);
1987
+ this.riskBidInitialized = true;
1988
+ }
1989
+ if (askPoints && !this.riskAskInitialized) {
1990
+ ixs.push(
1991
+ this.pool.setRiskCurveAbsolute(authority, {
1992
+ side: 1 /* Ask */,
1993
+ defaultInterpolation: 0 /* Step */,
1994
+ points: askPoints,
1995
+ riskMode: 1 /* Integrated */
1996
+ })
1997
+ );
1998
+ this.committedAskPoints = _HadronOrderbook.toCommittedPoints(askPoints);
1999
+ this.riskAskInitialized = true;
2000
+ }
2001
+ }
2002
+ return ixs;
2003
+ }
2004
+ /**
2005
+ * Build curve update instructions (submit + apply pairs) for both sides.
2006
+ * Falls back to full setRiskCurve rewrite if diff is too large.
2007
+ */
2008
+ buildCurveUpdateIxs(authority) {
2009
+ const bidPoints = this.buildBidRiskPoints();
2010
+ const askPoints = this.buildAskRiskPoints();
2011
+ const desiredBid = _HadronOrderbook.toCommittedPoints(bidPoints);
2012
+ const desiredAsk = _HadronOrderbook.toCommittedPoints(askPoints);
2013
+ const bidOps = _HadronOrderbook.computeCurveOps(
2014
+ this.committedBidPoints,
2015
+ desiredBid,
2016
+ 2 /* RiskBid */
2017
+ );
2018
+ const askOps = _HadronOrderbook.computeCurveOps(
2019
+ this.committedAskPoints,
2020
+ desiredAsk,
2021
+ 3 /* RiskAsk */
2022
+ );
2023
+ const allOps = [...bidOps, ...askOps];
2024
+ if (allOps.length === 0) {
2025
+ return [];
2026
+ }
2027
+ if (allOps.length > MAX_CURVE_UPDATE_OPS * 4) {
2028
+ return this.buildFallbackRewrite(authority, bidPoints, askPoints);
2029
+ }
2030
+ const ixs = [];
2031
+ for (let i = 0; i < allOps.length; i += MAX_CURVE_UPDATE_OPS) {
2032
+ const batch = allOps.slice(i, i + MAX_CURVE_UPDATE_OPS);
2033
+ ixs.push(this.pool.submitCurveUpdates(authority, batch));
2034
+ ixs.push(this.pool.applyCurveUpdates(authority));
2035
+ }
2036
+ this.committedBidPoints = desiredBid;
2037
+ this.committedAskPoints = desiredAsk;
2038
+ return ixs;
2039
+ }
2040
+ /** Fallback: full setRiskCurve rewrite for both sides. */
2041
+ buildFallbackRewrite(authority, bidPoints, askPoints) {
2042
+ const [bidIx, askIx] = this.pool.setRiskCurveAbsoluteBoth(authority, {
2043
+ bid: {
2044
+ defaultInterpolation: 0 /* Step */,
2045
+ points: bidPoints,
2046
+ riskMode: 1 /* Integrated */
2047
+ },
2048
+ ask: {
2049
+ defaultInterpolation: 0 /* Step */,
2050
+ points: askPoints,
2051
+ riskMode: 1 /* Integrated */
2052
+ }
2053
+ });
2054
+ this.committedBidPoints = _HadronOrderbook.toCommittedPoints(bidPoints);
2055
+ this.committedAskPoints = _HadronOrderbook.toCommittedPoints(askPoints);
2056
+ return [bidIx, askIx];
2057
+ }
2058
+ buildBidRiskPoints() {
2059
+ const points = [];
2060
+ let cumulativeAtoms = this.initialVaultX;
2061
+ for (const order of this.bidOrders) {
2062
+ const priceFactor = 1 - order.spreadBps / 1e4;
2063
+ points.push({
2064
+ vaultBalance: cumulativeAtoms,
2065
+ priceFactor,
2066
+ interpolation: 0 /* Step */
2067
+ });
2068
+ cumulativeAtoms += tokensToAtoms(order.size, this.decimalsX);
2069
+ }
2070
+ const lastFactor = this.bidOrders.length > 0 ? 1 - this.bidOrders[this.bidOrders.length - 1].spreadBps / 1e4 : 1;
2071
+ points.push({
2072
+ vaultBalance: cumulativeAtoms,
2073
+ priceFactor: lastFactor,
2074
+ interpolation: 0 /* Step */
2075
+ });
2076
+ return points;
2077
+ }
2078
+ buildAskRiskPoints() {
2079
+ const points = [];
2080
+ let cumulativeAtoms = this.initialVaultY;
2081
+ const midprice = this.pool.getMidprice();
2082
+ for (const order of this.askOrders) {
2083
+ const priceFactor = 1 + order.spreadBps / 1e4;
2084
+ points.push({
2085
+ vaultBalance: cumulativeAtoms,
2086
+ priceFactor,
2087
+ interpolation: 0 /* Step */
2088
+ });
2089
+ const quoteSizeTokens = order.size * midprice;
2090
+ cumulativeAtoms += tokensToAtoms(quoteSizeTokens, this.decimalsY);
2091
+ }
2092
+ const lastFactor = this.askOrders.length > 0 ? 1 + this.askOrders[this.askOrders.length - 1].spreadBps / 1e4 : 1;
2093
+ points.push({
2094
+ vaultBalance: cumulativeAtoms,
2095
+ priceFactor: lastFactor,
2096
+ interpolation: 0 /* Step */
2097
+ });
2098
+ return points;
2099
+ }
2100
+ /**
2101
+ * Reconstruct staged orders from on-chain risk curves (after load).
2102
+ * Assumes curves were set via this class (CurveXMode.Alternate, RiskMode.Integrated, Step).
2103
+ */
2104
+ reconstructFromCurves() {
2105
+ const curves = this.pool.getActiveCurves();
2106
+ if (curves.riskBid.xMode === 1 /* Alternate */ && curves.riskBid.riskMode === 1 /* Integrated */ && curves.riskBid.numPoints >= 2) {
2107
+ this.riskBidInitialized = true;
2108
+ this.priceCurvesSet = true;
2109
+ this.committedBidPoints = curves.riskBid.points.map((p) => ({
2110
+ amountIn: p.amountIn,
2111
+ priceFactorQ32: p.priceFactorQ32,
2112
+ interpolation: p.interpolation,
2113
+ params: new Uint8Array(p.params)
2114
+ }));
2115
+ }
2116
+ if (curves.riskAsk.xMode === 1 /* Alternate */ && curves.riskAsk.riskMode === 1 /* Integrated */ && curves.riskAsk.numPoints >= 2) {
2117
+ this.riskAskInitialized = true;
2118
+ this.priceCurvesSet = true;
2119
+ this.committedAskPoints = curves.riskAsk.points.map((p) => ({
2120
+ amountIn: p.amountIn,
2121
+ priceFactorQ32: p.priceFactorQ32,
2122
+ interpolation: p.interpolation,
2123
+ params: new Uint8Array(p.params)
2124
+ }));
2125
+ }
2126
+ this.bidOrders = this.reconstructSide(
2127
+ curves.riskBid,
2128
+ "bid",
2129
+ this.initialVaultX,
2130
+ this.decimalsX
2131
+ );
2132
+ this.askOrders = this.reconstructSide(
2133
+ curves.riskAsk,
2134
+ "ask",
2135
+ this.initialVaultY,
2136
+ this.decimalsY
2137
+ );
2138
+ }
2139
+ reconstructSide(curve, side, initialVault, decimals) {
2140
+ if (curve.xMode !== 1 /* Alternate */ || curve.riskMode !== 1 /* Integrated */) {
2141
+ return [];
2142
+ }
2143
+ const orders = [];
2144
+ const points = curve.points;
2145
+ for (let i = 0; i < points.length - 1; i++) {
2146
+ const pt = points[i];
2147
+ const nextPt = points[i + 1];
2148
+ const priceFactor = fromQ32(pt.priceFactorQ32);
2149
+ let spreadBps;
2150
+ if (side === "bid") {
2151
+ spreadBps = Math.round((1 - priceFactor) * 1e4);
2152
+ } else {
2153
+ spreadBps = Math.round((priceFactor - 1) * 1e4);
2154
+ }
2155
+ const sizeAtoms = nextPt.amountIn - pt.amountIn;
2156
+ let sizeTokens;
2157
+ if (side === "ask") {
2158
+ const midprice = this.pool.getMidprice();
2159
+ sizeTokens = atomsToTokens(sizeAtoms, decimals) / midprice;
2160
+ } else {
2161
+ sizeTokens = atomsToTokens(sizeAtoms, decimals);
2162
+ }
2163
+ orders.push({ size: sizeTokens, spreadBps });
2164
+ }
2165
+ return orders;
2166
+ }
2167
+ curveSideToLevels(curve, midprice, side, decimals) {
2168
+ if (curve.numPoints < 2) return [];
2169
+ const levels = [];
2170
+ let cumulative = 0;
2171
+ const points = curve.points;
2172
+ for (let i = 0; i < points.length - 1; i++) {
2173
+ const pt = points[i];
2174
+ const nextPt = points[i + 1];
2175
+ const priceFactor = fromQ32(pt.priceFactorQ32);
2176
+ const price = midprice * priceFactor;
2177
+ const sizeAtoms = nextPt.amountIn - pt.amountIn;
2178
+ let sizeTokens;
2179
+ if (side === "ask") {
2180
+ sizeTokens = atomsToTokens(sizeAtoms, decimals) / midprice;
2181
+ } else {
2182
+ sizeTokens = atomsToTokens(sizeAtoms, decimals);
2183
+ }
2184
+ cumulative += sizeTokens;
2185
+ levels.push({ price, size: sizeTokens, cumulative });
2186
+ }
2187
+ return levels;
2188
+ }
2189
+ };
2190
+
1541
2191
  // src/helpers/token.ts
1542
2192
 
1543
2193
 
@@ -1649,5 +2299,7 @@ async function getOrCreateAta(connection, mint, owner, payer, tokenProgram = _sp
1649
2299
 
1650
2300
 
1651
2301
 
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;
2302
+
2303
+
2304
+ 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
2305
  //# sourceMappingURL=index.js.map