@theliem/xmarket-sdk 3.23.0 → 3.25.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.d.mts +33 -4
- package/dist/index.d.ts +33 -4
- package/dist/index.js +355 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +355 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -661,6 +661,14 @@ declare class ClobClient {
|
|
|
661
661
|
* Returns undefined if ctfClient/qmConfigPda not injected or marketOracle not configured.
|
|
662
662
|
*/
|
|
663
663
|
private getMarketOracleVault;
|
|
664
|
+
/**
|
|
665
|
+
* Returns a createATA ix for the oracle vault when:
|
|
666
|
+
* - takerFee > 0
|
|
667
|
+
* - marketFeeOverride exists for the condition
|
|
668
|
+
* - oracle vault ATA is not yet initialized
|
|
669
|
+
* Idempotent — safe to call every tx; returns null if vault already exists.
|
|
670
|
+
*/
|
|
671
|
+
private buildInitOracleVaultIfNeeded;
|
|
664
672
|
get walletPubkey(): PublicKey;
|
|
665
673
|
/**
|
|
666
674
|
* Get or create an ALT for a condition.
|
|
@@ -684,6 +692,19 @@ declare class ClobClient {
|
|
|
684
692
|
registerOrder(signed: SignedOrder): Promise<string>;
|
|
685
693
|
/** Register only if the PDA doesn't exist yet (idempotent). */
|
|
686
694
|
private registerOrderIfNeeded;
|
|
695
|
+
/**
|
|
696
|
+
* Build a legacy Transaction to register a CollectFeeOrder on-chain.
|
|
697
|
+
* Must be signed and sent before calling buildBatchCollectRedeemEarlyTx.
|
|
698
|
+
*
|
|
699
|
+
* Flow: [Ed25519 ix (1 user sig) + register_collect_fee_order ix]
|
|
700
|
+
* Caller signs with payer keypair and sends.
|
|
701
|
+
*/
|
|
702
|
+
buildRegisterCollectFeeOrderTx(signedOrder: SignedCollectFeeOrder, payer: PublicKey): Promise<Transaction>;
|
|
703
|
+
/**
|
|
704
|
+
* Register a CollectFeeOrder on-chain and send immediately (operator-signed flow).
|
|
705
|
+
* Idempotent — skips if PDA already exists.
|
|
706
|
+
*/
|
|
707
|
+
registerCollectFeeOrderIfNeeded(signedOrder: SignedCollectFeeOrder): Promise<void>;
|
|
687
708
|
/** Cancel an order — closes its OrderRecord PDA and returns rent to maker. */
|
|
688
709
|
cancelOrder(nonce: anchor.BN): Promise<TxResult>;
|
|
689
710
|
initialize(operators: PublicKey[], feeRecipient: PublicKey, feeRateBps: number): Promise<TxResult>;
|
|
@@ -826,11 +847,13 @@ declare class ClobClient {
|
|
|
826
847
|
/**
|
|
827
848
|
* Build VersionedTransaction for batchCollectRedeemEarly.
|
|
828
849
|
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
850
|
+
* Prerequisite: each user's CollectFeeOrder must be registered on-chain via
|
|
851
|
+
* buildRegisterCollectFeeOrderTx (one tx per user).
|
|
852
|
+
*
|
|
853
|
+
* Flow: batchCollectRedeemEarly ix reads CollectFeeOrderRecord PDAs — no Ed25519 inline.
|
|
854
|
+
* No tx-size limit from signatures, supports 10+ orders in one tx.
|
|
832
855
|
*
|
|
833
|
-
* @param signedOrders - Array of { order, signature }
|
|
856
|
+
* @param signedOrders - Array of { order, signature } (used to derive record PDAs)
|
|
834
857
|
* @param condition - Market condition PDA
|
|
835
858
|
* @param outcomeIndex - 0 = NO wins, 1 = YES wins
|
|
836
859
|
* @param operator - Whitelisted operator pubkey (must sign)
|
|
@@ -841,6 +864,11 @@ declare class ClobClient {
|
|
|
841
864
|
marketOracleVault?: PublicKey;
|
|
842
865
|
lookupTable?: AddressLookupTableAccount;
|
|
843
866
|
}): Promise<VersionedTransaction>;
|
|
867
|
+
/**
|
|
868
|
+
* Build ALT for batchCollectRedeemEarly — includes per-user accounts so all
|
|
869
|
+
* remaining_accounts fit as 1-byte ALT indices instead of 32-byte inline addresses.
|
|
870
|
+
*/
|
|
871
|
+
buildAltForCollectBatch(condition: PublicKey, payer: PublicKey, collateralMint: PublicKey, signedOrders: SignedCollectFeeOrder[], outcomeIndex: 0 | 1): Promise<AddressLookupTableAccount>;
|
|
844
872
|
/**
|
|
845
873
|
* Build ALT with static accounts for a condition — no order-specific PDAs needed.
|
|
846
874
|
* Use before buildBatchCollectRedeemEarlyTx when no prior buildMatchOrdersTx ran in this session.
|
|
@@ -1158,6 +1186,7 @@ declare class PDA {
|
|
|
1158
1186
|
static clobConfig(programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1159
1187
|
static orderStatus(maker: PublicKey, nonce: BN, programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1160
1188
|
static orderRecord(maker: PublicKey, nonce: BN, programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1189
|
+
static collectFeeOrderRecord(user: PublicKey, nonce: BN, programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1161
1190
|
static feeConfig(owner: PublicKey, programIds: Pick<ProgramIds, "feeManagement">): [PublicKey, number];
|
|
1162
1191
|
static questionFee(conditionPda: PublicKey, programIds: Pick<ProgramIds, "feeManagement">): [PublicKey, number];
|
|
1163
1192
|
static marketFeeOverride(conditionPda: PublicKey, programIds: Pick<ProgramIds, "feeManagement">): [PublicKey, number];
|
package/dist/index.d.ts
CHANGED
|
@@ -661,6 +661,14 @@ declare class ClobClient {
|
|
|
661
661
|
* Returns undefined if ctfClient/qmConfigPda not injected or marketOracle not configured.
|
|
662
662
|
*/
|
|
663
663
|
private getMarketOracleVault;
|
|
664
|
+
/**
|
|
665
|
+
* Returns a createATA ix for the oracle vault when:
|
|
666
|
+
* - takerFee > 0
|
|
667
|
+
* - marketFeeOverride exists for the condition
|
|
668
|
+
* - oracle vault ATA is not yet initialized
|
|
669
|
+
* Idempotent — safe to call every tx; returns null if vault already exists.
|
|
670
|
+
*/
|
|
671
|
+
private buildInitOracleVaultIfNeeded;
|
|
664
672
|
get walletPubkey(): PublicKey;
|
|
665
673
|
/**
|
|
666
674
|
* Get or create an ALT for a condition.
|
|
@@ -684,6 +692,19 @@ declare class ClobClient {
|
|
|
684
692
|
registerOrder(signed: SignedOrder): Promise<string>;
|
|
685
693
|
/** Register only if the PDA doesn't exist yet (idempotent). */
|
|
686
694
|
private registerOrderIfNeeded;
|
|
695
|
+
/**
|
|
696
|
+
* Build a legacy Transaction to register a CollectFeeOrder on-chain.
|
|
697
|
+
* Must be signed and sent before calling buildBatchCollectRedeemEarlyTx.
|
|
698
|
+
*
|
|
699
|
+
* Flow: [Ed25519 ix (1 user sig) + register_collect_fee_order ix]
|
|
700
|
+
* Caller signs with payer keypair and sends.
|
|
701
|
+
*/
|
|
702
|
+
buildRegisterCollectFeeOrderTx(signedOrder: SignedCollectFeeOrder, payer: PublicKey): Promise<Transaction>;
|
|
703
|
+
/**
|
|
704
|
+
* Register a CollectFeeOrder on-chain and send immediately (operator-signed flow).
|
|
705
|
+
* Idempotent — skips if PDA already exists.
|
|
706
|
+
*/
|
|
707
|
+
registerCollectFeeOrderIfNeeded(signedOrder: SignedCollectFeeOrder): Promise<void>;
|
|
687
708
|
/** Cancel an order — closes its OrderRecord PDA and returns rent to maker. */
|
|
688
709
|
cancelOrder(nonce: anchor.BN): Promise<TxResult>;
|
|
689
710
|
initialize(operators: PublicKey[], feeRecipient: PublicKey, feeRateBps: number): Promise<TxResult>;
|
|
@@ -826,11 +847,13 @@ declare class ClobClient {
|
|
|
826
847
|
/**
|
|
827
848
|
* Build VersionedTransaction for batchCollectRedeemEarly.
|
|
828
849
|
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
850
|
+
* Prerequisite: each user's CollectFeeOrder must be registered on-chain via
|
|
851
|
+
* buildRegisterCollectFeeOrderTx (one tx per user).
|
|
852
|
+
*
|
|
853
|
+
* Flow: batchCollectRedeemEarly ix reads CollectFeeOrderRecord PDAs — no Ed25519 inline.
|
|
854
|
+
* No tx-size limit from signatures, supports 10+ orders in one tx.
|
|
832
855
|
*
|
|
833
|
-
* @param signedOrders - Array of { order, signature }
|
|
856
|
+
* @param signedOrders - Array of { order, signature } (used to derive record PDAs)
|
|
834
857
|
* @param condition - Market condition PDA
|
|
835
858
|
* @param outcomeIndex - 0 = NO wins, 1 = YES wins
|
|
836
859
|
* @param operator - Whitelisted operator pubkey (must sign)
|
|
@@ -841,6 +864,11 @@ declare class ClobClient {
|
|
|
841
864
|
marketOracleVault?: PublicKey;
|
|
842
865
|
lookupTable?: AddressLookupTableAccount;
|
|
843
866
|
}): Promise<VersionedTransaction>;
|
|
867
|
+
/**
|
|
868
|
+
* Build ALT for batchCollectRedeemEarly — includes per-user accounts so all
|
|
869
|
+
* remaining_accounts fit as 1-byte ALT indices instead of 32-byte inline addresses.
|
|
870
|
+
*/
|
|
871
|
+
buildAltForCollectBatch(condition: PublicKey, payer: PublicKey, collateralMint: PublicKey, signedOrders: SignedCollectFeeOrder[], outcomeIndex: 0 | 1): Promise<AddressLookupTableAccount>;
|
|
844
872
|
/**
|
|
845
873
|
* Build ALT with static accounts for a condition — no order-specific PDAs needed.
|
|
846
874
|
* Use before buildBatchCollectRedeemEarlyTx when no prior buildMatchOrdersTx ran in this session.
|
|
@@ -1158,6 +1186,7 @@ declare class PDA {
|
|
|
1158
1186
|
static clobConfig(programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1159
1187
|
static orderStatus(maker: PublicKey, nonce: BN, programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1160
1188
|
static orderRecord(maker: PublicKey, nonce: BN, programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1189
|
+
static collectFeeOrderRecord(user: PublicKey, nonce: BN, programIds: Pick<ProgramIds, "clobExchange">): [PublicKey, number];
|
|
1161
1190
|
static feeConfig(owner: PublicKey, programIds: Pick<ProgramIds, "feeManagement">): [PublicKey, number];
|
|
1162
1191
|
static questionFee(conditionPda: PublicKey, programIds: Pick<ProgramIds, "feeManagement">): [PublicKey, number];
|
|
1163
1192
|
static marketFeeOverride(conditionPda: PublicKey, programIds: Pick<ProgramIds, "feeManagement">): [PublicKey, number];
|
package/dist/index.js
CHANGED
|
@@ -187,6 +187,14 @@ 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
|
+
}
|
|
190
198
|
// ─── Fee Management ─────────────────────────────────────────────────────────
|
|
191
199
|
static feeConfig(owner, programIds) {
|
|
192
200
|
if (!programIds.feeManagement) throw new Error("feeManagement program ID not configured");
|
|
@@ -1644,6 +1652,39 @@ var ClobClient = class {
|
|
|
1644
1652
|
this._marketOracleVaultCache.set(key, vault);
|
|
1645
1653
|
return vault;
|
|
1646
1654
|
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Returns a createATA ix for the oracle vault when:
|
|
1657
|
+
* - takerFee > 0
|
|
1658
|
+
* - marketFeeOverride exists for the condition
|
|
1659
|
+
* - oracle vault ATA is not yet initialized
|
|
1660
|
+
* Idempotent — safe to call every tx; returns null if vault already exists.
|
|
1661
|
+
*/
|
|
1662
|
+
async buildInitOracleVaultIfNeeded(condition, collateralMint, takerFee, payer) {
|
|
1663
|
+
if (takerFee.isZero()) return null;
|
|
1664
|
+
if (!this.feeConfigOwner || !this.programIds.feeManagement) return null;
|
|
1665
|
+
if (!this.ctfClient || !this.qmConfigPda || !this.programIds.marketOracle) return null;
|
|
1666
|
+
const companyAddr = await this.companyAddress();
|
|
1667
|
+
const refVault = await this.referralVault();
|
|
1668
|
+
if (!companyAddr || !refVault) return null;
|
|
1669
|
+
const feeOverridePda = PDA.marketFeeOverride(condition, this.programIds)[0];
|
|
1670
|
+
const cond = await this.ctfClient.fetchCondition(condition);
|
|
1671
|
+
if (!cond) return null;
|
|
1672
|
+
const [questionPda] = PDA.question(this.qmConfigPda, cond.questionId, this.programIds);
|
|
1673
|
+
const [marketOraclePda] = PDA.marketOraclePda(questionPda, this.programIds);
|
|
1674
|
+
const vault = splToken.getAssociatedTokenAddressSync(collateralMint, marketOraclePda, true);
|
|
1675
|
+
const [feeOverrideInfo, vaultInfo] = await Promise.all([
|
|
1676
|
+
this.provider.connection.getAccountInfo(feeOverridePda),
|
|
1677
|
+
this.provider.connection.getAccountInfo(vault)
|
|
1678
|
+
]);
|
|
1679
|
+
if (!feeOverrideInfo) return null;
|
|
1680
|
+
if (vaultInfo) return null;
|
|
1681
|
+
return splToken.createAssociatedTokenAccountIdempotentInstruction(
|
|
1682
|
+
payer,
|
|
1683
|
+
vault,
|
|
1684
|
+
marketOraclePda,
|
|
1685
|
+
collateralMint
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1647
1688
|
get walletPubkey() {
|
|
1648
1689
|
return this.provider.wallet.publicKey;
|
|
1649
1690
|
}
|
|
@@ -1782,8 +1823,8 @@ var ClobClient = class {
|
|
|
1782
1823
|
async _sendLegacyTxSig(instructions) {
|
|
1783
1824
|
const { connection } = this.provider;
|
|
1784
1825
|
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
1785
|
-
const { Transaction:
|
|
1786
|
-
const tx = new
|
|
1826
|
+
const { Transaction: Transaction12 } = await import('@solana/web3.js');
|
|
1827
|
+
const tx = new Transaction12();
|
|
1787
1828
|
tx.recentBlockhash = blockhash;
|
|
1788
1829
|
tx.feePayer = this.walletPubkey;
|
|
1789
1830
|
tx.add(...instructions);
|
|
@@ -1809,10 +1850,10 @@ ${logs.join("\n")}`);
|
|
|
1809
1850
|
connection.getAccountInfo(clobYesAta),
|
|
1810
1851
|
connection.getAccountInfo(clobNoAta)
|
|
1811
1852
|
]);
|
|
1812
|
-
const { createAssociatedTokenAccountIdempotentInstruction:
|
|
1853
|
+
const { createAssociatedTokenAccountIdempotentInstruction: createAssociatedTokenAccountIdempotentInstruction5 } = await import('@solana/spl-token');
|
|
1813
1854
|
const ixs = [];
|
|
1814
1855
|
if (!yesInfo) {
|
|
1815
|
-
ixs.push(
|
|
1856
|
+
ixs.push(createAssociatedTokenAccountIdempotentInstruction5(
|
|
1816
1857
|
this.walletPubkey,
|
|
1817
1858
|
clobYesAta,
|
|
1818
1859
|
clobConfig,
|
|
@@ -1821,7 +1862,7 @@ ${logs.join("\n")}`);
|
|
|
1821
1862
|
));
|
|
1822
1863
|
}
|
|
1823
1864
|
if (!noInfo) {
|
|
1824
|
-
ixs.push(
|
|
1865
|
+
ixs.push(createAssociatedTokenAccountIdempotentInstruction5(
|
|
1825
1866
|
this.walletPubkey,
|
|
1826
1867
|
clobNoAta,
|
|
1827
1868
|
clobConfig,
|
|
@@ -1904,6 +1945,49 @@ ${logs.join("\n")}`);
|
|
|
1904
1945
|
await this.registerOrder(signed);
|
|
1905
1946
|
}
|
|
1906
1947
|
}
|
|
1948
|
+
// ─── Register CollectFeeOrder ────────────────────────────────────────────────
|
|
1949
|
+
/**
|
|
1950
|
+
* Build a legacy Transaction to register a CollectFeeOrder on-chain.
|
|
1951
|
+
* Must be signed and sent before calling buildBatchCollectRedeemEarlyTx.
|
|
1952
|
+
*
|
|
1953
|
+
* Flow: [Ed25519 ix (1 user sig) + register_collect_fee_order ix]
|
|
1954
|
+
* Caller signs with payer keypair and sends.
|
|
1955
|
+
*/
|
|
1956
|
+
async buildRegisterCollectFeeOrderTx(signedOrder, payer) {
|
|
1957
|
+
const { order } = signedOrder;
|
|
1958
|
+
const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
|
|
1959
|
+
const ed25519Ix = buildBatchedCollectFeeEd25519Instruction([signedOrder]);
|
|
1960
|
+
const registerIx = await this.program.methods.registerCollectFeeOrder(order.nonce).accounts({
|
|
1961
|
+
payer,
|
|
1962
|
+
clobConfig: this.configPda(),
|
|
1963
|
+
ixSysvar: IX_SYSVAR,
|
|
1964
|
+
orderSigner: order.user,
|
|
1965
|
+
collectFeeOrderRecord,
|
|
1966
|
+
systemProgram: web3_js.SystemProgram.programId
|
|
1967
|
+
}).instruction();
|
|
1968
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
1969
|
+
const tx = new web3_js.Transaction();
|
|
1970
|
+
tx.recentBlockhash = blockhash;
|
|
1971
|
+
tx.feePayer = payer;
|
|
1972
|
+
tx.add(ed25519Ix, registerIx);
|
|
1973
|
+
return tx;
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Register a CollectFeeOrder on-chain and send immediately (operator-signed flow).
|
|
1977
|
+
* Idempotent — skips if PDA already exists.
|
|
1978
|
+
*/
|
|
1979
|
+
async registerCollectFeeOrderIfNeeded(signedOrder) {
|
|
1980
|
+
const [pda] = PDA.collectFeeOrderRecord(
|
|
1981
|
+
signedOrder.order.user,
|
|
1982
|
+
signedOrder.order.nonce,
|
|
1983
|
+
this.programIds
|
|
1984
|
+
);
|
|
1985
|
+
const existing = await this.program.account.collectFeeOrderRecord?.fetchNullable(pda);
|
|
1986
|
+
if (!existing) {
|
|
1987
|
+
const tx = await this.buildRegisterCollectFeeOrderTx(signedOrder, this.walletPubkey);
|
|
1988
|
+
await this._sendLegacyTx(tx.instructions);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1907
1991
|
/** Cancel an order — closes its OrderRecord PDA and returns rent to maker. */
|
|
1908
1992
|
async cancelOrder(nonce) {
|
|
1909
1993
|
const [orderRecord] = PDA.orderRecord(this.walletPubkey, nonce, this.programIds);
|
|
@@ -2486,6 +2570,13 @@ ${logs.join("\n")}`);
|
|
|
2486
2570
|
if (yIx) hookInitIxs.push(yIx);
|
|
2487
2571
|
if (nIx) hookInitIxs.push(nIx);
|
|
2488
2572
|
}
|
|
2573
|
+
const oracleVaultInitIx = await this.buildInitOracleVaultIfNeeded(
|
|
2574
|
+
t.condition,
|
|
2575
|
+
collateralMint,
|
|
2576
|
+
t.fee,
|
|
2577
|
+
payer
|
|
2578
|
+
);
|
|
2579
|
+
const preIxs = oracleVaultInitIx ? [...hookInitIxs, oracleVaultInitIx] : hookInitIxs;
|
|
2489
2580
|
if (t.tokenId === m0.tokenId) {
|
|
2490
2581
|
let buySignedOrder, sellCandidates;
|
|
2491
2582
|
if (t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_SELL)) {
|
|
@@ -2505,7 +2596,7 @@ ${logs.join("\n")}`);
|
|
|
2505
2596
|
opts,
|
|
2506
2597
|
false
|
|
2507
2598
|
);
|
|
2508
|
-
return this._buildUnsignedVtx([...
|
|
2599
|
+
return this._buildUnsignedVtx([...preIxs, ...ixs3], alt, payer);
|
|
2509
2600
|
} else {
|
|
2510
2601
|
throw new InvalidParamError("COMPLEMENTARY requires one BUY and one or more SELLs on same tokenId");
|
|
2511
2602
|
}
|
|
@@ -2521,7 +2612,7 @@ ${logs.join("\n")}`);
|
|
|
2521
2612
|
operator,
|
|
2522
2613
|
opts
|
|
2523
2614
|
);
|
|
2524
|
-
return this._buildUnsignedVtx([...
|
|
2615
|
+
return this._buildUnsignedVtx([...preIxs, ...ixs2], alt, payer);
|
|
2525
2616
|
}
|
|
2526
2617
|
const allBuy = t.side === SIDE_BUY && makers.every((m) => m.order.side === SIDE_BUY);
|
|
2527
2618
|
const allSell = t.side === SIDE_SELL && makers.every((m) => m.order.side === SIDE_SELL);
|
|
@@ -2542,7 +2633,7 @@ ${logs.join("\n")}`);
|
|
|
2542
2633
|
]);
|
|
2543
2634
|
await this._ensureClobOutcomeAtas(yesMint, noMint, clobConfig, clobYesAta, clobNoAta);
|
|
2544
2635
|
const ix = allBuy ? await this._buildMintIx(taker, makers, collateralMint, operator, payer) : await this._buildMergeIx(taker, makers, collateralMint, operator, payer, opts);
|
|
2545
|
-
return this._buildUnsignedVtx([...
|
|
2636
|
+
return this._buildUnsignedVtx([...preIxs, ix], alt, payer);
|
|
2546
2637
|
}
|
|
2547
2638
|
await Promise.all([
|
|
2548
2639
|
this.registerOrderIfNeeded(taker),
|
|
@@ -2554,17 +2645,19 @@ ${logs.join("\n")}`);
|
|
|
2554
2645
|
const ix = allBuy ? await this._buildMintIx(yesMaker, [taker], collateralMint, operator, payer) : await this._buildMergeIx(yesMaker, [taker], collateralMint, operator, payer, opts);
|
|
2555
2646
|
ixs.push(ix);
|
|
2556
2647
|
}
|
|
2557
|
-
return this._buildUnsignedVtx([...
|
|
2648
|
+
return this._buildUnsignedVtx([...preIxs, ...ixs], alt, payer);
|
|
2558
2649
|
}
|
|
2559
2650
|
// ─── batchCollectRedeemEarly ─────────────────────────────────────────────────
|
|
2560
2651
|
/**
|
|
2561
2652
|
* Build VersionedTransaction for batchCollectRedeemEarly.
|
|
2562
2653
|
*
|
|
2563
|
-
*
|
|
2564
|
-
*
|
|
2565
|
-
* from that user, redeem via CTF, then distribute as fees.
|
|
2654
|
+
* Prerequisite: each user's CollectFeeOrder must be registered on-chain via
|
|
2655
|
+
* buildRegisterCollectFeeOrderTx (one tx per user).
|
|
2566
2656
|
*
|
|
2567
|
-
*
|
|
2657
|
+
* Flow: batchCollectRedeemEarly ix reads CollectFeeOrderRecord PDAs — no Ed25519 inline.
|
|
2658
|
+
* No tx-size limit from signatures, supports 10+ orders in one tx.
|
|
2659
|
+
*
|
|
2660
|
+
* @param signedOrders - Array of { order, signature } (used to derive record PDAs)
|
|
2568
2661
|
* @param condition - Market condition PDA
|
|
2569
2662
|
* @param outcomeIndex - 0 = NO wins, 1 = YES wins
|
|
2570
2663
|
* @param operator - Whitelisted operator pubkey (must sign)
|
|
@@ -2590,9 +2683,11 @@ ${logs.join("\n")}`);
|
|
|
2590
2683
|
const [clobNoPosition] = PDA.position(condition, 0, clobConfig, this.programIds);
|
|
2591
2684
|
const userAccounts = [];
|
|
2592
2685
|
for (const { order } of signedOrders) {
|
|
2686
|
+
const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
|
|
2593
2687
|
const userTokenAta = splToken.getAssociatedTokenAddressSync(outcomeMint, order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
|
|
2594
2688
|
const [userPosition] = PDA.position(condition, outcomeIndex, order.user, this.programIds);
|
|
2595
2689
|
userAccounts.push(
|
|
2690
|
+
{ pubkey: collectFeeOrderRecord, isSigner: false, isWritable: true },
|
|
2596
2691
|
{ pubkey: order.user, isSigner: false, isWritable: false },
|
|
2597
2692
|
{ pubkey: userTokenAta, isSigner: false, isWritable: true },
|
|
2598
2693
|
{ pubkey: userPosition, isSigner: false, isWritable: true }
|
|
@@ -2629,11 +2724,7 @@ ${logs.join("\n")}`);
|
|
|
2629
2724
|
const ix = await this.hookClient.buildInitHookIxIfNeeded(outcomeMint, payer);
|
|
2630
2725
|
if (ix) hookInitIxs.push(ix);
|
|
2631
2726
|
}
|
|
2632
|
-
const ed25519Ix = buildBatchedCollectFeeEd25519Instruction(signedOrders);
|
|
2633
|
-
const ed25519IxIndex = 2 + hookInitIxs.length;
|
|
2634
2727
|
const collectIx = await this.program.methods.batchCollectRedeemEarly(
|
|
2635
|
-
ed25519IxIndex,
|
|
2636
|
-
// ix_index: adjusted for any prepended hook init ixs
|
|
2637
2728
|
signedOrders.length,
|
|
2638
2729
|
outcomeIndex
|
|
2639
2730
|
).accounts({
|
|
@@ -2650,14 +2741,96 @@ ${logs.join("\n")}`);
|
|
|
2650
2741
|
clobNoAta,
|
|
2651
2742
|
clobYesPosition,
|
|
2652
2743
|
clobNoPosition,
|
|
2653
|
-
ixSysvar: IX_SYSVAR,
|
|
2654
2744
|
conditionalTokensProgram: this.programIds.conditionalTokens,
|
|
2655
2745
|
tokenProgram: splToken.TOKEN_PROGRAM_ID,
|
|
2656
2746
|
token2022Program: splToken.TOKEN_2022_PROGRAM_ID,
|
|
2657
2747
|
systemProgram: web3_js.SystemProgram.programId
|
|
2658
2748
|
}).remainingAccounts([...userAccounts, ...hookAccounts, ...feeAccounts]).instruction();
|
|
2659
|
-
const alt = opts?.lookupTable ??
|
|
2660
|
-
return this._buildUnsignedVtx([...hookInitIxs,
|
|
2749
|
+
const alt = opts?.lookupTable ?? await this.buildAltForCollectBatch(condition, payer, collateralMint, signedOrders, outcomeIndex);
|
|
2750
|
+
return this._buildUnsignedVtx([...hookInitIxs, collectIx], alt, payer);
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Build ALT for batchCollectRedeemEarly — includes per-user accounts so all
|
|
2754
|
+
* remaining_accounts fit as 1-byte ALT indices instead of 32-byte inline addresses.
|
|
2755
|
+
*/
|
|
2756
|
+
async buildAltForCollectBatch(condition, payer, collateralMint, signedOrders, outcomeIndex) {
|
|
2757
|
+
const userKeys = signedOrders.map((o) => o.order.user.toBase58()).sort((a, b) => a.localeCompare(b)).join(",");
|
|
2758
|
+
const cacheKey = `${condition.toBase58()}:collect:${userKeys}`;
|
|
2759
|
+
if (this._altCache.has(cacheKey)) return this._altCache.get(cacheKey);
|
|
2760
|
+
const { connection } = this.provider;
|
|
2761
|
+
const clobConfigPda = this.configPda();
|
|
2762
|
+
const [yesMint] = PDA.yesMint(condition, this.programIds);
|
|
2763
|
+
const [noMint] = PDA.noMint(condition, this.programIds);
|
|
2764
|
+
const outcomeMint = outcomeIndex === 1 ? yesMint : noMint;
|
|
2765
|
+
const [extraAccountMeta] = PDA.extraAccountMetaList(outcomeMint, this.programIds);
|
|
2766
|
+
const [hookConfig] = PDA.hookConfig(this.programIds);
|
|
2767
|
+
const [collateralVault] = PDA.collateralVault(collateralMint, this.programIds);
|
|
2768
|
+
const [vaultTokenAccount] = PDA.vaultToken(collateralMint, this.programIds);
|
|
2769
|
+
const clobYesAta = splToken.getAssociatedTokenAddressSync(yesMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
|
|
2770
|
+
const clobNoAta = splToken.getAssociatedTokenAddressSync(noMint, clobConfigPda, true, splToken.TOKEN_2022_PROGRAM_ID);
|
|
2771
|
+
const [clobYesPos] = PDA.position(condition, 1, clobConfigPda, this.programIds);
|
|
2772
|
+
const [clobNoPos] = PDA.position(condition, 0, clobConfigPda, this.programIds);
|
|
2773
|
+
const feeRecipientAddr = (await this.fetchConfig())?.feeRecipient;
|
|
2774
|
+
const addresses = [
|
|
2775
|
+
this.programIds.clobExchange,
|
|
2776
|
+
this.programIds.conditionalTokens,
|
|
2777
|
+
this.programIds.hook,
|
|
2778
|
+
splToken.TOKEN_PROGRAM_ID,
|
|
2779
|
+
splToken.TOKEN_2022_PROGRAM_ID,
|
|
2780
|
+
web3_js.SystemProgram.programId,
|
|
2781
|
+
clobConfigPda,
|
|
2782
|
+
hookConfig,
|
|
2783
|
+
condition,
|
|
2784
|
+
yesMint,
|
|
2785
|
+
noMint,
|
|
2786
|
+
extraAccountMeta,
|
|
2787
|
+
collateralVault,
|
|
2788
|
+
vaultTokenAccount,
|
|
2789
|
+
clobYesAta,
|
|
2790
|
+
clobNoAta,
|
|
2791
|
+
clobYesPos,
|
|
2792
|
+
clobNoPos
|
|
2793
|
+
];
|
|
2794
|
+
if (feeRecipientAddr) addresses.push(feeRecipientAddr);
|
|
2795
|
+
if (this.feeConfigOwner && this.programIds.feeManagement) {
|
|
2796
|
+
const companyAddr = await this.companyAddress();
|
|
2797
|
+
const refVault = await this.referralVault();
|
|
2798
|
+
addresses.push(this.programIds.feeManagement);
|
|
2799
|
+
addresses.push(PDA.feeConfig(this.feeConfigOwner, this.programIds)[0]);
|
|
2800
|
+
addresses.push(PDA.marketFeeOverride(condition, this.programIds)[0]);
|
|
2801
|
+
if (companyAddr) addresses.push(splToken.getAssociatedTokenAddressSync(collateralMint, companyAddr));
|
|
2802
|
+
if (refVault) addresses.push(refVault);
|
|
2803
|
+
const oracleVault = await this.getMarketOracleVault(condition, collateralMint) ?? payer;
|
|
2804
|
+
addresses.push(oracleVault);
|
|
2805
|
+
}
|
|
2806
|
+
for (const { order } of signedOrders) {
|
|
2807
|
+
const [collectFeeOrderRecord] = PDA.collectFeeOrderRecord(order.user, order.nonce, this.programIds);
|
|
2808
|
+
const userTokenAta = splToken.getAssociatedTokenAddressSync(outcomeMint, order.user, false, splToken.TOKEN_2022_PROGRAM_ID);
|
|
2809
|
+
const [userPosition] = PDA.position(condition, outcomeIndex, order.user, this.programIds);
|
|
2810
|
+
addresses.push(collectFeeOrderRecord, order.user, userTokenAta, userPosition);
|
|
2811
|
+
}
|
|
2812
|
+
const slot = await connection.getSlot("finalized");
|
|
2813
|
+
const [createIx, altAddress] = web3_js.AddressLookupTableProgram.createLookupTable(
|
|
2814
|
+
{ authority: payer, payer, recentSlot: slot }
|
|
2815
|
+
);
|
|
2816
|
+
const BATCH = 30;
|
|
2817
|
+
const extendIxs = [];
|
|
2818
|
+
for (let i = 0; i < addresses.length; i += BATCH) {
|
|
2819
|
+
extendIxs.push(web3_js.AddressLookupTableProgram.extendLookupTable(
|
|
2820
|
+
{ payer, authority: payer, lookupTable: altAddress, addresses: addresses.slice(i, i + BATCH) }
|
|
2821
|
+
));
|
|
2822
|
+
}
|
|
2823
|
+
await this._sendLegacyTx([createIx, extendIxs[0]]);
|
|
2824
|
+
for (let i = 1; i < extendIxs.length; i++) await this._sendLegacyTx([extendIxs[i]]);
|
|
2825
|
+
for (let attempt = 0; attempt < 30; attempt++) {
|
|
2826
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
2827
|
+
const res = await connection.getAddressLookupTable(altAddress);
|
|
2828
|
+
if (res.value && res.value.state.addresses.length === addresses.length) {
|
|
2829
|
+
this._altCache.set(cacheKey, res.value);
|
|
2830
|
+
return res.value;
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
throw new Error(`ALT ${altAddress.toBase58()} not active after 30s`);
|
|
2661
2834
|
}
|
|
2662
2835
|
/**
|
|
2663
2836
|
* Build ALT with static accounts for a condition — no order-specific PDAs needed.
|
|
@@ -9420,7 +9593,8 @@ var clob_exchange_default = {
|
|
|
9420
9593
|
name: "batch_collect_redeem_early",
|
|
9421
9594
|
docs: [
|
|
9422
9595
|
"Batch collect fee tokens from winning users after question resolution,",
|
|
9423
|
-
"redeem them via CTF, and distribute USDS via fee_management."
|
|
9596
|
+
"redeem them via CTF, and distribute USDS via fee_management.",
|
|
9597
|
+
"Requires each user's CollectFeeOrderRecord to be registered via register_collect_fee_order."
|
|
9424
9598
|
],
|
|
9425
9599
|
discriminator: [
|
|
9426
9600
|
87,
|
|
@@ -9530,7 +9704,7 @@ var clob_exchange_default = {
|
|
|
9530
9704
|
{
|
|
9531
9705
|
name: "clob_yes_ata",
|
|
9532
9706
|
docs: [
|
|
9533
|
-
"CLOB's YES ATA (Token-2022) \u2014 must be pre-initialized
|
|
9707
|
+
"CLOB's YES ATA (Token-2022) \u2014 must be pre-initialized."
|
|
9534
9708
|
],
|
|
9535
9709
|
writable: true
|
|
9536
9710
|
},
|
|
@@ -9549,9 +9723,6 @@ var clob_exchange_default = {
|
|
|
9549
9723
|
name: "clob_no_position",
|
|
9550
9724
|
writable: true
|
|
9551
9725
|
},
|
|
9552
|
-
{
|
|
9553
|
-
name: "ix_sysvar"
|
|
9554
|
-
},
|
|
9555
9726
|
{
|
|
9556
9727
|
name: "conditional_tokens_program",
|
|
9557
9728
|
address: "A6N1F8MRsdgcojAx8p6FaECvw8mo8w6qJcWsbKQBANK4"
|
|
@@ -9570,10 +9741,6 @@ var clob_exchange_default = {
|
|
|
9570
9741
|
}
|
|
9571
9742
|
],
|
|
9572
9743
|
args: [
|
|
9573
|
-
{
|
|
9574
|
-
name: "ix_index",
|
|
9575
|
-
type: "u8"
|
|
9576
|
-
},
|
|
9577
9744
|
{
|
|
9578
9745
|
name: "order_count",
|
|
9579
9746
|
type: "u8"
|
|
@@ -10519,6 +10686,105 @@ var clob_exchange_default = {
|
|
|
10519
10686
|
}
|
|
10520
10687
|
]
|
|
10521
10688
|
},
|
|
10689
|
+
{
|
|
10690
|
+
name: "register_collect_fee_order",
|
|
10691
|
+
docs: [
|
|
10692
|
+
"Register a signed CollectFeeOrder on-chain (Ed25519 verify at registration time).",
|
|
10693
|
+
"ix[0] must be Ed25519 precompile with 1 entry for order.user.",
|
|
10694
|
+
"Call once per user before batch_collect_redeem_early."
|
|
10695
|
+
],
|
|
10696
|
+
discriminator: [
|
|
10697
|
+
63,
|
|
10698
|
+
26,
|
|
10699
|
+
202,
|
|
10700
|
+
98,
|
|
10701
|
+
195,
|
|
10702
|
+
2,
|
|
10703
|
+
211,
|
|
10704
|
+
36
|
|
10705
|
+
],
|
|
10706
|
+
accounts: [
|
|
10707
|
+
{
|
|
10708
|
+
name: "payer",
|
|
10709
|
+
writable: true,
|
|
10710
|
+
signer: true
|
|
10711
|
+
},
|
|
10712
|
+
{
|
|
10713
|
+
name: "clob_config",
|
|
10714
|
+
pda: {
|
|
10715
|
+
seeds: [
|
|
10716
|
+
{
|
|
10717
|
+
kind: "const",
|
|
10718
|
+
value: [
|
|
10719
|
+
99,
|
|
10720
|
+
108,
|
|
10721
|
+
111,
|
|
10722
|
+
98,
|
|
10723
|
+
95,
|
|
10724
|
+
99,
|
|
10725
|
+
111,
|
|
10726
|
+
110,
|
|
10727
|
+
102,
|
|
10728
|
+
105,
|
|
10729
|
+
103
|
|
10730
|
+
]
|
|
10731
|
+
}
|
|
10732
|
+
]
|
|
10733
|
+
}
|
|
10734
|
+
},
|
|
10735
|
+
{
|
|
10736
|
+
name: "ix_sysvar",
|
|
10737
|
+
address: "Sysvar1nstructions1111111111111111111111111"
|
|
10738
|
+
},
|
|
10739
|
+
{
|
|
10740
|
+
name: "order_signer"
|
|
10741
|
+
},
|
|
10742
|
+
{
|
|
10743
|
+
name: "collect_fee_order_record",
|
|
10744
|
+
writable: true,
|
|
10745
|
+
pda: {
|
|
10746
|
+
seeds: [
|
|
10747
|
+
{
|
|
10748
|
+
kind: "const",
|
|
10749
|
+
value: [
|
|
10750
|
+
99,
|
|
10751
|
+
111,
|
|
10752
|
+
108,
|
|
10753
|
+
108,
|
|
10754
|
+
101,
|
|
10755
|
+
99,
|
|
10756
|
+
116,
|
|
10757
|
+
95,
|
|
10758
|
+
111,
|
|
10759
|
+
114,
|
|
10760
|
+
100,
|
|
10761
|
+
101,
|
|
10762
|
+
114
|
|
10763
|
+
]
|
|
10764
|
+
},
|
|
10765
|
+
{
|
|
10766
|
+
kind: "account",
|
|
10767
|
+
path: "order_signer"
|
|
10768
|
+
},
|
|
10769
|
+
{
|
|
10770
|
+
kind: "arg",
|
|
10771
|
+
path: "nonce"
|
|
10772
|
+
}
|
|
10773
|
+
]
|
|
10774
|
+
}
|
|
10775
|
+
},
|
|
10776
|
+
{
|
|
10777
|
+
name: "system_program",
|
|
10778
|
+
address: "11111111111111111111111111111111"
|
|
10779
|
+
}
|
|
10780
|
+
],
|
|
10781
|
+
args: [
|
|
10782
|
+
{
|
|
10783
|
+
name: "nonce",
|
|
10784
|
+
type: "u64"
|
|
10785
|
+
}
|
|
10786
|
+
]
|
|
10787
|
+
},
|
|
10522
10788
|
{
|
|
10523
10789
|
name: "register_order",
|
|
10524
10790
|
docs: [
|
|
@@ -10809,6 +11075,19 @@ var clob_exchange_default = {
|
|
|
10809
11075
|
160
|
|
10810
11076
|
]
|
|
10811
11077
|
},
|
|
11078
|
+
{
|
|
11079
|
+
name: "CollectFeeOrderRecord",
|
|
11080
|
+
discriminator: [
|
|
11081
|
+
145,
|
|
11082
|
+
140,
|
|
11083
|
+
193,
|
|
11084
|
+
74,
|
|
11085
|
+
12,
|
|
11086
|
+
98,
|
|
11087
|
+
74,
|
|
11088
|
+
130
|
|
11089
|
+
]
|
|
11090
|
+
},
|
|
10812
11091
|
{
|
|
10813
11092
|
name: "OrderStatus",
|
|
10814
11093
|
discriminator: [
|
|
@@ -11018,6 +11297,53 @@ var clob_exchange_default = {
|
|
|
11018
11297
|
]
|
|
11019
11298
|
}
|
|
11020
11299
|
},
|
|
11300
|
+
{
|
|
11301
|
+
name: "CollectFeeOrderRecord",
|
|
11302
|
+
docs: [
|
|
11303
|
+
"On-chain record of a signed CollectFeeOrder.",
|
|
11304
|
+
"Created by `register_collect_fee_order` after Ed25519 verification.",
|
|
11305
|
+
"Read and marked collected by `batch_collect_redeem_early`.",
|
|
11306
|
+
"",
|
|
11307
|
+
'PDA seeds: [b"collect_order", user, nonce_le_bytes]'
|
|
11308
|
+
],
|
|
11309
|
+
type: {
|
|
11310
|
+
kind: "struct",
|
|
11311
|
+
fields: [
|
|
11312
|
+
{
|
|
11313
|
+
name: "user",
|
|
11314
|
+
type: "pubkey"
|
|
11315
|
+
},
|
|
11316
|
+
{
|
|
11317
|
+
name: "condition",
|
|
11318
|
+
type: "pubkey"
|
|
11319
|
+
},
|
|
11320
|
+
{
|
|
11321
|
+
name: "token_mint",
|
|
11322
|
+
type: "pubkey"
|
|
11323
|
+
},
|
|
11324
|
+
{
|
|
11325
|
+
name: "amount",
|
|
11326
|
+
type: "u64"
|
|
11327
|
+
},
|
|
11328
|
+
{
|
|
11329
|
+
name: "nonce",
|
|
11330
|
+
type: "u64"
|
|
11331
|
+
},
|
|
11332
|
+
{
|
|
11333
|
+
name: "expiry",
|
|
11334
|
+
type: "i64"
|
|
11335
|
+
},
|
|
11336
|
+
{
|
|
11337
|
+
name: "is_collected",
|
|
11338
|
+
type: "bool"
|
|
11339
|
+
},
|
|
11340
|
+
{
|
|
11341
|
+
name: "bump",
|
|
11342
|
+
type: "u8"
|
|
11343
|
+
}
|
|
11344
|
+
]
|
|
11345
|
+
}
|
|
11346
|
+
},
|
|
11021
11347
|
{
|
|
11022
11348
|
name: "ComplementaryMatchEvent",
|
|
11023
11349
|
type: {
|