@vercora-protocol/sdk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +240 -0
- package/dist/client.d.ts +169 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +749 -0
- package/dist/client.js.map +1 -0
- package/dist/generated/vercora.d.ts +3717 -0
- package/dist/generated/vercora.d.ts.map +1 -0
- package/dist/generated/vercora.js +3 -0
- package/dist/generated/vercora.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/pda.d.ts +33 -0
- package/dist/pda.d.ts.map +1 -0
- package/dist/pda.js +50 -0
- package/dist/pda.js.map +1 -0
- package/dist/types.d.ts +268 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PredictionMarketClient = void 0;
|
|
4
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
5
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
6
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
7
|
+
const pda_1 = require("./pda");
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
class PredictionMarketClient {
|
|
10
|
+
constructor(program) {
|
|
11
|
+
this.program = program;
|
|
12
|
+
this.connection = program.provider.connection;
|
|
13
|
+
this.globalConfig = (0, pda_1.deriveGlobalConfig)(program.programId);
|
|
14
|
+
}
|
|
15
|
+
get walletKey() {
|
|
16
|
+
return this.program.provider.wallet.publicKey;
|
|
17
|
+
}
|
|
18
|
+
async collateralTokenProgramForMint(mint) {
|
|
19
|
+
const info = await this.connection.getAccountInfo(mint);
|
|
20
|
+
if (!info)
|
|
21
|
+
throw new Error(`Mint not found: ${mint.toBase58()}`);
|
|
22
|
+
return info.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
|
|
23
|
+
? spl_token_1.TOKEN_2022_PROGRAM_ID
|
|
24
|
+
: spl_token_1.TOKEN_PROGRAM_ID;
|
|
25
|
+
}
|
|
26
|
+
/** Platform treasury wallet ATA for `collateralMint`, created by payer if missing (e.g. before pari init). */
|
|
27
|
+
async treasuryCollateralAtaCreateIfMissingInstructions(collateralMint) {
|
|
28
|
+
const gc = await this.fetchGlobalConfig();
|
|
29
|
+
const treasuryWallet = gc.platformTreasury;
|
|
30
|
+
const tokenProg = await this.collateralTokenProgramForMint(collateralMint);
|
|
31
|
+
return this.createCollateralAtaIfMissingInstructions(this.walletKey, collateralMint, treasuryWallet, tokenProg);
|
|
32
|
+
}
|
|
33
|
+
/** Create collateral ATA for `owner` if it does not exist yet (bundled with `createMarket`). */
|
|
34
|
+
async createCollateralAtaIfMissingInstructions(payer, collateralMint, owner, tokenProgram) {
|
|
35
|
+
const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, owner, false, tokenProgram, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
36
|
+
const info = await this.connection.getAccountInfo(ata, 'confirmed');
|
|
37
|
+
if (info)
|
|
38
|
+
return [];
|
|
39
|
+
return [
|
|
40
|
+
(0, spl_token_1.createAssociatedTokenAccountInstruction)(payer, ata, owner, collateralMint, tokenProgram, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID),
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
// ─── Admin ──────────────────────────────────────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Initialize the global config. Must be called once by the platform authority.
|
|
46
|
+
* `platformTreasuryWallet` is the wallet address that receives platform fees;
|
|
47
|
+
* ATAs are derived per-mint automatically during mint/redeem.
|
|
48
|
+
*/
|
|
49
|
+
async initializeConfig(params, opts) {
|
|
50
|
+
return this.program.methods
|
|
51
|
+
.initializeConfig(params.secondaryAuthority, params.depositPlatformFeeBps, params.platformTreasuryWallet, params.platformFeeLamports, params.parimutuelPenaltyProtocolShareBps, params.parimutuelWithdrawPlatformFeeBps)
|
|
52
|
+
.accounts({
|
|
53
|
+
globalConfig: this.globalConfig,
|
|
54
|
+
authority: this.walletKey,
|
|
55
|
+
secondaryAuthority: params.secondaryAuthority,
|
|
56
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
57
|
+
})
|
|
58
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Update global config. Pass `newAuthority` equal to current authority to keep it unchanged.
|
|
62
|
+
* To rotate the primary authority pass the new pubkey — it must be a valid system account.
|
|
63
|
+
*/
|
|
64
|
+
async updateConfig(params, opts) {
|
|
65
|
+
return this.program.methods
|
|
66
|
+
.updateConfig(params.secondaryAuthority, params.depositPlatformFeeBps, params.platformTreasuryWallet, params.platformFeeLamports, params.parimutuelPenaltyProtocolShareBps, params.parimutuelWithdrawPlatformFeeBps)
|
|
67
|
+
.accounts({
|
|
68
|
+
globalConfig: this.globalConfig,
|
|
69
|
+
authority: this.walletKey,
|
|
70
|
+
newAuthority: params.newAuthority,
|
|
71
|
+
})
|
|
72
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Add a collateral mint to the allowlist.
|
|
76
|
+
* Only the global config authority can call this.
|
|
77
|
+
*/
|
|
78
|
+
async addAllowedCollateralMint(mint, opts) {
|
|
79
|
+
return this.program.methods
|
|
80
|
+
.addAllowedCollateralMint()
|
|
81
|
+
.accounts({
|
|
82
|
+
allowedMint: (0, pda_1.deriveAllowedMint)(this.program.programId, mint),
|
|
83
|
+
globalConfig: this.globalConfig,
|
|
84
|
+
authority: this.walletKey,
|
|
85
|
+
mint,
|
|
86
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
87
|
+
})
|
|
88
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
89
|
+
}
|
|
90
|
+
/** Remove a collateral mint from the allowlist. */
|
|
91
|
+
async removeAllowedCollateralMint(mint, opts) {
|
|
92
|
+
return this.program.methods
|
|
93
|
+
.removeAllowedCollateralMint()
|
|
94
|
+
.accounts({
|
|
95
|
+
allowedMint: (0, pda_1.deriveAllowedMint)(this.program.programId, mint),
|
|
96
|
+
globalConfig: this.globalConfig,
|
|
97
|
+
authority: this.walletKey,
|
|
98
|
+
mint,
|
|
99
|
+
})
|
|
100
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
101
|
+
}
|
|
102
|
+
// ─── Market creation (3 steps) ──────────────────────────────────────────────
|
|
103
|
+
/**
|
|
104
|
+
* Step 1 — Create Market + Vault.
|
|
105
|
+
* Returns the market PDA and the transaction signature.
|
|
106
|
+
*/
|
|
107
|
+
async createMarket(creator, collateralMint, creatorFeeAccount, params, opts) {
|
|
108
|
+
const marketPda = (0, pda_1.deriveMarket)(this.program.programId, creator, params.marketId);
|
|
109
|
+
const vaultPda = (0, pda_1.deriveVault)(this.program.programId, marketPda);
|
|
110
|
+
const collateralTokenProgram = await this.collateralTokenProgramForMint(collateralMint);
|
|
111
|
+
const expectedCreatorAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, creator, false, collateralTokenProgram, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
112
|
+
if (!expectedCreatorAta.equals(creatorFeeAccount)) {
|
|
113
|
+
throw new Error(`creatorFeeAccount must be the creator's collateral ATA (${expectedCreatorAta.toBase58()}) for this mint and token program`);
|
|
114
|
+
}
|
|
115
|
+
const gc = await this.program.account.globalConfig.fetch(this.globalConfig);
|
|
116
|
+
const platformTreasuryWallet = gc.platformTreasury;
|
|
117
|
+
const preIxs = [];
|
|
118
|
+
preIxs.push(...(await this.createCollateralAtaIfMissingInstructions(this.walletKey, collateralMint, creator, collateralTokenProgram)));
|
|
119
|
+
preIxs.push(...(await this.createCollateralAtaIfMissingInstructions(this.walletKey, collateralMint, platformTreasuryWallet, collateralTokenProgram)));
|
|
120
|
+
const createIx = await this.program.methods
|
|
121
|
+
.createMarket({
|
|
122
|
+
marketId: params.marketId,
|
|
123
|
+
outcomeCount: params.outcomeCount,
|
|
124
|
+
resolutionThreshold: params.resolutionThreshold,
|
|
125
|
+
closeAt: params.closeAt,
|
|
126
|
+
creatorFeeBps: params.creatorFeeBps,
|
|
127
|
+
depositPlatformFeeBps: params.depositPlatformFeeBps,
|
|
128
|
+
numResolvers: params.numResolvers,
|
|
129
|
+
title: params.title,
|
|
130
|
+
marketType: (0, types_1.toMarketTypeIx)(params.marketType),
|
|
131
|
+
})
|
|
132
|
+
.accounts({
|
|
133
|
+
payer: this.walletKey,
|
|
134
|
+
market: marketPda,
|
|
135
|
+
vault: vaultPda,
|
|
136
|
+
collateralMint,
|
|
137
|
+
creator,
|
|
138
|
+
creatorFeeAccount: expectedCreatorAta,
|
|
139
|
+
globalConfig: this.globalConfig,
|
|
140
|
+
allowedMint: (0, pda_1.deriveAllowedMint)(this.program.programId, collateralMint),
|
|
141
|
+
marketCategory: params.marketCategory ?? null,
|
|
142
|
+
collateralTokenProgram,
|
|
143
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
144
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
145
|
+
})
|
|
146
|
+
.instruction();
|
|
147
|
+
const tx = new web3_js_1.Transaction();
|
|
148
|
+
for (const ix of preIxs)
|
|
149
|
+
tx.add(ix);
|
|
150
|
+
tx.add(createIx);
|
|
151
|
+
const provider = this.program.provider;
|
|
152
|
+
const sig = await provider.sendAndConfirm(tx, [], opts ?? { commitment: 'confirmed', skipPreflight: true });
|
|
153
|
+
return { marketPda, sig };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Step 2 — Initialize resolver PDAs for slots `0..resolverPubkeys.length-1` in **one** transaction.
|
|
157
|
+
* Optional `parimutuelStateParams` appends `initializeParimutuelState` in the same tx (no ordering
|
|
158
|
+
* dependency vs resolvers on-chain).
|
|
159
|
+
*/
|
|
160
|
+
async initializeMarketResolverSlots(marketPda, params, opts, parimutuelStateParams) {
|
|
161
|
+
const provider = this.program.provider;
|
|
162
|
+
const tx = new web3_js_1.Transaction();
|
|
163
|
+
const { marketId, resolverPubkeys } = params;
|
|
164
|
+
if (parimutuelStateParams) {
|
|
165
|
+
const market = await this.fetchMarket(marketPda);
|
|
166
|
+
const treasuryPre = await this.treasuryCollateralAtaCreateIfMissingInstructions(market.collateralMint);
|
|
167
|
+
for (const ix of treasuryPre)
|
|
168
|
+
tx.add(ix);
|
|
169
|
+
}
|
|
170
|
+
for (let i = 0; i < resolverPubkeys.length; i++) {
|
|
171
|
+
const ix = await this.program.methods
|
|
172
|
+
.initializeMarketResolver({
|
|
173
|
+
marketId,
|
|
174
|
+
resolverIndex: i,
|
|
175
|
+
resolverPubkey: resolverPubkeys[i],
|
|
176
|
+
})
|
|
177
|
+
.accounts({
|
|
178
|
+
payer: this.walletKey,
|
|
179
|
+
market: marketPda,
|
|
180
|
+
resolver: (0, pda_1.deriveResolver)(this.program.programId, marketPda, i),
|
|
181
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
182
|
+
})
|
|
183
|
+
.instruction();
|
|
184
|
+
tx.add(ix);
|
|
185
|
+
}
|
|
186
|
+
if (parimutuelStateParams) {
|
|
187
|
+
const parimutuelState = (0, pda_1.deriveParimutuelState)(this.program.programId, marketPda);
|
|
188
|
+
const ix = await this.program.methods
|
|
189
|
+
.initializeParimutuelState({
|
|
190
|
+
marketId: parimutuelStateParams.marketId,
|
|
191
|
+
earlyWithdrawPenaltyBps: parimutuelStateParams.earlyWithdrawPenaltyBps,
|
|
192
|
+
penaltyKeptInPoolBps: parimutuelStateParams.penaltyKeptInPoolBps,
|
|
193
|
+
penaltySurplusCreatorShareBps: parimutuelStateParams.penaltySurplusCreatorShareBps,
|
|
194
|
+
})
|
|
195
|
+
.accounts({
|
|
196
|
+
payer: this.walletKey,
|
|
197
|
+
market: marketPda,
|
|
198
|
+
globalConfig: this.globalConfig,
|
|
199
|
+
parimutuelState,
|
|
200
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
201
|
+
})
|
|
202
|
+
.instruction();
|
|
203
|
+
tx.add(ix);
|
|
204
|
+
}
|
|
205
|
+
return await provider.sendAndConfirm(tx, undefined, opts ?? { skipPreflight: true });
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Step 3 — Initialize 8 Outcome Mints.
|
|
209
|
+
* Decimals are inherited from the collateral mint stored on the market account.
|
|
210
|
+
*/
|
|
211
|
+
async initializeMarketMints(marketPda, marketId, opts) {
|
|
212
|
+
const outcomeMints = (0, pda_1.deriveAllOutcomeMints)(this.program.programId, marketPda);
|
|
213
|
+
return this.program.methods
|
|
214
|
+
.initializeMarketMints({ marketId })
|
|
215
|
+
.accounts({
|
|
216
|
+
payer: this.walletKey,
|
|
217
|
+
market: marketPda,
|
|
218
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
219
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
220
|
+
outcomeMint0: outcomeMints[0],
|
|
221
|
+
outcomeMint1: outcomeMints[1],
|
|
222
|
+
outcomeMint2: outcomeMints[2],
|
|
223
|
+
outcomeMint3: outcomeMints[3],
|
|
224
|
+
outcomeMint4: outcomeMints[4],
|
|
225
|
+
outcomeMint5: outcomeMints[5],
|
|
226
|
+
outcomeMint6: outcomeMints[6],
|
|
227
|
+
outcomeMint7: outcomeMints[7],
|
|
228
|
+
})
|
|
229
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Convenience: run all 3 market creation steps in sequence.
|
|
233
|
+
* Returns the market PDA.
|
|
234
|
+
*/
|
|
235
|
+
async createMarketFull(creator, collateralMint, creatorFeeAccount,
|
|
236
|
+
/** Length must equal `params.numResolvers` (typically the first N of an 8-slot UI). */
|
|
237
|
+
resolverPubkeys, params, opts) {
|
|
238
|
+
const { marketPda } = await this.createMarket(creator, collateralMint, creatorFeeAccount, params, opts);
|
|
239
|
+
if (params.marketType === 'parimutuel') {
|
|
240
|
+
const gc = await this.fetchGlobalConfig();
|
|
241
|
+
const pi = params.parimutuelInit ?? {};
|
|
242
|
+
const penaltySurplusCreatorShareBps = pi.penaltySurplusCreatorShareBps ??
|
|
243
|
+
10000 - gc.parimutuelPenaltyProtocolShareBps;
|
|
244
|
+
await this.initializeMarketResolverSlots(marketPda, {
|
|
245
|
+
marketId: params.marketId,
|
|
246
|
+
resolverPubkeys: resolverPubkeys.slice(0, params.numResolvers),
|
|
247
|
+
}, opts, {
|
|
248
|
+
marketId: params.marketId,
|
|
249
|
+
earlyWithdrawPenaltyBps: pi.earlyWithdrawPenaltyBps ?? 500,
|
|
250
|
+
penaltyKeptInPoolBps: pi.penaltyKeptInPoolBps ?? 8000,
|
|
251
|
+
penaltySurplusCreatorShareBps,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
await this.initializeMarketResolverSlots(marketPda, {
|
|
256
|
+
marketId: params.marketId,
|
|
257
|
+
resolverPubkeys: resolverPubkeys.slice(0, params.numResolvers),
|
|
258
|
+
}, opts);
|
|
259
|
+
await this.initializeMarketMints(marketPda, params.marketId, opts);
|
|
260
|
+
}
|
|
261
|
+
return marketPda;
|
|
262
|
+
}
|
|
263
|
+
/** Pari-mutuel pool + penalty params (step after resolvers, replaces mint init). */
|
|
264
|
+
async initializeParimutuelState(marketPda, params, opts) {
|
|
265
|
+
const provider = this.program.provider;
|
|
266
|
+
const market = await this.fetchMarket(marketPda);
|
|
267
|
+
const treasuryPre = await this.treasuryCollateralAtaCreateIfMissingInstructions(market.collateralMint);
|
|
268
|
+
const parimutuelState = (0, pda_1.deriveParimutuelState)(this.program.programId, marketPda);
|
|
269
|
+
const pariIx = await this.program.methods
|
|
270
|
+
.initializeParimutuelState({
|
|
271
|
+
marketId: params.marketId,
|
|
272
|
+
earlyWithdrawPenaltyBps: params.earlyWithdrawPenaltyBps,
|
|
273
|
+
penaltyKeptInPoolBps: params.penaltyKeptInPoolBps,
|
|
274
|
+
penaltySurplusCreatorShareBps: params.penaltySurplusCreatorShareBps,
|
|
275
|
+
})
|
|
276
|
+
.accounts({
|
|
277
|
+
payer: this.walletKey,
|
|
278
|
+
market: marketPda,
|
|
279
|
+
globalConfig: this.globalConfig,
|
|
280
|
+
parimutuelState,
|
|
281
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
282
|
+
})
|
|
283
|
+
.instruction();
|
|
284
|
+
const tx = new web3_js_1.Transaction();
|
|
285
|
+
for (const ix of treasuryPre)
|
|
286
|
+
tx.add(ix);
|
|
287
|
+
tx.add(pariIx);
|
|
288
|
+
return await provider.sendAndConfirm(tx, [], opts ?? { commitment: 'confirmed', skipPreflight: true });
|
|
289
|
+
}
|
|
290
|
+
async parimutuelStake(marketPda, params, opts) {
|
|
291
|
+
const parimutuelState = (0, pda_1.deriveParimutuelState)(this.program.programId, marketPda);
|
|
292
|
+
const market = await this.fetchMarket(marketPda);
|
|
293
|
+
const globalConfig = await this.fetchGlobalConfig();
|
|
294
|
+
const position = (0, pda_1.deriveParimutuelPosition)(this.program.programId, marketPda, this.walletKey, params.outcomeIndex);
|
|
295
|
+
const vaultPda = (0, pda_1.deriveVault)(this.program.programId, marketPda);
|
|
296
|
+
const allowedMint = (0, pda_1.deriveAllowedMint)(this.program.programId, market.collateralMint);
|
|
297
|
+
const userCollateral = (0, spl_token_1.getAssociatedTokenAddressSync)(market.collateralMint, this.walletKey, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
298
|
+
const platformTreasuryAta = (0, spl_token_1.getAssociatedTokenAddressSync)(market.collateralMint, globalConfig.platformTreasury, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
299
|
+
return this.program.methods
|
|
300
|
+
.parimutuelStake({
|
|
301
|
+
marketId: params.marketId,
|
|
302
|
+
outcomeIndex: params.outcomeIndex,
|
|
303
|
+
amount: params.amount,
|
|
304
|
+
})
|
|
305
|
+
.accounts({
|
|
306
|
+
user: this.walletKey,
|
|
307
|
+
market: marketPda,
|
|
308
|
+
parimutuelState,
|
|
309
|
+
position,
|
|
310
|
+
vault: vaultPda,
|
|
311
|
+
collateralMint: market.collateralMint,
|
|
312
|
+
userCollateralAccount: userCollateral,
|
|
313
|
+
creatorFeeAccount: market.creatorFeeAccount,
|
|
314
|
+
globalConfig: this.globalConfig,
|
|
315
|
+
platformTreasuryWallet: globalConfig.platformTreasury,
|
|
316
|
+
platformTreasuryAta,
|
|
317
|
+
allowedMint,
|
|
318
|
+
collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
319
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
320
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
321
|
+
})
|
|
322
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
323
|
+
}
|
|
324
|
+
async parimutuelWithdraw(marketPda, params, opts) {
|
|
325
|
+
const parimutuelState = (0, pda_1.deriveParimutuelState)(this.program.programId, marketPda);
|
|
326
|
+
const market = await this.fetchMarket(marketPda);
|
|
327
|
+
const position = (0, pda_1.deriveParimutuelPosition)(this.program.programId, marketPda, this.walletKey, params.outcomeIndex);
|
|
328
|
+
const vaultPda = (0, pda_1.deriveVault)(this.program.programId, marketPda);
|
|
329
|
+
const globalConfig = await this.fetchGlobalConfig();
|
|
330
|
+
const userCollateral = (0, spl_token_1.getAssociatedTokenAddressSync)(market.collateralMint, this.walletKey, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
331
|
+
const platformTreasuryAta = (0, spl_token_1.getAssociatedTokenAddressSync)(market.collateralMint, globalConfig.platformTreasury, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
332
|
+
return this.program.methods
|
|
333
|
+
.parimutuelWithdraw({
|
|
334
|
+
marketId: params.marketId,
|
|
335
|
+
outcomeIndex: params.outcomeIndex,
|
|
336
|
+
amount: params.amount,
|
|
337
|
+
})
|
|
338
|
+
.accounts({
|
|
339
|
+
user: this.walletKey,
|
|
340
|
+
market: marketPda,
|
|
341
|
+
creatorFeeAccount: market.creatorFeeAccount,
|
|
342
|
+
parimutuelState,
|
|
343
|
+
position,
|
|
344
|
+
vault: vaultPda,
|
|
345
|
+
collateralMint: market.collateralMint,
|
|
346
|
+
userCollateralAccount: userCollateral,
|
|
347
|
+
globalConfig: this.globalConfig,
|
|
348
|
+
platformTreasuryWallet: globalConfig.platformTreasury,
|
|
349
|
+
platformTreasuryAta,
|
|
350
|
+
collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
351
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
352
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
353
|
+
})
|
|
354
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
355
|
+
}
|
|
356
|
+
async parimutuelClaim(marketPda, params, opts) {
|
|
357
|
+
const parimutuelState = (0, pda_1.deriveParimutuelState)(this.program.programId, marketPda);
|
|
358
|
+
const market = await this.fetchMarket(marketPda);
|
|
359
|
+
const position = (0, pda_1.deriveParimutuelPosition)(this.program.programId, marketPda, this.walletKey, params.outcomeIndex);
|
|
360
|
+
const vaultPda = (0, pda_1.deriveVault)(this.program.programId, marketPda);
|
|
361
|
+
const userCollateral = (0, spl_token_1.getAssociatedTokenAddressSync)(market.collateralMint, this.walletKey, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
362
|
+
return this.program.methods
|
|
363
|
+
.parimutuelClaim({
|
|
364
|
+
marketId: params.marketId,
|
|
365
|
+
outcomeIndex: params.outcomeIndex,
|
|
366
|
+
})
|
|
367
|
+
.accounts({
|
|
368
|
+
user: this.walletKey,
|
|
369
|
+
market: marketPda,
|
|
370
|
+
parimutuelState,
|
|
371
|
+
position,
|
|
372
|
+
vault: vaultPda,
|
|
373
|
+
collateralMint: market.collateralMint,
|
|
374
|
+
userCollateralAccount: userCollateral,
|
|
375
|
+
collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
376
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
377
|
+
})
|
|
378
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
379
|
+
}
|
|
380
|
+
// ─── Trading ────────────────────────────────────────────────────────────────
|
|
381
|
+
/**
|
|
382
|
+
* Mint a complete set of outcome tokens.
|
|
383
|
+
* Fetches `market.outcomeCount` and passes `2 * outcomeCount` remaining accounts:
|
|
384
|
+
* `[outcome_mint_i, user_ata_i]` for each active outcome.
|
|
385
|
+
* Creates any missing outcome ATAs for `user` before sending the instruction.
|
|
386
|
+
* `platformTreasuryWallet` must match GlobalConfig.platformTreasury. The treasury
|
|
387
|
+
* ATA for this collateral mint must already exist (create it client-side if needed).
|
|
388
|
+
* Pass `collateralTokenProgram` as TOKEN_2022_PROGRAM_ID for Token-2022 mints.
|
|
389
|
+
*/
|
|
390
|
+
async mintCompleteSet(user, marketPda, collateralMint, userCollateralAccount, platformTreasuryWallet, creatorFeeAccount, params, opts, collateralTokenProgram = spl_token_1.TOKEN_PROGRAM_ID) {
|
|
391
|
+
const market = await this.fetchMarket(marketPda);
|
|
392
|
+
const oc = market.outcomeCount;
|
|
393
|
+
const n = anchor_1.BN.isBN(oc) ? oc.toNumber() : Number(oc);
|
|
394
|
+
const outcomeMints = (0, pda_1.deriveAllOutcomeMints)(this.program.programId, marketPda).slice(0, n);
|
|
395
|
+
const userOutcomes = outcomeMints.map((m) => (0, spl_token_1.getAssociatedTokenAddressSync)(m, user, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
396
|
+
const platformTreasuryAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collateralMint, platformTreasuryWallet, false, collateralTokenProgram, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
397
|
+
await this._ensureAtas(user, outcomeMints, userOutcomes);
|
|
398
|
+
const remainingAccounts = outcomeMints.flatMap((mint, i) => [
|
|
399
|
+
{ pubkey: mint, isSigner: false, isWritable: true },
|
|
400
|
+
{ pubkey: userOutcomes[i], isSigner: false, isWritable: true },
|
|
401
|
+
]);
|
|
402
|
+
return this.program.methods
|
|
403
|
+
.mintCompleteSet({ amount: params.amount, marketId: params.marketId })
|
|
404
|
+
.accounts({
|
|
405
|
+
user,
|
|
406
|
+
market: marketPda,
|
|
407
|
+
vault: (0, pda_1.deriveVault)(this.program.programId, marketPda),
|
|
408
|
+
collateralMint,
|
|
409
|
+
userCollateralAccount,
|
|
410
|
+
creatorFeeAccount,
|
|
411
|
+
globalConfig: this.globalConfig,
|
|
412
|
+
allowedMint: (0, pda_1.deriveAllowedMint)(this.program.programId, collateralMint),
|
|
413
|
+
platformTreasuryWallet,
|
|
414
|
+
platformTreasuryAta,
|
|
415
|
+
collateralTokenProgram,
|
|
416
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
417
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
418
|
+
})
|
|
419
|
+
.remainingAccounts(remainingAccounts)
|
|
420
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Burn one complete set (10^decimals base units of each outcome) and receive
|
|
424
|
+
* the same amount of collateral base units back.
|
|
425
|
+
*/
|
|
426
|
+
async redeemCompleteSet(user, marketPda, collateralMint, userCollateralAccount, params, opts) {
|
|
427
|
+
const outcomeMints = (0, pda_1.deriveAllOutcomeMints)(this.program.programId, marketPda);
|
|
428
|
+
const userOutcomes = outcomeMints.map((m) => (0, spl_token_1.getAssociatedTokenAddressSync)(m, user, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
429
|
+
return this.program.methods
|
|
430
|
+
.redeemCompleteSet({ marketId: params.marketId })
|
|
431
|
+
.accounts({
|
|
432
|
+
user,
|
|
433
|
+
market: marketPda,
|
|
434
|
+
vault: (0, pda_1.deriveVault)(this.program.programId, marketPda),
|
|
435
|
+
collateralMint,
|
|
436
|
+
userCollateralAccount,
|
|
437
|
+
outcomeMint0: outcomeMints[0],
|
|
438
|
+
outcomeMint1: outcomeMints[1],
|
|
439
|
+
outcomeMint2: outcomeMints[2],
|
|
440
|
+
outcomeMint3: outcomeMints[3],
|
|
441
|
+
outcomeMint4: outcomeMints[4],
|
|
442
|
+
outcomeMint5: outcomeMints[5],
|
|
443
|
+
outcomeMint6: outcomeMints[6],
|
|
444
|
+
outcomeMint7: outcomeMints[7],
|
|
445
|
+
userOutcome0: userOutcomes[0],
|
|
446
|
+
userOutcome1: userOutcomes[1],
|
|
447
|
+
userOutcome2: userOutcomes[2],
|
|
448
|
+
userOutcome3: userOutcomes[3],
|
|
449
|
+
userOutcome4: userOutcomes[4],
|
|
450
|
+
userOutcome5: userOutcomes[5],
|
|
451
|
+
userOutcome6: userOutcomes[6],
|
|
452
|
+
userOutcome7: userOutcomes[7],
|
|
453
|
+
collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
454
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
455
|
+
})
|
|
456
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
457
|
+
}
|
|
458
|
+
// ─── Resolution ─────────────────────────────────────────────────────────────
|
|
459
|
+
/**
|
|
460
|
+
* Resolver casts a vote for an outcome. Fails if they already have an active vote;
|
|
461
|
+
* call `revokeResolutionVote` first to change outcome (tally 1 → 0 → 1).
|
|
462
|
+
*/
|
|
463
|
+
async voteResolution(marketPda, params, opts) {
|
|
464
|
+
const votePda = (0, pda_1.deriveResolutionVote)(this.program.programId, marketPda, params.resolverIndex);
|
|
465
|
+
const tallyPda = (0, pda_1.deriveOutcomeTally)(this.program.programId, marketPda, params.outcomeIndex);
|
|
466
|
+
return this.program.methods
|
|
467
|
+
.voteResolution({
|
|
468
|
+
marketId: params.marketId,
|
|
469
|
+
resolverIndex: params.resolverIndex,
|
|
470
|
+
outcomeIndex: params.outcomeIndex,
|
|
471
|
+
})
|
|
472
|
+
.accounts({
|
|
473
|
+
resolverSigner: this.walletKey,
|
|
474
|
+
market: marketPda,
|
|
475
|
+
resolver: (0, pda_1.deriveResolver)(this.program.programId, marketPda, params.resolverIndex),
|
|
476
|
+
resolutionVote: votePda,
|
|
477
|
+
outcomeTally: tallyPda,
|
|
478
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
479
|
+
})
|
|
480
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
481
|
+
}
|
|
482
|
+
/** Clears the resolver’s active vote and decrements that outcome’s on-chain tally. */
|
|
483
|
+
async revokeResolutionVote(marketPda, params, opts) {
|
|
484
|
+
const votePda = (0, pda_1.deriveResolutionVote)(this.program.programId, marketPda, params.resolverIndex);
|
|
485
|
+
const tallyPda = (0, pda_1.deriveOutcomeTally)(this.program.programId, marketPda, params.outcomeIndex);
|
|
486
|
+
return this.program.methods
|
|
487
|
+
.revokeResolutionVote({
|
|
488
|
+
marketId: params.marketId,
|
|
489
|
+
resolverIndex: params.resolverIndex,
|
|
490
|
+
outcomeIndex: params.outcomeIndex,
|
|
491
|
+
})
|
|
492
|
+
.accounts({
|
|
493
|
+
resolverSigner: this.walletKey,
|
|
494
|
+
market: marketPda,
|
|
495
|
+
resolver: (0, pda_1.deriveResolver)(this.program.programId, marketPda, params.resolverIndex),
|
|
496
|
+
resolutionVote: votePda,
|
|
497
|
+
outcomeTally: tallyPda,
|
|
498
|
+
})
|
|
499
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Anyone can call `finalizeResolution`. It is a no-op if the threshold is
|
|
503
|
+
* not yet reached; resolves the market once M votes agree on one outcome.
|
|
504
|
+
* Passes optional per-outcome tally accounts (null if that tally PDA was never created).
|
|
505
|
+
*/
|
|
506
|
+
async finalizeResolution(marketPda, params, opts) {
|
|
507
|
+
const tallies = (0, pda_1.deriveAllOutcomeTallies)(this.program.programId, marketPda);
|
|
508
|
+
const infos = await Promise.all(tallies.map((p) => this.connection.getAccountInfo(p)));
|
|
509
|
+
return this.program.methods
|
|
510
|
+
.finalizeResolution({ marketId: params.marketId })
|
|
511
|
+
.accounts({
|
|
512
|
+
market: marketPda,
|
|
513
|
+
outcomeTally0: infos[0] ? tallies[0] : null,
|
|
514
|
+
outcomeTally1: infos[1] ? tallies[1] : null,
|
|
515
|
+
outcomeTally2: infos[2] ? tallies[2] : null,
|
|
516
|
+
outcomeTally3: infos[3] ? tallies[3] : null,
|
|
517
|
+
outcomeTally4: infos[4] ? tallies[4] : null,
|
|
518
|
+
outcomeTally5: infos[5] ? tallies[5] : null,
|
|
519
|
+
outcomeTally6: infos[6] ? tallies[6] : null,
|
|
520
|
+
outcomeTally7: infos[7] ? tallies[7] : null,
|
|
521
|
+
})
|
|
522
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Burn `amount` winning outcome token base units and receive the same
|
|
526
|
+
* amount of collateral base units from the vault.
|
|
527
|
+
* `platformTreasuryWallet` is the wallet address from GlobalConfig — fetched
|
|
528
|
+
* automatically from on-chain state if not provided.
|
|
529
|
+
*/
|
|
530
|
+
async redeemWinning(user, marketPda, collateralMint, userCollateralAccount, params, opts, platformTreasuryWallet) {
|
|
531
|
+
const [market, config] = await Promise.all([
|
|
532
|
+
this.fetchMarket(marketPda),
|
|
533
|
+
platformTreasuryWallet ? Promise.resolve(null) : this.fetchGlobalConfig(),
|
|
534
|
+
]);
|
|
535
|
+
const treasuryWallet = platformTreasuryWallet ?? config.platformTreasury;
|
|
536
|
+
const outcomeMints = (0, pda_1.deriveAllOutcomeMints)(this.program.programId, marketPda);
|
|
537
|
+
const winningIndex = market.resolvedOutcomeIndex;
|
|
538
|
+
const userWinningOutcome = (0, spl_token_1.getAssociatedTokenAddressSync)(outcomeMints[winningIndex], user, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
539
|
+
return this.program.methods
|
|
540
|
+
.redeemWinning({ marketId: params.marketId, amount: params.amount })
|
|
541
|
+
.accounts({
|
|
542
|
+
user,
|
|
543
|
+
market: marketPda,
|
|
544
|
+
vault: (0, pda_1.deriveVault)(this.program.programId, marketPda),
|
|
545
|
+
collateralMint,
|
|
546
|
+
userCollateralAccount,
|
|
547
|
+
globalConfig: this.globalConfig,
|
|
548
|
+
platformTreasuryWallet: treasuryWallet,
|
|
549
|
+
outcomeMint0: outcomeMints[0],
|
|
550
|
+
outcomeMint1: outcomeMints[1],
|
|
551
|
+
outcomeMint2: outcomeMints[2],
|
|
552
|
+
outcomeMint3: outcomeMints[3],
|
|
553
|
+
outcomeMint4: outcomeMints[4],
|
|
554
|
+
outcomeMint5: outcomeMints[5],
|
|
555
|
+
outcomeMint6: outcomeMints[6],
|
|
556
|
+
outcomeMint7: outcomeMints[7],
|
|
557
|
+
userWinningOutcome,
|
|
558
|
+
collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
559
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
560
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
561
|
+
})
|
|
562
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
563
|
+
}
|
|
564
|
+
// ─── Market lifecycle ────────────────────────────────────────────────────────
|
|
565
|
+
/** Market creator or global config authority can close the market before `close_at`. */
|
|
566
|
+
async closeMarketEarly(marketPda, params, opts) {
|
|
567
|
+
return this.program.methods
|
|
568
|
+
.closeMarketEarly({ marketId: params.marketId })
|
|
569
|
+
.accounts({
|
|
570
|
+
signer: this.walletKey,
|
|
571
|
+
globalConfig: this.globalConfig,
|
|
572
|
+
market: marketPda,
|
|
573
|
+
})
|
|
574
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
575
|
+
}
|
|
576
|
+
/** Void the market (cancel); enables full-set redemption for all holders. Creator or global authority only. */
|
|
577
|
+
async voidMarket(marketPda, params, opts) {
|
|
578
|
+
return this.program.methods
|
|
579
|
+
.voidMarket({ marketId: params.marketId })
|
|
580
|
+
.accounts({
|
|
581
|
+
signer: this.walletKey,
|
|
582
|
+
globalConfig: this.globalConfig,
|
|
583
|
+
market: marketPda,
|
|
584
|
+
})
|
|
585
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
586
|
+
}
|
|
587
|
+
// ─── State readers ───────────────────────────────────────────────────────────
|
|
588
|
+
async fetchGlobalConfig() {
|
|
589
|
+
return this.program.account.globalConfig.fetch(this.globalConfig);
|
|
590
|
+
}
|
|
591
|
+
async fetchMarket(market) {
|
|
592
|
+
return this.program.account.market.fetch(market);
|
|
593
|
+
}
|
|
594
|
+
/** Returns the collateral balance (base units) held in the vault. */
|
|
595
|
+
async fetchVaultBalance(market) {
|
|
596
|
+
const vault = (0, pda_1.deriveVault)(this.program.programId, market);
|
|
597
|
+
const acc = await (0, spl_token_1.getAccount)(this.connection, vault, undefined, spl_token_1.TOKEN_PROGRAM_ID);
|
|
598
|
+
return acc.amount;
|
|
599
|
+
}
|
|
600
|
+
/** Returns the outcome token balance (base units) for a user and outcome index. */
|
|
601
|
+
async fetchOutcomeBalance(market, user, outcomeIndex) {
|
|
602
|
+
const mint = (0, pda_1.deriveOutcomeMint)(this.program.programId, market, outcomeIndex);
|
|
603
|
+
const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, user, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
604
|
+
const acc = await (0, spl_token_1.getAccount)(this.connection, ata, undefined, spl_token_1.TOKEN_PROGRAM_ID);
|
|
605
|
+
return acc.amount;
|
|
606
|
+
}
|
|
607
|
+
// ─── User profiles ───────────────────────────────────────────────────────────
|
|
608
|
+
/**
|
|
609
|
+
* Create or update the caller's on-chain user profile.
|
|
610
|
+
* The PDA `["user-profile", wallet]` is initialized on first call (payer = wallet);
|
|
611
|
+
* subsequent calls update `display_name` and `url` without resetting the `verified` flag.
|
|
612
|
+
*/
|
|
613
|
+
async upsertUserProfile(params, opts) {
|
|
614
|
+
return this.program.methods
|
|
615
|
+
.upsertUserProfile(params.displayName, params.url)
|
|
616
|
+
.accounts({
|
|
617
|
+
userProfile: (0, pda_1.deriveUserProfile)(this.program.programId, this.walletKey),
|
|
618
|
+
wallet: this.walletKey,
|
|
619
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
620
|
+
})
|
|
621
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Close the caller's user profile, reclaiming the rent lamports.
|
|
625
|
+
* The profile PDA is zeroed and lamports are returned to the wallet.
|
|
626
|
+
*/
|
|
627
|
+
async closeUserProfile(opts) {
|
|
628
|
+
return this.program.methods
|
|
629
|
+
.closeUserProfile()
|
|
630
|
+
.accounts({
|
|
631
|
+
userProfile: (0, pda_1.deriveUserProfile)(this.program.programId, this.walletKey),
|
|
632
|
+
wallet: this.walletKey,
|
|
633
|
+
})
|
|
634
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Set or revoke the `verified` flag on any user's profile.
|
|
638
|
+
* Only callable by the platform primary or secondary authority (stored in GlobalConfig).
|
|
639
|
+
*
|
|
640
|
+
* @param targetWallet - The wallet whose profile to update.
|
|
641
|
+
* @param params - `{ verified: boolean }` — true to verify, false to revoke.
|
|
642
|
+
*/
|
|
643
|
+
async verifyUserProfile(targetWallet, params, opts) {
|
|
644
|
+
return this.program.methods
|
|
645
|
+
.verifyUserProfile(params.verified)
|
|
646
|
+
.accounts({
|
|
647
|
+
userProfile: (0, pda_1.deriveUserProfile)(this.program.programId, targetWallet),
|
|
648
|
+
targetWallet,
|
|
649
|
+
authority: this.walletKey,
|
|
650
|
+
globalConfig: this.globalConfig,
|
|
651
|
+
})
|
|
652
|
+
.rpc(opts ?? { skipPreflight: true });
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Fetch a user's on-chain profile. Returns `null` if the profile has never
|
|
656
|
+
* been created (or has been closed).
|
|
657
|
+
*/
|
|
658
|
+
async fetchUserProfile(wallet) {
|
|
659
|
+
const pda = (0, pda_1.deriveUserProfile)(this.program.programId, wallet);
|
|
660
|
+
const info = await this.connection.getAccountInfo(pda);
|
|
661
|
+
if (!info)
|
|
662
|
+
return null;
|
|
663
|
+
return this.program.account.userProfile.fetch(pda);
|
|
664
|
+
}
|
|
665
|
+
/** Fetch the parimutuel pool state (pool totals, penalty parameters, resolved snapshots). */
|
|
666
|
+
async fetchParimutuelState(market) {
|
|
667
|
+
const pda = (0, pda_1.deriveParimutuelState)(this.program.programId, market);
|
|
668
|
+
return this.program.account.parimutuelState.fetch(pda);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Fetch a user's parimutuel stake position for one outcome.
|
|
672
|
+
* Returns `null` if the position PDA has never been initialized.
|
|
673
|
+
*/
|
|
674
|
+
async fetchParimutuelPosition(market, user, outcomeIndex) {
|
|
675
|
+
const pda = (0, pda_1.deriveParimutuelPosition)(this.program.programId, market, user, outcomeIndex);
|
|
676
|
+
const info = await this.connection.getAccountInfo(pda);
|
|
677
|
+
if (!info)
|
|
678
|
+
return null;
|
|
679
|
+
return this.program.account.parimutuelPosition.fetch(pda);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Fetch outcome token balances (base units) for all active outcomes for a user in a
|
|
683
|
+
* complete-set market. Returns an array of length `outcomeCount`; missing ATAs yield `0n`.
|
|
684
|
+
*/
|
|
685
|
+
async fetchAllOutcomeBalances(market, user, outcomeCount) {
|
|
686
|
+
const mints = (0, pda_1.deriveAllOutcomeMints)(this.program.programId, market).slice(0, outcomeCount);
|
|
687
|
+
const atas = mints.map((m) => (0, spl_token_1.getAssociatedTokenAddressSync)(m, user, false, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
688
|
+
return Promise.all(atas.map(async (ata) => {
|
|
689
|
+
try {
|
|
690
|
+
const acc = await (0, spl_token_1.getAccount)(this.connection, ata, undefined, spl_token_1.TOKEN_PROGRAM_ID);
|
|
691
|
+
return acc.amount;
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
return 0n;
|
|
695
|
+
}
|
|
696
|
+
}));
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Compute implied win probabilities and gross payout multipliers from a `ParimutuelStateAccount`.
|
|
700
|
+
* Outcomes with a zero pool are treated as having 0 probability and 0 multiplier.
|
|
701
|
+
*
|
|
702
|
+
* @param state - Result of `fetchParimutuelState`.
|
|
703
|
+
* @param outcomeCount - Number of active outcomes (from `MarketAccount.outcomeCount`).
|
|
704
|
+
*/
|
|
705
|
+
computeParimutuelOdds(state, outcomeCount) {
|
|
706
|
+
const pools = state.outcomePools.slice(0, outcomeCount).map((p) => p.toNumber());
|
|
707
|
+
const total = state.totalPool.toNumber();
|
|
708
|
+
const impliedProbabilities = pools.map((p) => (total > 0 ? p / total : 0));
|
|
709
|
+
const payoutMultipliers = pools.map((p) => (p > 0 ? total / p : 0));
|
|
710
|
+
return { impliedProbabilities, payoutMultipliers, totalPool: state.totalPool };
|
|
711
|
+
}
|
|
712
|
+
/** Fetch the resolver account for a given slot index (0–7). */
|
|
713
|
+
async fetchResolver(market, resolverIndex) {
|
|
714
|
+
const pda = (0, pda_1.deriveResolver)(this.program.programId, market, resolverIndex);
|
|
715
|
+
return this.program.account.resolver.fetch(pda);
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Fetch all initialized resolver accounts for a market in parallel.
|
|
719
|
+
* Slots whose PDA has never been created are omitted from the result.
|
|
720
|
+
*
|
|
721
|
+
* @param numResolvers - Value of `MarketAccount.numResolvers` (or an upper bound like 8).
|
|
722
|
+
*/
|
|
723
|
+
async fetchAllResolvers(market, numResolvers) {
|
|
724
|
+
const pdas = Array.from({ length: numResolvers }, (_, i) => (0, pda_1.deriveResolver)(this.program.programId, market, i));
|
|
725
|
+
const infos = await Promise.all(pdas.map((p) => this.connection.getAccountInfo(p)));
|
|
726
|
+
const results = await Promise.all(pdas.map(async (pda, i) => {
|
|
727
|
+
if (!infos[i])
|
|
728
|
+
return null;
|
|
729
|
+
const acc = (await this.program.account.resolver.fetch(pda));
|
|
730
|
+
return { index: i, resolverPubkey: acc.resolverPubkey };
|
|
731
|
+
}));
|
|
732
|
+
return results.filter((r) => r !== null);
|
|
733
|
+
}
|
|
734
|
+
// ─── Internal ────────────────────────────────────────────────────────────────
|
|
735
|
+
async _ensureAtas(owner, mints, atas) {
|
|
736
|
+
const checks = await Promise.all(atas.map((ata) => this.connection.getAccountInfo(ata).then((info) => info === null)));
|
|
737
|
+
const missing = mints
|
|
738
|
+
.map((mint, i) => ({ mint, ata: atas[i], create: checks[i] }))
|
|
739
|
+
.filter((x) => x.create)
|
|
740
|
+
.map((x) => (0, spl_token_1.createAssociatedTokenAccountInstruction)(this.walletKey, x.ata, owner, x.mint, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
741
|
+
if (missing.length === 0)
|
|
742
|
+
return;
|
|
743
|
+
const tx = new web3_js_1.Transaction().add(...missing);
|
|
744
|
+
const wallet = this.program.provider.wallet;
|
|
745
|
+
await (0, web3_js_1.sendAndConfirmTransaction)(this.connection, tx, [{ publicKey: wallet.publicKey, signTransaction: wallet.signTransaction.bind(wallet), signAllTransactions: wallet.signAllTransactions.bind(wallet) }], { skipPreflight: true });
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
exports.PredictionMarketClient = PredictionMarketClient;
|
|
749
|
+
//# sourceMappingURL=client.js.map
|