@theliem/xmarket-sdk 3.4.2 → 3.6.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/index.js CHANGED
@@ -4,7 +4,7 @@ var anchor5 = require('@coral-xyz/anchor');
4
4
  var web3_js = require('@solana/web3.js');
5
5
  var crypto = require('crypto');
6
6
  var splToken = require('@solana/spl-token');
7
- var BN5 = require('bn.js');
7
+ var BN4 = require('bn.js');
8
8
  var nacl = require('tweetnacl');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -28,7 +28,7 @@ function _interopNamespace(e) {
28
28
  }
29
29
 
30
30
  var anchor5__namespace = /*#__PURE__*/_interopNamespace(anchor5);
31
- var BN5__default = /*#__PURE__*/_interopDefault(BN5);
31
+ var BN4__default = /*#__PURE__*/_interopDefault(BN4);
32
32
  var nacl__namespace = /*#__PURE__*/_interopNamespace(nacl);
33
33
 
34
34
  // src/sdk.ts
@@ -1338,6 +1338,108 @@ function buildBatchedEd25519Instruction(orders) {
1338
1338
  });
1339
1339
  }
1340
1340
  var IX_SYSVAR = web3_js.SYSVAR_INSTRUCTIONS_PUBKEY;
1341
+ function buildOrder(params) {
1342
+ return {
1343
+ maker: params.maker,
1344
+ condition: params.condition,
1345
+ tokenId: params.tokenId,
1346
+ side: params.side,
1347
+ makerAmount: params.makerAmount,
1348
+ takerAmount: params.takerAmount,
1349
+ nonce: params.nonce ?? new BN4__default.default(Date.now()),
1350
+ expiry: params.expiry ?? new BN4__default.default(0),
1351
+ createdAt: params.createdAt ?? new BN4__default.default(Math.floor(Date.now() / 1e3)),
1352
+ fee: params.fee ?? new BN4__default.default(0),
1353
+ taker: params.taker ?? new web3_js.PublicKey(new Uint8Array(32)),
1354
+ signer: params.maker
1355
+ };
1356
+ }
1357
+ function orderAmountsFromPrice(side, price, quantity, decimals = 9) {
1358
+ const PRICE_PRECISION = new BN4__default.default(1e6);
1359
+ const DENOM = new BN4__default.default(100).mul(PRICE_PRECISION);
1360
+ const base = new BN4__default.default(10).pow(new BN4__default.default(decimals));
1361
+ const priceScaled = new BN4__default.default(Math.round(price * 1e6));
1362
+ const quantityBase = new BN4__default.default(quantity).mul(base);
1363
+ const collateral = quantityBase.mul(priceScaled).div(DENOM);
1364
+ return side === 0 ? { makerAmount: collateral, takerAmount: quantityBase } : { makerAmount: quantityBase, takerAmount: collateral };
1365
+ }
1366
+ function buildOrderFromPrice(params) {
1367
+ const { makerAmount, takerAmount } = orderAmountsFromPrice(
1368
+ params.side,
1369
+ params.price,
1370
+ params.quantity,
1371
+ params.decimals ?? 9
1372
+ );
1373
+ return buildOrder({ ...params, makerAmount, takerAmount });
1374
+ }
1375
+ function signOrderWithKeypair(order, keypair) {
1376
+ const message = serializeOrderToBytes(order);
1377
+ const signature = nacl__namespace.sign.detached(message, keypair.secretKey);
1378
+ return { order, signature };
1379
+ }
1380
+ function getOrderSignBytes(order) {
1381
+ return serializeOrderToBytes(order);
1382
+ }
1383
+ function serializeSignedOrder(signed) {
1384
+ const orderBytes = serializeOrderToBytes(signed.order);
1385
+ const buf = new Uint8Array(242);
1386
+ buf.set(orderBytes, 0);
1387
+ buf.set(signed.signature, 178);
1388
+ return buf;
1389
+ }
1390
+ function deserializeSignedOrder(bytes) {
1391
+ if (bytes.length !== 242) throw new InvalidParamError("SignedOrder must be 242 bytes");
1392
+ const readPubkey = (offset) => new web3_js.PublicKey(bytes.slice(offset, offset + 32));
1393
+ const readU64 = (offset) => new BN4__default.default(bytes.slice(offset, offset + 8), "le");
1394
+ const readI64 = (offset) => new BN4__default.default(bytes.slice(offset, offset + 8), "le");
1395
+ const order = {
1396
+ maker: readPubkey(0),
1397
+ condition: readPubkey(32),
1398
+ tokenId: bytes[64],
1399
+ side: bytes[65],
1400
+ makerAmount: readU64(66),
1401
+ takerAmount: readU64(74),
1402
+ nonce: readU64(82),
1403
+ expiry: readI64(90),
1404
+ createdAt: readI64(98),
1405
+ fee: readU64(106),
1406
+ taker: readPubkey(114),
1407
+ signer: readPubkey(146)
1408
+ };
1409
+ const signature = bytes.slice(178, 242);
1410
+ return { order, signature };
1411
+ }
1412
+ function verifySignedOrder(signed) {
1413
+ const message = serializeOrderToBytes(signed.order);
1414
+ return nacl__namespace.sign.detached.verify(
1415
+ message,
1416
+ signed.signature,
1417
+ signed.order.signer.toBytes()
1418
+ );
1419
+ }
1420
+ function detectMatchType(a, b) {
1421
+ const _a = "order" in a ? a.order : a;
1422
+ const _b = "order" in b ? b.order : b;
1423
+ return _detectMatchType(_a, _b);
1424
+ }
1425
+ function _detectMatchType(a, b) {
1426
+ const SIDE_BUY = 0;
1427
+ if (a.tokenId === b.tokenId) {
1428
+ const aBuy2 = a.side === SIDE_BUY;
1429
+ const bBuy2 = b.side === SIDE_BUY;
1430
+ if (aBuy2 !== bBuy2) return "COMPLEMENTARY";
1431
+ throw new InvalidParamError(
1432
+ "Orders with same tokenId must be one BUY and one SELL (COMPLEMENTARY)"
1433
+ );
1434
+ }
1435
+ const aBuy = a.side === SIDE_BUY;
1436
+ const bBuy = b.side === SIDE_BUY;
1437
+ if (aBuy && bBuy) return "MINT";
1438
+ if (!aBuy && !bBuy) return "MERGE";
1439
+ throw new InvalidParamError(
1440
+ "Orders with different tokenIds must both be BUY (MINT) or both SELL (MERGE)"
1441
+ );
1442
+ }
1341
1443
 
1342
1444
  // src/programs/clob.ts
1343
1445
  var ClobClient = class {
@@ -1407,15 +1509,39 @@ var ClobClient = class {
1407
1509
  takerOrderRecord
1408
1510
  ];
1409
1511
  if (feeRecipientAddr) addresses.push(feeRecipientAddr);
1512
+ const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
1513
+ const [vaultToken] = PDA.vaultToken(collateralMint, this.programIds);
1514
+ const [mintAuth] = PDA.mintAuthority(condition, this.programIds);
1515
+ const [extraMetaNo] = PDA.extraAccountMetaList(noMint, this.programIds);
1516
+ const clobUsdcAta = splToken.getAssociatedTokenAddressSync(collateralMint, clobConfigPda, true);
1517
+ const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
1518
+ const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
1519
+ const [clobYesPos] = PDA.position(condition, 1, clobConfigPda, this.programIds);
1520
+ const [clobNoPos] = PDA.position(condition, 0, clobConfigPda, this.programIds);
1521
+ addresses.push(
1522
+ collateralVault,
1523
+ vaultToken,
1524
+ mintAuth,
1525
+ extraMetaNo,
1526
+ clobUsdcAta,
1527
+ clobYesAta,
1528
+ clobNoAta,
1529
+ clobYesPos,
1530
+ clobNoPos,
1531
+ collateralMint,
1532
+ splToken.ASSOCIATED_TOKEN_PROGRAM_ID
1533
+ );
1410
1534
  for (const m of makers) {
1411
1535
  const seller = m.order.maker;
1412
- const [sellerPos] = PDA.position(condition, tokenId, seller, this.programIds);
1536
+ const makerTokenId = m.order.tokenId;
1537
+ const makerMint = makerTokenId === 1 ? yesMint : noMint;
1538
+ const [sellerPos] = PDA.position(condition, makerTokenId, seller, this.programIds);
1413
1539
  const [sellerStatus] = PDA.orderStatus(seller, m.order.nonce, this.programIds);
1414
1540
  const [sellerRecord] = PDA.orderRecord(seller, m.order.nonce, this.programIds);
1415
1541
  addresses.push(
1416
1542
  seller,
1417
1543
  sellerRecord,
1418
- splToken.getAssociatedTokenAddressSync(outcomeMint, seller, false, splToken.TOKEN_2022_PROGRAM_ID),
1544
+ splToken.getAssociatedTokenAddressSync(makerMint, seller, false, splToken.TOKEN_2022_PROGRAM_ID),
1419
1545
  splToken.getAssociatedTokenAddressSync(collateralMint, seller),
1420
1546
  sellerPos,
1421
1547
  sellerStatus
@@ -1487,6 +1613,36 @@ ${logs.join("\n")}`);
1487
1613
  async _sendLegacyTx(instructions) {
1488
1614
  await this._sendLegacyTxSig(instructions);
1489
1615
  }
1616
+ async _ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta) {
1617
+ const { connection } = this.provider;
1618
+ const [yesInfo, noInfo] = await Promise.all([
1619
+ connection.getAccountInfo(clobYesAta),
1620
+ connection.getAccountInfo(clobNoAta)
1621
+ ]);
1622
+ const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction2 } = await import('@solana/spl-token');
1623
+ const ixs = [];
1624
+ if (!yesInfo) {
1625
+ ixs.push(createAssociatedTokenAccountIdempotentInstruction2(
1626
+ this.walletPubkey,
1627
+ clobYesAta,
1628
+ clobConfig,
1629
+ yesMint,
1630
+ splToken.TOKEN_2022_PROGRAM_ID
1631
+ ));
1632
+ }
1633
+ if (!noInfo) {
1634
+ ixs.push(createAssociatedTokenAccountIdempotentInstruction2(
1635
+ this.walletPubkey,
1636
+ clobNoAta,
1637
+ clobConfig,
1638
+ noMint,
1639
+ splToken.TOKEN_2022_PROGRAM_ID
1640
+ ));
1641
+ }
1642
+ if (ixs.length > 0) {
1643
+ await this._sendLegacyTx(ixs);
1644
+ }
1645
+ }
1490
1646
  /**
1491
1647
  * Send a match transaction as versioned (v0) with optional ALT.
1492
1648
  * Both operator wallet and payer (this.provider.wallet) sign.
@@ -1494,11 +1650,12 @@ ${logs.join("\n")}`);
1494
1650
  async sendMatchTx(instructions, lookupTable, whitelistedWallet) {
1495
1651
  const { connection } = this.provider;
1496
1652
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1497
- const cuLimit = web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 8e5 });
1653
+ const cuLimit = web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 14e5 });
1654
+ const heapFrame = web3_js.ComputeBudgetProgram.requestHeapFrame({ bytes: 262144 });
1498
1655
  const message = new web3_js.TransactionMessage({
1499
1656
  payerKey: this.walletPubkey,
1500
1657
  recentBlockhash: blockhash,
1501
- instructions: [cuLimit, ...instructions]
1658
+ instructions: [cuLimit, heapFrame, ...instructions]
1502
1659
  }).compileToV0Message(lookupTable ? [lookupTable] : []);
1503
1660
  const vtx = new web3_js.VersionedTransaction(message);
1504
1661
  const signers = [this.provider.wallet.payer];
@@ -1507,7 +1664,7 @@ ${logs.join("\n")}`);
1507
1664
  }
1508
1665
  vtx.sign(signers);
1509
1666
  const sig = await connection.sendRawTransaction(vtx.serialize(), {
1510
- skipPreflight: true
1667
+ skipPreflight: false
1511
1668
  });
1512
1669
  const result = await connection.confirmTransaction(
1513
1670
  { signature: sig, blockhash, lastValidBlockHeight },
@@ -1619,7 +1776,7 @@ ${logs.join("\n")}`);
1619
1776
  * [extraAccountMetaList, hookConfig, hookProgram]
1620
1777
  * [feeManagement, fee_config, mkt_override, company_ata, oracle_vault] (when fee > 0)
1621
1778
  */
1622
- async buildMatchComplementaryIxs(takerSigned, makersSigned, collateralMint, feeRecipient, operator, opts) {
1779
+ async buildMatchComplementaryIxs(takerSigned, makersSigned, collateralMint, feeRecipient, operator, opts, useTakerPrice = false) {
1623
1780
  const condition = takerSigned.order.condition;
1624
1781
  const tokenId = takerSigned.order.tokenId;
1625
1782
  const taker = takerSigned.order.maker;
@@ -1669,7 +1826,7 @@ ${logs.join("\n")}`);
1669
1826
  ];
1670
1827
  }
1671
1828
  }
1672
- const matchIx = await this.program.methods.matchComplementary(takerNonce, fillAmount).accounts({
1829
+ const matchIx = await this.program.methods.matchComplementary(takerNonce, fillAmount, useTakerPrice).accounts({
1673
1830
  operator,
1674
1831
  payer: this.walletPubkey,
1675
1832
  clobConfig: this.configPda(),
@@ -1710,6 +1867,49 @@ ${logs.join("\n")}`);
1710
1867
  const sig = await this.sendMatchTx(ixs, lookupTable, operatorWallet);
1711
1868
  return { signature: sig };
1712
1869
  }
1870
+ /**
1871
+ * 1 SELL taker vs N BUY makers.
1872
+ * Rust program only supports BUY-as-taker, so we decompose into N instructions
1873
+ * (BUY_i as taker, SELL as single maker) combined into one atomic transaction.
1874
+ * Engine must use limitPrice for BUY.makerAmount to pass per-pair crossing check.
1875
+ */
1876
+ async matchComplementarySellVsMultiBuy(sellTaker, buyMakers, collateralMint, feeRecipient, operatorWallet, lookupTable, opts) {
1877
+ await Promise.all([
1878
+ this.registerOrderIfNeeded(sellTaker),
1879
+ ...buyMakers.map((m) => this.registerOrderIfNeeded(m))
1880
+ ]);
1881
+ const sell = sellTaker.order;
1882
+ const crossingBuys = buyMakers.filter((b) => {
1883
+ const buy = b.order;
1884
+ const lhs = BigInt(buy.makerAmount.toString()) * BigInt(sell.makerAmount.toString());
1885
+ const rhs = BigInt(buy.takerAmount.toString()) * BigInt(sell.takerAmount.toString());
1886
+ return lhs >= rhs;
1887
+ });
1888
+ if (crossingBuys.length === 0) {
1889
+ throw new InvalidParamError("COMPLEMENTARY: no BUY maker orders cross with the SELL taker");
1890
+ }
1891
+ if (crossingBuys.length < buyMakers.length) {
1892
+ console.warn(`[matchOrders] SELL+BUY: filtered ${buyMakers.length - crossingBuys.length} non-crossing BUY(s)`);
1893
+ }
1894
+ const allIxs = [];
1895
+ for (const buyMaker of crossingBuys) {
1896
+ const ixs = await this.buildMatchComplementaryIxs(
1897
+ buyMaker,
1898
+ // BUY as taker
1899
+ [sellTaker],
1900
+ // SELL as maker
1901
+ collateralMint,
1902
+ feeRecipient,
1903
+ operatorWallet.publicKey,
1904
+ opts,
1905
+ true
1906
+ // useTakerPrice: SELL gets filled at BUY's price (maker's price per doc)
1907
+ );
1908
+ allIxs.push(...ixs);
1909
+ }
1910
+ const sig = await this.sendMatchTx(allIxs, lookupTable, operatorWallet);
1911
+ return { signature: sig };
1912
+ }
1713
1913
  /**
1714
1914
  * MINT: 1 YES buyer (taker) + N NO buyers (makers).
1715
1915
  * Phase 1: register taker + all NO makers in parallel.
@@ -1727,6 +1927,7 @@ ${logs.join("\n")}`);
1727
1927
  const taker = yesSigned.order.maker;
1728
1928
  const takerNonce = yesSigned.order.nonce;
1729
1929
  const fillAmount = new anchor5__namespace.BN("18446744073709551615");
1930
+ const clobConfig = this.configPda();
1730
1931
  const [yesMint] = PDA.yesMint(condition, this.programIds);
1731
1932
  const [noMint] = PDA.noMint(condition, this.programIds);
1732
1933
  const [mintAuthority] = PDA.mintAuthority(condition, this.programIds);
@@ -1737,6 +1938,15 @@ ${logs.join("\n")}`);
1737
1938
  const takerYesToken = splToken.getAssociatedTokenAddressSync(yesMint, taker, false, splToken.TOKEN_2022_PROGRAM_ID);
1738
1939
  const [takerYesPosition] = PDA.position(condition, 1, taker, this.programIds);
1739
1940
  const [takerOrderStatus] = PDA.orderStatus(taker, takerNonce, this.programIds);
1941
+ const clobUsdcAta = splToken.getAssociatedTokenAddressSync(collateralMint, clobConfig, true);
1942
+ const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
1943
+ const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
1944
+ const [clobYesPos] = PDA.position(condition, 1, clobConfig, this.programIds);
1945
+ const [clobNoPos] = PDA.position(condition, 0, clobConfig, this.programIds);
1946
+ const [extraMetaYes] = PDA.extraAccountMetaList(yesMint, this.programIds);
1947
+ const [extraMetaNo] = PDA.extraAccountMetaList(noMint, this.programIds);
1948
+ const [hookConfig] = PDA.hookConfig(this.programIds);
1949
+ const hookProgram = this.programIds.hook;
1740
1950
  const remainingAccounts = [];
1741
1951
  for (const nm of noMakers) {
1742
1952
  const noMaker = nm.order.maker;
@@ -1746,6 +1956,7 @@ ${logs.join("\n")}`);
1746
1956
  const [noPosition] = PDA.position(condition, 0, noMaker, this.programIds);
1747
1957
  const [noStatus] = PDA.orderStatus(noMaker, nm.order.nonce, this.programIds);
1748
1958
  remainingAccounts.push(
1959
+ { pubkey: noMaker, isSigner: false, isWritable: false },
1749
1960
  { pubkey: noRecord, isSigner: false, isWritable: false },
1750
1961
  { pubkey: noToken, isSigner: false, isWritable: true },
1751
1962
  { pubkey: noCollateral, isSigner: false, isWritable: true },
@@ -1753,10 +1964,16 @@ ${logs.join("\n")}`);
1753
1964
  { pubkey: noStatus, isSigner: false, isWritable: true }
1754
1965
  );
1755
1966
  }
1967
+ remainingAccounts.push(
1968
+ { pubkey: extraMetaYes, isSigner: false, isWritable: false },
1969
+ { pubkey: extraMetaNo, isSigner: false, isWritable: false },
1970
+ { pubkey: hookConfig, isSigner: false, isWritable: false },
1971
+ { pubkey: hookProgram, isSigner: false, isWritable: false }
1972
+ );
1756
1973
  const matchIx = await this.program.methods.matchMintOrders(takerNonce, fillAmount).accounts({
1757
1974
  operator: operatorWallet.publicKey,
1758
1975
  payer: this.walletPubkey,
1759
- clobConfig: this.configPda(),
1976
+ clobConfig,
1760
1977
  condition,
1761
1978
  taker,
1762
1979
  takerOrderRecord,
@@ -1764,16 +1981,25 @@ ${logs.join("\n")}`);
1764
1981
  takerYesToken,
1765
1982
  takerYesPosition,
1766
1983
  takerOrderStatus,
1984
+ clobUsdcAta,
1985
+ clobYesAta,
1986
+ clobNoAta,
1987
+ clobYesPosition: clobYesPos,
1988
+ clobNoPosition: clobNoPos,
1767
1989
  collateralVault,
1768
1990
  vaultTokenAccount,
1991
+ collateralMint,
1769
1992
  yesMint,
1770
1993
  noMint,
1771
1994
  mintAuthority,
1772
- clobAuthority: this.configPda(),
1995
+ clobAuthority: clobConfig,
1773
1996
  conditionalTokensProgram: this.programIds.conditionalTokens,
1774
1997
  collateralTokenProgram: splToken.TOKEN_PROGRAM_ID,
1998
+ token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
1999
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
1775
2000
  systemProgram: web3_js.SystemProgram.programId
1776
2001
  }).remainingAccounts(remainingAccounts).instruction();
2002
+ await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
1777
2003
  const sig = await this.sendMatchTx([matchIx], lookupTable, operatorWallet);
1778
2004
  return { signature: sig };
1779
2005
  }
@@ -1795,9 +2021,9 @@ ${logs.join("\n")}`);
1795
2021
  const sellerYes = yesSigned.order.maker;
1796
2022
  const takerNonce = yesSigned.order.nonce;
1797
2023
  const fillAmount = new anchor5__namespace.BN("18446744073709551615");
2024
+ const clobConfig = this.configPda();
1798
2025
  const [yesMint] = PDA.yesMint(condition, this.programIds);
1799
2026
  const [noMint] = PDA.noMint(condition, this.programIds);
1800
- const [mintAuthority] = PDA.mintAuthority(condition, this.programIds);
1801
2027
  const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
1802
2028
  const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
1803
2029
  const [takerOrderRecord] = PDA.orderRecord(sellerYes, takerNonce, this.programIds);
@@ -1805,6 +2031,15 @@ ${logs.join("\n")}`);
1805
2031
  const [takerYesPosition] = PDA.position(condition, 1, sellerYes, this.programIds);
1806
2032
  const takerCollateral = splToken.getAssociatedTokenAddressSync(collateralMint, sellerYes);
1807
2033
  const [takerOrderStatus] = PDA.orderStatus(sellerYes, takerNonce, this.programIds);
2034
+ const clobUsdcAta = splToken.getAssociatedTokenAddressSync(collateralMint, clobConfig, true);
2035
+ const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
2036
+ const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
2037
+ const [clobYesPos] = PDA.position(condition, 1, clobConfig, this.programIds);
2038
+ const [clobNoPos] = PDA.position(condition, 0, clobConfig, this.programIds);
2039
+ const [extraMetaYes] = PDA.extraAccountMetaList(yesMint, this.programIds);
2040
+ const [extraMetaNo] = PDA.extraAccountMetaList(noMint, this.programIds);
2041
+ const [hookConfig] = PDA.hookConfig(this.programIds);
2042
+ const hookProgram = this.programIds.hook;
1808
2043
  const remainingAccounts = [];
1809
2044
  for (const nm of noMakers) {
1810
2045
  const noMaker = nm.order.maker;
@@ -1814,6 +2049,7 @@ ${logs.join("\n")}`);
1814
2049
  const [noPosition] = PDA.position(condition, 0, noMaker, this.programIds);
1815
2050
  const [noStatus] = PDA.orderStatus(noMaker, nm.order.nonce, this.programIds);
1816
2051
  remainingAccounts.push(
2052
+ { pubkey: noMaker, isSigner: false, isWritable: false },
1817
2053
  { pubkey: noRecord, isSigner: false, isWritable: false },
1818
2054
  { pubkey: noToken, isSigner: false, isWritable: true },
1819
2055
  { pubkey: noCollateral, isSigner: false, isWritable: true },
@@ -1821,6 +2057,12 @@ ${logs.join("\n")}`);
1821
2057
  { pubkey: noStatus, isSigner: false, isWritable: true }
1822
2058
  );
1823
2059
  }
2060
+ remainingAccounts.push(
2061
+ { pubkey: extraMetaYes, isSigner: false, isWritable: false },
2062
+ { pubkey: extraMetaNo, isSigner: false, isWritable: false },
2063
+ { pubkey: hookConfig, isSigner: false, isWritable: false },
2064
+ { pubkey: hookProgram, isSigner: false, isWritable: false }
2065
+ );
1824
2066
  if (yesSigned.order.fee.gtn(0) && this.programIds.feeManagement && this.feeConfigOwner) {
1825
2067
  const companyAddr = await this.companyAddress();
1826
2068
  if (companyAddr) {
@@ -1837,7 +2079,7 @@ ${logs.join("\n")}`);
1837
2079
  const matchIx = await this.program.methods.matchMergeOrders(takerNonce, fillAmount).accounts({
1838
2080
  operator: operatorWallet.publicKey,
1839
2081
  payer: this.walletPubkey,
1840
- clobConfig: this.configPda(),
2082
+ clobConfig,
1841
2083
  condition,
1842
2084
  taker: sellerYes,
1843
2085
  takerOrderRecord,
@@ -1845,17 +2087,24 @@ ${logs.join("\n")}`);
1845
2087
  takerYesPosition,
1846
2088
  takerCollateral,
1847
2089
  takerOrderStatus,
2090
+ clobUsdcAta,
2091
+ clobYesAta,
2092
+ clobNoAta,
2093
+ clobYesPosition: clobYesPos,
2094
+ clobNoPosition: clobNoPos,
1848
2095
  collateralVault,
1849
2096
  vaultTokenAccount,
2097
+ collateralMint,
1850
2098
  yesMint,
1851
2099
  noMint,
1852
- mintAuthority,
1853
- feeRecipient,
1854
- clobAuthority: this.configPda(),
2100
+ clobAuthority: clobConfig,
1855
2101
  conditionalTokensProgram: this.programIds.conditionalTokens,
1856
2102
  collateralTokenProgram: splToken.TOKEN_PROGRAM_ID,
2103
+ token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
2104
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
1857
2105
  systemProgram: web3_js.SystemProgram.programId
1858
2106
  }).remainingAccounts(remainingAccounts).instruction();
2107
+ await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
1859
2108
  const sig = await this.sendMatchTx([matchIx], lookupTable, operatorWallet);
1860
2109
  return { signature: sig };
1861
2110
  }
@@ -1896,38 +2145,22 @@ ${logs.join("\n")}`);
1896
2145
  buySignedOrder = taker;
1897
2146
  sellCandidates = makers;
1898
2147
  } else if (t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_BUY)) {
1899
- buySignedOrder = makers[0];
1900
- sellCandidates = [taker, ...makers.slice(1)];
2148
+ return this.matchComplementarySellVsMultiBuy(taker, makers, collateralMint, feeRecipient, operatorWallet, alt, opts);
1901
2149
  } else {
1902
2150
  throw new InvalidParamError("COMPLEMENTARY requires one BUY and one or more SELLs on same tokenId");
1903
2151
  }
1904
- const budget = BigInt(buySignedOrder.order.makerAmount.toString());
1905
- const totalAsk = sellCandidates.reduce(
1906
- (s, m) => s + BigInt(m.order.takerAmount.toString()),
1907
- BigInt(0)
1908
- );
1909
- let finalSells;
1910
- if (budget >= totalAsk) {
1911
- finalSells = sellCandidates;
1912
- } else {
1913
- const sorted = [...sellCandidates].sort((a, b) => {
1914
- const pa = BigInt(a.order.takerAmount.toString()) * BigInt(b.order.makerAmount.toString());
1915
- const pb = BigInt(b.order.takerAmount.toString()) * BigInt(a.order.makerAmount.toString());
1916
- return pa < pb ? -1 : pa > pb ? 1 : 0;
1917
- });
1918
- finalSells = [];
1919
- let remaining = budget;
1920
- for (const s of sorted) {
1921
- const cost = BigInt(s.order.takerAmount.toString());
1922
- if (remaining >= cost) {
1923
- finalSells.push(s);
1924
- remaining -= cost;
1925
- }
1926
- }
1927
- if (finalSells.length === 0) {
1928
- throw new InvalidParamError("COMPLEMENTARY: taker budget insufficient for any maker");
1929
- }
1930
- console.warn(`[matchOrders] budget ${budget} < totalAsk ${totalAsk}: matched ${finalSells.length}/${sellCandidates.length} makers`);
2152
+ const buy = buySignedOrder.order;
2153
+ const finalSells = sellCandidates.filter((s) => {
2154
+ const sell = s.order;
2155
+ const lhs = BigInt(buy.makerAmount.toString()) * BigInt(sell.makerAmount.toString());
2156
+ const rhs = BigInt(buy.takerAmount.toString()) * BigInt(sell.takerAmount.toString());
2157
+ return lhs >= rhs;
2158
+ });
2159
+ if (finalSells.length === 0) {
2160
+ throw new InvalidParamError("COMPLEMENTARY: no maker orders cross with the buy order");
2161
+ }
2162
+ if (finalSells.length < sellCandidates.length) {
2163
+ console.warn(`[matchOrders] filtered ${sellCandidates.length - finalSells.length} non-crossing maker(s)`);
1931
2164
  }
1932
2165
  return this.matchComplementary(buySignedOrder, finalSells, collateralMint, feeRecipient, operatorWallet, alt, opts);
1933
2166
  }
@@ -1939,6 +2172,53 @@ ${logs.join("\n")}`);
1939
2172
  if (allBuy) return this.matchMintOrders(taker, makers, collateralMint, feeRecipient, operatorWallet, alt);
1940
2173
  return this.matchMergeOrders(taker, makers, collateralMint, feeRecipient, operatorWallet, alt, opts);
1941
2174
  }
2175
+ /**
2176
+ * High-level match: caller passes price + quantity + keypair — SDK builds,
2177
+ * signs, and submits in one call. No manual amount calculation needed.
2178
+ *
2179
+ * Amounts are computed from each order's own limit `price` (percentage, e.g. 51 for 51%).
2180
+ * NEVER pass averageMatchedPrice — that is an engine output, not an order input.
2181
+ *
2182
+ * @param taker { keypair, condition, tokenId, side, price, quantity, nonce?, expiry? }
2183
+ * @param makers Array of same shape
2184
+ * @param decimals Collateral decimals (default 9 for USDS)
2185
+ */
2186
+ async matchOrdersFromPrice(taker, makers, decimals = 9, opts) {
2187
+ const nacl2 = await import('tweetnacl');
2188
+ const buildSigned = (p) => {
2189
+ const order = buildOrderFromPrice({
2190
+ maker: p.keypair.publicKey,
2191
+ condition: p.condition,
2192
+ tokenId: p.tokenId,
2193
+ side: p.side,
2194
+ price: p.price,
2195
+ quantity: p.quantity,
2196
+ decimals,
2197
+ nonce: p.nonce,
2198
+ expiry: p.expiry,
2199
+ fee: p.fee,
2200
+ taker: p.taker
2201
+ });
2202
+ const msg = serializeOrderToBytes(order);
2203
+ const sig = nacl2.default.sign.detached(msg, p.keypair.secretKey);
2204
+ return { order, signature: sig };
2205
+ };
2206
+ const takerSigned = buildSigned(taker);
2207
+ const sortedMakers = [...makers].sort((a, b) => {
2208
+ const sameToken = a.tokenId === taker.tokenId && b.tokenId === taker.tokenId;
2209
+ const isDirectBuy = taker.side === 0 && sameToken;
2210
+ const isDirectSell = taker.side === 1 && sameToken;
2211
+ const isMint = taker.side === 0 && taker.tokenId === 1;
2212
+ const isMerge = taker.side === 1 && taker.tokenId === 1;
2213
+ if (isDirectBuy) return a.price - b.price;
2214
+ if (isDirectSell) return b.price - a.price;
2215
+ if (isMint) return b.price - a.price;
2216
+ if (isMerge) return a.price - b.price;
2217
+ return 0;
2218
+ });
2219
+ const makersSigned = sortedMakers.map(buildSigned);
2220
+ return this.matchOrders(takerSigned, makersSigned, opts);
2221
+ }
1942
2222
  // ─── Queries ─────────────────────────────────────────────────────────────────
1943
2223
  async fetchConfig() {
1944
2224
  try {
@@ -8578,6 +8858,10 @@ var clob_exchange_default = {
8578
8858
  {
8579
8859
  name: "fill_amount",
8580
8860
  type: "u64"
8861
+ },
8862
+ {
8863
+ name: "use_taker_price",
8864
+ type: "bool"
8581
8865
  }
8582
8866
  ]
8583
8867
  },
@@ -8630,7 +8914,8 @@ var clob_exchange_default = {
8630
8914
  }
8631
8915
  },
8632
8916
  {
8633
- name: "condition"
8917
+ name: "condition",
8918
+ writable: true
8634
8919
  },
8635
8920
  {
8636
8921
  name: "taker"
@@ -8706,27 +8991,98 @@ var clob_exchange_default = {
8706
8991
  }
8707
8992
  },
8708
8993
  {
8709
- name: "collateral_vault",
8994
+ name: "clob_usdc_ata",
8995
+ docs: [
8996
+ "USDC ATA owned by clob_config \u2014 receives USDC from vault after merge"
8997
+ ],
8998
+ writable: true,
8999
+ pda: {
9000
+ seeds: [
9001
+ {
9002
+ kind: "account",
9003
+ path: "clob_config"
9004
+ },
9005
+ {
9006
+ kind: "account",
9007
+ path: "collateral_token_program"
9008
+ },
9009
+ {
9010
+ kind: "account",
9011
+ path: "collateral_mint"
9012
+ }
9013
+ ],
9014
+ program: {
9015
+ kind: "const",
9016
+ value: [
9017
+ 140,
9018
+ 151,
9019
+ 37,
9020
+ 143,
9021
+ 78,
9022
+ 36,
9023
+ 137,
9024
+ 241,
9025
+ 187,
9026
+ 61,
9027
+ 16,
9028
+ 41,
9029
+ 20,
9030
+ 142,
9031
+ 13,
9032
+ 131,
9033
+ 11,
9034
+ 90,
9035
+ 19,
9036
+ 153,
9037
+ 218,
9038
+ 255,
9039
+ 16,
9040
+ 132,
9041
+ 4,
9042
+ 142,
9043
+ 123,
9044
+ 216,
9045
+ 219,
9046
+ 233,
9047
+ 248,
9048
+ 89
9049
+ ]
9050
+ }
9051
+ }
9052
+ },
9053
+ {
9054
+ name: "clob_yes_ata",
8710
9055
  writable: true
8711
9056
  },
8712
9057
  {
8713
- name: "vault_token_account",
9058
+ name: "clob_no_ata",
8714
9059
  writable: true
8715
9060
  },
8716
9061
  {
8717
- name: "yes_mint",
9062
+ name: "clob_yes_position",
8718
9063
  writable: true
8719
9064
  },
8720
9065
  {
8721
- name: "no_mint",
9066
+ name: "clob_no_position",
8722
9067
  writable: true
8723
9068
  },
8724
9069
  {
8725
- name: "mint_authority",
9070
+ name: "collateral_vault",
8726
9071
  writable: true
8727
9072
  },
8728
9073
  {
8729
- name: "fee_recipient",
9074
+ name: "vault_token_account",
9075
+ writable: true
9076
+ },
9077
+ {
9078
+ name: "collateral_mint"
9079
+ },
9080
+ {
9081
+ name: "yes_mint",
9082
+ writable: true
9083
+ },
9084
+ {
9085
+ name: "no_mint",
8730
9086
  writable: true
8731
9087
  },
8732
9088
  {
@@ -8760,6 +9116,14 @@ var clob_exchange_default = {
8760
9116
  name: "collateral_token_program",
8761
9117
  address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
8762
9118
  },
9119
+ {
9120
+ name: "token_2022_program",
9121
+ address: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
9122
+ },
9123
+ {
9124
+ name: "associated_token_program",
9125
+ address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
9126
+ },
8763
9127
  {
8764
9128
  name: "system_program",
8765
9129
  address: "11111111111111111111111111111111"
@@ -8825,7 +9189,8 @@ var clob_exchange_default = {
8825
9189
  }
8826
9190
  },
8827
9191
  {
8828
- name: "condition"
9192
+ name: "condition",
9193
+ writable: true
8829
9194
  },
8830
9195
  {
8831
9196
  name: "taker"
@@ -8900,6 +9265,94 @@ var clob_exchange_default = {
8900
9265
  ]
8901
9266
  }
8902
9267
  },
9268
+ {
9269
+ name: "clob_usdc_ata",
9270
+ docs: [
9271
+ "USDC ATA owned by clob_config \u2014 receives combined USDC, then split_position pulls from here"
9272
+ ],
9273
+ writable: true,
9274
+ pda: {
9275
+ seeds: [
9276
+ {
9277
+ kind: "account",
9278
+ path: "clob_config"
9279
+ },
9280
+ {
9281
+ kind: "account",
9282
+ path: "collateral_token_program"
9283
+ },
9284
+ {
9285
+ kind: "account",
9286
+ path: "collateral_mint"
9287
+ }
9288
+ ],
9289
+ program: {
9290
+ kind: "const",
9291
+ value: [
9292
+ 140,
9293
+ 151,
9294
+ 37,
9295
+ 143,
9296
+ 78,
9297
+ 36,
9298
+ 137,
9299
+ 241,
9300
+ 187,
9301
+ 61,
9302
+ 16,
9303
+ 41,
9304
+ 20,
9305
+ 142,
9306
+ 13,
9307
+ 131,
9308
+ 11,
9309
+ 90,
9310
+ 19,
9311
+ 153,
9312
+ 218,
9313
+ 255,
9314
+ 16,
9315
+ 132,
9316
+ 4,
9317
+ 142,
9318
+ 123,
9319
+ 216,
9320
+ 219,
9321
+ 233,
9322
+ 248,
9323
+ 89
9324
+ ]
9325
+ }
9326
+ }
9327
+ },
9328
+ {
9329
+ name: "clob_yes_ata",
9330
+ docs: [
9331
+ "YES Token-2022 ATA owned by clob_config \u2014 receives YES from split, then transferred to taker"
9332
+ ],
9333
+ writable: true
9334
+ },
9335
+ {
9336
+ name: "clob_no_ata",
9337
+ docs: [
9338
+ "NO Token-2022 ATA owned by clob_config \u2014 receives NO from split, then transferred to makers"
9339
+ ],
9340
+ writable: true
9341
+ },
9342
+ {
9343
+ name: "clob_yes_position",
9344
+ docs: [
9345
+ "YES Position PDA for clob_config (set by CTF split_position init_if_needed)"
9346
+ ],
9347
+ writable: true
9348
+ },
9349
+ {
9350
+ name: "clob_no_position",
9351
+ docs: [
9352
+ "NO Position PDA for clob_config (set by CTF split_position init_if_needed)"
9353
+ ],
9354
+ writable: true
9355
+ },
8903
9356
  {
8904
9357
  name: "collateral_vault",
8905
9358
  writable: true
@@ -8908,6 +9361,9 @@ var clob_exchange_default = {
8908
9361
  name: "vault_token_account",
8909
9362
  writable: true
8910
9363
  },
9364
+ {
9365
+ name: "collateral_mint"
9366
+ },
8911
9367
  {
8912
9368
  name: "yes_mint",
8913
9369
  writable: true
@@ -8951,6 +9407,14 @@ var clob_exchange_default = {
8951
9407
  name: "collateral_token_program",
8952
9408
  address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
8953
9409
  },
9410
+ {
9411
+ name: "token_2022_program",
9412
+ address: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
9413
+ },
9414
+ {
9415
+ name: "associated_token_program",
9416
+ address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
9417
+ },
8954
9418
  {
8955
9419
  name: "system_program",
8956
9420
  address: "11111111111111111111111111111111"
@@ -14541,91 +15005,7 @@ var XMarketSDK = class {
14541
15005
  return this._admin;
14542
15006
  }
14543
15007
  };
14544
- function buildOrder(params) {
14545
- return {
14546
- maker: params.maker,
14547
- condition: params.condition,
14548
- tokenId: params.tokenId,
14549
- side: params.side,
14550
- makerAmount: params.makerAmount,
14551
- takerAmount: params.takerAmount,
14552
- nonce: params.nonce ?? new BN5__default.default(Date.now()),
14553
- expiry: params.expiry ?? new BN5__default.default(0),
14554
- createdAt: params.createdAt ?? new BN5__default.default(Math.floor(Date.now() / 1e3)),
14555
- fee: params.fee ?? new BN5__default.default(0),
14556
- taker: params.taker ?? new web3_js.PublicKey(new Uint8Array(32)),
14557
- signer: params.maker
14558
- };
14559
- }
14560
- function signOrderWithKeypair(order, keypair) {
14561
- const message = serializeOrderToBytes(order);
14562
- const signature = nacl__namespace.sign.detached(message, keypair.secretKey);
14563
- return { order, signature };
14564
- }
14565
- function getOrderSignBytes(order) {
14566
- return serializeOrderToBytes(order);
14567
- }
14568
- function serializeSignedOrder(signed) {
14569
- const orderBytes = serializeOrderToBytes(signed.order);
14570
- const buf = new Uint8Array(242);
14571
- buf.set(orderBytes, 0);
14572
- buf.set(signed.signature, 178);
14573
- return buf;
14574
- }
14575
- function deserializeSignedOrder(bytes) {
14576
- if (bytes.length !== 242) throw new InvalidParamError("SignedOrder must be 242 bytes");
14577
- const readPubkey = (offset) => new web3_js.PublicKey(bytes.slice(offset, offset + 32));
14578
- const readU64 = (offset) => new BN5__default.default(bytes.slice(offset, offset + 8), "le");
14579
- const readI64 = (offset) => new BN5__default.default(bytes.slice(offset, offset + 8), "le");
14580
- const order = {
14581
- maker: readPubkey(0),
14582
- condition: readPubkey(32),
14583
- tokenId: bytes[64],
14584
- side: bytes[65],
14585
- makerAmount: readU64(66),
14586
- takerAmount: readU64(74),
14587
- nonce: readU64(82),
14588
- expiry: readI64(90),
14589
- createdAt: readI64(98),
14590
- fee: readU64(106),
14591
- taker: readPubkey(114),
14592
- signer: readPubkey(146)
14593
- };
14594
- const signature = bytes.slice(178, 242);
14595
- return { order, signature };
14596
- }
14597
- function verifySignedOrder(signed) {
14598
- const message = serializeOrderToBytes(signed.order);
14599
- return nacl__namespace.sign.detached.verify(
14600
- message,
14601
- signed.signature,
14602
- signed.order.signer.toBytes()
14603
- );
14604
- }
14605
- function detectMatchType(a, b) {
14606
- const _a = "order" in a ? a.order : a;
14607
- const _b = "order" in b ? b.order : b;
14608
- return _detectMatchType(_a, _b);
14609
- }
14610
- function _detectMatchType(a, b) {
14611
- const SIDE_BUY = 0;
14612
- if (a.tokenId === b.tokenId) {
14613
- const aBuy2 = a.side === SIDE_BUY;
14614
- const bBuy2 = b.side === SIDE_BUY;
14615
- if (aBuy2 !== bBuy2) return "COMPLEMENTARY";
14616
- throw new InvalidParamError(
14617
- "Orders with same tokenId must be one BUY and one SELL (COMPLEMENTARY)"
14618
- );
14619
- }
14620
- const aBuy = a.side === SIDE_BUY;
14621
- const bBuy = b.side === SIDE_BUY;
14622
- if (aBuy && bBuy) return "MINT";
14623
- if (!aBuy && !bBuy) return "MERGE";
14624
- throw new InvalidParamError(
14625
- "Orders with different tokenIds must both be BUY (MINT) or both SELL (MERGE)"
14626
- );
14627
- }
14628
- var MAX_APPROVE_AMOUNT = new BN5__default.default("18446744073709551615");
15008
+ var MAX_APPROVE_AMOUNT = new BN4__default.default("18446744073709551615");
14629
15009
  function buildApproveCollateralTx(collateralMint, signer, payer, delegate, amount = MAX_APPROVE_AMOUNT) {
14630
15010
  const ownerAta = splToken.getAssociatedTokenAddressSync(collateralMint, signer, false, splToken.TOKEN_PROGRAM_ID);
14631
15011
  const approveIx = splToken.createApproveInstruction(
@@ -14693,11 +15073,13 @@ exports.buildApproveAllOutcomeTokensTx = buildApproveAllOutcomeTokensTx;
14693
15073
  exports.buildApproveCollateralTx = buildApproveCollateralTx;
14694
15074
  exports.buildBatchedEd25519Instruction = buildBatchedEd25519Instruction;
14695
15075
  exports.buildOrder = buildOrder;
15076
+ exports.buildOrderFromPrice = buildOrderFromPrice;
14696
15077
  exports.deserializeSignedOrder = deserializeSignedOrder;
14697
15078
  exports.detectMatchType = detectMatchType;
14698
15079
  exports.generateContentHash = generateContentHash;
14699
15080
  exports.generateQuestionId = generateQuestionId;
14700
15081
  exports.getOrderSignBytes = getOrderSignBytes;
15082
+ exports.orderAmountsFromPrice = orderAmountsFromPrice;
14701
15083
  exports.serializeOrderToBytes = serializeOrderToBytes;
14702
15084
  exports.serializeSignedOrder = serializeSignedOrder;
14703
15085
  exports.signOrder = signOrder;