@theliem/xmarket-sdk 3.0.0 → 3.1.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,11 +4,14 @@ var anchor4 = 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 oracleIdl = require('./oracle-FZJJIJGI.json');
7
+ var oracleIdl = require('./oracle-A5VOIMGM.json');
8
8
  var hookIdl = require('./hook-THBRGUM6.json');
9
- var questionMarketIdl = require('./question_market-CB6ZUZ5E.json');
9
+ var questionMarketIdl = require('./question_market-77YI4LRP.json');
10
10
  var conditionalTokensIdl = require('./conditional_tokens-3O5V46N5.json');
11
- var clobExchangeIdl = require('./clob_exchange-ATSH42KC.json');
11
+ var clobExchangeIdl = require('./clob_exchange-MQF4NI27.json');
12
+ var feeManagementIdl = require('./fee_management-VGF77YXG.json');
13
+ var presaleIdl = require('./presale-ZZJXXQS3.json');
14
+ var marketOracleIdl = require('./market_oracle-E6UUARGR.json');
12
15
  var BN4 = require('bn.js');
13
16
  var nacl = require('tweetnacl');
14
17
 
@@ -38,6 +41,9 @@ var hookIdl__default = /*#__PURE__*/_interopDefault(hookIdl);
38
41
  var questionMarketIdl__default = /*#__PURE__*/_interopDefault(questionMarketIdl);
39
42
  var conditionalTokensIdl__default = /*#__PURE__*/_interopDefault(conditionalTokensIdl);
40
43
  var clobExchangeIdl__default = /*#__PURE__*/_interopDefault(clobExchangeIdl);
44
+ var feeManagementIdl__default = /*#__PURE__*/_interopDefault(feeManagementIdl);
45
+ var presaleIdl__default = /*#__PURE__*/_interopDefault(presaleIdl);
46
+ var marketOracleIdl__default = /*#__PURE__*/_interopDefault(marketOracleIdl);
41
47
  var BN4__default = /*#__PURE__*/_interopDefault(BN4);
42
48
  var nacl__namespace = /*#__PURE__*/_interopNamespace(nacl);
43
49
 
@@ -58,7 +64,18 @@ var SEEDS = {
58
64
  hookConfig: Buffer.from("hook_config"),
59
65
  extraAccountMetas: Buffer.from("extra-account-metas"),
60
66
  clobConfig: Buffer.from("clob_config"),
61
- order: Buffer.from("order")
67
+ order: Buffer.from("order"),
68
+ feeConfig: Buffer.from("fee_config"),
69
+ questionFee: Buffer.from("question_fee"),
70
+ marketFee: Buffer.from("market_fee"),
71
+ // Presale
72
+ presale: Buffer.from("presale"),
73
+ qtMint: Buffer.from("qt_mint"),
74
+ qtAuthority: Buffer.from("qt_authority"),
75
+ userBuy: Buffer.from("user_buy"),
76
+ // Market Oracle
77
+ marketOracle: Buffer.from("market_oracle"),
78
+ userClaim: Buffer.from("user_claim")
62
79
  };
63
80
  var PDA = class {
64
81
  // ─── Question Market ────────────────────────────────────────────────────────
@@ -170,6 +187,74 @@ var PDA = class {
170
187
  programIds.clobExchange
171
188
  );
172
189
  }
190
+ // ─── Fee Management ─────────────────────────────────────────────────────────
191
+ static feeConfig(owner, programIds) {
192
+ if (!programIds.feeManagement) throw new Error("feeManagement program ID not configured");
193
+ return web3_js.PublicKey.findProgramAddressSync(
194
+ [SEEDS.feeConfig, owner.toBuffer()],
195
+ programIds.feeManagement
196
+ );
197
+ }
198
+ static questionFee(conditionPda, programIds) {
199
+ if (!programIds.feeManagement) throw new Error("feeManagement program ID not configured");
200
+ return web3_js.PublicKey.findProgramAddressSync(
201
+ [SEEDS.questionFee, conditionPda.toBuffer()],
202
+ programIds.feeManagement
203
+ );
204
+ }
205
+ static marketFeeOverride(conditionPda, programIds) {
206
+ if (!programIds.feeManagement) throw new Error("feeManagement program ID not configured");
207
+ return web3_js.PublicKey.findProgramAddressSync(
208
+ [SEEDS.marketFee, conditionPda.toBuffer()],
209
+ programIds.feeManagement
210
+ );
211
+ }
212
+ // ─── Presale ─────────────────────────────────────────────────────────────────
213
+ static presale(questionMarketConfig, presaleIndex, programIds) {
214
+ if (!programIds.presale) throw new Error("presale program ID not configured");
215
+ const idxBuf = Buffer.alloc(8);
216
+ idxBuf.writeBigUInt64LE(BigInt(presaleIndex.toString()));
217
+ return web3_js.PublicKey.findProgramAddressSync(
218
+ [SEEDS.presale, questionMarketConfig.toBuffer(), idxBuf],
219
+ programIds.presale
220
+ );
221
+ }
222
+ static qtMint(presalePda, programIds) {
223
+ if (!programIds.presale) throw new Error("presale program ID not configured");
224
+ return web3_js.PublicKey.findProgramAddressSync(
225
+ [SEEDS.qtMint, presalePda.toBuffer()],
226
+ programIds.presale
227
+ );
228
+ }
229
+ static qtAuthority(presalePda, programIds) {
230
+ if (!programIds.presale) throw new Error("presale program ID not configured");
231
+ return web3_js.PublicKey.findProgramAddressSync(
232
+ [SEEDS.qtAuthority, presalePda.toBuffer()],
233
+ programIds.presale
234
+ );
235
+ }
236
+ static userBuyRecord(presalePda, user, programIds) {
237
+ if (!programIds.presale) throw new Error("presale program ID not configured");
238
+ return web3_js.PublicKey.findProgramAddressSync(
239
+ [SEEDS.userBuy, presalePda.toBuffer(), user.toBuffer()],
240
+ programIds.presale
241
+ );
242
+ }
243
+ // ─── Market Oracle ────────────────────────────────────────────────────────────
244
+ static marketOraclePda(questionPda, programIds) {
245
+ if (!programIds.marketOracle) throw new Error("marketOracle program ID not configured");
246
+ return web3_js.PublicKey.findProgramAddressSync(
247
+ [SEEDS.marketOracle, questionPda.toBuffer()],
248
+ programIds.marketOracle
249
+ );
250
+ }
251
+ static userClaimRecord(marketOraclePda, user, programIds) {
252
+ if (!programIds.marketOracle) throw new Error("marketOracle program ID not configured");
253
+ return web3_js.PublicKey.findProgramAddressSync(
254
+ [SEEDS.userClaim, marketOraclePda.toBuffer(), user.toBuffer()],
255
+ programIds.marketOracle
256
+ );
257
+ }
173
258
  };
174
259
  function generateQuestionId(content, salt) {
175
260
  const input = content + (salt ?? Date.now());
@@ -249,6 +334,7 @@ var OracleClient = class {
249
334
  async updateAdmin(newAdmin, ownerPubkey) {
250
335
  const sig = await this.program.methods.updateAdmin(newAdmin).accounts({
251
336
  owner: this.walletPubkey,
337
+ payer: this.walletPubkey,
252
338
  oracleConfig: this.configPda(ownerPubkey)
253
339
  }).rpc();
254
340
  return { signature: sig };
@@ -430,6 +516,7 @@ var QuestionStatus = /* @__PURE__ */ ((QuestionStatus2) => {
430
516
  QuestionStatus2["Resolved"] = "resolved";
431
517
  return QuestionStatus2;
432
518
  })(QuestionStatus || {});
519
+ var FEE_DENOMINATOR = 1e6;
433
520
  var XMarketError = class extends Error {
434
521
  constructor(message, code) {
435
522
  super(message);
@@ -484,7 +571,7 @@ var MarketClient = class {
484
571
  * @param payer - Pays rent for all new accounts (can differ from creator)
485
572
  * @param creator - Identity of the question creator (signs but does not pay)
486
573
  */
487
- async createQuestion(params, oracle, creator = this.walletPubkey, payer = creator) {
574
+ async createQuestion(params, oracle, creator = this.walletPubkey, payer = this.walletPubkey) {
488
575
  const questionId = params.questionId ?? generateQuestionId(params.content);
489
576
  const contentHash = params.contentHash ?? generateContentHash(params.content);
490
577
  const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
@@ -523,7 +610,7 @@ var MarketClient = class {
523
610
  * @param creator - Whitelisted creator (must be in whitelist or be admin/owner)
524
611
  * @param payer - Fee payer (pays rent; can differ from creator)
525
612
  */
526
- async createQuestionAdmin(params, oracle, creator = this.walletPubkey, payer = creator) {
613
+ async createQuestionAdmin(params, oracle, creator = this.walletPubkey, payer = this.walletPubkey) {
527
614
  const questionId = params.questionId ?? generateQuestionId(params.content);
528
615
  const contentHash = params.contentHash ?? generateContentHash(params.content);
529
616
  const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
@@ -617,6 +704,7 @@ var MarketClient = class {
617
704
  questionCount: acc.questionCount.toNumber(),
618
705
  approvedCount: acc.approvedCount.toNumber(),
619
706
  rejectedCount: acc.rejectedCount.toNumber(),
707
+ presaleCount: acc.presaleCount.toNumber(),
620
708
  whitelist: acc.whitelist,
621
709
  whitelistLen: acc.whitelistLen,
622
710
  isPaused: acc.isPaused,
@@ -668,6 +756,157 @@ var MarketClient = class {
668
756
  if (!q || !q.condition) return { yes: null, no: null };
669
757
  return this.ctfClient.fetchBothPositions(q.condition, owner);
670
758
  }
759
+ // ─── Presale instructions ────────────────────────────────────────────────────
760
+ /**
761
+ * Any user creates a presale + initial buy (question-market::create_presale).
762
+ * Reads agents_rev / company_rev from fee_config.
763
+ *
764
+ * @param feeConfig fee_management fee_config PDA (owner = feeConfigOwner)
765
+ * @param currencyMint collateral token (e.g. USDC)
766
+ * @param presaleIndex config.presale_count — fetch config first or pass 0 for first presale
767
+ */
768
+ async createPresale(params, feeConfig, currencyMint, presaleIndex, creator = this.walletPubkey, payer = creator) {
769
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
770
+ const [presalePda] = PDA.presale(this.configPda, presaleIndex, this.programIds);
771
+ const [qtMint] = PDA.qtMint(presalePda, this.programIds);
772
+ const [qtAuthority] = PDA.qtAuthority(presalePda, this.programIds);
773
+ const [userBuyRecord] = PDA.userBuyRecord(presalePda, creator, this.programIds);
774
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
775
+ const creatorQtAta = splToken.getAssociatedTokenAddressSync(qtMint, creator);
776
+ const creatorCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, creator);
777
+ const tx = await this.program.methods.createPresale({
778
+ price: params.price,
779
+ startTime: params.startTime,
780
+ endTime: params.endTime,
781
+ initialBuyAmount: params.initialBuyAmount
782
+ }).accounts({
783
+ creator,
784
+ payer,
785
+ config: this.configPda,
786
+ feeConfig,
787
+ presale: presalePda,
788
+ qtMint,
789
+ qtAuthority,
790
+ currencyMint,
791
+ presaleVault,
792
+ creatorCurrencyAta,
793
+ creatorQtAta,
794
+ userBuyRecord,
795
+ presaleProgram: this.programIds.presale,
796
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
797
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
798
+ systemProgram: web3_js.SystemProgram.programId,
799
+ rent: web3_js.SYSVAR_RENT_PUBKEY
800
+ }).transaction();
801
+ return { tx, presalePda, qtMint };
802
+ }
803
+ /**
804
+ * Whitelist-only: approve presale → creates question + CTF condition + market_oracle in one tx.
805
+ *
806
+ * @param presalePda the presale account
807
+ * @param contentHash 32-byte hash for question content
808
+ * @param hookProgram token-2022 transfer hook program
809
+ * @param authorizedClob clob program allowed to do CTF transfers
810
+ * @param expirationTime Unix seconds
811
+ * @param creator presale creator pubkey (stored in question)
812
+ * @param currencyMint collateral mint
813
+ */
814
+ async approvePresale(presalePda, contentHash, hookProgram, authorizedClob, expirationTime, creator, currencyMint, caller = this.walletPubkey, payer = caller) {
815
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
816
+ if (!this.programIds.marketOracle) throw new Error("marketOracle program ID not configured");
817
+ const questionId = presalePda.toBytes();
818
+ const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
819
+ const marketConfig = await this.fetchConfig();
820
+ if (!marketConfig) throw new Error("QuestionMarketConfig not found");
821
+ const oraclePubkey = marketConfig.oracle;
822
+ const [conditionPda] = PDA.condition(oraclePubkey, questionId, this.programIds);
823
+ const [yesMint] = PDA.yesMint(conditionPda, this.programIds);
824
+ const [noMint] = PDA.noMint(conditionPda, this.programIds);
825
+ const [mintAuthority] = PDA.mintAuthority(conditionPda, this.programIds);
826
+ const [collateralVault] = PDA.collateralVault(currencyMint, this.programIds);
827
+ const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
828
+ const marketOracleVault = splToken.getAssociatedTokenAddressSync(currencyMint, marketOraclePda, true);
829
+ const tx = await this.program.methods.approvePresale({
830
+ contentHash: Array.from(contentHash),
831
+ hookProgram,
832
+ authorizedClob,
833
+ expirationTime,
834
+ creator
835
+ }).accounts({
836
+ caller,
837
+ payer,
838
+ config: this.configPda,
839
+ presale: presalePda,
840
+ question: questionPda,
841
+ currencyMint,
842
+ oracle: oraclePubkey,
843
+ condition: conditionPda,
844
+ yesMint,
845
+ noMint,
846
+ mintAuthority,
847
+ collateralVault,
848
+ marketOracle: marketOraclePda,
849
+ marketOracleVault,
850
+ conditionalTokensProgram: this.programIds.conditionalTokens,
851
+ presaleProgram: this.programIds.presale,
852
+ marketOracleProgram: this.programIds.marketOracle,
853
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
854
+ token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
855
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
856
+ systemProgram: web3_js.SystemProgram.programId,
857
+ rent: web3_js.SYSVAR_RENT_PUBKEY
858
+ }).transaction();
859
+ return { tx, questionPda, conditionPda, marketOraclePda, marketOracleVault };
860
+ }
861
+ /**
862
+ * Whitelist-only: reject presale so users can refund.
863
+ */
864
+ async rejectPresale(presalePda, caller = this.walletPubkey) {
865
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
866
+ return this.program.methods.rejectPresale().accounts({
867
+ caller,
868
+ config: this.configPda,
869
+ presale: presalePda,
870
+ presaleProgram: this.programIds.presale
871
+ }).transaction();
872
+ }
873
+ /**
874
+ * Whitelist-only: distribute presale vault funds → agents_rev% + company_rev% + rest to creator.
875
+ * Must be called after approvePresale.
876
+ */
877
+ async collectPresaleRevenue(presalePda, currencyMint, referralAddress, companyAddress, caller = this.walletPubkey) {
878
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
879
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
880
+ const referralTokenAccount = splToken.getAssociatedTokenAddressSync(currencyMint, referralAddress);
881
+ const companyTokenAccount = splToken.getAssociatedTokenAddressSync(currencyMint, companyAddress);
882
+ return this.program.methods.collectPresaleRevenue().accounts({
883
+ caller,
884
+ config: this.configPda,
885
+ presale: presalePda,
886
+ presaleVault,
887
+ currencyMint,
888
+ referralTokenAccount,
889
+ companyTokenAccount,
890
+ presaleProgram: this.programIds.presale,
891
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
892
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
893
+ systemProgram: web3_js.SystemProgram.programId
894
+ }).transaction();
895
+ }
896
+ /**
897
+ * Whitelist-only: snapshot MST supply so holders can claim trading fees.
898
+ * Call after oracle resolves the question.
899
+ */
900
+ async collectTradingFee(marketOraclePda, qtMint, caller = this.walletPubkey) {
901
+ if (!this.programIds.marketOracle) throw new Error("marketOracle program ID not configured");
902
+ return this.program.methods.collectTradingFee().accounts({
903
+ caller,
904
+ config: this.configPda,
905
+ marketOracle: marketOraclePda,
906
+ qtMint,
907
+ marketOracleProgram: this.programIds.marketOracle
908
+ }).transaction();
909
+ }
671
910
  };
672
911
  var CtfClient = class {
673
912
  constructor(program, provider, programIds) {
@@ -1077,14 +1316,138 @@ var IX_SYSVAR = web3_js.SYSVAR_INSTRUCTIONS_PUBKEY;
1077
1316
  // src/programs/clob.ts
1078
1317
  var CLOB_WHITELIST_SEED = Buffer.from("clob_whitelist");
1079
1318
  var ClobClient = class {
1080
- constructor(program, provider, programIds) {
1319
+ constructor(program, provider, programIds, networkConfig) {
1320
+ /** ALT cache: condition.toBase58() → loaded ALT account */
1321
+ this._altCache = /* @__PURE__ */ new Map();
1081
1322
  this.program = program;
1082
1323
  this.provider = provider;
1083
1324
  this.programIds = programIds;
1325
+ this.networkConfig = networkConfig;
1326
+ }
1327
+ async companyAddress() {
1328
+ if (!this.feeClient || !this.feeConfigOwner) return void 0;
1329
+ if (!this._companyAddress) {
1330
+ const cfg = await this.feeClient.fetchFeeConfig(this.feeConfigOwner);
1331
+ if (cfg) this._companyAddress = cfg.companyAddress;
1332
+ }
1333
+ return this._companyAddress;
1084
1334
  }
1085
1335
  get walletPubkey() {
1086
1336
  return this.provider.wallet.publicKey;
1087
1337
  }
1338
+ /**
1339
+ * Get or create an ALT for a condition.
1340
+ * First call: creates + extends ALT on-chain, waits for activation (~2s).
1341
+ * Subsequent calls for same condition: returns cached ALT instantly.
1342
+ * BE devs never interact with this — called automatically by matchOrders.
1343
+ */
1344
+ async ensureAlt(condition, collateralMint, buyerPubkey, buyerNonce, makers) {
1345
+ const cacheKey = condition.toBase58();
1346
+ if (this._altCache.has(cacheKey)) {
1347
+ return this._altCache.get(cacheKey);
1348
+ }
1349
+ const { connection } = this.provider;
1350
+ const payer = this.walletPubkey;
1351
+ const [yesMint] = PDA.yesMint(condition, this.programIds);
1352
+ const [extraAccountMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
1353
+ const [hookConfig] = PDA.hookConfig(this.programIds);
1354
+ const [buyOrderStatus] = PDA.orderStatus(buyerPubkey, buyerNonce, this.programIds);
1355
+ const [buyerPosition] = PDA.position(condition, 1, buyerPubkey, this.programIds);
1356
+ const clobConfigPda = this.configPda();
1357
+ const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
1358
+ const addresses = [
1359
+ // instruction program IDs — must be in ALT to keep static keys minimal
1360
+ web3_js.Ed25519Program.programId,
1361
+ this.programIds.clobExchange,
1362
+ // other programs
1363
+ this.programIds.conditionalTokens,
1364
+ this.programIds.hook,
1365
+ splToken.TOKEN_PROGRAM_ID,
1366
+ splToken.TOKEN_2022_PROGRAM_ID,
1367
+ web3_js.SystemProgram.programId,
1368
+ web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
1369
+ // config PDAs
1370
+ clobConfigPda,
1371
+ hookConfig,
1372
+ this.whitelistEntryPda(payer),
1373
+ // condition PDAs
1374
+ condition,
1375
+ yesMint,
1376
+ extraAccountMeta,
1377
+ // buyer pubkey + ATAs + PDAs
1378
+ buyerPubkey,
1379
+ splToken.getAssociatedTokenAddressSync(collateralMint, buyerPubkey),
1380
+ splToken.getAssociatedTokenAddressSync(yesMint, buyerPubkey, false, splToken.TOKEN_2022_PROGRAM_ID),
1381
+ buyerPosition,
1382
+ buyOrderStatus
1383
+ ];
1384
+ if (feeRecipientAddr) addresses.push(feeRecipientAddr);
1385
+ for (const m of makers) {
1386
+ const seller = m.order.maker;
1387
+ const [sellerPos] = PDA.position(condition, 1, seller, this.programIds);
1388
+ const [sellerStatus] = PDA.orderStatus(seller, m.order.nonce, this.programIds);
1389
+ addresses.push(
1390
+ seller,
1391
+ splToken.getAssociatedTokenAddressSync(yesMint, seller, false, splToken.TOKEN_2022_PROGRAM_ID),
1392
+ splToken.getAssociatedTokenAddressSync(collateralMint, seller),
1393
+ sellerPos,
1394
+ sellerStatus
1395
+ );
1396
+ }
1397
+ if (this.programIds.feeManagement && this.feeConfigOwner) {
1398
+ const companyAddr = await this.companyAddress();
1399
+ addresses.push(
1400
+ this.programIds.feeManagement,
1401
+ PDA.feeConfig(this.feeConfigOwner, this.programIds)[0],
1402
+ PDA.marketFeeOverride(condition, this.programIds)[0]
1403
+ );
1404
+ if (companyAddr) {
1405
+ addresses.push(splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr));
1406
+ }
1407
+ addresses.push(payer);
1408
+ }
1409
+ const slot = await connection.getSlot("finalized");
1410
+ const [createIx, altAddress] = web3_js.AddressLookupTableProgram.createLookupTable({
1411
+ authority: payer,
1412
+ payer,
1413
+ recentSlot: slot
1414
+ });
1415
+ const BATCH = 30;
1416
+ const extendIxs = [];
1417
+ for (let i = 0; i < addresses.length; i += BATCH) {
1418
+ extendIxs.push(web3_js.AddressLookupTableProgram.extendLookupTable({
1419
+ payer,
1420
+ authority: payer,
1421
+ lookupTable: altAddress,
1422
+ addresses: addresses.slice(i, i + BATCH)
1423
+ }));
1424
+ }
1425
+ await this._sendLegacyTx([createIx, extendIxs[0]]);
1426
+ for (let i = 1; i < extendIxs.length; i++) {
1427
+ await this._sendLegacyTx([extendIxs[i]]);
1428
+ }
1429
+ for (let attempt = 0; attempt < 30; attempt++) {
1430
+ await new Promise((r) => setTimeout(r, 1e3));
1431
+ const res = await connection.getAddressLookupTable(altAddress);
1432
+ if (res.value && res.value.state.addresses.length === addresses.length) {
1433
+ this._altCache.set(cacheKey, res.value);
1434
+ return res.value;
1435
+ }
1436
+ }
1437
+ throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
1438
+ }
1439
+ async _sendLegacyTx(instructions) {
1440
+ const { connection } = this.provider;
1441
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1442
+ const { Transaction: Transaction3 } = await import('@solana/web3.js');
1443
+ const tx = new Transaction3();
1444
+ tx.recentBlockhash = blockhash;
1445
+ tx.feePayer = this.walletPubkey;
1446
+ tx.add(...instructions);
1447
+ const signed = await this.provider.wallet.signTransaction(tx);
1448
+ const sig = await connection.sendRawTransaction(signed.serialize());
1449
+ await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, "confirmed");
1450
+ }
1088
1451
  /**
1089
1452
  * Send a match transaction as versioned (v0).
1090
1453
  * Pass a pre-built AddressLookupTableAccount to compress account keys and
@@ -1094,7 +1457,7 @@ var ClobClient = class {
1094
1457
  * If `whitelistedWallet` is provided and differs from `this.provider.wallet`,
1095
1458
  * both wallets sign the transaction (whitelisted operator + payer).
1096
1459
  */
1097
- async _sendMatchTx(instructions, lookupTable, whitelistedWallet) {
1460
+ async sendMatchTx(instructions, lookupTable, whitelistedWallet) {
1098
1461
  const { connection } = this.provider;
1099
1462
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1100
1463
  const message = new web3_js.TransactionMessage({
@@ -1203,7 +1566,13 @@ var ClobClient = class {
1203
1566
  *
1204
1567
  * remaining_accounts: [hook×3] [seller×5 × N]
1205
1568
  */
1206
- async matchComplementary(buySigned, makersSigned, collateralMint, feeRecipient, whitelistedWallet, lookupTable) {
1569
+ /** Build Ed25519 + matchComplementary instructions without sending.
1570
+ *
1571
+ * If `buySigned.order.fee > 0` and `feeClient` + `feeConfigOwner` are wired in,
1572
+ * automatically appends 5 fee_management remaining_accounts so the program CPIs
1573
+ * to distribute_fee internally. No manual `feeDistribute` param needed.
1574
+ */
1575
+ async buildMatchComplementaryIxs(buySigned, makersSigned, collateralMint, feeRecipient, whitelisted, opts) {
1207
1576
  const condition = buySigned.order.condition;
1208
1577
  const tokenId = buySigned.order.tokenId;
1209
1578
  const [outcomeMint] = tokenId === 1 ? PDA.yesMint(condition, this.programIds) : PDA.noMint(condition, this.programIds);
@@ -1233,12 +1602,23 @@ var ClobClient = class {
1233
1602
  { pubkey: sellOrderStatus, isSigner: false, isWritable: true }
1234
1603
  ];
1235
1604
  });
1236
- const whitelisted = whitelistedWallet.publicKey;
1605
+ let feeAccounts = [];
1606
+ if (buySigned.order.fee.gtn(0) && this.programIds.feeManagement && this.feeConfigOwner) {
1607
+ const companyAddr = await this.companyAddress();
1608
+ if (companyAddr) {
1609
+ const oracleVault = opts?.marketOracleVault ?? this.walletPubkey;
1610
+ feeAccounts = [
1611
+ { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
1612
+ { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
1613
+ { pubkey: PDA.marketFeeOverride(condition, this.programIds)[0], isSigner: false, isWritable: false },
1614
+ { pubkey: splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
1615
+ { pubkey: oracleVault, isSigner: false, isWritable: true }
1616
+ ];
1617
+ }
1618
+ }
1237
1619
  const whitelistEntry = this.whitelistEntryPda(whitelisted);
1238
1620
  const makerNonces = makersSigned.map((m) => m.order.nonce);
1239
- const ed25519Ixs = [
1240
- buildBatchedEd25519Instruction([buySigned, ...makersSigned])
1241
- ];
1621
+ const ed25519Ix = buildBatchedEd25519Instruction([buySigned, ...makersSigned]);
1242
1622
  const matchIx = await this.program.methods.matchComplementary(buySigned.order.nonce, makerNonces).accounts({
1243
1623
  whitelisted,
1244
1624
  payer: this.walletPubkey,
@@ -1257,12 +1637,19 @@ var ClobClient = class {
1257
1637
  tokenProgram: splToken.TOKEN_PROGRAM_ID,
1258
1638
  token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
1259
1639
  systemProgram: web3_js.SystemProgram.programId
1260
- }).remainingAccounts([...hookAccounts, ...makerAccounts]).instruction();
1261
- const sig = await this._sendMatchTx(
1262
- [...ed25519Ixs, matchIx],
1263
- lookupTable,
1264
- whitelistedWallet
1640
+ }).remainingAccounts([...hookAccounts, ...makerAccounts, ...feeAccounts]).instruction();
1641
+ return [ed25519Ix, matchIx];
1642
+ }
1643
+ async matchComplementary(buySigned, makersSigned, collateralMint, feeRecipient, whitelistedWallet, lookupTable, opts) {
1644
+ const ixs = await this.buildMatchComplementaryIxs(
1645
+ buySigned,
1646
+ makersSigned,
1647
+ collateralMint,
1648
+ feeRecipient,
1649
+ whitelistedWallet.publicKey,
1650
+ opts
1265
1651
  );
1652
+ const sig = await this.sendMatchTx(ixs, lookupTable, whitelistedWallet);
1266
1653
  return { signature: sig };
1267
1654
  }
1268
1655
  /**
@@ -1275,14 +1662,13 @@ var ClobClient = class {
1275
1662
  *
1276
1663
  * remaining_accounts: [maker×5 × N] (no hook accounts — mint_to doesn't fire hook)
1277
1664
  */
1278
- async matchMintOrders(yesSigned, makersSigned, collateralMint, whitelistedWallet, lookupTable) {
1665
+ async matchMintOrders(yesSigned, makersSigned, collateralMint, feeRecipient, whitelistedWallet, lookupTable) {
1279
1666
  const condition = yesSigned.order.condition;
1280
1667
  const [yesMint] = PDA.yesMint(condition, this.programIds);
1281
1668
  const [noMint] = PDA.noMint(condition, this.programIds);
1282
1669
  const [mintAuthority] = PDA.mintAuthority(condition, this.programIds);
1283
1670
  const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
1284
1671
  const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
1285
- const [clobAuthority] = PDA.clobConfig(this.programIds);
1286
1672
  const buyerYes = yesSigned.order.maker;
1287
1673
  const buyerYesCollateral = splToken.getAssociatedTokenAddressSync(collateralMint, buyerYes);
1288
1674
  const buyerYesTokenAccount = splToken.getAssociatedTokenAddressSync(yesMint, buyerYes, false, splToken.TOKEN_2022_PROGRAM_ID);
@@ -1325,13 +1711,20 @@ var ClobClient = class {
1325
1711
  yesMint,
1326
1712
  noMint,
1327
1713
  mintAuthority,
1328
- clobAuthority,
1714
+ feeRecipient,
1329
1715
  conditionalTokensProgram: this.programIds.conditionalTokens,
1330
1716
  collateralTokenProgram: splToken.TOKEN_PROGRAM_ID,
1331
1717
  token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
1332
1718
  systemProgram: web3_js.SystemProgram.programId
1333
1719
  }).remainingAccounts(makerAccounts).instruction();
1334
- const sig = await this._sendMatchTx(
1720
+ const frKey = feeRecipient.toBase58();
1721
+ for (const k of matchIx.keys) {
1722
+ if (k.pubkey.toBase58() === frKey) {
1723
+ k.isWritable = true;
1724
+ break;
1725
+ }
1726
+ }
1727
+ const sig = await this.sendMatchTx(
1335
1728
  [...ed25519Ixs, matchIx],
1336
1729
  lookupTable,
1337
1730
  whitelistedWallet
@@ -1348,13 +1741,12 @@ var ClobClient = class {
1348
1741
  *
1349
1742
  * remaining_accounts: [maker×5 × N] (no hook accounts — burn doesn't fire hook)
1350
1743
  */
1351
- async matchMergeOrders(yesSigned, makersSigned, collateralMint, whitelistedWallet, lookupTable) {
1744
+ async matchMergeOrders(yesSigned, makersSigned, collateralMint, feeRecipient, whitelistedWallet, lookupTable) {
1352
1745
  const condition = yesSigned.order.condition;
1353
1746
  const [yesMint] = PDA.yesMint(condition, this.programIds);
1354
1747
  const [noMint] = PDA.noMint(condition, this.programIds);
1355
1748
  const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
1356
1749
  const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
1357
- const [clobAuthority] = PDA.clobConfig(this.programIds);
1358
1750
  const sellerYes = yesSigned.order.maker;
1359
1751
  const sellerYesTokenAccount = splToken.getAssociatedTokenAddressSync(yesMint, sellerYes, false, splToken.TOKEN_2022_PROGRAM_ID);
1360
1752
  const [sellerYesPosition] = PDA.position(condition, 1, sellerYes, this.programIds);
@@ -1396,13 +1788,20 @@ var ClobClient = class {
1396
1788
  vaultTokenAccount,
1397
1789
  yesMint,
1398
1790
  noMint,
1399
- clobAuthority,
1791
+ feeRecipient,
1400
1792
  conditionalTokensProgram: this.programIds.conditionalTokens,
1401
1793
  collateralTokenProgram: splToken.TOKEN_PROGRAM_ID,
1402
1794
  token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
1403
1795
  systemProgram: web3_js.SystemProgram.programId
1404
1796
  }).remainingAccounts(makerAccounts).instruction();
1405
- const sig = await this._sendMatchTx(
1797
+ const frKeyM = feeRecipient.toBase58();
1798
+ for (const k of matchIx.keys) {
1799
+ if (k.pubkey.toBase58() === frKeyM) {
1800
+ k.isWritable = true;
1801
+ break;
1802
+ }
1803
+ }
1804
+ const sig = await this.sendMatchTx(
1406
1805
  [...ed25519Ixs, matchIx],
1407
1806
  lookupTable,
1408
1807
  whitelistedWallet
@@ -1420,15 +1819,37 @@ var ClobClient = class {
1420
1819
  *
1421
1820
  * All makers must have the same tokenId and side as makers[0].
1422
1821
  */
1423
- async matchOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, lookupTable) {
1822
+ /**
1823
+ * Auto-detect match type and execute in a single transaction.
1824
+ * ALT is managed automatically (created on first call per condition, cached thereafter).
1825
+ * feeRecipient and collateralMint are derived from on-chain config.
1826
+ * Fee distribution (distribute_fee CPI) fires automatically when order.fee > 0.
1827
+ *
1828
+ * @param opts.marketOracleVault Required for presale markets (is_admin=false).
1829
+ * Pass the ATA of the market_oracle PDA so 50% of fee goes there.
1830
+ * For admin markets (is_admin=true) omit — payer is used as placeholder (no transfer).
1831
+ */
1832
+ async matchOrders(taker, makers, opts) {
1424
1833
  if (makers.length === 0) throw new InvalidParamError("At least 1 maker required");
1834
+ const collateralMint = this.networkConfig.defaultCollateral.mint;
1835
+ const cfg = await this.fetchConfig();
1836
+ if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
1837
+ const feeRecipient = cfg.feeRecipient;
1838
+ const whitelistedWallet = this.provider.wallet;
1839
+ const alt = await this.ensureAlt(
1840
+ taker.order.condition,
1841
+ collateralMint,
1842
+ taker.order.maker,
1843
+ taker.order.nonce,
1844
+ makers
1845
+ );
1425
1846
  const t = taker.order;
1426
1847
  const m0 = makers[0].order;
1427
- const SIDE_BUY = 1;
1428
- const SIDE_SELL = 0;
1848
+ const SIDE_BUY = 0;
1849
+ const SIDE_SELL = 1;
1429
1850
  if (t.tokenId === m0.tokenId) {
1430
1851
  if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
1431
- return this.matchComplementary(taker, makers, collateralMint, feeRecipient, whitelistedWallet, lookupTable);
1852
+ return this.matchComplementary(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt, opts);
1432
1853
  }
1433
1854
  if (t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_BUY)) {
1434
1855
  throw new InvalidParamError("COMPLEMENTARY N-maker: taker must be the BUY side");
@@ -1437,19 +1858,11 @@ var ClobClient = class {
1437
1858
  }
1438
1859
  const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
1439
1860
  const allSell = t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_SELL);
1440
- if (!allBuy && !allSell) {
1441
- throw new InvalidParamError("MINT/MERGE: all orders must be the same side (all BUY or all SELL)");
1442
- }
1443
- if (t.tokenId !== 1) {
1444
- throw new InvalidParamError("MINT/MERGE: taker must be the YES side (tokenId=1)");
1445
- }
1446
- if (!makers.every((m) => m.order.tokenId === 0)) {
1447
- throw new InvalidParamError("MINT/MERGE: all makers must be the NO side (tokenId=0)");
1448
- }
1449
- if (allBuy) {
1450
- return this.matchMintOrders(taker, makers, collateralMint, whitelistedWallet, lookupTable);
1451
- }
1452
- return this.matchMergeOrders(taker, makers, collateralMint, whitelistedWallet, lookupTable);
1861
+ if (!allBuy && !allSell) throw new InvalidParamError("MINT/MERGE: all orders must be same side");
1862
+ if (t.tokenId !== 1) throw new InvalidParamError("MINT/MERGE: taker must be YES (tokenId=1)");
1863
+ if (!makers.every((m) => m.order.tokenId === 0)) throw new InvalidParamError("MINT/MERGE: makers must be NO (tokenId=0)");
1864
+ if (allBuy) return this.matchMintOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt);
1865
+ return this.matchMergeOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt);
1453
1866
  }
1454
1867
  // ─── Whitelist admin ─────────────────────────────────────────────────────────
1455
1868
  /** Add an address to the CLOB whitelist (owner only). */
@@ -1548,6 +1961,369 @@ var ClobClient = class {
1548
1961
  return status?.isCancelled === true;
1549
1962
  }
1550
1963
  };
1964
+ var FeeManagementClient = class {
1965
+ constructor(program, provider, programIds) {
1966
+ this.program = program;
1967
+ this.provider = provider;
1968
+ this.programIds = programIds;
1969
+ }
1970
+ async initFeeConfig(admin, companyAddress, referralAddress, presaleRevenueAddress, investorsMarketRev, companyMarketRev, agentsPresaleRev, companyPresaleRev, authority, payer) {
1971
+ const [feeConfigPda] = PDA.feeConfig(authority, this.programIds);
1972
+ const sig = await this.program.methods.initialize({
1973
+ admin,
1974
+ companyAddress,
1975
+ referralAddress,
1976
+ presaleRevenueAddress,
1977
+ investorsMarketRev,
1978
+ companyMarketRev,
1979
+ agentsPresaleRev,
1980
+ companyPresaleRev
1981
+ }).accounts({
1982
+ authority,
1983
+ payer,
1984
+ feeConfig: feeConfigPda,
1985
+ systemProgram: web3_js.SystemProgram.programId
1986
+ }).signers([]).rpc();
1987
+ return { signature: sig };
1988
+ }
1989
+ async fetchFeeConfig(owner) {
1990
+ try {
1991
+ const [pda] = PDA.feeConfig(owner, this.programIds);
1992
+ return await this.program.account.feeConfig.fetch(pda);
1993
+ } catch {
1994
+ return null;
1995
+ }
1996
+ }
1997
+ /**
1998
+ * Fetch per-question fees for a condition.
1999
+ * Returns null if no fees have been set yet.
2000
+ */
2001
+ async fetchQuestionFee(conditionPda) {
2002
+ try {
2003
+ const [pda] = PDA.questionFee(conditionPda, this.programIds);
2004
+ const acc = await this.program.account.questionFee.fetch(pda);
2005
+ return {
2006
+ conditionId: new Uint8Array(acc.conditionId),
2007
+ mergeFee: acc.mergeFee,
2008
+ redeemFee: acc.redeemFee,
2009
+ swapFee: acc.swapFee,
2010
+ bump: acc.bump
2011
+ };
2012
+ } catch {
2013
+ return null;
2014
+ }
2015
+ }
2016
+ /**
2017
+ * Set per-question fees for a condition.
2018
+ * @param conditionPda - condition PDA from the question
2019
+ * @param mergeFee - fee out of FEE_DENOMINATOR (1_000_000), e.g. 2_000 = 0.2%
2020
+ * @param redeemFee - fee out of FEE_DENOMINATOR
2021
+ * @param swapFee - trading fee out of FEE_DENOMINATOR
2022
+ * @param feeConfigOwner - pubkey that owns the fee_config PDA (usually market deployer)
2023
+ * @param authority - signer authorized in fee_config whitelist / admin / owner
2024
+ * @param payer - rent + tx fee payer
2025
+ */
2026
+ async updateFeeConfig(companyAddress, referralAddress, presaleRevenueAddress, investorsMarketRev, companyMarketRev, agentsPresaleRev, companyPresaleRev, authority, payer) {
2027
+ const [feeConfigPda] = PDA.feeConfig(authority, this.programIds);
2028
+ const sig = await this.program.methods.updateConfig({
2029
+ companyAddress,
2030
+ referralAddress,
2031
+ presaleRevenueAddress,
2032
+ investorsMarketRev,
2033
+ companyMarketRev,
2034
+ agentsPresaleRev,
2035
+ companyPresaleRev
2036
+ }).accounts({
2037
+ authority,
2038
+ payer,
2039
+ feeConfig: feeConfigPda
2040
+ }).signers([]).rpc();
2041
+ return { signature: sig };
2042
+ }
2043
+ async setMarketFeeOverride(conditionPda, investors, company, isAdmin, feeConfigOwner, authority, payer) {
2044
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2045
+ const [marketFeeOverridePda] = PDA.marketFeeOverride(conditionPda, this.programIds);
2046
+ const sig = await this.program.methods.setMarketFeeOverride(conditionPda, investors, company, isAdmin).accounts({
2047
+ authority,
2048
+ payer,
2049
+ feeConfig: feeConfigPda,
2050
+ marketFeeOverride: marketFeeOverridePda,
2051
+ systemProgram: web3_js.SystemProgram.programId
2052
+ }).signers([]).rpc();
2053
+ return { signature: sig };
2054
+ }
2055
+ async buildDistributeFeeIx(conditionPda, amount, feeConfigOwner, sourceAta, companyAta, marketOracleVault, authority) {
2056
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2057
+ const [marketFeeOverridePda] = PDA.marketFeeOverride(conditionPda, this.programIds);
2058
+ return this.program.methods.distributeFee(conditionPda, amount).accounts({
2059
+ feeConfig: feeConfigPda,
2060
+ marketFeeOverride: marketFeeOverridePda,
2061
+ authority,
2062
+ sourceAta,
2063
+ companyAta,
2064
+ marketOracleVault,
2065
+ tokenProgram: new web3_js.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
2066
+ }).instruction();
2067
+ }
2068
+ async distributeFee(conditionPda, amount, feeConfigOwner, sourceAta, companyAta, marketOracleVault, authority) {
2069
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2070
+ const [marketFeeOverridePda] = PDA.marketFeeOverride(conditionPda, this.programIds);
2071
+ const sig = await this.program.methods.distributeFee(conditionPda, amount).accounts({
2072
+ feeConfig: feeConfigPda,
2073
+ marketFeeOverride: marketFeeOverridePda,
2074
+ authority,
2075
+ sourceAta,
2076
+ companyAta,
2077
+ marketOracleVault,
2078
+ tokenProgram: new web3_js.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
2079
+ }).signers([]).rpc();
2080
+ return { signature: sig };
2081
+ }
2082
+ async setQuestionFee(conditionPda, mergeFee, redeemFee, swapFee, feeConfigOwner, authority, payer) {
2083
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2084
+ const [questionFeePda] = PDA.questionFee(conditionPda, this.programIds);
2085
+ const sig = await this.program.methods.setQuestionFee(
2086
+ Array.from(conditionPda.toBytes()),
2087
+ mergeFee,
2088
+ redeemFee,
2089
+ swapFee
2090
+ ).accounts({
2091
+ authority,
2092
+ payer,
2093
+ feeConfig: feeConfigPda,
2094
+ questionFee: questionFeePda,
2095
+ systemProgram: web3_js.SystemProgram.programId
2096
+ }).signers([]).rpc();
2097
+ return { signature: sig };
2098
+ }
2099
+ };
2100
+ var PresaleClient = class {
2101
+ constructor(program, provider, programIds) {
2102
+ this.program = program;
2103
+ this.provider = provider;
2104
+ this.programIds = programIds;
2105
+ }
2106
+ get walletPubkey() {
2107
+ return this.provider.wallet.publicKey;
2108
+ }
2109
+ presalePda(questionMarketConfig, presaleIndex) {
2110
+ return PDA.presale(questionMarketConfig, presaleIndex, this.programIds)[0];
2111
+ }
2112
+ qtMintPda(presalePda) {
2113
+ return PDA.qtMint(presalePda, this.programIds)[0];
2114
+ }
2115
+ qtAuthorityPda(presalePda) {
2116
+ return PDA.qtAuthority(presalePda, this.programIds)[0];
2117
+ }
2118
+ userBuyRecordPda(presalePda, user) {
2119
+ return PDA.userBuyRecord(presalePda, user, this.programIds)[0];
2120
+ }
2121
+ /**
2122
+ * Buy MST tokens during presale.
2123
+ * qtAmount: amount of MST to receive (9 decimals).
2124
+ * USDC cost = qtAmount * price / 1e9.
2125
+ */
2126
+ async buy(presalePda, qtAmount, buyer = this.walletPubkey, payer = this.walletPubkey, signers = []) {
2127
+ const presale = await this.fetchPresale(presalePda);
2128
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2129
+ const qtMint = presale.qtMint;
2130
+ const currencyMint = presale.currencyMint;
2131
+ const qtAuthority = this.qtAuthorityPda(presalePda);
2132
+ const userBuyRecord = this.userBuyRecordPda(presalePda, buyer);
2133
+ const buyerQtAta = splToken.getAssociatedTokenAddressSync(qtMint, buyer);
2134
+ const buyerCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, buyer);
2135
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2136
+ const sig = await this.program.methods.buy(qtAmount).accounts({
2137
+ buyer,
2138
+ payer,
2139
+ presale: presalePda,
2140
+ qtMint,
2141
+ qtAuthority,
2142
+ buyerQtAta,
2143
+ buyerCurrencyAta,
2144
+ presaleVault,
2145
+ currencyMint,
2146
+ userBuyRecord,
2147
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2148
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2149
+ systemProgram: web3_js.SystemProgram.programId
2150
+ }).signers(signers).rpc();
2151
+ return { signature: sig };
2152
+ }
2153
+ /**
2154
+ * Refund: burn user's MST and return USDC.
2155
+ * Only callable when presale status = Rejected.
2156
+ */
2157
+ async refund(presalePda, user = this.walletPubkey, signers = []) {
2158
+ const presale = await this.fetchPresale(presalePda);
2159
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2160
+ const qtMint = presale.qtMint;
2161
+ const currencyMint = presale.currencyMint;
2162
+ const userBuyRecord = this.userBuyRecordPda(presalePda, user);
2163
+ const userQtAta = splToken.getAssociatedTokenAddressSync(qtMint, user);
2164
+ const userCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, user);
2165
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2166
+ const sig = await this.program.methods.refund().accounts({
2167
+ user,
2168
+ presale: presalePda,
2169
+ qtMint,
2170
+ userQtAta,
2171
+ userCurrencyAta,
2172
+ presaleVault,
2173
+ currencyMint,
2174
+ userBuyRecord,
2175
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2176
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2177
+ systemProgram: web3_js.SystemProgram.programId
2178
+ }).signers(signers).rpc();
2179
+ return { signature: sig };
2180
+ }
2181
+ /**
2182
+ * Creator claims their share of presale revenue after distribute_presale_revenue.
2183
+ */
2184
+ async claimRevenue(presalePda, creator = this.walletPubkey, signers = []) {
2185
+ const presale = await this.fetchPresale(presalePda);
2186
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2187
+ const currencyMint = presale.currencyMint;
2188
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2189
+ const creatorCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, creator);
2190
+ const sig = await this.program.methods.claimRevenue().accounts({
2191
+ creator,
2192
+ presale: presalePda,
2193
+ presaleVault,
2194
+ creatorCurrencyAta,
2195
+ currencyMint,
2196
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2197
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2198
+ systemProgram: web3_js.SystemProgram.programId
2199
+ }).signers(signers).rpc();
2200
+ return { signature: sig };
2201
+ }
2202
+ // ─── Queries ─────────────────────────────────────────────────────────────────
2203
+ async fetchPresale(presalePda) {
2204
+ try {
2205
+ const acc = await this.program.account.presaleAccount.fetch(presalePda);
2206
+ const rawStatus = acc.status;
2207
+ const status = rawStatus.approved !== void 0 ? "approved" : rawStatus.rejected !== void 0 ? "rejected" : "pending";
2208
+ return {
2209
+ version: acc.version,
2210
+ questionMarketConfig: acc.questionMarketConfig,
2211
+ presaleIndex: acc.presaleIndex,
2212
+ creator: acc.creator,
2213
+ currencyMint: acc.currencyMint,
2214
+ qtMint: acc.qtMint,
2215
+ price: acc.price,
2216
+ startTime: acc.startTime,
2217
+ endTime: acc.endTime,
2218
+ status,
2219
+ soldTokenAmount: acc.soldTokenAmount,
2220
+ initialTokenAmountCreator: acc.initialTokenAmountCreator,
2221
+ agentsRev: acc.agentsRev,
2222
+ companyRev: acc.companyRev,
2223
+ referralAddress: acc.referralAddress,
2224
+ companyAddress: acc.companyAddress,
2225
+ isDistributeRevenue: acc.isDistributeRevenue,
2226
+ creatorClaimableRevenue: acc.creatorClaimableRevenue,
2227
+ bump: acc.bump
2228
+ };
2229
+ } catch {
2230
+ return null;
2231
+ }
2232
+ }
2233
+ async fetchUserBuyRecord(presalePda, user) {
2234
+ try {
2235
+ const pda = this.userBuyRecordPda(presalePda, user);
2236
+ const acc = await this.program.account.userBuyRecord.fetch(pda);
2237
+ return {
2238
+ user: acc.user,
2239
+ presale: acc.presale,
2240
+ currencyAmount: acc.currencyAmount,
2241
+ bump: acc.bump
2242
+ };
2243
+ } catch {
2244
+ return null;
2245
+ }
2246
+ }
2247
+ };
2248
+ var MarketOracleClient = class {
2249
+ constructor(program, provider, programIds) {
2250
+ this.program = program;
2251
+ this.provider = provider;
2252
+ this.programIds = programIds;
2253
+ }
2254
+ get walletPubkey() {
2255
+ return this.provider.wallet.publicKey;
2256
+ }
2257
+ marketOraclePda(questionPda) {
2258
+ return PDA.marketOraclePda(questionPda, this.programIds)[0];
2259
+ }
2260
+ userClaimRecordPda(marketOraclePda, user) {
2261
+ return PDA.userClaimRecord(marketOraclePda, user, this.programIds)[0];
2262
+ }
2263
+ /**
2264
+ * User burns their MST and claims proportional share of oracle vault USDC.
2265
+ * Call after market.collectTradingFee snapshotted qt supply.
2266
+ */
2267
+ async claimFeesShare(marketOraclePda, user = this.walletPubkey, payer = this.walletPubkey, signers = []) {
2268
+ const oracle = await this.fetchMarketOracle(marketOraclePda);
2269
+ if (!oracle) throw new Error(`MarketOracle not found: ${marketOraclePda.toBase58()}`);
2270
+ const currencyMint = oracle.currencyMint;
2271
+ const qtMint = oracle.qtMint;
2272
+ const marketOracleVault = splToken.getAssociatedTokenAddressSync(currencyMint, marketOraclePda, true);
2273
+ const userQtAta = splToken.getAssociatedTokenAddressSync(qtMint, user);
2274
+ const userCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, user);
2275
+ const userClaimRecord = this.userClaimRecordPda(marketOraclePda, user);
2276
+ const sig = await this.program.methods.claimFeesShare().accounts({
2277
+ user,
2278
+ payer,
2279
+ marketOracle: marketOraclePda,
2280
+ marketOracleVault,
2281
+ currencyMint,
2282
+ qtMint,
2283
+ userQtAta,
2284
+ userCurrencyAta,
2285
+ userClaimRecord,
2286
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2287
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2288
+ systemProgram: web3_js.SystemProgram.programId
2289
+ }).signers(signers).rpc();
2290
+ return { signature: sig };
2291
+ }
2292
+ // ─── Queries ─────────────────────────────────────────────────────────────────
2293
+ async fetchMarketOracle(marketOraclePda) {
2294
+ try {
2295
+ const acc = await this.program.account.marketOracleAccount.fetch(marketOraclePda);
2296
+ return {
2297
+ version: acc.version,
2298
+ question: acc.question,
2299
+ questionMarketConfig: acc.questionMarketConfig,
2300
+ currencyMint: acc.currencyMint,
2301
+ creator: acc.creator,
2302
+ qtMint: acc.qtMint,
2303
+ feesDistributed: acc.feesDistributed,
2304
+ qtTotalSupply: acc.qtTotalSupply,
2305
+ totalClaimed: acc.totalClaimed,
2306
+ bump: acc.bump
2307
+ };
2308
+ } catch {
2309
+ return null;
2310
+ }
2311
+ }
2312
+ async fetchUserClaimRecord(marketOraclePda, user) {
2313
+ try {
2314
+ const pda = this.userClaimRecordPda(marketOraclePda, user);
2315
+ const acc = await this.program.account.userClaimRecord.fetch(pda);
2316
+ return {
2317
+ user: acc.user,
2318
+ marketOracle: acc.marketOracle,
2319
+ hasClaimed: acc.hasClaimed,
2320
+ bump: acc.bump
2321
+ };
2322
+ } catch {
2323
+ return null;
2324
+ }
2325
+ }
2326
+ };
1551
2327
  var XMarketSDK = class {
1552
2328
  constructor(config, wallet, marketOwner) {
1553
2329
  this.networkConfig = config;
@@ -1595,10 +2371,38 @@ var XMarketSDK = class {
1595
2371
  get clob() {
1596
2372
  if (!this._clob) {
1597
2373
  const program = new anchor4__namespace.Program(this._withAddress(clobExchangeIdl__default.default, this._programIds.clobExchange), this.provider);
1598
- this._clob = new ClobClient(program, this.provider, this._programIds);
2374
+ this._clob = new ClobClient(program, this.provider, this._programIds, this.networkConfig);
2375
+ if (this.networkConfig.feeConfigOwner && this._programIds.feeManagement) {
2376
+ this._clob.feeConfigOwner = this.networkConfig.feeConfigOwner;
2377
+ this._clob.feeClient = this.fee;
2378
+ }
1599
2379
  }
1600
2380
  return this._clob;
1601
2381
  }
2382
+ get fee() {
2383
+ if (!this._fee) {
2384
+ if (!this._programIds.feeManagement) throw new Error("feeManagement program ID not configured in NetworkConfig");
2385
+ const program = new anchor4__namespace.Program(this._withAddress(feeManagementIdl__default.default, this._programIds.feeManagement), this.provider);
2386
+ this._fee = new FeeManagementClient(program, this.provider, this._programIds);
2387
+ }
2388
+ return this._fee;
2389
+ }
2390
+ get presale() {
2391
+ if (!this._presale) {
2392
+ if (!this._programIds.presale) throw new Error("presale program ID not configured in NetworkConfig");
2393
+ const program = new anchor4__namespace.Program(this._withAddress(presaleIdl__default.default, this._programIds.presale), this.provider);
2394
+ this._presale = new PresaleClient(program, this.provider, this._programIds);
2395
+ }
2396
+ return this._presale;
2397
+ }
2398
+ get marketOracle() {
2399
+ if (!this._marketOracle) {
2400
+ if (!this._programIds.marketOracle) throw new Error("marketOracle program ID not configured in NetworkConfig");
2401
+ const program = new anchor4__namespace.Program(this._withAddress(marketOracleIdl__default.default, this._programIds.marketOracle), this.provider);
2402
+ this._marketOracle = new MarketOracleClient(program, this.provider, this._programIds);
2403
+ }
2404
+ return this._marketOracle;
2405
+ }
1602
2406
  };
1603
2407
  function buildOrder(params) {
1604
2408
  return {
@@ -1667,7 +2471,7 @@ function detectMatchType(a, b) {
1667
2471
  return _detectMatchType(_a, _b);
1668
2472
  }
1669
2473
  function _detectMatchType(a, b) {
1670
- const SIDE_BUY = 1;
2474
+ const SIDE_BUY = 0;
1671
2475
  if (a.tokenId === b.tokenId) {
1672
2476
  const aBuy2 = a.side === SIDE_BUY;
1673
2477
  const bBuy2 = b.side === SIDE_BUY;
@@ -1731,13 +2535,17 @@ function buildApproveAllOutcomeTokensTx(condition, signer, payer, delegate, prog
1731
2535
  exports.AccountNotFoundError = AccountNotFoundError;
1732
2536
  exports.ClobClient = ClobClient;
1733
2537
  exports.CtfClient = CtfClient;
2538
+ exports.FEE_DENOMINATOR = FEE_DENOMINATOR;
2539
+ exports.FeeManagementClient = FeeManagementClient;
1734
2540
  exports.HookClient = HookClient;
1735
2541
  exports.IX_SYSVAR = IX_SYSVAR;
1736
2542
  exports.InvalidParamError = InvalidParamError;
1737
2543
  exports.MAX_APPROVE_AMOUNT = MAX_APPROVE_AMOUNT;
1738
2544
  exports.MarketClient = MarketClient;
2545
+ exports.MarketOracleClient = MarketOracleClient;
1739
2546
  exports.OracleClient = OracleClient;
1740
2547
  exports.PDA = PDA;
2548
+ exports.PresaleClient = PresaleClient;
1741
2549
  exports.QuestionStatus = QuestionStatus;
1742
2550
  exports.SEEDS = SEEDS;
1743
2551
  exports.UnauthorizedError = UnauthorizedError;