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.
- package/LICENSE.md +21 -0
- package/README.md +476 -0
- package/package.json +28 -0
- 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
|
+
}
|