@theliem/xmarket-sdk 3.23.0 → 3.26.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
@@ -187,6 +187,22 @@ var PDA = class {
187
187
  programIds.clobExchange
188
188
  );
189
189
  }
190
+ static collectFeeOrderRecord(user, nonce, programIds) {
191
+ const nonceBuf = Buffer.alloc(8);
192
+ nonceBuf.writeBigUInt64LE(BigInt(nonce.toString()));
193
+ return web3_js.PublicKey.findProgramAddressSync(
194
+ [Buffer.from("collect_order"), user.toBuffer(), nonceBuf],
195
+ programIds.clobExchange
196
+ );
197
+ }
198
+ static redeemFeeOrderRecord(user, nonce, programIds) {
199
+ const nonceBuf = Buffer.alloc(8);
200
+ nonceBuf.writeBigUInt64LE(BigInt(nonce.toString()));
201
+ return web3_js.PublicKey.findProgramAddressSync(
202
+ [Buffer.from("redeem_fee_order"), user.toBuffer(), nonceBuf],
203
+ programIds.clobExchange
204
+ );
205
+ }
190
206
  // ─── Fee Management ─────────────────────────────────────────────────────────
191
207
  static feeConfig(owner, programIds) {
192
208
  if (!programIds.feeManagement) throw new Error("feeManagement program ID not configured");
@@ -1496,6 +1512,50 @@ function buildBatchedCollectFeeEd25519Instruction(orders) {
1496
1512
  data
1497
1513
  });
1498
1514
  }
1515
+ function serializeRedeemFeeOrderToBytes(order) {
1516
+ const buf = new Uint8Array(128);
1517
+ buf.set(order.user.toBytes(), 0);
1518
+ buf.set(order.condition.toBytes(), 32);
1519
+ buf.set(order.tokenMint.toBytes(), 64);
1520
+ buf.set(order.amount.toArrayLike(Buffer, "le", 8), 96);
1521
+ buf.set(order.takerAmount.toArrayLike(Buffer, "le", 8), 104);
1522
+ buf.set(order.nonce.toArrayLike(Buffer, "le", 8), 112);
1523
+ buf.set(order.expiry.toArrayLike(Buffer, "le", 8), 120);
1524
+ return buf;
1525
+ }
1526
+ function buildBatchedRedeemFeeEd25519Instruction(orders) {
1527
+ const N = orders.length;
1528
+ if (N === 0) throw new Error("At least 1 order required");
1529
+ const MSG_SIZE = 128;
1530
+ const SIG_SIZE = 64;
1531
+ const PK_SIZE = 32;
1532
+ const HEADER = 2 + N * 14;
1533
+ const sigBase = HEADER;
1534
+ const pkBase = sigBase + N * SIG_SIZE;
1535
+ const msgBase = pkBase + N * PK_SIZE;
1536
+ const totalSize = msgBase + N * MSG_SIZE;
1537
+ const data = Buffer.alloc(totalSize);
1538
+ data[0] = N;
1539
+ data[1] = 0;
1540
+ for (let i = 0; i < N; i++) {
1541
+ const e = 2 + i * 14;
1542
+ data.writeUInt16LE(sigBase + i * SIG_SIZE, e);
1543
+ data.writeUInt16LE(65535, e + 2);
1544
+ data.writeUInt16LE(pkBase + i * PK_SIZE, e + 4);
1545
+ data.writeUInt16LE(65535, e + 6);
1546
+ data.writeUInt16LE(msgBase + i * MSG_SIZE, e + 8);
1547
+ data.writeUInt16LE(MSG_SIZE, e + 10);
1548
+ data.writeUInt16LE(65535, e + 12);
1549
+ data.set(orders[i].signature, sigBase + i * SIG_SIZE);
1550
+ data.set(orders[i].order.user.toBytes(), pkBase + i * PK_SIZE);
1551
+ data.set(serializeRedeemFeeOrderToBytes(orders[i].order), msgBase + i * MSG_SIZE);
1552
+ }
1553
+ return new web3_js.TransactionInstruction({
1554
+ keys: [],
1555
+ programId: web3_js.Ed25519Program.programId,
1556
+ data
1557
+ });
1558
+ }
1499
1559
  function buildOrder(params) {
1500
1560
  return {
1501
1561
  maker: params.maker,
@@ -1644,6 +1704,39 @@ var ClobClient = class {
1644
1704
  this._marketOracleVaultCache.set(key, vault);
1645
1705
  return vault;
1646
1706
  }
1707
+ /**
1708
+ * Returns a createATA ix for the oracle vault when:
1709
+ * - takerFee > 0
1710
+ * - marketFeeOverride exists for the condition
1711
+ * - oracle vault ATA is not yet initialized
1712
+ * Idempotent — safe to call every tx; returns null if vault already exists.
1713
+ */
1714
+ async buildInitOracleVaultIfNeeded(condition, collateralMint, takerFee, payer) {
1715
+ if (takerFee.isZero()) return null;
1716
+ if (!this.feeConfigOwner || !this.programIds.feeManagement) return null;
1717
+ if (!this.ctfClient || !this.qmConfigPda || !this.programIds.marketOracle) return null;
1718
+ const companyAddr = await this.companyAddress();
1719
+ const refVault = await this.referralVault();
1720
+ if (!companyAddr || !refVault) return null;
1721
+ const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
1722
+ const cond = await this.ctfClient.fetchCondition(condition);
1723
+ if (!cond) return null;
1724
+ const [questionPda] = PDA.question(this.qmConfigPda, cond.questionId, this.programIds);
1725
+ const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
1726
+ const vault = splToken.getAssociatedTokenAddressSync(collateralMint, marketOraclePda, true);
1727
+ const [feeOverrideInfo, vaultInfo] = await Promise.all([
1728
+ this.provider.connection.getAccountInfo(feeOverridePda),
1729
+ this.provider.connection.getAccountInfo(vault)
1730
+ ]);
1731
+ if (!feeOverrideInfo) return null;
1732
+ if (vaultInfo) return null;
1733
+ return splToken.createAssociatedTokenAccountIdempotentInstruction(
1734
+ payer,
1735
+ vault,
1736
+ marketOraclePda,
1737
+ collateralMint
1738
+ );
1739
+ }
1647
1740
  get walletPubkey() {
1648
1741
  return this.provider.wallet.publicKey;
1649
1742
  }
@@ -1782,8 +1875,8 @@ var ClobClient = class {
1782
1875
  async _sendLegacyTxSig(instructions) {
1783
1876
  const { connection } = this.provider;
1784
1877
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1785
- const { Transaction: Transaction11 } = await import('@solana/web3.js');
1786
- const tx = new Transaction11();
1878
+ const { Transaction: Transaction12 } = await import('@solana/web3.js');
1879
+ const tx = new Transaction12();
1787
1880
  tx.recentBlockhash = blockhash;
1788
1881
  tx.feePayer = this.walletPubkey;
1789
1882
  tx.add(...instructions);
@@ -1809,10 +1902,10 @@ ${logs.join("\n")}`);
1809
1902
  connection.getAccountInfo(clobYesAta),
1810
1903
  connection.getAccountInfo(clobNoAta)
1811
1904
  ]);
1812
- const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction4 } = await import('@solana/spl-token');
1905
+ const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction5 } = await import('@solana/spl-token');
1813
1906
  const ixs = [];
1814
1907
  if (!yesInfo) {
1815
- ixs.push(createAssociatedTokenAccountIdempotentInstruction4(
1908
+ ixs.push(createAssociatedTokenAccountIdempotentInstruction5(
1816
1909
  this.walletPubkey,
1817
1910
  clobYesAta,
1818
1911
  clobConfig,
@@ -1821,7 +1914,7 @@ ${logs.join("\n")}`);
1821
1914
  ));
1822
1915
  }
1823
1916
  if (!noInfo) {
1824
- ixs.push(createAssociatedTokenAccountIdempotentInstruction4(
1917
+ ixs.push(createAssociatedTokenAccountIdempotentInstruction5(
1825
1918
  this.walletPubkey,
1826
1919
  clobNoAta,
1827
1920
  clobConfig,
@@ -1904,6 +1997,49 @@ ${logs.join("\n")}`);
1904
1997
  await this.registerOrder(signed);
1905
1998
  }
1906
1999
  }
2000
+ // ─── Register CollectFeeOrder ────────────────────────────────────────────────
2001
+ /**
2002
+ * Build a legacy Transaction to register a CollectFeeOrder on-chain.
2003
+ * Must be signed and sent before calling buildBatchCollectRedeemEarlyTx.
2004
+ *
2005
+ * Flow: [Ed25519 ix (1 user sig) + register_collect_fee_order ix]
2006
+ * Caller signs with payer keypair and sends.
2007
+ */
2008
+ async buildRegisterCollectFeeOrderTx(signedOrder, payer) {
2009
+ const { order } = signedOrder;
2010
+ const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
2011
+ const ed25519Ix = buildBatchedCollectFeeEd25519Instruction([signedOrder]);
2012
+ const registerIx = await this.program.methods.registerCollectFeeOrder(order.nonce).accounts({
2013
+ payer,
2014
+ clobConfig: this.configPda(),
2015
+ ixSysvar: IX_SYSVAR,
2016
+ orderSigner: order.user,
2017
+ collectFeeOrderRecord,
2018
+ systemProgram: web3_js.SystemProgram.programId
2019
+ }).instruction();
2020
+ const { blockhash } = await this.provider.connection.getLatestBlockhash();
2021
+ const tx = new web3_js.Transaction();
2022
+ tx.recentBlockhash = blockhash;
2023
+ tx.feePayer = payer;
2024
+ tx.add(ed25519Ix, registerIx);
2025
+ return tx;
2026
+ }
2027
+ /**
2028
+ * Register a CollectFeeOrder on-chain and send immediately (operator-signed flow).
2029
+ * Idempotent — skips if PDA already exists.
2030
+ */
2031
+ async registerCollectFeeOrderIfNeeded(signedOrder) {
2032
+ const [pda] = PDA.collectFeeOrderRecord(
2033
+ signedOrder.order.user,
2034
+ signedOrder.order.nonce,
2035
+ this.programIds
2036
+ );
2037
+ const existing = await this.program.account.collectFeeOrderRecord?.fetchNullable(pda);
2038
+ if (!existing) {
2039
+ const tx = await this.buildRegisterCollectFeeOrderTx(signedOrder, this.walletPubkey);
2040
+ await this._sendLegacyTx(tx.instructions);
2041
+ }
2042
+ }
1907
2043
  /** Cancel an order — closes its OrderRecord PDA and returns rent to maker. */
1908
2044
  async cancelOrder(nonce) {
1909
2045
  const [orderRecord] = PDA.orderRecord(this.walletPubkey, nonce, this.programIds);
@@ -2486,6 +2622,13 @@ ${logs.join("\n")}`);
2486
2622
  if (yIx) hookInitIxs.push(yIx);
2487
2623
  if (nIx) hookInitIxs.push(nIx);
2488
2624
  }
2625
+ const oracleVaultInitIx = await this.buildInitOracleVaultIfNeeded(
2626
+ t.condition,
2627
+ collateralMint,
2628
+ t.fee,
2629
+ payer
2630
+ );
2631
+ const preIxs = oracleVaultInitIx ? [...hookInitIxs, oracleVaultInitIx] : hookInitIxs;
2489
2632
  if (t.tokenId === m0.tokenId) {
2490
2633
  let buySignedOrder, sellCandidates;
2491
2634
  if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
@@ -2505,7 +2648,7 @@ ${logs.join("\n")}`);
2505
2648
  opts,
2506
2649
  false
2507
2650
  );
2508
- return this._buildUnsignedVtx([...hookInitIxs, ...ixs3], alt, payer);
2651
+ return this._buildUnsignedVtx([...preIxs, ...ixs3], alt, payer);
2509
2652
  } else {
2510
2653
  throw new InvalidParamError("COMPLEMENTARY requires one BUY and one or more SELLs on same tokenId");
2511
2654
  }
@@ -2521,7 +2664,7 @@ ${logs.join("\n")}`);
2521
2664
  operator,
2522
2665
  opts
2523
2666
  );
2524
- return this._buildUnsignedVtx([...hookInitIxs, ...ixs2], alt, payer);
2667
+ return this._buildUnsignedVtx([...preIxs, ...ixs2], alt, payer);
2525
2668
  }
2526
2669
  const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
2527
2670
  const allSell = t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_SELL);
@@ -2542,7 +2685,7 @@ ${logs.join("\n")}`);
2542
2685
  ]);
2543
2686
  await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
2544
2687
  const ix = allBuy ? await this._buildMintIx(taker, makers, collateralMint, operator, payer) : await this._buildMergeIx(taker, makers, collateralMint, operator, payer, opts);
2545
- return this._buildUnsignedVtx([...hookInitIxs, ix], alt, payer);
2688
+ return this._buildUnsignedVtx([...preIxs, ix], alt, payer);
2546
2689
  }
2547
2690
  await Promise.all([
2548
2691
  this.registerOrderIfNeeded(taker),
@@ -2554,17 +2697,19 @@ ${logs.join("\n")}`);
2554
2697
  const ix = allBuy ? await this._buildMintIx(yesMaker, [taker], collateralMint, operator, payer) : await this._buildMergeIx(yesMaker, [taker], collateralMint, operator, payer, opts);
2555
2698
  ixs.push(ix);
2556
2699
  }
2557
- return this._buildUnsignedVtx([...hookInitIxs, ...ixs], alt, payer);
2700
+ return this._buildUnsignedVtx([...preIxs, ...ixs], alt, payer);
2558
2701
  }
2559
2702
  // ─── batchCollectRedeemEarly ─────────────────────────────────────────────────
2560
2703
  /**
2561
2704
  * Build VersionedTransaction for batchCollectRedeemEarly.
2562
2705
  *
2563
- * Flow: Ed25519 ix (user sigs) + batchCollectRedeemEarly ix.
2564
- * Each SignedCollectFeeOrder authorizes CLOB to collect `amount` winning tokens
2565
- * from that user, redeem via CTF, then distribute as fees.
2706
+ * Prerequisite: each user's CollectFeeOrder must be registered on-chain via
2707
+ * buildRegisterCollectFeeOrderTx (one tx per user).
2566
2708
  *
2567
- * @param signedOrders - Array of { order, signature } from winning users
2709
+ * Flow: batchCollectRedeemEarly ix reads CollectFeeOrderRecord PDAs no Ed25519 inline.
2710
+ * No tx-size limit from signatures, supports 10+ orders in one tx.
2711
+ *
2712
+ * @param signedOrders - Array of { order, signature } (used to derive record PDAs)
2568
2713
  * @param condition - Market condition PDA
2569
2714
  * @param outcomeIndex - 0 = NO wins, 1 = YES wins
2570
2715
  * @param operator - Whitelisted operator pubkey (must sign)
@@ -2590,9 +2735,11 @@ ${logs.join("\n")}`);
2590
2735
  const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
2591
2736
  const userAccounts = [];
2592
2737
  for (const { order } of signedOrders) {
2738
+ const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
2593
2739
  const userTokenAta = splToken.getAssociatedTokenAddressSync(outcomeMint, order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
2594
2740
  const [userPosition] = PDA.position(condition, outcomeIndex, order.user, this.programIds);
2595
2741
  userAccounts.push(
2742
+ { pubkey: collectFeeOrderRecord, isSigner: false, isWritable: true },
2596
2743
  { pubkey: order.user, isSigner: false, isWritable: false },
2597
2744
  { pubkey: userTokenAta, isSigner: false, isWritable: true },
2598
2745
  { pubkey: userPosition, isSigner: false, isWritable: true }
@@ -2629,11 +2776,7 @@ ${logs.join("\n")}`);
2629
2776
  const ix = await this.hookClient.buildInitHookIxIfNeeded(outcomeMint, payer);
2630
2777
  if (ix) hookInitIxs.push(ix);
2631
2778
  }
2632
- const ed25519Ix = buildBatchedCollectFeeEd25519Instruction(signedOrders);
2633
- const ed25519IxIndex = 2 + hookInitIxs.length;
2634
2779
  const collectIx = await this.program.methods.batchCollectRedeemEarly(
2635
- ed25519IxIndex,
2636
- // ix_index: adjusted for any prepended hook init ixs
2637
2780
  signedOrders.length,
2638
2781
  outcomeIndex
2639
2782
  ).accounts({
@@ -2650,44 +2793,273 @@ ${logs.join("\n")}`);
2650
2793
  clobNoAta,
2651
2794
  clobYesPosition,
2652
2795
  clobNoPosition,
2653
- ixSysvar: IX_SYSVAR,
2654
2796
  conditionalTokensProgram: this.programIds.conditionalTokens,
2655
2797
  tokenProgram: splToken.TOKEN_PROGRAM_ID,
2656
2798
  token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
2657
2799
  systemProgram: web3_js.SystemProgram.programId
2658
2800
  }).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
2659
- const alt = opts?.lookupTable ?? this._altCache.get(condition.toBase58()) ?? await this.buildAltForCondition(condition, payer, collateralMint);
2660
- return this._buildUnsignedVtx([...hookInitIxs, ed25519Ix, collectIx], alt, payer);
2801
+ const alt = opts?.lookupTable ?? await this.buildAltForCollectBatch(condition, payer, collateralMint, signedOrders, outcomeIndex);
2802
+ return this._buildUnsignedVtx([...hookInitIxs, collectIx], alt, payer);
2661
2803
  }
2804
+ // ─── Register RedeemFeeOrder ─────────────────────────────────────────────────
2662
2805
  /**
2663
- * Build ALT with static accounts for a condition no order-specific PDAs needed.
2664
- * Use before buildBatchCollectRedeemEarlyTx when no prior buildMatchOrdersTx ran in this session.
2806
+ * Build a Transaction to register a RedeemFeeOrder on-chain (Ed25519 verify + PDA).
2807
+ * Must be sent before calling buildBatchRedeemWithFeeTx / buildBatchMergeWithFeeTx.
2808
+ *
2809
+ * ix[0]: batched Ed25519 { pubkey=order.user, sig, msg=128 bytes }
2810
+ * ix[1]: register_redeem_fee_order(nonce)
2665
2811
  */
2666
- async buildAltForCondition(condition, _payer, collateralMint) {
2667
- const cacheKey = condition.toBase58();
2812
+ async buildRegisterRedeemFeeOrderTx(signedOrder, payer) {
2813
+ const { order } = signedOrder;
2814
+ const [redeemFeeOrderRecord] = PDA.redeemFeeOrderRecord(order.user, order.nonce, this.programIds);
2815
+ const ed25519Ix = buildBatchedRedeemFeeEd25519Instruction([signedOrder]);
2816
+ const registerIx = await this.program.methods.registerRedeemFeeOrder(order.nonce).accounts({
2817
+ payer,
2818
+ clobConfig: this.configPda(),
2819
+ ixSysvar: IX_SYSVAR,
2820
+ orderSigner: order.user,
2821
+ redeemFeeOrderRecord,
2822
+ systemProgram: anchor5__namespace.web3.SystemProgram.programId
2823
+ }).instruction();
2824
+ const tx = new anchor5__namespace.web3.Transaction();
2825
+ tx.add(ed25519Ix, registerIx);
2826
+ return tx;
2827
+ }
2828
+ // ─── batchRedeemWithFee ───────────────────────────────────────────────────────
2829
+ /**
2830
+ * Build a VersionedTransaction for batchRedeemWithFee.
2831
+ * Each pair has a YES order + NO order for the same user (same signer or different).
2832
+ * After resolution: YES holder taker_amount > 0, NO holder taker_amount = 0 (or vice versa).
2833
+ *
2834
+ * Prerequisite: each order's RedeemFeeOrderRecord must be registered via buildRegisterRedeemFeeOrderTx.
2835
+ */
2836
+ async buildBatchRedeemWithFeeTx(orderPairs, condition, operator, payer, opts) {
2837
+ if (orderPairs.length === 0) throw new InvalidParamError("At least 1 order pair required");
2838
+ const collateralMint = this.networkConfig.defaultCollateral.mint;
2839
+ const cfg = await this.fetchConfig();
2840
+ if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
2841
+ const clobConfig = this.configPda();
2842
+ const [yesMint] = PDA.yesMint(condition, this.programIds);
2843
+ const [noMint] = PDA.noMint(condition, this.programIds);
2844
+ const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
2845
+ const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
2846
+ const [yesExtraMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
2847
+ const [noExtraMeta] = PDA.extraAccountMetaList(noMint, this.programIds);
2848
+ const [hookConfig] = PDA.hookConfig(this.programIds);
2849
+ const [clobYesPosition] = PDA.position(condition, 1, clobConfig, this.programIds);
2850
+ const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
2851
+ const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
2852
+ const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
2853
+ const userAccounts = [];
2854
+ for (const { yes, no } of orderPairs) {
2855
+ const [yesRecord] = PDA.redeemFeeOrderRecord(yes.order.user, yes.order.nonce, this.programIds);
2856
+ const [noRecord] = PDA.redeemFeeOrderRecord(no.order.user, no.order.nonce, this.programIds);
2857
+ const userYesAta = splToken.getAssociatedTokenAddressSync(yesMint, yes.order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
2858
+ const userNoAta = splToken.getAssociatedTokenAddressSync(noMint, no.order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
2859
+ const [userYesPos] = PDA.position(condition, 1, yes.order.user, this.programIds);
2860
+ const [userNoPos] = PDA.position(condition, 0, no.order.user, this.programIds);
2861
+ const yesCollateral = splToken.getAssociatedTokenAddressSync(collateralMint, yes.order.user);
2862
+ const noCollateral = splToken.getAssociatedTokenAddressSync(collateralMint, no.order.user);
2863
+ userAccounts.push(
2864
+ { pubkey: yesRecord, isSigner: false, isWritable: true },
2865
+ { pubkey: noRecord, isSigner: false, isWritable: true },
2866
+ { pubkey: yes.order.user, isSigner: false, isWritable: false },
2867
+ { pubkey: no.order.user, isSigner: false, isWritable: false },
2868
+ { pubkey: userYesAta, isSigner: false, isWritable: true },
2869
+ { pubkey: userNoAta, isSigner: false, isWritable: true },
2870
+ { pubkey: userYesPos, isSigner: false, isWritable: true },
2871
+ { pubkey: userNoPos, isSigner: false, isWritable: true },
2872
+ { pubkey: yesCollateral, isSigner: false, isWritable: true },
2873
+ { pubkey: noCollateral, isSigner: false, isWritable: true }
2874
+ );
2875
+ }
2876
+ const hookAccounts = [
2877
+ { pubkey: yesExtraMeta, isSigner: false, isWritable: false },
2878
+ { pubkey: noExtraMeta, isSigner: false, isWritable: false },
2879
+ { pubkey: hookConfig, isSigner: false, isWritable: false },
2880
+ { pubkey: this.programIds.hook, isSigner: false, isWritable: false }
2881
+ ];
2882
+ let feeAccounts = [];
2883
+ if (this.programIds.feeManagement && this.feeConfigOwner) {
2884
+ const companyAddr = await this.companyAddress();
2885
+ const refVault = await this.referralVault();
2886
+ if (companyAddr && refVault) {
2887
+ const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
2888
+ const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
2889
+ if (feeOverrideExists) {
2890
+ const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? payer;
2891
+ feeAccounts = [
2892
+ { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
2893
+ { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
2894
+ { pubkey: feeOverridePda, isSigner: false, isWritable: false },
2895
+ { pubkey: splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
2896
+ { pubkey: oracleVault, isSigner: false, isWritable: true },
2897
+ { pubkey: refVault, isSigner: false, isWritable: true }
2898
+ ];
2899
+ }
2900
+ }
2901
+ }
2902
+ await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
2903
+ const hookInitIxs = [];
2904
+ if (this.hookClient) {
2905
+ for (const mint of [yesMint, noMint]) {
2906
+ const ix = await this.hookClient.buildInitHookIxIfNeeded(mint, payer);
2907
+ if (ix) hookInitIxs.push(ix);
2908
+ }
2909
+ }
2910
+ const redeemIx = await this.program.methods.batchRedeemWithFee(orderPairs.length).accounts({
2911
+ operator,
2912
+ payer,
2913
+ clobConfig,
2914
+ condition,
2915
+ collateralVault,
2916
+ vaultTokenAccount,
2917
+ feeRecipient: cfg.feeRecipient,
2918
+ yesMint,
2919
+ noMint,
2920
+ clobYesAta,
2921
+ clobNoAta,
2922
+ clobYesPosition,
2923
+ clobNoPosition,
2924
+ conditionalTokensProgram: this.programIds.conditionalTokens,
2925
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
2926
+ token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
2927
+ systemProgram: anchor5__namespace.web3.SystemProgram.programId
2928
+ }).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
2929
+ const alt = opts?.lookupTable ?? await this.buildAltForCondition(condition, payer, collateralMint);
2930
+ return this._buildUnsignedVtx([...hookInitIxs, redeemIx], alt, payer);
2931
+ }
2932
+ // ─── batchMergeWithFee ────────────────────────────────────────────────────────
2933
+ /**
2934
+ * Build a VersionedTransaction for batchMergeWithFee (pre-resolution merge with fee).
2935
+ * Each pair: YES order + NO order with equal amounts for the same user.
2936
+ * Only yes_signer receives net USDS (mirrors EVM _matchMergeOrder).
2937
+ *
2938
+ * Prerequisite: each order's RedeemFeeOrderRecord must be registered via buildRegisterRedeemFeeOrderTx.
2939
+ */
2940
+ async buildBatchMergeWithFeeTx(orderPairs, condition, operator, payer, opts) {
2941
+ if (orderPairs.length === 0) throw new InvalidParamError("At least 1 order pair required");
2942
+ const collateralMint = this.networkConfig.defaultCollateral.mint;
2943
+ const cfg = await this.fetchConfig();
2944
+ if (!cfg) throw new InvalidParamError("CLOB config not found on-chain");
2945
+ const clobConfig = this.configPda();
2946
+ const [yesMint] = PDA.yesMint(condition, this.programIds);
2947
+ const [noMint] = PDA.noMint(condition, this.programIds);
2948
+ const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
2949
+ const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
2950
+ const [yesExtraMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
2951
+ const [noExtraMeta] = PDA.extraAccountMetaList(noMint, this.programIds);
2952
+ const [hookConfig] = PDA.hookConfig(this.programIds);
2953
+ const [clobYesPosition] = PDA.position(condition, 1, clobConfig, this.programIds);
2954
+ const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
2955
+ const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
2956
+ const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfig, true, splToken.TOKEN_2022_PROGRAM_ID);
2957
+ const userAccounts = [];
2958
+ for (const { yes, no } of orderPairs) {
2959
+ const [yesRecord] = PDA.redeemFeeOrderRecord(yes.order.user, yes.order.nonce, this.programIds);
2960
+ const [noRecord] = PDA.redeemFeeOrderRecord(no.order.user, no.order.nonce, this.programIds);
2961
+ const userYesAta = splToken.getAssociatedTokenAddressSync(yesMint, yes.order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
2962
+ const userNoAta = splToken.getAssociatedTokenAddressSync(noMint, no.order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
2963
+ const [userYesPos] = PDA.position(condition, 1, yes.order.user, this.programIds);
2964
+ const [userNoPos] = PDA.position(condition, 0, no.order.user, this.programIds);
2965
+ const yesCollateral = splToken.getAssociatedTokenAddressSync(collateralMint, yes.order.user);
2966
+ userAccounts.push(
2967
+ { pubkey: yesRecord, isSigner: false, isWritable: true },
2968
+ { pubkey: noRecord, isSigner: false, isWritable: true },
2969
+ { pubkey: yes.order.user, isSigner: false, isWritable: false },
2970
+ { pubkey: no.order.user, isSigner: false, isWritable: false },
2971
+ { pubkey: userYesAta, isSigner: false, isWritable: true },
2972
+ { pubkey: userNoAta, isSigner: false, isWritable: true },
2973
+ { pubkey: userYesPos, isSigner: false, isWritable: true },
2974
+ { pubkey: userNoPos, isSigner: false, isWritable: true },
2975
+ { pubkey: yesCollateral, isSigner: false, isWritable: true }
2976
+ );
2977
+ }
2978
+ const hookAccounts = [
2979
+ { pubkey: yesExtraMeta, isSigner: false, isWritable: false },
2980
+ { pubkey: noExtraMeta, isSigner: false, isWritable: false },
2981
+ { pubkey: hookConfig, isSigner: false, isWritable: false },
2982
+ { pubkey: this.programIds.hook, isSigner: false, isWritable: false }
2983
+ ];
2984
+ let feeAccounts = [];
2985
+ if (this.programIds.feeManagement && this.feeConfigOwner) {
2986
+ const companyAddr = await this.companyAddress();
2987
+ const refVault = await this.referralVault();
2988
+ if (companyAddr && refVault) {
2989
+ const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
2990
+ const feeOverrideExists = await this.provider.connection.getAccountInfo(feeOverridePda);
2991
+ if (feeOverrideExists) {
2992
+ const oracleVault = opts?.marketOracleVault ?? await this.getMarketOracleVault(condition, collateralMint) ?? payer;
2993
+ feeAccounts = [
2994
+ { pubkey: this.programIds.feeManagement, isSigner: false, isWritable: false },
2995
+ { pubkey: PDA.feeConfig(this.feeConfigOwner, this.programIds)[0], isSigner: false, isWritable: false },
2996
+ { pubkey: feeOverridePda, isSigner: false, isWritable: false },
2997
+ { pubkey: splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr), isSigner: false, isWritable: true },
2998
+ { pubkey: oracleVault, isSigner: false, isWritable: true },
2999
+ { pubkey: refVault, isSigner: false, isWritable: true }
3000
+ ];
3001
+ }
3002
+ }
3003
+ }
3004
+ await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
3005
+ const hookInitIxs = [];
3006
+ if (this.hookClient) {
3007
+ for (const mint of [yesMint, noMint]) {
3008
+ const ix = await this.hookClient.buildInitHookIxIfNeeded(mint, payer);
3009
+ if (ix) hookInitIxs.push(ix);
3010
+ }
3011
+ }
3012
+ const mergeIx = await this.program.methods.batchMergeWithFee(orderPairs.length).accounts({
3013
+ operator,
3014
+ payer,
3015
+ clobConfig,
3016
+ condition,
3017
+ collateralVault,
3018
+ vaultTokenAccount,
3019
+ feeRecipient: cfg.feeRecipient,
3020
+ yesMint,
3021
+ noMint,
3022
+ clobYesAta,
3023
+ clobNoAta,
3024
+ clobYesPosition,
3025
+ clobNoPosition,
3026
+ conditionalTokensProgram: this.programIds.conditionalTokens,
3027
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
3028
+ token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
3029
+ systemProgram: anchor5__namespace.web3.SystemProgram.programId
3030
+ }).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
3031
+ const alt = opts?.lookupTable ?? await this.buildAltForCondition(condition, payer, collateralMint);
3032
+ return this._buildUnsignedVtx([...hookInitIxs, mergeIx], alt, payer);
3033
+ }
3034
+ /**
3035
+ * Build ALT for batchCollectRedeemEarly — includes per-user accounts so all
3036
+ * remaining_accounts fit as 1-byte ALT indices instead of 32-byte inline addresses.
3037
+ */
3038
+ async buildAltForCollectBatch(condition, payer, collateralMint, signedOrders, outcomeIndex) {
3039
+ const userKeys = signedOrders.map((o) => o.order.user.toBase58()).sort((a, b) => a.localeCompare(b)).join(",");
3040
+ const cacheKey = `${condition.toBase58()}:collect:${userKeys}`;
2668
3041
  if (this._altCache.has(cacheKey)) return this._altCache.get(cacheKey);
2669
- const mint = collateralMint ?? this.networkConfig.defaultCollateral.mint;
2670
- const payer = this.walletPubkey;
2671
3042
  const { connection } = this.provider;
2672
3043
  const clobConfigPda = this.configPda();
2673
3044
  const [yesMint] = PDA.yesMint(condition, this.programIds);
2674
3045
  const [noMint] = PDA.noMint(condition, this.programIds);
2675
- const [extraAccountMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
3046
+ const outcomeMint = outcomeIndex === 1 ? yesMint : noMint;
3047
+ const [extraAccountMeta] = PDA.extraAccountMetaList(outcomeMint, this.programIds);
2676
3048
  const [hookConfig] = PDA.hookConfig(this.programIds);
2677
- const [collateralVault] = PDA.collateralVault(mint, this.programIds);
2678
- const [vaultTokenAccount] = PDA.vaultToken(mint, this.programIds);
3049
+ const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
3050
+ const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
2679
3051
  const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
2680
3052
  const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
3053
+ const [clobYesPos] = PDA.position(condition, 1, clobConfigPda, this.programIds);
3054
+ const [clobNoPos] = PDA.position(condition, 0, clobConfigPda, this.programIds);
2681
3055
  const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
2682
3056
  const addresses = [
2683
- web3_js.Ed25519Program.programId,
2684
3057
  this.programIds.clobExchange,
2685
3058
  this.programIds.conditionalTokens,
2686
3059
  this.programIds.hook,
2687
3060
  splToken.TOKEN_PROGRAM_ID,
2688
3061
  splToken.TOKEN_2022_PROGRAM_ID,
2689
3062
  web3_js.SystemProgram.programId,
2690
- web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
2691
3063
  clobConfigPda,
2692
3064
  hookConfig,
2693
3065
  condition,
@@ -2697,7 +3069,9 @@ ${logs.join("\n")}`);
2697
3069
  collateralVault,
2698
3070
  vaultTokenAccount,
2699
3071
  clobYesAta,
2700
- clobNoAta
3072
+ clobNoAta,
3073
+ clobYesPos,
3074
+ clobNoPos
2701
3075
  ];
2702
3076
  if (feeRecipientAddr) addresses.push(feeRecipientAddr);
2703
3077
  if (this.feeConfigOwner && this.programIds.feeManagement) {
@@ -2706,11 +3080,17 @@ ${logs.join("\n")}`);
2706
3080
  addresses.push(this.programIds.feeManagement);
2707
3081
  addresses.push(PDA.feeConfig(this.feeConfigOwner, this.programIds)[0]);
2708
3082
  addresses.push(PDA.marketFeeOverride(condition, this.programIds)[0]);
2709
- if (companyAddr) addresses.push(splToken.getAssociatedTokenAddressSync(mint, companyAddr));
3083
+ if (companyAddr) addresses.push(splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr));
2710
3084
  if (refVault) addresses.push(refVault);
2711
- const oracleVault = await this.getMarketOracleVault(condition, mint) ?? payer;
3085
+ const oracleVault = await this.getMarketOracleVault(condition, collateralMint) ?? payer;
2712
3086
  addresses.push(oracleVault);
2713
3087
  }
3088
+ for (const { order } of signedOrders) {
3089
+ const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
3090
+ const userTokenAta = splToken.getAssociatedTokenAddressSync(outcomeMint, order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
3091
+ const [userPosition] = PDA.position(condition, outcomeIndex, order.user, this.programIds);
3092
+ addresses.push(collectFeeOrderRecord, order.user, userTokenAta, userPosition);
3093
+ }
2714
3094
  const slot = await connection.getSlot("finalized");
2715
3095
  const [createIx, altAddress] = web3_js.AddressLookupTableProgram.createLookupTable(
2716
3096
  { authority: payer, payer, recentSlot: slot }
@@ -2734,11 +3114,86 @@ ${logs.join("\n")}`);
2734
3114
  }
2735
3115
  throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
2736
3116
  }
2737
- // ─── Queries ─────────────────────────────────────────────────────────────────
2738
- async fetchConfig() {
2739
- try {
2740
- const acc = await this.program.account.clobConfig.fetch(this.configPda());
2741
- return {
3117
+ /**
3118
+ * Build ALT with static accounts for a condition — no order-specific PDAs needed.
3119
+ * Use before buildBatchCollectRedeemEarlyTx when no prior buildMatchOrdersTx ran in this session.
3120
+ */
3121
+ async buildAltForCondition(condition, _payer, collateralMint) {
3122
+ const cacheKey = condition.toBase58();
3123
+ if (this._altCache.has(cacheKey)) return this._altCache.get(cacheKey);
3124
+ const mint = collateralMint ?? this.networkConfig.defaultCollateral.mint;
3125
+ const payer = this.walletPubkey;
3126
+ const { connection } = this.provider;
3127
+ const clobConfigPda = this.configPda();
3128
+ const [yesMint] = PDA.yesMint(condition, this.programIds);
3129
+ const [noMint] = PDA.noMint(condition, this.programIds);
3130
+ const [extraAccountMeta] = PDA.extraAccountMetaList(yesMint, this.programIds);
3131
+ const [hookConfig] = PDA.hookConfig(this.programIds);
3132
+ const [collateralVault] = PDA.collateralVault(mint, this.programIds);
3133
+ const [vaultTokenAccount] = PDA.vaultToken(mint, this.programIds);
3134
+ const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
3135
+ const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
3136
+ const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
3137
+ const addresses = [
3138
+ web3_js.Ed25519Program.programId,
3139
+ this.programIds.clobExchange,
3140
+ this.programIds.conditionalTokens,
3141
+ this.programIds.hook,
3142
+ splToken.TOKEN_PROGRAM_ID,
3143
+ splToken.TOKEN_2022_PROGRAM_ID,
3144
+ web3_js.SystemProgram.programId,
3145
+ web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
3146
+ clobConfigPda,
3147
+ hookConfig,
3148
+ condition,
3149
+ yesMint,
3150
+ noMint,
3151
+ extraAccountMeta,
3152
+ collateralVault,
3153
+ vaultTokenAccount,
3154
+ clobYesAta,
3155
+ clobNoAta
3156
+ ];
3157
+ if (feeRecipientAddr) addresses.push(feeRecipientAddr);
3158
+ if (this.feeConfigOwner && this.programIds.feeManagement) {
3159
+ const companyAddr = await this.companyAddress();
3160
+ const refVault = await this.referralVault();
3161
+ addresses.push(this.programIds.feeManagement);
3162
+ addresses.push(PDA.feeConfig(this.feeConfigOwner, this.programIds)[0]);
3163
+ addresses.push(PDA.marketFeeOverride(condition, this.programIds)[0]);
3164
+ if (companyAddr) addresses.push(splToken.getAssociatedTokenAddressSync(mint, companyAddr));
3165
+ if (refVault) addresses.push(refVault);
3166
+ const oracleVault = await this.getMarketOracleVault(condition, mint) ?? payer;
3167
+ addresses.push(oracleVault);
3168
+ }
3169
+ const slot = await connection.getSlot("finalized");
3170
+ const [createIx, altAddress] = web3_js.AddressLookupTableProgram.createLookupTable(
3171
+ { authority: payer, payer, recentSlot: slot }
3172
+ );
3173
+ const BATCH = 30;
3174
+ const extendIxs = [];
3175
+ for (let i = 0; i < addresses.length; i += BATCH) {
3176
+ extendIxs.push(web3_js.AddressLookupTableProgram.extendLookupTable(
3177
+ { payer, authority: payer, lookupTable: altAddress, addresses: addresses.slice(i, i + BATCH) }
3178
+ ));
3179
+ }
3180
+ await this._sendLegacyTx([createIx, extendIxs[0]]);
3181
+ for (let i = 1; i < extendIxs.length; i++) await this._sendLegacyTx([extendIxs[i]]);
3182
+ for (let attempt = 0; attempt < 30; attempt++) {
3183
+ await new Promise((r) => setTimeout(r, 1e3));
3184
+ const res = await connection.getAddressLookupTable(altAddress);
3185
+ if (res.value && res.value.state.addresses.length === addresses.length) {
3186
+ this._altCache.set(cacheKey, res.value);
3187
+ return res.value;
3188
+ }
3189
+ }
3190
+ throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
3191
+ }
3192
+ // ─── Queries ─────────────────────────────────────────────────────────────────
3193
+ async fetchConfig() {
3194
+ try {
3195
+ const acc = await this.program.account.clobConfig.fetch(this.configPda());
3196
+ return {
2742
3197
  owner: acc.owner,
2743
3198
  operators: acc.operators,
2744
3199
  operatorsLen: acc.operatorsLen,
@@ -9420,7 +9875,8 @@ var clob_exchange_default = {
9420
9875
  name: "batch_collect_redeem_early",
9421
9876
  docs: [
9422
9877
  "Batch collect fee tokens from winning users after question resolution,",
9423
- "redeem them via CTF, and distribute USDS via fee_management."
9878
+ "redeem them via CTF, and distribute USDS via fee_management.",
9879
+ "Requires each user's CollectFeeOrderRecord to be registered via register_collect_fee_order."
9424
9880
  ],
9425
9881
  discriminator: [
9426
9882
  87,
@@ -9530,7 +9986,7 @@ var clob_exchange_default = {
9530
9986
  {
9531
9987
  name: "clob_yes_ata",
9532
9988
  docs: [
9533
- "CLOB's YES ATA (Token-2022) \u2014 must be pre-initialized (created during prior matchOrders)."
9989
+ "CLOB's YES ATA (Token-2022) \u2014 must be pre-initialized."
9534
9990
  ],
9535
9991
  writable: true
9536
9992
  },
@@ -9549,9 +10005,6 @@ var clob_exchange_default = {
9549
10005
  name: "clob_no_position",
9550
10006
  writable: true
9551
10007
  },
9552
- {
9553
- name: "ix_sysvar"
9554
- },
9555
10008
  {
9556
10009
  name: "conditional_tokens_program",
9557
10010
  address: "A6N1F8MRsdgcojAx8p6FaECvw8mo8w6qJcWsbKQBANK4"
@@ -9570,10 +10023,6 @@ var clob_exchange_default = {
9570
10023
  }
9571
10024
  ],
9572
10025
  args: [
9573
- {
9574
- name: "ix_index",
9575
- type: "u8"
9576
- },
9577
10026
  {
9578
10027
  name: "order_count",
9579
10028
  type: "u8"
@@ -9585,61 +10034,144 @@ var clob_exchange_default = {
9585
10034
  ]
9586
10035
  },
9587
10036
  {
9588
- name: "cancel_order",
10037
+ name: "batch_merge_with_fee",
9589
10038
  docs: [
9590
- "Cancel an order \u2014 close OrderRecord PDA, return rent to maker."
10039
+ "Batch merge YES+NO tokens before resolution. Returns net USDS to yes_signer, keeps fee.",
10040
+ "Mirrors EVM _matchMergeOrder flow."
9591
10041
  ],
9592
10042
  discriminator: [
9593
- 95,
9594
- 129,
9595
- 237,
9596
- 240,
9597
- 8,
9598
- 49,
9599
- 223,
9600
- 132
10043
+ 56,
10044
+ 127,
10045
+ 113,
10046
+ 232,
10047
+ 208,
10048
+ 177,
10049
+ 157,
10050
+ 230
9601
10051
  ],
9602
10052
  accounts: [
9603
10053
  {
9604
- name: "maker",
9605
- docs: [
9606
- "Must be the order maker/signer"
9607
- ],
10054
+ name: "operator",
9608
10055
  signer: true
9609
10056
  },
9610
10057
  {
9611
- name: "order_record",
10058
+ name: "payer",
10059
+ writable: true,
10060
+ signer: true
10061
+ },
10062
+ {
10063
+ name: "clob_config",
9612
10064
  writable: true,
9613
10065
  pda: {
9614
10066
  seeds: [
9615
10067
  {
9616
10068
  kind: "const",
9617
10069
  value: [
10070
+ 99,
10071
+ 108,
9618
10072
  111,
9619
- 114,
9620
- 100,
9621
- 101,
9622
- 114,
10073
+ 98,
9623
10074
  95,
9624
- 114,
9625
- 101,
9626
10075
  99,
9627
10076
  111,
10077
+ 110,
10078
+ 102,
10079
+ 105,
10080
+ 103
10081
+ ]
10082
+ }
10083
+ ]
10084
+ }
10085
+ },
10086
+ {
10087
+ name: "condition",
10088
+ writable: true
10089
+ },
10090
+ {
10091
+ name: "collateral_vault",
10092
+ writable: true,
10093
+ pda: {
10094
+ seeds: [
10095
+ {
10096
+ kind: "const",
10097
+ value: [
10098
+ 99,
10099
+ 111,
10100
+ 108,
10101
+ 108,
10102
+ 97,
10103
+ 116,
10104
+ 101,
9628
10105
  114,
9629
- 100
10106
+ 97,
10107
+ 108,
10108
+ 95,
10109
+ 118,
10110
+ 97,
10111
+ 117,
10112
+ 108,
10113
+ 116
9630
10114
  ]
9631
10115
  },
9632
10116
  {
9633
10117
  kind: "account",
9634
- path: "maker"
9635
- },
9636
- {
9637
- kind: "arg",
9638
- path: "nonce"
10118
+ path: "condition.collateral_mint",
10119
+ account: "Condition"
9639
10120
  }
9640
- ]
10121
+ ],
10122
+ program: {
10123
+ kind: "account",
10124
+ path: "conditional_tokens_program"
10125
+ }
9641
10126
  }
9642
10127
  },
10128
+ {
10129
+ name: "vault_token_account",
10130
+ writable: true
10131
+ },
10132
+ {
10133
+ name: "fee_recipient",
10134
+ docs: [
10135
+ "CLOB USDS ATA \u2014 receives USDS from merge, distributes to users + fee."
10136
+ ],
10137
+ writable: true
10138
+ },
10139
+ {
10140
+ name: "yes_mint",
10141
+ writable: true
10142
+ },
10143
+ {
10144
+ name: "no_mint",
10145
+ writable: true
10146
+ },
10147
+ {
10148
+ name: "clob_yes_ata",
10149
+ writable: true
10150
+ },
10151
+ {
10152
+ name: "clob_no_ata",
10153
+ writable: true
10154
+ },
10155
+ {
10156
+ name: "clob_yes_position",
10157
+ writable: true
10158
+ },
10159
+ {
10160
+ name: "clob_no_position",
10161
+ writable: true
10162
+ },
10163
+ {
10164
+ name: "conditional_tokens_program",
10165
+ address: "A6N1F8MRsdgcojAx8p6FaECvw8mo8w6qJcWsbKQBANK4"
10166
+ },
10167
+ {
10168
+ name: "token_program",
10169
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
10170
+ },
10171
+ {
10172
+ name: "token_2022_program",
10173
+ address: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
10174
+ },
9643
10175
  {
9644
10176
  name: "system_program",
9645
10177
  address: "11111111111111111111111111111111"
@@ -9647,38 +10179,257 @@ var clob_exchange_default = {
9647
10179
  ],
9648
10180
  args: [
9649
10181
  {
9650
- name: "nonce",
9651
- type: "u64"
10182
+ name: "order_count",
10183
+ type: "u8"
9652
10184
  }
9653
10185
  ]
9654
10186
  },
9655
10187
  {
9656
- name: "force_reset_clob",
10188
+ name: "batch_redeem_with_fee",
10189
+ docs: [
10190
+ "Batch redeem YES+NO tokens after resolution. Returns net USDS to users, keeps fee.",
10191
+ "Mirrors EVM _matchRedeemOrder flow."
10192
+ ],
9657
10193
  discriminator: [
9658
- 96,
9659
- 95,
9660
- 187,
9661
- 95,
9662
- 65,
9663
- 135,
9664
- 120,
9665
- 204
10194
+ 240,
10195
+ 238,
10196
+ 168,
10197
+ 110,
10198
+ 84,
10199
+ 41,
10200
+ 165,
10201
+ 2
9666
10202
  ],
9667
10203
  accounts: [
9668
10204
  {
9669
- name: "upgrade_authority",
9670
- docs: [
9671
- "Must be the upgrade authority (deployer). Checked in handler via",
9672
- "programdata account so we don't hardcode the pubkey."
9673
- ],
9674
- writable: true,
10205
+ name: "operator",
9675
10206
  signer: true
9676
10207
  },
9677
10208
  {
9678
- name: "program_data",
9679
- docs: [
9680
- "The clob_exchange programdata account (BPF loader stores the upgrade authority here)."
9681
- ]
10209
+ name: "payer",
10210
+ writable: true,
10211
+ signer: true
10212
+ },
10213
+ {
10214
+ name: "clob_config",
10215
+ writable: true,
10216
+ pda: {
10217
+ seeds: [
10218
+ {
10219
+ kind: "const",
10220
+ value: [
10221
+ 99,
10222
+ 108,
10223
+ 111,
10224
+ 98,
10225
+ 95,
10226
+ 99,
10227
+ 111,
10228
+ 110,
10229
+ 102,
10230
+ 105,
10231
+ 103
10232
+ ]
10233
+ }
10234
+ ]
10235
+ }
10236
+ },
10237
+ {
10238
+ name: "condition",
10239
+ writable: true
10240
+ },
10241
+ {
10242
+ name: "collateral_vault",
10243
+ writable: true,
10244
+ pda: {
10245
+ seeds: [
10246
+ {
10247
+ kind: "const",
10248
+ value: [
10249
+ 99,
10250
+ 111,
10251
+ 108,
10252
+ 108,
10253
+ 97,
10254
+ 116,
10255
+ 101,
10256
+ 114,
10257
+ 97,
10258
+ 108,
10259
+ 95,
10260
+ 118,
10261
+ 97,
10262
+ 117,
10263
+ 108,
10264
+ 116
10265
+ ]
10266
+ },
10267
+ {
10268
+ kind: "account",
10269
+ path: "condition.collateral_mint",
10270
+ account: "Condition"
10271
+ }
10272
+ ],
10273
+ program: {
10274
+ kind: "account",
10275
+ path: "conditional_tokens_program"
10276
+ }
10277
+ }
10278
+ },
10279
+ {
10280
+ name: "vault_token_account",
10281
+ writable: true
10282
+ },
10283
+ {
10284
+ name: "fee_recipient",
10285
+ docs: [
10286
+ "CLOB USDS ATA \u2014 receives USDS from redeem, distributes to users + fee."
10287
+ ],
10288
+ writable: true
10289
+ },
10290
+ {
10291
+ name: "yes_mint",
10292
+ writable: true
10293
+ },
10294
+ {
10295
+ name: "no_mint",
10296
+ writable: true
10297
+ },
10298
+ {
10299
+ name: "clob_yes_ata",
10300
+ writable: true
10301
+ },
10302
+ {
10303
+ name: "clob_no_ata",
10304
+ writable: true
10305
+ },
10306
+ {
10307
+ name: "clob_yes_position",
10308
+ writable: true
10309
+ },
10310
+ {
10311
+ name: "clob_no_position",
10312
+ writable: true
10313
+ },
10314
+ {
10315
+ name: "conditional_tokens_program",
10316
+ address: "A6N1F8MRsdgcojAx8p6FaECvw8mo8w6qJcWsbKQBANK4"
10317
+ },
10318
+ {
10319
+ name: "token_program",
10320
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
10321
+ },
10322
+ {
10323
+ name: "token_2022_program",
10324
+ address: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
10325
+ },
10326
+ {
10327
+ name: "system_program",
10328
+ address: "11111111111111111111111111111111"
10329
+ }
10330
+ ],
10331
+ args: [
10332
+ {
10333
+ name: "order_count",
10334
+ type: "u8"
10335
+ }
10336
+ ]
10337
+ },
10338
+ {
10339
+ name: "cancel_order",
10340
+ docs: [
10341
+ "Cancel an order \u2014 close OrderRecord PDA, return rent to maker."
10342
+ ],
10343
+ discriminator: [
10344
+ 95,
10345
+ 129,
10346
+ 237,
10347
+ 240,
10348
+ 8,
10349
+ 49,
10350
+ 223,
10351
+ 132
10352
+ ],
10353
+ accounts: [
10354
+ {
10355
+ name: "maker",
10356
+ docs: [
10357
+ "Must be the order maker/signer"
10358
+ ],
10359
+ signer: true
10360
+ },
10361
+ {
10362
+ name: "order_record",
10363
+ writable: true,
10364
+ pda: {
10365
+ seeds: [
10366
+ {
10367
+ kind: "const",
10368
+ value: [
10369
+ 111,
10370
+ 114,
10371
+ 100,
10372
+ 101,
10373
+ 114,
10374
+ 95,
10375
+ 114,
10376
+ 101,
10377
+ 99,
10378
+ 111,
10379
+ 114,
10380
+ 100
10381
+ ]
10382
+ },
10383
+ {
10384
+ kind: "account",
10385
+ path: "maker"
10386
+ },
10387
+ {
10388
+ kind: "arg",
10389
+ path: "nonce"
10390
+ }
10391
+ ]
10392
+ }
10393
+ },
10394
+ {
10395
+ name: "system_program",
10396
+ address: "11111111111111111111111111111111"
10397
+ }
10398
+ ],
10399
+ args: [
10400
+ {
10401
+ name: "nonce",
10402
+ type: "u64"
10403
+ }
10404
+ ]
10405
+ },
10406
+ {
10407
+ name: "force_reset_clob",
10408
+ discriminator: [
10409
+ 96,
10410
+ 95,
10411
+ 187,
10412
+ 95,
10413
+ 65,
10414
+ 135,
10415
+ 120,
10416
+ 204
10417
+ ],
10418
+ accounts: [
10419
+ {
10420
+ name: "upgrade_authority",
10421
+ docs: [
10422
+ "Must be the upgrade authority (deployer). Checked in handler via",
10423
+ "programdata account so we don't hardcode the pubkey."
10424
+ ],
10425
+ writable: true,
10426
+ signer: true
10427
+ },
10428
+ {
10429
+ name: "program_data",
10430
+ docs: [
10431
+ "The clob_exchange programdata account (BPF loader stores the upgrade authority here)."
10432
+ ]
9682
10433
  },
9683
10434
  {
9684
10435
  name: "clob_config",
@@ -10520,27 +11271,25 @@ var clob_exchange_default = {
10520
11271
  ]
10521
11272
  },
10522
11273
  {
10523
- name: "register_order",
11274
+ name: "register_collect_fee_order",
10524
11275
  docs: [
10525
- "Register a signed order on-chain (Ed25519 verify at registration time).",
10526
- "ix[0] must be Ed25519 precompile with 1 entry for order.signer."
11276
+ "Register a signed CollectFeeOrder on-chain (Ed25519 verify at registration time).",
11277
+ "ix[0] must be Ed25519 precompile with 1 entry for order.user.",
11278
+ "Call once per user before batch_collect_redeem_early."
10527
11279
  ],
10528
11280
  discriminator: [
10529
- 92,
10530
- 37,
10531
- 29,
10532
- 46,
10533
- 77,
10534
- 250,
10535
- 219,
10536
- 6
11281
+ 63,
11282
+ 26,
11283
+ 202,
11284
+ 98,
11285
+ 195,
11286
+ 2,
11287
+ 211,
11288
+ 36
10537
11289
  ],
10538
11290
  accounts: [
10539
11291
  {
10540
11292
  name: "payer",
10541
- docs: [
10542
- "Operator pays rent for the OrderRecord PDA."
10543
- ],
10544
11293
  writable: true,
10545
11294
  signer: true
10546
11295
  },
@@ -10575,27 +11324,128 @@ var clob_exchange_default = {
10575
11324
  name: "order_signer"
10576
11325
  },
10577
11326
  {
10578
- name: "order_record",
11327
+ name: "collect_fee_order_record",
10579
11328
  writable: true,
10580
11329
  pda: {
10581
11330
  seeds: [
10582
11331
  {
10583
11332
  kind: "const",
10584
11333
  value: [
11334
+ 99,
10585
11335
  111,
10586
- 114,
10587
- 100,
10588
- 101,
10589
- 114,
10590
- 95,
10591
- 114,
11336
+ 108,
11337
+ 108,
10592
11338
  101,
10593
11339
  99,
11340
+ 116,
11341
+ 95,
10594
11342
  111,
10595
11343
  114,
10596
- 100
10597
- ]
10598
- },
11344
+ 100,
11345
+ 101,
11346
+ 114
11347
+ ]
11348
+ },
11349
+ {
11350
+ kind: "account",
11351
+ path: "order_signer"
11352
+ },
11353
+ {
11354
+ kind: "arg",
11355
+ path: "nonce"
11356
+ }
11357
+ ]
11358
+ }
11359
+ },
11360
+ {
11361
+ name: "system_program",
11362
+ address: "11111111111111111111111111111111"
11363
+ }
11364
+ ],
11365
+ args: [
11366
+ {
11367
+ name: "nonce",
11368
+ type: "u64"
11369
+ }
11370
+ ]
11371
+ },
11372
+ {
11373
+ name: "register_order",
11374
+ docs: [
11375
+ "Register a signed order on-chain (Ed25519 verify at registration time).",
11376
+ "ix[0] must be Ed25519 precompile with 1 entry for order.signer."
11377
+ ],
11378
+ discriminator: [
11379
+ 92,
11380
+ 37,
11381
+ 29,
11382
+ 46,
11383
+ 77,
11384
+ 250,
11385
+ 219,
11386
+ 6
11387
+ ],
11388
+ accounts: [
11389
+ {
11390
+ name: "payer",
11391
+ docs: [
11392
+ "Operator pays rent for the OrderRecord PDA."
11393
+ ],
11394
+ writable: true,
11395
+ signer: true
11396
+ },
11397
+ {
11398
+ name: "clob_config",
11399
+ pda: {
11400
+ seeds: [
11401
+ {
11402
+ kind: "const",
11403
+ value: [
11404
+ 99,
11405
+ 108,
11406
+ 111,
11407
+ 98,
11408
+ 95,
11409
+ 99,
11410
+ 111,
11411
+ 110,
11412
+ 102,
11413
+ 105,
11414
+ 103
11415
+ ]
11416
+ }
11417
+ ]
11418
+ }
11419
+ },
11420
+ {
11421
+ name: "ix_sysvar",
11422
+ address: "Sysvar1nstructions1111111111111111111111111"
11423
+ },
11424
+ {
11425
+ name: "order_signer"
11426
+ },
11427
+ {
11428
+ name: "order_record",
11429
+ writable: true,
11430
+ pda: {
11431
+ seeds: [
11432
+ {
11433
+ kind: "const",
11434
+ value: [
11435
+ 111,
11436
+ 114,
11437
+ 100,
11438
+ 101,
11439
+ 114,
11440
+ 95,
11441
+ 114,
11442
+ 101,
11443
+ 99,
11444
+ 111,
11445
+ 114,
11446
+ 100
11447
+ ]
11448
+ },
10599
11449
  {
10600
11450
  kind: "account",
10601
11451
  path: "order_signer"
@@ -10645,6 +11495,107 @@ var clob_exchange_default = {
10645
11495
  }
10646
11496
  ]
10647
11497
  },
11498
+ {
11499
+ name: "register_redeem_fee_order",
11500
+ docs: [
11501
+ "Register a signed RedeemFeeOrder on-chain (Ed25519 verify + PDA creation).",
11502
+ "Prerequisite for batch_redeem_with_fee and batch_merge_with_fee."
11503
+ ],
11504
+ discriminator: [
11505
+ 129,
11506
+ 167,
11507
+ 199,
11508
+ 14,
11509
+ 217,
11510
+ 12,
11511
+ 76,
11512
+ 162
11513
+ ],
11514
+ accounts: [
11515
+ {
11516
+ name: "payer",
11517
+ writable: true,
11518
+ signer: true
11519
+ },
11520
+ {
11521
+ name: "clob_config",
11522
+ pda: {
11523
+ seeds: [
11524
+ {
11525
+ kind: "const",
11526
+ value: [
11527
+ 99,
11528
+ 108,
11529
+ 111,
11530
+ 98,
11531
+ 95,
11532
+ 99,
11533
+ 111,
11534
+ 110,
11535
+ 102,
11536
+ 105,
11537
+ 103
11538
+ ]
11539
+ }
11540
+ ]
11541
+ }
11542
+ },
11543
+ {
11544
+ name: "ix_sysvar",
11545
+ address: "Sysvar1nstructions1111111111111111111111111"
11546
+ },
11547
+ {
11548
+ name: "order_signer"
11549
+ },
11550
+ {
11551
+ name: "redeem_fee_order_record",
11552
+ writable: true,
11553
+ pda: {
11554
+ seeds: [
11555
+ {
11556
+ kind: "const",
11557
+ value: [
11558
+ 114,
11559
+ 101,
11560
+ 100,
11561
+ 101,
11562
+ 101,
11563
+ 109,
11564
+ 95,
11565
+ 102,
11566
+ 101,
11567
+ 101,
11568
+ 95,
11569
+ 111,
11570
+ 114,
11571
+ 100,
11572
+ 101,
11573
+ 114
11574
+ ]
11575
+ },
11576
+ {
11577
+ kind: "account",
11578
+ path: "order_signer"
11579
+ },
11580
+ {
11581
+ kind: "arg",
11582
+ path: "nonce"
11583
+ }
11584
+ ]
11585
+ }
11586
+ },
11587
+ {
11588
+ name: "system_program",
11589
+ address: "11111111111111111111111111111111"
11590
+ }
11591
+ ],
11592
+ args: [
11593
+ {
11594
+ name: "nonce",
11595
+ type: "u64"
11596
+ }
11597
+ ]
11598
+ },
10648
11599
  {
10649
11600
  name: "reinit_clob",
10650
11601
  discriminator: [
@@ -10809,6 +11760,19 @@ var clob_exchange_default = {
10809
11760
  160
10810
11761
  ]
10811
11762
  },
11763
+ {
11764
+ name: "CollectFeeOrderRecord",
11765
+ discriminator: [
11766
+ 145,
11767
+ 140,
11768
+ 193,
11769
+ 74,
11770
+ 12,
11771
+ 98,
11772
+ 74,
11773
+ 130
11774
+ ]
11775
+ },
10812
11776
  {
10813
11777
  name: "OrderStatus",
10814
11778
  discriminator: [
@@ -10822,6 +11786,19 @@ var clob_exchange_default = {
10822
11786
  3
10823
11787
  ]
10824
11788
  },
11789
+ {
11790
+ name: "RedeemFeeOrderRecord",
11791
+ discriminator: [
11792
+ 88,
11793
+ 3,
11794
+ 122,
11795
+ 192,
11796
+ 131,
11797
+ 243,
11798
+ 143,
11799
+ 146
11800
+ ]
11801
+ },
10825
11802
  {
10826
11803
  name: "SignedOrderRecord",
10827
11804
  discriminator: [
@@ -11018,6 +11995,53 @@ var clob_exchange_default = {
11018
11995
  ]
11019
11996
  }
11020
11997
  },
11998
+ {
11999
+ name: "CollectFeeOrderRecord",
12000
+ docs: [
12001
+ "On-chain record of a signed CollectFeeOrder.",
12002
+ "Created by `register_collect_fee_order` after Ed25519 verification.",
12003
+ "Read and marked collected by `batch_collect_redeem_early`.",
12004
+ "",
12005
+ 'PDA seeds: [b"collect_order", user, nonce_le_bytes]'
12006
+ ],
12007
+ type: {
12008
+ kind: "struct",
12009
+ fields: [
12010
+ {
12011
+ name: "user",
12012
+ type: "pubkey"
12013
+ },
12014
+ {
12015
+ name: "condition",
12016
+ type: "pubkey"
12017
+ },
12018
+ {
12019
+ name: "token_mint",
12020
+ type: "pubkey"
12021
+ },
12022
+ {
12023
+ name: "amount",
12024
+ type: "u64"
12025
+ },
12026
+ {
12027
+ name: "nonce",
12028
+ type: "u64"
12029
+ },
12030
+ {
12031
+ name: "expiry",
12032
+ type: "i64"
12033
+ },
12034
+ {
12035
+ name: "is_collected",
12036
+ type: "bool"
12037
+ },
12038
+ {
12039
+ name: "bump",
12040
+ type: "u8"
12041
+ }
12042
+ ]
12043
+ }
12044
+ },
11021
12045
  {
11022
12046
  name: "ComplementaryMatchEvent",
11023
12047
  type: {
@@ -11250,6 +12274,57 @@ var clob_exchange_default = {
11250
12274
  ]
11251
12275
  }
11252
12276
  },
12277
+ {
12278
+ name: "RedeemFeeOrderRecord",
12279
+ docs: [
12280
+ "On-chain record of a signed RedeemFeeOrder.",
12281
+ "Created by `register_redeem_fee_order` after Ed25519 verification.",
12282
+ "Read and marked collected by `batch_redeem_with_fee` / `batch_merge_with_fee`.",
12283
+ "",
12284
+ 'PDA seeds: [b"redeem_fee_order", user, nonce_le_bytes]'
12285
+ ],
12286
+ type: {
12287
+ kind: "struct",
12288
+ fields: [
12289
+ {
12290
+ name: "user",
12291
+ type: "pubkey"
12292
+ },
12293
+ {
12294
+ name: "condition",
12295
+ type: "pubkey"
12296
+ },
12297
+ {
12298
+ name: "token_mint",
12299
+ type: "pubkey"
12300
+ },
12301
+ {
12302
+ name: "amount",
12303
+ type: "u64"
12304
+ },
12305
+ {
12306
+ name: "taker_amount",
12307
+ type: "u64"
12308
+ },
12309
+ {
12310
+ name: "nonce",
12311
+ type: "u64"
12312
+ },
12313
+ {
12314
+ name: "expiry",
12315
+ type: "i64"
12316
+ },
12317
+ {
12318
+ name: "is_collected",
12319
+ type: "bool"
12320
+ },
12321
+ {
12322
+ name: "bump",
12323
+ type: "u8"
12324
+ }
12325
+ ]
12326
+ }
12327
+ },
11253
12328
  {
11254
12329
  name: "SignedOrderRecord",
11255
12330
  docs: [
@@ -17850,6 +18925,7 @@ exports.buildApproveAllOutcomeTokensTx = buildApproveAllOutcomeTokensTx;
17850
18925
  exports.buildApproveCollateralTx = buildApproveCollateralTx;
17851
18926
  exports.buildBatchedCollectFeeEd25519Instruction = buildBatchedCollectFeeEd25519Instruction;
17852
18927
  exports.buildBatchedEd25519Instruction = buildBatchedEd25519Instruction;
18928
+ exports.buildBatchedRedeemFeeEd25519Instruction = buildBatchedRedeemFeeEd25519Instruction;
17853
18929
  exports.buildCreateUserAtasTx = buildCreateUserAtasTx;
17854
18930
  exports.buildOrder = buildOrder;
17855
18931
  exports.buildOrderFromPrice = buildOrderFromPrice;
@@ -17861,6 +18937,7 @@ exports.getOrderSignBytes = getOrderSignBytes;
17861
18937
  exports.orderAmountsFromPrice = orderAmountsFromPrice;
17862
18938
  exports.serializeCollectFeeOrderToBytes = serializeCollectFeeOrderToBytes;
17863
18939
  exports.serializeOrderToBytes = serializeOrderToBytes;
18940
+ exports.serializeRedeemFeeOrderToBytes = serializeRedeemFeeOrderToBytes;
17864
18941
  exports.serializeSignedOrder = serializeSignedOrder;
17865
18942
  exports.signOrder = signOrder;
17866
18943
  exports.signOrderWithKeypair = signOrderWithKeypair;