solotto 1.0.0 → 1.0.2

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 CHANGED
@@ -98,7 +98,7 @@ const manager = new LotteryManager(connection, programId);
98
98
 
99
99
  ### Lottery
100
100
 
101
- Used by **players** to buy tickets and claim prizes, and by anyone to read lottery state.
101
+ Used by **players** to buy tickets and claim prizes, and by anyone to read lottery state. Extends `EventEmitter` to provide real-time draw notifications via `WatchDraw()`.
102
102
 
103
103
  ```js
104
104
  import { Lottery } from "solotto";
@@ -368,12 +368,42 @@ const myTickets = await lottery.GetTickets(authority, lotteryId, buyer);
368
368
 
369
369
  Opens a WebSocket subscription to listen for draw events in real time. Requires the `wss` parameter to be set in the `Lottery` constructor. Automatically reconnects on connection failure.
370
370
 
371
+ The `Lottery` class extends `EventEmitter`, so you subscribe to draw results and connection lifecycle events using standard `.on()` listeners.
372
+
371
373
  ```js
372
374
  const lottery = new Lottery(connection, "wss://your-rpc.com", programId);
375
+
376
+ // Listen for draw results
377
+ lottery.on("draw", ({ winningTicketNumber, signature }) => {
378
+ console.log(`Winning ticket: #${winningTicketNumber}`);
379
+ console.log(`Transaction: ${signature}`);
380
+ });
381
+
382
+ // Connection lifecycle events (optional)
383
+ lottery.on("connected", () => {
384
+ console.log("WebSocket connected");
385
+ });
386
+
387
+ lottery.on("error", (err) => {
388
+ console.error("WebSocket error:", err);
389
+ });
390
+
391
+ lottery.on("reconnecting", ({ delay }) => {
392
+ console.log(`Reconnecting in ${delay / 1000}s...`);
393
+ });
394
+
395
+ // Start watching
373
396
  await lottery.WatchDraw();
374
397
  ```
375
398
 
376
- > **Note:** This method is designed for browser environments and interacts with DOM elements (`spin-btn`, `stop-ticket`, `copy-signature-btn`) to trigger UI updates when a draw occurs.
399
+ **Emitted Events:**
400
+
401
+ | Event | Payload | Description |
402
+ |---|---|---|
403
+ | `draw` | `{ winningTicketNumber: Number, signature: String }` | Fired when a lottery draw is finalized on-chain. |
404
+ | `connected` | — | Fired when the WebSocket connection is established. |
405
+ | `error` | `Error` | Fired when the WebSocket encounters a connection error. |
406
+ | `reconnecting` | `{ delay: Number }` | Fired before an automatic reconnection attempt. `delay` is in milliseconds. |
377
407
 
378
408
  ---
379
409
 
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "solotto",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Solana lottery client",
5
5
  "type": "module",
6
+ "types": "./solotto.d.ts",
6
7
  "main": "solotto.js",
7
8
  "scripts": {
8
9
  "start": "node test.js"
package/solotto.d.ts ADDED
@@ -0,0 +1,325 @@
1
+ declare module "solotto" {
2
+ import { Connection, PublicKey, Keypair, TransactionInstruction, VersionedTransaction } from "@solana/web3.js";
3
+ import { EventEmitter } from "events";
4
+
5
+ // ── Shared Types ──────────────────────────────────────────────────────
6
+
7
+ type PriorityLevel = "Low" | "Medium" | "High" | "VeryHigh" | "Extreme";
8
+
9
+ interface TxParams {
10
+ /** Payer public key as a base-58 string. */
11
+ account: string;
12
+ /** Array of transaction instructions. */
13
+ instructions: TransactionInstruction[];
14
+ /** Keypairs to pre-sign the transaction, or `false`. */
15
+ signers?: Keypair[] | false;
16
+ /** Priority fee level. Defaults to `"Low"`. */
17
+ priority?: PriorityLevel;
18
+ /** Compute unit safety multiplier. Defaults to `1.1`. */
19
+ tolerance?: number | false;
20
+ /** If `true`, serialize the transaction to a `Uint8Array`. */
21
+ serialize?: boolean;
22
+ /** If `true`, base64-encode the serialized transaction. */
23
+ encode?: boolean;
24
+ /** If `true`, simulate and optimise compute units. Defaults to `true`. */
25
+ compute?: boolean;
26
+ /** If `true`, estimate and set priority fees. Defaults to `true`. */
27
+ fees?: boolean;
28
+ /** Address Lookup Table account, or `false`. */
29
+ table?: any | false;
30
+ /** Optional memo string, or `false`. */
31
+ memo?: string | false;
32
+ }
33
+
34
+ interface TxResult {
35
+ status: "ok" | "error";
36
+ message: string;
37
+ transaction?: VersionedTransaction | Uint8Array | string;
38
+ }
39
+
40
+ interface SimulationError {
41
+ status: "error";
42
+ message: string;
43
+ details?: any;
44
+ logs?: string[];
45
+ }
46
+
47
+ interface SendError {
48
+ status: "error";
49
+ message: any;
50
+ }
51
+
52
+ // ── Lottery State ─────────────────────────────────────────────────────
53
+
54
+ interface LotteryState {
55
+ /** Lottery authority public key. */
56
+ authority: string;
57
+ /** Lottery numeric identifier. */
58
+ lotteryId: number;
59
+ /** Ticket price in lamports. */
60
+ ticketPrice: number;
61
+ /** Total number of tickets sold. */
62
+ totalTickets: number;
63
+ /** Winning ticket number, or `0`/`null` if not yet drawn. */
64
+ winnerTicketNumber: number | null;
65
+ /** Winner's public key, or `null` if not yet drawn. */
66
+ winnerAddress: string | null;
67
+ /** Whether the lottery is currently active. */
68
+ isActive: boolean;
69
+ /** Prize pool balance in lamports (net of fees when `fees` param is `true`). */
70
+ prizePoolBalance: number;
71
+ /** Whether a draw has been initiated. */
72
+ drawInitiated: boolean;
73
+ /** Prize pool PDA as a base-58 string. */
74
+ prizePoolAddress: string;
75
+ /** Lottery PDA as a base-58 string. */
76
+ lotteryAddress: string;
77
+ }
78
+
79
+ // ── Ticket State ──────────────────────────────────────────────────────
80
+
81
+ interface TicketInfo {
82
+ /** Ticket owner public key. */
83
+ ticketOwner: string;
84
+ /** Ticket receipt account public key. */
85
+ ticketReceipt: string;
86
+ /** The ticket number. */
87
+ ticketNumber: number;
88
+ /** Ticket PDA as a base-58 string. */
89
+ ticketPda: string;
90
+ /** Lottery numeric identifier. */
91
+ lotteryId: number;
92
+ /** Lottery PDA as a base-58 string. */
93
+ lotteryAddress: string;
94
+ /** Lottery authority public key. */
95
+ lotteryAuth: string;
96
+ }
97
+
98
+ interface TicketListItem {
99
+ owner: string;
100
+ lottery: string;
101
+ ticketReceipt: string;
102
+ ticketNumber: number;
103
+ ticketPda: string;
104
+ }
105
+
106
+ interface TicketListResult {
107
+ lotteryId: number;
108
+ lotteryAddress: string;
109
+ lotteryAuth: string;
110
+ /** The buyer's public key, or `"All"` if unfiltered. */
111
+ buyer: string;
112
+ /** Tickets sorted descending by ticket number. */
113
+ tickets: TicketListItem[];
114
+ }
115
+
116
+ // ── WatchDraw Events ──────────────────────────────────────────────────
117
+
118
+ interface DrawEvent {
119
+ /** The winning ticket number. */
120
+ winningTicketNumber: number;
121
+ /** The on-chain transaction signature. */
122
+ signature: string;
123
+ }
124
+
125
+ interface ReconnectingEvent {
126
+ /** Delay in milliseconds before the next reconnection attempt. */
127
+ delay: number;
128
+ }
129
+
130
+ interface LotteryEvents {
131
+ draw: (event: DrawEvent) => void;
132
+ connected: () => void;
133
+ error: (err: any) => void;
134
+ reconnecting: (event: ReconnectingEvent) => void;
135
+ }
136
+
137
+ // ── Authority-like objects ────────────────────────────────────────────
138
+
139
+ /** An object with at least a `publicKey` property (e.g. a Keypair without the secret key). */
140
+ interface HasPublicKey {
141
+ publicKey: PublicKey;
142
+ }
143
+
144
+ // ── Classes ───────────────────────────────────────────────────────────
145
+
146
+ /**
147
+ * Low-level transaction builder, sender, and confirmation poller.
148
+ */
149
+ export class LotteryNetwork {
150
+ connection: Connection;
151
+
152
+ constructor(connection: Connection);
153
+
154
+ /** Build a versioned transaction with automatic compute and fee estimation. */
155
+ Tx(params: TxParams): Promise<TxResult | SimulationError>;
156
+
157
+ /** Send a signed transaction to the network. */
158
+ Send(tx: VersionedTransaction): Promise<string | SendError>;
159
+
160
+ /**
161
+ * Poll for transaction confirmation.
162
+ * @param sig - Transaction signature.
163
+ * @param max - Maximum polling attempts (default `10`).
164
+ * @param int - Seconds between polls (default `3`).
165
+ */
166
+ Status(sig: string, max?: number, int?: number): Promise<string>;
167
+
168
+ /** Simulate a transaction and return the optimised compute unit limit. */
169
+ Compute(
170
+ payer: PublicKey,
171
+ ix: TransactionInstruction[],
172
+ tolerance: number,
173
+ blockhash: string,
174
+ table?: any[] | false
175
+ ): Promise<number | SimulationError>;
176
+
177
+ /** Estimate priority fees for a transaction. */
178
+ Estimate(
179
+ payer: HasPublicKey,
180
+ priority_level: PriorityLevel,
181
+ instructions: TransactionInstruction[],
182
+ blockhash: string,
183
+ table?: any[] | false
184
+ ): Promise<number>;
185
+ }
186
+
187
+ /**
188
+ * Player and read operations — buy tickets, claim prizes, query state,
189
+ * and watch draws via WebSocket. Extends `EventEmitter`.
190
+ */
191
+ export class Lottery extends EventEmitter {
192
+ connection: Connection;
193
+ wss: string | false;
194
+ program: PublicKey;
195
+
196
+ constructor(connection: Connection, wss: string | false, program: PublicKey);
197
+
198
+ // ── Event emitter overrides for type-safe listeners ──
199
+
200
+ on<K extends keyof LotteryEvents>(event: K, listener: LotteryEvents[K]): this;
201
+ once<K extends keyof LotteryEvents>(event: K, listener: LotteryEvents[K]): this;
202
+ off<K extends keyof LotteryEvents>(event: K, listener: LotteryEvents[K]): this;
203
+ emit<K extends keyof LotteryEvents>(event: K, ...args: Parameters<LotteryEvents[K]>): boolean;
204
+
205
+ /** Start a WebSocket subscription for real-time draw events. Auto-reconnects on failure. */
206
+ WatchDraw(): Promise<void>;
207
+
208
+ /**
209
+ * Buy one or more tickets.
210
+ * @param buyer - The ticket buyer's keypair.
211
+ * @param authority - The lottery authority (only `publicKey` required).
212
+ * @param lotteryId - Lottery numeric identifier.
213
+ * @param amount - Number of tickets to buy (1–4, default `1`).
214
+ * @param encoded - If `true`, return a base64-encoded transaction.
215
+ */
216
+ BuyTickets(
217
+ buyer: Keypair,
218
+ authority: HasPublicKey,
219
+ lotteryId: number,
220
+ amount?: number,
221
+ encoded?: boolean
222
+ ): Promise<string | TxResult>;
223
+
224
+ /**
225
+ * Claim the prize for the winning ticket.
226
+ * @param authority - The lottery authority (only `publicKey` required).
227
+ * @param lotteryId - Lottery numeric identifier.
228
+ * @param winner - The winning ticket owner's keypair.
229
+ * @param encoded - If `true`, return a base64-encoded transaction.
230
+ */
231
+ ClaimTicket(
232
+ authority: HasPublicKey,
233
+ lotteryId: number,
234
+ winner: Keypair,
235
+ encoded?: boolean
236
+ ): Promise<string | TxResult>;
237
+
238
+ /** Fetch the on-chain state of a lottery. */
239
+ GetLottery(
240
+ authority: HasPublicKey,
241
+ lotteryId: number,
242
+ fees?: boolean
243
+ ): Promise<LotteryState>;
244
+
245
+ /** Fetch a single ticket by its ticket number. */
246
+ GetTicket(
247
+ authority: HasPublicKey,
248
+ lotteryId: number,
249
+ ticket: number
250
+ ): Promise<TicketInfo>;
251
+
252
+ /** Fetch all tickets for a lottery, optionally filtered by buyer. */
253
+ GetTickets(
254
+ authority: HasPublicKey,
255
+ lotteryId: number,
256
+ buyer?: HasPublicKey | false
257
+ ): Promise<TicketListResult>;
258
+
259
+ /** Derive the lottery PDA. */
260
+ DeriveLotteryPDA(
261
+ authority: PublicKey,
262
+ lotteryId: number
263
+ ): Promise<[PublicKey, number]>;
264
+
265
+ /** Derive a ticket PDA. */
266
+ DeriveTicketPDA(
267
+ lotteryPDA: PublicKey,
268
+ buyer: PublicKey,
269
+ ticketReceipt: PublicKey
270
+ ): Promise<[PublicKey, number]>;
271
+
272
+ /** Derive the prize pool PDA. */
273
+ DerivePrizePoolPDA(): Promise<[PublicKey, number]>;
274
+ }
275
+
276
+ /**
277
+ * Admin operations — initialize lotteries, trigger draws, lock/unlock ticket sales.
278
+ */
279
+ export class LotteryManager {
280
+ connection: Connection;
281
+ program: PublicKey;
282
+
283
+ constructor(connection: Connection, program: PublicKey);
284
+
285
+ /**
286
+ * Create a new on-chain lottery.
287
+ * @param authority - The authority keypair that will own the lottery.
288
+ * @param ticketPrice - Ticket price in lamports.
289
+ * @param lotteryId - A unique numeric lottery identifier.
290
+ * @param encoded - If `true`, return a base64-encoded transaction.
291
+ */
292
+ Initialize(
293
+ authority: Keypair,
294
+ ticketPrice: number,
295
+ lotteryId: number,
296
+ encoded?: boolean
297
+ ): Promise<string | TxResult>;
298
+
299
+ /**
300
+ * Trigger the on-chain random draw to select a winner.
301
+ * @param authority - The lottery authority keypair.
302
+ * @param lotteryId - Lottery numeric identifier.
303
+ * @param encoded - If `true`, return a base64-encoded transaction.
304
+ */
305
+ RandomDraw(
306
+ authority: Keypair,
307
+ lotteryId: number,
308
+ encoded?: boolean
309
+ ): Promise<LotteryState | string | TxResult | undefined>;
310
+
311
+ /**
312
+ * Lock or unlock ticket sales.
313
+ * @param authority - The lottery authority keypair.
314
+ * @param lotteryId - Lottery numeric identifier.
315
+ * @param lockState - `0` to lock (stop sales), `1` to unlock.
316
+ * @param encoded - If `true`, return a base64-encoded transaction.
317
+ */
318
+ LockLottery(
319
+ authority: Keypair,
320
+ lotteryId: number,
321
+ lockState: 0 | 1,
322
+ encoded?: boolean
323
+ ): Promise<LotteryState | string | TxResult | undefined>;
324
+ }
325
+ }
package/solotto.js CHANGED
@@ -162,7 +162,7 @@ class LotteryNetwork {
162
162
  }
163
163
  }
164
164
 
165
- class Lottery {
165
+ class Lottery extends EventEmitter {
166
166
 
167
167
  /***
168
168
  * @param {Connection} connection - Solana connection
@@ -170,6 +170,7 @@ class Lottery {
170
170
  * @param {PublicKey} program - Lottery Program Id
171
171
  */
172
172
  constructor(connection, wss = false, program){
173
+ super();
173
174
  this.connection=connection;
174
175
  this.wss=wss;
175
176
  this.program=program;
@@ -190,8 +191,6 @@ class Lottery {
190
191
  async function connect() {
191
192
  try{
192
193
  console.log('WatchDraw: Connecting to websocket...');
193
- new EventEmitter();
194
- EventEmitter.defaultMaxListeners = 1;
195
194
  const abortController = new AbortController();
196
195
  const subscriptions = createSolanaRpcSubscriptions(self.wss, {intervalMs: 30000});
197
196
  const allNotifications = await subscriptions.logsNotifications(
@@ -204,6 +203,7 @@ class Lottery {
204
203
  ).subscribe({abortSignal:abortController.signal});
205
204
 
206
205
  console.log('WatchDraw: Connected successfully');
206
+ self.emit('connected');
207
207
 
208
208
  for await (const noti of allNotifications) {
209
209
  const signature = noti.value.signature;
@@ -221,18 +221,15 @@ class Lottery {
221
221
  }
222
222
  }
223
223
  if(isDraw){
224
- const stopTicketInput = document.getElementById('stop-ticket');
225
- stopTicketInput.value = winningTicketNumber;
226
- const spinBtn = document.getElementById('spin-btn');
227
- const copySignatureBtn = document.getElementById('copy-signature-btn');
228
- copySignatureBtn.innerHTML = signature;
229
- spinBtn.click();
224
+ self.emit('draw', { winningTicketNumber, signature });
230
225
  }
231
226
  }
232
227
  }
233
228
  catch(err){
234
229
  console.log('WatchDraw: Connection error:', err);
230
+ self.emit('error', err);
235
231
  console.log(`WatchDraw: Reconnecting in ${RECONNECT_DELAY/1000} seconds...`);
232
+ self.emit('reconnecting', { delay: RECONNECT_DELAY });
236
233
  setTimeout(() => {
237
234
  connect();
238
235
  }, RECONNECT_DELAY);