@slvr-labs/sdk 0.1.0 → 0.1.1

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 (83) hide show
  1. package/README.md +42 -0
  2. package/dist/index.d.mts +2117 -0
  3. package/dist/index.d.ts +1903 -34
  4. package/dist/index.js +3091 -427
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +3035 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +17 -10
  9. package/skills/slvr-bot/SKILL.md +9 -8
  10. package/skills/slvr-bot/references/api.md +8 -0
  11. package/dist/connect.d.ts +0 -47
  12. package/dist/connect.d.ts.map +0 -1
  13. package/dist/connect.js +0 -56
  14. package/dist/connect.js.map +0 -1
  15. package/dist/contracts/autoCommit.d.ts +0 -151
  16. package/dist/contracts/autoCommit.d.ts.map +0 -1
  17. package/dist/contracts/autoCommit.js +0 -426
  18. package/dist/contracts/autoCommit.js.map +0 -1
  19. package/dist/contracts/hub.d.ts +0 -50
  20. package/dist/contracts/hub.d.ts.map +0 -1
  21. package/dist/contracts/hub.js +0 -118
  22. package/dist/contracts/hub.js.map +0 -1
  23. package/dist/contracts/index.d.ts +0 -8
  24. package/dist/contracts/index.d.ts.map +0 -1
  25. package/dist/contracts/index.js +0 -18
  26. package/dist/contracts/index.js.map +0 -1
  27. package/dist/contracts/jackpot.d.ts +0 -21
  28. package/dist/contracts/jackpot.d.ts.map +0 -1
  29. package/dist/contracts/jackpot.js +0 -44
  30. package/dist/contracts/jackpot.js.map +0 -1
  31. package/dist/contracts/lottery.d.ts +0 -256
  32. package/dist/contracts/lottery.d.ts.map +0 -1
  33. package/dist/contracts/lottery.js +0 -767
  34. package/dist/contracts/lottery.js.map +0 -1
  35. package/dist/contracts/registry.d.ts +0 -53
  36. package/dist/contracts/registry.d.ts.map +0 -1
  37. package/dist/contracts/registry.js +0 -143
  38. package/dist/contracts/registry.js.map +0 -1
  39. package/dist/contracts/staking.d.ts +0 -101
  40. package/dist/contracts/staking.d.ts.map +0 -1
  41. package/dist/contracts/staking.js +0 -306
  42. package/dist/contracts/staking.js.map +0 -1
  43. package/dist/contracts/token.d.ts +0 -95
  44. package/dist/contracts/token.d.ts.map +0 -1
  45. package/dist/contracts/token.js +0 -273
  46. package/dist/contracts/token.js.map +0 -1
  47. package/dist/deployments.d.ts +0 -117
  48. package/dist/deployments.d.ts.map +0 -1
  49. package/dist/deployments.js +0 -74
  50. package/dist/deployments.js.map +0 -1
  51. package/dist/errors.d.ts +0 -39
  52. package/dist/errors.d.ts.map +0 -1
  53. package/dist/errors.js +0 -67
  54. package/dist/errors.js.map +0 -1
  55. package/dist/ev.d.ts +0 -123
  56. package/dist/ev.d.ts.map +0 -1
  57. package/dist/ev.js +0 -111
  58. package/dist/ev.js.map +0 -1
  59. package/dist/events.d.ts +0 -418
  60. package/dist/events.d.ts.map +0 -1
  61. package/dist/events.js +0 -87
  62. package/dist/events.js.map +0 -1
  63. package/dist/index.d.ts.map +0 -1
  64. package/dist/oracle.d.ts +0 -48
  65. package/dist/oracle.d.ts.map +0 -1
  66. package/dist/oracle.js +0 -77
  67. package/dist/oracle.js.map +0 -1
  68. package/dist/price.d.ts +0 -52
  69. package/dist/price.d.ts.map +0 -1
  70. package/dist/price.js +0 -78
  71. package/dist/price.js.map +0 -1
  72. package/dist/transaction.d.ts +0 -54
  73. package/dist/transaction.d.ts.map +0 -1
  74. package/dist/transaction.js +0 -105
  75. package/dist/transaction.js.map +0 -1
  76. package/dist/types.d.ts +0 -162
  77. package/dist/types.d.ts.map +0 -1
  78. package/dist/types.js +0 -23
  79. package/dist/types.js.map +0 -1
  80. package/dist/utils.d.ts +0 -58
  81. package/dist/utils.d.ts.map +0 -1
  82. package/dist/utils.js +0 -110
  83. package/dist/utils.js.map +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,3035 @@
1
+ // src/index.ts
2
+ import { formatEther } from "viem";
3
+
4
+ // src/contracts/lottery.ts
5
+ import { parseAbi as parseAbi2 } from "viem";
6
+
7
+ // src/errors.ts
8
+ import { BaseError, ContractFunctionRevertedError } from "viem";
9
+ var SlvrSDKError = class _SlvrSDKError extends Error {
10
+ constructor(message, code) {
11
+ super(message);
12
+ this.code = code;
13
+ this.name = "SlvrSDKError";
14
+ Object.setPrototypeOf(this, _SlvrSDKError.prototype);
15
+ }
16
+ };
17
+ var WalletClientRequiredError = class _WalletClientRequiredError extends SlvrSDKError {
18
+ constructor(operation) {
19
+ super(`Wallet client required for ${operation}`, "WALLET_CLIENT_REQUIRED");
20
+ this.name = "WalletClientRequiredError";
21
+ Object.setPrototypeOf(this, _WalletClientRequiredError.prototype);
22
+ }
23
+ };
24
+ var ContractCallError = class _ContractCallError extends SlvrSDKError {
25
+ constructor(message, cause) {
26
+ super(message, "CONTRACT_CALL_ERROR");
27
+ this.cause = cause;
28
+ this.name = "ContractCallError";
29
+ Object.setPrototypeOf(this, _ContractCallError.prototype);
30
+ }
31
+ };
32
+ var ValidationError = class _ValidationError extends SlvrSDKError {
33
+ constructor(message, field) {
34
+ super(message, "VALIDATION_ERROR");
35
+ this.field = field;
36
+ this.name = "ValidationError";
37
+ Object.setPrototypeOf(this, _ValidationError.prototype);
38
+ }
39
+ };
40
+ var TransactionError = class _TransactionError extends SlvrSDKError {
41
+ constructor(message, txHash, cause) {
42
+ super(message, "TRANSACTION_ERROR");
43
+ this.txHash = txHash;
44
+ this.cause = cause;
45
+ this.name = "TransactionError";
46
+ Object.setPrototypeOf(this, _TransactionError.prototype);
47
+ }
48
+ };
49
+ var REVERT_HINTS = {
50
+ InsufficientValue: "msg.value did not cover the bet total (+ the one-time account-opening deposit for a new account).",
51
+ RoundNotOpen: "betting is closed for this round.",
52
+ NotResolved: "the round is not resolved yet.",
53
+ ResolveRequested: "the round is already being resolved; betting is closed.",
54
+ BadClaim: "nothing to claim \u2014 no winning bet on this round, or already claimed.",
55
+ NoAccount: "no miner account for this address yet (place a bet first to open one).",
56
+ BadAmount: "a zero or invalid amount.",
57
+ BadSquare: "a square index outside 0\u201324.",
58
+ DupSquare: "the same square appears twice in one bet.",
59
+ BadArrays: "squares and amounts arrays have mismatched lengths.",
60
+ ValueNotSum: "msg.value does not equal the sum of the bet amounts.",
61
+ MustSumTo100Percent: "allocation percentages must sum to 100%.",
62
+ NotAuthorized: "caller is not authorized (not the user or an approved delegate).",
63
+ NotCurrentRound: "that round is not the current one.",
64
+ CannotDelegateToSelf: "you cannot delegate to your own address.",
65
+ RandomnessNotSettled: "randomness for this round has not settled yet.",
66
+ TransferFailed: "an ETH/token transfer failed.",
67
+ JackpotNotSet: "no jackpot is configured for this round."
68
+ };
69
+ var SlvrRevertError = class _SlvrRevertError extends SlvrSDKError {
70
+ constructor(errorName, message, args, cause) {
71
+ super(message, "CONTRACT_REVERT");
72
+ this.errorName = errorName;
73
+ this.args = args;
74
+ this.cause = cause;
75
+ this.name = "SlvrRevertError";
76
+ Object.setPrototypeOf(this, _SlvrRevertError.prototype);
77
+ }
78
+ };
79
+ function decodeSlvrRevert(err) {
80
+ if (!(err instanceof BaseError)) return null;
81
+ const revert = err.walk((e) => e instanceof ContractFunctionRevertedError);
82
+ if (!revert) return null;
83
+ const data = revert.data;
84
+ const name = data?.errorName;
85
+ const hint = name ? REVERT_HINTS[name] : void 0;
86
+ const message = name ? `Reverted: ${name}${hint ? ` \u2014 ${hint}` : ""}` : revert.shortMessage || err.shortMessage || "contract reverted";
87
+ return new SlvrRevertError(name, message, data?.args, err);
88
+ }
89
+
90
+ // src/utils.ts
91
+ import { isAddress, getAddress } from "viem";
92
+ function validateAddress(address, fieldName = "address") {
93
+ if (!isAddress(address)) {
94
+ throw new ValidationError(`Invalid ${fieldName}: ${address}`, fieldName);
95
+ }
96
+ return getAddress(address);
97
+ }
98
+ function validateAmount(amount, fieldName = "amount") {
99
+ if (amount < 0n) {
100
+ throw new ValidationError(`${fieldName} must be non-negative`, fieldName);
101
+ }
102
+ if (amount === 0n) {
103
+ throw new ValidationError(`${fieldName} must be greater than zero`, fieldName);
104
+ }
105
+ }
106
+ function validateSquares(squares, gridSize = 25) {
107
+ if (squares.length === 0) {
108
+ throw new ValidationError("At least one square must be provided", "squares");
109
+ }
110
+ for (const square of squares) {
111
+ if (!Number.isInteger(square) || square < 0 || square >= gridSize) {
112
+ throw new ValidationError(
113
+ `Invalid square index: ${square}. Must be between 0 and ${gridSize - 1}`,
114
+ "squares"
115
+ );
116
+ }
117
+ }
118
+ }
119
+ function validateArrayLengths(arrays, fieldNames) {
120
+ if (arrays.length < 2) {
121
+ return;
122
+ }
123
+ const firstLength = arrays[0]?.length ?? 0;
124
+ for (let i = 1; i < arrays.length; i++) {
125
+ const currentArray = arrays[i];
126
+ const currentFieldName = fieldNames[i];
127
+ if (!currentArray || currentArray.length !== firstLength) {
128
+ throw new ValidationError(
129
+ `Array length mismatch: ${fieldNames[0]} has ${firstLength} items, but ${currentFieldName ?? `array ${i}`} has ${currentArray?.length ?? 0} items`,
130
+ currentFieldName
131
+ );
132
+ }
133
+ }
134
+ }
135
+ function validateBps(bps, fieldName = "bps") {
136
+ if (!Number.isInteger(bps) || bps < 0 || bps > 1e4) {
137
+ throw new ValidationError(
138
+ `${fieldName} must be an integer between 0 and 10000 (got ${bps})`,
139
+ fieldName
140
+ );
141
+ }
142
+ }
143
+ function validateBpsSum(bpsArray, fieldName = "bpsAlloc") {
144
+ const sum = bpsArray.reduce((acc, bps) => acc + bps, 0);
145
+ if (sum !== 1e4) {
146
+ throw new ValidationError(
147
+ `${fieldName} must sum to 10000 (got ${sum})`,
148
+ fieldName
149
+ );
150
+ }
151
+ }
152
+ async function waitForTransactionReceipt(publicClient, hash, timeout = 12e4) {
153
+ if (!publicClient.waitForTransactionReceipt) {
154
+ throw new Error("Public client does not support waitForTransactionReceipt");
155
+ }
156
+ return await publicClient.waitForTransactionReceipt({ hash, timeout });
157
+ }
158
+
159
+ // src/events.ts
160
+ import { parseAbi, decodeEventLog } from "viem";
161
+ var SlvrGridLotteryEvents = parseAbi([
162
+ "event BetPlaced(uint256 indexed roundId, address indexed beneficiary, uint256 total, uint8[] squares)",
163
+ "event Claimed(uint256 indexed roundId, address indexed user, uint256 nativeOut, uint256 slvrOut, uint256 refinedOut, uint256 refiningFee)",
164
+ "event RoundResolved(uint256 indexed roundId, uint8 winningSquare, bool jackpotHit, bool singleMinerRound, address indexed singleMinerWinner, uint256 winnerTotal, uint256 potForWinners, uint256 slvrForWinners, uint256 totalUnclaimedSlvr)",
165
+ "event RandomnessRequested(uint256 indexed roundId, bytes32 randomnessId)"
166
+ ]);
167
+ var SlvrStakingEvents = parseAbi([
168
+ "event Staked(uint256 indexed tokenId, address indexed user, uint256 weight)",
169
+ "event Unstaked(uint256 indexed tokenId, address indexed user, uint256 weight)",
170
+ "event RewardClaimed(uint256 indexed tokenId, address indexed user, uint256 amount)",
171
+ "event RewardDistributed(uint256 amount)",
172
+ "event Checkpoint(uint256 indexed tokenId, uint256 oldWeight, uint256 newWeight)",
173
+ "event RewardsSettledOnBurn(uint256 indexed tokenId, address indexed owner, uint256 amount)",
174
+ "event PendingRewardsClaimed(address indexed user, uint256 amount)"
175
+ ]);
176
+ var SlvrTokenEvents = parseAbi([
177
+ "event Transfer(address indexed from, address indexed to, uint256 value)",
178
+ "event Approval(address indexed owner, address indexed spender, uint256 value)"
179
+ ]);
180
+ var SlvrAutoCommitEvents = parseAbi([
181
+ "event PlanConfigured(address indexed user, uint256 nextRoundId, uint32 plays, uint256 amountPerPlay, bool autoClaim)",
182
+ "event PlanDisabled(address indexed user)",
183
+ "event PlanCancelled(address indexed user, uint256 refundAmount)",
184
+ "event Deposited(address indexed user, uint256 amount)",
185
+ "event Withdrawn(address indexed user, uint256 amount, address to)",
186
+ "event RoundExecuted(address indexed user, uint256 indexed roundId, uint32 playsRemaining)",
187
+ "event Claimed(address indexed user, uint256 indexed roundId, uint256 nativeAmount, uint256 addedToBalance)",
188
+ "event BalanceUpdated(address indexed user, uint256 newBalance, uint256 amountAdded)",
189
+ "event ExecutorFeePaid(address indexed user, address indexed executor, uint256 fee, uint256 gasUsed)"
190
+ ]);
191
+ function decodeEvent(abi, log) {
192
+ try {
193
+ const decoded = decodeEventLog({
194
+ abi,
195
+ data: log.data,
196
+ topics: log.topics
197
+ });
198
+ return decoded;
199
+ } catch {
200
+ return null;
201
+ }
202
+ }
203
+ function decodeEvents(abi, logs, eventName) {
204
+ const decoded = [];
205
+ for (const log of logs) {
206
+ try {
207
+ const decodedEvent = decodeEvent(abi, log);
208
+ if (decodedEvent) {
209
+ if (!eventName || decodedEvent.eventName === eventName) {
210
+ decoded.push(decodedEvent);
211
+ }
212
+ }
213
+ } catch {
214
+ }
215
+ }
216
+ return decoded;
217
+ }
218
+
219
+ // src/contracts/lottery.ts
220
+ var _SlvrGridLottery = class _SlvrGridLottery {
221
+ constructor(publicClient, walletClient, address) {
222
+ this.publicClient = publicClient;
223
+ this.walletClient = walletClient;
224
+ this.address = address;
225
+ }
226
+ /**
227
+ * Update the wallet client
228
+ * @param walletClient New wallet client
229
+ */
230
+ setWalletClient(walletClient) {
231
+ this.walletClient = walletClient;
232
+ }
233
+ /**
234
+ * Get the current round ID
235
+ */
236
+ async currentRoundId() {
237
+ return await this.publicClient.readContract({
238
+ address: this.address,
239
+ abi: _SlvrGridLottery.ABI,
240
+ functionName: "currentRoundId"
241
+ });
242
+ }
243
+ /**
244
+ * Get round start time
245
+ */
246
+ async roundStart(roundId) {
247
+ return await this.publicClient.readContract({
248
+ address: this.address,
249
+ abi: _SlvrGridLottery.ABI,
250
+ functionName: "roundStart",
251
+ args: [roundId]
252
+ });
253
+ }
254
+ /**
255
+ * Get round end time
256
+ */
257
+ async roundEnd(roundId) {
258
+ return await this.publicClient.readContract({
259
+ address: this.address,
260
+ abi: _SlvrGridLottery.ABI,
261
+ functionName: "roundEnd",
262
+ args: [roundId]
263
+ });
264
+ }
265
+ /**
266
+ * Check if round is open for betting
267
+ */
268
+ async roundOpen(roundId) {
269
+ return await this.publicClient.readContract({
270
+ address: this.address,
271
+ abi: _SlvrGridLottery.ABI,
272
+ functionName: "roundOpen",
273
+ args: [roundId]
274
+ });
275
+ }
276
+ /**
277
+ * Get the betting cutoff timestamp (unix seconds) for a round.
278
+ *
279
+ * Bets are only accepted until this time, which can be earlier than
280
+ * {@link roundEnd}. Bots deciding whether they can still bet should gate on
281
+ * this rather than on `roundEnd`.
282
+ */
283
+ async bettingEnd(roundId) {
284
+ return await this.publicClient.readContract({
285
+ address: this.address,
286
+ abi: _SlvrGridLottery.ABI,
287
+ functionName: "bettingEnd",
288
+ args: [roundId]
289
+ });
290
+ }
291
+ /**
292
+ * Estimate the reward an account would receive for a round, in wei.
293
+ *
294
+ * Thin pass-through to the contract's `getExpectedReward`. Useful for sizing
295
+ * bets or deciding whether a claim is worth the gas.
296
+ */
297
+ async getExpectedReward(account, roundId) {
298
+ return await this.publicClient.readContract({
299
+ address: this.address,
300
+ abi: _SlvrGridLottery.ABI,
301
+ functionName: "getExpectedReward",
302
+ args: [account, roundId]
303
+ });
304
+ }
305
+ /**
306
+ * Get the latest resolved round ID (type(uint256).max sentinel when none resolved yet)
307
+ */
308
+ async latestResolvedRoundId() {
309
+ return await this.publicClient.readContract({
310
+ address: this.address,
311
+ abi: _SlvrGridLottery.ABI,
312
+ functionName: "latestResolvedRoundId"
313
+ });
314
+ }
315
+ /**
316
+ * Get full round information
317
+ */
318
+ async getRound(roundId) {
319
+ const result = await this.publicClient.readContract({
320
+ address: this.address,
321
+ abi: _SlvrGridLottery.ABI,
322
+ functionName: "getRound",
323
+ args: [roundId]
324
+ });
325
+ return {
326
+ roundId,
327
+ requestedAt: result[0],
328
+ resolved: result[1],
329
+ randomnessId: result[2],
330
+ randomnessValue: result[3],
331
+ winningSquare: Number(result[4]),
332
+ jackpotHit: result[5],
333
+ singleMinerRound: result[6],
334
+ singleMinerWinner: result[7],
335
+ totalWager: result[8],
336
+ fee: result[9],
337
+ winnerTotal: result[10],
338
+ potForWinners: result[11],
339
+ slvrForWinners: result[12],
340
+ payoutMulWad: result[13],
341
+ slvrMulWad: result[14],
342
+ totalUnclaimedSlvr: result[15]
343
+ };
344
+ }
345
+ /**
346
+ * Get a compact, batched snapshot of a round's live state — the fields a bot
347
+ * checks every tick — in one shot. Defaults to the current round.
348
+ *
349
+ * `secondsUntilBettingClose` is computed from the chain's `block.timestamp`
350
+ * (not the local clock), so it's accurate even if your device clock drifts.
351
+ * With a multicall-batching client the reads collapse into ~2 RPC calls.
352
+ */
353
+ async getRoundState(roundId) {
354
+ const id = roundId ?? await this.currentRoundId();
355
+ const [round, open, bettingEnd, roundEnd, block] = await Promise.all([
356
+ this.getRound(id),
357
+ this.roundOpen(id),
358
+ this.bettingEnd(id),
359
+ this.roundEnd(id),
360
+ this.publicClient.getBlock()
361
+ ]);
362
+ const secondsLeft = Number(bettingEnd - block.timestamp);
363
+ return {
364
+ roundId: id,
365
+ open,
366
+ resolved: round.resolved,
367
+ bettingEnd,
368
+ roundEnd,
369
+ totalWager: round.totalWager,
370
+ winningSquare: round.winningSquare,
371
+ secondsUntilBettingClose: secondsLeft > 0 ? secondsLeft : 0
372
+ };
373
+ }
374
+ /**
375
+ * Get the jackpot contract address for a round (zero address if no jackpot is
376
+ * set). Read that contract's `jackpotPool()` for the pool balance — or use the
377
+ * SDK helper `getJackpotPool(roundId)`.
378
+ */
379
+ async getRoundJackpot(roundId) {
380
+ return await this.publicClient.readContract({
381
+ address: this.address,
382
+ abi: _SlvrGridLottery.ABI,
383
+ functionName: "getRoundJackpot",
384
+ args: [roundId]
385
+ });
386
+ }
387
+ /**
388
+ * Get total amount bet on a square for a round
389
+ */
390
+ async getTotalOnSquare(roundId, square) {
391
+ return await this.publicClient.readContract({
392
+ address: this.address,
393
+ abi: _SlvrGridLottery.ABI,
394
+ functionName: "getTotalOnSquare",
395
+ args: [roundId, square]
396
+ });
397
+ }
398
+ /**
399
+ * Get number of bettors on a square for a round
400
+ */
401
+ async getBettorsOnSquare(roundId, square) {
402
+ return await this.publicClient.readContract({
403
+ address: this.address,
404
+ abi: _SlvrGridLottery.ABI,
405
+ functionName: "getBettorsOnSquare",
406
+ args: [roundId, square]
407
+ });
408
+ }
409
+ /**
410
+ * Get a user's bet amount on a square for a round
411
+ */
412
+ async getUserBet(roundId, square, bettor) {
413
+ return await this.publicClient.readContract({
414
+ address: this.address,
415
+ abi: _SlvrGridLottery.ABI,
416
+ functionName: "getUserBet",
417
+ args: [roundId, square, bettor]
418
+ });
419
+ }
420
+ /**
421
+ * Check if a user has claimed rewards for a round
422
+ */
423
+ async getHasClaimed(roundId, user) {
424
+ return await this.publicClient.readContract({
425
+ address: this.address,
426
+ abi: _SlvrGridLottery.ABI,
427
+ functionName: "getHasClaimed",
428
+ args: [roundId, user]
429
+ });
430
+ }
431
+ /**
432
+ * Get miner state for an account
433
+ */
434
+ async getMinerState(account) {
435
+ const result = await this.publicClient.readContract({
436
+ address: this.address,
437
+ abi: _SlvrGridLottery.ABI,
438
+ functionName: "getMinerState",
439
+ args: [account]
440
+ });
441
+ return {
442
+ rewardsSlvr: result[0],
443
+ refinedAccrued: result[1],
444
+ indexSnapshot: result[2],
445
+ hasAccount: result[3]
446
+ };
447
+ }
448
+ /**
449
+ * Check if an account exists
450
+ */
451
+ async hasAccount(account) {
452
+ return await this.publicClient.readContract({
453
+ address: this.address,
454
+ abi: _SlvrGridLottery.ABI,
455
+ functionName: "getHasAccount",
456
+ args: [account]
457
+ });
458
+ }
459
+ /**
460
+ * Get carry-over winner native pool (when no winners)
461
+ */
462
+ async carryWinnerNativePool() {
463
+ return await this.publicClient.readContract({
464
+ address: this.address,
465
+ abi: _SlvrGridLottery.ABI,
466
+ functionName: "carryWinnerNativePool"
467
+ });
468
+ }
469
+ /**
470
+ * Get staker rewards that failed to distribute (owed)
471
+ */
472
+ async carryStakerNativeOwed() {
473
+ return await this.publicClient.readContract({
474
+ address: this.address,
475
+ abi: _SlvrGridLottery.ABI,
476
+ functionName: "carryStakerNativeOwed"
477
+ });
478
+ }
479
+ /**
480
+ * Get jackpot funds that failed to add (owed)
481
+ */
482
+ async carryJackpotNativeOwed() {
483
+ return await this.publicClient.readContract({
484
+ address: this.address,
485
+ abi: _SlvrGridLottery.ABI,
486
+ functionName: "carryJackpotNativeOwed"
487
+ });
488
+ }
489
+ /**
490
+ * Get carry-over SLVR pool
491
+ */
492
+ async carrySlvrPool() {
493
+ return await this.publicClient.readContract({
494
+ address: this.address,
495
+ abi: _SlvrGridLottery.ABI,
496
+ functionName: "carrySlvrPool"
497
+ });
498
+ }
499
+ /**
500
+ * Get the configured SLVR-per-round value.
501
+ *
502
+ * NOTE: This is no longer the authoritative emission number. Emission is hub-gated: the amount
503
+ * actually minted each round is bounded by the game's streamed emission budget on SlvrHub
504
+ * (see SlvrHub.pendingEmission / mintReward). Treat this only as the requested/target value.
505
+ */
506
+ async slvrPerRound() {
507
+ return await this.publicClient.readContract({
508
+ address: this.address,
509
+ abi: _SlvrGridLottery.ABI,
510
+ functionName: "slvrPerRound"
511
+ });
512
+ }
513
+ /**
514
+ * Get protocol fee in basis points
515
+ */
516
+ async protocolFeeBps() {
517
+ return await this.publicClient.readContract({
518
+ address: this.address,
519
+ abi: _SlvrGridLottery.ABI,
520
+ functionName: "protocolFeeBps"
521
+ });
522
+ }
523
+ /**
524
+ * Get grid size (should be 25)
525
+ */
526
+ async grid() {
527
+ return await this.publicClient.readContract({
528
+ address: this.address,
529
+ abi: _SlvrGridLottery.ABI,
530
+ functionName: "GRID"
531
+ });
532
+ }
533
+ /**
534
+ * Get account deposit amount
535
+ */
536
+ async accountDeposit() {
537
+ return await this.publicClient.readContract({
538
+ address: this.address,
539
+ abi: _SlvrGridLottery.ABI,
540
+ functionName: "ACCOUNT_DEPOSIT"
541
+ });
542
+ }
543
+ /**
544
+ * Place a bet on the current round
545
+ * Note: The contract will automatically handle account deposit for new accounts.
546
+ * If the beneficiary is a new account, ensure msg.value >= ACCOUNT_DEPOSIT + total bet amount.
547
+ * @param params Bet parameters
548
+ * @returns Transaction hash
549
+ * @throws WalletClientRequiredError if wallet client is not available
550
+ * @throws ValidationError if inputs are invalid
551
+ * @throws ContractCallError if contract call fails
552
+ */
553
+ async bet(params) {
554
+ if (!this.walletClient) {
555
+ throw new WalletClientRequiredError("betting");
556
+ }
557
+ const { roundId, squares, amounts, beneficiary, overrides } = params;
558
+ if (roundId < 0n) {
559
+ throw new ContractCallError("Round ID must be non-negative");
560
+ }
561
+ validateSquares(squares);
562
+ validateArrayLengths([squares, amounts], ["squares", "amounts"]);
563
+ for (const amount of amounts) {
564
+ validateAmount(amount, "bet amount");
565
+ }
566
+ const totalAmount = amounts.reduce((sum, amt) => sum + amt, 0n);
567
+ try {
568
+ const beneficiaryAddress = beneficiary ? validateAddress(beneficiary, "beneficiary") : this.walletClient.account.address;
569
+ const hasAccount_ = await this.hasAccount(beneficiaryAddress);
570
+ const accountDeposit_ = hasAccount_ ? 0n : await this.accountDeposit();
571
+ const totalValue = totalAmount + accountDeposit_;
572
+ const req = beneficiary ? { functionName: "betFor", args: [roundId, beneficiaryAddress, squares, amounts] } : { functionName: "bet", args: [roundId, squares, amounts] };
573
+ return await this.walletClient.writeContract({
574
+ address: this.address,
575
+ abi: _SlvrGridLottery.ABI,
576
+ functionName: req.functionName,
577
+ args: req.args,
578
+ value: totalValue,
579
+ account: this.walletClient.account,
580
+ chain: null,
581
+ ...overrides ?? {}
582
+ });
583
+ } catch (error) {
584
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
585
+ throw error;
586
+ }
587
+ const revert = decodeSlvrRevert(error);
588
+ if (revert) throw revert;
589
+ throw new ContractCallError(`Failed to place bet: ${error instanceof Error ? error.message : String(error)}`, error);
590
+ }
591
+ }
592
+ /**
593
+ * Preflight a bet with `eth_call` (no transaction sent). Resolves if the bet
594
+ * would succeed; otherwise throws a {@link SlvrRevertError} with the decoded
595
+ * custom error (e.g. `RoundNotOpen`, `InsufficientValue`). Catch reverts before
596
+ * spending gas.
597
+ */
598
+ async simulateBet(params) {
599
+ if (!this.walletClient) {
600
+ throw new WalletClientRequiredError("simulating a bet");
601
+ }
602
+ const { roundId, squares, amounts, beneficiary } = params;
603
+ validateSquares(squares);
604
+ validateArrayLengths([squares, amounts], ["squares", "amounts"]);
605
+ const totalAmount = amounts.reduce((sum, amt) => sum + amt, 0n);
606
+ const beneficiaryAddress = beneficiary ? validateAddress(beneficiary, "beneficiary") : this.walletClient.account.address;
607
+ const hasAccount_ = await this.hasAccount(beneficiaryAddress);
608
+ const value = totalAmount + (hasAccount_ ? 0n : await this.accountDeposit());
609
+ const req = beneficiary ? { functionName: "betFor", args: [roundId, beneficiaryAddress, squares, amounts] } : { functionName: "bet", args: [roundId, squares, amounts] };
610
+ try {
611
+ await this.publicClient.simulateContract({
612
+ address: this.address,
613
+ abi: _SlvrGridLottery.ABI,
614
+ functionName: req.functionName,
615
+ args: req.args,
616
+ value,
617
+ account: this.walletClient.account
618
+ });
619
+ } catch (error) {
620
+ throw decodeSlvrRevert(error) ?? error;
621
+ }
622
+ }
623
+ /**
624
+ * Claim rewards for a round
625
+ * @param params Claim parameters
626
+ * @returns Transaction hash
627
+ * @throws WalletClientRequiredError if wallet client is not available
628
+ * @throws ValidationError if inputs are invalid
629
+ * @throws ContractCallError if contract call fails
630
+ */
631
+ async claim(params) {
632
+ if (!this.walletClient) {
633
+ throw new WalletClientRequiredError("claiming");
634
+ }
635
+ if (params.roundId < 0n) {
636
+ throw new ContractCallError("Round ID must be non-negative");
637
+ }
638
+ try {
639
+ return await this.walletClient.writeContract({
640
+ address: this.address,
641
+ abi: _SlvrGridLottery.ABI,
642
+ functionName: "claim",
643
+ args: [params.roundId],
644
+ account: this.walletClient.account,
645
+ chain: null,
646
+ ...params.overrides ?? {}
647
+ });
648
+ } catch (error) {
649
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
650
+ throw error;
651
+ }
652
+ const revert = decodeSlvrRevert(error);
653
+ if (revert) throw revert;
654
+ throw new ContractCallError(`Failed to claim rewards: ${error instanceof Error ? error.message : String(error)}`, error);
655
+ }
656
+ }
657
+ /**
658
+ * Advanced claim function covering all claim variants
659
+ * Handles: user vs delegate, native/SLVR recipients, bypassFee, ethOnly
660
+ * @param params Advanced claim parameters
661
+ * @returns Transaction hash
662
+ * @throws WalletClientRequiredError if wallet client is not available
663
+ * @throws ValidationError if inputs are invalid
664
+ * @throws ContractCallError if contract call fails
665
+ */
666
+ async claimAdvanced(params) {
667
+ if (!this.walletClient) {
668
+ throw new WalletClientRequiredError("claiming");
669
+ }
670
+ if (params.roundId < 0n) {
671
+ throw new ContractCallError("Round ID must be non-negative");
672
+ }
673
+ const user = validateAddress(params.user, "user");
674
+ const recipientNative = params.recipientNative ? validateAddress(params.recipientNative, "recipientNative") : "0x0000000000000000000000000000000000000000";
675
+ const recipientSlvr = params.recipientSlvr ? validateAddress(params.recipientSlvr, "recipientSlvr") : "0x0000000000000000000000000000000000000000";
676
+ try {
677
+ return await this.walletClient.writeContract({
678
+ address: this.address,
679
+ abi: _SlvrGridLottery.ABI,
680
+ functionName: "claimAdvanced",
681
+ args: [{
682
+ user,
683
+ roundId: params.roundId,
684
+ recipientNative,
685
+ recipientSlvr,
686
+ bypassFee: params.bypassFee || false,
687
+ ethOnly: params.ethOnly || false
688
+ }],
689
+ account: this.walletClient.account,
690
+ chain: null
691
+ });
692
+ } catch (error) {
693
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
694
+ throw error;
695
+ }
696
+ throw new ContractCallError(`Failed to claim rewards: ${error instanceof Error ? error.message : String(error)}`, error);
697
+ }
698
+ }
699
+ /**
700
+ * Batch claim rewards for multiple rounds
701
+ * Note: This sends multiple transactions sequentially. For better UX, consider
702
+ * using a multicall contract or batching in your application layer.
703
+ * @param roundIds Array of round IDs to claim
704
+ * @param user User address to claim for (defaults to wallet client account)
705
+ * @param options Optional configuration
706
+ * @param options.waitForReceipt Whether to wait for each transaction receipt (default: false)
707
+ * @returns Array of transaction hashes
708
+ * @throws WalletClientRequiredError if wallet client is not available
709
+ * @throws ValidationError if inputs are invalid
710
+ * @throws ContractCallError if contract call fails
711
+ */
712
+ async batchClaim(roundIds, user, options) {
713
+ if (!this.walletClient) {
714
+ throw new WalletClientRequiredError("batch claiming");
715
+ }
716
+ if (roundIds.length === 0) {
717
+ throw new ContractCallError("No rounds to claim");
718
+ }
719
+ const claimUser = user ? validateAddress(user, "user") : this.walletClient.account.address;
720
+ const hashes = [];
721
+ for (const roundId of roundIds) {
722
+ if (roundId < 0n) {
723
+ throw new ContractCallError(`Invalid round ID: ${roundId}`);
724
+ }
725
+ try {
726
+ const hash = await this.claimAdvanced({
727
+ user: claimUser,
728
+ roundId
729
+ });
730
+ hashes.push(hash);
731
+ if (options?.waitForReceipt) {
732
+ await waitForTransactionReceipt(this.publicClient, hash);
733
+ }
734
+ } catch (error) {
735
+ throw new ContractCallError(
736
+ `Failed to claim round ${roundId}: ${error instanceof Error ? error.message : String(error)}`,
737
+ error
738
+ );
739
+ }
740
+ }
741
+ return hashes;
742
+ }
743
+ /**
744
+ * Approve a delegate to claim rewards on your behalf
745
+ */
746
+ async approveDelegate(delegate) {
747
+ if (!this.walletClient) {
748
+ throw new WalletClientRequiredError("approving delegate");
749
+ }
750
+ const delegateAddress = validateAddress(delegate, "delegate");
751
+ return await this.walletClient.writeContract({
752
+ address: this.address,
753
+ abi: _SlvrGridLottery.ABI,
754
+ functionName: "approveDelegate",
755
+ args: [delegateAddress],
756
+ account: this.walletClient.account,
757
+ chain: null
758
+ });
759
+ }
760
+ /**
761
+ * Revoke a delegate's approval to claim on your behalf
762
+ */
763
+ async revokeDelegate(delegate) {
764
+ if (!this.walletClient) {
765
+ throw new WalletClientRequiredError("revoking delegate");
766
+ }
767
+ const delegateAddress = validateAddress(delegate, "delegate");
768
+ return await this.walletClient.writeContract({
769
+ address: this.address,
770
+ abi: _SlvrGridLottery.ABI,
771
+ functionName: "revokeDelegate",
772
+ args: [delegateAddress],
773
+ account: this.walletClient.account,
774
+ chain: null
775
+ });
776
+ }
777
+ /**
778
+ * Check if a delegate is approved for a user
779
+ */
780
+ async getDelegate(user, delegate) {
781
+ return await this.publicClient.readContract({
782
+ address: this.address,
783
+ abi: _SlvrGridLottery.ABI,
784
+ functionName: "getDelegate",
785
+ args: [user, delegate]
786
+ });
787
+ }
788
+ /**
789
+ * Donate SLVR tokens directly to the jackpot pool.
790
+ * Note: caller must have approved the lottery to spend `amount` of SLVR beforehand.
791
+ */
792
+ async donateSlvrToJackpot(amount) {
793
+ if (!this.walletClient) {
794
+ throw new WalletClientRequiredError("donating to jackpot");
795
+ }
796
+ validateAmount(amount, "donation amount");
797
+ return await this.walletClient.writeContract({
798
+ address: this.address,
799
+ abi: _SlvrGridLottery.ABI,
800
+ functionName: "donateSlvrToJackpot",
801
+ args: [amount],
802
+ account: this.walletClient.account,
803
+ chain: null
804
+ });
805
+ }
806
+ /**
807
+ * Add ETH (native) tokens to the jackpot pool
808
+ * @param value Amount of native ETH to deposit
809
+ */
810
+ async addEthToJackpot(value) {
811
+ if (!this.walletClient) {
812
+ throw new WalletClientRequiredError("adding ETH to jackpot");
813
+ }
814
+ validateAmount(value, "jackpot amount");
815
+ return await this.walletClient.writeContract({
816
+ address: this.address,
817
+ abi: _SlvrGridLottery.ABI,
818
+ functionName: "addEthToJackpot",
819
+ value,
820
+ account: this.walletClient.account,
821
+ chain: null
822
+ });
823
+ }
824
+ /**
825
+ * Withdraw your accumulated mined SLVR (the "refine and cash out" path).
826
+ *
827
+ * Settles your miner accrual (auto-checkpoints) and transfers your SLVR out,
828
+ * net of the refining fee. This is the function a mining bot wants to realize
829
+ * SLVR rewards outside of a specific round claim. Reverts if you have nothing to
830
+ * withdraw. The exact `(totalPayout, refiningFee)` are in the emitted `Claimed`
831
+ * event; this returns the transaction hash.
832
+ */
833
+ async withdrawUnrefinedSlvr() {
834
+ if (!this.walletClient) {
835
+ throw new WalletClientRequiredError("withdrawing unrefined SLVR");
836
+ }
837
+ return await this.walletClient.writeContract({
838
+ address: this.address,
839
+ abi: _SlvrGridLottery.ABI,
840
+ functionName: "withdrawUnrefinedSlvr",
841
+ account: this.walletClient.account,
842
+ chain: null
843
+ });
844
+ }
845
+ /**
846
+ * Force on-chain settlement of a miner's refined-reward accrual (lazy
847
+ * "checkpoint"). Permissionless — you can checkpoint any account.
848
+ *
849
+ * This only updates accounting (rolls index growth into `refinedAccrued`); it
850
+ * moves no tokens. You rarely need it: `claim` and {@link withdrawUnrefinedSlvr}
851
+ * both checkpoint internally, and the up-to-date figure can be derived off-chain
852
+ * from {@link getMinerState}. Use it when you want state settled without a
853
+ * claim/withdraw — e.g. to make a subsequent `getMinerState` read exact.
854
+ */
855
+ async checkpoint(account) {
856
+ if (!this.walletClient) {
857
+ throw new WalletClientRequiredError("checkpointing");
858
+ }
859
+ return await this.walletClient.writeContract({
860
+ address: this.address,
861
+ abi: _SlvrGridLottery.ABI,
862
+ functionName: "checkpoint",
863
+ args: [account],
864
+ account: this.walletClient.account,
865
+ chain: null
866
+ });
867
+ }
868
+ /**
869
+ * Get all square data for a round
870
+ */
871
+ async getRoundSquares(roundId) {
872
+ const gridSize = await this.grid();
873
+ const squares = Array.from({ length: gridSize }, (_, i) => i);
874
+ try {
875
+ const contracts = squares.flatMap((sq) => [
876
+ { address: this.address, abi: _SlvrGridLottery.ABI, functionName: "getTotalOnSquare", args: [roundId, sq] },
877
+ { address: this.address, abi: _SlvrGridLottery.ABI, functionName: "getBettorsOnSquare", args: [roundId, sq] }
878
+ ]);
879
+ const res = await this.publicClient.multicall({
880
+ contracts,
881
+ allowFailure: false
882
+ });
883
+ return squares.map((square, i) => ({ square, total: res[i * 2], bettors: res[i * 2 + 1] }));
884
+ } catch {
885
+ const [totals, bettors] = await Promise.all([
886
+ Promise.all(squares.map((sq) => this.getTotalOnSquare(roundId, sq))),
887
+ Promise.all(squares.map((sq) => this.getBettorsOnSquare(roundId, sq)))
888
+ ]);
889
+ return squares.map((square, i) => ({ square, total: totals[i], bettors: bettors[i] }));
890
+ }
891
+ }
892
+ /**
893
+ * Get user's bets for a round
894
+ */
895
+ async getUserBets(roundId, user) {
896
+ const gridSize = await this.grid();
897
+ const squares = Array.from({ length: gridSize }, (_, i) => i);
898
+ let bets;
899
+ try {
900
+ const contracts = squares.map((sq) => ({
901
+ address: this.address,
902
+ abi: _SlvrGridLottery.ABI,
903
+ functionName: "getUserBet",
904
+ args: [roundId, sq, user]
905
+ }));
906
+ bets = await this.publicClient.multicall({ contracts, allowFailure: false });
907
+ } catch {
908
+ bets = await Promise.all(squares.map((sq) => this.getUserBet(roundId, sq, user)));
909
+ }
910
+ return squares.map((square, i) => ({ square, amount: bets[i] })).filter((bet) => bet.amount > 0n);
911
+ }
912
+ // ---------------------------------------------------------------------------
913
+ // Reactive helpers
914
+ // ---------------------------------------------------------------------------
915
+ /**
916
+ * Wait until a round is resolved, resolving with its final {@link RoundInfo}.
917
+ * Polls `getRound` on an interval — handy for "bet, then claim once it settles".
918
+ *
919
+ * @param opts.pollIntervalMs how often to check (default 4000)
920
+ * @param opts.timeoutMs give up after this long (default: wait indefinitely)
921
+ * @throws {Error} if `timeoutMs` elapses before resolution
922
+ */
923
+ async waitForResolution(roundId, opts = {}) {
924
+ const interval = opts.pollIntervalMs ?? 4e3;
925
+ const deadline = opts.timeoutMs !== void 0 ? Date.now() + opts.timeoutMs : Infinity;
926
+ for (; ; ) {
927
+ const round = await this.getRound(roundId);
928
+ if (round.resolved) return round;
929
+ if (Date.now() >= deadline) throw new Error(`round ${roundId} not resolved within ${opts.timeoutMs}ms`);
930
+ await new Promise((r) => setTimeout(r, interval));
931
+ }
932
+ }
933
+ /**
934
+ * Subscribe to `RoundResolved` events. Returns an unsubscribe function.
935
+ *
936
+ * @example
937
+ * ```typescript
938
+ * const stop = sdk.lottery.watchRoundResolved((e) => {
939
+ * console.log(`round ${e.roundId} won by square ${e.winningSquare}`);
940
+ * });
941
+ * // later: stop();
942
+ * ```
943
+ */
944
+ watchRoundResolved(onResolved, opts = {}) {
945
+ return this.publicClient.watchContractEvent({
946
+ address: this.address,
947
+ abi: SlvrGridLotteryEvents,
948
+ eventName: "RoundResolved",
949
+ onLogs: (logs) => {
950
+ for (const log of logs) onResolved(log.args);
951
+ },
952
+ onError: opts.onError,
953
+ poll: true
954
+ });
955
+ }
956
+ /**
957
+ * Subscribe to `BetPlaced` events (optionally for one round). Returns an
958
+ * unsubscribe function. Useful for reacting to pot changes in real time.
959
+ */
960
+ watchBets(onBet, opts = {}) {
961
+ return this.publicClient.watchContractEvent({
962
+ address: this.address,
963
+ abi: SlvrGridLotteryEvents,
964
+ eventName: "BetPlaced",
965
+ args: opts.roundId !== void 0 ? { roundId: opts.roundId } : void 0,
966
+ onLogs: (logs) => {
967
+ for (const log of logs) onBet(log.args);
968
+ },
969
+ onError: opts.onError,
970
+ poll: true
971
+ });
972
+ }
973
+ };
974
+ // ABI for SlvrGridLottery contract
975
+ _SlvrGridLottery.ABI = parseAbi2([
976
+ // View functions
977
+ "function currentRoundId() view returns (uint256)",
978
+ "function roundStart(uint256 roundId) view returns (uint256)",
979
+ "function roundEnd(uint256 roundId) view returns (uint256)",
980
+ "function roundOpen(uint256 roundId) view returns (bool)",
981
+ // Betting cutoff for a round. This is the timestamp bets stop being accepted,
982
+ // which can be earlier than roundEnd() — gate bots on this, not roundEnd.
983
+ "function bettingEnd(uint256 roundId) view returns (uint256)",
984
+ "function getExpectedReward(address account, uint256 roundId) view returns (uint256)",
985
+ "function latestResolvedRoundId() view returns (uint256)",
986
+ // getRound returns a flat 16-value tuple (no wrapping struct)
987
+ "function getRound(uint256 roundId) view returns (uint64 requestedAt, bool resolved, bytes32 randomnessId, uint256 randomnessValue, uint8 winningSquare, bool jackpotHit, bool singleMinerRound, address singleMinerWinner, uint256 totalWager, uint256 fee, uint256 winnerTotal, uint256 potForWinners, uint256 slvrForWinners, uint256 payoutMulWad, uint256 slvrMulWad, uint256 totalUnclaimedSlvr)",
988
+ "function getTotalOnSquare(uint256 roundId, uint8 square) view returns (uint256)",
989
+ "function getBettorsOnSquare(uint256 roundId, uint8 square) view returns (uint256)",
990
+ "function getUserBet(uint256 roundId, uint8 square, address bettor) view returns (uint256)",
991
+ "function getHasClaimed(uint256 roundId, address user) view returns (bool)",
992
+ "function getMinerState(address account) view returns (uint256 rewardsSlvr, uint256 refinedAccrued, uint256 indexSnapshot, bool hasAccount)",
993
+ "function getHasAccount(address account) view returns (bool)",
994
+ "function getDelegate(address user, address delegate) view returns (bool)",
995
+ "function carryWinnerNativePool() view returns (uint256)",
996
+ "function carryStakerNativeOwed() view returns (uint256)",
997
+ "function carryJackpotNativeOwed() view returns (uint256)",
998
+ "function carrySlvrPool() view returns (uint256)",
999
+ // NOTE: slvrPerRound() still exists as a public var but is NO LONGER the authoritative
1000
+ // emission number. Emission is hub-gated (see SlvrHub.pendingEmission / mintReward), so the
1001
+ // amount actually minted per round is bounded by the game's streamed emission budget.
1002
+ "function slvrPerRound() view returns (uint256)",
1003
+ "function protocolFeeBps() view returns (uint16)",
1004
+ "function GRID() view returns (uint8)",
1005
+ "function ACCOUNT_DEPOSIT() view returns (uint256)",
1006
+ // Write functions
1007
+ "function bet(uint256 roundId, uint8[] squares, uint256[] amounts) payable",
1008
+ "function betFor(uint256 roundId, address beneficiary, uint8[] squares, uint256[] amounts) payable",
1009
+ "function claim(uint256 roundId)",
1010
+ "function claimAdvanced((address user, uint256 roundId, address recipientNative, address recipientSlvr, bool bypassFee, bool ethOnly) params)",
1011
+ "function approveDelegate(address delegate)",
1012
+ "function revokeDelegate(address delegate)",
1013
+ "function donateSlvrToJackpot(uint256 amount)",
1014
+ "function addEthToJackpot() payable",
1015
+ "function checkpoint(address account)",
1016
+ "function withdrawUnrefinedSlvr() returns (uint256 totalPayout, uint256 refiningFee)",
1017
+ "function getRoundJackpot(uint256 roundId) view returns (address)",
1018
+ // Custom errors — so viem can decode reverts (see decodeSlvrRevert / simulateBet)
1019
+ "error InsufficientValue()",
1020
+ "error RoundNotOpen()",
1021
+ "error NotResolved()",
1022
+ "error ResolveRequested()",
1023
+ "error BadClaim()",
1024
+ "error NoAccount()",
1025
+ "error BadAmount()",
1026
+ "error BadSquare()",
1027
+ "error DupSquare()",
1028
+ "error BadArrays()",
1029
+ "error ValueNotSum()",
1030
+ "error MustSumTo100Percent()",
1031
+ "error NotAuthorized()",
1032
+ "error NotCurrentRound()",
1033
+ "error CannotDelegateToSelf()",
1034
+ "error RandomnessNotSettled()",
1035
+ "error TransferFailed()",
1036
+ "error JackpotNotSet()"
1037
+ ]);
1038
+ var SlvrGridLottery = _SlvrGridLottery;
1039
+
1040
+ // src/contracts/staking.ts
1041
+ import { parseAbi as parseAbi3 } from "viem";
1042
+ var _SlvrStaking = class _SlvrStaking {
1043
+ constructor(publicClient, walletClient, address) {
1044
+ this.publicClient = publicClient;
1045
+ this.walletClient = walletClient;
1046
+ this.address = address;
1047
+ }
1048
+ /**
1049
+ * Update the wallet client
1050
+ * @param walletClient New wallet client
1051
+ */
1052
+ setWalletClient(walletClient) {
1053
+ this.walletClient = walletClient;
1054
+ }
1055
+ // ---------------------------------------------------------------------------
1056
+ // Views
1057
+ // ---------------------------------------------------------------------------
1058
+ /**
1059
+ * Get claimable rewards for a staked veNFT tokenId
1060
+ */
1061
+ async getStakerRewards(tokenId) {
1062
+ return await this.publicClient.readContract({
1063
+ address: this.address,
1064
+ abi: _SlvrStaking.ABI,
1065
+ functionName: "getStakerRewards",
1066
+ args: [tokenId]
1067
+ });
1068
+ }
1069
+ /**
1070
+ * Get the total tracked weight across all staked tokens
1071
+ */
1072
+ async getTotalWeight() {
1073
+ return await this.publicClient.readContract({
1074
+ address: this.address,
1075
+ abi: _SlvrStaking.ABI,
1076
+ functionName: "getTotalWeight"
1077
+ });
1078
+ }
1079
+ /**
1080
+ * Get the accumulated reward per unit of weight (1e18 precision)
1081
+ */
1082
+ async rewardPerWeightStored() {
1083
+ return await this.publicClient.readContract({
1084
+ address: this.address,
1085
+ abi: _SlvrStaking.ABI,
1086
+ functionName: "rewardPerWeightStored"
1087
+ });
1088
+ }
1089
+ /**
1090
+ * Get the last rewardPerWeightStored snapshot recorded for a tokenId
1091
+ */
1092
+ async rewardPerWeightPaid(tokenId) {
1093
+ return await this.publicClient.readContract({
1094
+ address: this.address,
1095
+ abi: _SlvrStaking.ABI,
1096
+ functionName: "rewardPerWeightPaid",
1097
+ args: [tokenId]
1098
+ });
1099
+ }
1100
+ /**
1101
+ * Get the tracked weight (balance) for a staked tokenId
1102
+ */
1103
+ async balance(tokenId) {
1104
+ return await this.publicClient.readContract({
1105
+ address: this.address,
1106
+ abi: _SlvrStaking.ABI,
1107
+ functionName: "balance",
1108
+ args: [tokenId]
1109
+ });
1110
+ }
1111
+ /**
1112
+ * Get the accrued (checkpointed) claimable rewards stored for a tokenId
1113
+ */
1114
+ async rewards(tokenId) {
1115
+ return await this.publicClient.readContract({
1116
+ address: this.address,
1117
+ abi: _SlvrStaking.ABI,
1118
+ functionName: "rewards",
1119
+ args: [tokenId]
1120
+ });
1121
+ }
1122
+ /**
1123
+ * Get the total tracked weight (public storage var)
1124
+ */
1125
+ async totalWeight() {
1126
+ return await this.publicClient.readContract({
1127
+ address: this.address,
1128
+ abi: _SlvrStaking.ABI,
1129
+ functionName: "totalWeight"
1130
+ });
1131
+ }
1132
+ /**
1133
+ * Get unallocated rewards (distributed while no weight was staked)
1134
+ */
1135
+ async unallocated() {
1136
+ return await this.publicClient.readContract({
1137
+ address: this.address,
1138
+ abi: _SlvrStaking.ABI,
1139
+ functionName: "unallocated"
1140
+ });
1141
+ }
1142
+ /**
1143
+ * Get the last distributed round ID (type(uint256).max sentinel when none)
1144
+ */
1145
+ async lastDistributedRoundId() {
1146
+ return await this.publicClient.readContract({
1147
+ address: this.address,
1148
+ abi: _SlvrStaking.ABI,
1149
+ functionName: "lastDistributedRoundId"
1150
+ });
1151
+ }
1152
+ /**
1153
+ * Get the lottery address authorized to distribute rewards
1154
+ */
1155
+ async lottery() {
1156
+ return await this.publicClient.readContract({
1157
+ address: this.address,
1158
+ abi: _SlvrStaking.ABI,
1159
+ functionName: "LOTTERY"
1160
+ });
1161
+ }
1162
+ /**
1163
+ * Get complete staking info for a staked veNFT tokenId
1164
+ */
1165
+ async getStakingInfo(tokenId) {
1166
+ const [totalWeight_, balance_, rewards_, rewardPerWeightStored_] = await Promise.all([
1167
+ this.totalWeight(),
1168
+ this.balance(tokenId),
1169
+ this.getStakerRewards(tokenId),
1170
+ this.rewardPerWeightStored()
1171
+ ]);
1172
+ return {
1173
+ totalWeight: totalWeight_,
1174
+ balance: balance_,
1175
+ rewards: rewards_,
1176
+ rewardPerWeightStored: rewardPerWeightStored_
1177
+ };
1178
+ }
1179
+ // ---------------------------------------------------------------------------
1180
+ // Writes
1181
+ // ---------------------------------------------------------------------------
1182
+ /**
1183
+ * Stake a vote-escrow NFT by tokenId
1184
+ * @param tokenId The veNFT token ID to stake
1185
+ * @returns Transaction hash
1186
+ */
1187
+ async stake(tokenId) {
1188
+ if (!this.walletClient) {
1189
+ throw new WalletClientRequiredError("staking");
1190
+ }
1191
+ try {
1192
+ return await this.walletClient.writeContract({
1193
+ address: this.address,
1194
+ abi: _SlvrStaking.ABI,
1195
+ functionName: "stake",
1196
+ args: [tokenId],
1197
+ account: this.walletClient.account,
1198
+ chain: null
1199
+ });
1200
+ } catch (error) {
1201
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1202
+ throw error;
1203
+ }
1204
+ throw new ContractCallError(`Failed to stake: ${error instanceof Error ? error.message : String(error)}`, error);
1205
+ }
1206
+ }
1207
+ /**
1208
+ * Unstake a vote-escrow NFT by tokenId
1209
+ * @param tokenId The veNFT token ID to unstake
1210
+ * @returns Transaction hash
1211
+ */
1212
+ async unstake(tokenId) {
1213
+ if (!this.walletClient) {
1214
+ throw new WalletClientRequiredError("unstaking");
1215
+ }
1216
+ try {
1217
+ return await this.walletClient.writeContract({
1218
+ address: this.address,
1219
+ abi: _SlvrStaking.ABI,
1220
+ functionName: "unstake",
1221
+ args: [tokenId],
1222
+ account: this.walletClient.account,
1223
+ chain: null
1224
+ });
1225
+ } catch (error) {
1226
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1227
+ throw error;
1228
+ }
1229
+ throw new ContractCallError(`Failed to unstake: ${error instanceof Error ? error.message : String(error)}`, error);
1230
+ }
1231
+ }
1232
+ /**
1233
+ * Claim staker rewards for a tokenId
1234
+ * @param tokenId The veNFT token ID to claim rewards for
1235
+ * @returns Transaction hash
1236
+ */
1237
+ async claimStakerRewards(tokenId) {
1238
+ if (!this.walletClient) {
1239
+ throw new WalletClientRequiredError("claiming rewards");
1240
+ }
1241
+ try {
1242
+ return await this.walletClient.writeContract({
1243
+ address: this.address,
1244
+ abi: _SlvrStaking.ABI,
1245
+ functionName: "claimStakerRewards",
1246
+ args: [tokenId],
1247
+ account: this.walletClient.account,
1248
+ chain: null
1249
+ });
1250
+ } catch (error) {
1251
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1252
+ throw error;
1253
+ }
1254
+ throw new ContractCallError(`Failed to claim rewards: ${error instanceof Error ? error.message : String(error)}`, error);
1255
+ }
1256
+ }
1257
+ /**
1258
+ * Checkpoint a token's reward accounting
1259
+ * @param tokenId The veNFT token ID to checkpoint
1260
+ * @returns Transaction hash
1261
+ */
1262
+ async checkpoint(tokenId) {
1263
+ if (!this.walletClient) {
1264
+ throw new WalletClientRequiredError("checkpointing");
1265
+ }
1266
+ return await this.walletClient.writeContract({
1267
+ address: this.address,
1268
+ abi: _SlvrStaking.ABI,
1269
+ functionName: "checkpoint",
1270
+ args: [tokenId],
1271
+ account: this.walletClient.account,
1272
+ chain: null
1273
+ });
1274
+ }
1275
+ /**
1276
+ * Poke a token to refresh its tracked weight
1277
+ * @param tokenId The veNFT token ID to poke
1278
+ * @returns Transaction hash
1279
+ */
1280
+ async poke(tokenId) {
1281
+ if (!this.walletClient) {
1282
+ throw new WalletClientRequiredError("poking");
1283
+ }
1284
+ return await this.walletClient.writeContract({
1285
+ address: this.address,
1286
+ abi: _SlvrStaking.ABI,
1287
+ functionName: "poke",
1288
+ args: [tokenId],
1289
+ account: this.walletClient.account,
1290
+ chain: null
1291
+ });
1292
+ }
1293
+ /**
1294
+ * Poke many tokens to refresh their tracked weight in one call
1295
+ * @param tokenIds The veNFT token IDs to poke
1296
+ * @returns Transaction hash
1297
+ */
1298
+ async pokeMany(tokenIds) {
1299
+ if (!this.walletClient) {
1300
+ throw new WalletClientRequiredError("poking");
1301
+ }
1302
+ return await this.walletClient.writeContract({
1303
+ address: this.address,
1304
+ abi: _SlvrStaking.ABI,
1305
+ functionName: "pokeMany",
1306
+ args: [tokenIds],
1307
+ account: this.walletClient.account,
1308
+ chain: null
1309
+ });
1310
+ }
1311
+ };
1312
+ _SlvrStaking.ABI = parseAbi3([
1313
+ // View functions
1314
+ "function getStakerRewards(uint256 tokenId) view returns (uint256)",
1315
+ "function getTotalWeight() view returns (uint256)",
1316
+ "function rewardPerWeightStored() view returns (uint256)",
1317
+ "function rewardPerWeightPaid(uint256 tokenId) view returns (uint256)",
1318
+ "function balance(uint256 tokenId) view returns (uint256)",
1319
+ "function rewards(uint256 tokenId) view returns (uint256)",
1320
+ "function totalWeight() view returns (uint256)",
1321
+ "function unallocated() view returns (uint256)",
1322
+ "function lastDistributedRoundId() view returns (uint256)",
1323
+ "function LOTTERY() view returns (address)",
1324
+ // Write functions
1325
+ "function stake(uint256 tokenId)",
1326
+ "function unstake(uint256 tokenId)",
1327
+ "function claimStakerRewards(uint256 tokenId)",
1328
+ "function checkpoint(uint256 tokenId)",
1329
+ "function poke(uint256 tokenId)",
1330
+ "function pokeMany(uint256[] tokenIds)"
1331
+ ]);
1332
+ var SlvrStaking = _SlvrStaking;
1333
+
1334
+ // src/contracts/token.ts
1335
+ import { parseAbi as parseAbi4 } from "viem";
1336
+ var _SlvrToken = class _SlvrToken {
1337
+ constructor(publicClient, walletClient, address) {
1338
+ this.publicClient = publicClient;
1339
+ this.walletClient = walletClient;
1340
+ this.address = address;
1341
+ }
1342
+ /**
1343
+ * Update the wallet client
1344
+ * @param walletClient New wallet client
1345
+ */
1346
+ setWalletClient(walletClient) {
1347
+ this.walletClient = walletClient;
1348
+ }
1349
+ /**
1350
+ * Get total supply
1351
+ */
1352
+ async totalSupply() {
1353
+ return await this.publicClient.readContract({
1354
+ address: this.address,
1355
+ abi: _SlvrToken.ABI,
1356
+ functionName: "totalSupply"
1357
+ });
1358
+ }
1359
+ /**
1360
+ * Get balance of an account
1361
+ */
1362
+ async balanceOf(account) {
1363
+ return await this.publicClient.readContract({
1364
+ address: this.address,
1365
+ abi: _SlvrToken.ABI,
1366
+ functionName: "balanceOf",
1367
+ args: [account]
1368
+ });
1369
+ }
1370
+ /**
1371
+ * Get allowance
1372
+ */
1373
+ async allowance(owner, spender) {
1374
+ return await this.publicClient.readContract({
1375
+ address: this.address,
1376
+ abi: _SlvrToken.ABI,
1377
+ functionName: "allowance",
1378
+ args: [owner, spender]
1379
+ });
1380
+ }
1381
+ /**
1382
+ * Get token name
1383
+ */
1384
+ async name() {
1385
+ return await this.publicClient.readContract({
1386
+ address: this.address,
1387
+ abi: _SlvrToken.ABI,
1388
+ functionName: "name"
1389
+ });
1390
+ }
1391
+ /**
1392
+ * Get token symbol
1393
+ */
1394
+ async symbol() {
1395
+ return await this.publicClient.readContract({
1396
+ address: this.address,
1397
+ abi: _SlvrToken.ABI,
1398
+ functionName: "symbol"
1399
+ });
1400
+ }
1401
+ /**
1402
+ * Get token decimals
1403
+ */
1404
+ async decimals() {
1405
+ return await this.publicClient.readContract({
1406
+ address: this.address,
1407
+ abi: _SlvrToken.ABI,
1408
+ functionName: "decimals"
1409
+ });
1410
+ }
1411
+ /**
1412
+ * Get maximum supply
1413
+ */
1414
+ async maxSupply() {
1415
+ return await this.publicClient.readContract({
1416
+ address: this.address,
1417
+ abi: _SlvrToken.ABI,
1418
+ functionName: "MAX_SUPPLY"
1419
+ });
1420
+ }
1421
+ /**
1422
+ * Transfer tokens
1423
+ * @param to Recipient address
1424
+ * @param amount Amount to transfer
1425
+ * @returns Transaction hash
1426
+ * @throws WalletClientRequiredError if wallet client is not available
1427
+ * @throws ValidationError if inputs are invalid
1428
+ * @throws ContractCallError if contract call fails
1429
+ */
1430
+ async transfer(to, amount) {
1431
+ if (!this.walletClient) {
1432
+ throw new WalletClientRequiredError("transferring");
1433
+ }
1434
+ const recipient = validateAddress(to, "recipient");
1435
+ validateAmount(amount, "transfer amount");
1436
+ try {
1437
+ return await this.walletClient.writeContract({
1438
+ address: this.address,
1439
+ abi: _SlvrToken.ABI,
1440
+ functionName: "transfer",
1441
+ args: [recipient, amount],
1442
+ account: this.walletClient.account,
1443
+ chain: null
1444
+ });
1445
+ } catch (error) {
1446
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1447
+ throw error;
1448
+ }
1449
+ throw new ContractCallError(`Failed to transfer: ${error instanceof Error ? error.message : String(error)}`, error);
1450
+ }
1451
+ }
1452
+ /**
1453
+ * Approve spender
1454
+ * @param spender Address to approve
1455
+ * @param amount Amount to approve
1456
+ * @returns Transaction hash
1457
+ * @throws WalletClientRequiredError if wallet client is not available
1458
+ * @throws ValidationError if inputs are invalid
1459
+ * @throws ContractCallError if contract call fails
1460
+ */
1461
+ async approve(spender, amount) {
1462
+ if (!this.walletClient) {
1463
+ throw new WalletClientRequiredError("approving");
1464
+ }
1465
+ const spenderAddress = validateAddress(spender, "spender");
1466
+ validateAmount(amount, "approval amount");
1467
+ try {
1468
+ return await this.walletClient.writeContract({
1469
+ address: this.address,
1470
+ abi: _SlvrToken.ABI,
1471
+ functionName: "approve",
1472
+ args: [spenderAddress, amount],
1473
+ account: this.walletClient.account,
1474
+ chain: null
1475
+ });
1476
+ } catch (error) {
1477
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1478
+ throw error;
1479
+ }
1480
+ throw new ContractCallError(`Failed to approve: ${error instanceof Error ? error.message : String(error)}`, error);
1481
+ }
1482
+ }
1483
+ /**
1484
+ * Transfer from (requires approval)
1485
+ * @param from Sender address
1486
+ * @param to Recipient address
1487
+ * @param amount Amount to transfer
1488
+ * @returns Transaction hash
1489
+ * @throws WalletClientRequiredError if wallet client is not available
1490
+ * @throws ValidationError if inputs are invalid
1491
+ * @throws ContractCallError if contract call fails
1492
+ */
1493
+ async transferFrom(from, to, amount) {
1494
+ if (!this.walletClient) {
1495
+ throw new WalletClientRequiredError("transferFrom");
1496
+ }
1497
+ const fromAddress = validateAddress(from, "from");
1498
+ const toAddress = validateAddress(to, "to");
1499
+ validateAmount(amount, "transfer amount");
1500
+ try {
1501
+ return await this.walletClient.writeContract({
1502
+ address: this.address,
1503
+ abi: _SlvrToken.ABI,
1504
+ functionName: "transferFrom",
1505
+ args: [fromAddress, toAddress, amount],
1506
+ account: this.walletClient.account,
1507
+ chain: null
1508
+ });
1509
+ } catch (error) {
1510
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1511
+ throw error;
1512
+ }
1513
+ throw new ContractCallError(`Failed to transferFrom: ${error instanceof Error ? error.message : String(error)}`, error);
1514
+ }
1515
+ }
1516
+ /**
1517
+ * Burn tokens
1518
+ * @param amount Amount to burn
1519
+ * @returns Transaction hash
1520
+ * @throws WalletClientRequiredError if wallet client is not available
1521
+ * @throws ValidationError if amount is invalid
1522
+ * @throws ContractCallError if contract call fails
1523
+ */
1524
+ async burn(amount) {
1525
+ if (!this.walletClient) {
1526
+ throw new WalletClientRequiredError("burning");
1527
+ }
1528
+ validateAmount(amount, "burn amount");
1529
+ try {
1530
+ return await this.walletClient.writeContract({
1531
+ address: this.address,
1532
+ abi: _SlvrToken.ABI,
1533
+ functionName: "burn",
1534
+ args: [amount],
1535
+ account: this.walletClient.account,
1536
+ chain: null
1537
+ });
1538
+ } catch (error) {
1539
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1540
+ throw error;
1541
+ }
1542
+ throw new ContractCallError(`Failed to burn: ${error instanceof Error ? error.message : String(error)}`, error);
1543
+ }
1544
+ }
1545
+ /**
1546
+ * Burn tokens from account
1547
+ * @param account Account to burn from
1548
+ * @param amount Amount to burn
1549
+ * @returns Transaction hash
1550
+ * @throws WalletClientRequiredError if wallet client is not available
1551
+ * @throws ValidationError if inputs are invalid
1552
+ * @throws ContractCallError if contract call fails
1553
+ */
1554
+ async burnFrom(account, amount) {
1555
+ if (!this.walletClient) {
1556
+ throw new WalletClientRequiredError("burnFrom");
1557
+ }
1558
+ const accountAddress = validateAddress(account, "account");
1559
+ validateAmount(amount, "burn amount");
1560
+ try {
1561
+ return await this.walletClient.writeContract({
1562
+ address: this.address,
1563
+ abi: _SlvrToken.ABI,
1564
+ functionName: "burnFrom",
1565
+ args: [accountAddress, amount],
1566
+ account: this.walletClient.account,
1567
+ chain: null
1568
+ });
1569
+ } catch (error) {
1570
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1571
+ throw error;
1572
+ }
1573
+ throw new ContractCallError(`Failed to burnFrom: ${error instanceof Error ? error.message : String(error)}`, error);
1574
+ }
1575
+ }
1576
+ };
1577
+ _SlvrToken.ABI = parseAbi4([
1578
+ // ERC20 standard functions
1579
+ "function totalSupply() view returns (uint256)",
1580
+ "function balanceOf(address account) view returns (uint256)",
1581
+ "function transfer(address to, uint256 amount) returns (bool)",
1582
+ "function allowance(address owner, address spender) view returns (uint256)",
1583
+ "function approve(address spender, uint256 amount) returns (bool)",
1584
+ "function transferFrom(address from, address to, uint256 amount) returns (bool)",
1585
+ // SlvrToken specific
1586
+ "function name() view returns (string)",
1587
+ "function symbol() view returns (string)",
1588
+ "function decimals() view returns (uint8)",
1589
+ "function MAX_SUPPLY() view returns (uint256)",
1590
+ "function burn(uint256 amount)",
1591
+ "function burnFrom(address account, uint256 amount)"
1592
+ ]);
1593
+ var SlvrToken = _SlvrToken;
1594
+
1595
+ // src/contracts/autoCommit.ts
1596
+ import { parseAbi as parseAbi5 } from "viem";
1597
+ var _SlvrAutoCommit = class _SlvrAutoCommit {
1598
+ constructor(publicClient, walletClient, address) {
1599
+ this.publicClient = publicClient;
1600
+ this.walletClient = walletClient;
1601
+ this.address = address;
1602
+ }
1603
+ /**
1604
+ * Update the wallet client
1605
+ * @param walletClient New wallet client
1606
+ */
1607
+ setWalletClient(walletClient) {
1608
+ this.walletClient = walletClient;
1609
+ }
1610
+ /**
1611
+ * Get plan information for a user
1612
+ */
1613
+ async planInfo(user) {
1614
+ const result = await this.publicClient.readContract({
1615
+ address: this.address,
1616
+ abi: _SlvrAutoCommit.ABI,
1617
+ functionName: "planInfo",
1618
+ args: [user]
1619
+ });
1620
+ return {
1621
+ enabled: result[0],
1622
+ nextRoundId: result[1],
1623
+ playsRemaining: Number(result[2]),
1624
+ amountPerPlay: result[3],
1625
+ balance: result[4],
1626
+ autoClaim: result[5],
1627
+ squares: result[6].map((s) => Number(s)),
1628
+ bpsAlloc: result[7].map((b) => Number(b)),
1629
+ planStartRoundId: result[8]
1630
+ };
1631
+ }
1632
+ /**
1633
+ * Check whether a plan is ready to execute, and if not, why
1634
+ */
1635
+ async needsExecution(user) {
1636
+ const [ready, reason] = await this.publicClient.readContract({
1637
+ address: this.address,
1638
+ abi: _SlvrAutoCommit.ABI,
1639
+ functionName: "needsExecution",
1640
+ args: [user]
1641
+ });
1642
+ return { ready, reason };
1643
+ }
1644
+ /**
1645
+ * Check whether a specific round has already been executed for a user
1646
+ */
1647
+ async executedRound(user, roundId) {
1648
+ return await this.publicClient.readContract({
1649
+ address: this.address,
1650
+ abi: _SlvrAutoCommit.ABI,
1651
+ functionName: "executedRounds",
1652
+ args: [user, roundId]
1653
+ });
1654
+ }
1655
+ /**
1656
+ * Get lottery contract address
1657
+ */
1658
+ async lottery() {
1659
+ return await this.publicClient.readContract({
1660
+ address: this.address,
1661
+ abi: _SlvrAutoCommit.ABI,
1662
+ functionName: "LOTTERY"
1663
+ });
1664
+ }
1665
+ /**
1666
+ * Get the sentinel plays value meaning "unlimited plays"
1667
+ */
1668
+ async unlimitedPlays() {
1669
+ return await this.publicClient.readContract({
1670
+ address: this.address,
1671
+ abi: _SlvrAutoCommit.ABI,
1672
+ functionName: "UNLIMITED_PLAYS"
1673
+ });
1674
+ }
1675
+ /**
1676
+ * Get max plays per execution
1677
+ */
1678
+ async maxPlaysPerExecution() {
1679
+ return await this.publicClient.readContract({
1680
+ address: this.address,
1681
+ abi: _SlvrAutoCommit.ABI,
1682
+ functionName: "MAX_PLAYS_PER_EXECUTION"
1683
+ });
1684
+ }
1685
+ /**
1686
+ * Get max claim rounds accepted per execution
1687
+ */
1688
+ async maxClaimsPerExecution() {
1689
+ return await this.publicClient.readContract({
1690
+ address: this.address,
1691
+ abi: _SlvrAutoCommit.ABI,
1692
+ functionName: "MAX_CLAIMS_PER_EXECUTION"
1693
+ });
1694
+ }
1695
+ /**
1696
+ * Get the current cap on the executor fee reimbursed per execution
1697
+ * (owner-tunable within the contract's hard ceiling)
1698
+ */
1699
+ async maxFeePerExecution() {
1700
+ return await this.publicClient.readContract({
1701
+ address: this.address,
1702
+ abi: _SlvrAutoCommit.ABI,
1703
+ functionName: "maxFeePerExecution"
1704
+ });
1705
+ }
1706
+ /**
1707
+ * Get the current executor-fee premium in basis points
1708
+ * (owner-tunable within the contract's hard ceiling)
1709
+ */
1710
+ async feePremiumBps() {
1711
+ return await this.publicClient.readContract({
1712
+ address: this.address,
1713
+ abi: _SlvrAutoCommit.ABI,
1714
+ functionName: "feePremiumBps"
1715
+ });
1716
+ }
1717
+ /**
1718
+ * Deposit native tokens to auto-commit plan
1719
+ * @param value Amount to deposit
1720
+ * @returns Transaction hash
1721
+ * @throws WalletClientRequiredError if wallet client is not available
1722
+ * @throws ValidationError if amount is invalid
1723
+ * @throws ContractCallError if contract call fails
1724
+ */
1725
+ async deposit(value) {
1726
+ if (!this.walletClient) {
1727
+ throw new WalletClientRequiredError("depositing");
1728
+ }
1729
+ validateAmount(value, "deposit amount");
1730
+ try {
1731
+ return await this.walletClient.writeContract({
1732
+ address: this.address,
1733
+ abi: _SlvrAutoCommit.ABI,
1734
+ functionName: "deposit",
1735
+ value,
1736
+ account: this.walletClient.account,
1737
+ chain: null
1738
+ });
1739
+ } catch (error) {
1740
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1741
+ throw error;
1742
+ }
1743
+ throw new ContractCallError(`Failed to deposit: ${error instanceof Error ? error.message : String(error)}`, error);
1744
+ }
1745
+ }
1746
+ /**
1747
+ * Withdraw from auto-commit plan
1748
+ * @param amount Amount to withdraw
1749
+ * @param to Recipient address
1750
+ * @returns Transaction hash
1751
+ * @throws WalletClientRequiredError if wallet client is not available
1752
+ * @throws ValidationError if inputs are invalid
1753
+ * @throws ContractCallError if contract call fails
1754
+ */
1755
+ async withdraw(amount, to) {
1756
+ if (!this.walletClient) {
1757
+ throw new WalletClientRequiredError("withdrawing");
1758
+ }
1759
+ validateAmount(amount, "withdraw amount");
1760
+ const recipient = validateAddress(to, "recipient");
1761
+ try {
1762
+ return await this.walletClient.writeContract({
1763
+ address: this.address,
1764
+ abi: _SlvrAutoCommit.ABI,
1765
+ functionName: "withdraw",
1766
+ args: [amount, recipient],
1767
+ account: this.walletClient.account,
1768
+ chain: null
1769
+ });
1770
+ } catch (error) {
1771
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1772
+ throw error;
1773
+ }
1774
+ throw new ContractCallError(`Failed to withdraw: ${error instanceof Error ? error.message : String(error)}`, error);
1775
+ }
1776
+ }
1777
+ /**
1778
+ * Configure auto-commit plan
1779
+ * @param plays Number of plays (UNLIMITED_PLAYS = 4294967295 for unlimited;
1780
+ * forced to unlimited on-chain when autoClaim is true)
1781
+ * @param amountPerPlay Amount per play
1782
+ * @param squares Square indices to bet on
1783
+ * @param bpsAlloc Basis points allocation for each square (must sum to 10000)
1784
+ * @param autoClaim Whether the keeper should claim winnings back into the plan balance
1785
+ * @returns Transaction hash
1786
+ * @throws WalletClientRequiredError if wallet client is not available
1787
+ * @throws ValidationError if inputs are invalid
1788
+ * @throws ContractCallError if contract call fails
1789
+ */
1790
+ async configurePlan(plays, amountPerPlay, squares, bpsAlloc, autoClaim) {
1791
+ if (!this.walletClient) {
1792
+ throw new WalletClientRequiredError("configuring plan");
1793
+ }
1794
+ this.validatePlanConfig(plays, amountPerPlay, squares, bpsAlloc);
1795
+ try {
1796
+ return await this.walletClient.writeContract({
1797
+ address: this.address,
1798
+ abi: _SlvrAutoCommit.ABI,
1799
+ functionName: "configurePlan",
1800
+ args: [plays, amountPerPlay, squares, bpsAlloc, autoClaim],
1801
+ account: this.walletClient.account,
1802
+ chain: null
1803
+ });
1804
+ } catch (error) {
1805
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1806
+ throw error;
1807
+ }
1808
+ throw new ContractCallError(`Failed to configure plan: ${error instanceof Error ? error.message : String(error)}`, error);
1809
+ }
1810
+ }
1811
+ /**
1812
+ * Configure auto-commit plan and deposit funds in a single transaction
1813
+ * @param plays Number of plays (see configurePlan)
1814
+ * @param amountPerPlay Amount per play
1815
+ * @param squares Square indices to bet on
1816
+ * @param bpsAlloc Basis points allocation for each square (must sum to 10000)
1817
+ * @param autoClaim Whether the keeper should claim winnings back into the plan balance
1818
+ * @param value Amount to deposit
1819
+ * @returns Transaction hash
1820
+ * @throws WalletClientRequiredError if wallet client is not available
1821
+ * @throws ValidationError if inputs are invalid
1822
+ * @throws ContractCallError if contract call fails
1823
+ */
1824
+ async configurePlanAndDeposit(plays, amountPerPlay, squares, bpsAlloc, autoClaim, value) {
1825
+ if (!this.walletClient) {
1826
+ throw new WalletClientRequiredError("configuring plan");
1827
+ }
1828
+ this.validatePlanConfig(plays, amountPerPlay, squares, bpsAlloc);
1829
+ validateAmount(value, "deposit amount");
1830
+ try {
1831
+ return await this.walletClient.writeContract({
1832
+ address: this.address,
1833
+ abi: _SlvrAutoCommit.ABI,
1834
+ functionName: "configurePlanAndDeposit",
1835
+ args: [plays, amountPerPlay, squares, bpsAlloc, autoClaim],
1836
+ value,
1837
+ account: this.walletClient.account,
1838
+ chain: null
1839
+ });
1840
+ } catch (error) {
1841
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1842
+ throw error;
1843
+ }
1844
+ throw new ContractCallError(`Failed to configure plan: ${error instanceof Error ? error.message : String(error)}`, error);
1845
+ }
1846
+ }
1847
+ /**
1848
+ * Disable auto-commit plan
1849
+ * @returns Transaction hash
1850
+ * @throws WalletClientRequiredError if wallet client is not available
1851
+ * @throws ContractCallError if contract call fails
1852
+ */
1853
+ async disablePlan() {
1854
+ if (!this.walletClient) {
1855
+ throw new WalletClientRequiredError("disabling plan");
1856
+ }
1857
+ try {
1858
+ return await this.walletClient.writeContract({
1859
+ address: this.address,
1860
+ abi: _SlvrAutoCommit.ABI,
1861
+ functionName: "disablePlan",
1862
+ account: this.walletClient.account,
1863
+ chain: null
1864
+ });
1865
+ } catch (error) {
1866
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1867
+ throw error;
1868
+ }
1869
+ throw new ContractCallError(`Failed to disable plan: ${error instanceof Error ? error.message : String(error)}`, error);
1870
+ }
1871
+ }
1872
+ /**
1873
+ * Cancel the plan and refund the entire remaining balance to the caller
1874
+ * @returns Transaction hash
1875
+ * @throws WalletClientRequiredError if wallet client is not available
1876
+ * @throws ContractCallError if contract call fails
1877
+ */
1878
+ async cancelPlan() {
1879
+ if (!this.walletClient) {
1880
+ throw new WalletClientRequiredError("cancelling plan");
1881
+ }
1882
+ try {
1883
+ return await this.walletClient.writeContract({
1884
+ address: this.address,
1885
+ abi: _SlvrAutoCommit.ABI,
1886
+ functionName: "cancelPlan",
1887
+ account: this.walletClient.account,
1888
+ chain: null
1889
+ });
1890
+ } catch (error) {
1891
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1892
+ throw error;
1893
+ }
1894
+ throw new ContractCallError(`Failed to cancel plan: ${error instanceof Error ? error.message : String(error)}`, error);
1895
+ }
1896
+ }
1897
+ /**
1898
+ * Execute auto-commit plan for a user (anyone can call).
1899
+ * The caller is reimbursed metered gas + premium from the user's plan balance.
1900
+ * @param user User address
1901
+ * @param maxPlays Maximum number of plays to execute
1902
+ * @param claimRounds Winning round ids to claim into the plan balance before
1903
+ * betting (discovered off-chain; non-claimable entries are no-ops)
1904
+ * @returns Transaction hash
1905
+ * @throws WalletClientRequiredError if wallet client is not available
1906
+ * @throws ValidationError if inputs are invalid
1907
+ * @throws ContractCallError if contract call fails
1908
+ */
1909
+ async executeFor(user, maxPlays, claimRounds = []) {
1910
+ if (!this.walletClient) {
1911
+ throw new WalletClientRequiredError("executing plan");
1912
+ }
1913
+ const userAddress = validateAddress(user, "user");
1914
+ if (!Number.isInteger(maxPlays) || maxPlays <= 0) {
1915
+ throw new ContractCallError("maxPlays must be a positive integer");
1916
+ }
1917
+ try {
1918
+ return await this.walletClient.writeContract({
1919
+ address: this.address,
1920
+ abi: _SlvrAutoCommit.ABI,
1921
+ functionName: "executeFor",
1922
+ args: [userAddress, maxPlays, claimRounds],
1923
+ account: this.walletClient.account,
1924
+ chain: null
1925
+ });
1926
+ } catch (error) {
1927
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1928
+ throw error;
1929
+ }
1930
+ throw new ContractCallError(`Failed to execute plan: ${error instanceof Error ? error.message : String(error)}`, error);
1931
+ }
1932
+ }
1933
+ /**
1934
+ * Claim winning rounds into a user's plan balance without betting (anyone can
1935
+ * call). Useful when the plan balance is too low to bet until winnings land.
1936
+ * @param user User address
1937
+ * @param claimRounds Winning round ids to claim (discovered off-chain)
1938
+ * @returns Transaction hash
1939
+ * @throws WalletClientRequiredError if wallet client is not available
1940
+ * @throws ValidationError if inputs are invalid
1941
+ * @throws ContractCallError if contract call fails
1942
+ */
1943
+ async claimFor(user, claimRounds) {
1944
+ if (!this.walletClient) {
1945
+ throw new WalletClientRequiredError("claiming");
1946
+ }
1947
+ const userAddress = validateAddress(user, "user");
1948
+ if (claimRounds.length === 0) {
1949
+ throw new ContractCallError("claimRounds must not be empty");
1950
+ }
1951
+ try {
1952
+ return await this.walletClient.writeContract({
1953
+ address: this.address,
1954
+ abi: _SlvrAutoCommit.ABI,
1955
+ functionName: "claimFor",
1956
+ args: [userAddress, claimRounds],
1957
+ account: this.walletClient.account,
1958
+ chain: null
1959
+ });
1960
+ } catch (error) {
1961
+ if (error instanceof WalletClientRequiredError || error instanceof ContractCallError) {
1962
+ throw error;
1963
+ }
1964
+ throw new ContractCallError(`Failed to claim: ${error instanceof Error ? error.message : String(error)}`, error);
1965
+ }
1966
+ }
1967
+ validatePlanConfig(plays, amountPerPlay, squares, bpsAlloc) {
1968
+ if (!Number.isInteger(plays) || plays <= 0) {
1969
+ throw new ContractCallError("Plays must be a positive integer");
1970
+ }
1971
+ validateAmount(amountPerPlay, "amountPerPlay");
1972
+ validateSquares(squares);
1973
+ validateArrayLengths([squares, bpsAlloc], ["squares", "bpsAlloc"]);
1974
+ validateBpsSum(bpsAlloc, "bpsAlloc");
1975
+ }
1976
+ };
1977
+ _SlvrAutoCommit.ABI = parseAbi5([
1978
+ // View functions
1979
+ "function planInfo(address user) view returns (bool enabled, uint256 nextRoundId, uint32 playsRemaining, uint256 amountPerPlay, uint256 balance, bool autoClaim, uint8[] squares, uint16[] bpsAlloc, uint256 planStartRoundId)",
1980
+ "function needsExecution(address user) view returns (bool ready, string reason)",
1981
+ "function executedRounds(address user, uint256 roundId) view returns (bool)",
1982
+ "function LOTTERY() view returns (address)",
1983
+ "function UNLIMITED_PLAYS() view returns (uint32)",
1984
+ "function MAX_PLAYS_PER_EXECUTION() view returns (uint32)",
1985
+ "function MAX_CLAIMS_PER_EXECUTION() view returns (uint256)",
1986
+ "function maxFeePerExecution() view returns (uint256)",
1987
+ "function feePremiumBps() view returns (uint16)",
1988
+ // Write functions
1989
+ "function deposit() payable",
1990
+ "function withdraw(uint256 amount, address to)",
1991
+ "function configurePlan(uint32 plays, uint256 amountPerPlay, uint8[] squares, uint16[] bpsAlloc, bool autoClaim)",
1992
+ "function configurePlanAndDeposit(uint32 plays, uint256 amountPerPlay, uint8[] squares, uint16[] bpsAlloc, bool autoClaim) payable",
1993
+ "function disablePlan()",
1994
+ "function cancelPlan()",
1995
+ "function executeFor(address user, uint32 maxPlays, uint256[] claimRounds)",
1996
+ "function claimFor(address user, uint256[] claimRounds) returns (uint256 claimed)"
1997
+ ]);
1998
+ var SlvrAutoCommit = _SlvrAutoCommit;
1999
+
2000
+ // src/contracts/hub.ts
2001
+ import { parseAbi as parseAbi6 } from "viem";
2002
+ var _SlvrHub = class _SlvrHub {
2003
+ constructor(publicClient, _walletClient, address) {
2004
+ this.publicClient = publicClient;
2005
+ this.address = address;
2006
+ }
2007
+ // Read-only module; kept for a uniform module interface.
2008
+ setWalletClient(_walletClient) {
2009
+ }
2010
+ // ---------------------------------------------------------------------------
2011
+ // Views
2012
+ // ---------------------------------------------------------------------------
2013
+ /**
2014
+ * Accrued-but-unminted SLVR emission currently available to a game (its bucket + streamed since
2015
+ * last accrual, hard-capped at one maxAccrualSeconds window of the game's effective rate).
2016
+ */
2017
+ async pendingEmission(gameId) {
2018
+ return await this.publicClient.readContract({
2019
+ address: this.address,
2020
+ abi: _SlvrHub.ABI,
2021
+ functionName: "pendingEmission",
2022
+ args: [gameId]
2023
+ });
2024
+ }
2025
+ /**
2026
+ * Base SLVR/sec emission rate across the whole active game set
2027
+ */
2028
+ async emissionRatePerSec() {
2029
+ return await this.publicClient.readContract({
2030
+ address: this.address,
2031
+ abi: _SlvrHub.ABI,
2032
+ functionName: "emissionRatePerSec"
2033
+ });
2034
+ }
2035
+ /**
2036
+ * Soft-cap target supply (0 => use token MAX_SUPPLY)
2037
+ */
2038
+ async targetSupply() {
2039
+ return await this.publicClient.readContract({
2040
+ address: this.address,
2041
+ abi: _SlvrHub.ABI,
2042
+ functionName: "targetSupply"
2043
+ });
2044
+ }
2045
+ /**
2046
+ * Idle-forfeiture window: cap on dt per accrual (seconds)
2047
+ */
2048
+ async maxAccrualSeconds() {
2049
+ return await this.publicClient.readContract({
2050
+ address: this.address,
2051
+ abi: _SlvrHub.ABI,
2052
+ functionName: "maxAccrualSeconds"
2053
+ });
2054
+ }
2055
+ /**
2056
+ * Address of the shared veNFT staking contract
2057
+ */
2058
+ async staking() {
2059
+ return await this.publicClient.readContract({
2060
+ address: this.address,
2061
+ abi: _SlvrHub.ABI,
2062
+ functionName: "staking"
2063
+ });
2064
+ }
2065
+ /**
2066
+ * Address of the shared jackpot contract
2067
+ */
2068
+ async jackpot() {
2069
+ return await this.publicClient.readContract({
2070
+ address: this.address,
2071
+ abi: _SlvrHub.ABI,
2072
+ functionName: "jackpot"
2073
+ });
2074
+ }
2075
+ /**
2076
+ * Hub-owned sequential counter used to feed the staking contract's round numbering
2077
+ */
2078
+ async stakerSeq() {
2079
+ return await this.publicClient.readContract({
2080
+ address: this.address,
2081
+ abi: _SlvrHub.ABI,
2082
+ functionName: "stakerSeq"
2083
+ });
2084
+ }
2085
+ /**
2086
+ * Native staker rewards received but not yet flushed to the staking stream
2087
+ */
2088
+ async pendingStakerRewards() {
2089
+ return await this.publicClient.readContract({
2090
+ address: this.address,
2091
+ abi: _SlvrHub.ABI,
2092
+ functionName: "pendingStakerRewards"
2093
+ });
2094
+ }
2095
+ };
2096
+ _SlvrHub.ABI = parseAbi6([
2097
+ // View functions
2098
+ "function pendingEmission(uint256 gameId) view returns (uint256)",
2099
+ "function emissionRatePerSec() view returns (uint256)",
2100
+ "function targetSupply() view returns (uint256)",
2101
+ "function maxAccrualSeconds() view returns (uint256)",
2102
+ "function staking() view returns (address)",
2103
+ "function jackpot() view returns (address)",
2104
+ "function stakerSeq() view returns (uint256)",
2105
+ "function pendingStakerRewards() view returns (uint256)"
2106
+ ]);
2107
+ var SlvrHub = _SlvrHub;
2108
+
2109
+ // src/contracts/registry.ts
2110
+ import { parseAbi as parseAbi7 } from "viem";
2111
+ var _SlvrGameRegistry = class _SlvrGameRegistry {
2112
+ constructor(publicClient, _walletClient, address) {
2113
+ this.publicClient = publicClient;
2114
+ this.address = address;
2115
+ }
2116
+ /** Read-only contract: no wallet client needed. Kept for a consistent SDK surface. */
2117
+ setWalletClient(_walletClient) {
2118
+ }
2119
+ /**
2120
+ * Registry id for a game address (0 if not registered; ids are 1-based)
2121
+ */
2122
+ async gameIdOf(game) {
2123
+ return await this.publicClient.readContract({
2124
+ address: this.address,
2125
+ abi: _SlvrGameRegistry.ABI,
2126
+ functionName: "gameIdOf",
2127
+ args: [game]
2128
+ });
2129
+ }
2130
+ /**
2131
+ * Full record for a game id
2132
+ */
2133
+ async gameInfo(gameId) {
2134
+ const result = await this.publicClient.readContract({
2135
+ address: this.address,
2136
+ abi: _SlvrGameRegistry.ABI,
2137
+ functionName: "gameInfo",
2138
+ args: [gameId]
2139
+ });
2140
+ return {
2141
+ game: result.game,
2142
+ gameType: result.gameType,
2143
+ status: result.status,
2144
+ tier: result.tier,
2145
+ emissionWeight: Number(result.emissionWeight),
2146
+ maxWeightBps: Number(result.maxWeightBps),
2147
+ exists: result.exists
2148
+ };
2149
+ }
2150
+ /**
2151
+ * Is this address a registered, Active game?
2152
+ */
2153
+ async isActive(game) {
2154
+ return await this.publicClient.readContract({
2155
+ address: this.address,
2156
+ abi: _SlvrGameRegistry.ABI,
2157
+ functionName: "isActive",
2158
+ args: [game]
2159
+ });
2160
+ }
2161
+ /**
2162
+ * Status enum for a game id
2163
+ */
2164
+ async statusOf(gameId) {
2165
+ const result = await this.publicClient.readContract({
2166
+ address: this.address,
2167
+ abi: _SlvrGameRegistry.ABI,
2168
+ functionName: "statusOf",
2169
+ args: [gameId]
2170
+ });
2171
+ return result;
2172
+ }
2173
+ /**
2174
+ * Tier enum for a game id
2175
+ */
2176
+ async tierOf(gameId) {
2177
+ const result = await this.publicClient.readContract({
2178
+ address: this.address,
2179
+ abi: _SlvrGameRegistry.ABI,
2180
+ functionName: "tierOf",
2181
+ args: [gameId]
2182
+ });
2183
+ return result;
2184
+ }
2185
+ /**
2186
+ * Emission weight (relative share of the global stream) for a game id
2187
+ */
2188
+ async weightOf(gameId) {
2189
+ const result = await this.publicClient.readContract({
2190
+ address: this.address,
2191
+ abi: _SlvrGameRegistry.ABI,
2192
+ functionName: "weightOf",
2193
+ args: [gameId]
2194
+ });
2195
+ return BigInt(result);
2196
+ }
2197
+ /**
2198
+ * Max realized-fraction ceiling (bps of 10000) for a game id
2199
+ */
2200
+ async maxWeightBpsOf(gameId) {
2201
+ const result = await this.publicClient.readContract({
2202
+ address: this.address,
2203
+ abi: _SlvrGameRegistry.ABI,
2204
+ functionName: "maxWeightBpsOf",
2205
+ args: [gameId]
2206
+ });
2207
+ return Number(result);
2208
+ }
2209
+ /**
2210
+ * Sum of emissionWeight across all Active games (denominator for the split)
2211
+ */
2212
+ async totalActiveWeight() {
2213
+ return await this.publicClient.readContract({
2214
+ address: this.address,
2215
+ abi: _SlvrGameRegistry.ABI,
2216
+ functionName: "totalActiveWeight"
2217
+ });
2218
+ }
2219
+ /**
2220
+ * Number of registered games
2221
+ */
2222
+ async gameCount() {
2223
+ return await this.publicClient.readContract({
2224
+ address: this.address,
2225
+ abi: _SlvrGameRegistry.ABI,
2226
+ functionName: "gameCount"
2227
+ });
2228
+ }
2229
+ };
2230
+ _SlvrGameRegistry.ABI = parseAbi7([
2231
+ "function gameIdOf(address game) view returns (uint256)",
2232
+ "function gameInfo(uint256 gameId) view returns ((address game, bytes32 gameType, uint8 status, uint8 tier, uint32 emissionWeight, uint16 maxWeightBps, bool exists))",
2233
+ "function isActive(address game) view returns (bool)",
2234
+ "function statusOf(uint256 gameId) view returns (uint8)",
2235
+ "function tierOf(uint256 gameId) view returns (uint8)",
2236
+ "function weightOf(uint256 gameId) view returns (uint32)",
2237
+ "function maxWeightBpsOf(uint256 gameId) view returns (uint16)",
2238
+ "function totalActiveWeight() view returns (uint256)",
2239
+ "function gameCount() view returns (uint256)"
2240
+ ]);
2241
+ var SlvrGameRegistry = _SlvrGameRegistry;
2242
+
2243
+ // src/contracts/jackpot.ts
2244
+ import { parseAbi as parseAbi8 } from "viem";
2245
+ var _SlvrJackpot = class _SlvrJackpot {
2246
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2247
+ constructor(publicClient, _walletClient, address) {
2248
+ this.publicClient = publicClient;
2249
+ this.address = address;
2250
+ }
2251
+ /** Read-only contract: no wallet client needed. Kept for a consistent SDK surface. */
2252
+ setWalletClient(_walletClient) {
2253
+ }
2254
+ /**
2255
+ * Native (ETH) jackpot pool balance
2256
+ */
2257
+ async jackpotPool() {
2258
+ return await this.publicClient.readContract({
2259
+ address: this.address,
2260
+ abi: _SlvrJackpot.ABI,
2261
+ functionName: "jackpotPool"
2262
+ });
2263
+ }
2264
+ /**
2265
+ * SLVR jackpot pool balance
2266
+ */
2267
+ async jackpotSlvrPool() {
2268
+ return await this.publicClient.readContract({
2269
+ address: this.address,
2270
+ abi: _SlvrJackpot.ABI,
2271
+ functionName: "jackpotSlvrPool"
2272
+ });
2273
+ }
2274
+ };
2275
+ _SlvrJackpot.ABI = parseAbi8([
2276
+ "function jackpotPool() view returns (uint256)",
2277
+ "function jackpotSlvrPool() view returns (uint256)"
2278
+ ]);
2279
+ var SlvrJackpot = _SlvrJackpot;
2280
+
2281
+ // src/price.ts
2282
+ import { parseAbi as parseAbi9 } from "viem";
2283
+ var _SlvrPrice = class _SlvrPrice {
2284
+ constructor(publicClient, pairAddress, slvrTokenAddress) {
2285
+ this.publicClient = publicClient;
2286
+ this.pairAddress = pairAddress;
2287
+ this.slvrTokenAddress = slvrTokenAddress;
2288
+ }
2289
+ /** Read the pair reserves and resolve which side is SLVR. */
2290
+ async getReserves() {
2291
+ const [reserves, token0] = await Promise.all([
2292
+ this.publicClient.readContract({
2293
+ address: this.pairAddress,
2294
+ abi: _SlvrPrice.PAIR_ABI,
2295
+ functionName: "getReserves"
2296
+ }),
2297
+ this.publicClient.readContract({
2298
+ address: this.pairAddress,
2299
+ abi: _SlvrPrice.PAIR_ABI,
2300
+ functionName: "token0"
2301
+ })
2302
+ ]);
2303
+ const token0IsSlvr = token0.toLowerCase() === this.slvrTokenAddress.toLowerCase();
2304
+ const slvrReserve = token0IsSlvr ? reserves[0] : reserves[1];
2305
+ const ethReserve = token0IsSlvr ? reserves[1] : reserves[0];
2306
+ return { slvrReserve, ethReserve, token0IsSlvr };
2307
+ }
2308
+ /**
2309
+ * SLVR price in ETH (ETH per SLVR), as a floating-point number.
2310
+ * @throws {Error} if the pair has no SLVR liquidity (zero reserve).
2311
+ */
2312
+ async getPriceInEth() {
2313
+ const { slvrReserve, ethReserve } = await this.getReserves();
2314
+ if (slvrReserve === 0n) {
2315
+ throw new Error("SLVR/ETH pair has zero SLVR reserve; cannot price");
2316
+ }
2317
+ return Number(ethReserve) / Number(slvrReserve);
2318
+ }
2319
+ /**
2320
+ * SLVR price in ETH as a WAD (1e18-scaled `bigint`), for callers that want to
2321
+ * stay in integer math: `priceWad = ethReserve * 1e18 / slvrReserve`.
2322
+ * @throws {Error} if the pair has no SLVR liquidity (zero reserve).
2323
+ */
2324
+ async getPriceInEthWad() {
2325
+ const { slvrReserve, ethReserve } = await this.getReserves();
2326
+ if (slvrReserve === 0n) {
2327
+ throw new Error("SLVR/ETH pair has zero SLVR reserve; cannot price");
2328
+ }
2329
+ return ethReserve * 10n ** 18n / slvrReserve;
2330
+ }
2331
+ };
2332
+ _SlvrPrice.PAIR_ABI = parseAbi9([
2333
+ "function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)",
2334
+ "function token0() view returns (address)"
2335
+ ]);
2336
+ var SlvrPrice = _SlvrPrice;
2337
+
2338
+ // src/oracle.ts
2339
+ import { parseAbi as parseAbi10 } from "viem";
2340
+ var _ChainlinkPriceFeed = class _ChainlinkPriceFeed {
2341
+ /**
2342
+ * @param publicClient viem public client
2343
+ * @param feedAddress the aggregator address
2344
+ * @param opts.maxStalenessSec if set, {@link getPrice} throws when the feed's
2345
+ * `updatedAt` is older than this many seconds (checked against the local
2346
+ * clock). Off by default — enable it only where the local clock is trusted.
2347
+ */
2348
+ constructor(publicClient, feedAddress, opts) {
2349
+ this.publicClient = publicClient;
2350
+ this.feedAddress = feedAddress;
2351
+ this.maxStalenessSec = opts?.maxStalenessSec;
2352
+ }
2353
+ /** Raw `latestRoundData` answer plus the feed's decimals. */
2354
+ async getRoundData() {
2355
+ const [decimals, roundData] = await Promise.all([
2356
+ this.publicClient.readContract({
2357
+ address: this.feedAddress,
2358
+ abi: _ChainlinkPriceFeed.ABI,
2359
+ functionName: "decimals"
2360
+ }),
2361
+ this.publicClient.readContract({
2362
+ address: this.feedAddress,
2363
+ abi: _ChainlinkPriceFeed.ABI,
2364
+ functionName: "latestRoundData"
2365
+ })
2366
+ ]);
2367
+ return { answer: roundData[1], decimals, updatedAt: roundData[3] };
2368
+ }
2369
+ /**
2370
+ * The feed price as a floating-point number (`answer / 10 ** decimals`).
2371
+ * @throws {Error} if the answer is non-positive, or (when `maxStalenessSec` is
2372
+ * set) if the feed is stale.
2373
+ */
2374
+ async getPrice() {
2375
+ const { answer, decimals, updatedAt } = await this.getRoundData();
2376
+ if (answer <= 0n) {
2377
+ throw new Error(`Chainlink feed ${this.feedAddress} returned a non-positive answer`);
2378
+ }
2379
+ if (this.maxStalenessSec !== void 0) {
2380
+ const nowSec = Math.floor(Date.now() / 1e3);
2381
+ const age = nowSec - Number(updatedAt);
2382
+ if (age > this.maxStalenessSec) {
2383
+ throw new Error(
2384
+ `Chainlink feed ${this.feedAddress} is stale: ${age}s old (max ${this.maxStalenessSec}s)`
2385
+ );
2386
+ }
2387
+ }
2388
+ return Number(answer) / 10 ** decimals;
2389
+ }
2390
+ };
2391
+ _ChainlinkPriceFeed.ABI = parseAbi10([
2392
+ "function decimals() view returns (uint8)",
2393
+ "function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
2394
+ ]);
2395
+ var ChainlinkPriceFeed = _ChainlinkPriceFeed;
2396
+
2397
+ // src/ev.ts
2398
+ var GRID_SIZE = 25;
2399
+ var SINGLE_SQUARE_WIN_PROBABILITY = 1 / GRID_SIZE;
2400
+ var PROTOCOL_FEE_BPS = 1e3;
2401
+ var REFINING_FEE_BPS = 1e3;
2402
+ var JACKPOT_ODDS = 625;
2403
+ function computeGridMiningEv(input) {
2404
+ const {
2405
+ stake,
2406
+ pot,
2407
+ emissionPerRound,
2408
+ slvrPriceEth,
2409
+ feeBps = PROTOCOL_FEE_BPS,
2410
+ cashOut = false,
2411
+ refineFeeBps = REFINING_FEE_BPS,
2412
+ jackpotPool = 0,
2413
+ jackpotOdds = JACKPOT_ODDS
2414
+ } = input;
2415
+ if (!(stake > 0)) throw new Error(`stake must be positive (got ${stake})`);
2416
+ if (!(pot > 0)) throw new Error(`pot must be positive (got ${pot})`);
2417
+ const feeFraction = feeBps / 1e4;
2418
+ const realize = cashOut ? 1 - refineFeeBps / 1e4 : 1;
2419
+ const share = stake / pot;
2420
+ const ethBleed = feeFraction * stake;
2421
+ const slvrMined = share * emissionPerRound;
2422
+ const slvrValueEth = slvrMined * slvrPriceEth * realize;
2423
+ const jackpotEvEth = jackpotOdds > 0 && Number.isFinite(jackpotOdds) ? 1 / jackpotOdds * share * jackpotPool : 0;
2424
+ const netEthNoJackpot = slvrValueEth - ethBleed;
2425
+ const netEth = netEthNoJackpot + jackpotEvEth;
2426
+ const perShareEthReward = emissionPerRound * slvrPriceEth * realize + (jackpotOdds > 0 && Number.isFinite(jackpotOdds) ? jackpotPool / jackpotOdds : 0);
2427
+ const breakEvenPot = feeFraction > 0 ? perShareEthReward / feeFraction : Infinity;
2428
+ const slvrRewardDenom = share * emissionPerRound * realize;
2429
+ const breakEvenSlvrPriceEth = slvrRewardDenom > 0 ? (ethBleed - jackpotEvEth) / slvrRewardDenom : Infinity;
2430
+ return {
2431
+ share,
2432
+ ethBleed,
2433
+ slvrMined,
2434
+ slvrValueEth,
2435
+ jackpotEvEth,
2436
+ netEthNoJackpot,
2437
+ netEth,
2438
+ edgeRatio: netEth / stake,
2439
+ breakEvenPot,
2440
+ breakEvenSlvrPriceEth,
2441
+ profitable: netEth > 0
2442
+ };
2443
+ }
2444
+
2445
+ // src/connect.ts
2446
+ import {
2447
+ createPublicClient,
2448
+ createWalletClient,
2449
+ defineChain as defineChain2,
2450
+ http
2451
+ } from "viem";
2452
+ import { privateKeyToAccount } from "viem/accounts";
2453
+
2454
+ // src/deployments.ts
2455
+ import { defineChain } from "viem";
2456
+ var robinhood = {
2457
+ chainId: 4663,
2458
+ name: "Robinhood Chain",
2459
+ rpcUrl: "https://rpc.mainnet.chain.robinhood.com",
2460
+ blockExplorer: "https://robinhoodchain.blockscout.com/",
2461
+ subgraphUrl: "https://api.goldsky.com/api/public/project_cmre158qbffn101xe929tflsk/subgraphs/slvr-robinhood/1.2.0/gn",
2462
+ addresses: {
2463
+ lottery: "0x284Eb4016305Fa7FbC162Fb68F27227271001c7f",
2464
+ staking: "0xaF68598eBd245DC3cB92FF16E9Ba1814DD137200",
2465
+ token: "0x791229E3EbD6CFdC3D8157f48722684173C29aD9",
2466
+ autoCommit: "0x314c8D5755468224AC60c36FB5494F0D7D5Abb3B",
2467
+ voteEscrow: "0xd9b8FBD61033145c5496132153CE675756313B71",
2468
+ slvrEthPair: "0xe365b92239097Ed3322131411DbE15a5c4068eff",
2469
+ // Chainlink ETH/USD feed (standard AggregatorV3 proxy; "ETH / USD", 8 decimals).
2470
+ // Verified on-chain. There is also an SVR proxy at
2471
+ // 0x5058aDee53b04e374d8bEDbAD634Bc4778F50b22 with identical price data — that
2472
+ // one is for protocols integrating Chainlink SVR (OEV recapture); for plain
2473
+ // price reads the standard proxy below is the right choice.
2474
+ chainlinkEthUsd: "0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9",
2475
+ // Multicall3 at its canonical cross-chain address (verified deployed on Robinhood Chain).
2476
+ multicall3: "0xcA11bde05977b3631167028862bE2a173976CA11"
2477
+ }
2478
+ };
2479
+ var deployments = {
2480
+ robinhood
2481
+ };
2482
+ var robinhoodChain = defineChain({
2483
+ id: robinhood.chainId,
2484
+ name: robinhood.name,
2485
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2486
+ rpcUrls: { default: { http: [robinhood.rpcUrl] } },
2487
+ blockExplorers: {
2488
+ default: { name: "Blockscout", url: "https://robinhoodchain.blockscout.com" }
2489
+ },
2490
+ // Registering Multicall3 lets viem batch reads: the SDK's multi-square reads
2491
+ // become one RPC call, and clients created with `batch: { multicall: true }`
2492
+ // auto-batch every concurrent read.
2493
+ contracts: {
2494
+ multicall3: { address: robinhood.addresses.multicall3 }
2495
+ }
2496
+ });
2497
+
2498
+ // src/connect.ts
2499
+ function chainFromDeployment(deployment) {
2500
+ if (deployment.chainId === robinhood.chainId) return robinhoodChain;
2501
+ return defineChain2({
2502
+ id: deployment.chainId,
2503
+ name: deployment.name,
2504
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2505
+ rpcUrls: { default: { http: [deployment.rpcUrl] } },
2506
+ ...deployment.blockExplorer ? { blockExplorers: { default: { name: "Explorer", url: deployment.blockExplorer } } } : {},
2507
+ ...deployment.addresses.multicall3 ? { contracts: { multicall3: { address: deployment.addresses.multicall3 } } } : {}
2508
+ });
2509
+ }
2510
+ function createSlvrClients(opts = {}) {
2511
+ const deployment = opts.deployment ?? robinhood;
2512
+ const chain = chainFromDeployment(deployment);
2513
+ const transport = opts.transport ?? http(opts.rpcUrl ?? deployment.rpcUrl, { timeout: 2e4, retryCount: 3 });
2514
+ const publicClient = createPublicClient({
2515
+ chain,
2516
+ transport,
2517
+ batch: { multicall: opts.batchMulticall ?? true },
2518
+ ...opts.pollingInterval ? { pollingInterval: opts.pollingInterval } : {}
2519
+ });
2520
+ const account = opts.account ?? (opts.privateKey ? privateKeyToAccount(opts.privateKey) : void 0);
2521
+ const walletClient = account ? createWalletClient({ chain, transport, account }) : void 0;
2522
+ return { chain, publicClient, walletClient };
2523
+ }
2524
+
2525
+ // src/types.ts
2526
+ var GameStatus = /* @__PURE__ */ ((GameStatus2) => {
2527
+ GameStatus2[GameStatus2["Pending"] = 0] = "Pending";
2528
+ GameStatus2[GameStatus2["Active"] = 1] = "Active";
2529
+ GameStatus2[GameStatus2["Paused"] = 2] = "Paused";
2530
+ GameStatus2[GameStatus2["Retired"] = 3] = "Retired";
2531
+ return GameStatus2;
2532
+ })(GameStatus || {});
2533
+ var GameTier = /* @__PURE__ */ ((GameTier2) => {
2534
+ GameTier2[GameTier2["Core"] = 0] = "Core";
2535
+ GameTier2[GameTier2["Community"] = 1] = "Community";
2536
+ GameTier2[GameTier2["Experimental"] = 2] = "Experimental";
2537
+ return GameTier2;
2538
+ })(GameTier || {});
2539
+
2540
+ // src/transaction.ts
2541
+ async function estimateGas(publicClient, address, abi, functionName, args, value, account) {
2542
+ try {
2543
+ const estimateParams = {
2544
+ address,
2545
+ abi,
2546
+ functionName,
2547
+ args
2548
+ };
2549
+ if (value !== void 0) {
2550
+ estimateParams.value = value;
2551
+ }
2552
+ if (account !== void 0) {
2553
+ estimateParams.account = account;
2554
+ }
2555
+ const gas = await publicClient.estimateContractGas(estimateParams);
2556
+ return gas;
2557
+ } catch (error) {
2558
+ throw new TransactionError(
2559
+ `Failed to estimate gas for ${functionName}: ${error instanceof Error ? error.message : String(error)}`,
2560
+ void 0,
2561
+ error
2562
+ );
2563
+ }
2564
+ }
2565
+ async function waitForReceipt(publicClient, hash, timeout = 12e4) {
2566
+ try {
2567
+ const receipt = await publicClient.waitForTransactionReceipt({
2568
+ hash,
2569
+ timeout
2570
+ });
2571
+ if (receipt && typeof receipt === "object" && "status" in receipt) {
2572
+ if (receipt.status === "reverted") {
2573
+ throw new TransactionError(
2574
+ `Transaction reverted: ${hash}`,
2575
+ hash
2576
+ );
2577
+ }
2578
+ }
2579
+ return receipt;
2580
+ } catch (error) {
2581
+ if (error instanceof TransactionError) {
2582
+ throw error;
2583
+ }
2584
+ throw new TransactionError(
2585
+ `Failed to wait for transaction receipt: ${error instanceof Error ? error.message : String(error)}`,
2586
+ hash,
2587
+ error
2588
+ );
2589
+ }
2590
+ }
2591
+ async function executeTransaction(walletClient, publicClient, address, abi, functionName, args, options = {}) {
2592
+ const {
2593
+ gas,
2594
+ gasPrice,
2595
+ maxFeePerGas,
2596
+ maxPriorityFeePerGas,
2597
+ waitForReceipt: waitForReceipt_,
2598
+ receiptTimeout,
2599
+ value
2600
+ } = options;
2601
+ const writeParams = {
2602
+ address,
2603
+ abi,
2604
+ functionName,
2605
+ args,
2606
+ account: walletClient.account,
2607
+ chain: null,
2608
+ ...value !== void 0 && { value },
2609
+ ...gas !== void 0 && { gas },
2610
+ ...gasPrice !== void 0 && { gasPrice },
2611
+ ...maxFeePerGas !== void 0 && { maxFeePerGas },
2612
+ ...maxPriorityFeePerGas !== void 0 && { maxPriorityFeePerGas }
2613
+ };
2614
+ try {
2615
+ const hash = await walletClient.writeContract(writeParams);
2616
+ if (waitForReceipt_) {
2617
+ return await waitForReceipt(publicClient, hash, receiptTimeout);
2618
+ }
2619
+ return hash;
2620
+ } catch (error) {
2621
+ throw new TransactionError(
2622
+ `Failed to execute transaction: ${error instanceof Error ? error.message : String(error)}`,
2623
+ void 0,
2624
+ error
2625
+ );
2626
+ }
2627
+ }
2628
+
2629
+ // src/index.ts
2630
+ var SlvrSDK = class _SlvrSDK {
2631
+ constructor(config) {
2632
+ this.config = config;
2633
+ this.lottery = new SlvrGridLottery(
2634
+ config.publicClient,
2635
+ config.walletClient,
2636
+ config.addresses.lottery
2637
+ );
2638
+ this.staking = new SlvrStaking(
2639
+ config.publicClient,
2640
+ config.walletClient,
2641
+ config.addresses.staking
2642
+ );
2643
+ this.token = new SlvrToken(
2644
+ config.publicClient,
2645
+ config.walletClient,
2646
+ config.addresses.token
2647
+ );
2648
+ if (config.addresses.autoCommit) {
2649
+ this.autoCommit = new SlvrAutoCommit(
2650
+ config.publicClient,
2651
+ config.walletClient,
2652
+ config.addresses.autoCommit
2653
+ );
2654
+ }
2655
+ if (config.addresses.hub) {
2656
+ this.hub = new SlvrHub(
2657
+ config.publicClient,
2658
+ config.walletClient,
2659
+ config.addresses.hub
2660
+ );
2661
+ }
2662
+ if (config.addresses.registry) {
2663
+ this.registry = new SlvrGameRegistry(
2664
+ config.publicClient,
2665
+ config.walletClient,
2666
+ config.addresses.registry
2667
+ );
2668
+ }
2669
+ if (config.addresses.jackpot) {
2670
+ this.jackpot = new SlvrJackpot(
2671
+ config.publicClient,
2672
+ config.walletClient,
2673
+ config.addresses.jackpot
2674
+ );
2675
+ }
2676
+ if (config.addresses.slvrEthPair) {
2677
+ this.price = new SlvrPrice(
2678
+ config.publicClient,
2679
+ config.addresses.slvrEthPair,
2680
+ config.addresses.token
2681
+ );
2682
+ }
2683
+ if (config.addresses.chainlinkEthUsd) {
2684
+ this.ethUsd = new ChainlinkPriceFeed(config.publicClient, config.addresses.chainlinkEthUsd);
2685
+ }
2686
+ }
2687
+ /**
2688
+ * One-line setup: build clients (with Multicall3 batching + resilient transport
2689
+ * defaults) and an SDK for a deployment. Pass a `privateKey`/`account` to enable
2690
+ * writes; omit it for a read-only SDK.
2691
+ *
2692
+ * @example
2693
+ * ```typescript
2694
+ * import { SlvrSDK } from '@slvr-labs/sdk';
2695
+ * const sdk = SlvrSDK.connect(); // read-only, Robinhood Chain
2696
+ * const bot = SlvrSDK.connect({ privateKey: process.env.PK }); // wallet-backed
2697
+ * ```
2698
+ */
2699
+ static connect(opts = {}) {
2700
+ const deployment = opts.deployment ?? robinhood;
2701
+ const { publicClient, walletClient } = createSlvrClients(opts);
2702
+ return new _SlvrSDK({ publicClient, walletClient, addresses: deployment.addresses });
2703
+ }
2704
+ /**
2705
+ * Get the public client
2706
+ */
2707
+ getPublicClient() {
2708
+ return this.config.publicClient;
2709
+ }
2710
+ /**
2711
+ * Get the wallet client (if available)
2712
+ */
2713
+ getWalletClient() {
2714
+ return this.config.walletClient;
2715
+ }
2716
+ /**
2717
+ * Update the wallet client and reinitialize contracts
2718
+ * @param walletClient New wallet client (or undefined to remove)
2719
+ */
2720
+ setWalletClient(walletClient) {
2721
+ this.config.walletClient = walletClient;
2722
+ this.lottery.setWalletClient(walletClient);
2723
+ this.staking.setWalletClient(walletClient);
2724
+ this.token.setWalletClient(walletClient);
2725
+ if (this.autoCommit) {
2726
+ this.autoCommit.setWalletClient(walletClient);
2727
+ }
2728
+ if (this.hub) {
2729
+ this.hub.setWalletClient(walletClient);
2730
+ }
2731
+ if (this.registry) {
2732
+ this.registry.setWalletClient(walletClient);
2733
+ }
2734
+ if (this.jackpot) {
2735
+ this.jackpot.setWalletClient(walletClient);
2736
+ }
2737
+ }
2738
+ /**
2739
+ * Helper: Calculate bet amounts from percentages
2740
+ * @param totalAmount Total amount to bet
2741
+ * @param percentages Array of percentages (0-100) for each square
2742
+ * @returns Array of amounts in wei
2743
+ * @throws ValidationError if percentages don't sum to 100 or if totalAmount is invalid
2744
+ */
2745
+ static calculateBetAmounts(totalAmount, percentages) {
2746
+ if (percentages.length === 0) {
2747
+ throw new ValidationError("Percentages array cannot be empty", "percentages");
2748
+ }
2749
+ const totalPercent = percentages.reduce((sum, p) => sum + p, 0);
2750
+ if (Math.abs(totalPercent - 100) > 0.01) {
2751
+ throw new ValidationError(`Percentages must sum to 100 (got ${totalPercent})`, "percentages");
2752
+ }
2753
+ const amounts = [];
2754
+ let allocated = 0n;
2755
+ for (let i = 0; i < percentages.length; i++) {
2756
+ const percentage = percentages[i];
2757
+ if (percentage === void 0) continue;
2758
+ const amount = totalAmount * BigInt(Math.round(percentage * 100)) / 10000n;
2759
+ amounts.push(amount);
2760
+ allocated += amount;
2761
+ }
2762
+ if (allocated !== totalAmount && amounts.length > 0 && amounts[0] !== void 0) {
2763
+ amounts[0] += totalAmount - allocated;
2764
+ }
2765
+ return amounts;
2766
+ }
2767
+ /**
2768
+ * Helper: Format bigint to human-readable string.
2769
+ *
2770
+ * `precision` caps the number of decimal places shown; trailing zeros are
2771
+ * stripped (like viem's `formatEther`), so `formatToken(1.5e18)` is `"1.5"`,
2772
+ * not `"1.5000"`.
2773
+ * @param value Value in wei
2774
+ * @param decimals Number of decimals (default 18)
2775
+ * @param precision Max decimal places to show (default 4)
2776
+ */
2777
+ static formatToken(value, decimals = 18, precision = 4) {
2778
+ const divisor = BigInt(10 ** decimals);
2779
+ const whole = value / divisor;
2780
+ const remainder = value % divisor;
2781
+ const fractional = remainder * BigInt(10 ** precision) / divisor;
2782
+ if (fractional === 0n) {
2783
+ return whole.toString();
2784
+ }
2785
+ const fractionalStr = fractional.toString().padStart(precision, "0").replace(/0+$/, "");
2786
+ return fractionalStr ? `${whole}.${fractionalStr}` : whole.toString();
2787
+ }
2788
+ /**
2789
+ * Helper: Parse human-readable string to bigint
2790
+ * @param value Human-readable value (e.g., "1.5")
2791
+ * @param decimals Number of decimals (default 18)
2792
+ */
2793
+ static parseToken(value, decimals = 18) {
2794
+ const parts = value.split(".");
2795
+ const whole = BigInt(parts[0] || "0");
2796
+ const fractional = parts[1] ? BigInt(parts[1].padEnd(decimals, "0").slice(0, decimals)) : 0n;
2797
+ return whole * BigInt(10 ** decimals) + fractional;
2798
+ }
2799
+ /**
2800
+ * Helper: Calculate time remaining until round ends
2801
+ * @param roundId Round ID
2802
+ * @returns Time remaining in seconds, or 0 if round has ended
2803
+ */
2804
+ async getTimeRemaining(roundId) {
2805
+ const [roundEnd, block] = await Promise.all([
2806
+ this.lottery.roundEnd(roundId),
2807
+ this.config.publicClient.getBlock()
2808
+ ]);
2809
+ const remaining = roundEnd - block.timestamp;
2810
+ return remaining > 0n ? Number(remaining) : 0;
2811
+ }
2812
+ /**
2813
+ * Helper: Check if user can claim rewards for a round
2814
+ * @param roundId Round ID
2815
+ * @param user User address
2816
+ * @returns True if user can claim
2817
+ */
2818
+ async canClaim(roundId, user) {
2819
+ const [round, hasClaimed_] = await Promise.all([
2820
+ this.lottery.getRound(roundId),
2821
+ this.lottery.getHasClaimed(roundId, user)
2822
+ ]);
2823
+ if (!round.resolved || hasClaimed_) {
2824
+ return false;
2825
+ }
2826
+ const userBet = await this.lottery.getUserBet(roundId, round.winningSquare, user);
2827
+ return userBet > 0n;
2828
+ }
2829
+ /**
2830
+ * Helper: Get user's claimable rounds
2831
+ * @param user User address
2832
+ * @param startRoundId Start round ID to check from
2833
+ * @param endRoundId End round ID to check to
2834
+ * @returns Array of round IDs that can be claimed
2835
+ */
2836
+ async getClaimableRounds(user, startRoundId, endRoundId) {
2837
+ const rounds = [];
2838
+ for (let roundId = startRoundId; roundId <= endRoundId; roundId++) {
2839
+ rounds.push(roundId);
2840
+ }
2841
+ const flags = await Promise.all(rounds.map((roundId) => this.canClaim(roundId, user)));
2842
+ return rounds.filter((_, i) => flags[i]);
2843
+ }
2844
+ /**
2845
+ * Helper: Compute a game's effective SLVR/sec emission rate.
2846
+ *
2847
+ * Mirrors SlvrHub._effectiveRatePerSec: the game's weighted share of the global emission stream,
2848
+ * i.e. emissionRatePerSec * weightOf(gameId) / totalActiveWeight. NOTE: this does not apply the
2849
+ * per-game maxWeightBps ceiling that the contract also enforces; it is the pre-cap weighted rate.
2850
+ *
2851
+ * Requires both `hub` and `registry` addresses to be configured.
2852
+ * @param gameId Registry game id
2853
+ * @returns Effective emission rate in SLVR/sec (0 if there is no active weight)
2854
+ */
2855
+ async effectiveEmissionRate(gameId) {
2856
+ if (!this.hub) {
2857
+ throw new ValidationError("hub address is required for effectiveEmissionRate", "hub");
2858
+ }
2859
+ if (!this.registry) {
2860
+ throw new ValidationError("registry address is required for effectiveEmissionRate", "registry");
2861
+ }
2862
+ const [rate, weight, totalWeight] = await Promise.all([
2863
+ this.hub.emissionRatePerSec(),
2864
+ this.registry.weightOf(gameId),
2865
+ this.registry.totalActiveWeight()
2866
+ ]);
2867
+ if (totalWeight === 0n) {
2868
+ return 0n;
2869
+ }
2870
+ return rate * weight / totalWeight;
2871
+ }
2872
+ /**
2873
+ * Helper: Get the accrued-but-unminted SLVR emission currently available to a game.
2874
+ *
2875
+ * Thin pass-through to SlvrHub.pendingEmission. Requires `hub` to be configured.
2876
+ * @param gameId Registry game id
2877
+ */
2878
+ async pendingEmission(gameId) {
2879
+ if (!this.hub) {
2880
+ throw new ValidationError("hub address is required for pendingEmission", "hub");
2881
+ }
2882
+ return await this.hub.pendingEmission(gameId);
2883
+ }
2884
+ /**
2885
+ * Helper: current SLVR spot price in ETH (ETH per SLVR).
2886
+ *
2887
+ * Requires a `slvrEthPair` address to be configured (so `sdk.price` exists).
2888
+ * @throws ValidationError if no pair address was configured.
2889
+ */
2890
+ async getSlvrPriceInEth() {
2891
+ if (!this.price) {
2892
+ throw new ValidationError("slvrEthPair address is required for getSlvrPriceInEth", "slvrEthPair");
2893
+ }
2894
+ return await this.price.getPriceInEth();
2895
+ }
2896
+ /**
2897
+ * Helper: current ETH price in USD from the configured Chainlink feed.
2898
+ *
2899
+ * Requires a `chainlinkEthUsd` address (so `sdk.ethUsd` exists). This is wired
2900
+ * for Robinhood Chain in `deployments.robinhood`; on chains without a feed,
2901
+ * supply one or pass `ethUsd` to {@link getSlvrPrice}.
2902
+ * @throws ValidationError if no ETH/USD feed was configured.
2903
+ */
2904
+ async getEthPriceUsd() {
2905
+ if (!this.ethUsd) {
2906
+ throw new ValidationError("chainlinkEthUsd address is required for getEthPriceUsd", "chainlinkEthUsd");
2907
+ }
2908
+ return await this.ethUsd.getPrice();
2909
+ }
2910
+ /**
2911
+ * Helper: current SLVR price in **both ETH and USD**.
2912
+ *
2913
+ * SLVR/ETH comes from the UniswapV2 pair (`sdk.price`). The USD value uses, in
2914
+ * order: an explicit `opts.ethUsd`, else the configured Chainlink ETH/USD feed
2915
+ * (`sdk.ethUsd`), else `usd` is `null`.
2916
+ *
2917
+ * @param opts.ethUsd override the ETH/USD price (e.g. from your own off-chain source)
2918
+ * @throws ValidationError if no `slvrEthPair` was configured.
2919
+ *
2920
+ * @example
2921
+ * ```typescript
2922
+ * const { eth, usd } = await sdk.getSlvrPrice(); // uses Chainlink feed if configured
2923
+ * const q = await sdk.getSlvrPrice({ ethUsd: 1797.35 }); // or supply ETH/USD yourself
2924
+ * ```
2925
+ */
2926
+ async getSlvrPrice(opts) {
2927
+ if (!this.price) {
2928
+ throw new ValidationError("slvrEthPair address is required for getSlvrPrice", "slvrEthPair");
2929
+ }
2930
+ const eth = await this.price.getPriceInEth();
2931
+ let ethUsd = opts?.ethUsd;
2932
+ if (ethUsd === void 0 && this.ethUsd) {
2933
+ ethUsd = await this.ethUsd.getPrice();
2934
+ }
2935
+ return { eth, usd: ethUsd !== void 0 ? eth * ethUsd : null };
2936
+ }
2937
+ /**
2938
+ * Helper: estimate the per-round expected value of grid mining for the given
2939
+ * stake, pulling live pot, emission and SLVR price on-chain.
2940
+ *
2941
+ * This is the SDK-level convenience around {@link computeGridMiningEv}: it reads
2942
+ * the round's pot (sum of all squares), the emission target (`slvrPerRound`), the
2943
+ * protocol fee (`protocolFeeBps`), and the SLVR price (via `sdk.price`), then
2944
+ * returns the full EV breakdown. Any of those can be overridden via `params`.
2945
+ *
2946
+ * The jackpot pool is **not** auto-read (it isn't exposed as a single call on the
2947
+ * live lottery) — pass `jackpotPool` if you want the jackpot term included.
2948
+ *
2949
+ * @remarks `slvrPerRound` is the emission *target*; actual emission is hub-gated
2950
+ * and may be lower, so treat the result as an upper-ish estimate. Requires a
2951
+ * configured `slvrEthPair` unless you pass `slvrPriceEth`.
2952
+ */
2953
+ async estimateRoundEv(params) {
2954
+ const roundId = params.roundId ?? await this.lottery.currentRoundId();
2955
+ const [potEth, emissionPerRound, slvrPriceEth, feeBps, jackpotPoolEth] = await Promise.all([
2956
+ params.pot !== void 0 ? Promise.resolve(params.pot) : this.lottery.getRoundSquares(roundId).then((sqs) => Number(formatEther(sqs.reduce((sum, s) => sum + s.total, 0n)))),
2957
+ params.emissionPerRound !== void 0 ? Promise.resolve(params.emissionPerRound) : this.lottery.slvrPerRound().then((v) => Number(formatEther(v))),
2958
+ params.slvrPriceEth !== void 0 ? Promise.resolve(params.slvrPriceEth) : this.getSlvrPriceInEth(),
2959
+ this.lottery.protocolFeeBps(),
2960
+ // Auto-read the jackpot pool (0 if no jackpot is set for the round / read fails).
2961
+ params.jackpotPool !== void 0 ? Promise.resolve(params.jackpotPool) : this.getJackpotPool(roundId).then((v) => Number(formatEther(v))).catch(() => 0)
2962
+ ]);
2963
+ return computeGridMiningEv({
2964
+ stake: params.stake,
2965
+ pot: potEth,
2966
+ emissionPerRound,
2967
+ slvrPriceEth,
2968
+ feeBps,
2969
+ cashOut: params.cashOut,
2970
+ jackpotPool: jackpotPoolEth,
2971
+ jackpotOdds: params.jackpotOdds
2972
+ });
2973
+ }
2974
+ /**
2975
+ * Helper: current jackpot pool for a round, in wei (0 if no jackpot is set).
2976
+ *
2977
+ * Resolves the round's jackpot contract via `getRoundJackpot` and reads its
2978
+ * `jackpotPool()`. Defaults to the current round.
2979
+ */
2980
+ async getJackpotPool(roundId) {
2981
+ const id = roundId ?? await this.lottery.currentRoundId();
2982
+ const jackpotAddress = await this.lottery.getRoundJackpot(id);
2983
+ if (jackpotAddress === "0x0000000000000000000000000000000000000000") return 0n;
2984
+ return await new SlvrJackpot(this.config.publicClient, void 0, jackpotAddress).jackpotPool();
2985
+ }
2986
+ };
2987
+ export {
2988
+ ChainlinkPriceFeed,
2989
+ ContractCallError,
2990
+ GRID_SIZE,
2991
+ GameStatus,
2992
+ GameTier,
2993
+ JACKPOT_ODDS,
2994
+ PROTOCOL_FEE_BPS,
2995
+ REFINING_FEE_BPS,
2996
+ SINGLE_SQUARE_WIN_PROBABILITY,
2997
+ SlvrAutoCommit,
2998
+ SlvrAutoCommitEvents,
2999
+ SlvrGameRegistry,
3000
+ SlvrGridLottery,
3001
+ SlvrGridLotteryEvents,
3002
+ SlvrHub,
3003
+ SlvrJackpot,
3004
+ SlvrPrice,
3005
+ SlvrRevertError,
3006
+ SlvrSDK,
3007
+ SlvrSDKError,
3008
+ SlvrStaking,
3009
+ SlvrStakingEvents,
3010
+ SlvrToken,
3011
+ SlvrTokenEvents,
3012
+ TransactionError,
3013
+ ValidationError,
3014
+ WalletClientRequiredError,
3015
+ chainFromDeployment,
3016
+ computeGridMiningEv,
3017
+ createSlvrClients,
3018
+ decodeEvent,
3019
+ decodeEvents,
3020
+ decodeSlvrRevert,
3021
+ deployments,
3022
+ estimateGas,
3023
+ executeTransaction,
3024
+ robinhood,
3025
+ robinhoodChain,
3026
+ validateAddress,
3027
+ validateAmount,
3028
+ validateArrayLengths,
3029
+ validateBps,
3030
+ validateBpsSum,
3031
+ validateSquares,
3032
+ waitForReceipt,
3033
+ waitForTransactionReceipt
3034
+ };
3035
+ //# sourceMappingURL=index.mjs.map