solotto 1.0.7 → 1.0.9

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.
Files changed (4) hide show
  1. package/README.md +98 -72
  2. package/package.json +1 -1
  3. package/solotto.d.ts +58 -48
  4. package/solotto.js +164 -153
package/README.md CHANGED
@@ -19,11 +19,11 @@ A JavaScript SDK for interacting with the Solotto on-chain lottery program on So
19
19
  - [RandomDraw](#randomdraw)
20
20
  - [LockLottery](#locklottery)
21
21
  - [ClaimExpired](#claimexpired)
22
- - [Boost](#boost)
23
- - [GetBoosters](#getboosters)
24
22
  - [Lottery](#lottery-api)
25
23
  - [BuyTickets](#buytickets)
26
24
  - [ClaimTicket](#claimticket)
25
+ - [Boost](#boost)
26
+ - [GetBoosters](#getboosters)
27
27
  - [GetLottery](#getlottery)
28
28
  - [GetTicket](#getticket)
29
29
  - [GetTickets](#gettickets)
@@ -72,8 +72,8 @@ Solotto is organized into three exported classes, each handling a different laye
72
72
 
73
73
  | Class | Role |
74
74
  |---|---|
75
- | **`LotteryManager`** | Admin operations — initialize lotteries, trigger draws, lock/unlock ticket sales. |
76
- | **`Lottery`** | Player & read operations — buy tickets, claim prizes, query lottery/ticket state, watch draws via WebSocket. |
75
+ | **`LotteryManager`** | Admin operations — initialize lotteries, trigger draws, lock/unlock ticket sales, reclaim expired prizes. |
76
+ | **`Lottery`** | Player & read operations — buy tickets, claim prizes, boost prize pools, query lottery/ticket/booster state, watch draws via WebSocket. |
77
77
  | **`LotteryNetwork`** | Low-level transaction utilities — build, simulate, send, and confirm transactions with automatic compute budget and priority fee estimation. |
78
78
 
79
79
  ---
@@ -219,16 +219,77 @@ const result = await manager.ClaimExpired(authority, lotteryId, encoded);
219
219
 
220
220
  ---
221
221
 
222
+ ### Lottery API
223
+
224
+ #### BuyTickets
225
+
226
+ Purchases one or more tickets for a lottery. Supports buying up to 4 tickets in a single transaction.
227
+
228
+ ```js
229
+ const lottery = new Lottery(connection, false, programId);
230
+
231
+ const result = await lottery.BuyTickets(buyer, authority, lotteryId, amount, encoded);
232
+ ```
233
+
234
+ | Parameter | Type | Default | Description |
235
+ |---|---|---|---|
236
+ | `buyer` | `Keypair` | — | The keypair of the ticket buyer. |
237
+ | `authority` | `Keypair \| {publicKey}` | — | The lottery authority (only `publicKey` is needed). |
238
+ | `lotteryId` | `Number` | — | The lottery ID to buy tickets for. |
239
+ | `amount` | `Number` | `1` | Number of tickets to purchase (1–4). |
240
+ | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
241
+
242
+ **Returns:** `"finalized"` on success, `"Lottery is not active, no tickets can be sold"` if the lottery is inactive, or the transaction object when encoded.
243
+
244
+ **Example:**
245
+
246
+ ```js
247
+ import { Connection, PublicKey, Keypair } from "@solana/web3.js";
248
+ import { Lottery } from "solotto";
249
+
250
+ const connection = new Connection("https://api.mainnet-beta.solana.com");
251
+ const programId = new PublicKey("YOUR_PROGRAM_ID");
252
+ const lottery = new Lottery(connection, false, programId);
253
+
254
+ const buyer = Keypair.generate();
255
+ const authority = { publicKey: new PublicKey("LOTTERY_AUTHORITY_PUBKEY") };
256
+
257
+ // Buy 2 tickets for lottery #1
258
+ const result = await lottery.BuyTickets(buyer, authority, 1, 2);
259
+ console.log(result); // "finalized"
260
+ ```
261
+
262
+ ---
263
+
264
+ #### ClaimTicket
265
+
266
+ Claims the prize for the winning ticket. Must be called by the ticket owner.
267
+
268
+ ```js
269
+ const result = await lottery.ClaimTicket(authority, lotteryId, winner, encoded);
270
+ ```
271
+
272
+ | Parameter | Type | Default | Description |
273
+ |---|---|---|---|
274
+ | `authority` | `{publicKey}` | — | The lottery authority (only `publicKey` is needed). |
275
+ | `lotteryId` | `Number` | — | The lottery ID. |
276
+ | `winner` | `Keypair` | — | The keypair of the winning ticket's owner. |
277
+ | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
278
+
279
+ **Returns:** `"finalized"` on success, the simulation log array (`string[]`) if the transaction fails simulation, or the transaction object when encoded.
280
+
281
+ ---
282
+
222
283
  #### Boost
223
284
 
224
285
  Boosts a lottery's prize pool by transferring SOL from any wallet. Can be called by anyone, not just the authority. Optionally attaches a memo message to the transaction.
225
286
 
226
287
  ```js
227
288
  // Boost lottery #1 with 0.5 SOL
228
- const result = await manager.Boost(authority, lotteryId, booster, 0.5);
289
+ const result = await lottery.Boost(authority, lotteryId, booster, 0.5);
229
290
 
230
291
  // Boost with a memo message
231
- const result = await manager.Boost(authority, lotteryId, booster, 1.0, "Good luck everyone!");
292
+ const result = await lottery.Boost(authority, lotteryId, booster, 1.0, "Good luck everyone!");
232
293
  ```
233
294
 
234
295
  | Parameter | Type | Default | Description |
@@ -252,13 +313,13 @@ Retrieves boost history by scanning on-chain program logs for boost transactions
252
313
 
253
314
  ```js
254
315
  // Get all boosters for a specific lottery
255
- const boosters = await manager.GetBoosters(authority, lotteryId);
316
+ const boosters = await lottery.GetBoosters(authority, lotteryId);
256
317
 
257
318
  // Get all boosters across all lotteries (up to 500 transactions)
258
- const allBoosters = await manager.GetBoosters(false, false, false, 500);
319
+ const allBoosters = await lottery.GetBoosters(false, false, false, 500);
259
320
 
260
321
  // Get boosters grouped by wallet address
261
- const grouped = await manager.GetBoosters(authority, lotteryId, true);
322
+ const grouped = await lottery.GetBoosters(authority, lotteryId, true);
262
323
  ```
263
324
 
264
325
  | Parameter | Type | Default | Description |
@@ -302,67 +363,6 @@ const grouped = await manager.GetBoosters(authority, lotteryId, true);
302
363
 
303
364
  ---
304
365
 
305
- ### Lottery API
306
-
307
- #### BuyTickets
308
-
309
- Purchases one or more tickets for a lottery. Supports buying up to 4 tickets in a single transaction.
310
-
311
- ```js
312
- const lottery = new Lottery(connection, false, programId);
313
-
314
- const result = await lottery.BuyTickets(buyer, authority, lotteryId, amount, encoded);
315
- ```
316
-
317
- | Parameter | Type | Default | Description |
318
- |---|---|---|---|
319
- | `buyer` | `Keypair` | — | The keypair of the ticket buyer. |
320
- | `authority` | `Keypair \| {publicKey}` | — | The lottery authority (only `publicKey` is needed). |
321
- | `lotteryId` | `Number` | — | The lottery ID to buy tickets for. |
322
- | `amount` | `Number` | `1` | Number of tickets to purchase (1–4). |
323
- | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
324
-
325
- **Returns:** `"finalized"` on success, `"Lottery is not active, no tickets can be sold"` if the lottery is inactive, or the transaction object when encoded.
326
-
327
- **Example:**
328
-
329
- ```js
330
- import { Connection, PublicKey, Keypair } from "@solana/web3.js";
331
- import { Lottery } from "solotto";
332
-
333
- const connection = new Connection("https://api.mainnet-beta.solana.com");
334
- const programId = new PublicKey("YOUR_PROGRAM_ID");
335
- const lottery = new Lottery(connection, false, programId);
336
-
337
- const buyer = Keypair.generate();
338
- const authority = { publicKey: new PublicKey("LOTTERY_AUTHORITY_PUBKEY") };
339
-
340
- // Buy 2 tickets for lottery #1
341
- const result = await lottery.BuyTickets(buyer, authority, 1, 2);
342
- console.log(result); // "finalized"
343
- ```
344
-
345
- ---
346
-
347
- #### ClaimTicket
348
-
349
- Claims the prize for the winning ticket. Must be called by the ticket owner.
350
-
351
- ```js
352
- const result = await lottery.ClaimTicket(authority, lotteryId, winner, encoded);
353
- ```
354
-
355
- | Parameter | Type | Default | Description |
356
- |---|---|---|---|
357
- | `authority` | `{publicKey}` | — | The lottery authority (only `publicKey` is needed). |
358
- | `lotteryId` | `Number` | — | The lottery ID. |
359
- | `winner` | `Keypair` | — | The keypair of the winning ticket's owner. |
360
- | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
361
-
362
- **Returns:** `"finalized"` on success, the simulation log array (`string[]`) if the transaction fails simulation, or the transaction object when encoded.
363
-
364
- ---
365
-
366
366
  #### GetLottery
367
367
 
368
368
  Fetches the full on-chain state of a lottery.
@@ -431,7 +431,7 @@ const ticket = await lottery.GetTicket(authority, lotteryId, ticketNumber);
431
431
 
432
432
  #### GetTickets
433
433
 
434
- Fetches all tickets for a lottery, optionally filtered by buyer.
434
+ Fetches all tickets for a lottery, optionally filtered by buyer and/or grouped by owner.
435
435
 
436
436
  ```js
437
437
  // Get all tickets
@@ -439,6 +439,9 @@ const allTickets = await lottery.GetTickets(authority, lotteryId);
439
439
 
440
440
  // Get tickets for a specific buyer
441
441
  const myTickets = await lottery.GetTickets(authority, lotteryId, buyer);
442
+
443
+ // Get all tickets grouped by owner
444
+ const grouped = await lottery.GetTickets(authority, lotteryId, false, true);
442
445
  ```
443
446
 
444
447
  | Parameter | Type | Default | Description |
@@ -446,8 +449,9 @@ const myTickets = await lottery.GetTickets(authority, lotteryId, buyer);
446
449
  | `authority` | `{publicKey}` | — | The lottery authority. |
447
450
  | `lotteryId` | `Number` | — | The lottery ID. |
448
451
  | `buyer` | `{publicKey} \| false` | `false` | Optional buyer to filter by. |
452
+ | `group` | `Boolean` | `false` | If `true`, groups tickets by owner. |
449
453
 
450
- **Returns:**
454
+ **Returns (ungrouped):**
451
455
 
452
456
  ```js
453
457
  {
@@ -468,6 +472,28 @@ const myTickets = await lottery.GetTickets(authority, lotteryId, buyer);
468
472
  }
469
473
  ```
470
474
 
475
+ **Returns (grouped, `group = true`):**
476
+
477
+ ```js
478
+ {
479
+ lotteryId: 1,
480
+ lotteryAddress: "Pubkey...",
481
+ lotteryAuth: "Pubkey...",
482
+ buyer: "All",
483
+ tickets: [
484
+ {
485
+ owner: "Pubkey...",
486
+ ticketCount: 3,
487
+ tickets: [
488
+ { owner: "Pubkey...", lottery: "Pubkey...", ticketReceipt: "Pubkey...", ticketNumber: 42, ticketPda: "Pubkey..." },
489
+ // ...
490
+ ],
491
+ },
492
+ // ... one entry per unique owner
493
+ ],
494
+ }
495
+ ```
496
+
471
497
  ---
472
498
 
473
499
  #### WatchDraw
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solotto",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Solana lottery client",
5
5
  "type": "module",
6
6
  "types": "./solotto.d.ts",
package/solotto.d.ts CHANGED
@@ -107,14 +107,23 @@ declare module "solotto" {
107
107
  ticketPda: string;
108
108
  }
109
109
 
110
+ interface GroupedTicketOwner {
111
+ /** Owner wallet public key. */
112
+ owner: string;
113
+ /** Number of tickets owned. */
114
+ ticketCount: number;
115
+ /** Array of ticket objects for this owner. */
116
+ tickets: TicketListItem[];
117
+ }
118
+
110
119
  interface TicketListResult {
111
120
  lotteryId: number;
112
121
  lotteryAddress: string;
113
122
  lotteryAuth: string;
114
123
  /** The buyer's public key, or `"All"` if unfiltered. */
115
124
  buyer: string;
116
- /** Tickets sorted descending by ticket number. */
117
- tickets: TicketListItem[];
125
+ /** Tickets sorted descending by ticket number, or grouped by owner when `group = true`. */
126
+ tickets: TicketListItem[] | GroupedTicketOwner[];
118
127
  }
119
128
 
120
129
  // ── WatchDraw Events ──────────────────────────────────────────────────
@@ -283,13 +292,58 @@ declare module "solotto" {
283
292
  ticket: number
284
293
  ): Promise<TicketInfo>;
285
294
 
286
- /** Fetch all tickets for a lottery, optionally filtered by buyer. */
295
+ /** Fetch all tickets for a lottery, optionally filtered by buyer and/or grouped by owner. */
287
296
  GetTickets(
288
297
  authority: HasPublicKey,
289
298
  lotteryId: number,
290
- buyer?: HasPublicKey | false
299
+ buyer?: HasPublicKey | false,
300
+ group?: boolean
291
301
  ): Promise<TicketListResult>;
292
302
 
303
+ /**
304
+ * Boost a lottery's prize pool by transferring SOL from any wallet.
305
+ * @param authority - The lottery authority (only `publicKey` needed).
306
+ * @param lotteryId - Lottery numeric identifier.
307
+ * @param booster - The keypair of the wallet sending the boost.
308
+ * @param amount - Amount of SOL to boost (e.g. `0.5` for 0.5 SOL).
309
+ * @param message - Optional memo string attached to the transaction.
310
+ * @param encoded - If `true`, return a base64-encoded transaction.
311
+ */
312
+ Boost(
313
+ authority: HasPublicKey,
314
+ lotteryId: number,
315
+ booster: Keypair,
316
+ amount: number,
317
+ message?: string | false,
318
+ encoded?: boolean
319
+ ): Promise<string | TxResult | undefined>;
320
+
321
+ /**
322
+ * Retrieve boost history from on-chain program logs.
323
+ * @param authority - Filter by lottery authority, or `false` for all.
324
+ * @param lotteryId - Filter by lottery ID, or `false` for all.
325
+ * @param group - If `true`, group results by booster wallet address.
326
+ * @param limit - Maximum number of recent transactions to scan (max 1000).
327
+ */
328
+ GetBoosters(
329
+ authority?: HasPublicKey | false,
330
+ lotteryId?: number | false,
331
+ group?: false,
332
+ limit?: number
333
+ ): Promise<BoosterRecord[]>;
334
+ GetBoosters(
335
+ authority: HasPublicKey | false,
336
+ lotteryId: number | false,
337
+ group: true,
338
+ limit?: number
339
+ ): Promise<GroupedBoostersResult>;
340
+ GetBoosters(
341
+ authority?: HasPublicKey | false,
342
+ lotteryId?: number | false,
343
+ group?: boolean,
344
+ limit?: number
345
+ ): Promise<BoosterRecord[] | GroupedBoostersResult>;
346
+
293
347
  /** Derive the lottery PDA. */
294
348
  DeriveLotteryPDA(
295
349
  authority: PublicKey,
@@ -367,49 +421,5 @@ declare module "solotto" {
367
421
  lotteryId: number,
368
422
  encoded?: boolean
369
423
  ): Promise<LotteryState | string | TxResult | undefined>;
370
-
371
- /**
372
- * Boost a lottery's prize pool by transferring SOL from any wallet.
373
- * @param authority - The lottery authority (only `publicKey` needed).
374
- * @param lotteryId - Lottery numeric identifier.
375
- * @param booster - The keypair of the wallet sending the boost.
376
- * @param amount - Amount of SOL to boost (e.g. `0.5` for 0.5 SOL).
377
- * @param message - Optional memo string attached to the transaction.
378
- * @param encoded - If `true`, return a base64-encoded transaction.
379
- */
380
- Boost(
381
- authority: HasPublicKey,
382
- lotteryId: number,
383
- booster: Keypair,
384
- amount: number,
385
- message?: string | false,
386
- encoded?: boolean
387
- ): Promise<string | TxResult | undefined>;
388
-
389
- /**
390
- * Retrieve boost history from on-chain transaction memos.
391
- * @param authority - Filter by lottery authority, or `false` for all.
392
- * @param lotteryId - Filter by lottery ID, or `false` for all.
393
- * @param group - If `true`, group results by booster wallet address.
394
- * @param limit - Maximum number of recent transactions to scan (max 1000).
395
- */
396
- GetBoosters(
397
- authority?: HasPublicKey | false,
398
- lotteryId?: number | false,
399
- group?: false,
400
- limit?: number
401
- ): Promise<BoosterRecord[]>;
402
- GetBoosters(
403
- authority: HasPublicKey | false,
404
- lotteryId: number | false,
405
- group: true,
406
- limit?: number
407
- ): Promise<GroupedBoostersResult>;
408
- GetBoosters(
409
- authority?: HasPublicKey | false,
410
- lotteryId?: number | false,
411
- group?: boolean,
412
- limit?: number
413
- ): Promise<BoosterRecord[] | GroupedBoostersResult>;
414
424
  }
415
425
  }
package/solotto.js CHANGED
@@ -429,12 +429,7 @@ class Lottery extends EventEmitter {
429
429
  * @param {Number} lotteryId - Lottery Id
430
430
  * @param {PublicKey} buyer - Ticket Buyer Optional
431
431
  */
432
- async GetTickets(authority, lotteryId, buyer = false) {
433
- async function numberToBase58(num, byteLength = 8) {
434
- const buffer = Buffer.alloc(byteLength);
435
- buffer.writeBigUInt64LE(BigInt(num), 0);
436
- return bs58.encode(buffer);
437
- }
432
+ async GetTickets(authority, lotteryId, buyer = false, group = false) {
438
433
  const [lotteryPDA] = await this.DeriveLotteryPDA(authority.publicKey, lotteryId);
439
434
  const filters = [];
440
435
  filters.push({dataSize: 104});
@@ -446,8 +441,7 @@ class Lottery extends EventEmitter {
446
441
  while(i < data_.length){
447
442
  const data = data_[i];
448
443
  const newTicket = {};
449
- const account = await this.connection.getAccountInfo(data.pubkey);
450
- const decoded = this.TICKET_STATE.decode(account.data);
444
+ const decoded = this.TICKET_STATE.decode(data.account.data);
451
445
  newTicket.owner = new PublicKey(decoded.owner).toString();
452
446
  newTicket.lottery = new PublicKey(decoded.lottery).toString();
453
447
  newTicket.ticketReceipt = new PublicKey(decoded.ticketReceipt).toString();
@@ -457,6 +451,24 @@ class Lottery extends EventEmitter {
457
451
  i++;
458
452
  }
459
453
  tickets.sort((a, b) => b.ticketNumber - a.ticketNumber);
454
+
455
+ let processedTickets = tickets;
456
+ if(group){
457
+ const grouped = {};
458
+ tickets.forEach(ticket => {
459
+ if(!grouped[ticket.owner]){
460
+ grouped[ticket.owner] = {
461
+ owner: ticket.owner,
462
+ ticketCount: 0,
463
+ tickets: []
464
+ };
465
+ }
466
+ grouped[ticket.owner].tickets.push(ticket);
467
+ grouped[ticket.owner].ticketCount++;
468
+ });
469
+ processedTickets = Object.values(grouped);
470
+ }
471
+
460
472
  let _buyer_ = "All";
461
473
  if(buyer){_buyer_ = buyer.publicKey.toString();}
462
474
  return {
@@ -464,7 +476,7 @@ class Lottery extends EventEmitter {
464
476
  lotteryAddress: lotteryPDA.toString(),
465
477
  lotteryAuth: authority.publicKey.toString(),
466
478
  buyer: _buyer_,
467
- tickets: tickets,
479
+ tickets: processedTickets,
468
480
  };
469
481
  }
470
482
 
@@ -574,6 +586,149 @@ class Lottery extends EventEmitter {
574
586
  return PublicKey.findProgramAddressSync([Buffer.from("prize-pool")], this.program);
575
587
  }
576
588
 
589
+ /**
590
+ * @param {Keypair} authority - Keypair
591
+ * @param {String} lotteryId - The lottery id
592
+ * @param {Keypair} booster - The booster's keypair
593
+ * @param {Number} amount - The amount of sol to boost
594
+ * @param {Boolean} encoded - true returns encoded transaction
595
+ */
596
+ async Boost(authority, lotteryId, booster, amount, message = false, encoded = false) {
597
+ try{
598
+ async function boostData(lotId, amount) {
599
+ const lamports = parseInt(amount * LAMPORTS_PER_SOL);
600
+ const buffer = Buffer.alloc(17); // 1 byte discriminator + 1 bytes price + 8 bytes id
601
+ buffer.writeUInt8(INSTRUCTIONS.BOOST_LOTTERY, 0); // boostLottery discriminator
602
+ buffer.writeBigUInt64LE(BigInt(lotId), 1);
603
+ buffer.writeBigUInt64LE(BigInt(lamports), 9);
604
+ return buffer;
605
+ }
606
+ if(message){message = ":booster:"+message;}
607
+ const network = new LotteryNetwork(this.connection);
608
+ const [lotteryPDA] = await this.DeriveLotteryPDA(authority.publicKey, lotteryId);
609
+ const LOTTO = await this.GetLottery(authority, lotteryId);
610
+ const keys = [
611
+ { pubkey: booster.publicKey, isSigner: true, isWritable: true },
612
+ { pubkey: lotteryPDA, isSigner: false, isWritable: true },
613
+ { pubkey: new PublicKey(LOTTO.prizePoolAddress), isSigner: false, isWritable: true },
614
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
615
+ ];
616
+ const ix = new TransactionInstruction(
617
+ {programId: this.program, keys, data: await boostData(lotteryId, amount)}
618
+ );
619
+ const _tx_ = {};
620
+ _tx_.account = booster.publicKey.toString(); // string : required
621
+ _tx_.instructions = [ix]; // array : required
622
+ _tx_.signers = false; // array : default false
623
+ _tx_.table = false; // array : default false
624
+ _tx_.tolerance = 1.2; // int : default 1.1
625
+ _tx_.compute = true; // bool : default true
626
+ _tx_.fees = true; // bool : default true
627
+ _tx_.priority = "Low"; // string : default Low
628
+ _tx_.memo = message;
629
+ if(encoded){
630
+ _tx_.serialize = true;
631
+ _tx_.encode = true;
632
+ }
633
+ else{
634
+ _tx_.serialize = false;
635
+ _tx_.encode = false;
636
+ }
637
+ const tx = await network.Tx(_tx_);
638
+ if(tx.logs && tx.logs.includes("Program log: Lottery draw has been initiated, cannot boost prize pool")){
639
+ return "Draw initiated, cannot boost this prize pool";
640
+ }
641
+ if(tx.status !== "ok"){return tx;}
642
+ if(booster.secretKey && !encoded){
643
+ tx.transaction.sign([booster]);
644
+ const sig = await network.Send(tx.transaction);
645
+ console.log("Signature:", sig);
646
+ const status = await network.Status(sig);
647
+ if(status == "finalized"){
648
+ return "boosted";
649
+ }
650
+ else{return status;}
651
+ }
652
+ else{return tx;}
653
+ }
654
+ catch (error) {
655
+ console.log(error);
656
+ }
657
+ }
658
+
659
+ /**
660
+ * @param {Keypair} authority - Keypair
661
+ * @param {Number} lotteryId - The lottery id
662
+ * @param {Boolean} group - if true, groups results by booster wallet address
663
+ * @param {Number} limit - the results to request (max 1000)
664
+ * @returns {Array|Object} - Array of booster objects or grouped booster objects if group=true
665
+ */
666
+ async GetBoosters(authority=false, lotteryId=false, group=false, limit=1000) {
667
+ try{
668
+ const initial = [];
669
+ const result = [];
670
+ const signatures = await this.connection.getSignaturesForAddress(this.program, {limit: limit,});
671
+ for await (const row of signatures) {
672
+ if(!row.err && row.confirmationStatus=="finalized" && row.memo && row.memo.includes(":booster:")){
673
+ initial.push(row);
674
+ }
675
+ }
676
+ for await (const init of initial) {
677
+ const tx = await this.connection.getTransaction(init.signature,{maxSupportedTransactionVersion:0});
678
+ const logs = tx.meta.logMessages;
679
+ const item = {};
680
+ let isBooster = false;
681
+ for await (const log of logs){if(log.includes("Program log: Boosted by")){isBooster=true;}}
682
+ if(!isBooster){continue;}
683
+ for await (const log of logs) {
684
+ if(log.includes("Program log: Boosted by ")){
685
+ item.booster = log.replace("Program log: Boosted by ","").trim();
686
+ }
687
+ else if(log.includes("Program log: Lottery ID ")){
688
+ item.lotteryId = Number(log.replace("Program log: Lottery ID ","").trim());
689
+ }
690
+ else if(log.includes("Program log: Boost amount: ")){
691
+ const data = log.replace("Program log: Boost amount: ","").trim();
692
+ item.amount = parseFloat(data.split(" SOL ")[0]);
693
+ }
694
+ else if(log.includes("Program log: Memo ")){
695
+ const parts = log.split(":booster:");
696
+ item.message = parts[1].slice(0, -1).trim();
697
+ }
698
+ else if(log.includes("Program log: Authority ")){
699
+ item.authority = log.replace("Program log: Authority ","").trim();
700
+ }
701
+ }
702
+ // Apply filters with safety checks
703
+ const matchesAuthority = authority ? (item.authority && authority.publicKey.toString() === item.authority) : true;
704
+ const matchesLotteryId = lotteryId ? (item.lotteryId !== undefined && lotteryId.toString() === item.lotteryId.toString()) : true;
705
+ if(matchesAuthority && matchesLotteryId && item.amount >= 0.0001){
706
+ item.signature = init.signature;
707
+ result.push(item);
708
+ }
709
+ }
710
+ // Group by booster if requested
711
+ if (group) {
712
+ const grouped = {};
713
+ result.forEach(item => {
714
+ if (!grouped[item.booster]) {
715
+ grouped[item.booster] = {
716
+ boost: [],
717
+ total: 0,
718
+ count: 0
719
+ };
720
+ }
721
+ grouped[item.booster].boost.push(item);
722
+ grouped[item.booster].total += item.amount;
723
+ grouped[item.booster].count = grouped[item.booster].boost.length;
724
+ });
725
+ return grouped;
726
+ }
727
+ return result;
728
+ }
729
+ catch (error) {return error;}
730
+ }
731
+
577
732
  }
578
733
 
579
734
  class LotteryManager {
@@ -823,150 +978,6 @@ class LotteryManager {
823
978
  }
824
979
  }
825
980
 
826
- /**
827
- * @param {Keypair} authority - Keypair
828
- * @param {String} lotteryId - The lottery id
829
- * @param {Keypair} booster - The booster's keypair
830
- * @param {Number} amount - The amount of sol to boost
831
- * @param {Boolean} encoded - true returns encoded transaction
832
- */
833
- async Boost(authority, lotteryId, booster, amount, message = false, encoded = false) {
834
- try{
835
- async function boostData(lotId, amount) {
836
- const lamports = parseInt(amount * LAMPORTS_PER_SOL);
837
- const buffer = Buffer.alloc(17); // 1 byte discriminator + 1 bytes price + 8 bytes id
838
- buffer.writeUInt8(INSTRUCTIONS.BOOST_LOTTERY, 0); // boostLottery discriminator
839
- buffer.writeBigUInt64LE(BigInt(lotId), 1);
840
- buffer.writeBigUInt64LE(BigInt(lamports), 9);
841
- return buffer;
842
- }
843
- if(message){message = ":booster:"+message;}
844
- const lottery = new Lottery(this.connection, false, this.program);
845
- const network = new LotteryNetwork(this.connection);
846
- const [lotteryPDA] = await lottery.DeriveLotteryPDA(authority.publicKey, lotteryId);
847
- const LOTTO = await lottery.GetLottery(authority, lotteryId);
848
- const keys = [
849
- { pubkey: booster.publicKey, isSigner: true, isWritable: true },
850
- { pubkey: lotteryPDA, isSigner: false, isWritable: true },
851
- { pubkey: new PublicKey(LOTTO.prizePoolAddress), isSigner: false, isWritable: true },
852
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
853
- ];
854
- const ix = new TransactionInstruction(
855
- {programId: this.program, keys, data: await boostData(lotteryId, amount)}
856
- );
857
- const _tx_ = {};
858
- _tx_.account = booster.publicKey.toString(); // string : required
859
- _tx_.instructions = [ix]; // array : required
860
- _tx_.signers = false; // array : default false
861
- _tx_.table = false; // array : default false
862
- _tx_.tolerance = 1.2; // int : default 1.1
863
- _tx_.compute = true; // bool : default true
864
- _tx_.fees = true; // bool : default true
865
- _tx_.priority = "Low"; // string : default Low
866
- _tx_.memo = message;
867
- if(encoded){
868
- _tx_.serialize = true;
869
- _tx_.encode = true;
870
- }
871
- else{
872
- _tx_.serialize = false;
873
- _tx_.encode = false;
874
- }
875
- const tx = await network.Tx(_tx_);
876
- if(tx.logs && tx.logs.includes("Program log: Lottery draw has been initiated, cannot boost prize pool")){
877
- return "Draw initiated, cannot boost this prize pool";
878
- }
879
- if(tx.status !== "ok"){return tx;}
880
- if(booster.secretKey && !encoded){
881
- tx.transaction.sign([booster]);
882
- const sig = await network.Send(tx.transaction);
883
- console.log("Signature:", sig);
884
- const status = await network.Status(sig);
885
- if(status == "finalized"){
886
- return "boosted";
887
- }
888
- else{return status;}
889
- }
890
- else{return tx;}
891
- }
892
- catch (error) {
893
- console.log(error);
894
- }
895
- }
896
-
897
- /**
898
- * @param {Keypair} authority - Keypair
899
- * @param {Number} lotteryId - The lottery id
900
- * @param {Boolean} group - if true, groups results by booster wallet address
901
- * @param {Number} limit - the results to request (max 1000)
902
- * @returns {Array|Object} - Array of booster objects or grouped booster objects if group=true
903
- */
904
- async GetBoosters(authority=false, lotteryId=false, group=false, limit=1000) {
905
- try{
906
- const initial = [];
907
- const result = [];
908
- const signatures = await this.connection.getSignaturesForAddress(this.program, {limit: limit,});
909
- for await (const row of signatures) {
910
- if(!row.err && row.confirmationStatus=="finalized" && row.memo && row.memo.includes(":booster:")){
911
- initial.push(row);
912
- }
913
- }
914
- for await (const init of initial) {
915
- const tx = await this.connection.getTransaction(init.signature,{maxSupportedTransactionVersion:0});
916
- const logs = tx.meta.logMessages;
917
- const item = {};
918
- let isBooster = false;
919
- for await (const log of logs){if(log.includes("Program log: Boosted by")){isBooster=true;}}
920
- if(!isBooster){continue;}
921
- for await (const log of logs) {
922
- if(log.includes("Program log: Boosted by ")){
923
- item.booster = log.replace("Program log: Boosted by ","").trim();
924
- }
925
- else if(log.includes("Program log: Lottery ID ")){
926
- item.lotteryId = Number(log.replace("Program log: Lottery ID ","").trim());
927
- }
928
- else if(log.includes("Program log: Boost amount: ")){
929
- const data = log.replace("Program log: Boost amount: ","").trim();
930
- item.amount = parseFloat(data.split(" SOL ")[0]);
931
- }
932
- else if(log.includes("Program log: Memo ")){
933
- const parts = log.split(":booster:");
934
- item.message = parts[1].slice(0, -1).trim();
935
- }
936
- else if(log.includes("Program log: Authority ")){
937
- item.authority = log.replace("Program log: Authority ","").trim();
938
- }
939
- }
940
- // Apply filters with safety checks
941
- const matchesAuthority = authority ? (item.authority && authority.publicKey.toString() === item.authority) : true;
942
- const matchesLotteryId = lotteryId ? (item.lotteryId !== undefined && lotteryId.toString() === item.lotteryId.toString()) : true;
943
- if(matchesAuthority && matchesLotteryId && item.amount >= 0.0001){
944
- item.signature = init.signature;
945
- result.push(item);
946
- }
947
- }
948
- // Group by booster if requested
949
- if (group) {
950
- const grouped = {};
951
- result.forEach(item => {
952
- if (!grouped[item.booster]) {
953
- grouped[item.booster] = {
954
- boost: [],
955
- total: 0,
956
- count: 0
957
- };
958
- }
959
- grouped[item.booster].boost.push(item);
960
- grouped[item.booster].total += item.amount;
961
- grouped[item.booster].count = grouped[item.booster].boost.length;
962
- });
963
- return grouped;
964
- }
965
- return result;
966
- }
967
- catch (error) {return error;}
968
- }
969
-
970
981
  }
971
982
 
972
983
  export {