@theliem/xmarket-sdk 3.5.0 → 3.6.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.mjs CHANGED
@@ -2,7 +2,7 @@ import * as anchor5 from '@coral-xyz/anchor';
2
2
  import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, SystemProgram, SYSVAR_RENT_PUBKEY, ComputeBudgetProgram, Transaction, TransactionInstruction, Ed25519Program, AddressLookupTableProgram, TransactionMessage, VersionedTransaction, Connection } from '@solana/web3.js';
3
3
  import { createHash } from 'crypto';
4
4
  import { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, createApproveInstruction } from '@solana/spl-token';
5
- import BN5 from 'bn.js';
5
+ import BN4 from 'bn.js';
6
6
  import * as nacl from 'tweetnacl';
7
7
 
8
8
  // src/sdk.ts
@@ -1312,6 +1312,108 @@ function buildBatchedEd25519Instruction(orders) {
1312
1312
  });
1313
1313
  }
1314
1314
  var IX_SYSVAR = SYSVAR_INSTRUCTIONS_PUBKEY;
1315
+ function buildOrder(params) {
1316
+ return {
1317
+ maker: params.maker,
1318
+ condition: params.condition,
1319
+ tokenId: params.tokenId,
1320
+ side: params.side,
1321
+ makerAmount: params.makerAmount,
1322
+ takerAmount: params.takerAmount,
1323
+ nonce: params.nonce ?? new BN4(Date.now()),
1324
+ expiry: params.expiry ?? new BN4(0),
1325
+ createdAt: params.createdAt ?? new BN4(Math.floor(Date.now() / 1e3)),
1326
+ fee: params.fee ?? new BN4(0),
1327
+ taker: params.taker ?? new PublicKey(new Uint8Array(32)),
1328
+ signer: params.maker
1329
+ };
1330
+ }
1331
+ function orderAmountsFromPrice(side, price, quantity, decimals = 9) {
1332
+ const PRICE_PRECISION = new BN4(1e6);
1333
+ const DENOM = new BN4(100).mul(PRICE_PRECISION);
1334
+ const base = new BN4(10).pow(new BN4(decimals));
1335
+ const priceScaled = new BN4(Math.round(price * 1e6));
1336
+ const quantityBase = new BN4(quantity).mul(base);
1337
+ const collateral = quantityBase.mul(priceScaled).div(DENOM);
1338
+ return side === 0 ? { makerAmount: collateral, takerAmount: quantityBase } : { makerAmount: quantityBase, takerAmount: collateral };
1339
+ }
1340
+ function buildOrderFromPrice(params) {
1341
+ const { makerAmount, takerAmount } = orderAmountsFromPrice(
1342
+ params.side,
1343
+ params.price,
1344
+ params.quantity,
1345
+ params.decimals ?? 9
1346
+ );
1347
+ return buildOrder({ ...params, makerAmount, takerAmount });
1348
+ }
1349
+ function signOrderWithKeypair(order, keypair) {
1350
+ const message = serializeOrderToBytes(order);
1351
+ const signature = nacl.sign.detached(message, keypair.secretKey);
1352
+ return { order, signature };
1353
+ }
1354
+ function getOrderSignBytes(order) {
1355
+ return serializeOrderToBytes(order);
1356
+ }
1357
+ function serializeSignedOrder(signed) {
1358
+ const orderBytes = serializeOrderToBytes(signed.order);
1359
+ const buf = new Uint8Array(242);
1360
+ buf.set(orderBytes, 0);
1361
+ buf.set(signed.signature, 178);
1362
+ return buf;
1363
+ }
1364
+ function deserializeSignedOrder(bytes) {
1365
+ if (bytes.length !== 242) throw new InvalidParamError("SignedOrder must be 242 bytes");
1366
+ const readPubkey = (offset) => new PublicKey(bytes.slice(offset, offset + 32));
1367
+ const readU64 = (offset) => new BN4(bytes.slice(offset, offset + 8), "le");
1368
+ const readI64 = (offset) => new BN4(bytes.slice(offset, offset + 8), "le");
1369
+ const order = {
1370
+ maker: readPubkey(0),
1371
+ condition: readPubkey(32),
1372
+ tokenId: bytes[64],
1373
+ side: bytes[65],
1374
+ makerAmount: readU64(66),
1375
+ takerAmount: readU64(74),
1376
+ nonce: readU64(82),
1377
+ expiry: readI64(90),
1378
+ createdAt: readI64(98),
1379
+ fee: readU64(106),
1380
+ taker: readPubkey(114),
1381
+ signer: readPubkey(146)
1382
+ };
1383
+ const signature = bytes.slice(178, 242);
1384
+ return { order, signature };
1385
+ }
1386
+ function verifySignedOrder(signed) {
1387
+ const message = serializeOrderToBytes(signed.order);
1388
+ return nacl.sign.detached.verify(
1389
+ message,
1390
+ signed.signature,
1391
+ signed.order.signer.toBytes()
1392
+ );
1393
+ }
1394
+ function detectMatchType(a, b) {
1395
+ const _a = "order" in a ? a.order : a;
1396
+ const _b = "order" in b ? b.order : b;
1397
+ return _detectMatchType(_a, _b);
1398
+ }
1399
+ function _detectMatchType(a, b) {
1400
+ const SIDE_BUY = 0;
1401
+ if (a.tokenId === b.tokenId) {
1402
+ const aBuy2 = a.side === SIDE_BUY;
1403
+ const bBuy2 = b.side === SIDE_BUY;
1404
+ if (aBuy2 !== bBuy2) return "COMPLEMENTARY";
1405
+ throw new InvalidParamError(
1406
+ "Orders with same tokenId must be one BUY and one SELL (COMPLEMENTARY)"
1407
+ );
1408
+ }
1409
+ const aBuy = a.side === SIDE_BUY;
1410
+ const bBuy = b.side === SIDE_BUY;
1411
+ if (aBuy && bBuy) return "MINT";
1412
+ if (!aBuy && !bBuy) return "MERGE";
1413
+ throw new InvalidParamError(
1414
+ "Orders with different tokenIds must both be BUY (MINT) or both SELL (MERGE)"
1415
+ );
1416
+ }
1315
1417
 
1316
1418
  // src/programs/clob.ts
1317
1419
  var ClobClient = class {
@@ -1648,7 +1750,7 @@ ${logs.join("\n")}`);
1648
1750
  * [extraAccountMetaList, hookConfig, hookProgram]
1649
1751
  * [feeManagement, fee_config, mkt_override, company_ata, oracle_vault] (when fee > 0)
1650
1752
  */
1651
- async buildMatchComplementaryIxs(takerSigned, makersSigned, collateralMint, feeRecipient, operator, opts) {
1753
+ async buildMatchComplementaryIxs(takerSigned, makersSigned, collateralMint, feeRecipient, operator, opts, useTakerPrice = false) {
1652
1754
  const condition = takerSigned.order.condition;
1653
1755
  const tokenId = takerSigned.order.tokenId;
1654
1756
  const taker = takerSigned.order.maker;
@@ -1698,7 +1800,7 @@ ${logs.join("\n")}`);
1698
1800
  ];
1699
1801
  }
1700
1802
  }
1701
- const matchIx = await this.program.methods.matchComplementary(takerNonce, fillAmount).accounts({
1803
+ const matchIx = await this.program.methods.matchComplementary(takerNonce, fillAmount, useTakerPrice).accounts({
1702
1804
  operator,
1703
1805
  payer: this.walletPubkey,
1704
1806
  clobConfig: this.configPda(),
@@ -1751,18 +1853,20 @@ ${logs.join("\n")}`);
1751
1853
  ...buyMakers.map((m) => this.registerOrderIfNeeded(m))
1752
1854
  ]);
1753
1855
  const sell = sellTaker.order;
1754
- const crossingBuys = buyMakers.filter((b) => {
1755
- const buy = b.order;
1756
- const lhs = BigInt(buy.makerAmount.toString()) * BigInt(sell.makerAmount.toString());
1757
- const rhs = BigInt(buy.takerAmount.toString()) * BigInt(sell.takerAmount.toString());
1758
- return lhs >= rhs;
1759
- });
1760
- if (crossingBuys.length === 0) {
1761
- throw new InvalidParamError("COMPLEMENTARY: no BUY maker orders cross with the SELL taker");
1856
+ if (buyMakers.length === 0) {
1857
+ throw new InvalidParamError("COMPLEMENTARY: no BUY maker orders provided");
1762
1858
  }
1763
- if (crossingBuys.length < buyMakers.length) {
1764
- console.warn(`[matchOrders] SELL+BUY: filtered ${buyMakers.length - crossingBuys.length} non-crossing BUY(s)`);
1859
+ const totalBuyPayment = buyMakers.reduce(
1860
+ (acc, b) => acc + BigInt(b.order.makerAmount.toString()),
1861
+ BigInt(0)
1862
+ );
1863
+ const sellExpected = BigInt(sell.takerAmount.toString());
1864
+ if (totalBuyPayment < sellExpected) {
1865
+ throw new InvalidParamError(
1866
+ `COMPLEMENTARY: total buyer payments ${totalBuyPayment} < seller expected ${sellExpected}`
1867
+ );
1765
1868
  }
1869
+ const crossingBuys = buyMakers;
1766
1870
  const allIxs = [];
1767
1871
  for (const buyMaker of crossingBuys) {
1768
1872
  const ixs = await this.buildMatchComplementaryIxs(
@@ -1773,7 +1877,9 @@ ${logs.join("\n")}`);
1773
1877
  collateralMint,
1774
1878
  feeRecipient,
1775
1879
  operatorWallet.publicKey,
1776
- opts
1880
+ opts,
1881
+ true
1882
+ // useTakerPrice: SELL gets filled at BUY's price (maker's price per doc)
1777
1883
  );
1778
1884
  allIxs.push(...ixs);
1779
1885
  }
@@ -2042,6 +2148,53 @@ ${logs.join("\n")}`);
2042
2148
  if (allBuy) return this.matchMintOrders(taker, makers, collateralMint, feeRecipient, operatorWallet, alt);
2043
2149
  return this.matchMergeOrders(taker, makers, collateralMint, feeRecipient, operatorWallet, alt, opts);
2044
2150
  }
2151
+ /**
2152
+ * High-level match: caller passes price + quantity + keypair — SDK builds,
2153
+ * signs, and submits in one call. No manual amount calculation needed.
2154
+ *
2155
+ * Amounts are computed from each order's own limit `price` (percentage, e.g. 51 for 51%).
2156
+ * NEVER pass averageMatchedPrice — that is an engine output, not an order input.
2157
+ *
2158
+ * @param taker { keypair, condition, tokenId, side, price, quantity, nonce?, expiry? }
2159
+ * @param makers Array of same shape
2160
+ * @param decimals Collateral decimals (default 9 for USDS)
2161
+ */
2162
+ async matchOrdersFromPrice(taker, makers, decimals = 9, opts) {
2163
+ const nacl2 = await import('tweetnacl');
2164
+ const buildSigned = (p) => {
2165
+ const order = buildOrderFromPrice({
2166
+ maker: p.keypair.publicKey,
2167
+ condition: p.condition,
2168
+ tokenId: p.tokenId,
2169
+ side: p.side,
2170
+ price: p.price,
2171
+ quantity: p.quantity,
2172
+ decimals,
2173
+ nonce: p.nonce,
2174
+ expiry: p.expiry,
2175
+ fee: p.fee,
2176
+ taker: p.taker
2177
+ });
2178
+ const msg = serializeOrderToBytes(order);
2179
+ const sig = nacl2.default.sign.detached(msg, p.keypair.secretKey);
2180
+ return { order, signature: sig };
2181
+ };
2182
+ const takerSigned = buildSigned(taker);
2183
+ const sortedMakers = [...makers].sort((a, b) => {
2184
+ const sameToken = a.tokenId === taker.tokenId && b.tokenId === taker.tokenId;
2185
+ const isDirectBuy = taker.side === 0 && sameToken;
2186
+ const isDirectSell = taker.side === 1 && sameToken;
2187
+ const isMint = taker.side === 0 && taker.tokenId === 1;
2188
+ const isMerge = taker.side === 1 && taker.tokenId === 1;
2189
+ if (isDirectBuy) return a.price - b.price;
2190
+ if (isDirectSell) return b.price - a.price;
2191
+ if (isMint) return b.price - a.price;
2192
+ if (isMerge) return a.price - b.price;
2193
+ return 0;
2194
+ });
2195
+ const makersSigned = sortedMakers.map(buildSigned);
2196
+ return this.matchOrders(takerSigned, makersSigned, opts);
2197
+ }
2045
2198
  // ─── Queries ─────────────────────────────────────────────────────────────────
2046
2199
  async fetchConfig() {
2047
2200
  try {
@@ -8681,6 +8834,10 @@ var clob_exchange_default = {
8681
8834
  {
8682
8835
  name: "fill_amount",
8683
8836
  type: "u64"
8837
+ },
8838
+ {
8839
+ name: "use_taker_price",
8840
+ type: "bool"
8684
8841
  }
8685
8842
  ]
8686
8843
  },
@@ -14824,91 +14981,7 @@ var XMarketSDK = class {
14824
14981
  return this._admin;
14825
14982
  }
14826
14983
  };
14827
- function buildOrder(params) {
14828
- return {
14829
- maker: params.maker,
14830
- condition: params.condition,
14831
- tokenId: params.tokenId,
14832
- side: params.side,
14833
- makerAmount: params.makerAmount,
14834
- takerAmount: params.takerAmount,
14835
- nonce: params.nonce ?? new BN5(Date.now()),
14836
- expiry: params.expiry ?? new BN5(0),
14837
- createdAt: params.createdAt ?? new BN5(Math.floor(Date.now() / 1e3)),
14838
- fee: params.fee ?? new BN5(0),
14839
- taker: params.taker ?? new PublicKey(new Uint8Array(32)),
14840
- signer: params.maker
14841
- };
14842
- }
14843
- function signOrderWithKeypair(order, keypair) {
14844
- const message = serializeOrderToBytes(order);
14845
- const signature = nacl.sign.detached(message, keypair.secretKey);
14846
- return { order, signature };
14847
- }
14848
- function getOrderSignBytes(order) {
14849
- return serializeOrderToBytes(order);
14850
- }
14851
- function serializeSignedOrder(signed) {
14852
- const orderBytes = serializeOrderToBytes(signed.order);
14853
- const buf = new Uint8Array(242);
14854
- buf.set(orderBytes, 0);
14855
- buf.set(signed.signature, 178);
14856
- return buf;
14857
- }
14858
- function deserializeSignedOrder(bytes) {
14859
- if (bytes.length !== 242) throw new InvalidParamError("SignedOrder must be 242 bytes");
14860
- const readPubkey = (offset) => new PublicKey(bytes.slice(offset, offset + 32));
14861
- const readU64 = (offset) => new BN5(bytes.slice(offset, offset + 8), "le");
14862
- const readI64 = (offset) => new BN5(bytes.slice(offset, offset + 8), "le");
14863
- const order = {
14864
- maker: readPubkey(0),
14865
- condition: readPubkey(32),
14866
- tokenId: bytes[64],
14867
- side: bytes[65],
14868
- makerAmount: readU64(66),
14869
- takerAmount: readU64(74),
14870
- nonce: readU64(82),
14871
- expiry: readI64(90),
14872
- createdAt: readI64(98),
14873
- fee: readU64(106),
14874
- taker: readPubkey(114),
14875
- signer: readPubkey(146)
14876
- };
14877
- const signature = bytes.slice(178, 242);
14878
- return { order, signature };
14879
- }
14880
- function verifySignedOrder(signed) {
14881
- const message = serializeOrderToBytes(signed.order);
14882
- return nacl.sign.detached.verify(
14883
- message,
14884
- signed.signature,
14885
- signed.order.signer.toBytes()
14886
- );
14887
- }
14888
- function detectMatchType(a, b) {
14889
- const _a = "order" in a ? a.order : a;
14890
- const _b = "order" in b ? b.order : b;
14891
- return _detectMatchType(_a, _b);
14892
- }
14893
- function _detectMatchType(a, b) {
14894
- const SIDE_BUY = 0;
14895
- if (a.tokenId === b.tokenId) {
14896
- const aBuy2 = a.side === SIDE_BUY;
14897
- const bBuy2 = b.side === SIDE_BUY;
14898
- if (aBuy2 !== bBuy2) return "COMPLEMENTARY";
14899
- throw new InvalidParamError(
14900
- "Orders with same tokenId must be one BUY and one SELL (COMPLEMENTARY)"
14901
- );
14902
- }
14903
- const aBuy = a.side === SIDE_BUY;
14904
- const bBuy = b.side === SIDE_BUY;
14905
- if (aBuy && bBuy) return "MINT";
14906
- if (!aBuy && !bBuy) return "MERGE";
14907
- throw new InvalidParamError(
14908
- "Orders with different tokenIds must both be BUY (MINT) or both SELL (MERGE)"
14909
- );
14910
- }
14911
- var MAX_APPROVE_AMOUNT = new BN5("18446744073709551615");
14984
+ var MAX_APPROVE_AMOUNT = new BN4("18446744073709551615");
14912
14985
  function buildApproveCollateralTx(collateralMint, signer, payer, delegate, amount = MAX_APPROVE_AMOUNT) {
14913
14986
  const ownerAta = getAssociatedTokenAddressSync(collateralMint, signer, false, TOKEN_PROGRAM_ID);
14914
14987
  const approveIx = createApproveInstruction(
@@ -14952,6 +15025,6 @@ function buildApproveAllOutcomeTokensTx(condition, signer, payer, delegate, prog
14952
15025
  return tx;
14953
15026
  }
14954
15027
 
14955
- export { AccountNotFoundError, AdminClient, ClobClient, CtfClient, FEE_DENOMINATOR, FeeManagementClient, HookClient, IX_SYSVAR, InvalidParamError, MAX_APPROVE_AMOUNT, MarketClient, MarketOracleClient, OracleClient, PDA, PresaleClient, QuestionStatus, SEEDS, UnauthorizedError, XMarketError, XMarketSDK, buildApproveAllOutcomeTokensTx, buildApproveCollateralTx, buildBatchedEd25519Instruction, buildOrder, deserializeSignedOrder, detectMatchType, generateContentHash, generateQuestionId, getOrderSignBytes, serializeOrderToBytes, serializeSignedOrder, signOrder, signOrderWithKeypair, verifySignedOrder };
15028
+ export { AccountNotFoundError, AdminClient, ClobClient, CtfClient, FEE_DENOMINATOR, FeeManagementClient, HookClient, IX_SYSVAR, InvalidParamError, MAX_APPROVE_AMOUNT, MarketClient, MarketOracleClient, OracleClient, PDA, PresaleClient, QuestionStatus, SEEDS, UnauthorizedError, XMarketError, XMarketSDK, buildApproveAllOutcomeTokensTx, buildApproveCollateralTx, buildBatchedEd25519Instruction, buildOrder, buildOrderFromPrice, deserializeSignedOrder, detectMatchType, generateContentHash, generateQuestionId, getOrderSignBytes, orderAmountsFromPrice, serializeOrderToBytes, serializeSignedOrder, signOrder, signOrderWithKeypair, verifySignedOrder };
14956
15029
  //# sourceMappingURL=index.mjs.map
14957
15030
  //# sourceMappingURL=index.mjs.map