solotto 1.0.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.
Files changed (4) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +476 -0
  3. package/package.json +28 -0
  4. package/solotto.js +749 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 Stratton Pelfmont
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,476 @@
1
+ # Solotto
2
+
3
+ A JavaScript SDK for interacting with the Solotto on-chain lottery program on Solana. Solotto provides a complete interface for creating lotteries, buying tickets, drawing winners, claiming prizes, and querying lottery state — all backed by a Solana smart contract.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [Quick Start](#quick-start)
11
+ - [Architecture](#architecture)
12
+ - [Classes](#classes)
13
+ - [LotteryManager](#lotterymanager)
14
+ - [Lottery](#lottery)
15
+ - [LotteryNetwork](#lotterynetwork)
16
+ - [API Reference](#api-reference)
17
+ - [LotteryManager](#lotterymanager-api)
18
+ - [Initialize](#initialize)
19
+ - [RandomDraw](#randomdraw)
20
+ - [LockLottery](#locklottery)
21
+ - [Lottery](#lottery-api)
22
+ - [BuyTickets](#buytickets)
23
+ - [ClaimTicket](#claimticket)
24
+ - [GetLottery](#getlottery)
25
+ - [GetTicket](#getticket)
26
+ - [GetTickets](#gettickets)
27
+ - [WatchDraw](#watchdraw)
28
+ - [LotteryNetwork](#lotterynetwork-api)
29
+ - [Tx](#tx)
30
+ - [Send](#send)
31
+ - [Status](#status)
32
+ - [Transaction Modes](#transaction-modes)
33
+ - [Dependencies](#dependencies)
34
+ - [License](#license)
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ npm install solotto
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ```js
49
+ import { Connection, PublicKey, Keypair } from "@solana/web3.js";
50
+ import { LotteryManager, Lottery } from "solotto";
51
+
52
+ const connection = new Connection("https://api.mainnet-beta.solana.com");
53
+ const programId = new PublicKey("YOUR_PROGRAM_ID");
54
+
55
+ // --- Authority creates a lottery ---
56
+ const authority = Keypair.generate(); // or load from file/wallet
57
+
58
+ const manager = new LotteryManager(connection, programId);
59
+ const result = await manager.Initialize(authority, 100000000, 1);
60
+ // Creates lottery #1 with a ticket price of 0.1 SOL (in lamports)
61
+ console.log(result); // "finalized"
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Architecture
67
+
68
+ Solotto is organized into three exported classes, each handling a different layer of responsibility:
69
+
70
+ | Class | Role |
71
+ |---|---|
72
+ | **`LotteryManager`** | Admin operations — initialize lotteries, trigger draws, lock/unlock ticket sales. |
73
+ | **`Lottery`** | Player & read operations — buy tickets, claim prizes, query lottery/ticket state, watch draws via WebSocket. |
74
+ | **`LotteryNetwork`** | Low-level transaction utilities — build, simulate, send, and confirm transactions with automatic compute budget and priority fee estimation. |
75
+
76
+ ---
77
+
78
+ ## Classes
79
+
80
+ ### LotteryManager
81
+
82
+ Used by the **lottery authority** (admin) to manage lotteries.
83
+
84
+ ```js
85
+ import { LotteryManager } from "solotto";
86
+
87
+ const manager = new LotteryManager(connection, programId);
88
+ ```
89
+
90
+ **Constructor Parameters:**
91
+
92
+ | Parameter | Type | Description |
93
+ |---|---|---|
94
+ | `connection` | `Connection` | A `@solana/web3.js` Connection instance. |
95
+ | `program` | `PublicKey` | The Solotto on-chain program ID. |
96
+
97
+ ---
98
+
99
+ ### Lottery
100
+
101
+ Used by **players** to buy tickets and claim prizes, and by anyone to read lottery state.
102
+
103
+ ```js
104
+ import { Lottery } from "solotto";
105
+
106
+ const lottery = new Lottery(connection, "wss://your-rpc.com", programId);
107
+ ```
108
+
109
+ **Constructor Parameters:**
110
+
111
+ | Parameter | Type | Default | Description |
112
+ |---|---|---|---|
113
+ | `connection` | `Connection` | — | A `@solana/web3.js` Connection instance. |
114
+ | `wss` | `String \| false` | `false` | WebSocket URL for real-time draw monitoring. Only required if using `WatchDraw()`. |
115
+ | `program` | `PublicKey` | — | The Solotto on-chain program ID. |
116
+
117
+ ---
118
+
119
+ ### LotteryNetwork
120
+
121
+ Low-level transaction builder and sender. Used internally by `LotteryManager` and `Lottery`, but can also be used directly for custom transaction flows.
122
+
123
+ ```js
124
+ import { LotteryNetwork } from "solotto";
125
+
126
+ const network = new LotteryNetwork(connection);
127
+ ```
128
+
129
+ **Constructor Parameters:**
130
+
131
+ | Parameter | Type | Description |
132
+ |---|---|---|
133
+ | `connection` | `Connection` | A `@solana/web3.js` Connection instance. |
134
+
135
+ ---
136
+
137
+ ## API Reference
138
+
139
+ ### LotteryManager API
140
+
141
+ #### Initialize
142
+
143
+ Creates a new on-chain lottery.
144
+
145
+ ```js
146
+ const result = await manager.Initialize(authority, ticketPrice, lotteryId, encoded);
147
+ ```
148
+
149
+ | Parameter | Type | Default | Description |
150
+ |---|---|---|---|
151
+ | `authority` | `Keypair` | — | The keypair that will own and control the lottery. |
152
+ | `ticketPrice` | `Number` | — | Price per ticket in **lamports** (1 SOL = 1,000,000,000 lamports). |
153
+ | `lotteryId` | `String` | — | A unique numeric identifier for this lottery. |
154
+ | `encoded` | `Boolean` | `false` | If `true`, returns a base64-encoded transaction instead of signing and sending. |
155
+
156
+ **Returns:** `"finalized"` on success when `encoded` is `false`, or a transaction object when `encoded` is `true`.
157
+
158
+ ---
159
+
160
+ #### RandomDraw
161
+
162
+ Triggers the on-chain random draw to select a winning ticket. Only callable by the lottery authority.
163
+
164
+ ```js
165
+ const result = await manager.RandomDraw(authority, lotteryId, encoded);
166
+ ```
167
+
168
+ | Parameter | Type | Default | Description |
169
+ |---|---|---|---|
170
+ | `authority` | `Keypair` | — | The lottery authority keypair. |
171
+ | `lotteryId` | `String` | — | The lottery ID to draw. |
172
+ | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
173
+
174
+ **Returns:** When `encoded` is `false` and the transaction finalizes, returns the updated lottery state object (see [GetLottery](#getlottery)). Otherwise returns the transaction object.
175
+
176
+ ---
177
+
178
+ #### LockLottery
179
+
180
+ Locks or unlocks ticket sales for a lottery. Only callable by the lottery authority.
181
+
182
+ ```js
183
+ // Lock ticket sales
184
+ await manager.LockLottery(authority, lotteryId, 0);
185
+
186
+ // Unlock ticket sales
187
+ await manager.LockLottery(authority, lotteryId, 1);
188
+ ```
189
+
190
+ | Parameter | Type | Default | Description |
191
+ |---|---|---|---|
192
+ | `authority` | `Keypair` | — | The lottery authority keypair. |
193
+ | `lotteryId` | `String` | — | The lottery ID. |
194
+ | `lockState` | `Number` | — | `0` to lock (stop sales), `1` to unlock (resume sales). |
195
+ | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
196
+
197
+ **Returns:** Updated lottery state object on finalization, or the transaction object when encoded.
198
+
199
+ ---
200
+
201
+ ### Lottery API
202
+
203
+ #### BuyTickets
204
+
205
+ Purchases one or more tickets for a lottery. Supports buying up to 4 tickets in a single transaction.
206
+
207
+ ```js
208
+ const lottery = new Lottery(connection, false, programId);
209
+
210
+ const result = await lottery.BuyTickets(buyer, authority, lotteryId, amount, encoded);
211
+ ```
212
+
213
+ | Parameter | Type | Default | Description |
214
+ |---|---|---|---|
215
+ | `buyer` | `Keypair` | — | The keypair of the ticket buyer. |
216
+ | `authority` | `Keypair \| {publicKey}` | — | The lottery authority (only `publicKey` is needed). |
217
+ | `lotteryId` | `Number` | — | The lottery ID to buy tickets for. |
218
+ | `amount` | `Number` | `1` | Number of tickets to purchase (1–4). |
219
+ | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
220
+
221
+ **Returns:** `"finalized"` on success, or the transaction object when encoded.
222
+
223
+ **Example:**
224
+
225
+ ```js
226
+ import { Connection, PublicKey, Keypair } from "@solana/web3.js";
227
+ import { Lottery } from "solotto";
228
+
229
+ const connection = new Connection("https://api.mainnet-beta.solana.com");
230
+ const programId = new PublicKey("YOUR_PROGRAM_ID");
231
+ const lottery = new Lottery(connection, false, programId);
232
+
233
+ const buyer = Keypair.generate();
234
+ const authority = { publicKey: new PublicKey("LOTTERY_AUTHORITY_PUBKEY") };
235
+
236
+ // Buy 2 tickets for lottery #1
237
+ const result = await lottery.BuyTickets(buyer, authority, 1, 2);
238
+ console.log(result); // "finalized"
239
+ ```
240
+
241
+ ---
242
+
243
+ #### ClaimTicket
244
+
245
+ Claims the prize for the winning ticket. Must be called by the ticket owner.
246
+
247
+ ```js
248
+ const result = await lottery.ClaimTicket(authority, lotteryId, winner, encoded);
249
+ ```
250
+
251
+ | Parameter | Type | Default | Description |
252
+ |---|---|---|---|
253
+ | `authority` | `{publicKey}` | — | The lottery authority (only `publicKey` is needed). |
254
+ | `lotteryId` | `Number` | — | The lottery ID. |
255
+ | `winner` | `Keypair` | — | The keypair of the winning ticket's owner. |
256
+ | `encoded` | `Boolean` | `false` | If `true`, returns encoded transaction. |
257
+
258
+ **Returns:** `"finalized"` on success, or the transaction object when encoded.
259
+
260
+ ---
261
+
262
+ #### GetLottery
263
+
264
+ Fetches the full on-chain state of a lottery.
265
+
266
+ ```js
267
+ const state = await lottery.GetLottery(authority, lotteryId, fees);
268
+ ```
269
+
270
+ | Parameter | Type | Default | Description |
271
+ |---|---|---|---|
272
+ | `authority` | `{publicKey}` | — | The lottery authority (only `publicKey` is needed). |
273
+ | `lotteryId` | `Number` | — | The lottery ID. |
274
+ | `fees` | `Boolean` | `true` | If `true`, `prizePoolBalance` reflects the pool minus the 10% protocol fee. Set to `false` for the raw balance. |
275
+
276
+ **Returns:**
277
+
278
+ ```js
279
+ {
280
+ authority: "Pubkey...", // Lottery authority public key
281
+ lotteryId: 1, // Lottery ID
282
+ ticketPrice: 100000000, // Ticket price in lamports
283
+ totalTickets: 42, // Total tickets sold
284
+ winnerTicketNumber: 17, // Winning ticket number (0/null if not drawn)
285
+ winnerAddress: "Pubkey...", // Winner's public key (null if not drawn)
286
+ isActive: true, // Whether the lottery is active
287
+ prizePoolBalance: 3780000000, // Prize pool in lamports (after fees if fees=true)
288
+ drawInitiated: false, // Whether a draw has been initiated
289
+ prizePoolAddress: "Pubkey...", // Prize pool PDA
290
+ lotteryAddress: "Pubkey...", // Lottery PDA
291
+ }
292
+ ```
293
+
294
+ ---
295
+
296
+ #### GetTicket
297
+
298
+ Fetches a single ticket by its ticket number.
299
+
300
+ ```js
301
+ const ticket = await lottery.GetTicket(authority, lotteryId, ticketNumber);
302
+ ```
303
+
304
+ | Parameter | Type | Description |
305
+ |---|---|---|
306
+ | `authority` | `{publicKey}` | The lottery authority (only `publicKey` is needed). |
307
+ | `lotteryId` | `Number` | The lottery ID. |
308
+ | `ticketNumber` | `Number` | The ticket number to look up. |
309
+
310
+ **Returns:**
311
+
312
+ ```js
313
+ {
314
+ ticketOwner: "Pubkey...", // Owner of the ticket
315
+ ticketReceipt: "Pubkey...", // Receipt account public key
316
+ ticketNumber: 17, // The ticket number
317
+ ticketPda: "Pubkey...", // Ticket PDA
318
+ lotteryId: 1, // Lottery ID
319
+ lotteryAddress: "Pubkey...", // Lottery PDA
320
+ lotteryAuth: "Pubkey...", // Lottery authority
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ #### GetTickets
327
+
328
+ Fetches all tickets for a lottery, optionally filtered by buyer.
329
+
330
+ ```js
331
+ // Get all tickets
332
+ const allTickets = await lottery.GetTickets(authority, lotteryId);
333
+
334
+ // Get tickets for a specific buyer
335
+ const myTickets = await lottery.GetTickets(authority, lotteryId, buyer);
336
+ ```
337
+
338
+ | Parameter | Type | Default | Description |
339
+ |---|---|---|---|
340
+ | `authority` | `{publicKey}` | — | The lottery authority. |
341
+ | `lotteryId` | `Number` | — | The lottery ID. |
342
+ | `buyer` | `{publicKey} \| false` | `false` | Optional buyer to filter by. |
343
+
344
+ **Returns:**
345
+
346
+ ```js
347
+ {
348
+ lotteryId: 1,
349
+ lotteryAddress: "Pubkey...",
350
+ lotteryAuth: "Pubkey...",
351
+ buyer: "All", // or the buyer's public key
352
+ tickets: [
353
+ {
354
+ owner: "Pubkey...",
355
+ lottery: "Pubkey...",
356
+ ticketReceipt: "Pubkey...",
357
+ ticketNumber: 42,
358
+ ticketPda: "Pubkey...",
359
+ },
360
+ // ... sorted descending by ticket number
361
+ ],
362
+ }
363
+ ```
364
+
365
+ ---
366
+
367
+ #### WatchDraw
368
+
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
+
371
+ ```js
372
+ const lottery = new Lottery(connection, "wss://your-rpc.com", programId);
373
+ await lottery.WatchDraw();
374
+ ```
375
+
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.
377
+
378
+ ---
379
+
380
+ ### LotteryNetwork API
381
+
382
+ The `LotteryNetwork` class handles low-level transaction construction, simulation, and sending. It is used internally but can also be used directly.
383
+
384
+ #### Tx
385
+
386
+ Builds a versioned transaction with automatic compute budget optimization and priority fee estimation.
387
+
388
+ ```js
389
+ const network = new LotteryNetwork(connection);
390
+
391
+ const result = await network.Tx({
392
+ account: "PublicKeyString", // (required) Payer public key as string
393
+ instructions: [ix1, ix2], // (required) Array of TransactionInstruction
394
+ signers: false, // Array of Keypairs to pre-sign, or false
395
+ priority: "Low", // "Low" | "Medium" | "High" | "VeryHigh" | "Extreme"
396
+ tolerance: 1.1, // Compute unit multiplier for safety margin
397
+ serialize: false, // If true, serializes the transaction
398
+ encode: false, // If true, base64-encodes the serialized transaction
399
+ compute: true, // If true, simulates and optimizes compute units
400
+ fees: true, // If true, estimates and sets priority fees
401
+ table: false, // Address Lookup Table account, or false
402
+ memo: false, // Optional memo string, or false
403
+ });
404
+
405
+ // result.status → "ok" | "error"
406
+ // result.message → "success" | error description
407
+ // result.transaction → VersionedTransaction (or serialized/encoded form)
408
+ ```
409
+
410
+ ---
411
+
412
+ #### Send
413
+
414
+ Sends a signed transaction to the network.
415
+
416
+ ```js
417
+ const signature = await network.Send(signedTransaction);
418
+ ```
419
+
420
+ **Returns:** A transaction signature string, or an error object `{ status: "error", message: ... }`.
421
+
422
+ ---
423
+
424
+ #### Status
425
+
426
+ Polls for transaction confirmation until finalized or timeout.
427
+
428
+ ```js
429
+ const status = await network.Status(signature, maxRetries, intervalSeconds);
430
+ ```
431
+
432
+ | Parameter | Type | Default | Description |
433
+ |---|---|---|---|
434
+ | `signature` | `String` | — | The transaction signature. |
435
+ | `maxRetries` | `Number` | `10` | Maximum number of polling attempts. |
436
+ | `intervalSeconds` | `Number` | `3` | Seconds between each poll. |
437
+
438
+ **Returns:** `"finalized"`, `"program error!"`, or a timeout message string.
439
+
440
+ ---
441
+
442
+ ## Transaction Modes
443
+
444
+ Every write method (`Initialize`, `RandomDraw`, `LockLottery`, `BuyTickets`, `ClaimTicket`) supports two modes controlled by the `encoded` parameter:
445
+
446
+ **Direct Mode** (`encoded = false`, default) — The SDK signs, sends, and confirms the transaction. Requires the keypair to have a `secretKey`. Returns the final status or lottery state.
447
+
448
+ ```js
449
+ const result = await manager.Initialize(authority, 100000000, 1);
450
+ // result → "finalized"
451
+ ```
452
+
453
+ **Encoded Mode** (`encoded = true`) — The SDK builds the transaction and returns it as a base64-encoded string. Useful for wallet adapters or server-side signing flows where the private key is not directly available.
454
+
455
+ ```js
456
+ const result = await manager.Initialize(authority, 100000000, 1, true);
457
+ // result.transaction → base64-encoded transaction string
458
+ // Sign and send with your wallet adapter
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Dependencies
464
+
465
+ - `@solana/web3.js` — Solana JavaScript SDK
466
+ - `@solana/spl-memo` — Memo program instruction helper
467
+ - `@solana/kit` — WebSocket subscriptions (for `WatchDraw`)
468
+ - `bn.js` — Big number library
469
+ - `bs58` — Base58 encoding/decoding
470
+ - `buffer-layout` — Buffer struct layout parsing
471
+
472
+ ---
473
+
474
+ ## License
475
+
476
+ See [LICENSE](./LICENSE) for details.
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "solotto",
3
+ "version": "1.0.0",
4
+ "description": "Solana lottery client",
5
+ "type": "module",
6
+ "main": "solotto.js",
7
+ "scripts": {
8
+ "start": "node test.js"
9
+ },
10
+ "keywords": [
11
+ "solana",
12
+ "lottery"
13
+ ],
14
+ "author": "@SolDapper",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@solana/kit": "^6.0.1",
18
+ "@solana/spl-memo": "^0.2.5",
19
+ "@solana/web3.js": "^1.98.4",
20
+ "bn.js": "^5.2.2",
21
+ "bs58": "^6.0.0",
22
+ "buffer-layout": "^1.2.2",
23
+ "events": "^3.3.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.10.0"
27
+ }
28
+ }
package/solotto.js ADDED
@@ -0,0 +1,749 @@
1
+ import {Connection, PublicKey, TransactionMessage, TransactionInstruction, VersionedTransaction, ComputeBudgetProgram, SystemProgram, Keypair, SYSVAR_SLOT_HASHES_PUBKEY, SYSVAR_CLOCK_PUBKEY, SYSVAR_RECENT_BLOCKHASHES_PUBKEY} from '@solana/web3.js';
2
+ import bs58 from 'bs58';
3
+ import BN from 'bn.js';
4
+ import {createMemoInstruction} from '@solana/spl-memo';
5
+ import BufferLayout from "buffer-layout";
6
+ const publicKey=(property="publicKey")=>{return BufferLayout.blob(32,property);};const uint64=(property="uint64")=>{return BufferLayout.blob(8,property);}
7
+ import {createSolanaRpcSubscriptions} from "@solana/kit";
8
+ import {EventEmitter} from 'events';
9
+
10
+ const INSTRUCTIONS = {
11
+ INITIALIZE_LOTTERY: 0,
12
+ BUY_TICKET: 1,
13
+ ENTROPY: 2,
14
+ CLAIM_PRIZE: 3,
15
+ LOCK_LOTTERY: 4,
16
+ };
17
+
18
+ class LotteryNetwork {
19
+ /*** @param {Connection} connection - Solana connection */
20
+ constructor(connection){this.connection=connection;}
21
+ async Tx(_data_){
22
+ let _obj_={};let _account_;let _instructions_;let _signers_;let _priority_;let _tolerance_;let _serialize_;let _encode_;let _table_;let _compute_;let _fees_;let _memo_;
23
+ if(typeof _data_.account=="undefined"){_obj_.message="missing account";return _obj_;}else{_account_=_data_.account;}
24
+ if(typeof _data_.instructions=="undefined"){_obj_.message="missing instructions";return _obj_;}else{_instructions_=_data_.instructions;}
25
+ if(typeof _data_.signers=="undefined" || _data_.signers==false){_signers_=false;}else{_signers_=_data_.signers;}
26
+ if(typeof _data_.priority=="undefined"){_priority_="Low";}else{_priority_=_data_.priority;}
27
+ if(typeof _data_.tolerance=="undefined" || _data_.tolerance==false){_tolerance_=1.1;}else{_tolerance_=_data_.tolerance;}
28
+ if(typeof _data_.serialize=="undefined"){_serialize_=false;}else{_serialize_=_data_.serialize;}
29
+ if(typeof _data_.encode=="undefined"){_encode_=false;}else{_encode_=_data_.encode;}
30
+ if(typeof _data_.compute=="undefined"){_compute_=true;}else{_compute_=_data_.compute;}
31
+ if(typeof _data_.fees=="undefined"){_fees_=true;}else{_fees_=_data_.fees;}
32
+ if(typeof _data_.table=="undefined" || _data_.table==false){_table_=[];}else{_table_=[_data_.table];}
33
+ if(typeof _data_.memo!="undefined" && _data_.memo!=false){_memo_=_data_.memo;}else{_memo_=false;}
34
+ const _wallet_ = new PublicKey(_account_);
35
+ const _blockhash_ = (await this.connection.getLatestBlockhash('confirmed')).blockhash;
36
+ if(_priority_=="Extreme"){_priority_="VeryHigh";}
37
+ let _payer_ = { publicKey : _wallet_ }
38
+ if(_memo_ != false){const memoIx = createMemoInstruction(_memo_,[new PublicKey(_account_)]);_instructions_.push(memoIx);}
39
+ if(_compute_ != false){
40
+ let _cu_ = null;
41
+ _cu_= await this.Compute(_wallet_,_instructions_,_tolerance_,_blockhash_,_table_);
42
+ if(typeof _cu_.logs != "undefined"){
43
+ _obj_.status="error";
44
+ _cu_.message="there was an error when simulating the transaction";
45
+ return _cu_;
46
+ }
47
+ else if(_cu_==null){
48
+ _obj_.status="error";
49
+ _obj_.message="there was an error when optimizing compute limit";
50
+ return _obj_;
51
+ }
52
+ _instructions_.unshift(ComputeBudgetProgram.setComputeUnitLimit({units:_cu_}));
53
+ }
54
+ if(_fees_ != false){
55
+ const get_priority = await this.Estimate(_payer_,_priority_,_instructions_,_blockhash_,_table_);
56
+ _instructions_.unshift(ComputeBudgetProgram.setComputeUnitPrice({microLamports:get_priority}));
57
+ }
58
+ let _message_ = new TransactionMessage({payerKey:_wallet_,recentBlockhash:_blockhash_,instructions:_instructions_,}).compileToV0Message(_table_);
59
+ let _tx_ = new VersionedTransaction(_message_);
60
+ if(_signers_!=false){
61
+ _tx_.sign(_signers_);
62
+ }
63
+ if(_serialize_ === true){
64
+ _tx_=_tx_.serialize();
65
+ }
66
+ if(_encode_ === true){
67
+ _tx_= Buffer.from(_tx_).toString("base64");
68
+ }
69
+ _obj_.status="ok";
70
+ _obj_.message="success";
71
+ _obj_.transaction=_tx_;
72
+ return _obj_;
73
+ }
74
+ async Send(tx){
75
+ try{
76
+ const signature = await this.connection.sendRawTransaction(tx.serialize(),{skipPreflight:true,maxRetries:0});
77
+ return signature;
78
+ }
79
+ catch(err){
80
+ const _error_ = {}
81
+ _error_.status="error";
82
+ _error_.message=err;
83
+ return _error_;
84
+ }
85
+ }
86
+ async Status(sig,max=10,int=3){
87
+ return await new Promise(resolve=>{
88
+ let start = 1;
89
+ let intervalID = setInterval(async()=>{
90
+ let tx_status = null;
91
+ tx_status = await this.connection.getSignatureStatuses([sig], {searchTransactionHistory: true,});
92
+ if (tx_status == null ||
93
+ typeof tx_status.value == "undefined" ||
94
+ tx_status.value == null ||
95
+ tx_status.value[0] == null ||
96
+ typeof tx_status.value[0] == "undefined" ||
97
+ typeof tx_status.value[0].confirmationStatus == "undefined"){
98
+ // console.log("trying again...");
99
+ }
100
+ else if(tx_status.value[0].confirmationStatus == "processed"){
101
+ start = 1;
102
+ }
103
+ else if(tx_status.value[0].confirmationStatus == "confirmed"){
104
+ // console.log("confirming...");
105
+ start = 1;
106
+ }
107
+ else if (tx_status.value[0].confirmationStatus == "finalized"){
108
+ if(tx_status.value[0].err != null){
109
+ resolve('program error!');
110
+ clearInterval(intervalID);
111
+ }
112
+ resolve('finalized');
113
+ clearInterval(intervalID);
114
+ }
115
+ start++;
116
+ if(start == max + 1){
117
+ resolve((max * int)+' seconds max wait reached');
118
+ clearInterval(intervalID);
119
+ }
120
+ },(int * 1000));
121
+ });
122
+ }
123
+ async Compute(payer,ix,tolerance,blockhash,table=false){
124
+ const sim_limit = ComputeBudgetProgram.setComputeUnitLimit({units:1400000});
125
+ const fee_limit = ComputeBudgetProgram.setComputeUnitPrice({microLamports:10000});
126
+ let re = []; for (let o in ix) {re.push(ix[o]);}
127
+ ix = re; ix.unshift(sim_limit); ix.unshift(fee_limit);
128
+ const msg = new TransactionMessage({payerKey:payer,recentBlockhash:blockhash,instructions:ix,}).compileToV0Message(table);
129
+ const tx = new VersionedTransaction(msg);
130
+ const res = await this.connection.simulateTransaction(tx,{replaceRecentBlockhash:true,sigVerify:false,});
131
+ // console.log(res);
132
+ if(res.value.err != null){return {"status":"error","message":"simulation error","details":res.value.err,"logs":res.value.logs};}
133
+ const consumed = res.value.unitsConsumed;
134
+ return Math.ceil(consumed * tolerance);
135
+ }
136
+ async Estimate(payer,priority_level,instructions,blockhash,table=false){
137
+ let re_ix = [];
138
+ for (let o in instructions) {re_ix.push(instructions[o]);}
139
+ instructions = re_ix;
140
+ const _msg = new TransactionMessage({payerKey:payer.publicKey,recentBlockhash:blockhash,instructions:instructions,}).compileToV0Message(table);
141
+ const tx = new VersionedTransaction(_msg);
142
+ const response = await fetch(this.connection.rpcEndpoint, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify({
146
+ jsonrpc: "2.0",
147
+ id: "1",
148
+ method: "getPriorityFeeEstimate",
149
+ params: [
150
+ {
151
+ transaction: bs58.encode(tx.serialize()), // Pass the serialized transaction in Base58
152
+ options: { priorityLevel: priority_level },
153
+ },
154
+ ],
155
+ }),
156
+ });
157
+ let data = await response.json();
158
+ data = parseInt(data.result.priorityFeeEstimate);
159
+ if(data == 1){data = 100000;}
160
+ if(data < 10000){data = 10000;}
161
+ return data;
162
+ }
163
+ }
164
+
165
+ class Lottery {
166
+
167
+ /***
168
+ * @param {Connection} connection - Solana connection
169
+ * @param {String} wss - Web Socket URL
170
+ * @param {PublicKey} program - Lottery Program Id
171
+ */
172
+ constructor(connection, wss = false, program){
173
+ this.connection=connection;
174
+ this.wss=wss;
175
+ this.program=program;
176
+ this.TICKET_STATE = BufferLayout.struct([
177
+ publicKey("owner"),
178
+ publicKey("lottery"),
179
+ publicKey("ticketReceipt"),
180
+ uint64("ticketNumber"),
181
+ ]);
182
+ }
183
+
184
+ /***
185
+ */
186
+ async WatchDraw(){
187
+ const self = this;
188
+ const RECONNECT_DELAY = 5000; // 5 seconds
189
+
190
+ async function connect() {
191
+ try{
192
+ console.log('WatchDraw: Connecting to websocket...');
193
+ new EventEmitter();
194
+ EventEmitter.defaultMaxListeners = 1;
195
+ const abortController = new AbortController();
196
+ const subscriptions = createSolanaRpcSubscriptions(self.wss, {intervalMs: 30000});
197
+ const allNotifications = await subscriptions.logsNotifications(
198
+ {
199
+ mentions: [self.program.toString()]
200
+ },
201
+ {
202
+ commitment: "finalized"
203
+ }
204
+ ).subscribe({abortSignal:abortController.signal});
205
+
206
+ console.log('WatchDraw: Connected successfully');
207
+
208
+ for await (const noti of allNotifications) {
209
+ const signature = noti.value.signature;
210
+ const logs = noti.value.logs;
211
+ const pattern_1 = "Program log: Winning ticket number: ";
212
+ const pattern_2 = "Program log: Fee amount: ";
213
+ let winningTicketNumber = 0;
214
+ let isDraw = false;
215
+ for await (const log of logs) {
216
+ if(log.includes(pattern_1)){
217
+ winningTicketNumber = parseInt(log.replace(pattern_1, ""));
218
+ }
219
+ if(log.includes(pattern_2)){
220
+ isDraw = true;
221
+ }
222
+ }
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();
230
+ }
231
+ }
232
+ }
233
+ catch(err){
234
+ console.log('WatchDraw: Connection error:', err);
235
+ console.log(`WatchDraw: Reconnecting in ${RECONNECT_DELAY/1000} seconds...`);
236
+ setTimeout(() => {
237
+ connect();
238
+ }, RECONNECT_DELAY);
239
+ }
240
+ }
241
+
242
+ // Start the connection
243
+ connect();
244
+ }
245
+
246
+ /***
247
+ * @param {PublicKey} authority - Keypair with no secretKey
248
+ * @param {Number} lotteryId - Lottery Id Number
249
+ * @param {PublicKey} winner - Keypair with no secretKey
250
+ * @param {Boolean} encoded - true returns encoded transaction
251
+ */
252
+ async ClaimTicket(authority, lotteryId, winner, encoded = false){
253
+ async function claimData() {
254
+ const buffer = Buffer.alloc(1);
255
+ buffer.writeUInt8(INSTRUCTIONS.CLAIM_PRIZE, 0);
256
+ return buffer;
257
+ }
258
+ const network = new LotteryNetwork(this.connection);
259
+ const LOTTO = await this.GetLottery(authority, lotteryId);
260
+ const TICKET_NUMBER = LOTTO.winnerTicketNumber;
261
+ LOTTO.ticket = await this.GetTicket(authority, lotteryId, TICKET_NUMBER);
262
+ const keys = [
263
+ { pubkey: new PublicKey(LOTTO.ticket.ticketOwner), isSigner: true, isWritable: true },
264
+ { pubkey: new PublicKey(LOTTO.ticket.lotteryAddress), isSigner: false, isWritable: true },
265
+ { pubkey: new PublicKey(LOTTO.ticket.ticketReceipt), isSigner: false, isWritable: false },
266
+ { pubkey: new PublicKey(LOTTO.ticket.ticketPda), isSigner: false, isWritable: false },
267
+ { pubkey: new PublicKey(LOTTO.prizePoolAddress), isSigner: false, isWritable: true },
268
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
269
+ ];
270
+ const ix = new TransactionInstruction({programId: this.program, keys, data: await claimData()});
271
+ const _tx_ = {};
272
+ _tx_.account = LOTTO.ticket.ticketOwner; // string : required
273
+ _tx_.instructions = [ix]; // array : required
274
+ _tx_.signers = false; // array : default false
275
+ _tx_.table = false; // array : default false
276
+ _tx_.tolerance = 1.2; // int : default 1.1
277
+ _tx_.compute = true; // bool : default true
278
+ _tx_.fees = true; // bool : default true
279
+ _tx_.priority = "Low";
280
+ if(encoded){
281
+ _tx_.serialize = true;
282
+ _tx_.encode = true;
283
+ }
284
+ else{
285
+ _tx_.serialize = false;
286
+ _tx_.encode = false;
287
+ }
288
+ const tx = await network.Tx(_tx_);
289
+ if(winner.secretKey && !encoded){
290
+ tx.transaction.sign([winner]);
291
+ const sig = await network.Send(tx.transaction);
292
+ console.log("Signature:", sig);
293
+ return await network.Status(sig);
294
+ }
295
+ else{return tx;}
296
+ }
297
+
298
+ /***
299
+ * @param {PublicKey} buyer - Keypair with no secretKey
300
+ * @param {PublicKey} authority - Keypair with no secretKey
301
+ * @param {Number} lotteryId - Lottery Id Number
302
+ * @param {Number} amount - Ticket Qty 1-4
303
+ * @param {Boolean} encoded - true returns encoded transaction
304
+ */
305
+ async BuyTickets(buyer, authority, lotteryId, amount = 1, encoded = false){
306
+ const network = new LotteryNetwork(this.connection);
307
+ const result = await this.BundleTickets(authority, buyer, lotteryId, amount);
308
+ const _tx_ = {};
309
+ _tx_.account = buyer.publicKey.toString(); // string : required
310
+ _tx_.instructions = result.ixs; // array : required
311
+ _tx_.signers = result.signers; // array : default false
312
+ _tx_.table = false; // array : default false
313
+ _tx_.tolerance = 1.2; // int : default 1.1
314
+ _tx_.compute = true; // bool : default true
315
+ _tx_.fees = true; // bool : default true
316
+ _tx_.priority = "Low";
317
+ if(encoded){
318
+ _tx_.serialize = true;
319
+ _tx_.encode = true;
320
+ }
321
+ else{
322
+ _tx_.serialize = false;
323
+ _tx_.encode = false;
324
+ }
325
+ const tx = await network.Tx(_tx_);
326
+ if(buyer.secretKey && !encoded){
327
+ tx.transaction.sign([buyer]);
328
+ const sig = await network.Send(tx.transaction);
329
+ console.log("Signature:", sig);
330
+ return await network.Status(sig);
331
+ }
332
+ else{return tx;}
333
+ }
334
+ async BundleTickets(authority, buyer, lotteryId, amount) {
335
+ async function ticketData() {
336
+ const buffer = Buffer.alloc(1);
337
+ buffer.writeUInt8(INSTRUCTIONS.BUY_TICKET, 0); // BuyTicket discriminator
338
+ return buffer;
339
+ }
340
+ const [lotteryPDA] = await this.DeriveLotteryPDA(authority.publicKey, lotteryId);
341
+ const [prizePoolPDA] = await this.DerivePrizePoolPDA(lotteryPDA);
342
+ const ixs = [];
343
+ const signers = [];
344
+ let i = 0;
345
+ while (i < amount) {
346
+ const ticketReceipt = new Keypair();
347
+ signers.push(ticketReceipt);
348
+ const rent = await this.connection.getMinimumBalanceForRentExemption(0);
349
+ const createReceiptAccountIx = SystemProgram.createAccount({
350
+ programId: this.program,
351
+ space: 0,
352
+ lamports: rent,
353
+ fromPubkey: buyer.publicKey,
354
+ newAccountPubkey: ticketReceipt.publicKey
355
+ });
356
+ ixs.push(createReceiptAccountIx);
357
+ const [ticketPDA] = await this.DeriveTicketPDA(lotteryPDA, buyer.publicKey, ticketReceipt.publicKey);
358
+ const ix = new TransactionInstruction({
359
+ keys: [
360
+ { pubkey: buyer.publicKey, isSigner: true, isWritable: true },
361
+ { pubkey: lotteryPDA, isSigner: false, isWritable: true },
362
+ { pubkey: ticketPDA, isSigner: false, isWritable: true },
363
+ { pubkey: prizePoolPDA, isSigner: false, isWritable: true },
364
+ { pubkey: ticketReceipt.publicKey, isSigner: true, isWritable: true },
365
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
366
+ ],
367
+ programId: this.program,
368
+ data: await ticketData(),
369
+ });
370
+ ixs.push(ix);
371
+ i++;
372
+ }
373
+ return {
374
+ ixs,
375
+ signers
376
+ };
377
+ }
378
+
379
+ /***
380
+ * @param {PublicKey} authority - Keypair with no secretKey
381
+ * @param {Number} lotteryId - Lottery Id
382
+ * @param {Number} ticket - Ticket Number
383
+ */
384
+ async GetTicket(authority, lotteryId, ticket) {
385
+ async function numberToBase58(num, byteLength = 8) {
386
+ const buffer = Buffer.alloc(byteLength);
387
+ buffer.writeBigUInt64LE(BigInt(num), 0);
388
+ return bs58.encode(buffer);
389
+ }
390
+ const [lotteryPDA] = await this.DeriveLotteryPDA(authority.publicKey, lotteryId);
391
+ const data_ = await this.connection.getProgramAccounts(this.program, {
392
+ filters:[
393
+ {dataSize: 104},
394
+ {memcmp:{
395
+ offset: 32,
396
+ bytes: lotteryPDA.toString(),
397
+ },},
398
+ {memcmp:{
399
+ offset: 96,
400
+ bytes: await numberToBase58(ticket),
401
+ },},
402
+ ],
403
+ });
404
+ const data = data_[0];
405
+ const accountInfo = await this.connection.getAccountInfo(data.pubkey);
406
+ const decoded = this.TICKET_STATE.decode(accountInfo.data);
407
+ const owner_ = new PublicKey(decoded.owner).toString();
408
+ const lottery_ = new PublicKey(decoded.lottery).toString();
409
+ const ticketReceipt_ = new PublicKey(decoded.ticketReceipt).toString();
410
+ const ticketNumber_ = parseInt(new BN(decoded.ticketNumber, 10, "le"));
411
+ return {
412
+ ticketOwner: owner_,
413
+ ticketReceipt: ticketReceipt_,
414
+ ticketNumber: ticketNumber_,
415
+ ticketPda: data.pubkey.toString(),
416
+ lotteryId: lotteryId,
417
+ lotteryAddress: lottery_,
418
+ lotteryAuth: authority.publicKey.toString(),
419
+ };
420
+ }
421
+
422
+ /***
423
+ * @param {PublicKey} authority - Keypair with no secretKey
424
+ * @param {Number} lotteryId - Lottery Id
425
+ * @param {PublicKey} buyer - Ticket Buyer Optional
426
+ */
427
+ async GetTickets(authority, lotteryId, buyer = false) {
428
+ async function numberToBase58(num, byteLength = 8) {
429
+ const buffer = Buffer.alloc(byteLength);
430
+ buffer.writeBigUInt64LE(BigInt(num), 0);
431
+ return bs58.encode(buffer);
432
+ }
433
+ const [lotteryPDA] = await this.DeriveLotteryPDA(authority.publicKey, lotteryId);
434
+ const filters = [];
435
+ filters.push({dataSize: 104});
436
+ if(buyer){filters.push({memcmp:{offset: 0, bytes: buyer.publicKey.toString(),},});}
437
+ filters.push({memcmp:{offset: 32,bytes: lotteryPDA.toString(),},});
438
+ const data_ = await this.connection.getProgramAccounts(this.program, {filters:filters,});
439
+ const tickets = [];
440
+ let i = 0;
441
+ while(i < data_.length){
442
+ const data = data_[i];
443
+ const newTicket = {};
444
+ const account = await this.connection.getAccountInfo(data.pubkey);
445
+ const decoded = this.TICKET_STATE.decode(account.data);
446
+ newTicket.owner = new PublicKey(decoded.owner).toString();
447
+ newTicket.lottery = new PublicKey(decoded.lottery).toString();
448
+ newTicket.ticketReceipt = new PublicKey(decoded.ticketReceipt).toString();
449
+ newTicket.ticketNumber = parseInt(new BN(decoded.ticketNumber, 10, "le"));
450
+ newTicket.ticketPda = data.pubkey.toString();
451
+ tickets.push(newTicket);
452
+ i++;
453
+ }
454
+ tickets.sort((a, b) => b.ticketNumber - a.ticketNumber);
455
+ let _buyer_ = "All";
456
+ if(buyer){_buyer_ = buyer.publicKey.toString();}
457
+ return {
458
+ lotteryId: lotteryId,
459
+ lotteryAddress: lotteryPDA.toString(),
460
+ lotteryAuth: authority.publicKey.toString(),
461
+ buyer: _buyer_,
462
+ tickets: tickets,
463
+ };
464
+ }
465
+
466
+ /***
467
+ * @param {PublicKey} authority - Keypair with no secretKey
468
+ * @param {Number} lotteryId - Lottery Id
469
+ * @param {Boolean} fees - true = prize pool - 10% (for display before drawing)
470
+ */
471
+ async GetLottery(authority, lotteryId, fees = true) {
472
+ const [lotteryPDA] = await this.DeriveLotteryPDA(authority.publicKey, lotteryId);
473
+ const account = await this.connection.getAccountInfo(lotteryPDA);
474
+ return await this.DecodeLotteryState(account.data, fees);
475
+ }
476
+ async DecodeLotteryState(buffer, fees = true){
477
+ let offset = 0;
478
+ // Helper to handle the 1-byte Option flag
479
+ const readOption = (readFn) => {
480
+ const hasValue = buffer.readUInt8(offset) === 1;
481
+ offset += 1;
482
+ return hasValue ? readFn() : null;
483
+ };
484
+ // 1. authority: Pubkey (32 bytes)
485
+ const auth = new PublicKey(buffer.slice(offset, offset + 32)).toString();
486
+ offset += 32;
487
+ // 2. lottery_id: u64 (8 bytes)
488
+ const lotteryId = Number(buffer.readBigUInt64LE(offset));
489
+ offset += 8;
490
+ // 3. ticket_price: u64 (8 bytes)
491
+ const ticketPrice = Number(buffer.readBigUInt64LE(offset));
492
+ offset += 8;
493
+ // 4. total_tickets: u64 (8 bytes)
494
+ const totalTickets = Number(buffer.readBigUInt64LE(offset));
495
+ offset += 8;
496
+ // 5. winner_ticket_number: Option<u64> (1 + 8 bytes)
497
+ const winnerTicketNumber = readOption(() => {
498
+ const val = buffer.readBigUInt64LE(offset);
499
+ offset += 8;
500
+ return val;
501
+ });
502
+ // 6. winner_address: Option<Pubkey> (1 + 32 bytes)
503
+ const winnerAddress = readOption(() => {
504
+ const val = buffer.slice(offset, offset + 32);
505
+ offset += 32;
506
+ return val;
507
+ });
508
+ // 7. is_active: bool (1 byte)
509
+ const isActive = buffer.readUInt8(offset) === 1;
510
+ offset += 1;
511
+ // 8. prize_pool: u64 (8 bytes)
512
+ const prizePool = Number(buffer.readBigUInt64LE(offset));
513
+ offset += 8;
514
+ const drawInitiated = buffer.readUInt8(offset) === 1;
515
+ offset += 1;
516
+ const prizePoolAddress = await this.DerivePrizePoolPDA();
517
+ const lotteryAddress = await this.DeriveLotteryPDA(new PublicKey(auth), lotteryId);
518
+ let prizePoolBalance = prizePool;
519
+ if(fees){prizePoolBalance = prizePool - (prizePool * 0.1);}
520
+ return {
521
+ authority: auth,
522
+ lotteryId,
523
+ ticketPrice,
524
+ totalTickets,
525
+ winnerTicketNumber: Number(winnerTicketNumber),
526
+ winnerAddress,
527
+ isActive,
528
+ prizePoolBalance,
529
+ drawInitiated,
530
+ prizePoolAddress: prizePoolAddress[0].toString(),
531
+ lotteryAddress: lotteryAddress[0].toString(),
532
+ };
533
+ };
534
+
535
+ /*** Derivation Helpers */
536
+ async DeriveLotteryPDA(authority, lotteryId) {
537
+ const idBuffer = Buffer.alloc(8);
538
+ idBuffer.writeBigUInt64LE(BigInt(lotteryId));
539
+ return PublicKey.findProgramAddressSync([Buffer.from("lottery"), authority.toBuffer(), idBuffer], this.program);
540
+ }
541
+ async DeriveTicketPDA(lotteryPDA, buyer, ticketReceipt) {
542
+ const programId = this.program;
543
+ return PublicKey.findProgramAddressSync(
544
+ [
545
+ Buffer.from("ticket"),
546
+ lotteryPDA.toBuffer(),
547
+ buyer.toBuffer(),
548
+ ticketReceipt.toBuffer()
549
+ ],
550
+ programId
551
+ );
552
+ }
553
+ async DerivePrizePoolPDA() {
554
+ return PublicKey.findProgramAddressSync([Buffer.from("prize-pool")], this.program);
555
+ }
556
+
557
+ }
558
+
559
+ class LotteryManager {
560
+
561
+ /***
562
+ * @param {Connection} connection - Solana connection
563
+ * @param {PublicKey} program - Lottery Program Id
564
+ */
565
+ constructor(connection, program){this.connection=connection;this.program=program;}
566
+
567
+ /***
568
+ * @param {Keypair} authority - Keypair
569
+ * @param {Number} ticketPrice - Number
570
+ * @param {String} lotteryId - String
571
+ * @param {Boolean} encoded - true returns encoded transaction
572
+ */
573
+ async Initialize(authority, ticketPrice, lotteryId, encoded = false){
574
+ const lottery = new Lottery(this.connection, false, this.program);
575
+ const network = new LotteryNetwork(this.connection);
576
+ async function initializeData(tketPrice, lotId) {
577
+ const buffer = Buffer.alloc(17); // 1 byte discriminator + 8 bytes price + 8 bytes id
578
+ buffer.writeUInt8(INSTRUCTIONS.INITIALIZE_LOTTERY, 0); // initializeLottery discriminator
579
+ buffer.writeBigUInt64LE(BigInt(tketPrice), 1);
580
+ buffer.writeBigUInt64LE(BigInt(lotId), 9);
581
+ return buffer;
582
+ }
583
+ const [lotteryPDA, bump] = await lottery.DeriveLotteryPDA(authority.publicKey, lotteryId);
584
+ console.log("Lottery PDA:", lotteryPDA.toString());
585
+ const ix = new TransactionInstruction({
586
+ keys: [
587
+ { pubkey: authority.publicKey, isSigner: true, isWritable: true },
588
+ { pubkey: lotteryPDA, isSigner: false, isWritable: true },
589
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
590
+ ],
591
+ programId: this.program,
592
+ data: await initializeData(ticketPrice, lotteryId),
593
+ });
594
+ const _tx_ = {};
595
+ _tx_.account = authority.publicKey.toString(); // string : required
596
+ _tx_.instructions = [ix]; // array : required
597
+ _tx_.signers = false; // array : default false
598
+ _tx_.table = false; // array : default false
599
+ _tx_.tolerance = 1.2; // int : default 1.1
600
+ _tx_.compute = true; // bool : default true
601
+ _tx_.fees = true; // bool : default true
602
+ _tx_.priority = "Low"; // string : default Low
603
+ if(encoded){
604
+ _tx_.serialize = true;
605
+ _tx_.encode = true;
606
+ }
607
+ else{
608
+ _tx_.serialize = false;
609
+ _tx_.encode = false;
610
+ }
611
+ const tx = await network.Tx(_tx_);
612
+ if(tx.status !== "ok"){return tx;}
613
+ if(authority.secretKey && !encoded){
614
+ tx.transaction.sign([authority]);
615
+ const sig = await network.Send(tx.transaction);
616
+ console.log("Signature:", sig);
617
+ return await network.Status(sig);
618
+ }
619
+ else{return tx;}
620
+ }
621
+
622
+ /**
623
+ * @param {Keypair} authority - Keypair
624
+ * @param {String} lotteryId - The lottery id
625
+ * @param {Boolean} encoded - true returns encoded transaction
626
+ */
627
+ async RandomDraw(authority, lotteryId, encoded = false) {
628
+ try{
629
+ async function randomnessData() {
630
+ const buffer = Buffer.alloc(1);
631
+ buffer.writeUInt8(INSTRUCTIONS.ENTROPY, 0);
632
+ return buffer;
633
+ }
634
+ const lottery = new Lottery(this.connection, false, this.program);
635
+ const network = new LotteryNetwork(this.connection);
636
+ const [lotteryPDA] = await lottery.DeriveLotteryPDA(authority.publicKey, lotteryId);
637
+ const [prizePoolPDA] = await lottery.DerivePrizePoolPDA();
638
+ const keys = [
639
+ { pubkey: authority.publicKey, isSigner: true, isWritable: false },
640
+ { pubkey: lotteryPDA, isSigner: false, isWritable: true },
641
+ { pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, isSigner: false, isWritable: false },
642
+ { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
643
+ { pubkey: prizePoolPDA, isSigner: false, isWritable: true },
644
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
645
+ ];
646
+ const ix = new TransactionInstruction({programId: this.program, keys, data: await randomnessData()});
647
+ const _tx_ = {};
648
+ _tx_.account = authority.publicKey.toString(); // string : required
649
+ _tx_.instructions = [ix]; // array : required
650
+ _tx_.signers = false; // array : default false
651
+ _tx_.table = false; // array : default false
652
+ _tx_.tolerance = 1.2; // int : default 1.1
653
+ _tx_.compute = true; // bool : default true
654
+ _tx_.fees = true; // bool : default true
655
+ _tx_.priority = "Low"; // string : default Low
656
+ _tx_.memo = "draw";
657
+ if(encoded){
658
+ _tx_.serialize = true;
659
+ _tx_.encode = true;
660
+ }
661
+ else{
662
+ _tx_.serialize = false;
663
+ _tx_.encode = false;
664
+ }
665
+ const tx = await network.Tx(_tx_); // build the tx
666
+ if(tx.status !== "ok"){return tx;}
667
+ if(authority.secretKey && !encoded){
668
+ tx.transaction.sign([authority]);
669
+ const sig = await network.Send(tx.transaction);
670
+ console.log("Signature:", sig);
671
+ const status = await network.Status(sig);
672
+ if(status == "finalized"){
673
+ return await lottery.GetLottery({publicKey: authority.publicKey}, lotteryId, false);
674
+ }
675
+ else{return status;}
676
+ }
677
+ else{return tx;}
678
+ }
679
+ catch (error) {
680
+ console.log(error);
681
+ }
682
+ }
683
+
684
+ /**
685
+ * @param {Keypair} authority - Keypair
686
+ * @param {String} lotteryId - The lottery id
687
+ * @param {Number} lockState - 0 = lock ticket sales, 1 = unlock (requires authority)
688
+ * @param {Boolean} encoded - true returns encoded transaction
689
+ */
690
+ async LockLottery(authority, lotteryId, lockState, encoded = false) {
691
+ try{
692
+ async function lockData(lock) {
693
+ const buffer = Buffer.alloc(2); // 1 byte discriminator + 1 bytes lock status
694
+ buffer.writeUInt8(INSTRUCTIONS.LOCK_LOTTERY, 0); // lock discriminator
695
+ buffer.writeUInt8(lock, 1); // write the new lock status
696
+ return buffer;
697
+ }
698
+ const lottery = new Lottery(this.connection, false, this.program);
699
+ const network = new LotteryNetwork(this.connection);
700
+ const [lotteryPDA] = await lottery.DeriveLotteryPDA(authority.publicKey, lotteryId);
701
+ const keys = [
702
+ { pubkey: authority.publicKey, isSigner: true, isWritable: false },
703
+ { pubkey: lotteryPDA, isSigner: false, isWritable: true },
704
+ ];
705
+ const ix = new TransactionInstruction({programId: this.program, keys, data: await lockData(lockState)});
706
+ const _tx_ = {};
707
+ _tx_.account = authority.publicKey.toString(); // string : required
708
+ _tx_.instructions = [ix]; // array : required
709
+ _tx_.signers = false; // array : default false
710
+ _tx_.table = false; // array : default false
711
+ _tx_.tolerance = 1.2; // int : default 1.1
712
+ _tx_.compute = true; // bool : default true
713
+ _tx_.fees = true; // bool : default true
714
+ _tx_.priority = "Low"; // string : default Low
715
+ _tx_.memo = false;
716
+ if(encoded){
717
+ _tx_.serialize = true;
718
+ _tx_.encode = true;
719
+ }
720
+ else{
721
+ _tx_.serialize = false;
722
+ _tx_.encode = false;
723
+ }
724
+ const tx = await network.Tx(_tx_); // build the tx
725
+ if(tx.status !== "ok"){return tx;}
726
+ if(authority.secretKey && !encoded){
727
+ tx.transaction.sign([authority]);
728
+ const sig = await network.Send(tx.transaction);
729
+ console.log("Signature:", sig);
730
+ const status = await network.Status(sig);
731
+ if(status == "finalized"){
732
+ return await lottery.GetLottery({publicKey: authority.publicKey}, lotteryId, false);
733
+ }
734
+ else{return status;}
735
+ }
736
+ else{return tx;}
737
+ }
738
+ catch (error) {
739
+ console.log(error);
740
+ }
741
+ }
742
+
743
+ }
744
+
745
+ export {
746
+ Lottery,
747
+ LotteryNetwork,
748
+ LotteryManager
749
+ }