@theliem/xmarket-sdk 3.0.1 → 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
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
  /**
@@ -1337,7 +1724,7 @@ var ClobClient = class {
1337
1724
  break;
1338
1725
  }
1339
1726
  }
1340
- const sig = await this._sendMatchTx(
1727
+ const sig = await this.sendMatchTx(
1341
1728
  [...ed25519Ixs, matchIx],
1342
1729
  lookupTable,
1343
1730
  whitelistedWallet
@@ -1414,7 +1801,7 @@ var ClobClient = class {
1414
1801
  break;
1415
1802
  }
1416
1803
  }
1417
- const sig = await this._sendMatchTx(
1804
+ const sig = await this.sendMatchTx(
1418
1805
  [...ed25519Ixs, matchIx],
1419
1806
  lookupTable,
1420
1807
  whitelistedWallet
@@ -1432,15 +1819,37 @@ var ClobClient = class {
1432
1819
  *
1433
1820
  * All makers must have the same tokenId and side as makers[0].
1434
1821
  */
1435
- 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) {
1436
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
+ );
1437
1846
  const t = taker.order;
1438
1847
  const m0 = makers[0].order;
1439
1848
  const SIDE_BUY = 0;
1440
1849
  const SIDE_SELL = 1;
1441
1850
  if (t.tokenId === m0.tokenId) {
1442
1851
  if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
1443
- return this.matchComplementary(taker, makers, collateralMint, feeRecipient, whitelistedWallet, lookupTable);
1852
+ return this.matchComplementary(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt, opts);
1444
1853
  }
1445
1854
  if (t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_BUY)) {
1446
1855
  throw new InvalidParamError("COMPLEMENTARY N-maker: taker must be the BUY side");
@@ -1449,19 +1858,11 @@ var ClobClient = class {
1449
1858
  }
1450
1859
  const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
1451
1860
  const allSell = t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_SELL);
1452
- if (!allBuy && !allSell) {
1453
- throw new InvalidParamError("MINT/MERGE: all orders must be the same side (all BUY or all SELL)");
1454
- }
1455
- if (t.tokenId !== 1) {
1456
- throw new InvalidParamError("MINT/MERGE: taker must be the YES side (tokenId=1)");
1457
- }
1458
- if (!makers.every((m) => m.order.tokenId === 0)) {
1459
- throw new InvalidParamError("MINT/MERGE: all makers must be the NO side (tokenId=0)");
1460
- }
1461
- if (allBuy) {
1462
- return this.matchMintOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, lookupTable);
1463
- }
1464
- return this.matchMergeOrders(taker, makers, collateralMint, feeRecipient, 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);
1465
1866
  }
1466
1867
  // ─── Whitelist admin ─────────────────────────────────────────────────────────
1467
1868
  /** Add an address to the CLOB whitelist (owner only). */
@@ -1560,6 +1961,369 @@ var ClobClient = class {
1560
1961
  return status?.isCancelled === true;
1561
1962
  }
1562
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
+ };
1563
2327
  var XMarketSDK = class {
1564
2328
  constructor(config, wallet, marketOwner) {
1565
2329
  this.networkConfig = config;
@@ -1607,10 +2371,38 @@ var XMarketSDK = class {
1607
2371
  get clob() {
1608
2372
  if (!this._clob) {
1609
2373
  const program = new anchor4__namespace.Program(this._withAddress(clobExchangeIdl__default.default, this._programIds.clobExchange), this.provider);
1610
- 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
+ }
1611
2379
  }
1612
2380
  return this._clob;
1613
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
+ }
1614
2406
  };
1615
2407
  function buildOrder(params) {
1616
2408
  return {
@@ -1743,13 +2535,17 @@ function buildApproveAllOutcomeTokensTx(condition, signer, payer, delegate, prog
1743
2535
  exports.AccountNotFoundError = AccountNotFoundError;
1744
2536
  exports.ClobClient = ClobClient;
1745
2537
  exports.CtfClient = CtfClient;
2538
+ exports.FEE_DENOMINATOR = FEE_DENOMINATOR;
2539
+ exports.FeeManagementClient = FeeManagementClient;
1746
2540
  exports.HookClient = HookClient;
1747
2541
  exports.IX_SYSVAR = IX_SYSVAR;
1748
2542
  exports.InvalidParamError = InvalidParamError;
1749
2543
  exports.MAX_APPROVE_AMOUNT = MAX_APPROVE_AMOUNT;
1750
2544
  exports.MarketClient = MarketClient;
2545
+ exports.MarketOracleClient = MarketOracleClient;
1751
2546
  exports.OracleClient = OracleClient;
1752
2547
  exports.PDA = PDA;
2548
+ exports.PresaleClient = PresaleClient;
1753
2549
  exports.QuestionStatus = QuestionStatus;
1754
2550
  exports.SEEDS = SEEDS;
1755
2551
  exports.UnauthorizedError = UnauthorizedError;