@theliem/xmarket-sdk 3.29.0 → 4.0.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.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import * as anchor6 from '@coral-xyz/anchor';
2
2
  import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, SystemProgram, SYSVAR_RENT_PUBKEY, ComputeBudgetProgram, TransactionMessage, VersionedTransaction, Transaction, TransactionInstruction, Ed25519Program, AddressLookupTableProgram, Connection } from '@solana/web3.js';
3
3
  import { createHash } from 'crypto';
4
- import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, ASSOCIATED_TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, TOKEN_2022_PROGRAM_ID, createApproveInstruction } from '@solana/spl-token';
4
+ import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, ASSOCIATED_TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, createApproveInstruction } from '@solana/spl-token';
5
5
  import BN4 from 'bn.js';
6
6
  import * as nacl from 'tweetnacl';
7
7
 
8
- // src/sdk-v2.ts
8
+ // src/sdk.ts
9
9
  var SEEDS = {
10
10
  config: Buffer.from("config"),
11
11
  question: Buffer.from("question"),
@@ -19,8 +19,6 @@ var SEEDS = {
19
19
  reporter: Buffer.from("reporter"),
20
20
  result: Buffer.from("result"),
21
21
  ctfConfig: Buffer.from("ctf_config"),
22
- hookConfig: Buffer.from("hook_config"),
23
- extraAccountMetas: Buffer.from("extra-account-metas"),
24
22
  clobConfig: Buffer.from("clob_config"),
25
23
  order: Buffer.from("order"),
26
24
  feeConfig: Buffer.from("fee_config"),
@@ -125,19 +123,6 @@ var PDA = class {
125
123
  programIds.oracle
126
124
  );
127
125
  }
128
- // ─── Hook ───────────────────────────────────────────────────────────────────
129
- static hookConfig(programIds) {
130
- return PublicKey.findProgramAddressSync(
131
- [SEEDS.hookConfig],
132
- programIds.hook
133
- );
134
- }
135
- static extraAccountMetaList(mint, programIds) {
136
- return PublicKey.findProgramAddressSync(
137
- [SEEDS.extraAccountMetas, mint.toBuffer()],
138
- programIds.hook
139
- );
140
- }
141
126
  // ─── CLOB ───────────────────────────────────────────────────────────────────
142
127
  static clobConfig(programIds) {
143
128
  return PublicKey.findProgramAddressSync(
@@ -322,41 +307,49 @@ var OracleClient = class {
322
307
  }).rpc();
323
308
  return { signature: sig };
324
309
  }
325
- /** Admin or owner adds an address to the oracle resolver whitelist. */
326
- async addToWhitelist(address, ownerPubkey) {
327
- const sig = await this.program.methods.addToWhitelist(address).accounts({
310
+ /** Admin or owner registers a reporter PDA. */
311
+ async addReporter(reporterAddress, ownerPubkey) {
312
+ const oracleConfig = this.configPda(ownerPubkey);
313
+ const [reporterPda] = PDA.reporter(oracleConfig, reporterAddress, this.programIds);
314
+ const sig = await this.program.methods.addReporter().accounts({
328
315
  authority: this.walletPubkey,
329
- payer: this.walletPubkey,
330
- oracleConfig: this.configPda(ownerPubkey)
316
+ oracleConfig,
317
+ reporterAddress,
318
+ reporter: reporterPda,
319
+ systemProgram: SystemProgram.programId
331
320
  }).rpc();
332
321
  return { signature: sig };
333
322
  }
334
- /** Admin or owner removes an address from the oracle resolver whitelist. */
335
- async removeFromWhitelist(address, ownerPubkey) {
336
- const sig = await this.program.methods.removeFromWhitelist(address).accounts({
323
+ /** Admin or owner removes a reporter PDA. */
324
+ async removeReporter(reporterAddress, ownerPubkey) {
325
+ const oracleConfig = this.configPda(ownerPubkey);
326
+ const [reporterPda] = PDA.reporter(oracleConfig, reporterAddress, this.programIds);
327
+ const sig = await this.program.methods.removeReporter().accounts({
337
328
  authority: this.walletPubkey,
338
- payer: this.walletPubkey,
339
- oracleConfig: this.configPda(ownerPubkey)
329
+ oracleConfig,
330
+ reporterAddress,
331
+ reporter: reporterPda,
332
+ systemProgram: SystemProgram.programId
340
333
  }).rpc();
341
334
  return { signature: sig };
342
335
  }
343
336
  /**
344
- * Whitelisted reporter resolves a question.
345
- * reporter signs the instruction; payer covers the tx fee (Kora pattern).
337
+ * Registered reporter resolves a question.
338
+ * reporterSigner signs; payer covers rent for question_result PDA.
346
339
  */
347
- async resolveQuestion(questionId, outcomeCount, payoutNumerators, conditionPda, reporter = this.walletPubkey, payer = this.walletPubkey, ownerPubkey) {
340
+ async resolveQuestion(questionId, outcomeCount, payoutNumerators, reporterSigner = this.walletPubkey, payer = this.walletPubkey, ownerPubkey) {
348
341
  const oracleConfig = this.configPda(ownerPubkey);
342
+ const [reporterPda] = PDA.reporter(oracleConfig, reporterSigner, this.programIds);
349
343
  const [questionResultPda] = PDA.questionResult(oracleConfig, questionId, this.programIds);
350
344
  return this.program.methods.resolveQuestion(
351
345
  Array.from(questionId),
352
346
  outcomeCount,
353
347
  payoutNumerators.map((n) => new anchor6.BN(n))
354
348
  ).accounts({
355
- reporter,
349
+ reporterSigner,
356
350
  oracleConfig,
351
+ reporter: reporterPda,
357
352
  questionResult: questionResultPda,
358
- condition: conditionPda,
359
- conditionalTokensProgram: this.programIds.conditionalTokens,
360
353
  payer,
361
354
  systemProgram: SystemProgram.programId
362
355
  }).transaction();
@@ -396,8 +389,7 @@ var OracleClient = class {
396
389
  owner: acc.owner,
397
390
  admin: acc.admin,
398
391
  questionCount: acc.questionCount.toNumber(),
399
- whitelist: acc.whitelist.slice(0, acc.whitelistLen),
400
- whitelistLen: acc.whitelistLen,
392
+ reporterCount: acc.reporterCount.toNumber(),
401
393
  isPaused: acc.isPaused,
402
394
  bump: acc.bump
403
395
  };
@@ -405,6 +397,17 @@ var OracleClient = class {
405
397
  return null;
406
398
  }
407
399
  }
400
+ /** Check if an address is a registered reporter (Reporter PDA exists). */
401
+ async isReporter(reporterAddress, ownerPubkey) {
402
+ try {
403
+ const oracleConfig = this.configPda(ownerPubkey);
404
+ const [reporterPda] = PDA.reporter(oracleConfig, reporterAddress, this.programIds);
405
+ const info = await this.provider.connection.getAccountInfo(reporterPda);
406
+ return info !== null;
407
+ } catch {
408
+ return false;
409
+ }
410
+ }
408
411
  async fetchQuestionResult(questionId, ownerPubkey) {
409
412
  try {
410
413
  const oracleConfig = this.configPda(ownerPubkey);
@@ -463,9 +466,9 @@ var InvalidParamError = class extends XMarketError {
463
466
  }
464
467
  };
465
468
 
466
- // src/programs/market-v2.ts
469
+ // src/programs/market.ts
467
470
  var TOKEN_METADATA_PROGRAM_ID = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
468
- var MarketClientV2 = class {
471
+ var MarketClient = class {
469
472
  constructor(program, provider, programIds, ownerPubkey) {
470
473
  this.program = program;
471
474
  this.provider = provider;
@@ -658,7 +661,7 @@ var MarketClientV2 = class {
658
661
  /**
659
662
  * Convenience: fetch YES and NO token balances for a question.
660
663
  * Returns null for each if question not approved or position not initialized.
661
- * Requires ctfClient to be injected (done automatically by XMarketSDKV2).
664
+ * Requires ctfClient to be injected (done automatically by XMarketSDK).
662
665
  */
663
666
  async fetchQuestionBalances(questionPda, owner) {
664
667
  if (!this.ctfClient) return { yes: null, no: null };
@@ -879,6 +882,27 @@ var MarketClientV2 = class {
879
882
  systemProgram: SystemProgram.programId
880
883
  }).transaction();
881
884
  }
885
+ /**
886
+ * Resolve a question by reading the oracle question_result PDA and updating the CTF condition.
887
+ * Must be called after sdk.oracle.resolveQuestion records the result.
888
+ * Caller must be on the question_market whitelist.
889
+ */
890
+ async resolveQuestion(questionId, conditionPda, oracleOwner, payer = this.walletPubkey) {
891
+ if (!this.programIds.oracle) throw new Error("oracle program ID not configured");
892
+ const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
893
+ const oracleConfigPda = PDA.oracleConfig(oracleOwner, this.programIds)[0];
894
+ const [questionResult] = PDA.questionResult(oracleConfigPda, questionId, this.programIds);
895
+ return this.program.methods.resolveQuestion(Array.from(questionId)).accounts({
896
+ payer,
897
+ config: this.configPda,
898
+ question: questionPda,
899
+ questionResult,
900
+ condition: conditionPda,
901
+ ctfProgram: this.programIds.conditionalTokens,
902
+ oracleProgram: this.programIds.oracle,
903
+ systemProgram: SystemProgram.programId
904
+ }).transaction();
905
+ }
882
906
  /**
883
907
  * Whitelist-only: snapshot MST supply so holders can claim trading fees.
884
908
  * Call after oracle resolves the question.
@@ -894,7 +918,7 @@ var MarketClientV2 = class {
894
918
  }).transaction();
895
919
  }
896
920
  };
897
- var CtfClientV2 = class {
921
+ var CtfClient = class {
898
922
  constructor(program, provider, programIds) {
899
923
  this.program = program;
900
924
  this.provider = provider;
@@ -1483,8 +1507,8 @@ function _detectMatchType(a, b) {
1483
1507
  );
1484
1508
  }
1485
1509
 
1486
- // src/programs/clob-v2.ts
1487
- var ClobClientV2 = class {
1510
+ // src/programs/clob.ts
1511
+ var ClobClient = class {
1488
1512
  constructor(program, provider, programIds, networkConfig) {
1489
1513
  /** Cache: conditionPda.toBase58() → marketOracleVault ATA */
1490
1514
  this._marketOracleVaultCache = /* @__PURE__ */ new Map();
@@ -1692,8 +1716,8 @@ var ClobClientV2 = class {
1692
1716
  async _sendLegacyTxSig(instructions) {
1693
1717
  const { connection } = this.provider;
1694
1718
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1695
- const { Transaction: Transaction15 } = await import('@solana/web3.js');
1696
- const tx = new Transaction15();
1719
+ const { Transaction: Transaction12 } = await import('@solana/web3.js');
1720
+ const tx = new Transaction12();
1697
1721
  tx.recentBlockhash = blockhash;
1698
1722
  tx.feePayer = this.walletPubkey;
1699
1723
  tx.add(...instructions);
@@ -1719,10 +1743,10 @@ ${logs.join("\n")}`);
1719
1743
  connection.getAccountInfo(clobYesAta),
1720
1744
  connection.getAccountInfo(clobNoAta)
1721
1745
  ]);
1722
- const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction7 } = await import('@solana/spl-token');
1746
+ const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction5 } = await import('@solana/spl-token');
1723
1747
  const ixs = [];
1724
1748
  if (!yesInfo) {
1725
- ixs.push(createAssociatedTokenAccountIdempotentInstruction7(
1749
+ ixs.push(createAssociatedTokenAccountIdempotentInstruction5(
1726
1750
  this.walletPubkey,
1727
1751
  clobYesAta,
1728
1752
  clobConfig,
@@ -1731,7 +1755,7 @@ ${logs.join("\n")}`);
1731
1755
  ));
1732
1756
  }
1733
1757
  if (!noInfo) {
1734
- ixs.push(createAssociatedTokenAccountIdempotentInstruction7(
1758
+ ixs.push(createAssociatedTokenAccountIdempotentInstruction5(
1735
1759
  this.walletPubkey,
1736
1760
  clobNoAta,
1737
1761
  clobConfig,
@@ -3725,75 +3749,97 @@ var DisputeClient = class {
3725
3749
 
3726
3750
  // src/idls/oracle.json
3727
3751
  var oracle_default = {
3728
- address: "FzLnnbKtQCTKviEbrDYMLZYXNDHQdjSSa4Ybj4jAbrUL",
3752
+ address: "JE3iUvfv2DjhHnEqf2cFtzMtqn1AYQ5s32e1fzkL6KTo",
3729
3753
  metadata: {
3730
- name: "oracle",
3754
+ name: "oracle_v2",
3731
3755
  version: "0.1.0",
3732
3756
  spec: "0.1.0",
3733
3757
  description: "Oracle program for XMarket prediction platform"
3734
3758
  },
3735
3759
  instructions: [
3736
3760
  {
3737
- name: "add_to_whitelist",
3761
+ name: "add_reporter",
3738
3762
  docs: [
3739
- "Add an address to the oracle resolver whitelist (admin/owner only)"
3763
+ "Add a new reporter to the oracle whitelist"
3740
3764
  ],
3741
3765
  discriminator: [
3742
- 157,
3743
- 211,
3744
- 52,
3745
- 54,
3746
- 144,
3747
- 81,
3748
- 5,
3749
- 55
3766
+ 158,
3767
+ 153,
3768
+ 12,
3769
+ 6,
3770
+ 217,
3771
+ 41,
3772
+ 252,
3773
+ 157
3750
3774
  ],
3751
3775
  accounts: [
3752
3776
  {
3753
3777
  name: "authority",
3754
- signer: true
3755
- },
3756
- {
3757
- name: "payer",
3778
+ docs: [
3779
+ "Admin or owner who can add reporters"
3780
+ ],
3758
3781
  writable: true,
3759
3782
  signer: true
3760
3783
  },
3761
3784
  {
3762
3785
  name: "oracle_config",
3786
+ docs: [
3787
+ "The oracle configuration"
3788
+ ],
3789
+ writable: true
3790
+ },
3791
+ {
3792
+ name: "reporter_address",
3793
+ docs: [
3794
+ "The address being whitelisted as a reporter"
3795
+ ]
3796
+ },
3797
+ {
3798
+ name: "reporter",
3799
+ docs: [
3800
+ "The reporter account (PDA)"
3801
+ ],
3763
3802
  writable: true,
3764
3803
  pda: {
3765
3804
  seeds: [
3766
3805
  {
3767
3806
  kind: "const",
3768
3807
  value: [
3769
- 99,
3808
+ 114,
3809
+ 101,
3810
+ 112,
3770
3811
  111,
3771
- 110,
3772
- 102,
3773
- 105,
3774
- 103
3812
+ 114,
3813
+ 116,
3814
+ 101,
3815
+ 114
3775
3816
  ]
3776
3817
  },
3777
3818
  {
3778
3819
  kind: "account",
3779
- path: "oracle_config.owner",
3780
- account: "OracleConfig"
3820
+ path: "oracle_config"
3821
+ },
3822
+ {
3823
+ kind: "account",
3824
+ path: "reporter_address"
3781
3825
  }
3782
3826
  ]
3783
3827
  }
3784
- }
3785
- ],
3786
- args: [
3828
+ },
3787
3829
  {
3788
- name: "address",
3789
- type: "pubkey"
3830
+ name: "system_program",
3831
+ docs: [
3832
+ "System program for account creation"
3833
+ ],
3834
+ address: "11111111111111111111111111111111"
3790
3835
  }
3791
- ]
3836
+ ],
3837
+ args: []
3792
3838
  },
3793
3839
  {
3794
3840
  name: "initialize",
3795
3841
  docs: [
3796
- "Initialize a new Oracle configuration. Caller becomes owner and is auto-whitelisted."
3842
+ "Initialize a new Oracle configuration"
3797
3843
  ],
3798
3844
  discriminator: [
3799
3845
  175,
@@ -3849,7 +3895,7 @@ var oracle_default = {
3849
3895
  {
3850
3896
  name: "pause",
3851
3897
  docs: [
3852
- "Pause or unpause the oracle (admin/owner only)"
3898
+ "Pause or unpause the oracle"
3853
3899
  ],
3854
3900
  discriminator: [
3855
3901
  211,
@@ -3869,11 +3915,6 @@ var oracle_default = {
3869
3915
  ],
3870
3916
  signer: true
3871
3917
  },
3872
- {
3873
- name: "payer",
3874
- writable: true,
3875
- signer: true
3876
- },
3877
3918
  {
3878
3919
  name: "oracle_config",
3879
3920
  docs: [
@@ -3890,67 +3931,82 @@ var oracle_default = {
3890
3931
  ]
3891
3932
  },
3892
3933
  {
3893
- name: "remove_from_whitelist",
3934
+ name: "remove_reporter",
3894
3935
  docs: [
3895
- "Remove an address from the oracle resolver whitelist (admin/owner only)"
3936
+ "Remove (deactivate) a reporter from the oracle whitelist"
3896
3937
  ],
3897
3938
  discriminator: [
3898
- 7,
3899
- 144,
3900
- 216,
3901
- 239,
3902
- 243,
3903
- 236,
3904
- 193,
3905
- 235
3939
+ 165,
3940
+ 229,
3941
+ 140,
3942
+ 210,
3943
+ 40,
3944
+ 24,
3945
+ 27,
3946
+ 34
3906
3947
  ],
3907
3948
  accounts: [
3908
3949
  {
3909
3950
  name: "authority",
3951
+ docs: [
3952
+ "Admin or owner who can remove reporters"
3953
+ ],
3910
3954
  signer: true
3911
3955
  },
3912
3956
  {
3913
- name: "payer",
3914
- writable: true,
3915
- signer: true
3957
+ name: "oracle_config",
3958
+ docs: [
3959
+ "The oracle configuration"
3960
+ ],
3961
+ writable: true
3916
3962
  },
3917
3963
  {
3918
- name: "oracle_config",
3964
+ name: "reporter_address",
3965
+ docs: [
3966
+ "The address being removed as a reporter"
3967
+ ]
3968
+ },
3969
+ {
3970
+ name: "reporter",
3971
+ docs: [
3972
+ "The reporter account (PDA)"
3973
+ ],
3919
3974
  writable: true,
3920
3975
  pda: {
3921
3976
  seeds: [
3922
3977
  {
3923
3978
  kind: "const",
3924
3979
  value: [
3925
- 99,
3980
+ 114,
3981
+ 101,
3982
+ 112,
3926
3983
  111,
3927
- 110,
3928
- 102,
3929
- 105,
3930
- 103
3984
+ 114,
3985
+ 116,
3986
+ 101,
3987
+ 114
3931
3988
  ]
3932
3989
  },
3933
3990
  {
3934
3991
  kind: "account",
3935
- path: "oracle_config.owner",
3936
- account: "OracleConfig"
3992
+ path: "oracle_config"
3993
+ },
3994
+ {
3995
+ kind: "account",
3996
+ path: "reporter_address"
3937
3997
  }
3938
3998
  ]
3939
3999
  }
3940
4000
  }
3941
4001
  ],
3942
- args: [
3943
- {
3944
- name: "address",
3945
- type: "pubkey"
3946
- }
3947
- ]
4002
+ args: []
3948
4003
  },
3949
4004
  {
3950
4005
  name: "resolve_question",
3951
4006
  docs: [
3952
- "Resolve a question \u2014 whitelisted address only.",
3953
- "Stores QuestionResult PDA and CPIs to CTF.set_payout in one transaction."
4007
+ "Resolve a question \u2014 called by whitelisted reporter",
4008
+ "Creates a QuestionResult PDA with the payout vector.",
4009
+ "QuestionMarket reads this to finalize the condition."
3954
4010
  ],
3955
4011
  discriminator: [
3956
4012
  52,
@@ -3964,43 +4020,23 @@ var oracle_default = {
3964
4020
  ],
3965
4021
  accounts: [
3966
4022
  {
3967
- name: "reporter",
4023
+ name: "reporter_signer",
3968
4024
  docs: [
3969
- "Whitelisted address that submits the resolution"
4025
+ "The reporter resolving this question"
3970
4026
  ],
3971
4027
  signer: true
3972
4028
  },
3973
4029
  {
3974
4030
  name: "oracle_config",
3975
4031
  docs: [
3976
- "Oracle configuration"
4032
+ "The oracle configuration"
3977
4033
  ],
3978
- writable: true,
3979
- pda: {
3980
- seeds: [
3981
- {
3982
- kind: "const",
3983
- value: [
3984
- 99,
3985
- 111,
3986
- 110,
3987
- 102,
3988
- 105,
3989
- 103
3990
- ]
3991
- },
3992
- {
3993
- kind: "account",
3994
- path: "oracle_config.owner",
3995
- account: "OracleConfig"
3996
- }
3997
- ]
3998
- }
4034
+ writable: true
3999
4035
  },
4000
4036
  {
4001
- name: "question_result",
4037
+ name: "reporter",
4002
4038
  docs: [
4003
- "QuestionResult PDA \u2014 seeds: [result, oracle_config, question_id]"
4039
+ "The reporter account (must be whitelisted and active)"
4004
4040
  ],
4005
4041
  writable: true,
4006
4042
  pda: {
@@ -4010,10 +4046,12 @@ var oracle_default = {
4010
4046
  value: [
4011
4047
  114,
4012
4048
  101,
4013
- 115,
4014
- 117,
4015
- 108,
4016
- 116
4049
+ 112,
4050
+ 111,
4051
+ 114,
4052
+ 116,
4053
+ 101,
4054
+ 114
4017
4055
  ]
4018
4056
  },
4019
4057
  {
@@ -4021,16 +4059,16 @@ var oracle_default = {
4021
4059
  path: "oracle_config"
4022
4060
  },
4023
4061
  {
4024
- kind: "arg",
4025
- path: "question_id"
4062
+ kind: "account",
4063
+ path: "reporter_signer"
4026
4064
  }
4027
4065
  ]
4028
4066
  }
4029
4067
  },
4030
4068
  {
4031
- name: "condition",
4069
+ name: "question_result",
4032
4070
  docs: [
4033
- "Condition PDA in CTF \u2014 seeds: [condition, oracle_config, question_id] (CTF program)"
4071
+ "The question result account (PDA) - stores the resolution"
4034
4072
  ],
4035
4073
  writable: true,
4036
4074
  pda: {
@@ -4038,15 +4076,12 @@ var oracle_default = {
4038
4076
  {
4039
4077
  kind: "const",
4040
4078
  value: [
4041
- 99,
4042
- 111,
4043
- 110,
4044
- 100,
4045
- 105,
4046
- 116,
4047
- 105,
4048
- 111,
4049
- 110
4079
+ 114,
4080
+ 101,
4081
+ 115,
4082
+ 117,
4083
+ 108,
4084
+ 116
4050
4085
  ]
4051
4086
  },
4052
4087
  {
@@ -4057,29 +4092,22 @@ var oracle_default = {
4057
4092
  kind: "arg",
4058
4093
  path: "question_id"
4059
4094
  }
4060
- ],
4061
- program: {
4062
- kind: "account",
4063
- path: "conditional_tokens_program"
4064
- }
4095
+ ]
4065
4096
  }
4066
4097
  },
4067
- {
4068
- name: "conditional_tokens_program",
4069
- docs: [
4070
- "ConditionalTokens program"
4071
- ]
4072
- },
4073
4098
  {
4074
4099
  name: "payer",
4075
4100
  docs: [
4076
- "Payer for QuestionResult account creation"
4101
+ "Payer for account creation"
4077
4102
  ],
4078
4103
  writable: true,
4079
4104
  signer: true
4080
4105
  },
4081
4106
  {
4082
4107
  name: "system_program",
4108
+ docs: [
4109
+ "System program for account creation"
4110
+ ],
4083
4111
  address: "11111111111111111111111111111111"
4084
4112
  }
4085
4113
  ],
@@ -4146,7 +4174,7 @@ var oracle_default = {
4146
4174
  {
4147
4175
  name: "update_admin",
4148
4176
  docs: [
4149
- "Update the admin of the oracle (owner only)"
4177
+ "Update the admin of the oracle"
4150
4178
  ],
4151
4179
  discriminator: [
4152
4180
  161,
@@ -4166,11 +4194,6 @@ var oracle_default = {
4166
4194
  ],
4167
4195
  signer: true
4168
4196
  },
4169
- {
4170
- name: "payer",
4171
- writable: true,
4172
- signer: true
4173
- },
4174
4197
  {
4175
4198
  name: "oracle_config",
4176
4199
  docs: [
@@ -4213,72 +4236,81 @@ var oracle_default = {
4213
4236
  254,
4214
4237
  226
4215
4238
  ]
4239
+ },
4240
+ {
4241
+ name: "Reporter",
4242
+ discriminator: [
4243
+ 233,
4244
+ 37,
4245
+ 148,
4246
+ 250,
4247
+ 155,
4248
+ 158,
4249
+ 118,
4250
+ 161
4251
+ ]
4216
4252
  }
4217
4253
  ],
4218
- events: [
4219
- {
4220
- name: "WhitelistUpdated",
4221
- discriminator: [
4222
- 205,
4223
- 110,
4224
- 205,
4225
- 193,
4226
- 238,
4227
- 237,
4228
- 220,
4229
- 22
4230
- ]
4231
- }
4232
- ],
4233
- types: [
4254
+ types: [
4234
4255
  {
4235
4256
  name: "OracleConfig",
4257
+ docs: [
4258
+ "Global configuration for the Oracle program"
4259
+ ],
4236
4260
  type: {
4237
4261
  kind: "struct",
4238
4262
  fields: [
4239
- {
4240
- name: "version",
4241
- type: "u8"
4242
- },
4243
4263
  {
4244
4264
  name: "owner",
4265
+ docs: [
4266
+ "Owner of the oracle (can transfer ownership)"
4267
+ ],
4245
4268
  type: "pubkey"
4246
4269
  },
4247
4270
  {
4248
4271
  name: "admin",
4272
+ docs: [
4273
+ "Admin who can add/remove reporters"
4274
+ ],
4249
4275
  type: "pubkey"
4250
4276
  },
4251
4277
  {
4252
4278
  name: "question_count",
4279
+ docs: [
4280
+ "Total number of questions resolved through this oracle"
4281
+ ],
4253
4282
  type: "u64"
4254
4283
  },
4255
4284
  {
4256
- name: "whitelist",
4257
- type: {
4258
- array: [
4259
- "pubkey",
4260
- 10
4261
- ]
4262
- }
4263
- },
4264
- {
4265
- name: "whitelist_len",
4266
- type: "u8"
4285
+ name: "reporter_count",
4286
+ docs: [
4287
+ "Total number of active reporters"
4288
+ ],
4289
+ type: "u64"
4267
4290
  },
4268
4291
  {
4269
4292
  name: "is_paused",
4293
+ docs: [
4294
+ "Whether the oracle is paused"
4295
+ ],
4270
4296
  type: "bool"
4271
4297
  },
4272
4298
  {
4273
4299
  name: "bump",
4300
+ docs: [
4301
+ "Bump seed for PDA derivation"
4302
+ ],
4274
4303
  type: "u8"
4275
4304
  },
4276
4305
  {
4277
4306
  name: "_reserved",
4307
+ docs: [
4308
+ "Reserved space for future upgrades"
4309
+ ],
4278
4310
  type: {
4279
4311
  array: [
4280
4312
  "u8",
4281
- 128
4313
+ 64
4282
4314
  ]
4283
4315
  }
4284
4316
  }
@@ -4287,19 +4319,24 @@ var oracle_default = {
4287
4319
  },
4288
4320
  {
4289
4321
  name: "QuestionResult",
4322
+ docs: [
4323
+ "Stores the resolution result for a question"
4324
+ ],
4290
4325
  type: {
4291
4326
  kind: "struct",
4292
4327
  fields: [
4293
- {
4294
- name: "version",
4295
- type: "u8"
4296
- },
4297
4328
  {
4298
4329
  name: "oracle_config",
4330
+ docs: [
4331
+ "The oracle config that resolved this question"
4332
+ ],
4299
4333
  type: "pubkey"
4300
4334
  },
4301
4335
  {
4302
4336
  name: "question_id",
4337
+ docs: [
4338
+ "Unique identifier for the question (32-byte hash)"
4339
+ ],
4303
4340
  type: {
4304
4341
  array: [
4305
4342
  "u8",
@@ -4309,78 +4346,125 @@ var oracle_default = {
4309
4346
  },
4310
4347
  {
4311
4348
  name: "outcome_index",
4349
+ docs: [
4350
+ "Which outcome won (for binary: 0=NO, 1=YES)"
4351
+ ],
4312
4352
  type: "u8"
4313
4353
  },
4314
4354
  {
4315
4355
  name: "outcome_count",
4356
+ docs: [
4357
+ "Number of outcomes in this question"
4358
+ ],
4316
4359
  type: "u8"
4317
4360
  },
4318
4361
  {
4319
4362
  name: "payout_numerators",
4320
4363
  docs: [
4321
- "Fixed-size payout array. Only [0..outcome_count] slots are meaningful."
4364
+ "Full payout vector representing the payout ratios",
4365
+ "For binary YES win: [0, 1], for NO win: [1, 0]",
4366
+ "For multi-outcome: [0, 0, 1, 0] means outcome 2 won"
4322
4367
  ],
4323
4368
  type: {
4324
- array: [
4325
- "u64",
4326
- 8
4327
- ]
4369
+ vec: "u64"
4328
4370
  }
4329
4371
  },
4330
4372
  {
4331
4373
  name: "is_resolved",
4374
+ docs: [
4375
+ "Whether the question has been resolved"
4376
+ ],
4332
4377
  type: "bool"
4333
4378
  },
4334
4379
  {
4335
4380
  name: "resolved_at",
4381
+ docs: [
4382
+ "Timestamp when the question was resolved"
4383
+ ],
4336
4384
  type: "i64"
4337
4385
  },
4338
4386
  {
4339
4387
  name: "reporter",
4388
+ docs: [
4389
+ "The reporter who resolved this question"
4390
+ ],
4340
4391
  type: "pubkey"
4341
4392
  },
4342
4393
  {
4343
4394
  name: "condition",
4395
+ docs: [
4396
+ "Optional link to ConditionalTokens condition (if resolved via CPI)"
4397
+ ],
4344
4398
  type: {
4345
4399
  option: "pubkey"
4346
4400
  }
4347
4401
  },
4348
4402
  {
4349
4403
  name: "bump",
4404
+ docs: [
4405
+ "Bump seed for PDA derivation"
4406
+ ],
4350
4407
  type: "u8"
4351
- },
4352
- {
4353
- name: "_reserved",
4354
- type: {
4355
- array: [
4356
- "u8",
4357
- 64
4358
- ]
4359
- }
4360
4408
  }
4361
4409
  ]
4362
4410
  }
4363
4411
  },
4364
4412
  {
4365
- name: "WhitelistUpdated",
4413
+ name: "Reporter",
4414
+ docs: [
4415
+ "Represents a whitelisted reporter who can resolve questions"
4416
+ ],
4366
4417
  type: {
4367
4418
  kind: "struct",
4368
4419
  fields: [
4369
4420
  {
4370
4421
  name: "oracle_config",
4422
+ docs: [
4423
+ "The oracle config this reporter belongs to"
4424
+ ],
4371
4425
  type: "pubkey"
4372
4426
  },
4373
4427
  {
4374
- name: "address",
4428
+ name: "reporter",
4429
+ docs: [
4430
+ "The reporter's public key"
4431
+ ],
4375
4432
  type: "pubkey"
4376
4433
  },
4377
4434
  {
4378
- name: "added",
4435
+ name: "is_active",
4436
+ docs: [
4437
+ "Whether the reporter is currently active"
4438
+ ],
4379
4439
  type: "bool"
4380
4440
  },
4381
4441
  {
4382
- name: "updated_by",
4383
- type: "pubkey"
4442
+ name: "questions_reported",
4443
+ docs: [
4444
+ "Number of questions this reporter has resolved"
4445
+ ],
4446
+ type: "u64"
4447
+ },
4448
+ {
4449
+ name: "added_at",
4450
+ docs: [
4451
+ "Timestamp when the reporter was added"
4452
+ ],
4453
+ type: "i64"
4454
+ },
4455
+ {
4456
+ name: "last_updated",
4457
+ docs: [
4458
+ "Timestamp when the reporter was last updated"
4459
+ ],
4460
+ type: "i64"
4461
+ },
4462
+ {
4463
+ name: "bump",
4464
+ docs: [
4465
+ "Bump seed for PDA derivation"
4466
+ ],
4467
+ type: "u8"
4384
4468
  }
4385
4469
  ]
4386
4470
  }
@@ -4388,8 +4472,8 @@ var oracle_default = {
4388
4472
  ]
4389
4473
  };
4390
4474
 
4391
- // src/idls/question_market_v2.json
4392
- var question_market_v2_default = {
4475
+ // src/idls/question_market.json
4476
+ var question_market_default = {
4393
4477
  address: "6aYGVgSPNgFxNB9f25UvKrzMs91cGh7urNZKdtnMUm6k",
4394
4478
  metadata: {
4395
4479
  name: "question_market_v2",
@@ -4724,19 +4808,19 @@ var question_market_v2_default = {
4724
4808
  },
4725
4809
  {
4726
4810
  name: "fee_management_program",
4727
- address: "DuYyXguB5PVSKg6E2p4XPrrXZSCJnuBhoGpkGCBN5bBb"
4811
+ address: "HZ5ec3NbwceUfyzZkwGEzDxQHpr4p4L9JV1gmcPDyXYw"
4728
4812
  },
4729
4813
  {
4730
4814
  name: "presale_program",
4731
- address: "2Rnw1VoEtsUMQ7wkvYZjDehqSqRob6uNkeymDfvKrquB"
4815
+ address: "H9GzTEu5giM1k9HJm126Pp6C6b3iKWahbF7RJj8Jr3zJ"
4732
4816
  },
4733
4817
  {
4734
4818
  name: "market_oracle_program",
4735
- address: "ADWF4J3nCJ2kWnCtycuem2jhu7amUqJWQG3oa5xF67QJ"
4819
+ address: "4o7VyLGV79AynuTjTxDvsUfZKS11bX5SkSsk5zHKVovG"
4736
4820
  },
4737
4821
  {
4738
4822
  name: "admin_program",
4739
- address: "4NdD5962SfGqofmeyjfifJpdGnwTAiKaUKB5Z42UDc9T"
4823
+ address: "3E2b2H3ZDMXjqQ1VKDz8zsVXWdRaH5Mjdd7qFB13Uejn"
4740
4824
  },
4741
4825
  {
4742
4826
  name: "token_program",
@@ -4998,11 +5082,11 @@ var question_market_v2_default = {
4998
5082
  },
4999
5083
  {
5000
5084
  name: "presale_program",
5001
- address: "2Rnw1VoEtsUMQ7wkvYZjDehqSqRob6uNkeymDfvKrquB"
5085
+ address: "H9GzTEu5giM1k9HJm126Pp6C6b3iKWahbF7RJj8Jr3zJ"
5002
5086
  },
5003
5087
  {
5004
5088
  name: "admin_program",
5005
- address: "4NdD5962SfGqofmeyjfifJpdGnwTAiKaUKB5Z42UDc9T"
5089
+ address: "3E2b2H3ZDMXjqQ1VKDz8zsVXWdRaH5Mjdd7qFB13Uejn"
5006
5090
  },
5007
5091
  {
5008
5092
  name: "token_program",
@@ -5084,7 +5168,7 @@ var question_market_v2_default = {
5084
5168
  },
5085
5169
  {
5086
5170
  name: "market_oracle_program",
5087
- address: "ADWF4J3nCJ2kWnCtycuem2jhu7amUqJWQG3oa5xF67QJ"
5171
+ address: "4o7VyLGV79AynuTjTxDvsUfZKS11bX5SkSsk5zHKVovG"
5088
5172
  }
5089
5173
  ],
5090
5174
  args: []
@@ -5312,7 +5396,7 @@ var question_market_v2_default = {
5312
5396
  },
5313
5397
  {
5314
5398
  name: "presale_program",
5315
- address: "2Rnw1VoEtsUMQ7wkvYZjDehqSqRob6uNkeymDfvKrquB"
5399
+ address: "H9GzTEu5giM1k9HJm126Pp6C6b3iKWahbF7RJj8Jr3zJ"
5316
5400
  },
5317
5401
  {
5318
5402
  name: "token_program",
@@ -5448,7 +5532,7 @@ var question_market_v2_default = {
5448
5532
  },
5449
5533
  {
5450
5534
  name: "fee_management_program",
5451
- address: "DuYyXguB5PVSKg6E2p4XPrrXZSCJnuBhoGpkGCBN5bBb"
5535
+ address: "HZ5ec3NbwceUfyzZkwGEzDxQHpr4p4L9JV1gmcPDyXYw"
5452
5536
  },
5453
5537
  {
5454
5538
  name: "token_program",
@@ -5641,7 +5725,7 @@ var question_market_v2_default = {
5641
5725
  },
5642
5726
  {
5643
5727
  name: "presale_program",
5644
- address: "2Rnw1VoEtsUMQ7wkvYZjDehqSqRob6uNkeymDfvKrquB"
5728
+ address: "H9GzTEu5giM1k9HJm126Pp6C6b3iKWahbF7RJj8Jr3zJ"
5645
5729
  }
5646
5730
  ],
5647
5731
  args: []
@@ -7247,8 +7331,8 @@ var question_market_v2_default = {
7247
7331
  ]
7248
7332
  };
7249
7333
 
7250
- // src/idls/conditional_tokens_v2.json
7251
- var conditional_tokens_v2_default = {
7334
+ // src/idls/conditional_tokens.json
7335
+ var conditional_tokens_default = {
7252
7336
  address: "3f4vBEgXo9y4Ndrn8KWnNpLLtt4VAGkmKWqXpGS1BLDB",
7253
7337
  metadata: {
7254
7338
  name: "conditional_tokens_v2",
@@ -8950,8 +9034,8 @@ var conditional_tokens_v2_default = {
8950
9034
  ]
8951
9035
  };
8952
9036
 
8953
- // src/idls/clob_exchange_v2.json
8954
- var clob_exchange_v2_default = {
9037
+ // src/idls/clob_exchange.json
9038
+ var clob_exchange_default = {
8955
9039
  address: "43ubx86ZnGJ4bzD8uZY91uoUzJdK2jULwSSVk8zHjzEY",
8956
9040
  metadata: {
8957
9041
  name: "clob_exchange_v2",
@@ -17783,8 +17867,8 @@ var dispute_default = {
17783
17867
  ]
17784
17868
  };
17785
17869
 
17786
- // src/sdk-v2.ts
17787
- var XMarketSDKV2 = class {
17870
+ // src/sdk.ts
17871
+ var XMarketSDK = class {
17788
17872
  constructor(config, wallet, marketOwner) {
17789
17873
  this.networkConfig = config;
17790
17874
  this.provider = new anchor6.AnchorProvider(
@@ -17806,17 +17890,10 @@ var XMarketSDKV2 = class {
17806
17890
  }
17807
17891
  return this._oracle;
17808
17892
  }
17809
- /**
17810
- * V2 does not use a hook program — calling this getter throws an error.
17811
- * The hook is not needed for plain SPL YES/NO mints.
17812
- */
17813
- get hook() {
17814
- throw new Error("XMarketSDKV2: no hook client \u2014 V2 uses plain SPL token (TOKEN_PROGRAM_ID), no transfer hook.");
17815
- }
17816
17893
  get market() {
17817
17894
  if (!this._market) {
17818
- const program = new anchor6.Program(this._withAddress(question_market_v2_default, this._programIds.questionMarket), this.provider);
17819
- this._market = new MarketClientV2(program, this.provider, this._programIds, this._marketOwner);
17895
+ const program = new anchor6.Program(this._withAddress(question_market_default, this._programIds.questionMarket), this.provider);
17896
+ this._market = new MarketClient(program, this.provider, this._programIds, this._marketOwner);
17820
17897
  this._market.ctfClient = this.ctf;
17821
17898
  this._market.feeConfigOwner = this.networkConfig.feeConfigOwner ?? this._marketOwner;
17822
17899
  }
@@ -17824,15 +17901,15 @@ var XMarketSDKV2 = class {
17824
17901
  }
17825
17902
  get ctf() {
17826
17903
  if (!this._ctf) {
17827
- const program = new anchor6.Program(this._withAddress(conditional_tokens_v2_default, this._programIds.conditionalTokens), this.provider);
17828
- this._ctf = new CtfClientV2(program, this.provider, this._programIds);
17904
+ const program = new anchor6.Program(this._withAddress(conditional_tokens_default, this._programIds.conditionalTokens), this.provider);
17905
+ this._ctf = new CtfClient(program, this.provider, this._programIds);
17829
17906
  }
17830
17907
  return this._ctf;
17831
17908
  }
17832
17909
  get clob() {
17833
17910
  if (!this._clob) {
17834
- const program = new anchor6.Program(this._withAddress(clob_exchange_v2_default, this._programIds.clobExchange), this.provider);
17835
- this._clob = new ClobClientV2(program, this.provider, this._programIds, this.networkConfig);
17911
+ const program = new anchor6.Program(this._withAddress(clob_exchange_default, this._programIds.clobExchange), this.provider);
17912
+ this._clob = new ClobClient(program, this.provider, this._programIds, this.networkConfig);
17836
17913
  if (this.networkConfig.feeConfigOwner && this._programIds.feeManagement) {
17837
17914
  this._clob.feeConfigOwner = this.networkConfig.feeConfigOwner;
17838
17915
  this._clob.feeClient = this.fee;
@@ -17891,2521 +17968,27 @@ var XMarketSDKV2 = class {
17891
17968
  return this._dispute;
17892
17969
  }
17893
17970
  };
17894
- var HookClient = class {
17895
- constructor(program, provider, programIds) {
17896
- this.program = program;
17897
- this.provider = provider;
17898
- this.programIds = programIds;
17899
- }
17900
- get walletPubkey() {
17901
- return this.provider.wallet.publicKey;
17902
- }
17903
- configPda() {
17904
- return PDA.hookConfig(this.programIds)[0];
17905
- }
17906
- // ─── Instructions ────────────────────────────────────────────────────────────
17907
- /** One-time setup. Caller becomes owner. */
17908
- async initialize(initialWhitelist) {
17909
- const sig = await this.program.methods.initializeHookConfig(initialWhitelist).accounts({
17910
- owner: this.walletPubkey,
17911
- hookConfig: this.configPda(),
17912
- systemProgram: SystemProgram.programId
17913
- }).rpc();
17914
- return { signature: sig };
17915
- }
17916
- /**
17917
- * Returns initializeExtraAccountMetaList instruction if the account doesn't exist yet, else null.
17918
- * Used by ClobClient to auto-prepend when extraAccountMetaList is missing.
17919
- */
17920
- async buildInitHookIxIfNeeded(mint, payer) {
17921
- const [extraAccountMetaList] = PDA.extraAccountMetaList(mint, this.programIds);
17922
- const existing = await this.provider.connection.getAccountInfo(extraAccountMetaList);
17923
- if (existing) return null;
17924
- return this.program.methods.initializeExtraAccountMetaList().accounts({
17925
- payer,
17926
- extraAccountMetaList,
17927
- mint,
17928
- hookConfig: this.configPda(),
17929
- tokenProgram: TOKEN_2022_PROGRAM_ID,
17930
- systemProgram: SystemProgram.programId
17931
- }).instruction();
17932
- }
17933
- /**
17934
- * Register extra account metas for a Token-2022 YES/NO mint.
17935
- * Must be called once per mint after CTF creates it.
17936
- */
17937
- async initializeExtraAccountMetaList(mint) {
17938
- const [extraAccountMetaList] = PDA.extraAccountMetaList(mint, this.programIds);
17939
- const sig = await this.program.methods.initializeExtraAccountMetaList().accounts({
17940
- payer: this.walletPubkey,
17941
- extraAccountMetaList,
17942
- mint,
17943
- hookConfig: this.configPda(),
17944
- tokenProgram: TOKEN_2022_PROGRAM_ID,
17945
- systemProgram: SystemProgram.programId
17946
- }).rpc();
17947
- return { signature: sig };
17948
- }
17949
- /** Owner adds a program to the transfer whitelist. */
17950
- async addToWhitelist(program) {
17951
- const sig = await this.program.methods.addToWhitelist(program).accounts({
17952
- owner: this.walletPubkey,
17953
- hookConfig: this.configPda()
17954
- }).rpc();
17955
- return { signature: sig };
17956
- }
17957
- /** Owner removes a program from the transfer whitelist. */
17958
- async removeFromWhitelist(program) {
17959
- const sig = await this.program.methods.removeFromWhitelist(program).accounts({
17960
- owner: this.walletPubkey,
17961
- hookConfig: this.configPda()
17962
- }).rpc();
17963
- return { signature: sig };
17964
- }
17965
- /** Permanently freeze the whitelist — no further changes allowed. */
17966
- async freezeWhitelist() {
17967
- const sig = await this.program.methods.freezeWhitelist().accounts({
17968
- owner: this.walletPubkey,
17969
- hookConfig: this.configPda()
17970
- }).rpc();
17971
- return { signature: sig };
17972
- }
17973
- /**
17974
- * SPL Transfer-Hook `execute` — invoked automatically by Token-2022 on every
17975
- * YES/NO token transfer. Validates the destination against the whitelist.
17976
- *
17977
- * Calling this directly is useful for:
17978
- * - Off-chain simulation ("would this transfer be allowed?")
17979
- * - Integration tests that verify whitelist enforcement
17980
- *
17981
- * Token-2022 calls this automatically; you normally don't call it manually.
17982
- *
17983
- * @param sourceToken - Source token account (tokens leaving)
17984
- * @param mint - The YES/NO Token-2022 mint
17985
- * @param destinationToken - Destination token account (tokens arriving)
17986
- * @param owner - Authority that authorized the transfer
17987
- * @param amount - Token amount being transferred
17988
- */
17989
- async execute(sourceToken, mint, destinationToken, owner, amount) {
17990
- const [extraAccountMetaList] = PDA.extraAccountMetaList(mint, this.programIds);
17991
- const sig = await this.program.methods.execute(amount).accounts({
17992
- sourceToken,
17993
- mint,
17994
- destinationToken,
17995
- owner,
17996
- extraAccountMetaList,
17997
- hookConfig: this.configPda()
17998
- }).rpc();
17999
- return { signature: sig };
18000
- }
18001
- // ─── Queries ─────────────────────────────────────────────────────────────────
18002
- async fetchConfig() {
18003
- try {
18004
- const acc = await this.program.account.hookConfig.fetch(this.configPda());
18005
- return {
18006
- owner: acc.owner,
18007
- whitelist: acc.whitelist.slice(0, acc.whitelistLen),
18008
- whitelistLen: acc.whitelistLen,
18009
- isFrozen: acc.isFrozen,
18010
- bump: acc.bump
18011
- };
18012
- } catch {
18013
- return null;
18014
- }
18015
- }
18016
- async isWhitelisted(program) {
18017
- const config = await this.fetchConfig();
18018
- if (!config) return false;
18019
- return config.whitelist.some((p) => p.equals(program));
18020
- }
18021
- };
18022
- var TOKEN_METADATA_PROGRAM_ID2 = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
18023
- var MarketClient = class {
18024
- constructor(program, provider, programIds, ownerPubkey) {
18025
- this.program = program;
18026
- this.provider = provider;
18027
- this.programIds = programIds;
18028
- this.configPda = PDA.questionMarketConfig(ownerPubkey, programIds)[0];
18029
- }
18030
- get walletPubkey() {
18031
- return this.provider.wallet.publicKey;
18032
- }
18033
- // ─── Instructions (return Transaction — caller signs + sends) ───────────────
18034
- async initialize(admin, oracle, owner = this.walletPubkey) {
18035
- return this.program.methods.initialize({
18036
- admin,
18037
- conditionalTokensProgram: this.programIds.conditionalTokens,
18038
- oracle
18039
- }).accounts({
18040
- owner,
18041
- config: this.configPda,
18042
- systemProgram: SystemProgram.programId
18043
- }).transaction();
18044
- }
18045
- /**
18046
- * Build createQuestionAdmin transaction (whitelist/admin path — status = Approved immediately).
18047
- * @param creator - Whitelisted creator (must be in whitelist or be admin/owner)
18048
- * @param payer - Fee payer (pays rent; can differ from creator)
18049
- */
18050
- async createQuestionAdmin(params, oracle, creator = this.walletPubkey, payer = this.walletPubkey) {
18051
- const questionId = params.questionId ?? generateQuestionId(params.content);
18052
- const contentHash = params.contentHash ?? generateContentHash(params.content);
18053
- const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
18054
- const [conditionPda] = PDA.condition(oracle, questionId, this.programIds);
18055
- const [yesMint] = PDA.yesMint(conditionPda, this.programIds);
18056
- const [noMint] = PDA.noMint(conditionPda, this.programIds);
18057
- const [mintAuthority] = PDA.mintAuthority(conditionPda, this.programIds);
18058
- const [collateralVault] = PDA.collateralVault(params.collateralMint, this.programIds);
18059
- if (!this.programIds.feeManagement) throw new Error("feeManagement program ID not configured");
18060
- if (!this.feeConfigOwner) throw new Error("feeConfigOwner not configured");
18061
- const [questionFeePda] = PDA.questionFee(conditionPda, this.programIds);
18062
- const [feeConfigPda] = PDA.feeConfig(this.feeConfigOwner, this.programIds);
18063
- const [marketFeeOverride] = PDA.marketFeeOverride(conditionPda, this.programIds);
18064
- const tx = await this.program.methods.createQuestionAdmin({
18065
- questionId: Array.from(questionId),
18066
- contentHash: Array.from(contentHash),
18067
- hookProgram: params.hookProgram,
18068
- authorizedClob: params.authorizedClob,
18069
- expirationTime: new anchor6.BN(params.expirationTime)
18070
- }).accounts({
18071
- creator,
17971
+ var MAX_APPROVE_AMOUNT = new BN4("18446744073709551615");
17972
+ function buildCreateUserAtasTx(condition, user, payer, programIds) {
17973
+ const [yesMint] = PDA.yesMint(condition, programIds);
17974
+ const [noMint] = PDA.noMint(condition, programIds);
17975
+ const yesAta = getAssociatedTokenAddressSync(yesMint, user, false, TOKEN_PROGRAM_ID);
17976
+ const noAta = getAssociatedTokenAddressSync(noMint, user, false, TOKEN_PROGRAM_ID);
17977
+ const tx = new Transaction();
17978
+ tx.feePayer = payer;
17979
+ tx.add(
17980
+ createAssociatedTokenAccountIdempotentInstruction(
18072
17981
  payer,
18073
- config: this.configPda,
18074
- question: questionPda,
18075
- currencyMint: params.collateralMint,
18076
- oracle,
18077
- condition: conditionPda,
17982
+ yesAta,
17983
+ user,
18078
17984
  yesMint,
18079
- noMint,
18080
- mintAuthority,
18081
- collateralVault,
18082
- conditionalTokensProgram: this.programIds.conditionalTokens,
18083
- questionFee: questionFeePda,
18084
- feeConfig: feeConfigPda,
18085
- marketFeeOverride,
18086
- feeManagementProgram: this.programIds.feeManagement,
18087
- tokenProgram: TOKEN_2022_PROGRAM_ID,
18088
- systemProgram: SystemProgram.programId,
18089
- rent: SYSVAR_RENT_PUBKEY
18090
- }).transaction();
18091
- return { tx, questionPda, conditionPda, questionId };
18092
- }
18093
- async approveQuestion(questionPda, admin = this.walletPubkey, payer = admin) {
18094
- return this.program.methods.approveQuestion().accounts({
18095
- admin,
18096
- payer,
18097
- config: this.configPda,
18098
- question: questionPda
18099
- }).transaction();
18100
- }
18101
- async updateConfig(params, authority = this.walletPubkey, payer = authority) {
18102
- return this.program.methods.updateConfig({
18103
- newAdmin: params.newAdmin ?? null,
18104
- newOracle: params.newOracle ?? null,
18105
- isPaused: params.isPaused ?? null,
18106
- newConditionalTokensProgram: params.newConditionalTokensProgram ?? null
18107
- }).accounts({
18108
- authority,
18109
- payer,
18110
- config: this.configPda
18111
- }).transaction();
18112
- }
18113
- /** Set the admin (owner only). Replaces any existing admin. */
18114
- async addAdmin(newAdmin, owner = this.walletPubkey) {
18115
- return this.program.methods.addAdmin(newAdmin).accounts({
18116
- owner,
18117
- config: this.configPda
18118
- }).transaction();
18119
- }
18120
- /** Clear the admin (owner only). Sets admin to default pubkey. */
18121
- async removeAdmin(owner = this.walletPubkey) {
18122
- return this.program.methods.removeAdmin().accounts({
18123
- owner,
18124
- config: this.configPda
18125
- }).transaction();
18126
- }
18127
- async addToWhitelist(address, authority = this.walletPubkey, payer = authority) {
18128
- return this.program.methods.addToWhitelist(address).accounts({
18129
- authority,
18130
- payer,
18131
- config: this.configPda
18132
- }).transaction();
18133
- }
18134
- async removeFromWhitelist(address, authority = this.walletPubkey, payer = authority) {
18135
- return this.program.methods.removeFromWhitelist(address).accounts({
18136
- authority,
18137
- payer,
18138
- config: this.configPda
18139
- }).transaction();
18140
- }
18141
- async growConfig(owner = this.walletPubkey, payer = owner) {
18142
- return this.program.methods.growConfig().accounts({
18143
- payer,
18144
- owner,
18145
- config: this.configPda,
18146
- systemProgram: SystemProgram.programId
18147
- }).transaction();
18148
- }
18149
- async restoreConfig(snapshot, owner = this.walletPubkey, payer = owner) {
18150
- return this.program.methods.restoreConfig(Array.from(snapshot)).accounts({
18151
- payer,
18152
- owner,
18153
- config: this.configPda,
18154
- systemProgram: SystemProgram.programId
18155
- }).transaction();
18156
- }
18157
- async bumpPresaleCount(count, authority) {
18158
- return this.program.methods.bumpPresaleCount(new anchor6.BN(count)).accounts({ authority, config: this.configPda }).transaction();
18159
- }
18160
- // ─── Queries ─────────────────────────────────────────────────────────────────
18161
- async fetchConfig() {
18162
- try {
18163
- const acc = await this.program.account.questionMarketConfig.fetch(this.configPda);
18164
- return {
18165
- owner: acc.owner,
18166
- admin: acc.admin,
18167
- oracle: acc.oracle,
18168
- conditionalTokensProgram: acc.conditionalTokensProgram,
18169
- questionCount: acc.questionCount.toNumber(),
18170
- approvedCount: acc.approvedCount.toNumber(),
18171
- rejectedCount: acc.rejectedCount.toNumber(),
18172
- presaleCount: acc.presaleCount.toNumber(),
18173
- whitelist: acc.whitelist.slice(0, acc.whitelistLen),
18174
- whitelistLen: acc.whitelistLen,
18175
- isPaused: acc.isPaused,
18176
- bump: acc.bump
18177
- };
18178
- } catch {
18179
- return null;
18180
- }
18181
- }
18182
- async fetchQuestion(questionPda) {
18183
- try {
18184
- const acc = await this.program.account.question.fetch(questionPda);
18185
- return {
18186
- config: acc.config,
18187
- questionId: new Uint8Array(acc.questionId),
18188
- contentHash: new Uint8Array(acc.contentHash),
18189
- expirationTime: acc.expirationTime.toNumber(),
18190
- currencyMint: acc.currencyMint,
18191
- creator: acc.creator,
18192
- condition: acc.condition ?? null,
18193
- status: this._parseStatus(acc.status),
18194
- createdAt: acc.createdAt.toNumber(),
18195
- approvedAt: acc.approvedAt.toNumber(),
18196
- resolvedAt: acc.resolvedAt.toNumber(),
18197
- bump: acc.bump
18198
- };
18199
- } catch {
18200
- return null;
18201
- }
18202
- }
18203
- questionPda(questionId) {
18204
- return PDA.question(this.configPda, questionId, this.programIds)[0];
18205
- }
18206
- _parseStatus(raw) {
18207
- if (raw.pending !== void 0) return "pending" /* Pending */;
18208
- if (raw.approved !== void 0) return "approved" /* Approved */;
18209
- if (raw.rejected !== void 0) return "rejected" /* Rejected */;
18210
- if (raw.resolved !== void 0) return "resolved" /* Resolved */;
18211
- return "pending" /* Pending */;
18212
- }
18213
- /**
18214
- * Convenience: fetch YES and NO token balances for a question.
18215
- * Returns null for each if question not approved or position not initialized.
18216
- * Requires ctfClient to be injected (done automatically by XMarketSDK).
18217
- */
18218
- async fetchQuestionBalances(questionPda, owner) {
18219
- if (!this.ctfClient) return { yes: null, no: null };
18220
- const q = await this.fetchQuestion(questionPda);
18221
- if (!q || !q.condition) return { yes: null, no: null };
18222
- return this.ctfClient.fetchBothPositions(q.condition, owner);
18223
- }
18224
- // ─── Presale instructions ────────────────────────────────────────────────────
18225
- /**
18226
- * Any user creates a presale + initial buy (question-market::create_presale).
18227
- * Reads agents_rev / company_rev from fee_config.
18228
- *
18229
- * @param feeConfig fee_management fee_config PDA (owner = feeConfigOwner)
18230
- * @param currencyMint collateral token (e.g. USDC)
18231
- * @param presaleIndex config.presale_count — fetch config first or pass 0 for first presale
18232
- */
18233
- async createPresale(params, feeConfig, currencyMint, presaleIndex, creator = this.walletPubkey, payer = creator) {
18234
- if (!this.programIds.presale) throw new Error("presale program ID not configured");
18235
- const [presalePda] = PDA.presale(this.configPda, presaleIndex, this.programIds);
18236
- const [qtMint] = PDA.qtMint(presalePda, this.programIds);
18237
- const [qtAuthority] = PDA.qtAuthority(presalePda, this.programIds);
18238
- const [userBuyRecord] = PDA.userBuyRecord(presalePda, creator, this.programIds);
18239
- const presaleVault = getAssociatedTokenAddressSync(currencyMint, presalePda, true);
18240
- const creatorQtAta = getAssociatedTokenAddressSync(qtMint, creator);
18241
- const creatorCurrencyAta = getAssociatedTokenAddressSync(currencyMint, creator);
18242
- const [qtMetadata] = PublicKey.findProgramAddressSync(
18243
- [Buffer.from("metadata"), TOKEN_METADATA_PROGRAM_ID2.toBuffer(), qtMint.toBuffer()],
18244
- TOKEN_METADATA_PROGRAM_ID2
18245
- );
18246
- const tx = await this.program.methods.createPresale({
18247
- price: params.price,
18248
- startTime: params.startTime,
18249
- endTime: params.endTime,
18250
- initialBuyAmount: params.initialBuyAmount
18251
- }).accounts({
18252
- creator,
18253
- payer,
18254
- config: this.configPda,
18255
- feeConfig,
18256
- presale: presalePda,
18257
- qtMint,
18258
- qtAuthority,
18259
- currencyMint,
18260
- presaleVault,
18261
- creatorCurrencyAta,
18262
- creatorQtAta,
18263
- userBuyRecord,
18264
- qtMetadata,
18265
- tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID2,
18266
- presaleProgram: this.programIds.presale,
18267
- tokenProgram: TOKEN_PROGRAM_ID,
18268
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
18269
- systemProgram: SystemProgram.programId,
18270
- rent: SYSVAR_RENT_PUBKEY
18271
- }).transaction();
18272
- tx.instructions.unshift(
18273
- ComputeBudgetProgram.setComputeUnitLimit({ units: 4e5 })
18274
- );
18275
- return { tx, presalePda, qtMint };
18276
- }
18277
- /**
18278
- * Whitelist-only: approve presale → creates question + CTF condition + market_oracle in one tx.
18279
- *
18280
- * @param presalePda the presale account
18281
- * @param contentHash 32-byte hash for question content
18282
- * @param hookProgram token-2022 transfer hook program
18283
- * @param authorizedClob clob program allowed to do CTF transfers
18284
- * @param expirationTime Unix seconds
18285
- * @param creator presale creator pubkey (stored in question)
18286
- * @param currencyMint collateral mint
18287
- */
18288
- async approvePresale(params) {
18289
- const {
18290
- presalePda,
18291
- contentHash,
18292
- hookProgram,
18293
- authorizedClob,
18294
- expirationTime,
18295
- creator,
18296
- currencyMint,
18297
- referralAddress,
18298
- companyAddress,
18299
- adminOwner
18300
- } = params;
18301
- const caller = params.caller ?? this.walletPubkey;
18302
- const payer = params.payer ?? caller;
18303
- if (!this.programIds.presale) throw new Error("presale program ID not configured");
18304
- if (!this.programIds.marketOracle) throw new Error("marketOracle program ID not configured");
18305
- if (!this.programIds.feeManagement) throw new Error("feeManagement program ID not configured");
18306
- if (!this.programIds.adminContract) throw new Error("adminContract program ID not configured");
18307
- const questionId = presalePda.toBytes();
18308
- const [questionPda] = PDA.question(this.configPda, questionId, this.programIds);
18309
- const marketConfig = await this.fetchConfig();
18310
- if (!marketConfig) throw new Error("QuestionMarketConfig not found");
18311
- const oraclePubkey = marketConfig.oracle;
18312
- const [conditionPda] = PDA.condition(oraclePubkey, questionId, this.programIds);
18313
- const [yesMint] = PDA.yesMint(conditionPda, this.programIds);
18314
- const [noMint] = PDA.noMint(conditionPda, this.programIds);
18315
- const [mintAuthority] = PDA.mintAuthority(conditionPda, this.programIds);
18316
- const [collateralVault] = PDA.collateralVault(currencyMint, this.programIds);
18317
- const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
18318
- const marketOracleVault = getAssociatedTokenAddressSync(currencyMint, marketOraclePda, true);
18319
- const [questionFeePda] = PDA.questionFee(conditionPda, this.programIds);
18320
- const presaleVault = getAssociatedTokenAddressSync(currencyMint, presalePda, true);
18321
- const referralTokenAccount = getAssociatedTokenAddressSync(currencyMint, referralAddress);
18322
- const companyTokenAccount = getAssociatedTokenAddressSync(currencyMint, companyAddress);
18323
- const [adminConfig] = PDA.adminConfig(adminOwner, this.programIds);
18324
- const adminVault = getAssociatedTokenAddressSync(currencyMint, adminConfig, true);
18325
- const [claimRecord] = PDA.claimRecord(conditionPda.toBytes(), this.programIds);
18326
- const feeConfigOwner = this.feeConfigOwner ?? adminOwner;
18327
- const [feeConfigPda] = PDA.feeConfig(feeConfigOwner, this.programIds);
18328
- const [marketFeeOverride] = PDA.marketFeeOverride(conditionPda, this.programIds);
18329
- const builder = this.program.methods.approvePresale({
18330
- contentHash: Array.from(contentHash),
18331
- hookProgram,
18332
- authorizedClob,
18333
- expirationTime,
18334
- creator
18335
- }).accounts({
18336
- caller,
18337
- payer,
18338
- config: this.configPda,
18339
- presale: presalePda,
18340
- question: questionPda,
18341
- currencyMint,
18342
- oracle: oraclePubkey,
18343
- condition: conditionPda,
18344
- yesMint,
18345
- noMint,
18346
- mintAuthority,
18347
- collateralVault,
18348
- marketOracle: marketOraclePda,
18349
- marketOracleVault,
18350
- presaleVault,
18351
- referralTokenAccount,
18352
- companyTokenAccount,
18353
- adminVault,
18354
- adminConfig,
18355
- claimRecord,
18356
- questionFee: questionFeePda,
18357
- feeConfig: feeConfigPda,
18358
- marketFeeOverride,
18359
- feeManagementProgram: this.programIds.feeManagement,
18360
- conditionalTokensProgram: this.programIds.conditionalTokens,
18361
- presaleProgram: this.programIds.presale,
18362
- marketOracleProgram: this.programIds.marketOracle,
18363
- adminProgram: this.programIds.adminContract,
18364
- tokenProgram: TOKEN_PROGRAM_ID,
18365
- token2022Program: TOKEN_2022_PROGRAM_ID,
18366
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
18367
- systemProgram: SystemProgram.programId,
18368
- rent: SYSVAR_RENT_PUBKEY
18369
- });
18370
- let tx;
18371
- if (params.lookupTable) {
18372
- const ix = await builder.instruction();
18373
- const cu = ComputeBudgetProgram.setComputeUnitLimit({ units: 14e5 });
18374
- const { blockhash } = await this.provider.connection.getLatestBlockhash();
18375
- const msg = new TransactionMessage({
18376
- payerKey: payer,
18377
- recentBlockhash: blockhash,
18378
- instructions: [cu, ix]
18379
- }).compileToV0Message([params.lookupTable]);
18380
- tx = new VersionedTransaction(msg);
18381
- } else {
18382
- tx = await builder.transaction();
18383
- }
18384
- return { tx, questionPda, conditionPda, marketOraclePda, marketOracleVault };
18385
- }
18386
- /**
18387
- * Whitelist-only: reject presale so users can refund.
18388
- */
18389
- async rejectPresale(presalePda, caller = this.walletPubkey) {
18390
- if (!this.programIds.presale) throw new Error("presale program ID not configured");
18391
- return this.program.methods.rejectPresale().accounts({
18392
- caller,
18393
- config: this.configPda,
18394
- presale: presalePda,
18395
- presaleProgram: this.programIds.presale
18396
- }).transaction();
18397
- }
18398
- /**
18399
- * Whitelist-only: distribute presale vault → 10% agents + 10% company + 80% botmm.
18400
- * Closes presale_vault ATA and presale PDA after distribution; lamports returned to payer.
18401
- */
18402
- async collectPresaleRevenue(params) {
18403
- const {
18404
- presalePda,
18405
- currencyMint,
18406
- conditionId,
18407
- referralAddress,
18408
- companyAddress,
18409
- adminOwner
18410
- } = params;
18411
- const caller = params.caller ?? this.walletPubkey;
18412
- const payer = params.payer ?? this.walletPubkey;
18413
- if (!this.programIds.presale) throw new Error("presale program ID not configured");
18414
- if (!this.programIds.adminContract) throw new Error("adminContract program ID not configured");
18415
- const presaleVault = getAssociatedTokenAddressSync(currencyMint, presalePda, true);
18416
- const referralTokenAccount = getAssociatedTokenAddressSync(currencyMint, referralAddress);
18417
- const companyTokenAccount = getAssociatedTokenAddressSync(currencyMint, companyAddress);
18418
- const [adminConfig] = PDA.adminConfig(adminOwner, this.programIds);
18419
- const [claimRecord] = PDA.claimRecord(conditionId, this.programIds);
18420
- const adminVault = getAssociatedTokenAddressSync(currencyMint, adminConfig, true);
18421
- return this.program.methods.collectPresaleRevenue(Array.from(conditionId)).accounts({
18422
- caller,
18423
- config: this.configPda,
18424
- presale: presalePda,
18425
- presaleVault,
18426
- currencyMint,
18427
- referralTokenAccount,
18428
- companyTokenAccount,
18429
- adminVault,
18430
- adminConfig,
18431
- claimRecord,
18432
- payer,
18433
- presaleProgram: this.programIds.presale,
18434
- adminProgram: this.programIds.adminContract,
18435
- tokenProgram: TOKEN_PROGRAM_ID,
18436
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
18437
- systemProgram: SystemProgram.programId
18438
- }).transaction();
18439
- }
18440
- /**
18441
- * Whitelist-only: snapshot MST supply so holders can claim trading fees.
18442
- * Call after oracle resolves the question.
18443
- */
18444
- async collectTradingFee(marketOraclePda, qtMint, caller = this.walletPubkey) {
18445
- if (!this.programIds.marketOracle) throw new Error("marketOracle program ID not configured");
18446
- return this.program.methods.collectTradingFee().accounts({
18447
- caller,
18448
- config: this.configPda,
18449
- marketOracle: marketOraclePda,
18450
- qtMint,
18451
- marketOracleProgram: this.programIds.marketOracle
18452
- }).transaction();
18453
- }
18454
- };
18455
- var CtfClient = class {
18456
- constructor(program, provider, programIds) {
18457
- this.program = program;
18458
- this.provider = provider;
18459
- this.programIds = programIds;
18460
- }
18461
- get walletPubkey() {
18462
- return this.provider.wallet.publicKey;
18463
- }
18464
- // ─── Instructions ────────────────────────────────────────────────────────────
18465
- /**
18466
- * Create a Condition directly (bypasses QuestionMarket).
18467
- * oracle is UncheckedAccount — set it to any pubkey (e.g. user wallet)
18468
- * so that wallet can later sign reportPayouts.
18469
- * payer covers rent for condition + mints.
18470
- */
18471
- async prepareCondition(questionId, oracle, collateralMint, hookProgram, authorizedClob, payer = this.walletPubkey, signers = []) {
18472
- const [conditionPda] = PDA.condition(oracle, questionId, this.programIds);
18473
- const [yesMint] = PDA.yesMint(conditionPda, this.programIds);
18474
- const [noMint] = PDA.noMint(conditionPda, this.programIds);
18475
- const [mintAuthority] = PDA.mintAuthority(conditionPda, this.programIds);
18476
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
18477
- const sig = await this.program.methods.prepareCondition(
18478
- Array.from(questionId),
18479
- hookProgram,
18480
- authorizedClob
18481
- ).accounts({
18482
- oracle,
18483
- condition: conditionPda,
18484
- yesMint,
18485
- noMint,
18486
- mintAuthority,
18487
- collateralMint,
18488
- collateralVault,
18489
- payer,
18490
- tokenProgram: TOKEN_2022_PROGRAM_ID,
18491
- systemProgram: SystemProgram.programId,
18492
- rent: SYSVAR_RENT_PUBKEY
18493
- }).signers(signers).rpc({ skipPreflight: true });
18494
- return { signature: sig, conditionPda, yesMint, noMint };
18495
- }
18496
- /** One-time setup. Caller becomes the CTF owner. Can only be called once. */
18497
- async initializeCtfConfig() {
18498
- const [ctfConfig] = PDA.ctfConfig(this.programIds);
18499
- const sig = await this.program.methods.initializeCtfConfig().accounts({
18500
- payer: this.walletPubkey,
18501
- ctfConfig,
18502
- systemProgram: SystemProgram.programId
18503
- }).rpc();
18504
- return { signature: sig };
18505
- }
18506
- /**
18507
- * Create the shared collateral vault for a given collateral mint (e.g. USDC).
18508
- * Only callable by the CTF owner (as stored in ctf_config).
18509
- * authority defaults to the wallet — pass a different signer if needed.
18510
- */
18511
- async initializeVault(collateralMint) {
18512
- const [ctfConfig] = PDA.ctfConfig(this.programIds);
18513
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
18514
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
18515
- const sig = await this.program.methods.initializeVault().accounts({
18516
- authority: this.walletPubkey,
18517
- payer: this.walletPubkey,
18518
- ctfConfig,
18519
- collateralMint,
18520
- collateralVault,
18521
- vaultTokenAccount,
18522
- systemProgram: SystemProgram.programId,
18523
- tokenProgram: TOKEN_PROGRAM_ID,
18524
- rent: SYSVAR_RENT_PUBKEY
18525
- }).rpc();
18526
- return { signature: sig };
18527
- }
18528
- /**
18529
- * Initialize an empty Position account (balance = 0) for a user.
18530
- * Needed before redeemPositions when the user only holds the opposite outcome
18531
- * (e.g. buyer received YES via CLOB match but never had a NO position).
18532
- *
18533
- * Idempotent — call `fetchPosition` first to skip if already initialized.
18534
- */
18535
- async initPosition(condition, outcomeIndex, user = this.walletPubkey) {
18536
- const [position] = PDA.position(condition, outcomeIndex, user, this.programIds);
18537
- const sig = await this.program.methods.initPosition(outcomeIndex).accounts({
18538
- user,
18539
- condition,
18540
- position,
18541
- systemProgram: SystemProgram.programId
18542
- }).rpc();
18543
- return { signature: sig };
18544
- }
18545
- /**
18546
- * Split `amount` collateral into equal YES + NO tokens.
18547
- * ATAs created automatically via `init_if_needed`.
18548
- */
18549
- async splitPosition(condition, collateralMint, amount, user = this.walletPubkey, payer = this.walletPubkey) {
18550
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
18551
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
18552
- const [yesMint] = PDA.yesMint(condition, this.programIds);
18553
- const [noMint] = PDA.noMint(condition, this.programIds);
18554
- const [mintAuthority] = PDA.mintAuthority(condition, this.programIds);
18555
- const [yesPosition] = PDA.position(condition, 1, user, this.programIds);
18556
- const [noPosition] = PDA.position(condition, 0, user, this.programIds);
18557
- const userYesAta = getAssociatedTokenAddressSync(yesMint, user, false, TOKEN_2022_PROGRAM_ID);
18558
- const userNoAta = getAssociatedTokenAddressSync(noMint, user, false, TOKEN_2022_PROGRAM_ID);
18559
- const userCollateral = getAssociatedTokenAddressSync(collateralMint, user);
18560
- return this.program.methods.splitPosition(amount).accounts({
18561
- user,
18562
- payer,
18563
- condition,
18564
- collateralVault,
18565
- vaultTokenAccount,
18566
- userCollateral,
18567
- yesMint,
18568
- userYesAta,
18569
- noMint,
18570
- userNoAta,
18571
- yesPosition,
18572
- noPosition,
18573
- mintAuthority,
18574
- tokenProgram: TOKEN_PROGRAM_ID,
18575
- token2022Program: TOKEN_2022_PROGRAM_ID,
18576
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
18577
- systemProgram: SystemProgram.programId
18578
- }).transaction();
18579
- }
18580
- /**
18581
- * Merge `amount` YES + NO tokens back into collateral.
18582
- * Both token balances must be ≥ amount.
18583
- */
18584
- async mergePosition(condition, collateralMint, amount, user = this.walletPubkey, payer = this.walletPubkey) {
18585
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
18586
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
18587
- const [yesMint] = PDA.yesMint(condition, this.programIds);
18588
- const [noMint] = PDA.noMint(condition, this.programIds);
18589
- const [yesPosition] = PDA.position(condition, 1, user, this.programIds);
18590
- const [noPosition] = PDA.position(condition, 0, user, this.programIds);
18591
- const userYesAta = getAssociatedTokenAddressSync(yesMint, user, false, TOKEN_2022_PROGRAM_ID);
18592
- const userNoAta = getAssociatedTokenAddressSync(noMint, user, false, TOKEN_2022_PROGRAM_ID);
18593
- const userCollateral = getAssociatedTokenAddressSync(collateralMint, user);
18594
- return this.program.methods.mergePosition(amount).accounts({
18595
- user,
18596
- payer,
18597
- condition,
18598
- collateralVault,
18599
- vaultTokenAccount,
18600
- userCollateral,
18601
- yesMint,
18602
- userYesAta,
18603
- noMint,
18604
- userNoAta,
18605
- yesPosition,
18606
- noPosition,
18607
- tokenProgram: TOKEN_PROGRAM_ID,
18608
- token2022Program: TOKEN_2022_PROGRAM_ID,
18609
- systemProgram: SystemProgram.programId
18610
- }).transaction();
18611
- }
18612
- /**
18613
- * After condition resolves: burn outcome tokens proportional to payout
18614
- * and receive USDC. Works for winning, losing, or both positions.
18615
- */
18616
- async redeemPositions(condition, collateralMint, user = this.walletPubkey, payer = this.walletPubkey) {
18617
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
18618
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
18619
- const [yesMint] = PDA.yesMint(condition, this.programIds);
18620
- const [noMint] = PDA.noMint(condition, this.programIds);
18621
- const [yesPosition] = PDA.position(condition, 1, user, this.programIds);
18622
- const [noPosition] = PDA.position(condition, 0, user, this.programIds);
18623
- const userYesAta = getAssociatedTokenAddressSync(yesMint, user, false, TOKEN_2022_PROGRAM_ID);
18624
- const userNoAta = getAssociatedTokenAddressSync(noMint, user, false, TOKEN_2022_PROGRAM_ID);
18625
- const userCollateral = getAssociatedTokenAddressSync(collateralMint, user);
18626
- const createYesAtaIx = createAssociatedTokenAccountIdempotentInstruction(
18627
- payer,
18628
- userYesAta,
18629
- user,
18630
- yesMint,
18631
- TOKEN_2022_PROGRAM_ID
18632
- );
18633
- const createNoAtaIx = createAssociatedTokenAccountIdempotentInstruction(
18634
- payer,
18635
- userNoAta,
18636
- user,
18637
- noMint,
18638
- TOKEN_2022_PROGRAM_ID
18639
- );
18640
- const redeemIx = await this.program.methods.redeemPositions().accounts({
18641
- user,
18642
- payer,
18643
- condition,
18644
- collateralVault,
18645
- vaultTokenAccount,
18646
- userCollateral,
18647
- yesMint,
18648
- userYesAta,
18649
- noMint,
18650
- userNoAta,
18651
- yesPosition,
18652
- noPosition,
18653
- tokenProgram: TOKEN_PROGRAM_ID,
18654
- token2022Program: TOKEN_2022_PROGRAM_ID,
18655
- systemProgram: SystemProgram.programId
18656
- }).instruction();
18657
- const tx = new Transaction();
18658
- tx.add(createYesAtaIx, createNoAtaIx, redeemIx);
18659
- return tx;
18660
- }
18661
- /**
18662
- * Oracle directly resolves a condition with payout numerators.
18663
- * Bypasses QuestionMarket — only for oracle-owned conditions.
18664
- */
18665
- async reportPayouts(condition, payoutNumerators) {
18666
- const sig = await this.program.methods.reportPayouts(payoutNumerators.map((n) => new anchor6.BN(n))).accounts({
18667
- oracle: this.walletPubkey,
18668
- condition
18669
- }).rpc();
18670
- return { signature: sig };
18671
- }
18672
- /**
18673
- * Build the `transfer_position` instruction for use in a CLOB CPI.
18674
- *
18675
- * This instruction requires `clob_authority` (the CLOB's PDA) to sign.
18676
- * A PDA can only sign from within its own program via CPI — this method
18677
- * CANNOT be called directly from a wallet transaction.
18678
- *
18679
- * Use-cases:
18680
- * - Custom CLOB implementations that call CTF.transfer_position via CPI
18681
- * - Anchor CPI builders in other Rust programs
18682
- *
18683
- * @param condition - Condition PDA
18684
- * @param outcomeMint - YES or NO mint
18685
- * @param fromUser - Source wallet
18686
- * @param fromTokenAccount - Source ATA
18687
- * @param fromPosition - Source Position PDA
18688
- * @param toUser - Destination wallet
18689
- * @param toTokenAccount - Destination ATA (created if needed — payer covers rent)
18690
- * @param toPosition - Destination Position PDA
18691
- * @param payer - Pays rent for toPosition + toTokenAccount creation
18692
- * @param clobAuthority - CLOB config PDA (must sign via CPI)
18693
- * @param outcomeIndex - 0 = NO, 1 = YES
18694
- * @param amount - Token amount to transfer
18695
- */
18696
- async transferPositionIx(condition, outcomeMint, fromUser, fromTokenAccount, fromPosition, toUser, toTokenAccount, toPosition, payer, clobAuthority, outcomeIndex, amount) {
18697
- return this.program.methods.transferPosition(outcomeIndex, amount).accounts({
18698
- payer,
18699
- clobAuthority,
18700
- condition,
18701
- outcomeMint,
18702
- fromUser,
18703
- fromTokenAccount,
18704
- fromPosition,
18705
- toUser,
18706
- toTokenAccount,
18707
- toPosition,
18708
- token2022Program: TOKEN_2022_PROGRAM_ID,
18709
- systemProgram: SystemProgram.programId
18710
- }).instruction();
18711
- }
18712
- /**
18713
- * Update the authorized CLOB on a condition.
18714
- * Only callable by the condition's oracle (the wallet that signed createQuestion).
18715
- */
18716
- async updateAuthorizedClob(condition, newAuthorizedClob) {
18717
- const sig = await this.program.methods.updateAuthorizedClob(newAuthorizedClob).accounts({
18718
- oracle: this.walletPubkey,
18719
- condition
18720
- }).rpc();
18721
- return { signature: sig };
18722
- }
18723
- // ─── Queries ─────────────────────────────────────────────────────────────────
18724
- async fetchCtfConfig() {
18725
- try {
18726
- const [ctfConfig] = PDA.ctfConfig(this.programIds);
18727
- const acc = await this.program.account.ctfConfig.fetch(ctfConfig);
18728
- return {
18729
- owner: acc.owner,
18730
- bump: acc.bump
18731
- };
18732
- } catch {
18733
- return null;
18734
- }
18735
- }
18736
- async fetchCondition(conditionPda) {
18737
- try {
18738
- const acc = await this.program.account.condition.fetch(conditionPda);
18739
- return {
18740
- oracle: acc.oracle,
18741
- questionId: new Uint8Array(acc.questionId),
18742
- outcomeSlotCount: acc.outcomeSlotCount,
18743
- payoutNumerators: acc.payoutNumerators,
18744
- payoutDenominator: acc.payoutDenominator,
18745
- totalCollateral: acc.totalCollateral,
18746
- collateralMint: acc.collateralMint,
18747
- collateralVault: acc.collateralVault,
18748
- yesMint: acc.yesMint,
18749
- noMint: acc.noMint,
18750
- hookProgram: acc.hookProgram,
18751
- authorizedClob: acc.authorizedClob,
18752
- isResolved: acc.isResolved,
18753
- resolvedAt: acc.resolvedAt?.toNumber() ?? 0,
18754
- bump: acc.bump
18755
- };
18756
- } catch {
18757
- return null;
18758
- }
18759
- }
18760
- async fetchVault(collateralMint) {
18761
- try {
18762
- const [vaultPda] = PDA.collateralVault(collateralMint, this.programIds);
18763
- const acc = await this.program.account.collateralVault.fetch(vaultPda);
18764
- return {
18765
- collateralMint: acc.collateralMint,
18766
- vaultTokenAccount: acc.vaultTokenAccount,
18767
- totalLocked: acc.totalLocked,
18768
- conditionCount: acc.conditionCount,
18769
- bump: acc.bump,
18770
- vaultBump: acc.vaultBump
18771
- };
18772
- } catch {
18773
- return null;
18774
- }
18775
- }
18776
- async fetchPosition(condition, outcomeIndex, owner = this.walletPubkey) {
18777
- try {
18778
- const [pda] = PDA.position(condition, outcomeIndex, owner, this.programIds);
18779
- const acc = await this.program.account.position.fetch(pda);
18780
- return {
18781
- owner: acc.owner,
18782
- condition: acc.condition,
18783
- outcomeIndex: acc.outcomeIndex,
18784
- balance: acc.balance,
18785
- bump: acc.bump
18786
- };
18787
- } catch {
18788
- return null;
18789
- }
18790
- }
18791
- /** YES = outcome index 1, NO = outcome index 0. */
18792
- async fetchBothPositions(condition, owner = this.walletPubkey) {
18793
- const [yes, no] = await Promise.all([
18794
- this.fetchPosition(condition, 1, owner),
18795
- this.fetchPosition(condition, 0, owner)
18796
- ]);
18797
- return { yes, no };
18798
- }
18799
- /** Thin wrapper: fetch position for a single outcome token (0=NO, 1=YES). */
18800
- async fetchTokenBalance(condition, tokenId, owner = this.walletPubkey) {
18801
- return this.fetchPosition(condition, tokenId, owner);
18802
- }
18803
- // ─── PDA helpers (public for consumers) ──────────────────────────────────────
18804
- yesMintPda(condition) {
18805
- return PDA.yesMint(condition, this.programIds)[0];
18806
- }
18807
- noMintPda(condition) {
18808
- return PDA.noMint(condition, this.programIds)[0];
18809
- }
18810
- mintAuthorityPda(condition) {
18811
- return PDA.mintAuthority(condition, this.programIds)[0];
18812
- }
18813
- collateralVaultPda(collateralMint) {
18814
- return PDA.collateralVault(collateralMint, this.programIds)[0];
18815
- }
18816
- };
18817
- var ClobClient = class {
18818
- constructor(program, provider, programIds, networkConfig) {
18819
- /** Cache: conditionPda.toBase58() → marketOracleVault ATA */
18820
- this._marketOracleVaultCache = /* @__PURE__ */ new Map();
18821
- /** ALT cache: condition.toBase58() → loaded ALT account */
18822
- this._altCache = /* @__PURE__ */ new Map();
18823
- this.program = program;
18824
- this.provider = provider;
18825
- this.programIds = programIds;
18826
- this.networkConfig = networkConfig;
18827
- }
18828
- async companyAddress() {
18829
- if (!this.feeClient || !this.feeConfigOwner) return void 0;
18830
- if (!this._companyAddress) {
18831
- const cfg = await this.feeClient.fetchFeeConfig(this.feeConfigOwner);
18832
- if (cfg) {
18833
- this._companyAddress = cfg.companyAddress;
18834
- this._referralVault = cfg.referralVault;
18835
- }
18836
- }
18837
- return this._companyAddress;
18838
- }
18839
- async referralVault() {
18840
- await this.companyAddress();
18841
- return this._referralVault;
18842
- }
18843
- /**
18844
- * Derive marketOracleVault ATA for a condition, cached per condition.
18845
- * Works for presale markets (market_oracle initialized by approvePresale).
18846
- * Returns undefined if ctfClient/qmConfigPda not injected or marketOracle not configured.
18847
- */
18848
- async getMarketOracleVault(condition, collateralMint) {
18849
- if (!this.ctfClient || !this.qmConfigPda || !this.programIds.marketOracle) return void 0;
18850
- const key = condition.toBase58();
18851
- const cached = this._marketOracleVaultCache.get(key);
18852
- if (cached) return cached;
18853
- const cond = await this.ctfClient.fetchCondition(condition);
18854
- if (!cond) return void 0;
18855
- const [questionPda] = PDA.question(this.qmConfigPda, cond.questionId, this.programIds);
18856
- const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
18857
- const vault = getAssociatedTokenAddressSync(collateralMint, marketOraclePda, true);
18858
- this._marketOracleVaultCache.set(key, vault);
18859
- return vault;
18860
- }
18861
- /**
18862
- * Returns a createATA ix for the oracle vault when:
18863
- * - takerFee > 0
18864
- * - marketFeeOverride exists for the condition
18865
- * - oracle vault ATA is not yet initialized
18866
- * Idempotent — safe to call every tx; returns null if vault already exists.
18867
- */
18868
- async buildInitOracleVaultIfNeeded(condition, collateralMint, takerFee, payer) {
18869
- if (takerFee.isZero()) return null;
18870
- if (!this.feeConfigOwner || !this.programIds.feeManagement) return null;
18871
- if (!this.ctfClient || !this.qmConfigPda || !this.programIds.marketOracle) return null;
18872
- const companyAddr = await this.companyAddress();
18873
- const refVault = await this.referralVault();
18874
- if (!companyAddr || !refVault) return null;
18875
- const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
18876
- const cond = await this.ctfClient.fetchCondition(condition);
18877
- if (!cond) return null;
18878
- const [questionPda] = PDA.question(this.qmConfigPda, cond.questionId, this.programIds);
18879
- const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
18880
- const vault = getAssociatedTokenAddressSync(collateralMint, marketOraclePda, true);
18881
- const [feeOverrideInfo, vaultInfo] = await Promise.all([
18882
- this.provider.connection.getAccountInfo(feeOverridePda),
18883
- this.provider.connection.getAccountInfo(vault)
18884
- ]);
18885
- if (!feeOverrideInfo) return null;
18886
- if (vaultInfo) return null;
18887
- return createAssociatedTokenAccountIdempotentInstruction(
18888
- payer,
18889
- vault,
18890
- marketOraclePda,
18891
- collateralMint
18892
- );
18893
- }
18894
- get walletPubkey() {
18895
- return this.provider.wallet.publicKey;
18896
- }
18897
- /**
18898
- * Get or create an ALT for a condition.
18899
- * Includes order_record PDAs for taker + all makers so match tx stays under 1232 bytes.
18900
- */
18901
- async ensureAlt(condition, collateralMint, takerSigned, makers) {
18902
- const cacheKey = condition.toBase58();
18903
- if (this._altCache.has(cacheKey)) {
18904
- return this._altCache.get(cacheKey);
18905
- }
18906
- const { connection } = this.provider;
18907
- const payer = this.walletPubkey;
18908
- const taker = takerSigned.order.maker;
18909
- const takerNonce = takerSigned.order.nonce;
18910
- const tokenId = takerSigned.order.tokenId;
18911
- const [yesMint] = PDA.yesMint(condition, this.programIds);
18912
- const [noMint] = PDA.noMint(condition, this.programIds);
18913
- const [extraAccountMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
18914
- const [hookConfig] = PDA.hookConfig(this.programIds);
18915
- const [takerOrderStatus] = PDA.orderStatus(taker, takerNonce, this.programIds);
18916
- const [takerOrderRecord] = PDA.orderRecord(taker, takerNonce, this.programIds);
18917
- const [takerPosition] = PDA.position(condition, tokenId, taker, this.programIds);
18918
- const clobConfigPda = this.configPda();
18919
- const outcomeMint = tokenId === 1 ? yesMint : noMint;
18920
- const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
18921
- const addresses = [
18922
- Ed25519Program.programId,
18923
- this.programIds.clobExchange,
18924
- this.programIds.conditionalTokens,
18925
- this.programIds.hook,
18926
- TOKEN_PROGRAM_ID,
18927
- TOKEN_2022_PROGRAM_ID,
18928
- SystemProgram.programId,
18929
- SYSVAR_INSTRUCTIONS_PUBKEY,
18930
- clobConfigPda,
18931
- hookConfig,
18932
- condition,
18933
- yesMint,
18934
- noMint,
18935
- extraAccountMeta,
18936
- taker,
18937
- getAssociatedTokenAddressSync(collateralMint, taker),
18938
- getAssociatedTokenAddressSync(outcomeMint, taker, false, TOKEN_2022_PROGRAM_ID),
18939
- takerPosition,
18940
- takerOrderStatus,
18941
- takerOrderRecord
18942
- ];
18943
- if (feeRecipientAddr) addresses.push(feeRecipientAddr);
18944
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
18945
- const [vaultToken] = PDA.vaultToken(collateralMint, this.programIds);
18946
- const [mintAuth] = PDA.mintAuthority(condition, this.programIds);
18947
- const [extraMetaNo] = PDA.extraAccountMetaList(noMint, this.programIds);
18948
- const clobUsdcAta = getAssociatedTokenAddressSync(collateralMint, clobConfigPda, true);
18949
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, TOKEN_2022_PROGRAM_ID);
18950
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfigPda, true, TOKEN_2022_PROGRAM_ID);
18951
- const [clobYesPos] = PDA.position(condition, 1, clobConfigPda, this.programIds);
18952
- const [clobNoPos] = PDA.position(condition, 0, clobConfigPda, this.programIds);
18953
- addresses.push(
18954
- collateralVault,
18955
- vaultToken,
18956
- mintAuth,
18957
- extraMetaNo,
18958
- clobUsdcAta,
18959
- clobYesAta,
18960
- clobNoAta,
18961
- clobYesPos,
18962
- clobNoPos,
18963
- collateralMint,
18964
- ASSOCIATED_TOKEN_PROGRAM_ID
18965
- );
18966
- for (const m of makers) {
18967
- const seller = m.order.maker;
18968
- const makerTokenId = m.order.tokenId;
18969
- const makerMint = makerTokenId === 1 ? yesMint : noMint;
18970
- const [sellerPos] = PDA.position(condition, makerTokenId, seller, this.programIds);
18971
- const [sellerStatus] = PDA.orderStatus(seller, m.order.nonce, this.programIds);
18972
- const [sellerRecord] = PDA.orderRecord(seller, m.order.nonce, this.programIds);
18973
- addresses.push(
18974
- seller,
18975
- sellerRecord,
18976
- getAssociatedTokenAddressSync(makerMint, seller, false, TOKEN_2022_PROGRAM_ID),
18977
- getAssociatedTokenAddressSync(collateralMint, seller),
18978
- sellerPos,
18979
- sellerStatus
18980
- );
18981
- }
18982
- if (this.programIds.feeManagement && this.feeConfigOwner) {
18983
- const companyAddr = await this.companyAddress();
18984
- const refVault = await this.referralVault();
18985
- addresses.push(
18986
- this.programIds.feeManagement,
18987
- PDA.feeConfig(this.feeConfigOwner, this.programIds)[0],
18988
- PDA.marketFeeOverride(condition, this.programIds)[0]
18989
- );
18990
- if (companyAddr) {
18991
- addresses.push(getAssociatedTokenAddressSync(collateralMint, companyAddr));
18992
- }
18993
- const oracleVaultForAlt = await this.getMarketOracleVault(condition, collateralMint) ?? payer;
18994
- addresses.push(oracleVaultForAlt);
18995
- if (refVault) {
18996
- addresses.push(refVault);
18997
- }
18998
- }
18999
- const slot = await connection.getSlot("finalized");
19000
- const [createIx, altAddress] = AddressLookupTableProgram.createLookupTable({
19001
- authority: payer,
19002
- payer,
19003
- recentSlot: slot
19004
- });
19005
- const BATCH = 30;
19006
- const extendIxs = [];
19007
- for (let i = 0; i < addresses.length; i += BATCH) {
19008
- extendIxs.push(AddressLookupTableProgram.extendLookupTable({
19009
- payer,
19010
- authority: payer,
19011
- lookupTable: altAddress,
19012
- addresses: addresses.slice(i, i + BATCH)
19013
- }));
19014
- }
19015
- await this._sendLegacyTx([createIx, extendIxs[0]]);
19016
- for (let i = 1; i < extendIxs.length; i++) {
19017
- await this._sendLegacyTx([extendIxs[i]]);
19018
- }
19019
- for (let attempt = 0; attempt < 30; attempt++) {
19020
- await new Promise((r) => setTimeout(r, 1e3));
19021
- const res = await connection.getAddressLookupTable(altAddress);
19022
- if (res.value && res.value.state.addresses.length === addresses.length) {
19023
- this._altCache.set(cacheKey, res.value);
19024
- return res.value;
19025
- }
19026
- }
19027
- throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
19028
- }
19029
- async _sendLegacyTxSig(instructions) {
19030
- const { connection } = this.provider;
19031
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
19032
- const { Transaction: Transaction15 } = await import('@solana/web3.js');
19033
- const tx = new Transaction15();
19034
- tx.recentBlockhash = blockhash;
19035
- tx.feePayer = this.walletPubkey;
19036
- tx.add(...instructions);
19037
- const signed = await this.provider.wallet.signTransaction(tx);
19038
- const sig = await connection.sendRawTransaction(signed.serialize(), { skipPreflight: true });
19039
- const result = await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, "confirmed");
19040
- if (result.value.err) {
19041
- const txInfo = await connection.getTransaction(sig, { maxSupportedTransactionVersion: 0 });
19042
- const logs = txInfo?.meta?.logMessages ?? [];
19043
- const err = new Error(`Register tx failed: ${JSON.stringify(result.value.err)}
19044
- ${logs.join("\n")}`);
19045
- err.logs = logs;
19046
- throw err;
19047
- }
19048
- return sig;
19049
- }
19050
- async _sendLegacyTx(instructions) {
19051
- await this._sendLegacyTxSig(instructions);
19052
- }
19053
- async _ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta) {
19054
- const { connection } = this.provider;
19055
- const [yesInfo, noInfo] = await Promise.all([
19056
- connection.getAccountInfo(clobYesAta),
19057
- connection.getAccountInfo(clobNoAta)
19058
- ]);
19059
- const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction7 } = await import('@solana/spl-token');
19060
- const ixs = [];
19061
- if (!yesInfo) {
19062
- ixs.push(createAssociatedTokenAccountIdempotentInstruction7(
19063
- this.walletPubkey,
19064
- clobYesAta,
19065
- clobConfig,
19066
- yesMint,
19067
- TOKEN_2022_PROGRAM_ID
19068
- ));
19069
- }
19070
- if (!noInfo) {
19071
- ixs.push(createAssociatedTokenAccountIdempotentInstruction7(
19072
- this.walletPubkey,
19073
- clobNoAta,
19074
- clobConfig,
19075
- noMint,
19076
- TOKEN_2022_PROGRAM_ID
19077
- ));
19078
- }
19079
- if (ixs.length > 0) {
19080
- await this._sendLegacyTx(ixs);
19081
- }
19082
- }
19083
- /**
19084
- * Send a match transaction as versioned (v0) with optional ALT.
19085
- * Both operator wallet and payer (this.provider.wallet) sign.
19086
- */
19087
- async sendMatchTx(instructions, lookupTable, whitelistedWallet) {
19088
- const { connection } = this.provider;
19089
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
19090
- const cuLimit = ComputeBudgetProgram.setComputeUnitLimit({ units: 14e5 });
19091
- const heapFrame = ComputeBudgetProgram.requestHeapFrame({ bytes: 262144 });
19092
- const message = new TransactionMessage({
19093
- payerKey: this.walletPubkey,
19094
- recentBlockhash: blockhash,
19095
- instructions: [cuLimit, heapFrame, ...instructions]
19096
- }).compileToV0Message(lookupTable ? [lookupTable] : []);
19097
- const vtx = new VersionedTransaction(message);
19098
- const signers = [this.provider.wallet.payer];
19099
- if (whitelistedWallet && !whitelistedWallet.publicKey.equals(this.walletPubkey)) {
19100
- signers.push(whitelistedWallet.payer);
19101
- }
19102
- vtx.sign(signers);
19103
- const sig = await connection.sendRawTransaction(vtx.serialize(), {
19104
- skipPreflight: false
19105
- });
19106
- const result = await connection.confirmTransaction(
19107
- { signature: sig, blockhash, lastValidBlockHeight },
19108
- "confirmed"
19109
- );
19110
- if (result.value.err) {
19111
- const txInfo = await connection.getTransaction(sig, { maxSupportedTransactionVersion: 0 });
19112
- const logs = txInfo?.meta?.logMessages ?? [];
19113
- const err = new Error(`Match tx failed: ${JSON.stringify(result.value.err)}
19114
- ${logs.join("\n")}`);
19115
- err.logs = logs;
19116
- throw err;
19117
- }
19118
- return sig;
19119
- }
19120
- configPda() {
19121
- return PDA.clobConfig(this.programIds)[0];
19122
- }
19123
- // ─── Register / Cancel ───────────────────────────────────────────────────────
19124
- /**
19125
- * Register a signed order on-chain.
19126
- * Sends Ed25519 precompile ix + register_order ix in one legacy tx.
19127
- * Called at match time (engine-triggered), not at order placement.
19128
- */
19129
- async registerOrder(signed) {
19130
- const nonce = signed.order.nonce;
19131
- const orderSigner = signed.order.maker;
19132
- const [orderRecord] = PDA.orderRecord(orderSigner, nonce, this.programIds);
19133
- const ed25519Ix = buildBatchedEd25519Instruction([signed]);
19134
- const [orderStatus] = PDA.orderStatus(orderSigner, nonce, this.programIds);
19135
- const registerIx = await this.program.methods.registerOrder(nonce).accounts({
19136
- payer: this.walletPubkey,
19137
- clobConfig: this.configPda(),
19138
- ixSysvar: IX_SYSVAR,
19139
- orderSigner,
19140
- orderRecord,
19141
- orderStatus,
19142
- systemProgram: SystemProgram.programId
19143
- }).instruction();
19144
- return this._sendLegacyTxSig([ed25519Ix, registerIx]);
19145
- }
19146
- /** Register only if the PDA doesn't exist yet (idempotent). */
19147
- async registerOrderIfNeeded(signed) {
19148
- const [pda] = PDA.orderRecord(signed.order.maker, signed.order.nonce, this.programIds);
19149
- const existing = await this.program.account.signedOrderRecord.fetchNullable(pda);
19150
- if (!existing) {
19151
- await this.registerOrder(signed);
19152
- }
19153
- }
19154
- // ─── Register CollectFeeOrder ────────────────────────────────────────────────
19155
- /**
19156
- * Build a legacy Transaction to register a CollectFeeOrder on-chain.
19157
- * Must be signed and sent before calling buildBatchCollectRedeemEarlyTx.
19158
- *
19159
- * Flow: [Ed25519 ix (1 user sig) + register_collect_fee_order ix]
19160
- * Caller signs with payer keypair and sends.
19161
- */
19162
- async buildRegisterCollectFeeOrderTx(signedOrder, payer) {
19163
- const { order } = signedOrder;
19164
- const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
19165
- const ed25519Ix = buildBatchedCollectFeeEd25519Instruction([signedOrder]);
19166
- const registerIx = await this.program.methods.registerCollectFeeOrder(order.nonce).accounts({
19167
- payer,
19168
- clobConfig: this.configPda(),
19169
- ixSysvar: IX_SYSVAR,
19170
- orderSigner: order.user,
19171
- collectFeeOrderRecord,
19172
- systemProgram: SystemProgram.programId
19173
- }).instruction();
19174
- const { blockhash } = await this.provider.connection.getLatestBlockhash();
19175
- const tx = new Transaction();
19176
- tx.recentBlockhash = blockhash;
19177
- tx.feePayer = payer;
19178
- tx.add(ed25519Ix, registerIx);
19179
- return tx;
19180
- }
19181
- /**
19182
- * Register a CollectFeeOrder on-chain and send immediately (operator-signed flow).
19183
- * Idempotent — skips if PDA already exists.
19184
- */
19185
- async registerCollectFeeOrderIfNeeded(signedOrder) {
19186
- const [pda] = PDA.collectFeeOrderRecord(
19187
- signedOrder.order.user,
19188
- signedOrder.order.nonce,
19189
- this.programIds
19190
- );
19191
- const existing = await this.program.account.collectFeeOrderRecord?.fetchNullable(pda);
19192
- if (!existing) {
19193
- const tx = await this.buildRegisterCollectFeeOrderTx(signedOrder, this.walletPubkey);
19194
- await this._sendLegacyTx(tx.instructions);
19195
- }
19196
- }
19197
- /** Cancel an order — closes its OrderRecord PDA and returns rent to maker. */
19198
- async cancelOrder(nonce) {
19199
- const [orderRecord] = PDA.orderRecord(this.walletPubkey, nonce, this.programIds);
19200
- const sig = await this.program.methods.cancelOrder(nonce).accounts({
19201
- maker: this.walletPubkey,
19202
- orderRecord,
19203
- systemProgram: SystemProgram.programId
19204
- }).rpc();
19205
- return { signature: sig };
19206
- }
19207
- // ─── Admin ───────────────────────────────────────────────────────────────────
19208
- async initialize(operators, feeRecipient, feeRateBps) {
19209
- const sig = await this.program.methods.initializeClob(
19210
- operators,
19211
- feeRecipient,
19212
- feeRateBps,
19213
- this.programIds.conditionalTokens
19214
- ).accounts({
19215
- owner: this.walletPubkey,
19216
- clobConfig: this.configPda(),
19217
- systemProgram: SystemProgram.programId
19218
- }).rpc();
19219
- return { signature: sig };
19220
- }
19221
- async addOperator(operator) {
19222
- const sig = await this.program.methods.addOperator(operator).accounts({
19223
- admin: this.walletPubkey,
19224
- clobConfig: this.configPda()
19225
- }).rpc();
19226
- return { signature: sig };
19227
- }
19228
- async removeOperator(operator) {
19229
- const sig = await this.program.methods.removeOperator(operator).accounts({
19230
- admin: this.walletPubkey,
19231
- clobConfig: this.configPda()
19232
- }).rpc();
19233
- return { signature: sig };
19234
- }
19235
- async setPaused(paused) {
19236
- const sig = await this.program.methods.setPaused(paused).accounts({
19237
- admin: this.walletPubkey,
19238
- clobConfig: this.configPda()
19239
- }).rpc();
19240
- return { signature: sig };
19241
- }
19242
- async forceResetClob(programData, newAdmin, newOperators, newFeeRecipient, newFeeRateBps) {
19243
- const sig = await this.program.methods.forceResetClob(newAdmin, newOperators, newFeeRecipient, newFeeRateBps).accounts({
19244
- upgradeAuthority: this.walletPubkey,
19245
- programData,
19246
- clobConfig: this.configPda()
19247
- }).rpc();
19248
- return { signature: sig };
19249
- }
19250
- // ─── Match instructions ───────────────────────────────────────────────────────
19251
- /**
19252
- * Build match_complementary instruction (no Ed25519 — OrderRecord PDAs used).
19253
- *
19254
- * remaining_accounts layout:
19255
- * [seller, order_record, seller_token, seller_collateral, seller_position, sell_status] × N
19256
- * [extraAccountMetaList, hookConfig, hookProgram]
19257
- * [feeManagement, fee_config, mkt_override, company_ata, oracle_vault] (when fee > 0)
19258
- */
19259
- async buildMatchComplementaryIxs(takerSigned, makersSigned, collateralMint, feeRecipient, operator, opts, useTakerPrice = false, skipCrossingCheck = true) {
19260
- const condition = takerSigned.order.condition;
19261
- const tokenId = takerSigned.order.tokenId;
19262
- const taker = takerSigned.order.maker;
19263
- const takerNonce = takerSigned.order.nonce;
19264
- const fillAmount = opts?.fillAmount ?? new anchor6.BN("18446744073709551615");
19265
- const [outcomeMint] = tokenId === 1 ? PDA.yesMint(condition, this.programIds) : PDA.noMint(condition, this.programIds);
19266
- const [takerOrderRecord] = PDA.orderRecord(taker, takerNonce, this.programIds);
19267
- const takerCollateral = getAssociatedTokenAddressSync(collateralMint, taker);
19268
- const takerTokenAccount = getAssociatedTokenAddressSync(outcomeMint, taker, false, TOKEN_2022_PROGRAM_ID);
19269
- const [takerPosition] = PDA.position(condition, tokenId, taker, this.programIds);
19270
- const [takerOrderStatus] = PDA.orderStatus(taker, takerNonce, this.programIds);
19271
- const makerAccounts = [];
19272
- for (const ms of makersSigned) {
19273
- const seller = ms.order.maker;
19274
- const [sellerRecord] = PDA.orderRecord(seller, ms.order.nonce, this.programIds);
19275
- const sellerToken = getAssociatedTokenAddressSync(outcomeMint, seller, false, TOKEN_2022_PROGRAM_ID);
19276
- const sellerCollateral = getAssociatedTokenAddressSync(collateralMint, seller);
19277
- const [sellerPosition] = PDA.position(condition, tokenId, seller, this.programIds);
19278
- const [sellerStatus] = PDA.orderStatus(seller, ms.order.nonce, this.programIds);
19279
- makerAccounts.push(
19280
- { pubkey: seller, isSigner: false, isWritable: false },
19281
- { pubkey: sellerRecord, isSigner: false, isWritable: false },
19282
- { pubkey: sellerToken, isSigner: false, isWritable: true },
19283
- { pubkey: sellerCollateral, isSigner: false, isWritable: true },
19284
- { pubkey: sellerPosition, isSigner: false, isWritable: true },
19285
- { pubkey: sellerStatus, isSigner: false, isWritable: true }
19286
- );
19287
- }
19288
- const [extraAccountMetaList] = PDA.extraAccountMetaList(outcomeMint, this.programIds);
19289
- const [hookConfig] = PDA.hookConfig(this.programIds);
19290
- const hookAccounts = [
19291
- { pubkey: extraAccountMetaList, isSigner: false, isWritable: false },
19292
- { pubkey: hookConfig, isSigner: false, isWritable: false },
19293
- { pubkey: this.programIds.hook, isSigner: false, isWritable: false }
19294
- ];
19295
- let feeAccounts = [];
19296
- if (takerSigned.order.fee.gtn(0) && this.programIds.feeManagement && this.feeConfigOwner) {
19297
- const companyAddr = await this.companyAddress();
19298
- const refVault = await this.referralVault();
19299
- if (companyAddr && refVault) {
19300
- const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
19301
- const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
19302
- if (feeOverrideExists) {
19303
- const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? this.walletPubkey;
19304
- feeAccounts = [
19305
- { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
19306
- { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
19307
- { pubkey: feeOverridePda, isSigner: false, isWritable: false },
19308
- { pubkey: getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
19309
- { pubkey: oracleVault, isSigner: false, isWritable: true },
19310
- { pubkey: refVault, isSigner: false, isWritable: true }
19311
- ];
19312
- }
19313
- }
19314
- }
19315
- const matchIx = await this.program.methods.matchComplementary(takerNonce, fillAmount, useTakerPrice, skipCrossingCheck).accounts({
19316
- operator,
19317
- payer: this.walletPubkey,
19318
- clobConfig: this.configPda(),
19319
- condition,
19320
- taker,
19321
- takerOrderRecord,
19322
- takerCollateral,
19323
- takerTokenAccount,
19324
- takerPosition,
19325
- takerOrderStatus,
19326
- feeRecipient,
19327
- outcomeMint,
19328
- conditionalTokensProgram: this.programIds.conditionalTokens,
19329
- tokenProgram: TOKEN_PROGRAM_ID,
19330
- token2022Program: TOKEN_2022_PROGRAM_ID,
19331
- systemProgram: SystemProgram.programId
19332
- }).remainingAccounts([...makerAccounts, ...hookAccounts, ...feeAccounts]).instruction();
19333
- return [matchIx];
19334
- }
19335
- /**
19336
- * COMPLEMENTARY: 1 taker (BUY) + N makers (SELL), same tokenId.
19337
- * Phase 1: register taker + all makers in parallel.
19338
- * Phase 2: 1 atomic match tx.
19339
- */
19340
- async matchComplementary(takerSigned, makersSigned, collateralMint, feeRecipient, operatorWallet, lookupTable, opts) {
19341
- await Promise.all([
19342
- this.registerOrderIfNeeded(takerSigned),
19343
- ...makersSigned.map((m) => this.registerOrderIfNeeded(m))
19344
- ]);
19345
- const ixs = await this.buildMatchComplementaryIxs(
19346
- takerSigned,
19347
- makersSigned,
19348
- collateralMint,
19349
- feeRecipient,
19350
- operatorWallet.publicKey,
19351
- opts
19352
- );
19353
- const sig = await this.sendMatchTx(ixs, lookupTable, operatorWallet);
19354
- return { signature: sig };
19355
- }
19356
- /**
19357
- * 1 SELL taker vs N BUY makers.
19358
- * Program now supports SELL-as-taker natively → 1 instruction with all BUY makers
19359
- * in remaining_accounts (same structure as BUY taker case).
19360
- */
19361
- async matchComplementarySellVsMultiBuy(sellTaker, buyMakers, collateralMint, feeRecipient, operatorWallet, lookupTable, opts) {
19362
- if (buyMakers.length === 0) {
19363
- throw new InvalidParamError("COMPLEMENTARY: no BUY maker orders provided");
19364
- }
19365
- const sell = sellTaker.order;
19366
- const totalBuyPayment = buyMakers.reduce(
19367
- (acc, b) => acc + BigInt(b.order.makerAmount.toString()),
19368
- BigInt(0)
19369
- );
19370
- const sellExpected = BigInt(sell.takerAmount.toString());
19371
- if (totalBuyPayment < sellExpected) {
19372
- throw new InvalidParamError(
19373
- `COMPLEMENTARY: total buyer payments ${totalBuyPayment} < seller expected ${sellExpected}`
19374
- );
19375
- }
19376
- await Promise.all([
19377
- this.registerOrderIfNeeded(sellTaker),
19378
- ...buyMakers.map((m) => this.registerOrderIfNeeded(m))
19379
- ]);
19380
- const ixs = await this.buildMatchComplementaryIxs(
19381
- sellTaker,
19382
- buyMakers,
19383
- collateralMint,
19384
- feeRecipient,
19385
- operatorWallet.publicKey,
19386
- opts,
19387
- false
19388
- );
19389
- const sig = await this.sendMatchTx(ixs, lookupTable, operatorWallet);
19390
- return { signature: sig };
19391
- }
19392
- /**
19393
- * MINT: 1 YES buyer (taker) + N NO buyers (makers).
19394
- * Phase 1: register taker + all NO makers in parallel.
19395
- * Phase 2: 1 atomic match tx.
19396
- *
19397
- * remaining_accounts per NO maker (5):
19398
- * [order_record, buyer_no_token, buyer_no_collateral, buyer_no_position, no_order_status]
19399
- */
19400
- /** Build the match_mint_orders instruction (no signing, no sending). */
19401
- async _buildMintIx(yesSigned, noMakers, collateralMint, operator, payer) {
19402
- const condition = yesSigned.order.condition;
19403
- const taker = yesSigned.order.maker;
19404
- const takerNonce = yesSigned.order.nonce;
19405
- const fillAmount = new anchor6.BN("18446744073709551615");
19406
- const clobConfig = this.configPda();
19407
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19408
- const [noMint] = PDA.noMint(condition, this.programIds);
19409
- const [mintAuthority] = PDA.mintAuthority(condition, this.programIds);
19410
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
19411
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
19412
- const [takerOrderRecord] = PDA.orderRecord(taker, takerNonce, this.programIds);
19413
- const takerCollateral = getAssociatedTokenAddressSync(collateralMint, taker);
19414
- const takerYesToken = getAssociatedTokenAddressSync(yesMint, taker, false, TOKEN_2022_PROGRAM_ID);
19415
- const [takerYesPosition] = PDA.position(condition, 1, taker, this.programIds);
19416
- const [takerOrderStatus] = PDA.orderStatus(taker, takerNonce, this.programIds);
19417
- const clobUsdcAta = getAssociatedTokenAddressSync(collateralMint, clobConfig, true);
19418
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19419
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19420
- const [clobYesPos] = PDA.position(condition, 1, clobConfig, this.programIds);
19421
- const [clobNoPos] = PDA.position(condition, 0, clobConfig, this.programIds);
19422
- const [extraMetaYes] = PDA.extraAccountMetaList(yesMint, this.programIds);
19423
- const [extraMetaNo] = PDA.extraAccountMetaList(noMint, this.programIds);
19424
- const [hookConfig] = PDA.hookConfig(this.programIds);
19425
- const hookProgram = this.programIds.hook;
19426
- const remainingAccounts = [];
19427
- for (const nm of noMakers) {
19428
- const noMaker = nm.order.maker;
19429
- const [noRecord] = PDA.orderRecord(noMaker, nm.order.nonce, this.programIds);
19430
- const noToken = getAssociatedTokenAddressSync(noMint, noMaker, false, TOKEN_2022_PROGRAM_ID);
19431
- const noCollateral = getAssociatedTokenAddressSync(collateralMint, noMaker);
19432
- const [noPosition] = PDA.position(condition, 0, noMaker, this.programIds);
19433
- const [noStatus] = PDA.orderStatus(noMaker, nm.order.nonce, this.programIds);
19434
- remainingAccounts.push(
19435
- { pubkey: noMaker, isSigner: false, isWritable: false },
19436
- { pubkey: noRecord, isSigner: false, isWritable: false },
19437
- { pubkey: noToken, isSigner: false, isWritable: true },
19438
- { pubkey: noCollateral, isSigner: false, isWritable: true },
19439
- { pubkey: noPosition, isSigner: false, isWritable: true },
19440
- { pubkey: noStatus, isSigner: false, isWritable: true }
19441
- );
19442
- }
19443
- remainingAccounts.push(
19444
- { pubkey: extraMetaYes, isSigner: false, isWritable: false },
19445
- { pubkey: extraMetaNo, isSigner: false, isWritable: false },
19446
- { pubkey: hookConfig, isSigner: false, isWritable: false },
19447
- { pubkey: hookProgram, isSigner: false, isWritable: false }
19448
- );
19449
- return this.program.methods.matchMintOrders(takerNonce, fillAmount, true).accounts({
19450
- operator,
19451
- payer,
19452
- clobConfig,
19453
- condition,
19454
- taker,
19455
- takerOrderRecord,
19456
- takerCollateral,
19457
- takerYesToken,
19458
- takerYesPosition,
19459
- takerOrderStatus,
19460
- clobUsdcAta,
19461
- clobYesAta,
19462
- clobNoAta,
19463
- clobYesPosition: clobYesPos,
19464
- clobNoPosition: clobNoPos,
19465
- collateralVault,
19466
- vaultTokenAccount,
19467
- collateralMint,
19468
- yesMint,
19469
- noMint,
19470
- mintAuthority,
19471
- clobAuthority: clobConfig,
19472
- conditionalTokensProgram: this.programIds.conditionalTokens,
19473
- collateralTokenProgram: TOKEN_PROGRAM_ID,
19474
- token2022Program: TOKEN_2022_PROGRAM_ID,
19475
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
19476
- systemProgram: SystemProgram.programId
19477
- }).remainingAccounts(remainingAccounts).instruction();
19478
- }
19479
- async matchMintOrders(yesSigned, noMakers, collateralMint, _feeRecipient, operatorWallet, lookupTable) {
19480
- await Promise.all([
19481
- this.registerOrderIfNeeded(yesSigned),
19482
- ...noMakers.map((m) => this.registerOrderIfNeeded(m))
19483
- ]);
19484
- const condition = yesSigned.order.condition;
19485
- const clobConfig = this.configPda();
19486
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19487
- const [noMint] = PDA.noMint(condition, this.programIds);
19488
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19489
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19490
- const matchIx = await this._buildMintIx(yesSigned, noMakers, collateralMint, operatorWallet.publicKey, this.walletPubkey);
19491
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
19492
- const sig = await this.sendMatchTx([matchIx], lookupTable, operatorWallet);
19493
- return { signature: sig };
19494
- }
19495
- /**
19496
- * MERGE: 1 YES seller (taker) + N NO sellers (makers).
19497
- * Phase 1: register taker + all NO makers in parallel.
19498
- * Phase 2: 1 atomic match tx.
19499
- *
19500
- * remaining_accounts per NO maker (5):
19501
- * [order_record, seller_no_token, seller_no_collateral, seller_no_position, no_order_status]
19502
- * After all makers, optional 5 fee accounts.
19503
- */
19504
- /** Build the match_merge_orders instruction (no signing, no sending). */
19505
- async _buildMergeIx(yesSigned, noMakers, collateralMint, operator, payer, opts) {
19506
- const condition = yesSigned.order.condition;
19507
- const sellerYes = yesSigned.order.maker;
19508
- const takerNonce = yesSigned.order.nonce;
19509
- const fillAmount = new anchor6.BN("18446744073709551615");
19510
- const clobConfig = this.configPda();
19511
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19512
- const [noMint] = PDA.noMint(condition, this.programIds);
19513
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
19514
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
19515
- const [takerOrderRecord] = PDA.orderRecord(sellerYes, takerNonce, this.programIds);
19516
- const takerYesToken = getAssociatedTokenAddressSync(yesMint, sellerYes, false, TOKEN_2022_PROGRAM_ID);
19517
- const [takerYesPosition] = PDA.position(condition, 1, sellerYes, this.programIds);
19518
- const takerCollateral = getAssociatedTokenAddressSync(collateralMint, sellerYes);
19519
- const [takerOrderStatus] = PDA.orderStatus(sellerYes, takerNonce, this.programIds);
19520
- const clobUsdcAta = getAssociatedTokenAddressSync(collateralMint, clobConfig, true);
19521
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19522
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19523
- const [clobYesPos] = PDA.position(condition, 1, clobConfig, this.programIds);
19524
- const [clobNoPos] = PDA.position(condition, 0, clobConfig, this.programIds);
19525
- const [extraMetaYes] = PDA.extraAccountMetaList(yesMint, this.programIds);
19526
- const [extraMetaNo] = PDA.extraAccountMetaList(noMint, this.programIds);
19527
- const [hookConfig] = PDA.hookConfig(this.programIds);
19528
- const hookProgram = this.programIds.hook;
19529
- const remainingAccounts = [];
19530
- for (const nm of noMakers) {
19531
- const noMaker = nm.order.maker;
19532
- const [noRecord] = PDA.orderRecord(noMaker, nm.order.nonce, this.programIds);
19533
- const noToken = getAssociatedTokenAddressSync(noMint, noMaker, false, TOKEN_2022_PROGRAM_ID);
19534
- const noCollateral = getAssociatedTokenAddressSync(collateralMint, noMaker);
19535
- const [noPosition] = PDA.position(condition, 0, noMaker, this.programIds);
19536
- const [noStatus] = PDA.orderStatus(noMaker, nm.order.nonce, this.programIds);
19537
- remainingAccounts.push(
19538
- { pubkey: noMaker, isSigner: false, isWritable: false },
19539
- { pubkey: noRecord, isSigner: false, isWritable: false },
19540
- { pubkey: noToken, isSigner: false, isWritable: true },
19541
- { pubkey: noCollateral, isSigner: false, isWritable: true },
19542
- { pubkey: noPosition, isSigner: false, isWritable: true },
19543
- { pubkey: noStatus, isSigner: false, isWritable: true }
19544
- );
19545
- }
19546
- remainingAccounts.push(
19547
- { pubkey: extraMetaYes, isSigner: false, isWritable: false },
19548
- { pubkey: extraMetaNo, isSigner: false, isWritable: false },
19549
- { pubkey: hookConfig, isSigner: false, isWritable: false },
19550
- { pubkey: hookProgram, isSigner: false, isWritable: false }
19551
- );
19552
- if (yesSigned.order.fee.gtn(0) && this.programIds.feeManagement && this.feeConfigOwner) {
19553
- const companyAddr = await this.companyAddress();
19554
- const refVault = await this.referralVault();
19555
- if (companyAddr && refVault) {
19556
- const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
19557
- const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
19558
- if (feeOverrideExists) {
19559
- const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? payer;
19560
- remainingAccounts.push(
19561
- { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
19562
- { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
19563
- { pubkey: feeOverridePda, isSigner: false, isWritable: false },
19564
- { pubkey: getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
19565
- { pubkey: oracleVault, isSigner: false, isWritable: true },
19566
- { pubkey: refVault, isSigner: false, isWritable: true }
19567
- );
19568
- }
19569
- }
19570
- }
19571
- return this.program.methods.matchMergeOrders(takerNonce, fillAmount, true).accounts({
19572
- operator,
19573
- payer,
19574
- clobConfig,
19575
- condition,
19576
- taker: sellerYes,
19577
- takerOrderRecord,
19578
- takerYesToken,
19579
- takerYesPosition,
19580
- takerCollateral,
19581
- takerOrderStatus,
19582
- clobUsdcAta,
19583
- clobYesAta,
19584
- clobNoAta,
19585
- clobYesPosition: clobYesPos,
19586
- clobNoPosition: clobNoPos,
19587
- collateralVault,
19588
- vaultTokenAccount,
19589
- collateralMint,
19590
- yesMint,
19591
- noMint,
19592
- clobAuthority: clobConfig,
19593
- conditionalTokensProgram: this.programIds.conditionalTokens,
19594
- collateralTokenProgram: TOKEN_PROGRAM_ID,
19595
- token2022Program: TOKEN_2022_PROGRAM_ID,
19596
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
19597
- systemProgram: SystemProgram.programId
19598
- }).remainingAccounts(remainingAccounts).instruction();
19599
- }
19600
- async matchMergeOrders(yesSigned, noMakers, collateralMint, _feeRecipient, operatorWallet, lookupTable, opts) {
19601
- await Promise.all([
19602
- this.registerOrderIfNeeded(yesSigned),
19603
- ...noMakers.map((m) => this.registerOrderIfNeeded(m))
19604
- ]);
19605
- const condition = yesSigned.order.condition;
19606
- const clobConfig = this.configPda();
19607
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19608
- const [noMint] = PDA.noMint(condition, this.programIds);
19609
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19610
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19611
- const matchIx = await this._buildMergeIx(yesSigned, noMakers, collateralMint, operatorWallet.publicKey, this.walletPubkey, opts);
19612
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
19613
- const sig = await this.sendMatchTx([matchIx], lookupTable, operatorWallet);
19614
- return { signature: sig };
19615
- }
19616
- /**
19617
- * Auto-detect match type and execute 2-phase:
19618
- * Phase 1 — register all orders (taker + makers) on-chain in parallel.
19619
- * Phase 2 — 1 atomic match transaction.
19620
- *
19621
- * Detection (pure, no RPC):
19622
- * taker.tokenId === makers[0].tokenId:
19623
- * taker BUY + all makers SELL → COMPLEMENTARY
19624
- * taker.tokenId !== makers[0].tokenId + all BUY → MINT
19625
- * taker.tokenId !== makers[0].tokenId + all SELL → MERGE
19626
- *
19627
- * @param opts.marketOracleVault Oracle vault ATA for presale markets.
19628
- * Omit for admin markets (payer used as placeholder).
19629
- */
19630
- async matchOrders(taker, makers, opts) {
19631
- if (makers.length === 0) throw new InvalidParamError("At least 1 maker required");
19632
- const collateralMint = this.networkConfig.defaultCollateral.mint;
19633
- const cfg = await this.fetchConfig();
19634
- if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
19635
- const feeRecipient = cfg.feeRecipient;
19636
- const operatorWallet = opts?.operatorWallet ?? this.provider.wallet;
19637
- const alt = await this.ensureAlt(
19638
- taker.order.condition,
19639
- collateralMint,
19640
- taker,
19641
- makers
19642
- );
19643
- const t = taker.order;
19644
- const m0 = makers[0].order;
19645
- const SIDE_BUY = 0;
19646
- const SIDE_SELL = 1;
19647
- if (t.tokenId === m0.tokenId) {
19648
- let buySignedOrder, sellCandidates;
19649
- if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
19650
- buySignedOrder = taker;
19651
- sellCandidates = makers;
19652
- } else if (t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_BUY)) {
19653
- return this.matchComplementarySellVsMultiBuy(taker, makers, collateralMint, feeRecipient, operatorWallet, alt, opts);
19654
- } else {
19655
- throw new InvalidParamError("COMPLEMENTARY requires one BUY and one or more SELLs on same tokenId");
19656
- }
19657
- const finalSells = sellCandidates;
19658
- return this.matchComplementary(buySignedOrder, finalSells, collateralMint, feeRecipient, operatorWallet, alt, opts);
19659
- }
19660
- const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
19661
- const allSell = t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_SELL);
19662
- if (!allBuy && !allSell) throw new InvalidParamError("MINT/MERGE: all orders must be same side");
19663
- const takerIsYes = t.tokenId === 1 && makers.every((m) => m.order.tokenId === 0);
19664
- const takerIsNo = t.tokenId === 0 && makers.every((m) => m.order.tokenId === 1);
19665
- if (!takerIsYes && !takerIsNo) throw new InvalidParamError("MINT/MERGE: orders must be complementary YES+NO pair");
19666
- if (takerIsYes) {
19667
- if (allBuy) return this.matchMintOrders(taker, makers, collateralMint, feeRecipient, operatorWallet, alt);
19668
- return this.matchMergeOrders(taker, makers, collateralMint, feeRecipient, operatorWallet, alt, opts);
19669
- }
19670
- let lastResult;
19671
- for (const yesMaker of makers) {
19672
- if (allBuy) lastResult = await this.matchMintOrders(yesMaker, [taker], collateralMint, feeRecipient, operatorWallet, alt);
19673
- else lastResult = await this.matchMergeOrders(yesMaker, [taker], collateralMint, feeRecipient, operatorWallet, alt, opts);
19674
- }
19675
- return lastResult;
19676
- }
19677
- /**
19678
- * High-level match: caller passes price + quantity + keypair — SDK builds,
19679
- * signs, and submits in one call. No manual amount calculation needed.
19680
- *
19681
- * Amounts are computed from each order's own limit `price` (percentage, e.g. 51 for 51%).
19682
- * NEVER pass averageMatchedPrice — that is an engine output, not an order input.
19683
- *
19684
- * @param taker { keypair, condition, tokenId, side, price, quantity, nonce?, expiry? }
19685
- * @param makers Array of same shape
19686
- * @param decimals Collateral decimals (default 9 for USDS)
19687
- */
19688
- async matchOrdersFromPrice(taker, makers, decimals = 9, opts) {
19689
- const nacl2 = await import('tweetnacl');
19690
- const buildSigned = (p) => {
19691
- const order = buildOrderFromPrice({
19692
- maker: p.keypair.publicKey,
19693
- condition: p.condition,
19694
- tokenId: p.tokenId,
19695
- side: p.side,
19696
- price: p.price,
19697
- quantity: p.quantity,
19698
- decimals,
19699
- nonce: p.nonce,
19700
- expiry: p.expiry,
19701
- fee: p.fee,
19702
- taker: p.taker
19703
- });
19704
- const msg = serializeOrderToBytes(order);
19705
- const sig = nacl2.default.sign.detached(msg, p.keypair.secretKey);
19706
- return { order, signature: sig };
19707
- };
19708
- const takerSigned = buildSigned(taker);
19709
- const sortedMakers = [...makers].sort((a, b) => {
19710
- const sameToken = a.tokenId === taker.tokenId && b.tokenId === taker.tokenId;
19711
- const isDirectBuy = taker.side === 0 && sameToken;
19712
- const isDirectSell = taker.side === 1 && sameToken;
19713
- const isMint = taker.side === 0 && taker.tokenId === 1;
19714
- const isMerge = taker.side === 1 && taker.tokenId === 1;
19715
- if (isDirectBuy) return a.price - b.price;
19716
- if (isDirectSell) return b.price - a.price;
19717
- if (isMint) return b.price - a.price;
19718
- if (isMerge) return a.price - b.price;
19719
- return 0;
19720
- });
19721
- const makersSigned = sortedMakers.map(buildSigned);
19722
- return this.matchOrders(takerSigned, makersSigned, opts);
19723
- }
19724
- /**
19725
- * Build an unsigned VersionedTransaction for a set of match instructions.
19726
- * Callers sign and send externally — mirrors createQuestionAdmin pattern.
19727
- */
19728
- async _buildUnsignedVtx(instructions, lookupTable, payer) {
19729
- const { connection } = this.provider;
19730
- const { blockhash } = await connection.getLatestBlockhash();
19731
- const cuLimit = ComputeBudgetProgram.setComputeUnitLimit({ units: 14e5 });
19732
- const heapFrame = ComputeBudgetProgram.requestHeapFrame({ bytes: 262144 });
19733
- const message = new TransactionMessage({
19734
- payerKey: payer,
19735
- recentBlockhash: blockhash,
19736
- instructions: [cuLimit, heapFrame, ...instructions]
19737
- }).compileToV0Message(lookupTable ? [lookupTable] : []);
19738
- return new VersionedTransaction(message);
19739
- }
19740
- /**
19741
- * Build unsigned VersionedTransaction(s) for matching orders.
19742
- *
19743
- * Mirrors createQuestionAdmin pattern — SDK registers orders internally but
19744
- * returns the match tx unsigned. BE signs with both keypairs and sends:
19745
- *
19746
- * const [vtx] = await sdk.clob.buildMatchOrdersTx(taker, makers, operatorPk, payerPk);
19747
- * vtx.sign([feePayerKeypair, operatorKeypair]);
19748
- * await connection.sendRawTransaction(vtx.serialize());
19749
- *
19750
- * All match types pack into a single VersionedTransaction — same as createQuestionAdmin pattern.
19751
- * NO-taker MINT/MERGE decomposes into N instructions but all packed in 1 tx.
19752
- *
19753
- * @param operator - Whitelisted CLOB operator pubkey (must sign)
19754
- * @param payer - Fee payer pubkey (must sign, pays tx fee + rent)
19755
- */
19756
- async buildMatchOrdersTx(taker, makers, operator, payer, opts) {
19757
- if (makers.length === 0) throw new InvalidParamError("At least 1 maker required");
19758
- const collateralMint = this.networkConfig.defaultCollateral.mint;
19759
- const cfg = await this.fetchConfig();
19760
- if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
19761
- const feeRecipient = cfg.feeRecipient;
19762
- const alt = await this.ensureAlt(taker.order.condition, collateralMint, taker, makers);
19763
- const t = taker.order;
19764
- const m0 = makers[0].order;
19765
- const SIDE_BUY = 0;
19766
- const SIDE_SELL = 1;
19767
- const hookInitIxs = [];
19768
- if (this.hookClient) {
19769
- const condition0 = t.condition;
19770
- const [ym] = PDA.yesMint(condition0, this.programIds);
19771
- const [nm] = PDA.noMint(condition0, this.programIds);
19772
- const [yIx, nIx] = await Promise.all([
19773
- this.hookClient.buildInitHookIxIfNeeded(ym, payer),
19774
- this.hookClient.buildInitHookIxIfNeeded(nm, payer)
19775
- ]);
19776
- if (yIx) hookInitIxs.push(yIx);
19777
- if (nIx) hookInitIxs.push(nIx);
19778
- }
19779
- const oracleVaultInitIx = await this.buildInitOracleVaultIfNeeded(
19780
- t.condition,
19781
- collateralMint,
19782
- t.fee,
19783
- payer
19784
- );
19785
- const preIxs = oracleVaultInitIx ? [...hookInitIxs, oracleVaultInitIx] : hookInitIxs;
19786
- if (t.tokenId === m0.tokenId) {
19787
- let buySignedOrder, sellCandidates;
19788
- if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
19789
- buySignedOrder = taker;
19790
- sellCandidates = makers;
19791
- } else if (t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_BUY)) {
19792
- await Promise.all([
19793
- this.registerOrderIfNeeded(taker),
19794
- ...makers.map((m) => this.registerOrderIfNeeded(m))
19795
- ]);
19796
- const ixs3 = await this.buildMatchComplementaryIxs(
19797
- taker,
19798
- makers,
19799
- collateralMint,
19800
- feeRecipient,
19801
- operator,
19802
- opts,
19803
- false
19804
- );
19805
- return this._buildUnsignedVtx([...preIxs, ...ixs3], alt, payer);
19806
- } else {
19807
- throw new InvalidParamError("COMPLEMENTARY requires one BUY and one or more SELLs on same tokenId");
19808
- }
19809
- await Promise.all([
19810
- this.registerOrderIfNeeded(buySignedOrder),
19811
- ...sellCandidates.map((m) => this.registerOrderIfNeeded(m))
19812
- ]);
19813
- const ixs2 = await this.buildMatchComplementaryIxs(
19814
- buySignedOrder,
19815
- sellCandidates,
19816
- collateralMint,
19817
- feeRecipient,
19818
- operator,
19819
- opts
19820
- );
19821
- return this._buildUnsignedVtx([...preIxs, ...ixs2], alt, payer);
19822
- }
19823
- const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
19824
- const allSell = t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_SELL);
19825
- if (!allBuy && !allSell) throw new InvalidParamError("MINT/MERGE: all orders must be same side");
19826
- const takerIsYes = t.tokenId === 1 && makers.every((m) => m.order.tokenId === 0);
19827
- const takerIsNo = t.tokenId === 0 && makers.every((m) => m.order.tokenId === 1);
19828
- if (!takerIsYes && !takerIsNo) throw new InvalidParamError("MINT/MERGE: orders must be complementary YES+NO pair");
19829
- const condition = t.condition;
19830
- const clobConfig = this.configPda();
19831
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19832
- const [noMint] = PDA.noMint(condition, this.programIds);
19833
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19834
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19835
- if (takerIsYes) {
19836
- await Promise.all([
19837
- this.registerOrderIfNeeded(taker),
19838
- ...makers.map((m) => this.registerOrderIfNeeded(m))
19839
- ]);
19840
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
19841
- const ix = allBuy ? await this._buildMintIx(taker, makers, collateralMint, operator, payer) : await this._buildMergeIx(taker, makers, collateralMint, operator, payer, opts);
19842
- return this._buildUnsignedVtx([...preIxs, ix], alt, payer);
19843
- }
19844
- await Promise.all([
19845
- this.registerOrderIfNeeded(taker),
19846
- ...makers.map((m) => this.registerOrderIfNeeded(m))
19847
- ]);
19848
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
19849
- const ixs = [];
19850
- for (const yesMaker of makers) {
19851
- const ix = allBuy ? await this._buildMintIx(yesMaker, [taker], collateralMint, operator, payer) : await this._buildMergeIx(yesMaker, [taker], collateralMint, operator, payer, opts);
19852
- ixs.push(ix);
19853
- }
19854
- return this._buildUnsignedVtx([...preIxs, ...ixs], alt, payer);
19855
- }
19856
- // ─── batchCollectRedeemEarly ─────────────────────────────────────────────────
19857
- /**
19858
- * Build VersionedTransaction for batchCollectRedeemEarly.
19859
- *
19860
- * Prerequisite: each user's CollectFeeOrder must be registered on-chain via
19861
- * buildRegisterCollectFeeOrderTx (one tx per user).
19862
- *
19863
- * Flow: batchCollectRedeemEarly ix reads CollectFeeOrderRecord PDAs — no Ed25519 inline.
19864
- * No tx-size limit from signatures, supports 10+ orders in one tx.
19865
- *
19866
- * @param signedOrders - Array of { order, signature } (used to derive record PDAs)
19867
- * @param condition - Market condition PDA
19868
- * @param outcomeIndex - 0 = NO wins, 1 = YES wins
19869
- * @param operator - Whitelisted operator pubkey (must sign)
19870
- * @param payer - Fee payer pubkey (must sign)
19871
- * @param opts.marketOracleVault - MarketOracle vault for fee distribution
19872
- */
19873
- async buildBatchCollectRedeemEarlyTx(signedOrders, condition, outcomeIndex, operator, payer, opts) {
19874
- if (signedOrders.length === 0) throw new InvalidParamError("At least 1 order required");
19875
- const collateralMint = this.networkConfig.defaultCollateral.mint;
19876
- const cfg = await this.fetchConfig();
19877
- if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
19878
- const clobConfig = this.configPda();
19879
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19880
- const [noMint] = PDA.noMint(condition, this.programIds);
19881
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
19882
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
19883
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19884
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
19885
- const outcomeMint = outcomeIndex === 1 ? yesMint : noMint;
19886
- const [extraAccountMeta] = PDA.extraAccountMetaList(outcomeMint, this.programIds);
19887
- const [hookConfig] = PDA.hookConfig(this.programIds);
19888
- const [clobYesPosition] = PDA.position(condition, 1, clobConfig, this.programIds);
19889
- const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
19890
- const userAccounts = [];
19891
- for (const { order } of signedOrders) {
19892
- const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
19893
- const userTokenAta = getAssociatedTokenAddressSync(outcomeMint, order.user, false, TOKEN_2022_PROGRAM_ID);
19894
- const [userPosition] = PDA.position(condition, outcomeIndex, order.user, this.programIds);
19895
- userAccounts.push(
19896
- { pubkey: collectFeeOrderRecord, isSigner: false, isWritable: true },
19897
- { pubkey: order.user, isSigner: false, isWritable: false },
19898
- { pubkey: userTokenAta, isSigner: false, isWritable: true },
19899
- { pubkey: userPosition, isSigner: false, isWritable: true }
19900
- );
19901
- }
19902
- const hookAccounts = [
19903
- { pubkey: extraAccountMeta, isSigner: false, isWritable: false },
19904
- { pubkey: hookConfig, isSigner: false, isWritable: false },
19905
- { pubkey: this.programIds.hook, isSigner: false, isWritable: false }
19906
- ];
19907
- let feeAccounts = [];
19908
- if (this.programIds.feeManagement && this.feeConfigOwner) {
19909
- const companyAddr = await this.companyAddress();
19910
- const refVault = await this.referralVault();
19911
- if (companyAddr && refVault) {
19912
- const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
19913
- const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
19914
- if (feeOverrideExists) {
19915
- const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? payer;
19916
- feeAccounts = [
19917
- { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
19918
- { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
19919
- { pubkey: feeOverridePda, isSigner: false, isWritable: false },
19920
- { pubkey: getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
19921
- { pubkey: oracleVault, isSigner: false, isWritable: true },
19922
- { pubkey: refVault, isSigner: false, isWritable: true }
19923
- ];
19924
- }
19925
- }
19926
- }
19927
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
19928
- const hookInitIxs = [];
19929
- if (this.hookClient) {
19930
- const ix = await this.hookClient.buildInitHookIxIfNeeded(outcomeMint, payer);
19931
- if (ix) hookInitIxs.push(ix);
19932
- }
19933
- const collectIx = await this.program.methods.batchCollectRedeemEarly(
19934
- signedOrders.length,
19935
- outcomeIndex
19936
- ).accounts({
19937
- operator,
19938
- payer,
19939
- clobConfig,
19940
- condition,
19941
- collateralVault,
19942
- vaultTokenAccount,
19943
- feeRecipient: cfg.feeRecipient,
19944
- yesMint,
19945
- noMint,
19946
- clobYesAta,
19947
- clobNoAta,
19948
- clobYesPosition,
19949
- clobNoPosition,
19950
- conditionalTokensProgram: this.programIds.conditionalTokens,
19951
- tokenProgram: TOKEN_PROGRAM_ID,
19952
- token2022Program: TOKEN_2022_PROGRAM_ID,
19953
- systemProgram: SystemProgram.programId
19954
- }).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
19955
- const alt = opts?.lookupTable ?? await this.buildAltForCollectBatch(condition, payer, collateralMint, signedOrders, outcomeIndex);
19956
- return this._buildUnsignedVtx([...hookInitIxs, collectIx], alt, payer);
19957
- }
19958
- // ─── Register RedeemFeeOrder ─────────────────────────────────────────────────
19959
- /**
19960
- * Build a Transaction to register a RedeemFeeOrder on-chain (Ed25519 verify + PDA).
19961
- * Must be sent before calling buildBatchRedeemWithFeeTx / buildBatchMergeWithFeeTx.
19962
- *
19963
- * ix[0]: batched Ed25519 { pubkey=order.user, sig, msg=128 bytes }
19964
- * ix[1]: register_redeem_fee_order(nonce)
19965
- */
19966
- async buildRegisterRedeemFeeOrderTx(signedOrder, payer) {
19967
- const { order } = signedOrder;
19968
- const [redeemFeeOrderRecord] = PDA.redeemFeeOrderRecord(order.user, order.nonce, this.programIds);
19969
- const ed25519Ix = buildBatchedRedeemFeeEd25519Instruction([signedOrder]);
19970
- const registerIx = await this.program.methods.registerRedeemFeeOrder(order.nonce).accounts({
19971
- payer,
19972
- clobConfig: this.configPda(),
19973
- ixSysvar: IX_SYSVAR,
19974
- orderSigner: order.user,
19975
- redeemFeeOrderRecord,
19976
- systemProgram: anchor6.web3.SystemProgram.programId
19977
- }).instruction();
19978
- const tx = new anchor6.web3.Transaction();
19979
- tx.add(ed25519Ix, registerIx);
19980
- return tx;
19981
- }
19982
- // ─── batchRedeemWithFee ───────────────────────────────────────────────────────
19983
- /**
19984
- * Build a VersionedTransaction for batchRedeemWithFee.
19985
- * Each pair has a YES order + NO order for the same user (same signer or different).
19986
- * After resolution: YES holder taker_amount > 0, NO holder taker_amount = 0 (or vice versa).
19987
- *
19988
- * Prerequisite: each order's RedeemFeeOrderRecord must be registered via buildRegisterRedeemFeeOrderTx.
19989
- */
19990
- async buildBatchRedeemWithFeeTx(orderPairs, condition, operator, payer, opts) {
19991
- if (orderPairs.length === 0) throw new InvalidParamError("At least 1 order pair required");
19992
- const collateralMint = this.networkConfig.defaultCollateral.mint;
19993
- const cfg = await this.fetchConfig();
19994
- if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
19995
- const clobConfig = this.configPda();
19996
- const [yesMint] = PDA.yesMint(condition, this.programIds);
19997
- const [noMint] = PDA.noMint(condition, this.programIds);
19998
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
19999
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
20000
- const [yesExtraMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
20001
- const [noExtraMeta] = PDA.extraAccountMetaList(noMint, this.programIds);
20002
- const [hookConfig] = PDA.hookConfig(this.programIds);
20003
- const [clobYesPosition] = PDA.position(condition, 1, clobConfig, this.programIds);
20004
- const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
20005
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
20006
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
20007
- const userAccounts = [];
20008
- for (const { yes, no } of orderPairs) {
20009
- const [yesRecord] = PDA.redeemFeeOrderRecord(yes.order.user, yes.order.nonce, this.programIds);
20010
- const [noRecord] = PDA.redeemFeeOrderRecord(no.order.user, no.order.nonce, this.programIds);
20011
- const userYesAta = getAssociatedTokenAddressSync(yesMint, yes.order.user, false, TOKEN_2022_PROGRAM_ID);
20012
- const userNoAta = getAssociatedTokenAddressSync(noMint, no.order.user, false, TOKEN_2022_PROGRAM_ID);
20013
- const [userYesPos] = PDA.position(condition, 1, yes.order.user, this.programIds);
20014
- const [userNoPos] = PDA.position(condition, 0, no.order.user, this.programIds);
20015
- const yesCollateral = getAssociatedTokenAddressSync(collateralMint, yes.order.user);
20016
- const noCollateral = getAssociatedTokenAddressSync(collateralMint, no.order.user);
20017
- userAccounts.push(
20018
- { pubkey: yesRecord, isSigner: false, isWritable: true },
20019
- { pubkey: noRecord, isSigner: false, isWritable: true },
20020
- { pubkey: yes.order.user, isSigner: false, isWritable: false },
20021
- { pubkey: no.order.user, isSigner: false, isWritable: false },
20022
- { pubkey: userYesAta, isSigner: false, isWritable: true },
20023
- { pubkey: userNoAta, isSigner: false, isWritable: true },
20024
- { pubkey: userYesPos, isSigner: false, isWritable: true },
20025
- { pubkey: userNoPos, isSigner: false, isWritable: true },
20026
- { pubkey: yesCollateral, isSigner: false, isWritable: true },
20027
- { pubkey: noCollateral, isSigner: false, isWritable: true }
20028
- );
20029
- }
20030
- const hookAccounts = [
20031
- { pubkey: yesExtraMeta, isSigner: false, isWritable: false },
20032
- { pubkey: noExtraMeta, isSigner: false, isWritable: false },
20033
- { pubkey: hookConfig, isSigner: false, isWritable: false },
20034
- { pubkey: this.programIds.hook, isSigner: false, isWritable: false }
20035
- ];
20036
- let feeAccounts = [];
20037
- if (this.programIds.feeManagement && this.feeConfigOwner) {
20038
- const companyAddr = await this.companyAddress();
20039
- const refVault = await this.referralVault();
20040
- if (companyAddr && refVault) {
20041
- const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
20042
- const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
20043
- if (feeOverrideExists) {
20044
- const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? payer;
20045
- feeAccounts = [
20046
- { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
20047
- { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
20048
- { pubkey: feeOverridePda, isSigner: false, isWritable: false },
20049
- { pubkey: getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
20050
- { pubkey: oracleVault, isSigner: false, isWritable: true },
20051
- { pubkey: refVault, isSigner: false, isWritable: true }
20052
- ];
20053
- }
20054
- }
20055
- }
20056
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
20057
- const hookInitIxs = [];
20058
- if (this.hookClient) {
20059
- for (const mint of [yesMint, noMint]) {
20060
- const ix = await this.hookClient.buildInitHookIxIfNeeded(mint, payer);
20061
- if (ix) hookInitIxs.push(ix);
20062
- }
20063
- }
20064
- const redeemIx = await this.program.methods.batchRedeemWithFee(orderPairs.length).accounts({
20065
- operator,
20066
- payer,
20067
- clobConfig,
20068
- condition,
20069
- collateralVault,
20070
- vaultTokenAccount,
20071
- feeRecipient: cfg.feeRecipient,
20072
- yesMint,
20073
- noMint,
20074
- clobYesAta,
20075
- clobNoAta,
20076
- clobYesPosition,
20077
- clobNoPosition,
20078
- conditionalTokensProgram: this.programIds.conditionalTokens,
20079
- tokenProgram: TOKEN_PROGRAM_ID,
20080
- token2022Program: TOKEN_2022_PROGRAM_ID,
20081
- systemProgram: anchor6.web3.SystemProgram.programId
20082
- }).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
20083
- const alt = opts?.lookupTable ?? await this.buildAltForCondition(condition, payer, collateralMint);
20084
- return this._buildUnsignedVtx([...hookInitIxs, redeemIx], alt, payer);
20085
- }
20086
- // ─── batchMergeWithFee ────────────────────────────────────────────────────────
20087
- /**
20088
- * Build a VersionedTransaction for batchMergeWithFee (pre-resolution merge with fee).
20089
- * Each pair: YES order + NO order with equal amounts for the same user.
20090
- * Only yes_signer receives net USDS (mirrors EVM _matchMergeOrder).
20091
- *
20092
- * Prerequisite: each order's RedeemFeeOrderRecord must be registered via buildRegisterRedeemFeeOrderTx.
20093
- */
20094
- async buildBatchMergeWithFeeTx(orderPairs, condition, operator, payer, opts) {
20095
- if (orderPairs.length === 0) throw new InvalidParamError("At least 1 order pair required");
20096
- const collateralMint = this.networkConfig.defaultCollateral.mint;
20097
- const cfg = await this.fetchConfig();
20098
- if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
20099
- const clobConfig = this.configPda();
20100
- const [yesMint] = PDA.yesMint(condition, this.programIds);
20101
- const [noMint] = PDA.noMint(condition, this.programIds);
20102
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
20103
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
20104
- const [yesExtraMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
20105
- const [noExtraMeta] = PDA.extraAccountMetaList(noMint, this.programIds);
20106
- const [hookConfig] = PDA.hookConfig(this.programIds);
20107
- const [clobYesPosition] = PDA.position(condition, 1, clobConfig, this.programIds);
20108
- const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
20109
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
20110
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfig, true, TOKEN_2022_PROGRAM_ID);
20111
- const userAccounts = [];
20112
- for (const { yes, no } of orderPairs) {
20113
- const [yesRecord] = PDA.redeemFeeOrderRecord(yes.order.user, yes.order.nonce, this.programIds);
20114
- const [noRecord] = PDA.redeemFeeOrderRecord(no.order.user, no.order.nonce, this.programIds);
20115
- const userYesAta = getAssociatedTokenAddressSync(yesMint, yes.order.user, false, TOKEN_2022_PROGRAM_ID);
20116
- const userNoAta = getAssociatedTokenAddressSync(noMint, no.order.user, false, TOKEN_2022_PROGRAM_ID);
20117
- const [userYesPos] = PDA.position(condition, 1, yes.order.user, this.programIds);
20118
- const [userNoPos] = PDA.position(condition, 0, no.order.user, this.programIds);
20119
- const yesCollateral = getAssociatedTokenAddressSync(collateralMint, yes.order.user);
20120
- userAccounts.push(
20121
- { pubkey: yesRecord, isSigner: false, isWritable: true },
20122
- { pubkey: noRecord, isSigner: false, isWritable: true },
20123
- { pubkey: yes.order.user, isSigner: false, isWritable: false },
20124
- { pubkey: no.order.user, isSigner: false, isWritable: false },
20125
- { pubkey: userYesAta, isSigner: false, isWritable: true },
20126
- { pubkey: userNoAta, isSigner: false, isWritable: true },
20127
- { pubkey: userYesPos, isSigner: false, isWritable: true },
20128
- { pubkey: userNoPos, isSigner: false, isWritable: true },
20129
- { pubkey: yesCollateral, isSigner: false, isWritable: true }
20130
- );
20131
- }
20132
- const hookAccounts = [
20133
- { pubkey: yesExtraMeta, isSigner: false, isWritable: false },
20134
- { pubkey: noExtraMeta, isSigner: false, isWritable: false },
20135
- { pubkey: hookConfig, isSigner: false, isWritable: false },
20136
- { pubkey: this.programIds.hook, isSigner: false, isWritable: false }
20137
- ];
20138
- let feeAccounts = [];
20139
- if (this.programIds.feeManagement && this.feeConfigOwner) {
20140
- const companyAddr = await this.companyAddress();
20141
- const refVault = await this.referralVault();
20142
- if (companyAddr && refVault) {
20143
- const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
20144
- const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
20145
- if (feeOverrideExists) {
20146
- const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? payer;
20147
- feeAccounts = [
20148
- { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
20149
- { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
20150
- { pubkey: feeOverridePda, isSigner: false, isWritable: false },
20151
- { pubkey: getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
20152
- { pubkey: oracleVault, isSigner: false, isWritable: true },
20153
- { pubkey: refVault, isSigner: false, isWritable: true }
20154
- ];
20155
- }
20156
- }
20157
- }
20158
- await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
20159
- const hookInitIxs = [];
20160
- if (this.hookClient) {
20161
- for (const mint of [yesMint, noMint]) {
20162
- const ix = await this.hookClient.buildInitHookIxIfNeeded(mint, payer);
20163
- if (ix) hookInitIxs.push(ix);
20164
- }
20165
- }
20166
- const mergeIx = await this.program.methods.batchMergeWithFee(orderPairs.length).accounts({
20167
- operator,
20168
- payer,
20169
- clobConfig,
20170
- condition,
20171
- collateralVault,
20172
- vaultTokenAccount,
20173
- feeRecipient: cfg.feeRecipient,
20174
- yesMint,
20175
- noMint,
20176
- clobYesAta,
20177
- clobNoAta,
20178
- clobYesPosition,
20179
- clobNoPosition,
20180
- conditionalTokensProgram: this.programIds.conditionalTokens,
20181
- tokenProgram: TOKEN_PROGRAM_ID,
20182
- token2022Program: TOKEN_2022_PROGRAM_ID,
20183
- systemProgram: anchor6.web3.SystemProgram.programId
20184
- }).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
20185
- const alt = opts?.lookupTable ?? await this.buildAltForCondition(condition, payer, collateralMint);
20186
- return this._buildUnsignedVtx([...hookInitIxs, mergeIx], alt, payer);
20187
- }
20188
- /**
20189
- * Build ALT for batchCollectRedeemEarly — includes per-user accounts so all
20190
- * remaining_accounts fit as 1-byte ALT indices instead of 32-byte inline addresses.
20191
- */
20192
- async buildAltForCollectBatch(condition, payer, collateralMint, signedOrders, outcomeIndex) {
20193
- const userKeys = signedOrders.map((o) => o.order.user.toBase58()).sort((a, b) => a.localeCompare(b)).join(",");
20194
- const cacheKey = `${condition.toBase58()}:collect:${userKeys}`;
20195
- if (this._altCache.has(cacheKey)) return this._altCache.get(cacheKey);
20196
- const { connection } = this.provider;
20197
- const clobConfigPda = this.configPda();
20198
- const [yesMint] = PDA.yesMint(condition, this.programIds);
20199
- const [noMint] = PDA.noMint(condition, this.programIds);
20200
- const outcomeMint = outcomeIndex === 1 ? yesMint : noMint;
20201
- const [extraAccountMeta] = PDA.extraAccountMetaList(outcomeMint, this.programIds);
20202
- const [hookConfig] = PDA.hookConfig(this.programIds);
20203
- const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
20204
- const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
20205
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, TOKEN_2022_PROGRAM_ID);
20206
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfigPda, true, TOKEN_2022_PROGRAM_ID);
20207
- const [clobYesPos] = PDA.position(condition, 1, clobConfigPda, this.programIds);
20208
- const [clobNoPos] = PDA.position(condition, 0, clobConfigPda, this.programIds);
20209
- const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
20210
- const addresses = [
20211
- this.programIds.clobExchange,
20212
- this.programIds.conditionalTokens,
20213
- this.programIds.hook,
20214
- TOKEN_PROGRAM_ID,
20215
- TOKEN_2022_PROGRAM_ID,
20216
- SystemProgram.programId,
20217
- clobConfigPda,
20218
- hookConfig,
20219
- condition,
20220
- yesMint,
20221
- noMint,
20222
- extraAccountMeta,
20223
- collateralVault,
20224
- vaultTokenAccount,
20225
- clobYesAta,
20226
- clobNoAta,
20227
- clobYesPos,
20228
- clobNoPos
20229
- ];
20230
- if (feeRecipientAddr) addresses.push(feeRecipientAddr);
20231
- if (this.feeConfigOwner && this.programIds.feeManagement) {
20232
- const companyAddr = await this.companyAddress();
20233
- const refVault = await this.referralVault();
20234
- addresses.push(this.programIds.feeManagement);
20235
- addresses.push(PDA.feeConfig(this.feeConfigOwner, this.programIds)[0]);
20236
- addresses.push(PDA.marketFeeOverride(condition, this.programIds)[0]);
20237
- if (companyAddr) addresses.push(getAssociatedTokenAddressSync(collateralMint, companyAddr));
20238
- if (refVault) addresses.push(refVault);
20239
- const oracleVault = await this.getMarketOracleVault(condition, collateralMint) ?? payer;
20240
- addresses.push(oracleVault);
20241
- }
20242
- for (const { order } of signedOrders) {
20243
- const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
20244
- const userTokenAta = getAssociatedTokenAddressSync(outcomeMint, order.user, false, TOKEN_2022_PROGRAM_ID);
20245
- const [userPosition] = PDA.position(condition, outcomeIndex, order.user, this.programIds);
20246
- addresses.push(collectFeeOrderRecord, order.user, userTokenAta, userPosition);
20247
- }
20248
- const slot = await connection.getSlot("finalized");
20249
- const [createIx, altAddress] = AddressLookupTableProgram.createLookupTable(
20250
- { authority: payer, payer, recentSlot: slot }
20251
- );
20252
- const BATCH = 30;
20253
- const extendIxs = [];
20254
- for (let i = 0; i < addresses.length; i += BATCH) {
20255
- extendIxs.push(AddressLookupTableProgram.extendLookupTable(
20256
- { payer, authority: payer, lookupTable: altAddress, addresses: addresses.slice(i, i + BATCH) }
20257
- ));
20258
- }
20259
- await this._sendLegacyTx([createIx, extendIxs[0]]);
20260
- for (let i = 1; i < extendIxs.length; i++) await this._sendLegacyTx([extendIxs[i]]);
20261
- for (let attempt = 0; attempt < 30; attempt++) {
20262
- await new Promise((r) => setTimeout(r, 1e3));
20263
- const res = await connection.getAddressLookupTable(altAddress);
20264
- if (res.value && res.value.state.addresses.length === addresses.length) {
20265
- this._altCache.set(cacheKey, res.value);
20266
- return res.value;
20267
- }
20268
- }
20269
- throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
20270
- }
20271
- /**
20272
- * Build ALT with static accounts for a condition — no order-specific PDAs needed.
20273
- * Use before buildBatchCollectRedeemEarlyTx when no prior buildMatchOrdersTx ran in this session.
20274
- */
20275
- async buildAltForCondition(condition, _payer, collateralMint) {
20276
- const cacheKey = condition.toBase58();
20277
- if (this._altCache.has(cacheKey)) return this._altCache.get(cacheKey);
20278
- const mint = collateralMint ?? this.networkConfig.defaultCollateral.mint;
20279
- const payer = this.walletPubkey;
20280
- const { connection } = this.provider;
20281
- const clobConfigPda = this.configPda();
20282
- const [yesMint] = PDA.yesMint(condition, this.programIds);
20283
- const [noMint] = PDA.noMint(condition, this.programIds);
20284
- const [extraAccountMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
20285
- const [hookConfig] = PDA.hookConfig(this.programIds);
20286
- const [collateralVault] = PDA.collateralVault(mint, this.programIds);
20287
- const [vaultTokenAccount] = PDA.vaultToken(mint, this.programIds);
20288
- const clobYesAta = getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, TOKEN_2022_PROGRAM_ID);
20289
- const clobNoAta = getAssociatedTokenAddressSync(noMint, clobConfigPda, true, TOKEN_2022_PROGRAM_ID);
20290
- const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
20291
- const addresses = [
20292
- Ed25519Program.programId,
20293
- this.programIds.clobExchange,
20294
- this.programIds.conditionalTokens,
20295
- this.programIds.hook,
20296
- TOKEN_PROGRAM_ID,
20297
- TOKEN_2022_PROGRAM_ID,
20298
- SystemProgram.programId,
20299
- SYSVAR_INSTRUCTIONS_PUBKEY,
20300
- clobConfigPda,
20301
- hookConfig,
20302
- condition,
20303
- yesMint,
20304
- noMint,
20305
- extraAccountMeta,
20306
- collateralVault,
20307
- vaultTokenAccount,
20308
- clobYesAta,
20309
- clobNoAta
20310
- ];
20311
- if (feeRecipientAddr) addresses.push(feeRecipientAddr);
20312
- if (this.feeConfigOwner && this.programIds.feeManagement) {
20313
- const companyAddr = await this.companyAddress();
20314
- const refVault = await this.referralVault();
20315
- addresses.push(this.programIds.feeManagement);
20316
- addresses.push(PDA.feeConfig(this.feeConfigOwner, this.programIds)[0]);
20317
- addresses.push(PDA.marketFeeOverride(condition, this.programIds)[0]);
20318
- if (companyAddr) addresses.push(getAssociatedTokenAddressSync(mint, companyAddr));
20319
- if (refVault) addresses.push(refVault);
20320
- const oracleVault = await this.getMarketOracleVault(condition, mint) ?? payer;
20321
- addresses.push(oracleVault);
20322
- }
20323
- const slot = await connection.getSlot("finalized");
20324
- const [createIx, altAddress] = AddressLookupTableProgram.createLookupTable(
20325
- { authority: payer, payer, recentSlot: slot }
20326
- );
20327
- const BATCH = 30;
20328
- const extendIxs = [];
20329
- for (let i = 0; i < addresses.length; i += BATCH) {
20330
- extendIxs.push(AddressLookupTableProgram.extendLookupTable(
20331
- { payer, authority: payer, lookupTable: altAddress, addresses: addresses.slice(i, i + BATCH) }
20332
- ));
20333
- }
20334
- await this._sendLegacyTx([createIx, extendIxs[0]]);
20335
- for (let i = 1; i < extendIxs.length; i++) await this._sendLegacyTx([extendIxs[i]]);
20336
- for (let attempt = 0; attempt < 30; attempt++) {
20337
- await new Promise((r) => setTimeout(r, 1e3));
20338
- const res = await connection.getAddressLookupTable(altAddress);
20339
- if (res.value && res.value.state.addresses.length === addresses.length) {
20340
- this._altCache.set(cacheKey, res.value);
20341
- return res.value;
20342
- }
20343
- }
20344
- throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
20345
- }
20346
- // ─── Queries ─────────────────────────────────────────────────────────────────
20347
- async fetchConfig() {
20348
- try {
20349
- const acc = await this.program.account.clobConfig.fetch(this.configPda());
20350
- return {
20351
- owner: acc.owner,
20352
- operators: acc.operators,
20353
- operatorsLen: acc.operatorsLen,
20354
- feeRecipient: acc.feeRecipient,
20355
- feeRateBps: acc.feeRateBps,
20356
- conditionalTokensProgram: acc.conditionalTokensProgram,
20357
- isPaused: acc.isPaused,
20358
- bump: acc.bump
20359
- };
20360
- } catch {
20361
- return null;
20362
- }
20363
- }
20364
- async fetchOrderStatus(maker, nonce) {
20365
- try {
20366
- const [pda] = PDA.orderStatus(maker, nonce, this.programIds);
20367
- const acc = await this.program.account.orderStatus.fetch(pda);
20368
- return {
20369
- maker: acc.maker,
20370
- nonce: acc.nonce,
20371
- filledAmount: acc.filledAmount,
20372
- bump: acc.bump
20373
- };
20374
- } catch {
20375
- return null;
20376
- }
20377
- }
20378
- async fetchOrderRecord(maker, nonce) {
20379
- try {
20380
- const [pda] = PDA.orderRecord(maker, nonce, this.programIds);
20381
- const acc = await this.program.account.signedOrderRecord.fetch(pda);
20382
- return acc;
20383
- } catch {
20384
- return null;
20385
- }
20386
- }
20387
- };
20388
- var MAX_APPROVE_AMOUNT = new BN4("18446744073709551615");
20389
- function buildCreateUserAtasTx(condition, user, payer, programIds) {
20390
- const [yesMint] = PDA.yesMint(condition, programIds);
20391
- const [noMint] = PDA.noMint(condition, programIds);
20392
- const yesAta = getAssociatedTokenAddressSync(yesMint, user, false, TOKEN_PROGRAM_ID);
20393
- const noAta = getAssociatedTokenAddressSync(noMint, user, false, TOKEN_PROGRAM_ID);
20394
- const tx = new Transaction();
20395
- tx.feePayer = payer;
20396
- tx.add(
20397
- createAssociatedTokenAccountIdempotentInstruction(
20398
- payer,
20399
- yesAta,
20400
- user,
20401
- yesMint,
20402
- TOKEN_PROGRAM_ID,
20403
- ASSOCIATED_TOKEN_PROGRAM_ID
20404
- ),
20405
- createAssociatedTokenAccountIdempotentInstruction(
20406
- payer,
20407
- noAta,
20408
- user,
17985
+ TOKEN_PROGRAM_ID,
17986
+ ASSOCIATED_TOKEN_PROGRAM_ID
17987
+ ),
17988
+ createAssociatedTokenAccountIdempotentInstruction(
17989
+ payer,
17990
+ noAta,
17991
+ user,
20409
17992
  noMint,
20410
17993
  TOKEN_PROGRAM_ID,
20411
17994
  ASSOCIATED_TOKEN_PROGRAM_ID
@@ -20434,15 +18017,26 @@ function buildApproveAllOutcomeTokensTx(condition, signer, payer, delegate, prog
20434
18017
  const yesAta = getAssociatedTokenAddressSync(yesMint, signer, false, TOKEN_PROGRAM_ID);
20435
18018
  const noAta = getAssociatedTokenAddressSync(noMint, signer, false, TOKEN_PROGRAM_ID);
20436
18019
  const amountBig = BigInt(amount.toString());
18020
+ const approveYesIx = createApproveInstruction(
18021
+ yesAta,
18022
+ delegate,
18023
+ signer,
18024
+ amountBig,
18025
+ []
18026
+ );
18027
+ const approveNoIx = createApproveInstruction(
18028
+ noAta,
18029
+ delegate,
18030
+ signer,
18031
+ amountBig,
18032
+ []
18033
+ );
20437
18034
  const tx = new Transaction();
20438
18035
  tx.feePayer = payer;
20439
- tx.add(
20440
- createApproveInstruction(yesAta, delegate, signer, amountBig, [], TOKEN_PROGRAM_ID),
20441
- createApproveInstruction(noAta, delegate, signer, amountBig, [], TOKEN_PROGRAM_ID)
20442
- );
18036
+ tx.add(approveYesIx, approveNoIx);
20443
18037
  return tx;
20444
18038
  }
20445
18039
 
20446
- export { AccountNotFoundError, AdminClient, ClobClient, ClobClientV2, CtfClient, CtfClientV2, DisputeClient, FEE_DENOMINATOR, FeeManagementClient, HookClient, IX_SYSVAR, InvalidParamError, MAX_APPROVE_AMOUNT, MarketClient, MarketClientV2, MarketOracleClient, OracleClient, PDA, PresaleClient, QuestionStatus, ReferralClient, SEEDS, UnauthorizedError, XMarketError, XMarketSDKV2 as XMarketSDK, XMarketSDKV2, buildApproveAllOutcomeTokensTx, buildApproveCollateralTx, buildBatchedCollectFeeEd25519Instruction, buildBatchedEd25519Instruction, buildBatchedRedeemFeeEd25519Instruction, buildCreateUserAtasTx, buildOrder, buildOrderFromPrice, deserializeSignedOrder, detectMatchType, generateContentHash, generateQuestionId, getOrderSignBytes, orderAmountsFromPrice, serializeCollectFeeOrderToBytes, serializeOrderToBytes, serializeRedeemFeeOrderToBytes, serializeSignedOrder, signOrder, signOrderWithKeypair, verifySignedOrder };
18040
+ export { AccountNotFoundError, AdminClient, ClobClient, CtfClient, DisputeClient, FEE_DENOMINATOR, FeeManagementClient, IX_SYSVAR, InvalidParamError, MAX_APPROVE_AMOUNT, MarketClient, MarketOracleClient, OracleClient, PDA, PresaleClient, QuestionStatus, ReferralClient, SEEDS, UnauthorizedError, XMarketError, XMarketSDK, buildApproveAllOutcomeTokensTx, buildApproveCollateralTx, buildBatchedCollectFeeEd25519Instruction, buildBatchedEd25519Instruction, buildBatchedRedeemFeeEd25519Instruction, buildCreateUserAtasTx, buildOrder, buildOrderFromPrice, deserializeSignedOrder, detectMatchType, generateContentHash, generateQuestionId, getOrderSignBytes, orderAmountsFromPrice, serializeCollectFeeOrderToBytes, serializeOrderToBytes, serializeRedeemFeeOrderToBytes, serializeSignedOrder, signOrder, signOrderWithKeypair, verifySignedOrder };
20447
18041
  //# sourceMappingURL=index.mjs.map
20448
18042
  //# sourceMappingURL=index.mjs.map