@theliem/xmarket-sdk 3.0.1 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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-O53KMXDK.json');
8
8
  var hookIdl = require('./hook-THBRGUM6.json');
9
- var questionMarketIdl = require('./question_market-CB6ZUZ5E.json');
9
+ var questionMarketIdl = require('./question_market-RP3J3N2M.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-Q4NDVQFN.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());
@@ -202,7 +287,7 @@ var OracleClient = class {
202
287
  }).rpc();
203
288
  return { signature: sig };
204
289
  }
205
- /** Admin or owner adds an address to the oracle whitelist. */
290
+ /** Admin or owner adds an address to the oracle resolver whitelist. */
206
291
  async addToWhitelist(address, ownerPubkey) {
207
292
  const sig = await this.program.methods.addToWhitelist(address).accounts({
208
293
  authority: this.walletPubkey,
@@ -211,7 +296,7 @@ var OracleClient = class {
211
296
  }).rpc();
212
297
  return { signature: sig };
213
298
  }
214
- /** Admin or owner removes an address from the oracle whitelist. */
299
+ /** Admin or owner removes an address from the oracle resolver whitelist. */
215
300
  async removeFromWhitelist(address, ownerPubkey) {
216
301
  const sig = await this.program.methods.removeFromWhitelist(address).accounts({
217
302
  authority: this.walletPubkey,
@@ -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);
@@ -457,6 +544,7 @@ var InvalidParamError = class extends XMarketError {
457
544
  };
458
545
 
459
546
  // src/programs/market.ts
547
+ var TOKEN_METADATA_PROGRAM_ID = new web3_js.PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
460
548
  var MarketClient = class {
461
549
  constructor(program, provider, programIds, ownerPubkey) {
462
550
  this.program = program;
@@ -479,51 +567,12 @@ var MarketClient = class {
479
567
  systemProgram: web3_js.SystemProgram.programId
480
568
  }).transaction();
481
569
  }
482
- /**
483
- * Build createQuestion transaction.
484
- * @param payer - Pays rent for all new accounts (can differ from creator)
485
- * @param creator - Identity of the question creator (signs but does not pay)
486
- */
487
- async createQuestion(params, oracle, creator = this.walletPubkey, payer = creator) {
488
- const questionId = params.questionId ?? generateQuestionId(params.content);
489
- const contentHash = params.contentHash ?? generateContentHash(params.content);
490
- const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
491
- const [conditionPda] = PDA.condition(oracle, questionId, this.programIds);
492
- const [yesMint] = PDA.yesMint(conditionPda, this.programIds);
493
- const [noMint] = PDA.noMint(conditionPda, this.programIds);
494
- const [mintAuthority] = PDA.mintAuthority(conditionPda, this.programIds);
495
- const [collateralVault] = PDA.collateralVault(params.collateralMint, this.programIds);
496
- const tx = await this.program.methods.createQuestionWithCondition({
497
- questionId: Array.from(questionId),
498
- contentHash: Array.from(contentHash),
499
- hookProgram: params.hookProgram,
500
- authorizedClob: params.authorizedClob,
501
- expirationTime: new anchor4__namespace.BN(params.expirationTime)
502
- }).accounts({
503
- payer,
504
- creator,
505
- config: this.configPda,
506
- question: questionPda,
507
- currencyMint: params.collateralMint,
508
- oracle,
509
- condition: conditionPda,
510
- yesMint,
511
- noMint,
512
- mintAuthority,
513
- collateralVault,
514
- conditionalTokensProgram: this.programIds.conditionalTokens,
515
- tokenProgram: splToken.TOKEN_2022_PROGRAM_ID,
516
- systemProgram: web3_js.SystemProgram.programId,
517
- rent: web3_js.SYSVAR_RENT_PUBKEY
518
- }).transaction();
519
- return { tx, questionPda, conditionPda, questionId };
520
- }
521
570
  /**
522
571
  * Build createQuestionAdmin transaction (whitelist/admin path — status = Approved immediately).
523
572
  * @param creator - Whitelisted creator (must be in whitelist or be admin/owner)
524
573
  * @param payer - Fee payer (pays rent; can differ from creator)
525
574
  */
526
- async createQuestionAdmin(params, oracle, creator = this.walletPubkey, payer = creator) {
575
+ async createQuestionAdmin(params, oracle, creator = this.walletPubkey, payer = this.walletPubkey) {
527
576
  const questionId = params.questionId ?? generateQuestionId(params.content);
528
577
  const contentHash = params.contentHash ?? generateContentHash(params.content);
529
578
  const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
@@ -617,6 +666,7 @@ var MarketClient = class {
617
666
  questionCount: acc.questionCount.toNumber(),
618
667
  approvedCount: acc.approvedCount.toNumber(),
619
668
  rejectedCount: acc.rejectedCount.toNumber(),
669
+ presaleCount: acc.presaleCount.toNumber(),
620
670
  whitelist: acc.whitelist,
621
671
  whitelistLen: acc.whitelistLen,
622
672
  isPaused: acc.isPaused,
@@ -668,6 +718,163 @@ var MarketClient = class {
668
718
  if (!q || !q.condition) return { yes: null, no: null };
669
719
  return this.ctfClient.fetchBothPositions(q.condition, owner);
670
720
  }
721
+ // ─── Presale instructions ────────────────────────────────────────────────────
722
+ /**
723
+ * Any user creates a presale + initial buy (question-market::create_presale).
724
+ * Reads agents_rev / company_rev from fee_config.
725
+ *
726
+ * @param feeConfig fee_management fee_config PDA (owner = feeConfigOwner)
727
+ * @param currencyMint collateral token (e.g. USDC)
728
+ * @param presaleIndex config.presale_count — fetch config first or pass 0 for first presale
729
+ */
730
+ async createPresale(params, feeConfig, currencyMint, presaleIndex, creator = this.walletPubkey, payer = creator) {
731
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
732
+ const [presalePda] = PDA.presale(this.configPda, presaleIndex, this.programIds);
733
+ const [qtMint] = PDA.qtMint(presalePda, this.programIds);
734
+ const [qtAuthority] = PDA.qtAuthority(presalePda, this.programIds);
735
+ const [userBuyRecord] = PDA.userBuyRecord(presalePda, creator, this.programIds);
736
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
737
+ const creatorQtAta = splToken.getAssociatedTokenAddressSync(qtMint, creator);
738
+ const creatorCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, creator);
739
+ const [qtMetadata] = web3_js.PublicKey.findProgramAddressSync(
740
+ [Buffer.from("metadata"), TOKEN_METADATA_PROGRAM_ID.toBuffer(), qtMint.toBuffer()],
741
+ TOKEN_METADATA_PROGRAM_ID
742
+ );
743
+ const tx = await this.program.methods.createPresale({
744
+ price: params.price,
745
+ startTime: params.startTime,
746
+ endTime: params.endTime,
747
+ initialBuyAmount: params.initialBuyAmount
748
+ }).accounts({
749
+ creator,
750
+ payer,
751
+ config: this.configPda,
752
+ feeConfig,
753
+ presale: presalePda,
754
+ qtMint,
755
+ qtAuthority,
756
+ currencyMint,
757
+ presaleVault,
758
+ creatorCurrencyAta,
759
+ creatorQtAta,
760
+ userBuyRecord,
761
+ qtMetadata,
762
+ tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
763
+ presaleProgram: this.programIds.presale,
764
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
765
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
766
+ systemProgram: web3_js.SystemProgram.programId,
767
+ rent: web3_js.SYSVAR_RENT_PUBKEY
768
+ }).transaction();
769
+ return { tx, presalePda, qtMint };
770
+ }
771
+ /**
772
+ * Whitelist-only: approve presale → creates question + CTF condition + market_oracle in one tx.
773
+ *
774
+ * @param presalePda the presale account
775
+ * @param contentHash 32-byte hash for question content
776
+ * @param hookProgram token-2022 transfer hook program
777
+ * @param authorizedClob clob program allowed to do CTF transfers
778
+ * @param expirationTime Unix seconds
779
+ * @param creator presale creator pubkey (stored in question)
780
+ * @param currencyMint collateral mint
781
+ */
782
+ async approvePresale(presalePda, contentHash, hookProgram, authorizedClob, expirationTime, creator, currencyMint, caller = this.walletPubkey, payer = caller) {
783
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
784
+ if (!this.programIds.marketOracle) throw new Error("marketOracle program ID not configured");
785
+ const questionId = presalePda.toBytes();
786
+ const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
787
+ const marketConfig = await this.fetchConfig();
788
+ if (!marketConfig) throw new Error("QuestionMarketConfig not found");
789
+ const oraclePubkey = marketConfig.oracle;
790
+ const [conditionPda] = PDA.condition(oraclePubkey, questionId, this.programIds);
791
+ const [yesMint] = PDA.yesMint(conditionPda, this.programIds);
792
+ const [noMint] = PDA.noMint(conditionPda, this.programIds);
793
+ const [mintAuthority] = PDA.mintAuthority(conditionPda, this.programIds);
794
+ const [collateralVault] = PDA.collateralVault(currencyMint, this.programIds);
795
+ const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
796
+ const marketOracleVault = splToken.getAssociatedTokenAddressSync(currencyMint, marketOraclePda, true);
797
+ const tx = await this.program.methods.approvePresale({
798
+ contentHash: Array.from(contentHash),
799
+ hookProgram,
800
+ authorizedClob,
801
+ expirationTime,
802
+ creator
803
+ }).accounts({
804
+ caller,
805
+ payer,
806
+ config: this.configPda,
807
+ presale: presalePda,
808
+ question: questionPda,
809
+ currencyMint,
810
+ oracle: oraclePubkey,
811
+ condition: conditionPda,
812
+ yesMint,
813
+ noMint,
814
+ mintAuthority,
815
+ collateralVault,
816
+ marketOracle: marketOraclePda,
817
+ marketOracleVault,
818
+ conditionalTokensProgram: this.programIds.conditionalTokens,
819
+ presaleProgram: this.programIds.presale,
820
+ marketOracleProgram: this.programIds.marketOracle,
821
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
822
+ token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
823
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
824
+ systemProgram: web3_js.SystemProgram.programId,
825
+ rent: web3_js.SYSVAR_RENT_PUBKEY
826
+ }).transaction();
827
+ return { tx, questionPda, conditionPda, marketOraclePda, marketOracleVault };
828
+ }
829
+ /**
830
+ * Whitelist-only: reject presale so users can refund.
831
+ */
832
+ async rejectPresale(presalePda, caller = this.walletPubkey) {
833
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
834
+ return this.program.methods.rejectPresale().accounts({
835
+ caller,
836
+ config: this.configPda,
837
+ presale: presalePda,
838
+ presaleProgram: this.programIds.presale
839
+ }).transaction();
840
+ }
841
+ /**
842
+ * Whitelist-only: distribute presale vault funds → agents_rev% + company_rev% + rest to creator.
843
+ * Must be called after approvePresale.
844
+ */
845
+ async collectPresaleRevenue(presalePda, currencyMint, referralAddress, companyAddress, caller = this.walletPubkey) {
846
+ if (!this.programIds.presale) throw new Error("presale program ID not configured");
847
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
848
+ const referralTokenAccount = splToken.getAssociatedTokenAddressSync(currencyMint, referralAddress);
849
+ const companyTokenAccount = splToken.getAssociatedTokenAddressSync(currencyMint, companyAddress);
850
+ return this.program.methods.collectPresaleRevenue().accounts({
851
+ caller,
852
+ config: this.configPda,
853
+ presale: presalePda,
854
+ presaleVault,
855
+ currencyMint,
856
+ referralTokenAccount,
857
+ companyTokenAccount,
858
+ presaleProgram: this.programIds.presale,
859
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
860
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
861
+ systemProgram: web3_js.SystemProgram.programId
862
+ }).transaction();
863
+ }
864
+ /**
865
+ * Whitelist-only: snapshot MST supply so holders can claim trading fees.
866
+ * Call after oracle resolves the question.
867
+ */
868
+ async collectTradingFee(marketOraclePda, qtMint, caller = this.walletPubkey) {
869
+ if (!this.programIds.marketOracle) throw new Error("marketOracle program ID not configured");
870
+ return this.program.methods.collectTradingFee().accounts({
871
+ caller,
872
+ config: this.configPda,
873
+ marketOracle: marketOraclePda,
874
+ qtMint,
875
+ marketOracleProgram: this.programIds.marketOracle
876
+ }).transaction();
877
+ }
671
878
  };
672
879
  var CtfClient = class {
673
880
  constructor(program, provider, programIds) {
@@ -1077,14 +1284,138 @@ var IX_SYSVAR = web3_js.SYSVAR_INSTRUCTIONS_PUBKEY;
1077
1284
  // src/programs/clob.ts
1078
1285
  var CLOB_WHITELIST_SEED = Buffer.from("clob_whitelist");
1079
1286
  var ClobClient = class {
1080
- constructor(program, provider, programIds) {
1287
+ constructor(program, provider, programIds, networkConfig) {
1288
+ /** ALT cache: condition.toBase58() → loaded ALT account */
1289
+ this._altCache = /* @__PURE__ */ new Map();
1081
1290
  this.program = program;
1082
1291
  this.provider = provider;
1083
1292
  this.programIds = programIds;
1293
+ this.networkConfig = networkConfig;
1294
+ }
1295
+ async companyAddress() {
1296
+ if (!this.feeClient || !this.feeConfigOwner) return void 0;
1297
+ if (!this._companyAddress) {
1298
+ const cfg = await this.feeClient.fetchFeeConfig(this.feeConfigOwner);
1299
+ if (cfg) this._companyAddress = cfg.companyAddress;
1300
+ }
1301
+ return this._companyAddress;
1084
1302
  }
1085
1303
  get walletPubkey() {
1086
1304
  return this.provider.wallet.publicKey;
1087
1305
  }
1306
+ /**
1307
+ * Get or create an ALT for a condition.
1308
+ * First call: creates + extends ALT on-chain, waits for activation (~2s).
1309
+ * Subsequent calls for same condition: returns cached ALT instantly.
1310
+ * BE devs never interact with this — called automatically by matchOrders.
1311
+ */
1312
+ async ensureAlt(condition, collateralMint, buyerPubkey, buyerNonce, makers) {
1313
+ const cacheKey = condition.toBase58();
1314
+ if (this._altCache.has(cacheKey)) {
1315
+ return this._altCache.get(cacheKey);
1316
+ }
1317
+ const { connection } = this.provider;
1318
+ const payer = this.walletPubkey;
1319
+ const [yesMint] = PDA.yesMint(condition, this.programIds);
1320
+ const [extraAccountMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
1321
+ const [hookConfig] = PDA.hookConfig(this.programIds);
1322
+ const [buyOrderStatus] = PDA.orderStatus(buyerPubkey, buyerNonce, this.programIds);
1323
+ const [buyerPosition] = PDA.position(condition, 1, buyerPubkey, this.programIds);
1324
+ const clobConfigPda = this.configPda();
1325
+ const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
1326
+ const addresses = [
1327
+ // instruction program IDs — must be in ALT to keep static keys minimal
1328
+ web3_js.Ed25519Program.programId,
1329
+ this.programIds.clobExchange,
1330
+ // other programs
1331
+ this.programIds.conditionalTokens,
1332
+ this.programIds.hook,
1333
+ splToken.TOKEN_PROGRAM_ID,
1334
+ splToken.TOKEN_2022_PROGRAM_ID,
1335
+ web3_js.SystemProgram.programId,
1336
+ web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
1337
+ // config PDAs
1338
+ clobConfigPda,
1339
+ hookConfig,
1340
+ this.whitelistEntryPda(payer),
1341
+ // condition PDAs
1342
+ condition,
1343
+ yesMint,
1344
+ extraAccountMeta,
1345
+ // buyer pubkey + ATAs + PDAs
1346
+ buyerPubkey,
1347
+ splToken.getAssociatedTokenAddressSync(collateralMint, buyerPubkey),
1348
+ splToken.getAssociatedTokenAddressSync(yesMint, buyerPubkey, false, splToken.TOKEN_2022_PROGRAM_ID),
1349
+ buyerPosition,
1350
+ buyOrderStatus
1351
+ ];
1352
+ if (feeRecipientAddr) addresses.push(feeRecipientAddr);
1353
+ for (const m of makers) {
1354
+ const seller = m.order.maker;
1355
+ const [sellerPos] = PDA.position(condition, 1, seller, this.programIds);
1356
+ const [sellerStatus] = PDA.orderStatus(seller, m.order.nonce, this.programIds);
1357
+ addresses.push(
1358
+ seller,
1359
+ splToken.getAssociatedTokenAddressSync(yesMint, seller, false, splToken.TOKEN_2022_PROGRAM_ID),
1360
+ splToken.getAssociatedTokenAddressSync(collateralMint, seller),
1361
+ sellerPos,
1362
+ sellerStatus
1363
+ );
1364
+ }
1365
+ if (this.programIds.feeManagement && this.feeConfigOwner) {
1366
+ const companyAddr = await this.companyAddress();
1367
+ addresses.push(
1368
+ this.programIds.feeManagement,
1369
+ PDA.feeConfig(this.feeConfigOwner, this.programIds)[0],
1370
+ PDA.marketFeeOverride(condition, this.programIds)[0]
1371
+ );
1372
+ if (companyAddr) {
1373
+ addresses.push(splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr));
1374
+ }
1375
+ addresses.push(payer);
1376
+ }
1377
+ const slot = await connection.getSlot("finalized");
1378
+ const [createIx, altAddress] = web3_js.AddressLookupTableProgram.createLookupTable({
1379
+ authority: payer,
1380
+ payer,
1381
+ recentSlot: slot
1382
+ });
1383
+ const BATCH = 30;
1384
+ const extendIxs = [];
1385
+ for (let i = 0; i < addresses.length; i += BATCH) {
1386
+ extendIxs.push(web3_js.AddressLookupTableProgram.extendLookupTable({
1387
+ payer,
1388
+ authority: payer,
1389
+ lookupTable: altAddress,
1390
+ addresses: addresses.slice(i, i + BATCH)
1391
+ }));
1392
+ }
1393
+ await this._sendLegacyTx([createIx, extendIxs[0]]);
1394
+ for (let i = 1; i < extendIxs.length; i++) {
1395
+ await this._sendLegacyTx([extendIxs[i]]);
1396
+ }
1397
+ for (let attempt = 0; attempt < 30; attempt++) {
1398
+ await new Promise((r) => setTimeout(r, 1e3));
1399
+ const res = await connection.getAddressLookupTable(altAddress);
1400
+ if (res.value && res.value.state.addresses.length === addresses.length) {
1401
+ this._altCache.set(cacheKey, res.value);
1402
+ return res.value;
1403
+ }
1404
+ }
1405
+ throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
1406
+ }
1407
+ async _sendLegacyTx(instructions) {
1408
+ const { connection } = this.provider;
1409
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1410
+ const { Transaction: Transaction4 } = await import('@solana/web3.js');
1411
+ const tx = new Transaction4();
1412
+ tx.recentBlockhash = blockhash;
1413
+ tx.feePayer = this.walletPubkey;
1414
+ tx.add(...instructions);
1415
+ const signed = await this.provider.wallet.signTransaction(tx);
1416
+ const sig = await connection.sendRawTransaction(signed.serialize());
1417
+ await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, "confirmed");
1418
+ }
1088
1419
  /**
1089
1420
  * Send a match transaction as versioned (v0).
1090
1421
  * Pass a pre-built AddressLookupTableAccount to compress account keys and
@@ -1094,7 +1425,7 @@ var ClobClient = class {
1094
1425
  * If `whitelistedWallet` is provided and differs from `this.provider.wallet`,
1095
1426
  * both wallets sign the transaction (whitelisted operator + payer).
1096
1427
  */
1097
- async _sendMatchTx(instructions, lookupTable, whitelistedWallet) {
1428
+ async sendMatchTx(instructions, lookupTable, whitelistedWallet) {
1098
1429
  const { connection } = this.provider;
1099
1430
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1100
1431
  const message = new web3_js.TransactionMessage({
@@ -1203,7 +1534,13 @@ var ClobClient = class {
1203
1534
  *
1204
1535
  * remaining_accounts: [hook×3] [seller×5 × N]
1205
1536
  */
1206
- async matchComplementary(buySigned, makersSigned, collateralMint, feeRecipient, whitelistedWallet, lookupTable) {
1537
+ /** Build Ed25519 + matchComplementary instructions without sending.
1538
+ *
1539
+ * If `buySigned.order.fee > 0` and `feeClient` + `feeConfigOwner` are wired in,
1540
+ * automatically appends 5 fee_management remaining_accounts so the program CPIs
1541
+ * to distribute_fee internally. No manual `feeDistribute` param needed.
1542
+ */
1543
+ async buildMatchComplementaryIxs(buySigned, makersSigned, collateralMint, feeRecipient, whitelisted, opts) {
1207
1544
  const condition = buySigned.order.condition;
1208
1545
  const tokenId = buySigned.order.tokenId;
1209
1546
  const [outcomeMint] = tokenId === 1 ? PDA.yesMint(condition, this.programIds) : PDA.noMint(condition, this.programIds);
@@ -1233,12 +1570,23 @@ var ClobClient = class {
1233
1570
  { pubkey: sellOrderStatus, isSigner: false, isWritable: true }
1234
1571
  ];
1235
1572
  });
1236
- const whitelisted = whitelistedWallet.publicKey;
1573
+ let feeAccounts = [];
1574
+ if (buySigned.order.fee.gtn(0) && this.programIds.feeManagement && this.feeConfigOwner) {
1575
+ const companyAddr = await this.companyAddress();
1576
+ if (companyAddr) {
1577
+ const oracleVault = opts?.marketOracleVault ?? this.walletPubkey;
1578
+ feeAccounts = [
1579
+ { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
1580
+ { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
1581
+ { pubkey: PDA.marketFeeOverride(condition, this.programIds)[0], isSigner: false, isWritable: false },
1582
+ { pubkey: splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
1583
+ { pubkey: oracleVault, isSigner: false, isWritable: true }
1584
+ ];
1585
+ }
1586
+ }
1237
1587
  const whitelistEntry = this.whitelistEntryPda(whitelisted);
1238
1588
  const makerNonces = makersSigned.map((m) => m.order.nonce);
1239
- const ed25519Ixs = [
1240
- buildBatchedEd25519Instruction([buySigned, ...makersSigned])
1241
- ];
1589
+ const ed25519Ix = buildBatchedEd25519Instruction([buySigned, ...makersSigned]);
1242
1590
  const matchIx = await this.program.methods.matchComplementary(buySigned.order.nonce, makerNonces).accounts({
1243
1591
  whitelisted,
1244
1592
  payer: this.walletPubkey,
@@ -1257,12 +1605,19 @@ var ClobClient = class {
1257
1605
  tokenProgram: splToken.TOKEN_PROGRAM_ID,
1258
1606
  token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
1259
1607
  systemProgram: web3_js.SystemProgram.programId
1260
- }).remainingAccounts([...hookAccounts, ...makerAccounts]).instruction();
1261
- const sig = await this._sendMatchTx(
1262
- [...ed25519Ixs, matchIx],
1263
- lookupTable,
1264
- whitelistedWallet
1608
+ }).remainingAccounts([...hookAccounts, ...makerAccounts, ...feeAccounts]).instruction();
1609
+ return [ed25519Ix, matchIx];
1610
+ }
1611
+ async matchComplementary(buySigned, makersSigned, collateralMint, feeRecipient, whitelistedWallet, lookupTable, opts) {
1612
+ const ixs = await this.buildMatchComplementaryIxs(
1613
+ buySigned,
1614
+ makersSigned,
1615
+ collateralMint,
1616
+ feeRecipient,
1617
+ whitelistedWallet.publicKey,
1618
+ opts
1265
1619
  );
1620
+ const sig = await this.sendMatchTx(ixs, lookupTable, whitelistedWallet);
1266
1621
  return { signature: sig };
1267
1622
  }
1268
1623
  /**
@@ -1337,7 +1692,7 @@ var ClobClient = class {
1337
1692
  break;
1338
1693
  }
1339
1694
  }
1340
- const sig = await this._sendMatchTx(
1695
+ const sig = await this.sendMatchTx(
1341
1696
  [...ed25519Ixs, matchIx],
1342
1697
  lookupTable,
1343
1698
  whitelistedWallet
@@ -1414,7 +1769,7 @@ var ClobClient = class {
1414
1769
  break;
1415
1770
  }
1416
1771
  }
1417
- const sig = await this._sendMatchTx(
1772
+ const sig = await this.sendMatchTx(
1418
1773
  [...ed25519Ixs, matchIx],
1419
1774
  lookupTable,
1420
1775
  whitelistedWallet
@@ -1432,15 +1787,37 @@ var ClobClient = class {
1432
1787
  *
1433
1788
  * All makers must have the same tokenId and side as makers[0].
1434
1789
  */
1435
- async matchOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, lookupTable) {
1790
+ /**
1791
+ * Auto-detect match type and execute in a single transaction.
1792
+ * ALT is managed automatically (created on first call per condition, cached thereafter).
1793
+ * feeRecipient and collateralMint are derived from on-chain config.
1794
+ * Fee distribution (distribute_fee CPI) fires automatically when order.fee > 0.
1795
+ *
1796
+ * @param opts.marketOracleVault Required for presale markets (is_admin=false).
1797
+ * Pass the ATA of the market_oracle PDA so 50% of fee goes there.
1798
+ * For admin markets (is_admin=true) omit — payer is used as placeholder (no transfer).
1799
+ */
1800
+ async matchOrders(taker, makers, opts) {
1436
1801
  if (makers.length === 0) throw new InvalidParamError("At least 1 maker required");
1802
+ const collateralMint = this.networkConfig.defaultCollateral.mint;
1803
+ const cfg = await this.fetchConfig();
1804
+ if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
1805
+ const feeRecipient = cfg.feeRecipient;
1806
+ const whitelistedWallet = this.provider.wallet;
1807
+ const alt = await this.ensureAlt(
1808
+ taker.order.condition,
1809
+ collateralMint,
1810
+ taker.order.maker,
1811
+ taker.order.nonce,
1812
+ makers
1813
+ );
1437
1814
  const t = taker.order;
1438
1815
  const m0 = makers[0].order;
1439
1816
  const SIDE_BUY = 0;
1440
1817
  const SIDE_SELL = 1;
1441
1818
  if (t.tokenId === m0.tokenId) {
1442
1819
  if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
1443
- return this.matchComplementary(taker, makers, collateralMint, feeRecipient, whitelistedWallet, lookupTable);
1820
+ return this.matchComplementary(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt, opts);
1444
1821
  }
1445
1822
  if (t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_BUY)) {
1446
1823
  throw new InvalidParamError("COMPLEMENTARY N-maker: taker must be the BUY side");
@@ -1449,19 +1826,11 @@ var ClobClient = class {
1449
1826
  }
1450
1827
  const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
1451
1828
  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);
1829
+ if (!allBuy && !allSell) throw new InvalidParamError("MINT/MERGE: all orders must be same side");
1830
+ if (t.tokenId !== 1) throw new InvalidParamError("MINT/MERGE: taker must be YES (tokenId=1)");
1831
+ if (!makers.every((m) => m.order.tokenId === 0)) throw new InvalidParamError("MINT/MERGE: makers must be NO (tokenId=0)");
1832
+ if (allBuy) return this.matchMintOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt);
1833
+ return this.matchMergeOrders(taker, makers, collateralMint, feeRecipient, whitelistedWallet, alt);
1465
1834
  }
1466
1835
  // ─── Whitelist admin ─────────────────────────────────────────────────────────
1467
1836
  /** Add an address to the CLOB whitelist (owner only). */
@@ -1560,6 +1929,389 @@ var ClobClient = class {
1560
1929
  return status?.isCancelled === true;
1561
1930
  }
1562
1931
  };
1932
+ var FeeManagementClient = class {
1933
+ constructor(program, provider, programIds) {
1934
+ this.program = program;
1935
+ this.provider = provider;
1936
+ this.programIds = programIds;
1937
+ }
1938
+ async initFeeConfig(admin, companyAddress, referralAddress, presaleRevenueAddress, investorsMarketRev, companyMarketRev, agentsPresaleRev, companyPresaleRev, authority, payer) {
1939
+ const [feeConfigPda] = PDA.feeConfig(authority, this.programIds);
1940
+ const sig = await this.program.methods.initialize({
1941
+ admin,
1942
+ companyAddress,
1943
+ referralAddress,
1944
+ presaleRevenueAddress,
1945
+ investorsMarketRev,
1946
+ companyMarketRev,
1947
+ agentsPresaleRev,
1948
+ companyPresaleRev
1949
+ }).accounts({
1950
+ authority,
1951
+ payer,
1952
+ feeConfig: feeConfigPda,
1953
+ systemProgram: web3_js.SystemProgram.programId
1954
+ }).signers([]).rpc();
1955
+ return { signature: sig };
1956
+ }
1957
+ async fetchFeeConfig(owner) {
1958
+ try {
1959
+ const [pda] = PDA.feeConfig(owner, this.programIds);
1960
+ return await this.program.account.feeConfig.fetch(pda);
1961
+ } catch {
1962
+ return null;
1963
+ }
1964
+ }
1965
+ /**
1966
+ * Fetch per-question fees for a condition.
1967
+ * Returns null if no fees have been set yet.
1968
+ */
1969
+ async fetchQuestionFee(conditionPda) {
1970
+ try {
1971
+ const [pda] = PDA.questionFee(conditionPda, this.programIds);
1972
+ const acc = await this.program.account.questionFee.fetch(pda);
1973
+ return {
1974
+ conditionId: new Uint8Array(acc.conditionId),
1975
+ mergeFee: acc.mergeFee,
1976
+ redeemFee: acc.redeemFee,
1977
+ swapFee: acc.swapFee,
1978
+ bump: acc.bump
1979
+ };
1980
+ } catch {
1981
+ return null;
1982
+ }
1983
+ }
1984
+ /**
1985
+ * Set per-question fees for a condition.
1986
+ * @param conditionPda - condition PDA from the question
1987
+ * @param mergeFee - fee out of FEE_DENOMINATOR (1_000_000), e.g. 2_000 = 0.2%
1988
+ * @param redeemFee - fee out of FEE_DENOMINATOR
1989
+ * @param swapFee - trading fee out of FEE_DENOMINATOR
1990
+ * @param feeConfigOwner - pubkey that owns the fee_config PDA (usually market deployer)
1991
+ * @param authority - signer authorized in fee_config whitelist / admin / owner
1992
+ * @param payer - rent + tx fee payer
1993
+ */
1994
+ async updateFeeConfig(companyAddress, referralAddress, presaleRevenueAddress, investorsMarketRev, companyMarketRev, agentsPresaleRev, companyPresaleRev, authority, payer) {
1995
+ const [feeConfigPda] = PDA.feeConfig(authority, this.programIds);
1996
+ const sig = await this.program.methods.updateConfig({
1997
+ companyAddress,
1998
+ referralAddress,
1999
+ presaleRevenueAddress,
2000
+ investorsMarketRev,
2001
+ companyMarketRev,
2002
+ agentsPresaleRev,
2003
+ companyPresaleRev
2004
+ }).accounts({
2005
+ authority,
2006
+ payer,
2007
+ feeConfig: feeConfigPda
2008
+ }).signers([]).rpc();
2009
+ return { signature: sig };
2010
+ }
2011
+ async setMarketFeeOverride(conditionPda, investors, company, isAdmin, feeConfigOwner, authority, payer) {
2012
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2013
+ const [marketFeeOverridePda] = PDA.marketFeeOverride(conditionPda, this.programIds);
2014
+ const sig = await this.program.methods.setMarketFeeOverride(conditionPda, investors, company, isAdmin).accounts({
2015
+ authority,
2016
+ payer,
2017
+ feeConfig: feeConfigPda,
2018
+ marketFeeOverride: marketFeeOverridePda,
2019
+ systemProgram: web3_js.SystemProgram.programId
2020
+ }).signers([]).rpc();
2021
+ return { signature: sig };
2022
+ }
2023
+ async buildDistributeFeeIx(conditionPda, amount, feeConfigOwner, sourceAta, companyAta, marketOracleVault, authority) {
2024
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2025
+ const [marketFeeOverridePda] = PDA.marketFeeOverride(conditionPda, this.programIds);
2026
+ return this.program.methods.distributeFee(conditionPda, amount).accounts({
2027
+ feeConfig: feeConfigPda,
2028
+ marketFeeOverride: marketFeeOverridePda,
2029
+ authority,
2030
+ sourceAta,
2031
+ companyAta,
2032
+ marketOracleVault,
2033
+ tokenProgram: new web3_js.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
2034
+ }).instruction();
2035
+ }
2036
+ async distributeFee(conditionPda, amount, feeConfigOwner, sourceAta, companyAta, marketOracleVault, authority) {
2037
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2038
+ const [marketFeeOverridePda] = PDA.marketFeeOverride(conditionPda, this.programIds);
2039
+ const sig = await this.program.methods.distributeFee(conditionPda, amount).accounts({
2040
+ feeConfig: feeConfigPda,
2041
+ marketFeeOverride: marketFeeOverridePda,
2042
+ authority,
2043
+ sourceAta,
2044
+ companyAta,
2045
+ marketOracleVault,
2046
+ tokenProgram: new web3_js.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
2047
+ }).signers([]).rpc();
2048
+ return { signature: sig };
2049
+ }
2050
+ async setQuestionFee(conditionPda, mergeFee, redeemFee, swapFee, feeConfigOwner, authority, payer) {
2051
+ const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
2052
+ const [questionFeePda] = PDA.questionFee(conditionPda, this.programIds);
2053
+ const sig = await this.program.methods.setQuestionFee(
2054
+ Array.from(conditionPda.toBytes()),
2055
+ mergeFee,
2056
+ redeemFee,
2057
+ swapFee
2058
+ ).accounts({
2059
+ authority,
2060
+ payer,
2061
+ feeConfig: feeConfigPda,
2062
+ questionFee: questionFeePda,
2063
+ systemProgram: web3_js.SystemProgram.programId
2064
+ }).signers([]).rpc();
2065
+ return { signature: sig };
2066
+ }
2067
+ };
2068
+ var PresaleClient = class {
2069
+ constructor(program, provider, programIds) {
2070
+ this.program = program;
2071
+ this.provider = provider;
2072
+ this.programIds = programIds;
2073
+ }
2074
+ get walletPubkey() {
2075
+ return this.provider.wallet.publicKey;
2076
+ }
2077
+ presalePda(questionMarketConfig, presaleIndex) {
2078
+ return PDA.presale(questionMarketConfig, presaleIndex, this.programIds)[0];
2079
+ }
2080
+ qtMintPda(presalePda) {
2081
+ return PDA.qtMint(presalePda, this.programIds)[0];
2082
+ }
2083
+ qtAuthorityPda(presalePda) {
2084
+ return PDA.qtAuthority(presalePda, this.programIds)[0];
2085
+ }
2086
+ userBuyRecordPda(presalePda, user) {
2087
+ return PDA.userBuyRecord(presalePda, user, this.programIds)[0];
2088
+ }
2089
+ /**
2090
+ * Buy MST tokens during presale.
2091
+ * qtAmount: amount of MST to receive (9 decimals).
2092
+ * USDC cost = qtAmount * price / 1e9.
2093
+ */
2094
+ async buy(presalePda, qtAmount, buyer = this.walletPubkey, payer = this.walletPubkey, signers = []) {
2095
+ const presale = await this.fetchPresale(presalePda);
2096
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2097
+ const qtMint = presale.qtMint;
2098
+ const currencyMint = presale.currencyMint;
2099
+ const qtAuthority = this.qtAuthorityPda(presalePda);
2100
+ const userBuyRecord = this.userBuyRecordPda(presalePda, buyer);
2101
+ const buyerQtAta = splToken.getAssociatedTokenAddressSync(qtMint, buyer);
2102
+ const buyerCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, buyer);
2103
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2104
+ const sig = await this.program.methods.buy(qtAmount).accounts({
2105
+ buyer,
2106
+ payer,
2107
+ presale: presalePda,
2108
+ qtMint,
2109
+ qtAuthority,
2110
+ buyerQtAta,
2111
+ buyerCurrencyAta,
2112
+ presaleVault,
2113
+ currencyMint,
2114
+ userBuyRecord,
2115
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2116
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2117
+ systemProgram: web3_js.SystemProgram.programId
2118
+ }).signers(signers).rpc();
2119
+ return { signature: sig };
2120
+ }
2121
+ /**
2122
+ * Refund: burn user's MST and return USDC.
2123
+ * Only callable when presale status = Rejected.
2124
+ */
2125
+ async refund(presalePda, user = this.walletPubkey, signers = []) {
2126
+ const presale = await this.fetchPresale(presalePda);
2127
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2128
+ const qtMint = presale.qtMint;
2129
+ const currencyMint = presale.currencyMint;
2130
+ const userBuyRecord = this.userBuyRecordPda(presalePda, user);
2131
+ const userQtAta = splToken.getAssociatedTokenAddressSync(qtMint, user);
2132
+ const userCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, user);
2133
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2134
+ const sig = await this.program.methods.refund().accounts({
2135
+ user,
2136
+ presale: presalePda,
2137
+ qtMint,
2138
+ userQtAta,
2139
+ userCurrencyAta,
2140
+ presaleVault,
2141
+ currencyMint,
2142
+ userBuyRecord,
2143
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2144
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2145
+ systemProgram: web3_js.SystemProgram.programId
2146
+ }).signers(signers).rpc();
2147
+ return { signature: sig };
2148
+ }
2149
+ /**
2150
+ * Creator claims their share of presale revenue after distribute_presale_revenue.
2151
+ */
2152
+ async claimRevenue(presalePda, creator = this.walletPubkey, signers = []) {
2153
+ const presale = await this.fetchPresale(presalePda);
2154
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2155
+ const currencyMint = presale.currencyMint;
2156
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2157
+ const creatorCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, creator);
2158
+ const sig = await this.program.methods.claimRevenue().accounts({
2159
+ creator,
2160
+ presale: presalePda,
2161
+ presaleVault,
2162
+ creatorCurrencyAta,
2163
+ currencyMint,
2164
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2165
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2166
+ systemProgram: web3_js.SystemProgram.programId
2167
+ }).signers(signers).rpc();
2168
+ return { signature: sig };
2169
+ }
2170
+ /**
2171
+ * Transfer creator_claimable_revenue (80%) to BOTMM wallet.
2172
+ * Call after collectPresaleRevenue (distribute_presale_revenue).
2173
+ */
2174
+ async distributeBotmmRevenue(presalePda, botmmAddress, currencyMint) {
2175
+ if (!currencyMint) {
2176
+ const presale = await this.fetchPresale(presalePda);
2177
+ if (!presale) throw new Error(`Presale not found: ${presalePda.toBase58()}`);
2178
+ currencyMint = presale.currencyMint;
2179
+ }
2180
+ const presaleVault = splToken.getAssociatedTokenAddressSync(currencyMint, presalePda, true);
2181
+ const botmmTokenAccount = splToken.getAssociatedTokenAddressSync(currencyMint, botmmAddress);
2182
+ return this.program.methods.distributeBotmmRevenue().accounts({
2183
+ presale: presalePda,
2184
+ presaleVault,
2185
+ currencyMint,
2186
+ botmmTokenAccount,
2187
+ tokenProgram: splToken.TOKEN_PROGRAM_ID
2188
+ }).transaction();
2189
+ }
2190
+ // ─── Queries ─────────────────────────────────────────────────────────────────
2191
+ async fetchPresale(presalePda) {
2192
+ try {
2193
+ const acc = await this.program.account.presaleAccount.fetch(presalePda);
2194
+ const rawStatus = acc.status;
2195
+ const status = rawStatus.approved !== void 0 ? "approved" : rawStatus.rejected !== void 0 ? "rejected" : "pending";
2196
+ return {
2197
+ version: acc.version,
2198
+ questionMarketConfig: acc.questionMarketConfig,
2199
+ presaleIndex: acc.presaleIndex,
2200
+ creator: acc.creator,
2201
+ currencyMint: acc.currencyMint,
2202
+ qtMint: acc.qtMint,
2203
+ price: acc.price,
2204
+ startTime: acc.startTime,
2205
+ endTime: acc.endTime,
2206
+ status,
2207
+ soldTokenAmount: acc.soldTokenAmount,
2208
+ initialTokenAmountCreator: acc.initialTokenAmountCreator,
2209
+ agentsRev: acc.agentsRev,
2210
+ companyRev: acc.companyRev,
2211
+ referralAddress: acc.referralAddress,
2212
+ companyAddress: acc.companyAddress,
2213
+ isDistributeRevenue: acc.isDistributeRevenue,
2214
+ creatorClaimableRevenue: acc.creatorClaimableRevenue,
2215
+ bump: acc.bump
2216
+ };
2217
+ } catch {
2218
+ return null;
2219
+ }
2220
+ }
2221
+ async fetchUserBuyRecord(presalePda, user) {
2222
+ try {
2223
+ const pda = this.userBuyRecordPda(presalePda, user);
2224
+ const acc = await this.program.account.userBuyRecord.fetch(pda);
2225
+ return {
2226
+ user: acc.user,
2227
+ presale: acc.presale,
2228
+ currencyAmount: acc.currencyAmount,
2229
+ bump: acc.bump
2230
+ };
2231
+ } catch {
2232
+ return null;
2233
+ }
2234
+ }
2235
+ };
2236
+ var MarketOracleClient = class {
2237
+ constructor(program, provider, programIds) {
2238
+ this.program = program;
2239
+ this.provider = provider;
2240
+ this.programIds = programIds;
2241
+ }
2242
+ get walletPubkey() {
2243
+ return this.provider.wallet.publicKey;
2244
+ }
2245
+ marketOraclePda(questionPda) {
2246
+ return PDA.marketOraclePda(questionPda, this.programIds)[0];
2247
+ }
2248
+ userClaimRecordPda(marketOraclePda, user) {
2249
+ return PDA.userClaimRecord(marketOraclePda, user, this.programIds)[0];
2250
+ }
2251
+ /**
2252
+ * User burns their MST and claims proportional share of oracle vault USDC.
2253
+ * Call after market.collectTradingFee snapshotted qt supply.
2254
+ */
2255
+ async claimFeesShare(marketOraclePda, user = this.walletPubkey, payer = this.walletPubkey, signers = []) {
2256
+ const oracle = await this.fetchMarketOracle(marketOraclePda);
2257
+ if (!oracle) throw new Error(`MarketOracle not found: ${marketOraclePda.toBase58()}`);
2258
+ const currencyMint = oracle.currencyMint;
2259
+ const qtMint = oracle.qtMint;
2260
+ const marketOracleVault = splToken.getAssociatedTokenAddressSync(currencyMint, marketOraclePda, true);
2261
+ const userQtAta = splToken.getAssociatedTokenAddressSync(qtMint, user);
2262
+ const userCurrencyAta = splToken.getAssociatedTokenAddressSync(currencyMint, user);
2263
+ const userClaimRecord = this.userClaimRecordPda(marketOraclePda, user);
2264
+ const sig = await this.program.methods.claimFeesShare().accounts({
2265
+ user,
2266
+ payer,
2267
+ marketOracle: marketOraclePda,
2268
+ marketOracleVault,
2269
+ currencyMint,
2270
+ qtMint,
2271
+ userQtAta,
2272
+ userCurrencyAta,
2273
+ userClaimRecord,
2274
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2275
+ associatedTokenProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
2276
+ systemProgram: web3_js.SystemProgram.programId
2277
+ }).signers(signers).rpc();
2278
+ return { signature: sig };
2279
+ }
2280
+ // ─── Queries ─────────────────────────────────────────────────────────────────
2281
+ async fetchMarketOracle(marketOraclePda) {
2282
+ try {
2283
+ const acc = await this.program.account.marketOracleAccount.fetch(marketOraclePda);
2284
+ return {
2285
+ version: acc.version,
2286
+ question: acc.question,
2287
+ questionMarketConfig: acc.questionMarketConfig,
2288
+ currencyMint: acc.currencyMint,
2289
+ creator: acc.creator,
2290
+ qtMint: acc.qtMint,
2291
+ feesDistributed: acc.feesDistributed,
2292
+ qtTotalSupply: acc.qtTotalSupply,
2293
+ totalClaimed: acc.totalClaimed,
2294
+ bump: acc.bump
2295
+ };
2296
+ } catch {
2297
+ return null;
2298
+ }
2299
+ }
2300
+ async fetchUserClaimRecord(marketOraclePda, user) {
2301
+ try {
2302
+ const pda = this.userClaimRecordPda(marketOraclePda, user);
2303
+ const acc = await this.program.account.userClaimRecord.fetch(pda);
2304
+ return {
2305
+ user: acc.user,
2306
+ marketOracle: acc.marketOracle,
2307
+ hasClaimed: acc.hasClaimed,
2308
+ bump: acc.bump
2309
+ };
2310
+ } catch {
2311
+ return null;
2312
+ }
2313
+ }
2314
+ };
1563
2315
  var XMarketSDK = class {
1564
2316
  constructor(config, wallet, marketOwner) {
1565
2317
  this.networkConfig = config;
@@ -1607,10 +2359,38 @@ var XMarketSDK = class {
1607
2359
  get clob() {
1608
2360
  if (!this._clob) {
1609
2361
  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);
2362
+ this._clob = new ClobClient(program, this.provider, this._programIds, this.networkConfig);
2363
+ if (this.networkConfig.feeConfigOwner && this._programIds.feeManagement) {
2364
+ this._clob.feeConfigOwner = this.networkConfig.feeConfigOwner;
2365
+ this._clob.feeClient = this.fee;
2366
+ }
1611
2367
  }
1612
2368
  return this._clob;
1613
2369
  }
2370
+ get fee() {
2371
+ if (!this._fee) {
2372
+ if (!this._programIds.feeManagement) throw new Error("feeManagement program ID not configured in NetworkConfig");
2373
+ const program = new anchor4__namespace.Program(this._withAddress(feeManagementIdl__default.default, this._programIds.feeManagement), this.provider);
2374
+ this._fee = new FeeManagementClient(program, this.provider, this._programIds);
2375
+ }
2376
+ return this._fee;
2377
+ }
2378
+ get presale() {
2379
+ if (!this._presale) {
2380
+ if (!this._programIds.presale) throw new Error("presale program ID not configured in NetworkConfig");
2381
+ const program = new anchor4__namespace.Program(this._withAddress(presaleIdl__default.default, this._programIds.presale), this.provider);
2382
+ this._presale = new PresaleClient(program, this.provider, this._programIds);
2383
+ }
2384
+ return this._presale;
2385
+ }
2386
+ get marketOracle() {
2387
+ if (!this._marketOracle) {
2388
+ if (!this._programIds.marketOracle) throw new Error("marketOracle program ID not configured in NetworkConfig");
2389
+ const program = new anchor4__namespace.Program(this._withAddress(marketOracleIdl__default.default, this._programIds.marketOracle), this.provider);
2390
+ this._marketOracle = new MarketOracleClient(program, this.provider, this._programIds);
2391
+ }
2392
+ return this._marketOracle;
2393
+ }
1614
2394
  };
1615
2395
  function buildOrder(params) {
1616
2396
  return {
@@ -1743,13 +2523,17 @@ function buildApproveAllOutcomeTokensTx(condition, signer, payer, delegate, prog
1743
2523
  exports.AccountNotFoundError = AccountNotFoundError;
1744
2524
  exports.ClobClient = ClobClient;
1745
2525
  exports.CtfClient = CtfClient;
2526
+ exports.FEE_DENOMINATOR = FEE_DENOMINATOR;
2527
+ exports.FeeManagementClient = FeeManagementClient;
1746
2528
  exports.HookClient = HookClient;
1747
2529
  exports.IX_SYSVAR = IX_SYSVAR;
1748
2530
  exports.InvalidParamError = InvalidParamError;
1749
2531
  exports.MAX_APPROVE_AMOUNT = MAX_APPROVE_AMOUNT;
1750
2532
  exports.MarketClient = MarketClient;
2533
+ exports.MarketOracleClient = MarketOracleClient;
1751
2534
  exports.OracleClient = OracleClient;
1752
2535
  exports.PDA = PDA;
2536
+ exports.PresaleClient = PresaleClient;
1753
2537
  exports.QuestionStatus = QuestionStatus;
1754
2538
  exports.SEEDS = SEEDS;
1755
2539
  exports.UnauthorizedError = UnauthorizedError;