@slvr-labs/sdk 0.1.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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +849 -0
  3. package/dist/connect.d.ts +47 -0
  4. package/dist/connect.d.ts.map +1 -0
  5. package/dist/connect.js +56 -0
  6. package/dist/connect.js.map +1 -0
  7. package/dist/contracts/autoCommit.d.ts +151 -0
  8. package/dist/contracts/autoCommit.d.ts.map +1 -0
  9. package/dist/contracts/autoCommit.js +426 -0
  10. package/dist/contracts/autoCommit.js.map +1 -0
  11. package/dist/contracts/hub.d.ts +50 -0
  12. package/dist/contracts/hub.d.ts.map +1 -0
  13. package/dist/contracts/hub.js +118 -0
  14. package/dist/contracts/hub.js.map +1 -0
  15. package/dist/contracts/index.d.ts +8 -0
  16. package/dist/contracts/index.d.ts.map +1 -0
  17. package/dist/contracts/index.js +18 -0
  18. package/dist/contracts/index.js.map +1 -0
  19. package/dist/contracts/jackpot.d.ts +21 -0
  20. package/dist/contracts/jackpot.d.ts.map +1 -0
  21. package/dist/contracts/jackpot.js +44 -0
  22. package/dist/contracts/jackpot.js.map +1 -0
  23. package/dist/contracts/lottery.d.ts +256 -0
  24. package/dist/contracts/lottery.d.ts.map +1 -0
  25. package/dist/contracts/lottery.js +767 -0
  26. package/dist/contracts/lottery.js.map +1 -0
  27. package/dist/contracts/registry.d.ts +53 -0
  28. package/dist/contracts/registry.d.ts.map +1 -0
  29. package/dist/contracts/registry.js +143 -0
  30. package/dist/contracts/registry.js.map +1 -0
  31. package/dist/contracts/staking.d.ts +101 -0
  32. package/dist/contracts/staking.d.ts.map +1 -0
  33. package/dist/contracts/staking.js +306 -0
  34. package/dist/contracts/staking.js.map +1 -0
  35. package/dist/contracts/token.d.ts +95 -0
  36. package/dist/contracts/token.d.ts.map +1 -0
  37. package/dist/contracts/token.js +273 -0
  38. package/dist/contracts/token.js.map +1 -0
  39. package/dist/deployments.d.ts +117 -0
  40. package/dist/deployments.d.ts.map +1 -0
  41. package/dist/deployments.js +74 -0
  42. package/dist/deployments.js.map +1 -0
  43. package/dist/errors.d.ts +39 -0
  44. package/dist/errors.d.ts.map +1 -0
  45. package/dist/errors.js +67 -0
  46. package/dist/errors.js.map +1 -0
  47. package/dist/ev.d.ts +123 -0
  48. package/dist/ev.d.ts.map +1 -0
  49. package/dist/ev.js +111 -0
  50. package/dist/ev.js.map +1 -0
  51. package/dist/events.d.ts +418 -0
  52. package/dist/events.d.ts.map +1 -0
  53. package/dist/events.js +87 -0
  54. package/dist/events.js.map +1 -0
  55. package/dist/index.d.ts +248 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +436 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/oracle.d.ts +48 -0
  60. package/dist/oracle.d.ts.map +1 -0
  61. package/dist/oracle.js +77 -0
  62. package/dist/oracle.js.map +1 -0
  63. package/dist/price.d.ts +52 -0
  64. package/dist/price.d.ts.map +1 -0
  65. package/dist/price.js +78 -0
  66. package/dist/price.js.map +1 -0
  67. package/dist/transaction.d.ts +54 -0
  68. package/dist/transaction.d.ts.map +1 -0
  69. package/dist/transaction.js +105 -0
  70. package/dist/transaction.js.map +1 -0
  71. package/dist/types.d.ts +162 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +23 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/utils.d.ts +58 -0
  76. package/dist/utils.d.ts.map +1 -0
  77. package/dist/utils.js +110 -0
  78. package/dist/utils.js.map +1 -0
  79. package/examples/.env.example +22 -0
  80. package/examples/README.md +417 -0
  81. package/examples/automated-betting.ts +232 -0
  82. package/examples/combined-strategy.ts +227 -0
  83. package/examples/constants.ts +35 -0
  84. package/examples/custom-strategy-example.ts +231 -0
  85. package/examples/expected-value-strategy.ts +255 -0
  86. package/examples/fixed-squares-strategy.ts +176 -0
  87. package/examples/index.ts +30 -0
  88. package/examples/least-allocated-strategy.ts +224 -0
  89. package/examples/local-test.ts +172 -0
  90. package/examples/quickstart-bet.ts +72 -0
  91. package/examples/quickstart-read.ts +87 -0
  92. package/examples/simple-example.ts +122 -0
  93. package/examples/strategy-base.ts +283 -0
  94. package/package.json +71 -0
  95. package/skills/slvr-bot/SKILL.md +193 -0
  96. package/skills/slvr-bot/references/api.md +117 -0
@@ -0,0 +1,417 @@
1
+ # Slvr SDK Examples
2
+
3
+ This directory contains reference implementations demonstrating how to use the Slvr SDK for various betting strategies and automation.
4
+
5
+ > **How to use these:** they are reference code, not part of the importable API.
6
+ > Copy the file(s) you want into your own project and change the relative
7
+ > `../src` import at the top of each file to `@slvr-labs/sdk`. The import paths in the
8
+ > snippets below (e.g. `from './fixed-squares-strategy'`) assume you've copied
9
+ > the example files alongside your own code.
10
+
11
+ ## Getting Started
12
+
13
+ Brand new to the SDK? Run these two first β€” they use the addresses and chain that
14
+ ship with the SDK (`deployments.robinhood` + `robinhoodChain`), so there's nothing
15
+ to configure.
16
+
17
+ ### `quickstart-read.ts` β€” read-only tour (no wallet)
18
+
19
+ The fastest way to confirm everything works. Needs only an RPC β€” no private key.
20
+ Prints the current round, pot, and the SLVR price in **both ETH and USD**.
21
+
22
+ ```bash
23
+ npx ts-node quickstart-read.ts
24
+ npx ts-node quickstart-read.ts 0xYourAddress # also show that address's balance + bets
25
+ ```
26
+
27
+ ### `quickstart-bet.ts` β€” place a bet (wallet)
28
+
29
+ The minimal write path: builds a wallet-backed SDK, checks the round's expected
30
+ value, places one small bet, and shows how to claim after the round resolves. Bets
31
+ are paid in native ETH β€” no token approval needed.
32
+
33
+ ```bash
34
+ PRIVATE_KEY=0xabc... npx ts-node quickstart-bet.ts
35
+ ```
36
+
37
+ > Start with a small stake (the example defaults to 0.002 ETH). You can only claim
38
+ > a round you won and haven't already claimed.
39
+
40
+ ## Strategy System
41
+
42
+ The SDK includes a flexible strategy system that makes it easy to create custom betting strategies. All strategies extend the `BettingStrategy` base class, which handles:
43
+ - Round monitoring
44
+ - Threshold checking
45
+ - Error handling
46
+ - Bet execution
47
+
48
+ ### Creating Custom Strategies
49
+
50
+ Extend `BettingStrategy` and override these methods:
51
+ - `selectSquares()` - Choose which squares to bet on
52
+ - `calculateAmounts()` - Calculate bet amounts for each square
53
+ - `shouldBet()` - Optional: Add custom conditions for betting
54
+ - `onBetPlaced()` - Optional: Handle successful bets
55
+ - `onError()` - Optional: Custom error handling
56
+
57
+ ### Example: Fixed Squares Strategy
58
+
59
+ The simplest strategy - bet on the same squares every round:
60
+
61
+ ```typescript
62
+ import { FixedSquaresStrategy } from './fixed-squares-strategy';
63
+ import { parseEther } from 'viem';
64
+
65
+ const strategy = new FixedSquaresStrategy(sdk, {
66
+ squares: [0, 5, 10, 15, 20], // Bet on these squares
67
+ amountPerSquare: parseEther('1'), // 1 ETH per square
68
+ threshold: parseEther('100'), // Only bet when round < 100 ETH
69
+ checkInterval: 5000, // Check every 5 seconds
70
+ });
71
+
72
+ await strategy.start();
73
+ ```
74
+
75
+ ### Example: Custom Strategy
76
+
77
+ ```typescript
78
+ import { BettingStrategy } from './strategy-base';
79
+
80
+ class MyCustomStrategy extends BettingStrategy {
81
+ protected async selectSquares(roundId: bigint): Promise<number[]> {
82
+ // Your logic to select squares
83
+ return [0, 1, 2, 3, 4];
84
+ }
85
+
86
+ protected async calculateAmounts(
87
+ roundId: bigint,
88
+ round: any,
89
+ squares: number[]
90
+ ): Promise<bigint[]> {
91
+ // Your logic to calculate amounts
92
+ return squares.map(() => parseEther('1'));
93
+ }
94
+ }
95
+
96
+ const strategy = new MyCustomStrategy(sdk, {
97
+ threshold: parseEther('100'),
98
+ checkInterval: 5000,
99
+ });
100
+
101
+ await strategy.start();
102
+ ```
103
+
104
+ ## Examples
105
+
106
+ ### 1. Fixed Squares Strategy (`fixed-squares-strategy.ts`)
107
+
108
+ Bet on a fixed set of squares every round when threshold is met.
109
+
110
+ **Usage:**
111
+ ```typescript
112
+ import { FixedSquaresStrategy } from './fixed-squares-strategy';
113
+ import { parseEther } from 'viem';
114
+
115
+ const strategy = new FixedSquaresStrategy(sdk, {
116
+ squares: [0, 5, 10, 15, 20],
117
+ amountPerSquare: parseEther('1'),
118
+ threshold: parseEther('100'),
119
+ checkInterval: 5000,
120
+ });
121
+
122
+ await strategy.start();
123
+ ```
124
+
125
+ ### 3. Automated Betting Bot (`automated-betting.ts`)
126
+
127
+ A bot that automatically monitors rounds and places bets when the total wager is below a threshold (e.g., 100 ETH). It distributes bets across squares with the least allocation.
128
+
129
+ **Features:**
130
+ - Monitors rounds at configurable intervals
131
+ - Automatically bets when total wager < threshold
132
+ - Targets squares with least allocation
133
+ - Configurable bet amounts and square counts
134
+
135
+ **Usage:**
136
+ ```typescript
137
+ import { AutomatedBettingBot } from './automated-betting';
138
+ import { parseEther } from 'viem';
139
+
140
+ const bot = new AutomatedBettingBot(sdk, {
141
+ threshold: parseEther('100'), // Bet when round has < 100 ETH
142
+ betAmount: parseEther('1'), // 1 ETH per square
143
+ minSquares: 3,
144
+ maxSquares: 5,
145
+ checkInterval: 5000, // Check every 5 seconds
146
+ });
147
+
148
+ await bot.start();
149
+ // ... later
150
+ bot.stop();
151
+ ```
152
+
153
+ ### 2. Least Allocated Strategy (`least-allocated-strategy.ts`)
154
+
155
+ A strategy class that helps identify and bet on squares with the least funds allocated.
156
+
157
+ **Features:**
158
+ - Get squares sorted by allocation
159
+ - Calculate optimal bet distribution
160
+ - Analyze round allocation patterns
161
+ - Place bets on least allocated squares
162
+
163
+ **Usage:**
164
+ ```typescript
165
+ import { LeastAllocatedStrategy } from './least-allocated-strategy';
166
+ import { parseEther } from 'viem';
167
+
168
+ const strategy = new LeastAllocatedStrategy(sdk);
169
+ const roundId = await sdk.lottery.currentRoundId();
170
+
171
+ // Analyze the round
172
+ const analysis = await strategy.analyzeRound(roundId);
173
+ console.log('Least allocated:', analysis.leastAllocated);
174
+
175
+ // Place a bet
176
+ const txHash = await strategy.betOnLeastAllocated(
177
+ roundId,
178
+ parseEther('5'), // 5 ETH total
179
+ 5 // Bet on 5 squares
180
+ );
181
+ ```
182
+
183
+ ### 4. Combined Strategy (`combined-strategy.ts`)
184
+
185
+ Combines both strategies: monitors for low total wager AND bets on least allocated squares.
186
+
187
+ **Features:**
188
+ - Monitors rounds for total wager < threshold
189
+ - Automatically identifies least allocated squares
190
+ - Provides detailed logging and analysis
191
+ - Configurable parameters
192
+
193
+ **Usage:**
194
+ ```typescript
195
+ import { CombinedBettingBot } from './combined-strategy';
196
+ import { parseEther } from 'viem';
197
+
198
+ const bot = new CombinedBettingBot(sdk, {
199
+ threshold: parseEther('100'), // Bet when < 100 ETH
200
+ betAmount: parseEther('5'), // 5 ETH total bet
201
+ squareCount: 5, // Bet on 5 least allocated squares
202
+ checkInterval: 5000, // Check every 5 seconds
203
+ });
204
+
205
+ await bot.start();
206
+ // ... later
207
+ bot.stop();
208
+ ```
209
+
210
+ ### 6. Expected-Value Bot (`expected-value-strategy.ts`)
211
+
212
+ The most "quant" example: it **only bets when the round is positive expected
213
+ value**. It uses the SDK's built-in grid-mining calculator (`sdk.estimateRoundEv`
214
+ / `computeGridMiningEv`) and SLVR price reader (`sdk.price`), so its math matches
215
+ the protocol's own edge calculator.
216
+
217
+ **How it decides:** the winning square is drawn uniformly, so mining SLVR only
218
+ pays while the pot is small enough that your SLVR reward beats the ETH you bleed
219
+ to the protocol fee. The bot computes net EV per round and bets when it clears a
220
+ threshold β€” otherwise it waits for the pot to shrink or SLVR to rise.
221
+
222
+ **Features:**
223
+ - Live pot / emission / SLVR price pulled on-chain each round
224
+ - Logs the full EV breakdown (share, SLVR mined, ETH bleed, jackpot, net, break-even pot)
225
+ - Bets only on +EV rounds, covering the grid proportionally so it holds the winner
226
+ - Hold vs cash-out mode (accounts for the 10% refining fee when cashing SLVR to ETH)
227
+
228
+ **Usage:**
229
+ ```typescript
230
+ import { ExpectedValueBot } from './expected-value-strategy';
231
+
232
+ const bot = new ExpectedValueBot(sdk, {
233
+ stakeEth: 0.1, // commit 0.1 ETH per +EV round
234
+ minNetEth: 0, // bet on any positive-EV round (raise to demand more edge)
235
+ cashOut: false, // value mined SLVR at full price (holding/staking it)
236
+ // ethUsd: 1815, // optional override; otherwise the Chainlink feed is used
237
+ checkInterval: 5000,
238
+ });
239
+
240
+ await bot.start();
241
+ // ... later
242
+ bot.stop();
243
+ ```
244
+
245
+ The bot logs the SLVR price in **both ETH and USD**. USD comes from the SDK's
246
+ Chainlink ETH/USD feed (`sdk.ethUsd`), which is wired for Robinhood Chain, so USD
247
+ shows automatically. Pass `ethUsd` (or run with `ETH_USD=1815 …`) to override it,
248
+ or on a chain without a feed.
249
+
250
+ > Requires a `slvrEthPair` address so `sdk.price` works β€” it's included in
251
+ > `deployments.robinhood.addresses`, which this example uses.
252
+
253
+ ## Running Examples
254
+
255
+ ### 1. Install dependencies
256
+
257
+ From the `sdk/ts` directory:
258
+
259
+ ```bash
260
+ npm install
261
+ ```
262
+
263
+ ### 2. Run an example
264
+
265
+ The examples are TypeScript. The simplest way to run one is with `ts-node` via
266
+ `npx` (it's fetched on demand β€” no global install needed):
267
+
268
+ ```bash
269
+ # Read-only β€” needs nothing but an internet connection:
270
+ npx ts-node examples/quickstart-read.ts
271
+
272
+ # Pass an address to also see its balance/bets:
273
+ npx ts-node examples/quickstart-read.ts 0xYourAddress
274
+ ```
275
+
276
+ > Prefer to compile? `npx tsc` then `node dist-examples/...` also works. `ts-node`
277
+ > just skips the build step.
278
+
279
+ ### 3. Add a private key (only for examples that bet)
280
+
281
+ Read-only examples (like `quickstart-read.ts`) need **no key**. Examples that send
282
+ transactions read your key from the `PRIVATE_KEY` environment variable. The
283
+ simplest, most reliable way is to set it inline for the one command:
284
+
285
+ ```bash
286
+ PRIVATE_KEY=0xabc123... npx ts-node examples/quickstart-bet.ts
287
+ ```
288
+
289
+ Or keep it in a file: copy `.env.example` to `.env`, put your key in it, then load
290
+ it into your shell before running (the examples don't auto-load `.env`):
291
+
292
+ ```bash
293
+ cp .env.example .env # then edit .env and paste your key
294
+ set -a; source .env; set +a # load it into the environment
295
+ npx ts-node examples/quickstart-bet.ts
296
+ ```
297
+
298
+ **Key safety β€” please read:**
299
+ - Use a **fresh / burner** key, not one holding real value.
300
+ - The account needs a **small amount of ETH on Robinhood Chain** β€” for gas, plus
301
+ whatever you bet.
302
+ - **Never commit your key.** `.env` is git-ignored here; only `.env.example`
303
+ (which has a dummy key) is tracked. Setting the key inline avoids a file entirely.
304
+
305
+ ### 4. (Optional) Point at a custom deployment
306
+
307
+ `examples/constants.ts` defaults to the canonical Robinhood Chain mainnet addresses
308
+ that ship with the SDK (`deployments.robinhood`). Edit it only to target a local or
309
+ custom deployment.
310
+
311
+ ### Environment Variables
312
+
313
+ - `PRIVATE_KEY` β€” your wallet key, hex with `0x` prefix. Required only for examples
314
+ that send transactions; read-only examples ignore it.
315
+ - `ETH_USD` β€” optional ETH/USD override for USD display (otherwise the on-chain
316
+ Chainlink feed is used).
317
+
318
+ ### Basic Setup
319
+
320
+ All examples use the constants file and environment variables:
321
+
322
+ ```typescript
323
+ import { createPublicClient, createWalletClient, http } from 'viem';
324
+ import { privateKeyToAccount } from 'viem/accounts';
325
+ import { SlvrSDK } from '@slvr-labs/sdk';
326
+ import { CONTRACTS, ROBINHOOD_CHAIN } from './constants';
327
+
328
+ // ROBINHOOD_CHAIN is already a viem `Chain` β€” no need to wrap it.
329
+ const publicClient = createPublicClient({
330
+ chain: ROBINHOOD_CHAIN,
331
+ transport: http(),
332
+ });
333
+
334
+ // Get private key from environment
335
+ const privateKey = process.env.PRIVATE_KEY;
336
+ if (!privateKey) {
337
+ throw new Error('PRIVATE_KEY environment variable is required');
338
+ }
339
+
340
+ const account = privateKeyToAccount(privateKey as `0x${string}`);
341
+ const walletClient = createWalletClient({
342
+ chain: ROBINHOOD_CHAIN,
343
+ transport: http(),
344
+ account,
345
+ });
346
+
347
+ // Initialize SDK with contract addresses from constants
348
+ const sdk = new SlvrSDK({
349
+ publicClient,
350
+ walletClient,
351
+ addresses: {
352
+ lottery: CONTRACTS.LOTTERY,
353
+ staking: CONTRACTS.STAKING,
354
+ token: CONTRACTS.TOKEN,
355
+ autoCommit: CONTRACTS.AUTO_COMMIT !== '0x...' ? CONTRACTS.AUTO_COMMIT : undefined,
356
+ },
357
+ });
358
+ ```
359
+
360
+ ### 5. Custom Strategy Examples (`custom-strategy-example.ts`)
361
+
362
+ Several example custom strategies showing different approaches:
363
+ - `LeastAllocatedCustomStrategy` - Bet on squares with least allocation
364
+ - `RandomSquaresStrategy` - Bet on random squares
365
+ - `MostBettorsStrategy` - Bet on squares with most bettors (social proof)
366
+ - `WeightedAllocationStrategy` - Weight bets inversely by allocation
367
+ - `ConditionalStrategy` - Only bet when certain conditions are met
368
+
369
+ ## Strategy Ideas
370
+
371
+ You can easily create your own strategies by extending `BettingStrategy`:
372
+
373
+ ### 1. Equal Distribution Strategy
374
+ Bet equal amounts on all 25 squares to maximize coverage.
375
+
376
+ ### 2. Historical Analysis
377
+ Analyze past rounds to identify patterns and bet accordingly.
378
+
379
+ ### 3. Multi-Round Strategy
380
+ Spread bets across multiple rounds to diversify risk.
381
+
382
+ ### 4. Time-Based Strategy
383
+ Adjust betting based on time remaining in the round.
384
+
385
+ ### 5. Portfolio Strategy
386
+ Diversify across multiple strategies simultaneously.
387
+
388
+ ## Safety Considerations
389
+
390
+ ⚠️ **Important:** These examples are for educational purposes. When running automated bots:
391
+
392
+ 1. **Test on testnet first** - Never run untested code on mainnet
393
+ 2. **Set reasonable limits** - Don't bet more than you can afford to lose
394
+ 3. **Monitor gas costs** - Frequent transactions can be expensive
395
+ 4. **Handle errors gracefully** - Network issues can cause failures
396
+ 5. **Respect rate limits** - Don't spam the network
397
+ 6. **Secure your keys** - Never expose private keys in code
398
+
399
+ ## Customization
400
+
401
+ All examples are designed to be easily customizable. You can:
402
+
403
+ - Adjust thresholds and amounts
404
+ - Modify square selection logic
405
+ - Add additional conditions
406
+ - Integrate with other services (notifications, analytics, etc.)
407
+ - Combine multiple strategies
408
+
409
+ ## Contributing
410
+
411
+ Feel free to create your own examples and share them! Common patterns include:
412
+ - Integration with notification services
413
+ - Database logging
414
+ - Analytics and reporting
415
+ - Multi-account management
416
+ - Advanced risk management
417
+
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Example: Automated betting strategy
3
+ *
4
+ * This example demonstrates how to:
5
+ * 1. Monitor rounds and automatically bet when total wager is less than 100 ETH
6
+ * 2. Bet on squares with the least funds allocated
7
+ */
8
+
9
+ import { SlvrSDK } from '../src';
10
+ import { PublicClient, WalletClient, parseEther } from 'viem';
11
+
12
+ /**
13
+ * Automated betting bot that bets when round has less than threshold ETH
14
+ * and distributes bets across squares with least allocation
15
+ */
16
+ export class AutomatedBettingBot {
17
+ private sdk: SlvrSDK;
18
+ private threshold: bigint; // Minimum total wager threshold (e.g., 100 ETH)
19
+ private betAmount: bigint; // Amount to bet per square
20
+ private minSquares: number; // Minimum number of squares to bet on
21
+ private maxSquares: number; // Maximum number of squares to bet on
22
+ private checkInterval: number; // How often to check (in milliseconds)
23
+ private isRunning: boolean = false;
24
+ private intervalId?: NodeJS.Timeout;
25
+
26
+ constructor(
27
+ sdk: SlvrSDK,
28
+ options: {
29
+ threshold?: bigint; // Default: 100 ETH
30
+ betAmount?: bigint; // Default: 1 ETH per square
31
+ minSquares?: number; // Default: 3
32
+ maxSquares?: number; // Default: 5
33
+ checkInterval?: number; // Default: 5000ms (5 seconds)
34
+ } = {}
35
+ ) {
36
+ this.sdk = sdk;
37
+ this.threshold = options.threshold ?? parseEther('100');
38
+ this.betAmount = options.betAmount ?? parseEther('1');
39
+ this.minSquares = options.minSquares ?? 3;
40
+ this.maxSquares = options.maxSquares ?? 5;
41
+ this.checkInterval = options.checkInterval ?? 5000;
42
+ }
43
+
44
+ /**
45
+ * Start the automated betting bot
46
+ */
47
+ async start(): Promise<void> {
48
+ if (this.isRunning) {
49
+ console.log('Bot is already running');
50
+ return;
51
+ }
52
+
53
+ if (!this.sdk.getWalletClient()) {
54
+ throw new Error('Wallet client required for automated betting');
55
+ }
56
+
57
+ this.isRunning = true;
58
+ console.log('πŸ€– Automated betting bot started');
59
+ console.log(`Threshold: ${SlvrSDK.formatToken(this.threshold)} ETH`);
60
+ console.log(`Bet amount per square: ${SlvrSDK.formatToken(this.betAmount)} ETH`);
61
+ console.log(`Checking every ${this.checkInterval / 1000} seconds`);
62
+
63
+ // Check immediately
64
+ await this.checkAndBet();
65
+
66
+ // Then check at intervals
67
+ this.intervalId = setInterval(async () => {
68
+ await this.checkAndBet();
69
+ }, this.checkInterval);
70
+ }
71
+
72
+ /**
73
+ * Stop the automated betting bot
74
+ */
75
+ stop(): void {
76
+ if (!this.isRunning) {
77
+ return;
78
+ }
79
+
80
+ this.isRunning = false;
81
+ if (this.intervalId) {
82
+ clearInterval(this.intervalId);
83
+ this.intervalId = undefined;
84
+ }
85
+ console.log('πŸ›‘ Automated betting bot stopped');
86
+ }
87
+
88
+ /**
89
+ * Check current round and place bet if conditions are met
90
+ */
91
+ private async checkAndBet(): Promise<void> {
92
+ try {
93
+ const roundId = await this.sdk.lottery.currentRoundId();
94
+ const round = await this.sdk.lottery.getRound(roundId);
95
+ const isOpen = await this.sdk.lottery.roundOpen(roundId);
96
+
97
+ if (!isOpen) {
98
+ console.log(`⏸️ Round ${roundId} is not open for betting`);
99
+ return;
100
+ }
101
+
102
+ // Check if total wager is below threshold
103
+ if (round.totalWager >= this.threshold) {
104
+ console.log(
105
+ `πŸ’° Round ${roundId} has ${SlvrSDK.formatToken(round.totalWager)} ETH ` +
106
+ `(above threshold of ${SlvrSDK.formatToken(this.threshold)} ETH)`
107
+ );
108
+ return;
109
+ }
110
+
111
+ console.log(
112
+ `🎯 Round ${roundId} has ${SlvrSDK.formatToken(round.totalWager)} ETH ` +
113
+ `(below threshold of ${SlvrSDK.formatToken(this.threshold)} ETH)`
114
+ );
115
+
116
+ // Get squares with least allocation
117
+ const squares = await this.getLeastAllocatedSquares();
118
+
119
+ if (squares.length < this.minSquares) {
120
+ console.log(`⚠️ Not enough squares with low allocation (found ${squares.length}, need ${this.minSquares})`);
121
+ return;
122
+ }
123
+
124
+ // Limit to maxSquares
125
+ const squaresToBet = squares.slice(0, this.maxSquares);
126
+ const amounts = squaresToBet.map(() => this.betAmount);
127
+
128
+ console.log(`πŸ“Š Betting on squares: ${squaresToBet.join(', ')}`);
129
+ console.log(`πŸ’΅ Total bet: ${SlvrSDK.formatToken(amounts.reduce((sum, amt) => sum + amt, 0n))} ETH`);
130
+
131
+ // Place the bet
132
+ const txHash = await this.sdk.lottery.bet({
133
+ roundId,
134
+ squares: squaresToBet,
135
+ amounts,
136
+ });
137
+
138
+ console.log(`βœ… Bet placed! Transaction: ${txHash}`);
139
+ } catch (error) {
140
+ console.error('❌ Error in automated betting:', error);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Get squares with the least funds allocated, sorted by total amount
146
+ */
147
+ private async getLeastAllocatedSquares(): Promise<number[]> {
148
+ const roundId = await this.sdk.lottery.currentRoundId();
149
+ const squares = await this.sdk.lottery.getRoundSquares(roundId);
150
+
151
+ // Sort by total amount (ascending) - least allocated first
152
+ const sorted = squares
153
+ .map(({ square, total }) => ({ square, total }))
154
+ .sort((a, b) => {
155
+ if (a.total < b.total) return -1;
156
+ if (a.total > b.total) return 1;
157
+ return 0;
158
+ });
159
+
160
+ return sorted.map(({ square }) => square);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Example usage
166
+ *
167
+ * Set PRIVATE_KEY environment variable to enable betting
168
+ */
169
+ export async function exampleAutomatedBetting() {
170
+ const { createPublicClient, createWalletClient, http } = await import('viem');
171
+ const { defineChain } = await import('viem');
172
+ const { privateKeyToAccount } = await import('viem/accounts');
173
+ const { SlvrSDK } = await import('../src');
174
+ const { CONTRACTS, ROBINHOOD_CHAIN } = await import('./constants');
175
+
176
+ // Create clients
177
+ const robinhood = defineChain(ROBINHOOD_CHAIN);
178
+ const publicClient = createPublicClient({
179
+ chain: robinhood,
180
+ transport: http(),
181
+ });
182
+
183
+ const privateKey = process.env.PRIVATE_KEY;
184
+ if (!privateKey) {
185
+ throw new Error('PRIVATE_KEY environment variable is required');
186
+ }
187
+
188
+ const account = privateKeyToAccount(privateKey as `0x${string}`);
189
+ const walletClient = createWalletClient({
190
+ chain: robinhood,
191
+ transport: http(),
192
+ account,
193
+ });
194
+
195
+ // Initialize SDK
196
+ const sdk = new SlvrSDK({
197
+ publicClient,
198
+ walletClient,
199
+ addresses: {
200
+ lottery: CONTRACTS.LOTTERY,
201
+ staking: CONTRACTS.STAKING,
202
+ token: CONTRACTS.TOKEN,
203
+ autoCommit: CONTRACTS.AUTO_COMMIT !== '0x...' ? CONTRACTS.AUTO_COMMIT : undefined,
204
+ },
205
+ });
206
+
207
+ // Create bot with custom options
208
+ const bot = new AutomatedBettingBot(sdk, {
209
+ threshold: parseEther('100'), // Bet when round has less than 100 ETH
210
+ betAmount: parseEther('1'), // Bet 1 ETH per square
211
+ minSquares: 3, // Bet on at least 3 squares
212
+ maxSquares: 5, // Bet on at most 5 squares
213
+ checkInterval: 5000, // Check every 5 seconds
214
+ });
215
+
216
+ // Start the bot
217
+ await bot.start();
218
+
219
+ // Handle graceful shutdown
220
+ process.on('SIGINT', () => {
221
+ console.log('\nReceived SIGINT, stopping bot...');
222
+ bot.stop();
223
+ process.exit(0);
224
+ });
225
+
226
+ process.on('SIGTERM', () => {
227
+ console.log('\nReceived SIGTERM, stopping bot...');
228
+ bot.stop();
229
+ process.exit(0);
230
+ });
231
+ }
232
+