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