@provable-games/budokan-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.
package/dist/react.js ADDED
@@ -0,0 +1,2760 @@
1
+ import { createContext, useMemo, useRef, useEffect, useContext, useState, useCallback } from 'react';
2
+ import { num } from 'starknet';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/react/context.tsx
6
+
7
+ // src/errors/index.ts
8
+ var BudokanError = class extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "BudokanError";
12
+ }
13
+ };
14
+ var BudokanApiError = class extends BudokanError {
15
+ status;
16
+ statusText;
17
+ constructor(message, status, statusText = "") {
18
+ super(message);
19
+ this.name = "BudokanApiError";
20
+ this.status = status;
21
+ this.statusText = statusText;
22
+ }
23
+ };
24
+ var BudokanTimeoutError = class extends BudokanError {
25
+ constructor(message = "Request timed out") {
26
+ super(message);
27
+ this.name = "BudokanTimeoutError";
28
+ }
29
+ };
30
+ var BudokanConnectionError = class extends BudokanError {
31
+ constructor(message = "Connection failed") {
32
+ super(message);
33
+ this.name = "BudokanConnectionError";
34
+ }
35
+ };
36
+ var TournamentNotFoundError = class extends BudokanError {
37
+ tournamentId;
38
+ constructor(tournamentId) {
39
+ super(`Tournament not found: ${tournamentId}`);
40
+ this.name = "TournamentNotFoundError";
41
+ this.tournamentId = tournamentId;
42
+ }
43
+ };
44
+ var RpcError = class extends BudokanError {
45
+ contractAddress;
46
+ constructor(message, contractAddress) {
47
+ super(message);
48
+ this.name = "RpcError";
49
+ this.contractAddress = contractAddress;
50
+ }
51
+ };
52
+ var DataSourceError = class extends BudokanError {
53
+ primaryError;
54
+ fallbackError;
55
+ constructor(primaryError, fallbackError) {
56
+ super(`Both data sources failed. Primary: ${primaryError.message}. Fallback: ${fallbackError.message}`);
57
+ this.name = "DataSourceError";
58
+ this.primaryError = primaryError;
59
+ this.fallbackError = fallbackError;
60
+ }
61
+ };
62
+ function isNonRetryableError(error) {
63
+ if (error instanceof TournamentNotFoundError) return true;
64
+ if (error instanceof BudokanApiError && error.status >= 400 && error.status < 500 && error.status !== 429) {
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+
70
+ // src/utils/retry.ts
71
+ function calculateBackoff(attempt, baseDelay, maxDelay) {
72
+ let delay = baseDelay * Math.pow(2, attempt);
73
+ if (delay > maxDelay) delay = maxDelay;
74
+ const minDelay = delay / 2;
75
+ const jitter = Math.random() * (delay - minDelay);
76
+ return minDelay + jitter;
77
+ }
78
+ function sleep(ms) {
79
+ return new Promise((resolve) => setTimeout(resolve, ms));
80
+ }
81
+ async function withRetry(fn, attempts = 3, delay = 1e3) {
82
+ let lastError = null;
83
+ for (let attempt = 0; attempt < attempts; attempt++) {
84
+ try {
85
+ return await fn();
86
+ } catch (error) {
87
+ lastError = error;
88
+ if (isNonRetryableError(error)) {
89
+ throw error;
90
+ }
91
+ if (attempt === attempts - 1) break;
92
+ const backoff = calculateBackoff(attempt, delay, delay * 8);
93
+ await sleep(backoff);
94
+ }
95
+ }
96
+ throw lastError ?? new BudokanTimeoutError("Unknown error after retries");
97
+ }
98
+
99
+ // src/api/base.ts
100
+ var DEFAULT_TIMEOUT = 1e4;
101
+ var DEFAULT_RETRY_ATTEMPTS = 3;
102
+ var DEFAULT_RETRY_DELAY = 1e3;
103
+ async function apiFetch(url, options = {}) {
104
+ const {
105
+ method = "GET",
106
+ headers = {},
107
+ body,
108
+ signal,
109
+ timeout = DEFAULT_TIMEOUT,
110
+ retryAttempts = DEFAULT_RETRY_ATTEMPTS,
111
+ retryDelay = DEFAULT_RETRY_DELAY
112
+ } = options;
113
+ return withRetry(
114
+ async () => {
115
+ const controller = new AbortController();
116
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
117
+ if (signal) {
118
+ if (signal.aborted) {
119
+ clearTimeout(timeoutId);
120
+ throw new BudokanTimeoutError("Request was aborted");
121
+ }
122
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
123
+ }
124
+ try {
125
+ const response = await fetch(url, {
126
+ method,
127
+ headers: {
128
+ "Content-Type": "application/json",
129
+ ...headers
130
+ },
131
+ body: body ? JSON.stringify(body) : void 0,
132
+ signal: controller.signal
133
+ });
134
+ clearTimeout(timeoutId);
135
+ if (!response.ok) {
136
+ const errorBody = await response.json().catch(() => ({}));
137
+ throw new BudokanApiError(
138
+ errorBody.error ?? `API error: ${response.status}`,
139
+ response.status,
140
+ response.statusText
141
+ );
142
+ }
143
+ return await response.json();
144
+ } catch (error) {
145
+ clearTimeout(timeoutId);
146
+ if (error instanceof BudokanApiError) {
147
+ throw error;
148
+ }
149
+ if (error instanceof Error && error.name === "AbortError") {
150
+ if (signal?.aborted) throw new BudokanTimeoutError("Request was aborted");
151
+ throw new BudokanTimeoutError();
152
+ }
153
+ throw new BudokanConnectionError(
154
+ error instanceof Error ? error.message : "Connection failed"
155
+ );
156
+ }
157
+ },
158
+ retryAttempts,
159
+ retryDelay
160
+ );
161
+ }
162
+ function extractPagination(result, defaults) {
163
+ const pagination = result.pagination;
164
+ return {
165
+ total: pagination?.total ?? result.total,
166
+ limit: pagination?.limit ?? result.limit ?? defaults?.limit ?? 50,
167
+ offset: pagination?.offset ?? result.offset ?? defaults?.offset ?? 0
168
+ };
169
+ }
170
+ function buildQueryString(params) {
171
+ const qs = new URLSearchParams();
172
+ for (const [key, value] of Object.entries(params)) {
173
+ if (value !== void 0 && value !== null) {
174
+ qs.set(key, String(value));
175
+ }
176
+ }
177
+ const str = qs.toString();
178
+ return str ? `?${str}` : "";
179
+ }
180
+
181
+ // src/utils/mappers.ts
182
+ function toCamelCase(str) {
183
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
184
+ }
185
+ function snakeToCamel(obj) {
186
+ if (Array.isArray(obj)) {
187
+ return obj.map((item) => snakeToCamel(item));
188
+ }
189
+ if (obj !== null && typeof obj === "object" && !(obj instanceof Date)) {
190
+ const result = {};
191
+ for (const [key, value] of Object.entries(obj)) {
192
+ result[toCamelCase(key)] = snakeToCamel(value);
193
+ }
194
+ return result;
195
+ }
196
+ return obj;
197
+ }
198
+
199
+ // src/api/tournaments.ts
200
+ function normalizeTournament(raw) {
201
+ const t = snakeToCamel(raw);
202
+ if (t.id && !t.tournamentId) {
203
+ t.tournamentId = t.id;
204
+ } else if (t.tournamentId && !t.id) {
205
+ t.id = t.tournamentId;
206
+ }
207
+ return t;
208
+ }
209
+ function fetchOpts(ctx) {
210
+ return {
211
+ retryAttempts: ctx?.retryAttempts,
212
+ retryDelay: ctx?.retryDelay,
213
+ timeout: ctx?.timeout
214
+ };
215
+ }
216
+ async function getTournaments(baseUrl, params, ctx) {
217
+ const qs = buildQueryString({
218
+ game_address: params?.gameAddress,
219
+ creator: params?.creator,
220
+ phase: params?.phase,
221
+ limit: params?.limit,
222
+ offset: params?.offset,
223
+ sort: params?.sort,
224
+ from_id: params?.fromId,
225
+ exclude_ids: params?.excludeIds?.join(","),
226
+ whitelisted_extensions: params?.whitelistedExtensions?.join(","),
227
+ include_prizes: params?.includePrizeSummary
228
+ });
229
+ const result = await apiFetch(`${baseUrl}/tournaments${qs}`, fetchOpts(ctx));
230
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
231
+ return {
232
+ data: result.data.map((item) => normalizeTournament(item)),
233
+ total,
234
+ limit: resLimit,
235
+ offset: resOffset
236
+ };
237
+ }
238
+ async function getTournament(baseUrl, tournamentId, ctx) {
239
+ const result = await apiFetch(
240
+ `${baseUrl}/tournaments/${tournamentId}`,
241
+ fetchOpts(ctx)
242
+ );
243
+ return normalizeTournament(result.data);
244
+ }
245
+ async function getTournamentLeaderboard(baseUrl, tournamentId, ctx) {
246
+ const result = await apiFetch(
247
+ `${baseUrl}/tournaments/${tournamentId}/leaderboard`,
248
+ fetchOpts(ctx)
249
+ );
250
+ return result.data.map((item) => snakeToCamel(item));
251
+ }
252
+ async function getTournamentRegistrations(baseUrl, tournamentId, params, ctx) {
253
+ const qs = buildQueryString({
254
+ limit: params?.limit,
255
+ offset: params?.offset
256
+ });
257
+ const result = await apiFetch(`${baseUrl}/tournaments/${tournamentId}/registrations${qs}`, fetchOpts(ctx));
258
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
259
+ return {
260
+ data: result.data.map((item) => snakeToCamel(item)),
261
+ total,
262
+ limit: resLimit,
263
+ offset: resOffset
264
+ };
265
+ }
266
+ async function getTournamentPrizes(baseUrl, tournamentId, ctx) {
267
+ const result = await apiFetch(
268
+ `${baseUrl}/tournaments/${tournamentId}/prizes`,
269
+ fetchOpts(ctx)
270
+ );
271
+ return result.data.map((item) => snakeToCamel(item));
272
+ }
273
+ async function getTournamentRewardClaims(baseUrl, tournamentId, params, ctx) {
274
+ const qs = buildQueryString({
275
+ limit: params?.limit,
276
+ offset: params?.offset
277
+ });
278
+ const result = await apiFetch(`${baseUrl}/tournaments/${tournamentId}/reward-claims${qs}`, fetchOpts(ctx));
279
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
280
+ return {
281
+ data: result.data.map((item) => snakeToCamel(item)),
282
+ total,
283
+ limit: resLimit,
284
+ offset: resOffset
285
+ };
286
+ }
287
+ async function getTournamentRewardClaimsSummary(baseUrl, tournamentId, ctx) {
288
+ const result = await apiFetch(
289
+ `${baseUrl}/tournaments/${tournamentId}/reward-claims/summary`,
290
+ fetchOpts(ctx)
291
+ );
292
+ return snakeToCamel(result.data);
293
+ }
294
+ async function getTournamentQualifications(baseUrl, tournamentId, params, ctx) {
295
+ const qs = buildQueryString({
296
+ limit: params?.limit,
297
+ offset: params?.offset
298
+ });
299
+ const result = await apiFetch(`${baseUrl}/tournaments/${tournamentId}/qualifications${qs}`, fetchOpts(ctx));
300
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
301
+ return {
302
+ data: result.data.map((item) => snakeToCamel(item)),
303
+ total,
304
+ limit: resLimit,
305
+ offset: resOffset
306
+ };
307
+ }
308
+ async function getTournamentPrizeAggregation(baseUrl, tournamentId, ctx) {
309
+ const qs = buildQueryString({ include_aggregation: true });
310
+ const result = await apiFetch(
311
+ `${baseUrl}/tournaments/${tournamentId}/prizes${qs}`,
312
+ fetchOpts(ctx)
313
+ );
314
+ return result.data.map((item) => snakeToCamel(item));
315
+ }
316
+
317
+ // src/utils/address.ts
318
+ function normalizeAddress(address) {
319
+ const stripped = address.replace(/^0x0*/i, "");
320
+ return ("0x" + stripped.padStart(64, "0")).toLowerCase();
321
+ }
322
+
323
+ // src/api/players.ts
324
+ function fetchOpts2(ctx) {
325
+ return {
326
+ retryAttempts: ctx?.retryAttempts,
327
+ retryDelay: ctx?.retryDelay,
328
+ timeout: ctx?.timeout
329
+ };
330
+ }
331
+ async function getPlayerTournaments(baseUrl, address, params, ctx) {
332
+ const normalized = normalizeAddress(address);
333
+ const qs = buildQueryString({
334
+ limit: params?.limit,
335
+ offset: params?.offset,
336
+ phase: params?.phase,
337
+ game_token_ids: params?.gameTokenIds?.join(",")
338
+ });
339
+ const result = await apiFetch(`${baseUrl}/players/${normalized}/tournaments${qs}`, fetchOpts2(ctx));
340
+ return {
341
+ data: result.data.map((item) => snakeToCamel(item)),
342
+ total: result.pagination?.total ?? result.total,
343
+ limit: result.pagination?.limit ?? result.limit,
344
+ offset: result.pagination?.offset ?? result.offset
345
+ };
346
+ }
347
+ async function getPlayerStats(baseUrl, address, ctx) {
348
+ const normalized = normalizeAddress(address);
349
+ const result = await apiFetch(
350
+ `${baseUrl}/players/${normalized}/stats`,
351
+ fetchOpts2(ctx)
352
+ );
353
+ return snakeToCamel(result.data);
354
+ }
355
+
356
+ // src/api/games.ts
357
+ function fetchOpts3(ctx) {
358
+ return {
359
+ retryAttempts: ctx?.retryAttempts,
360
+ retryDelay: ctx?.retryDelay,
361
+ timeout: ctx?.timeout
362
+ };
363
+ }
364
+ async function getGameTournaments(baseUrl, gameAddress, params, ctx) {
365
+ const normalized = normalizeAddress(gameAddress);
366
+ const qs = buildQueryString({
367
+ creator: params?.creator,
368
+ phase: params?.phase,
369
+ limit: params?.limit,
370
+ offset: params?.offset
371
+ });
372
+ const result = await apiFetch(`${baseUrl}/games/${normalized}/tournaments${qs}`, fetchOpts3(ctx));
373
+ return {
374
+ data: result.data.map((item) => snakeToCamel(item)),
375
+ total: result.pagination?.total ?? result.total,
376
+ limit: result.pagination?.limit ?? result.limit,
377
+ offset: result.pagination?.offset ?? result.offset
378
+ };
379
+ }
380
+ async function getGameStats(baseUrl, gameAddress, ctx) {
381
+ const normalized = normalizeAddress(gameAddress);
382
+ const result = await apiFetch(
383
+ `${baseUrl}/games/${normalized}/stats`,
384
+ fetchOpts3(ctx)
385
+ );
386
+ return snakeToCamel(result.data);
387
+ }
388
+
389
+ // src/api/activity.ts
390
+ function fetchOpts4(ctx) {
391
+ return {
392
+ retryAttempts: ctx?.retryAttempts,
393
+ retryDelay: ctx?.retryDelay,
394
+ timeout: ctx?.timeout
395
+ };
396
+ }
397
+ async function getActivity(baseUrl, params, ctx) {
398
+ const qs = buildQueryString({
399
+ event_type: params?.eventType,
400
+ tournament_id: params?.tournamentId,
401
+ player_address: params?.playerAddress,
402
+ limit: params?.limit,
403
+ offset: params?.offset
404
+ });
405
+ const result = await apiFetch(`${baseUrl}/activity${qs}`, fetchOpts4(ctx));
406
+ return {
407
+ data: result.data.map((item) => snakeToCamel(item)),
408
+ total: result.pagination?.total ?? result.total,
409
+ limit: result.pagination?.limit ?? result.limit,
410
+ offset: result.pagination?.offset ?? result.offset
411
+ };
412
+ }
413
+ async function getActivityStats(baseUrl, ctx) {
414
+ const result = await apiFetch(
415
+ `${baseUrl}/activity/stats`,
416
+ fetchOpts4(ctx)
417
+ );
418
+ return snakeToCamel(result.data);
419
+ }
420
+ async function getPrizeStats(baseUrl, ctx) {
421
+ const result = await apiFetch(
422
+ `${baseUrl}/activity/prize-stats`,
423
+ fetchOpts4(ctx)
424
+ );
425
+ return snakeToCamel(result.data);
426
+ }
427
+
428
+ // src/ws/manager.ts
429
+ var DEFAULT_WS_CONFIG = {
430
+ maxReconnectAttempts: 10,
431
+ reconnectBaseDelay: 1e3
432
+ };
433
+ var WSManager = class {
434
+ ws = null;
435
+ wsUrl;
436
+ config;
437
+ reconnectAttempts = 0;
438
+ reconnectTimeout = null;
439
+ subscriptions = /* @__PURE__ */ new Map();
440
+ nextSubId = 1;
441
+ connected = false;
442
+ connectionListeners = /* @__PURE__ */ new Set();
443
+ constructor(wsUrl, config) {
444
+ this.wsUrl = wsUrl;
445
+ this.config = { ...DEFAULT_WS_CONFIG, ...config };
446
+ }
447
+ /**
448
+ * Open a WebSocket connection. No-op if already connected.
449
+ */
450
+ connect() {
451
+ if (this.ws) return;
452
+ try {
453
+ this.ws = new WebSocket(this.wsUrl);
454
+ this.ws.onopen = () => {
455
+ this.connected = true;
456
+ this.reconnectAttempts = 0;
457
+ this.notifyConnectionChange(true);
458
+ for (const [, sub] of this.subscriptions) {
459
+ this.sendSubscribe(sub.options);
460
+ }
461
+ };
462
+ this.ws.onmessage = (event) => {
463
+ try {
464
+ const message = JSON.parse(event.data);
465
+ if (message.type === "event") {
466
+ for (const [, sub] of this.subscriptions) {
467
+ sub.handler(message);
468
+ }
469
+ }
470
+ } catch {
471
+ }
472
+ };
473
+ this.ws.onclose = () => {
474
+ this.connected = false;
475
+ this.notifyConnectionChange(false);
476
+ this.ws = null;
477
+ this.attemptReconnect();
478
+ };
479
+ this.ws.onerror = () => {
480
+ };
481
+ } catch {
482
+ this.attemptReconnect();
483
+ }
484
+ }
485
+ /**
486
+ * Close the WebSocket connection and stop reconnecting.
487
+ */
488
+ disconnect() {
489
+ if (this.reconnectTimeout) {
490
+ clearTimeout(this.reconnectTimeout);
491
+ this.reconnectTimeout = null;
492
+ }
493
+ if (this.ws) {
494
+ this.ws.onclose = null;
495
+ this.ws.close();
496
+ this.ws = null;
497
+ }
498
+ this.connected = false;
499
+ this.notifyConnectionChange(false);
500
+ this.reconnectAttempts = 0;
501
+ }
502
+ /**
503
+ * Subscribe to channels with an optional tournament filter.
504
+ * Returns an unsubscribe function.
505
+ */
506
+ subscribe(options, handler) {
507
+ const id = String(this.nextSubId++);
508
+ this.subscriptions.set(id, { options, handler });
509
+ if (this.connected) {
510
+ this.sendSubscribe(options);
511
+ }
512
+ return () => {
513
+ this.subscriptions.delete(id);
514
+ if (this.connected && this.ws) {
515
+ this.ws.send(JSON.stringify({
516
+ type: "unsubscribe",
517
+ channels: options.channels
518
+ }));
519
+ }
520
+ };
521
+ }
522
+ /**
523
+ * Register a callback for a single message. Convenience wrapper around subscribe.
524
+ * Returns an unsubscribe function.
525
+ */
526
+ onMessage(callback) {
527
+ const id = String(this.nextSubId++);
528
+ this.subscriptions.set(id, {
529
+ options: { channels: [] },
530
+ handler: callback
531
+ });
532
+ return () => {
533
+ this.subscriptions.delete(id);
534
+ };
535
+ }
536
+ /**
537
+ * Whether the WebSocket is currently connected.
538
+ */
539
+ get isConnected() {
540
+ return this.connected;
541
+ }
542
+ /**
543
+ * Register a listener for connection state changes.
544
+ * Returns an unsubscribe function.
545
+ */
546
+ onConnectionChange(listener) {
547
+ this.connectionListeners.add(listener);
548
+ return () => {
549
+ this.connectionListeners.delete(listener);
550
+ };
551
+ }
552
+ notifyConnectionChange(isConnected) {
553
+ for (const listener of this.connectionListeners) {
554
+ try {
555
+ listener(isConnected);
556
+ } catch {
557
+ }
558
+ }
559
+ }
560
+ sendSubscribe(options) {
561
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
562
+ if (options.channels.length === 0) return;
563
+ this.ws.send(JSON.stringify({
564
+ type: "subscribe",
565
+ channels: options.channels,
566
+ tournamentIds: options.tournamentIds
567
+ }));
568
+ }
569
+ attemptReconnect() {
570
+ if (this.reconnectAttempts >= this.config.maxReconnectAttempts) return;
571
+ if (this.subscriptions.size === 0) return;
572
+ const delay = this.config.reconnectBaseDelay * Math.pow(2, this.reconnectAttempts);
573
+ this.reconnectAttempts++;
574
+ this.reconnectTimeout = setTimeout(() => {
575
+ this.connect();
576
+ }, Math.min(delay, 3e4));
577
+ }
578
+ };
579
+
580
+ // src/chains/constants.ts
581
+ var CHAINS = {
582
+ mainnet: {
583
+ rpcUrl: "https://api.cartridge.gg/x/starknet/mainnet/rpc/v0_10",
584
+ apiBaseUrl: "https://budokan-api-production.up.railway.app",
585
+ wsUrl: "wss://budokan-api-production.up.railway.app/ws",
586
+ budokanAddress: "",
587
+ // TODO: set after mainnet deployment
588
+ viewerAddress: ""
589
+ // TODO: set after mainnet deployment
590
+ },
591
+ sepolia: {
592
+ rpcUrl: "https://starknet-sepolia.public.blastapi.io",
593
+ apiBaseUrl: "https://budokan-api-sepolia.up.railway.app",
594
+ wsUrl: "wss://budokan-api-sepolia.up.railway.app/ws",
595
+ budokanAddress: "",
596
+ // TODO: set after sepolia deployment
597
+ viewerAddress: ""
598
+ // TODO: set after sepolia deployment
599
+ }
600
+ };
601
+ function getChainConfig(chain) {
602
+ return CHAINS[chain];
603
+ }
604
+
605
+ // src/datasource/health.ts
606
+ var ConnectionStatus = class {
607
+ status = {
608
+ api: { available: true, lastChecked: 0, latency: null, error: null },
609
+ rpc: { available: true, lastChecked: 0, latency: null, error: null },
610
+ mode: "api",
611
+ initialCheckComplete: false
612
+ };
613
+ listeners = /* @__PURE__ */ new Set();
614
+ checkInterval = null;
615
+ initialCheckTimeout = null;
616
+ apiUrl;
617
+ rpcUrl;
618
+ initialCheckDelay;
619
+ checkIntervalMs;
620
+ checkTimeoutMs;
621
+ constructor(apiUrl, rpcUrl, config) {
622
+ this.apiUrl = apiUrl;
623
+ this.rpcUrl = rpcUrl;
624
+ this.initialCheckDelay = Math.max(config?.initialCheckDelay ?? 1e3, 100);
625
+ this.checkIntervalMs = Math.max(config?.checkInterval ?? 3e4, 1e3);
626
+ this.checkTimeoutMs = Math.max(config?.checkTimeout ?? 5e3, 1e3);
627
+ }
628
+ getStatus() {
629
+ return { ...this.status };
630
+ }
631
+ get mode() {
632
+ return this.status.mode;
633
+ }
634
+ subscribe(listener) {
635
+ this.listeners.add(listener);
636
+ listener(this.status);
637
+ return () => {
638
+ this.listeners.delete(listener);
639
+ };
640
+ }
641
+ startMonitoring() {
642
+ if (this.checkInterval) return;
643
+ this.initialCheckTimeout = setTimeout(() => {
644
+ this.performHealthCheck();
645
+ this.checkInterval = setInterval(() => this.performHealthCheck(), this.checkIntervalMs);
646
+ }, this.initialCheckDelay);
647
+ }
648
+ stopMonitoring() {
649
+ if (this.checkInterval) {
650
+ clearInterval(this.checkInterval);
651
+ this.checkInterval = null;
652
+ }
653
+ if (this.initialCheckTimeout) {
654
+ clearTimeout(this.initialCheckTimeout);
655
+ this.initialCheckTimeout = null;
656
+ }
657
+ }
658
+ markApiUnavailable(error) {
659
+ this.updateStatus({
660
+ api: { ...this.status.api, available: false, lastChecked: Date.now(), error: error || "Request failed" }
661
+ });
662
+ }
663
+ markRpcUnavailable(error) {
664
+ this.updateStatus({
665
+ rpc: { ...this.status.rpc, available: false, lastChecked: Date.now(), error: error || "Request failed" }
666
+ });
667
+ }
668
+ async checkNow() {
669
+ await this.performHealthCheck();
670
+ }
671
+ destroy() {
672
+ this.stopMonitoring();
673
+ this.listeners.clear();
674
+ }
675
+ async performHealthCheck() {
676
+ const [apiResult, rpcResult] = await Promise.all([
677
+ this.checkApi(),
678
+ this.checkRpc()
679
+ ]);
680
+ this.updateStatus({ api: apiResult, rpc: rpcResult, initialCheckComplete: true });
681
+ }
682
+ async checkApi() {
683
+ const startTime = Date.now();
684
+ try {
685
+ const controller = new AbortController();
686
+ const timeoutId = setTimeout(() => controller.abort(), this.checkTimeoutMs);
687
+ const response = await fetch(`${this.apiUrl}/health`, { signal: controller.signal });
688
+ clearTimeout(timeoutId);
689
+ const latency = Date.now() - startTime;
690
+ if (!response.ok) {
691
+ return { available: false, lastChecked: Date.now(), latency, error: `HTTP ${response.status}` };
692
+ }
693
+ const data = await response.json();
694
+ const isAvailable = data.status === "healthy" || data.status === "degraded";
695
+ return { available: isAvailable, lastChecked: Date.now(), latency, error: isAvailable ? null : `API status: ${data.status}` };
696
+ } catch (error) {
697
+ return { available: false, lastChecked: Date.now(), latency: null, error: error instanceof Error ? error.message : "Network error" };
698
+ }
699
+ }
700
+ async checkRpc() {
701
+ const startTime = Date.now();
702
+ try {
703
+ const controller = new AbortController();
704
+ const timeoutId = setTimeout(() => controller.abort(), this.checkTimeoutMs);
705
+ const response = await fetch(this.rpcUrl, {
706
+ method: "POST",
707
+ headers: { "Content-Type": "application/json" },
708
+ body: JSON.stringify({ jsonrpc: "2.0", method: "starknet_chainId", params: [], id: 1 }),
709
+ signal: controller.signal
710
+ });
711
+ clearTimeout(timeoutId);
712
+ const latency = Date.now() - startTime;
713
+ if (!response.ok) {
714
+ return { available: false, lastChecked: Date.now(), latency, error: `HTTP ${response.status}` };
715
+ }
716
+ const data = await response.json();
717
+ const isHealthy = data.result !== void 0 && !data.error;
718
+ return { available: isHealthy, lastChecked: Date.now(), latency, error: isHealthy ? null : "RPC error" };
719
+ } catch (error) {
720
+ return { available: false, lastChecked: Date.now(), latency: null, error: error instanceof Error ? error.message : "Network error" };
721
+ }
722
+ }
723
+ updateStatus(partial) {
724
+ const newStatus = { ...this.status, ...partial };
725
+ if (newStatus.api.available) {
726
+ newStatus.mode = "api";
727
+ } else if (newStatus.rpc.available) {
728
+ newStatus.mode = "rpc-fallback";
729
+ } else {
730
+ newStatus.mode = "offline";
731
+ }
732
+ const changed = this.status.api.available !== newStatus.api.available || this.status.rpc.available !== newStatus.rpc.available || this.status.mode !== newStatus.mode || this.status.initialCheckComplete !== newStatus.initialCheckComplete;
733
+ this.status = newStatus;
734
+ if (changed) {
735
+ this.listeners.forEach((listener) => {
736
+ try {
737
+ listener(this.status);
738
+ } catch {
739
+ }
740
+ });
741
+ }
742
+ }
743
+ };
744
+
745
+ // src/datasource/resolver.ts
746
+ async function withFallback(primary, fallback, health) {
747
+ if (health) {
748
+ const mode = health.mode;
749
+ if (mode === "rpc-fallback") {
750
+ try {
751
+ return await fallback();
752
+ } catch (fallbackError) {
753
+ throw fallbackError;
754
+ }
755
+ }
756
+ }
757
+ let primaryError;
758
+ try {
759
+ return await primary();
760
+ } catch (error) {
761
+ primaryError = error;
762
+ if (health) {
763
+ health.markApiUnavailable(primaryError.message);
764
+ }
765
+ }
766
+ try {
767
+ return await fallback();
768
+ } catch (fallbackError) {
769
+ if (health) {
770
+ health.markRpcUnavailable(fallbackError.message);
771
+ }
772
+ throw new DataSourceError(primaryError, fallbackError);
773
+ }
774
+ }
775
+
776
+ // src/rpc/provider.ts
777
+ var starknetModule = null;
778
+ var useObjectConstructor = null;
779
+ async function getStarknet() {
780
+ if (!starknetModule) {
781
+ starknetModule = await import('starknet');
782
+ }
783
+ return starknetModule;
784
+ }
785
+ async function createProvider(rpcUrl) {
786
+ const { RpcProvider: StarknetRpcProvider } = await getStarknet();
787
+ const provider = new StarknetRpcProvider({ nodeUrl: rpcUrl });
788
+ return provider;
789
+ }
790
+ async function createContract(abi, address, provider) {
791
+ let resolvedAbi = abi;
792
+ if (abi && !Array.isArray(abi) && typeof abi === "object" && "default" in abi) {
793
+ resolvedAbi = abi.default;
794
+ }
795
+ const starknet = await getStarknet();
796
+ const StarknetContract = starknet.Contract;
797
+ if (useObjectConstructor === null) {
798
+ try {
799
+ const contract = new StarknetContract({
800
+ abi: resolvedAbi,
801
+ address,
802
+ providerOrAccount: provider
803
+ });
804
+ useObjectConstructor = true;
805
+ return contract;
806
+ } catch {
807
+ useObjectConstructor = false;
808
+ return new StarknetContract(resolvedAbi, address, provider);
809
+ }
810
+ }
811
+ if (useObjectConstructor) {
812
+ return new StarknetContract({
813
+ abi: resolvedAbi,
814
+ address,
815
+ providerOrAccount: provider
816
+ });
817
+ } else {
818
+ return new StarknetContract(resolvedAbi, address, provider);
819
+ }
820
+ }
821
+ function wrapRpcCall(fn, contractAddress) {
822
+ return fn().catch((error) => {
823
+ throw new RpcError(
824
+ error instanceof Error ? error.message : "RPC call failed",
825
+ contractAddress
826
+ );
827
+ });
828
+ }
829
+ function decodeShortString(value) {
830
+ if (!value) return "";
831
+ const hex = num.toHex(value);
832
+ if (hex === "0x0") return "";
833
+ const hexStr = hex.slice(2);
834
+ let result = "";
835
+ for (let i = 0; i < hexStr.length; i += 2) {
836
+ const charCode = parseInt(hexStr.slice(i, i + 2), 16);
837
+ if (charCode === 0) break;
838
+ result += String.fromCharCode(charCode);
839
+ }
840
+ return result;
841
+ }
842
+ function decodeByteArray(value) {
843
+ if (!value) return "";
844
+ const obj = value;
845
+ const data = obj.data;
846
+ const pendingWord = obj.pending_word;
847
+ const pendingWordLen = Number(obj.pending_word_len ?? 0);
848
+ let result = "";
849
+ if (data) {
850
+ for (const chunk of data) {
851
+ const hex = num.toHex(chunk).slice(2).padStart(62, "0");
852
+ for (let i = 0; i < 62; i += 2) {
853
+ const charCode = parseInt(hex.slice(i, i + 2), 16);
854
+ if (charCode !== 0) result += String.fromCharCode(charCode);
855
+ }
856
+ }
857
+ }
858
+ if (pendingWord && pendingWordLen > 0) {
859
+ const hex = num.toHex(pendingWord).slice(2).padStart(pendingWordLen * 2, "0");
860
+ for (let i = 0; i < pendingWordLen * 2; i += 2) {
861
+ const charCode = parseInt(hex.slice(i, i + 2), 16);
862
+ if (charCode !== 0) result += String.fromCharCode(charCode);
863
+ }
864
+ }
865
+ return result;
866
+ }
867
+ function phaseToRpcArg(phase) {
868
+ const map = {
869
+ scheduled: "Scheduled",
870
+ registration: "Registration",
871
+ staging: "Staging",
872
+ live: "Live",
873
+ submission: "Submission",
874
+ finalized: "Finalized"
875
+ };
876
+ return { [map[phase]]: {} };
877
+ }
878
+ function parseTournament(raw, entryCount) {
879
+ const obj = raw;
880
+ const id = String(obj.id ?? "0");
881
+ const createdAt = Number(obj.created_at ?? 0);
882
+ const createdBy = num.toHex(obj.created_by);
883
+ const creatorTokenId = obj.creator_token_id ? num.toHex(obj.creator_token_id) : null;
884
+ const metadata = obj.metadata;
885
+ const name = metadata ? decodeShortString(metadata.name) : "";
886
+ const description = metadata ? decodeByteArray(metadata.description) : "";
887
+ const sched = obj.schedule;
888
+ const registrationStartDelay = Number(sched?.registration_start_delay ?? 0);
889
+ const registrationEndDelay = Number(sched?.registration_end_delay ?? 0);
890
+ const gameStartDelay = Number(sched?.game_start_delay ?? 0);
891
+ const gameEndDelay = Number(sched?.game_end_delay ?? 0);
892
+ const submissionDuration = Number(sched?.submission_duration ?? 0);
893
+ const createdAtStr = String(createdAt);
894
+ const registrationStartTime = String(createdAt + registrationStartDelay);
895
+ const registrationEndTime = String(createdAt + registrationEndDelay);
896
+ const gameStartTime = String(createdAt + gameStartDelay);
897
+ const gameEndTime = String(createdAt + gameEndDelay);
898
+ const submissionEndTime = String(createdAt + gameEndDelay + submissionDuration);
899
+ const gc = obj.game_config;
900
+ const gameAddress = gc ? num.toHex(gc.game_address) : "";
901
+ const settingsId = Number(gc?.settings_id ?? 0);
902
+ const soulbound = Boolean(gc?.soulbound);
903
+ const paymaster = Boolean(gc?.paymaster);
904
+ const clientUrl = gc?.client_url ? decodeOptionalByteArray(gc.client_url) : null;
905
+ const renderer = gc?.renderer ? parseOptionalAddress(gc.renderer) : null;
906
+ const lc = obj.leaderboard_config;
907
+ const ascending = Boolean(lc?.ascending);
908
+ const gameMustBeOver = Boolean(lc?.game_must_be_over);
909
+ const entryFeeRaw = parseOption(obj.entry_fee);
910
+ let entryFeeToken = null;
911
+ let entryFeeAmount = null;
912
+ let entryFee = null;
913
+ if (entryFeeRaw) {
914
+ const ef = entryFeeRaw;
915
+ entryFeeToken = num.toHex(ef.token_address);
916
+ entryFeeAmount = String(ef.amount ?? "0");
917
+ entryFee = {
918
+ tokenAddress: entryFeeToken,
919
+ amount: entryFeeAmount,
920
+ tournamentCreatorShare: Number(ef.tournament_creator_share ?? 0),
921
+ gameCreatorShare: Number(ef.game_creator_share ?? 0),
922
+ refundShare: Number(ef.refund_share ?? 0),
923
+ distribution: ef.distribution ?? null,
924
+ distributionCount: Number(ef.distribution_count ?? 0)
925
+ };
926
+ }
927
+ const entryRequirement = parseOption(obj.entry_requirement) ?? null;
928
+ const hasEntryRequirement = entryRequirement !== null;
929
+ return {
930
+ id,
931
+ tournamentId: id,
932
+ gameAddress,
933
+ createdAt: new Date(createdAt * 1e3).toISOString(),
934
+ createdBy,
935
+ creatorTokenId,
936
+ name,
937
+ description,
938
+ registrationStartDelay,
939
+ registrationEndDelay,
940
+ gameStartDelay,
941
+ gameEndDelay,
942
+ submissionDuration,
943
+ createdAtOnchain: createdAtStr,
944
+ registrationStartTime,
945
+ registrationEndTime,
946
+ gameStartTime,
947
+ gameEndTime,
948
+ submissionEndTime,
949
+ settingsId,
950
+ soulbound,
951
+ paymaster,
952
+ clientUrl,
953
+ renderer,
954
+ leaderboardAscending: ascending,
955
+ leaderboardGameMustBeOver: gameMustBeOver,
956
+ entryFeeToken,
957
+ entryFeeAmount,
958
+ hasEntryRequirement,
959
+ schedule: {
960
+ registrationStartDelay,
961
+ registrationEndDelay,
962
+ gameStartDelay,
963
+ gameEndDelay,
964
+ submissionDuration
965
+ },
966
+ gameConfig: {
967
+ gameAddress,
968
+ settingsId,
969
+ soulbound,
970
+ paymaster,
971
+ clientUrl,
972
+ renderer
973
+ },
974
+ entryFee,
975
+ entryRequirement,
976
+ leaderboardConfig: { ascending, gameMustBeOver },
977
+ entryCount,
978
+ prizeCount: 0,
979
+ // Not available from viewer
980
+ submissionCount: 0,
981
+ // Not available from viewer
982
+ metadata: null
983
+ };
984
+ }
985
+ function parseLeaderboardEntry(raw) {
986
+ const obj = raw;
987
+ return {
988
+ position: Number(obj.position ?? 0),
989
+ tokenId: num.toHex(obj.token_id)
990
+ };
991
+ }
992
+ function parseRegistration(raw, tournamentId) {
993
+ const obj = raw;
994
+ return {
995
+ tournamentId,
996
+ gameTokenId: num.toHex(obj.game_token_id),
997
+ gameAddress: "",
998
+ // Not in on-chain struct
999
+ playerAddress: "",
1000
+ // Not in on-chain struct
1001
+ entryNumber: Number(obj.entry_id ?? 0),
1002
+ hasSubmitted: Boolean(obj.has_submitted),
1003
+ isBanned: Boolean(obj.is_banned)
1004
+ };
1005
+ }
1006
+ function parsePrize(raw) {
1007
+ const obj = raw;
1008
+ const tokenTypeData = obj.token_type;
1009
+ let tokenType = "erc20";
1010
+ let amount = null;
1011
+ let tokenId = null;
1012
+ let distributionType = null;
1013
+ let distributionWeight = null;
1014
+ let distributionCount = null;
1015
+ let payoutPosition = 0;
1016
+ if (tokenTypeData) {
1017
+ if ("erc20" in tokenTypeData) {
1018
+ const erc20 = tokenTypeData.erc20;
1019
+ tokenType = "erc20";
1020
+ amount = String(erc20.amount ?? "0");
1021
+ const dist = erc20.distribution;
1022
+ if (dist) {
1023
+ distributionType = String(dist.type ?? null);
1024
+ distributionWeight = dist.weight != null ? Number(dist.weight) : null;
1025
+ }
1026
+ distributionCount = erc20.distribution_count != null ? Number(erc20.distribution_count) : null;
1027
+ } else if ("erc721" in tokenTypeData) {
1028
+ const erc721 = tokenTypeData.erc721;
1029
+ tokenType = "erc721";
1030
+ tokenId = String(erc721.id ?? "0");
1031
+ } else if ("variant" in tokenTypeData) {
1032
+ const variant = tokenTypeData.variant?.toLowerCase();
1033
+ if (variant === "erc20") {
1034
+ tokenType = "erc20";
1035
+ amount = String(tokenTypeData.amount ?? "0");
1036
+ const dist = tokenTypeData.distribution;
1037
+ if (dist) {
1038
+ distributionType = String(dist.type ?? null);
1039
+ distributionWeight = dist.weight != null ? Number(dist.weight) : null;
1040
+ }
1041
+ distributionCount = tokenTypeData.distribution_count != null ? Number(tokenTypeData.distribution_count) : null;
1042
+ } else if (variant === "erc721") {
1043
+ tokenType = "erc721";
1044
+ tokenId = String(tokenTypeData.id ?? "0");
1045
+ }
1046
+ }
1047
+ }
1048
+ return {
1049
+ prizeId: String(obj.id ?? "0"),
1050
+ tournamentId: String(obj.context_id ?? "0"),
1051
+ payoutPosition,
1052
+ tokenAddress: num.toHex(obj.token_address),
1053
+ tokenType,
1054
+ amount,
1055
+ tokenId,
1056
+ distributionType,
1057
+ distributionWeight,
1058
+ distributionCount,
1059
+ sponsorAddress: num.toHex(obj.sponsor_address)
1060
+ };
1061
+ }
1062
+ function parseOption(raw) {
1063
+ if (raw === void 0 || raw === null) return null;
1064
+ const obj = raw;
1065
+ if ("Some" in obj) return obj.Some;
1066
+ if ("None" in obj) return null;
1067
+ return raw;
1068
+ }
1069
+ function decodeOptionalByteArray(raw) {
1070
+ const inner = parseOption(raw);
1071
+ if (!inner) return null;
1072
+ return decodeByteArray(inner);
1073
+ }
1074
+ function parseOptionalAddress(raw) {
1075
+ const inner = parseOption(raw);
1076
+ if (!inner) return null;
1077
+ const hex = num.toHex(inner);
1078
+ return hex === "0x0" ? null : hex;
1079
+ }
1080
+ function parseFilterResult(raw) {
1081
+ const obj = raw;
1082
+ const ids = obj.tournament_ids?.map((v) => String(v)) ?? [];
1083
+ const total = Number(obj.total ?? 0);
1084
+ return { tournamentIds: ids, total };
1085
+ }
1086
+ function parseTournamentFullState(raw) {
1087
+ const obj = raw;
1088
+ const entryCount = Number(obj.entry_count ?? 0);
1089
+ return parseTournament(obj.tournament, entryCount);
1090
+ }
1091
+ async function viewerTournaments(contract, offset, limit) {
1092
+ return wrapRpcCall(async () => {
1093
+ const result = await contract.call("tournaments", [offset, limit]);
1094
+ return parseFilterResult(result);
1095
+ }, contract.address);
1096
+ }
1097
+ async function viewerTournamentsByGame(contract, gameAddress, offset, limit) {
1098
+ return wrapRpcCall(async () => {
1099
+ const result = await contract.call("tournaments_by_game", [gameAddress, offset, limit]);
1100
+ return parseFilterResult(result);
1101
+ }, contract.address);
1102
+ }
1103
+ async function viewerTournamentsByCreator(contract, creator, offset, limit) {
1104
+ return wrapRpcCall(async () => {
1105
+ const result = await contract.call("tournaments_by_creator", [creator, offset, limit]);
1106
+ return parseFilterResult(result);
1107
+ }, contract.address);
1108
+ }
1109
+ async function viewerTournamentsByPhase(contract, phase, offset, limit) {
1110
+ return wrapRpcCall(async () => {
1111
+ const result = await contract.call("tournaments_by_phase", [phaseToRpcArg(phase), offset, limit]);
1112
+ return parseFilterResult(result);
1113
+ }, contract.address);
1114
+ }
1115
+ async function viewerTournamentDetail(contract, tournamentId) {
1116
+ return wrapRpcCall(async () => {
1117
+ const result = await contract.call("tournament_detail", [tournamentId]);
1118
+ return parseTournamentFullState(result);
1119
+ }, contract.address);
1120
+ }
1121
+ async function viewerTournamentsBatch(contract, tournamentIds) {
1122
+ return wrapRpcCall(async () => {
1123
+ const result = await contract.call("tournaments_batch", [tournamentIds]);
1124
+ return result.map(parseTournamentFullState);
1125
+ }, contract.address);
1126
+ }
1127
+ async function viewerRegistrations(contract, tournamentId, offset, limit) {
1128
+ return wrapRpcCall(async () => {
1129
+ const result = await contract.call("tournament_registrations", [tournamentId, offset, limit]);
1130
+ const obj = result;
1131
+ const entries = obj.entries ?? [];
1132
+ const total = Number(obj.total ?? 0);
1133
+ return {
1134
+ data: entries.map((e) => parseRegistration(e, tournamentId)),
1135
+ total,
1136
+ limit,
1137
+ offset
1138
+ };
1139
+ }, contract.address);
1140
+ }
1141
+ async function viewerLeaderboard(contract, tournamentId, offset, limit) {
1142
+ return wrapRpcCall(async () => {
1143
+ const result = await contract.call("leaderboard", [tournamentId, offset, limit]);
1144
+ return result.map(parseLeaderboardEntry);
1145
+ }, contract.address);
1146
+ }
1147
+ async function viewerPrizes(contract, tournamentId) {
1148
+ return wrapRpcCall(async () => {
1149
+ const result = await contract.call("tournament_prizes", [tournamentId]);
1150
+ return result.map(parsePrize);
1151
+ }, contract.address);
1152
+ }
1153
+
1154
+ // src/rpc/abis/budokanViewer.json
1155
+ var budokanViewer_default = [
1156
+ {
1157
+ type: "impl",
1158
+ name: "UpgradeableImpl",
1159
+ interface_name: "openzeppelin_interfaces::upgrades::IUpgradeable"
1160
+ },
1161
+ {
1162
+ type: "interface",
1163
+ name: "openzeppelin_interfaces::upgrades::IUpgradeable",
1164
+ items: [
1165
+ {
1166
+ type: "function",
1167
+ name: "upgrade",
1168
+ inputs: [
1169
+ {
1170
+ name: "new_class_hash",
1171
+ type: "core::starknet::class_hash::ClassHash"
1172
+ }
1173
+ ],
1174
+ outputs: [],
1175
+ state_mutability: "external"
1176
+ }
1177
+ ]
1178
+ },
1179
+ {
1180
+ type: "impl",
1181
+ name: "BudokanViewerImpl",
1182
+ interface_name: "budokan_interfaces::viewer::IBudokanViewer"
1183
+ },
1184
+ {
1185
+ type: "struct",
1186
+ name: "budokan_interfaces::viewer::TournamentFilterResult",
1187
+ members: [
1188
+ {
1189
+ name: "tournament_ids",
1190
+ type: "core::array::Array::<core::integer::u64>"
1191
+ },
1192
+ {
1193
+ name: "total",
1194
+ type: "core::integer::u64"
1195
+ }
1196
+ ]
1197
+ },
1198
+ {
1199
+ type: "enum",
1200
+ name: "budokan_interfaces::budokan::Phase",
1201
+ variants: [
1202
+ {
1203
+ name: "Scheduled",
1204
+ type: "()"
1205
+ },
1206
+ {
1207
+ name: "Registration",
1208
+ type: "()"
1209
+ },
1210
+ {
1211
+ name: "Staging",
1212
+ type: "()"
1213
+ },
1214
+ {
1215
+ name: "Live",
1216
+ type: "()"
1217
+ },
1218
+ {
1219
+ name: "Submission",
1220
+ type: "()"
1221
+ },
1222
+ {
1223
+ name: "Finalized",
1224
+ type: "()"
1225
+ }
1226
+ ]
1227
+ },
1228
+ {
1229
+ type: "struct",
1230
+ name: "core::byte_array::ByteArray",
1231
+ members: [
1232
+ {
1233
+ name: "data",
1234
+ type: "core::array::Array::<core::bytes_31::bytes31>"
1235
+ },
1236
+ {
1237
+ name: "pending_word",
1238
+ type: "core::felt252"
1239
+ },
1240
+ {
1241
+ name: "pending_word_len",
1242
+ type: "core::internal::bounded_int::BoundedInt::<0, 30>"
1243
+ }
1244
+ ]
1245
+ },
1246
+ {
1247
+ type: "struct",
1248
+ name: "budokan_interfaces::budokan::Metadata",
1249
+ members: [
1250
+ {
1251
+ name: "name",
1252
+ type: "core::felt252"
1253
+ },
1254
+ {
1255
+ name: "description",
1256
+ type: "core::byte_array::ByteArray"
1257
+ }
1258
+ ]
1259
+ },
1260
+ {
1261
+ type: "struct",
1262
+ name: "budokan_interfaces::budokan::Schedule",
1263
+ members: [
1264
+ {
1265
+ name: "registration_start_delay",
1266
+ type: "core::integer::u32"
1267
+ },
1268
+ {
1269
+ name: "registration_end_delay",
1270
+ type: "core::integer::u32"
1271
+ },
1272
+ {
1273
+ name: "game_start_delay",
1274
+ type: "core::integer::u32"
1275
+ },
1276
+ {
1277
+ name: "game_end_delay",
1278
+ type: "core::integer::u32"
1279
+ },
1280
+ {
1281
+ name: "submission_duration",
1282
+ type: "core::integer::u32"
1283
+ }
1284
+ ]
1285
+ },
1286
+ {
1287
+ type: "enum",
1288
+ name: "core::bool",
1289
+ variants: [
1290
+ {
1291
+ name: "False",
1292
+ type: "()"
1293
+ },
1294
+ {
1295
+ name: "True",
1296
+ type: "()"
1297
+ }
1298
+ ]
1299
+ },
1300
+ {
1301
+ type: "enum",
1302
+ name: "core::option::Option::<core::byte_array::ByteArray>",
1303
+ variants: [
1304
+ {
1305
+ name: "Some",
1306
+ type: "core::byte_array::ByteArray"
1307
+ },
1308
+ {
1309
+ name: "None",
1310
+ type: "()"
1311
+ }
1312
+ ]
1313
+ },
1314
+ {
1315
+ type: "enum",
1316
+ name: "core::option::Option::<core::starknet::contract_address::ContractAddress>",
1317
+ variants: [
1318
+ {
1319
+ name: "Some",
1320
+ type: "core::starknet::contract_address::ContractAddress"
1321
+ },
1322
+ {
1323
+ name: "None",
1324
+ type: "()"
1325
+ }
1326
+ ]
1327
+ },
1328
+ {
1329
+ type: "struct",
1330
+ name: "budokan_interfaces::budokan::GameConfig",
1331
+ members: [
1332
+ {
1333
+ name: "game_address",
1334
+ type: "core::starknet::contract_address::ContractAddress"
1335
+ },
1336
+ {
1337
+ name: "settings_id",
1338
+ type: "core::integer::u32"
1339
+ },
1340
+ {
1341
+ name: "soulbound",
1342
+ type: "core::bool"
1343
+ },
1344
+ {
1345
+ name: "paymaster",
1346
+ type: "core::bool"
1347
+ },
1348
+ {
1349
+ name: "client_url",
1350
+ type: "core::option::Option::<core::byte_array::ByteArray>"
1351
+ },
1352
+ {
1353
+ name: "renderer",
1354
+ type: "core::option::Option::<core::starknet::contract_address::ContractAddress>"
1355
+ }
1356
+ ]
1357
+ },
1358
+ {
1359
+ type: "struct",
1360
+ name: "core::array::Span::<core::integer::u16>",
1361
+ members: [
1362
+ {
1363
+ name: "snapshot",
1364
+ type: "@core::array::Array::<core::integer::u16>"
1365
+ }
1366
+ ]
1367
+ },
1368
+ {
1369
+ type: "enum",
1370
+ name: "game_components_interfaces::distribution::Distribution",
1371
+ variants: [
1372
+ {
1373
+ name: "Linear",
1374
+ type: "core::integer::u16"
1375
+ },
1376
+ {
1377
+ name: "Exponential",
1378
+ type: "core::integer::u16"
1379
+ },
1380
+ {
1381
+ name: "Uniform",
1382
+ type: "()"
1383
+ },
1384
+ {
1385
+ name: "Custom",
1386
+ type: "core::array::Span::<core::integer::u16>"
1387
+ }
1388
+ ]
1389
+ },
1390
+ {
1391
+ type: "struct",
1392
+ name: "budokan_interfaces::budokan::EntryFee",
1393
+ members: [
1394
+ {
1395
+ name: "token_address",
1396
+ type: "core::starknet::contract_address::ContractAddress"
1397
+ },
1398
+ {
1399
+ name: "amount",
1400
+ type: "core::integer::u128"
1401
+ },
1402
+ {
1403
+ name: "tournament_creator_share",
1404
+ type: "core::integer::u16"
1405
+ },
1406
+ {
1407
+ name: "game_creator_share",
1408
+ type: "core::integer::u16"
1409
+ },
1410
+ {
1411
+ name: "refund_share",
1412
+ type: "core::integer::u16"
1413
+ },
1414
+ {
1415
+ name: "distribution",
1416
+ type: "game_components_interfaces::distribution::Distribution"
1417
+ },
1418
+ {
1419
+ name: "distribution_count",
1420
+ type: "core::integer::u32"
1421
+ }
1422
+ ]
1423
+ },
1424
+ {
1425
+ type: "enum",
1426
+ name: "core::option::Option::<budokan_interfaces::budokan::EntryFee>",
1427
+ variants: [
1428
+ {
1429
+ name: "Some",
1430
+ type: "budokan_interfaces::budokan::EntryFee"
1431
+ },
1432
+ {
1433
+ name: "None",
1434
+ type: "()"
1435
+ }
1436
+ ]
1437
+ },
1438
+ {
1439
+ type: "struct",
1440
+ name: "core::array::Span::<core::starknet::contract_address::ContractAddress>",
1441
+ members: [
1442
+ {
1443
+ name: "snapshot",
1444
+ type: "@core::array::Array::<core::starknet::contract_address::ContractAddress>"
1445
+ }
1446
+ ]
1447
+ },
1448
+ {
1449
+ type: "struct",
1450
+ name: "core::array::Span::<core::felt252>",
1451
+ members: [
1452
+ {
1453
+ name: "snapshot",
1454
+ type: "@core::array::Array::<core::felt252>"
1455
+ }
1456
+ ]
1457
+ },
1458
+ {
1459
+ type: "struct",
1460
+ name: "interfaces::extension::ExtensionConfig",
1461
+ members: [
1462
+ {
1463
+ name: "address",
1464
+ type: "core::starknet::contract_address::ContractAddress"
1465
+ },
1466
+ {
1467
+ name: "config",
1468
+ type: "core::array::Span::<core::felt252>"
1469
+ }
1470
+ ]
1471
+ },
1472
+ {
1473
+ type: "enum",
1474
+ name: "game_components_interfaces::entry_requirement::EntryRequirementType",
1475
+ variants: [
1476
+ {
1477
+ name: "token",
1478
+ type: "core::starknet::contract_address::ContractAddress"
1479
+ },
1480
+ {
1481
+ name: "allowlist",
1482
+ type: "core::array::Span::<core::starknet::contract_address::ContractAddress>"
1483
+ },
1484
+ {
1485
+ name: "extension",
1486
+ type: "interfaces::extension::ExtensionConfig"
1487
+ }
1488
+ ]
1489
+ },
1490
+ {
1491
+ type: "struct",
1492
+ name: "game_components_interfaces::entry_requirement::EntryRequirement",
1493
+ members: [
1494
+ {
1495
+ name: "entry_limit",
1496
+ type: "core::integer::u32"
1497
+ },
1498
+ {
1499
+ name: "entry_requirement_type",
1500
+ type: "game_components_interfaces::entry_requirement::EntryRequirementType"
1501
+ }
1502
+ ]
1503
+ },
1504
+ {
1505
+ type: "enum",
1506
+ name: "core::option::Option::<game_components_interfaces::entry_requirement::EntryRequirement>",
1507
+ variants: [
1508
+ {
1509
+ name: "Some",
1510
+ type: "game_components_interfaces::entry_requirement::EntryRequirement"
1511
+ },
1512
+ {
1513
+ name: "None",
1514
+ type: "()"
1515
+ }
1516
+ ]
1517
+ },
1518
+ {
1519
+ type: "struct",
1520
+ name: "budokan_interfaces::budokan::LeaderboardConfig",
1521
+ members: [
1522
+ {
1523
+ name: "ascending",
1524
+ type: "core::bool"
1525
+ },
1526
+ {
1527
+ name: "game_must_be_over",
1528
+ type: "core::bool"
1529
+ }
1530
+ ]
1531
+ },
1532
+ {
1533
+ type: "struct",
1534
+ name: "budokan_interfaces::budokan::Tournament",
1535
+ members: [
1536
+ {
1537
+ name: "id",
1538
+ type: "core::integer::u64"
1539
+ },
1540
+ {
1541
+ name: "created_at",
1542
+ type: "core::integer::u64"
1543
+ },
1544
+ {
1545
+ name: "created_by",
1546
+ type: "core::starknet::contract_address::ContractAddress"
1547
+ },
1548
+ {
1549
+ name: "creator_token_id",
1550
+ type: "core::felt252"
1551
+ },
1552
+ {
1553
+ name: "metadata",
1554
+ type: "budokan_interfaces::budokan::Metadata"
1555
+ },
1556
+ {
1557
+ name: "schedule",
1558
+ type: "budokan_interfaces::budokan::Schedule"
1559
+ },
1560
+ {
1561
+ name: "game_config",
1562
+ type: "budokan_interfaces::budokan::GameConfig"
1563
+ },
1564
+ {
1565
+ name: "entry_fee",
1566
+ type: "core::option::Option::<budokan_interfaces::budokan::EntryFee>"
1567
+ },
1568
+ {
1569
+ name: "entry_requirement",
1570
+ type: "core::option::Option::<game_components_interfaces::entry_requirement::EntryRequirement>"
1571
+ },
1572
+ {
1573
+ name: "leaderboard_config",
1574
+ type: "budokan_interfaces::budokan::LeaderboardConfig"
1575
+ }
1576
+ ]
1577
+ },
1578
+ {
1579
+ type: "struct",
1580
+ name: "budokan_interfaces::viewer::TournamentFullState",
1581
+ members: [
1582
+ {
1583
+ name: "tournament",
1584
+ type: "budokan_interfaces::budokan::Tournament"
1585
+ },
1586
+ {
1587
+ name: "entry_count",
1588
+ type: "core::integer::u32"
1589
+ },
1590
+ {
1591
+ name: "phase",
1592
+ type: "budokan_interfaces::budokan::Phase"
1593
+ }
1594
+ ]
1595
+ },
1596
+ {
1597
+ type: "struct",
1598
+ name: "game_components_interfaces::registration::Registration",
1599
+ members: [
1600
+ {
1601
+ name: "context_id",
1602
+ type: "core::integer::u64"
1603
+ },
1604
+ {
1605
+ name: "entry_id",
1606
+ type: "core::integer::u32"
1607
+ },
1608
+ {
1609
+ name: "game_token_id",
1610
+ type: "core::felt252"
1611
+ },
1612
+ {
1613
+ name: "has_submitted",
1614
+ type: "core::bool"
1615
+ },
1616
+ {
1617
+ name: "is_banned",
1618
+ type: "core::bool"
1619
+ }
1620
+ ]
1621
+ },
1622
+ {
1623
+ type: "struct",
1624
+ name: "budokan_interfaces::viewer::RegistrationResult",
1625
+ members: [
1626
+ {
1627
+ name: "entries",
1628
+ type: "core::array::Array::<game_components_interfaces::registration::Registration>"
1629
+ },
1630
+ {
1631
+ name: "total",
1632
+ type: "core::integer::u32"
1633
+ }
1634
+ ]
1635
+ },
1636
+ {
1637
+ type: "struct",
1638
+ name: "budokan_interfaces::viewer::LeaderboardEntryView",
1639
+ members: [
1640
+ {
1641
+ name: "position",
1642
+ type: "core::integer::u32"
1643
+ },
1644
+ {
1645
+ name: "token_id",
1646
+ type: "core::felt252"
1647
+ }
1648
+ ]
1649
+ },
1650
+ {
1651
+ type: "enum",
1652
+ name: "core::option::Option::<game_components_interfaces::distribution::Distribution>",
1653
+ variants: [
1654
+ {
1655
+ name: "Some",
1656
+ type: "game_components_interfaces::distribution::Distribution"
1657
+ },
1658
+ {
1659
+ name: "None",
1660
+ type: "()"
1661
+ }
1662
+ ]
1663
+ },
1664
+ {
1665
+ type: "enum",
1666
+ name: "core::option::Option::<core::integer::u32>",
1667
+ variants: [
1668
+ {
1669
+ name: "Some",
1670
+ type: "core::integer::u32"
1671
+ },
1672
+ {
1673
+ name: "None",
1674
+ type: "()"
1675
+ }
1676
+ ]
1677
+ },
1678
+ {
1679
+ type: "struct",
1680
+ name: "game_components_interfaces::prize::ERC20Data",
1681
+ members: [
1682
+ {
1683
+ name: "amount",
1684
+ type: "core::integer::u128"
1685
+ },
1686
+ {
1687
+ name: "distribution",
1688
+ type: "core::option::Option::<game_components_interfaces::distribution::Distribution>"
1689
+ },
1690
+ {
1691
+ name: "distribution_count",
1692
+ type: "core::option::Option::<core::integer::u32>"
1693
+ }
1694
+ ]
1695
+ },
1696
+ {
1697
+ type: "struct",
1698
+ name: "game_components_interfaces::prize::ERC721Data",
1699
+ members: [
1700
+ {
1701
+ name: "id",
1702
+ type: "core::integer::u128"
1703
+ }
1704
+ ]
1705
+ },
1706
+ {
1707
+ type: "enum",
1708
+ name: "game_components_interfaces::prize::TokenTypeData",
1709
+ variants: [
1710
+ {
1711
+ name: "erc20",
1712
+ type: "game_components_interfaces::prize::ERC20Data"
1713
+ },
1714
+ {
1715
+ name: "erc721",
1716
+ type: "game_components_interfaces::prize::ERC721Data"
1717
+ }
1718
+ ]
1719
+ },
1720
+ {
1721
+ type: "struct",
1722
+ name: "game_components_interfaces::prize::PrizeData",
1723
+ members: [
1724
+ {
1725
+ name: "id",
1726
+ type: "core::integer::u64"
1727
+ },
1728
+ {
1729
+ name: "context_id",
1730
+ type: "core::integer::u64"
1731
+ },
1732
+ {
1733
+ name: "token_address",
1734
+ type: "core::starknet::contract_address::ContractAddress"
1735
+ },
1736
+ {
1737
+ name: "token_type",
1738
+ type: "game_components_interfaces::prize::TokenTypeData"
1739
+ },
1740
+ {
1741
+ name: "sponsor_address",
1742
+ type: "core::starknet::contract_address::ContractAddress"
1743
+ }
1744
+ ]
1745
+ },
1746
+ {
1747
+ type: "interface",
1748
+ name: "budokan_interfaces::viewer::IBudokanViewer",
1749
+ items: [
1750
+ {
1751
+ type: "function",
1752
+ name: "tournaments",
1753
+ inputs: [
1754
+ {
1755
+ name: "offset",
1756
+ type: "core::integer::u64"
1757
+ },
1758
+ {
1759
+ name: "limit",
1760
+ type: "core::integer::u64"
1761
+ }
1762
+ ],
1763
+ outputs: [
1764
+ {
1765
+ type: "budokan_interfaces::viewer::TournamentFilterResult"
1766
+ }
1767
+ ],
1768
+ state_mutability: "view"
1769
+ },
1770
+ {
1771
+ type: "function",
1772
+ name: "tournaments_by_game",
1773
+ inputs: [
1774
+ {
1775
+ name: "game_address",
1776
+ type: "core::starknet::contract_address::ContractAddress"
1777
+ },
1778
+ {
1779
+ name: "offset",
1780
+ type: "core::integer::u64"
1781
+ },
1782
+ {
1783
+ name: "limit",
1784
+ type: "core::integer::u64"
1785
+ }
1786
+ ],
1787
+ outputs: [
1788
+ {
1789
+ type: "budokan_interfaces::viewer::TournamentFilterResult"
1790
+ }
1791
+ ],
1792
+ state_mutability: "view"
1793
+ },
1794
+ {
1795
+ type: "function",
1796
+ name: "tournaments_by_creator",
1797
+ inputs: [
1798
+ {
1799
+ name: "creator",
1800
+ type: "core::starknet::contract_address::ContractAddress"
1801
+ },
1802
+ {
1803
+ name: "offset",
1804
+ type: "core::integer::u64"
1805
+ },
1806
+ {
1807
+ name: "limit",
1808
+ type: "core::integer::u64"
1809
+ }
1810
+ ],
1811
+ outputs: [
1812
+ {
1813
+ type: "budokan_interfaces::viewer::TournamentFilterResult"
1814
+ }
1815
+ ],
1816
+ state_mutability: "view"
1817
+ },
1818
+ {
1819
+ type: "function",
1820
+ name: "tournaments_by_phase",
1821
+ inputs: [
1822
+ {
1823
+ name: "phase",
1824
+ type: "budokan_interfaces::budokan::Phase"
1825
+ },
1826
+ {
1827
+ name: "offset",
1828
+ type: "core::integer::u64"
1829
+ },
1830
+ {
1831
+ name: "limit",
1832
+ type: "core::integer::u64"
1833
+ }
1834
+ ],
1835
+ outputs: [
1836
+ {
1837
+ type: "budokan_interfaces::viewer::TournamentFilterResult"
1838
+ }
1839
+ ],
1840
+ state_mutability: "view"
1841
+ },
1842
+ {
1843
+ type: "function",
1844
+ name: "count_tournaments",
1845
+ inputs: [],
1846
+ outputs: [
1847
+ {
1848
+ type: "core::integer::u64"
1849
+ }
1850
+ ],
1851
+ state_mutability: "view"
1852
+ },
1853
+ {
1854
+ type: "function",
1855
+ name: "count_tournaments_by_game",
1856
+ inputs: [
1857
+ {
1858
+ name: "game_address",
1859
+ type: "core::starknet::contract_address::ContractAddress"
1860
+ }
1861
+ ],
1862
+ outputs: [
1863
+ {
1864
+ type: "core::integer::u64"
1865
+ }
1866
+ ],
1867
+ state_mutability: "view"
1868
+ },
1869
+ {
1870
+ type: "function",
1871
+ name: "count_tournaments_by_creator",
1872
+ inputs: [
1873
+ {
1874
+ name: "creator",
1875
+ type: "core::starknet::contract_address::ContractAddress"
1876
+ }
1877
+ ],
1878
+ outputs: [
1879
+ {
1880
+ type: "core::integer::u64"
1881
+ }
1882
+ ],
1883
+ state_mutability: "view"
1884
+ },
1885
+ {
1886
+ type: "function",
1887
+ name: "count_tournaments_by_phase",
1888
+ inputs: [
1889
+ {
1890
+ name: "phase",
1891
+ type: "budokan_interfaces::budokan::Phase"
1892
+ }
1893
+ ],
1894
+ outputs: [
1895
+ {
1896
+ type: "core::integer::u64"
1897
+ }
1898
+ ],
1899
+ state_mutability: "view"
1900
+ },
1901
+ {
1902
+ type: "function",
1903
+ name: "tournament_detail",
1904
+ inputs: [
1905
+ {
1906
+ name: "tournament_id",
1907
+ type: "core::integer::u64"
1908
+ }
1909
+ ],
1910
+ outputs: [
1911
+ {
1912
+ type: "budokan_interfaces::viewer::TournamentFullState"
1913
+ }
1914
+ ],
1915
+ state_mutability: "view"
1916
+ },
1917
+ {
1918
+ type: "function",
1919
+ name: "tournaments_batch",
1920
+ inputs: [
1921
+ {
1922
+ name: "tournament_ids",
1923
+ type: "core::array::Array::<core::integer::u64>"
1924
+ }
1925
+ ],
1926
+ outputs: [
1927
+ {
1928
+ type: "core::array::Array::<budokan_interfaces::viewer::TournamentFullState>"
1929
+ }
1930
+ ],
1931
+ state_mutability: "view"
1932
+ },
1933
+ {
1934
+ type: "function",
1935
+ name: "tournament_registrations",
1936
+ inputs: [
1937
+ {
1938
+ name: "tournament_id",
1939
+ type: "core::integer::u64"
1940
+ },
1941
+ {
1942
+ name: "offset",
1943
+ type: "core::integer::u32"
1944
+ },
1945
+ {
1946
+ name: "limit",
1947
+ type: "core::integer::u32"
1948
+ }
1949
+ ],
1950
+ outputs: [
1951
+ {
1952
+ type: "budokan_interfaces::viewer::RegistrationResult"
1953
+ }
1954
+ ],
1955
+ state_mutability: "view"
1956
+ },
1957
+ {
1958
+ type: "function",
1959
+ name: "leaderboard",
1960
+ inputs: [
1961
+ {
1962
+ name: "tournament_id",
1963
+ type: "core::integer::u64"
1964
+ },
1965
+ {
1966
+ name: "offset",
1967
+ type: "core::integer::u32"
1968
+ },
1969
+ {
1970
+ name: "limit",
1971
+ type: "core::integer::u32"
1972
+ }
1973
+ ],
1974
+ outputs: [
1975
+ {
1976
+ type: "core::array::Array::<budokan_interfaces::viewer::LeaderboardEntryView>"
1977
+ }
1978
+ ],
1979
+ state_mutability: "view"
1980
+ },
1981
+ {
1982
+ type: "function",
1983
+ name: "tournament_prizes",
1984
+ inputs: [
1985
+ {
1986
+ name: "tournament_id",
1987
+ type: "core::integer::u64"
1988
+ }
1989
+ ],
1990
+ outputs: [
1991
+ {
1992
+ type: "core::array::Array::<game_components_interfaces::prize::PrizeData>"
1993
+ }
1994
+ ],
1995
+ state_mutability: "view"
1996
+ }
1997
+ ]
1998
+ },
1999
+ {
2000
+ type: "impl",
2001
+ name: "OwnableImpl",
2002
+ interface_name: "openzeppelin_interfaces::access::ownable::IOwnable"
2003
+ },
2004
+ {
2005
+ type: "interface",
2006
+ name: "openzeppelin_interfaces::access::ownable::IOwnable",
2007
+ items: [
2008
+ {
2009
+ type: "function",
2010
+ name: "owner",
2011
+ inputs: [],
2012
+ outputs: [
2013
+ {
2014
+ type: "core::starknet::contract_address::ContractAddress"
2015
+ }
2016
+ ],
2017
+ state_mutability: "view"
2018
+ },
2019
+ {
2020
+ type: "function",
2021
+ name: "transfer_ownership",
2022
+ inputs: [
2023
+ {
2024
+ name: "new_owner",
2025
+ type: "core::starknet::contract_address::ContractAddress"
2026
+ }
2027
+ ],
2028
+ outputs: [],
2029
+ state_mutability: "external"
2030
+ },
2031
+ {
2032
+ type: "function",
2033
+ name: "renounce_ownership",
2034
+ inputs: [],
2035
+ outputs: [],
2036
+ state_mutability: "external"
2037
+ }
2038
+ ]
2039
+ },
2040
+ {
2041
+ type: "impl",
2042
+ name: "OwnableCamelOnlyImpl",
2043
+ interface_name: "openzeppelin_interfaces::access::ownable::IOwnableCamelOnly"
2044
+ },
2045
+ {
2046
+ type: "interface",
2047
+ name: "openzeppelin_interfaces::access::ownable::IOwnableCamelOnly",
2048
+ items: [
2049
+ {
2050
+ type: "function",
2051
+ name: "transferOwnership",
2052
+ inputs: [
2053
+ {
2054
+ name: "newOwner",
2055
+ type: "core::starknet::contract_address::ContractAddress"
2056
+ }
2057
+ ],
2058
+ outputs: [],
2059
+ state_mutability: "external"
2060
+ },
2061
+ {
2062
+ type: "function",
2063
+ name: "renounceOwnership",
2064
+ inputs: [],
2065
+ outputs: [],
2066
+ state_mutability: "external"
2067
+ }
2068
+ ]
2069
+ },
2070
+ {
2071
+ type: "constructor",
2072
+ name: "constructor",
2073
+ inputs: [
2074
+ {
2075
+ name: "owner",
2076
+ type: "core::starknet::contract_address::ContractAddress"
2077
+ },
2078
+ {
2079
+ name: "budokan_address",
2080
+ type: "core::starknet::contract_address::ContractAddress"
2081
+ }
2082
+ ]
2083
+ },
2084
+ {
2085
+ type: "event",
2086
+ name: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred",
2087
+ kind: "struct",
2088
+ members: [
2089
+ {
2090
+ name: "previous_owner",
2091
+ type: "core::starknet::contract_address::ContractAddress",
2092
+ kind: "key"
2093
+ },
2094
+ {
2095
+ name: "new_owner",
2096
+ type: "core::starknet::contract_address::ContractAddress",
2097
+ kind: "key"
2098
+ }
2099
+ ]
2100
+ },
2101
+ {
2102
+ type: "event",
2103
+ name: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted",
2104
+ kind: "struct",
2105
+ members: [
2106
+ {
2107
+ name: "previous_owner",
2108
+ type: "core::starknet::contract_address::ContractAddress",
2109
+ kind: "key"
2110
+ },
2111
+ {
2112
+ name: "new_owner",
2113
+ type: "core::starknet::contract_address::ContractAddress",
2114
+ kind: "key"
2115
+ }
2116
+ ]
2117
+ },
2118
+ {
2119
+ type: "event",
2120
+ name: "openzeppelin_access::ownable::ownable::OwnableComponent::Event",
2121
+ kind: "enum",
2122
+ variants: [
2123
+ {
2124
+ name: "OwnershipTransferred",
2125
+ type: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred",
2126
+ kind: "nested"
2127
+ },
2128
+ {
2129
+ name: "OwnershipTransferStarted",
2130
+ type: "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted",
2131
+ kind: "nested"
2132
+ }
2133
+ ]
2134
+ },
2135
+ {
2136
+ type: "event",
2137
+ name: "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Upgraded",
2138
+ kind: "struct",
2139
+ members: [
2140
+ {
2141
+ name: "class_hash",
2142
+ type: "core::starknet::class_hash::ClassHash",
2143
+ kind: "data"
2144
+ }
2145
+ ]
2146
+ },
2147
+ {
2148
+ type: "event",
2149
+ name: "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Event",
2150
+ kind: "enum",
2151
+ variants: [
2152
+ {
2153
+ name: "Upgraded",
2154
+ type: "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Upgraded",
2155
+ kind: "nested"
2156
+ }
2157
+ ]
2158
+ },
2159
+ {
2160
+ type: "event",
2161
+ name: "budokan_viewer::budokan_viewer::BudokanViewer::Event",
2162
+ kind: "enum",
2163
+ variants: [
2164
+ {
2165
+ name: "OwnableEvent",
2166
+ type: "openzeppelin_access::ownable::ownable::OwnableComponent::Event",
2167
+ kind: "flat"
2168
+ },
2169
+ {
2170
+ name: "UpgradeableEvent",
2171
+ type: "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Event",
2172
+ kind: "flat"
2173
+ }
2174
+ ]
2175
+ }
2176
+ ];
2177
+
2178
+ // src/client.ts
2179
+ var BudokanClient = class {
2180
+ resolvedConfig;
2181
+ wsManager;
2182
+ connectionStatus;
2183
+ cachedProvider = null;
2184
+ cachedViewerContract = null;
2185
+ constructor(config) {
2186
+ const chainConfig = config.chain ? getChainConfig(config.chain) : void 0;
2187
+ this.resolvedConfig = {
2188
+ ...config,
2189
+ apiBaseUrl: config.apiBaseUrl ?? chainConfig?.apiBaseUrl ?? "",
2190
+ rpcUrl: config.rpcUrl ?? chainConfig?.rpcUrl ?? "",
2191
+ viewerAddress: config.viewerAddress ?? chainConfig?.viewerAddress ?? "",
2192
+ budokanAddress: config.budokanAddress ?? chainConfig?.budokanAddress ?? ""
2193
+ };
2194
+ const wsUrl = config.wsUrl ?? chainConfig?.wsUrl ?? this.resolvedConfig.apiBaseUrl.replace(/^http/, "ws").replace(/\/$/, "") + "/ws";
2195
+ this.wsManager = new WSManager(wsUrl);
2196
+ this.connectionStatus = new ConnectionStatus(
2197
+ this.resolvedConfig.apiBaseUrl,
2198
+ this.resolvedConfig.rpcUrl,
2199
+ config.health
2200
+ );
2201
+ if (this.resolvedConfig.apiBaseUrl && this.resolvedConfig.rpcUrl) {
2202
+ this.connectionStatus.startMonitoring();
2203
+ }
2204
+ }
2205
+ // ---- Configuration ----
2206
+ /** Returns the resolved configuration. */
2207
+ get clientConfig() {
2208
+ return { ...this.resolvedConfig };
2209
+ }
2210
+ /** Whether the WebSocket is currently connected. */
2211
+ get wsConnected() {
2212
+ return this.wsManager.isConnected;
2213
+ }
2214
+ // ---- Connection status ----
2215
+ /** Returns the current connection status (API, RPC, mode). */
2216
+ getConnectionStatus() {
2217
+ return this.connectionStatus.getStatus();
2218
+ }
2219
+ /** Subscribe to connection status changes. Returns an unsubscribe function. */
2220
+ onConnectionStatusChange(listener) {
2221
+ return this.connectionStatus.subscribe(listener);
2222
+ }
2223
+ // ---- Lazy RPC getters ----
2224
+ async getProvider() {
2225
+ if (this.cachedProvider) return this.cachedProvider;
2226
+ if (!this.resolvedConfig.rpcUrl) {
2227
+ throw new RpcError("No rpcUrl configured");
2228
+ }
2229
+ if (this.resolvedConfig.provider) {
2230
+ this.cachedProvider = this.resolvedConfig.provider;
2231
+ return this.cachedProvider;
2232
+ }
2233
+ this.cachedProvider = await createProvider(this.resolvedConfig.rpcUrl);
2234
+ return this.cachedProvider;
2235
+ }
2236
+ async getViewerContract() {
2237
+ if (this.cachedViewerContract) return this.cachedViewerContract;
2238
+ if (!this.resolvedConfig.viewerAddress) {
2239
+ throw new RpcError("No viewerAddress configured. Set viewerAddress in config or use a chain preset with a deployed viewer contract.");
2240
+ }
2241
+ const provider = await this.getProvider();
2242
+ this.cachedViewerContract = await createContract(
2243
+ budokanViewer_default,
2244
+ this.resolvedConfig.viewerAddress,
2245
+ provider
2246
+ );
2247
+ return this.cachedViewerContract;
2248
+ }
2249
+ // ---- API context ----
2250
+ get apiCtx() {
2251
+ return {
2252
+ retryAttempts: this.resolvedConfig.retryAttempts,
2253
+ retryDelay: this.resolvedConfig.retryDelay,
2254
+ timeout: this.resolvedConfig.timeout
2255
+ };
2256
+ }
2257
+ // ---- Tournament Queries ----
2258
+ /**
2259
+ * Fetch a paginated list of tournaments with optional filtering.
2260
+ * Supports RPC fallback when API is unavailable.
2261
+ */
2262
+ async getTournaments(params) {
2263
+ const rpcFallback = async () => {
2264
+ const contract = await this.getViewerContract();
2265
+ const offset = params?.offset ?? 0;
2266
+ const limit = params?.limit ?? 20;
2267
+ let filterResult;
2268
+ if (params?.phase) {
2269
+ filterResult = await viewerTournamentsByPhase(contract, params.phase, offset, limit);
2270
+ } else if (params?.gameAddress) {
2271
+ filterResult = await viewerTournamentsByGame(contract, params.gameAddress, offset, limit);
2272
+ } else if (params?.creator) {
2273
+ filterResult = await viewerTournamentsByCreator(contract, params.creator, offset, limit);
2274
+ } else {
2275
+ filterResult = await viewerTournaments(contract, offset, limit);
2276
+ }
2277
+ let data = [];
2278
+ if (filterResult.tournamentIds.length > 0) {
2279
+ data = await viewerTournamentsBatch(contract, filterResult.tournamentIds);
2280
+ }
2281
+ return { data, total: filterResult.total, limit, offset };
2282
+ };
2283
+ if (this.resolvedConfig.primarySource === "rpc") {
2284
+ return rpcFallback();
2285
+ }
2286
+ return withFallback(
2287
+ () => getTournaments(this.resolvedConfig.apiBaseUrl, params, this.apiCtx),
2288
+ rpcFallback,
2289
+ this.connectionStatus
2290
+ );
2291
+ }
2292
+ /**
2293
+ * Fetch a single tournament by its ID.
2294
+ * Supports RPC fallback when API is unavailable.
2295
+ */
2296
+ async getTournament(tournamentId) {
2297
+ const rpcFallback = async () => {
2298
+ const contract = await this.getViewerContract();
2299
+ return viewerTournamentDetail(contract, tournamentId);
2300
+ };
2301
+ if (this.resolvedConfig.primarySource === "rpc") {
2302
+ return rpcFallback();
2303
+ }
2304
+ return withFallback(
2305
+ () => getTournament(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx),
2306
+ rpcFallback,
2307
+ this.connectionStatus
2308
+ );
2309
+ }
2310
+ /**
2311
+ * Fetch the leaderboard for a tournament.
2312
+ * Supports RPC fallback when API is unavailable.
2313
+ */
2314
+ async getTournamentLeaderboard(tournamentId) {
2315
+ const rpcFallback = async () => {
2316
+ const contract = await this.getViewerContract();
2317
+ return viewerLeaderboard(contract, tournamentId, 0, 1e3);
2318
+ };
2319
+ if (this.resolvedConfig.primarySource === "rpc") {
2320
+ return rpcFallback();
2321
+ }
2322
+ return withFallback(
2323
+ () => getTournamentLeaderboard(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx),
2324
+ rpcFallback,
2325
+ this.connectionStatus
2326
+ );
2327
+ }
2328
+ /**
2329
+ * Fetch registrations for a tournament.
2330
+ * Supports RPC fallback when API is unavailable.
2331
+ * Note: In RPC mode, `playerAddress` and `gameAddress` fields will be empty strings.
2332
+ */
2333
+ async getTournamentRegistrations(tournamentId, params) {
2334
+ const rpcFallback = async () => {
2335
+ const contract = await this.getViewerContract();
2336
+ const offset = params?.offset ?? 0;
2337
+ const limit = params?.limit ?? 20;
2338
+ return viewerRegistrations(contract, tournamentId, offset, limit);
2339
+ };
2340
+ if (this.resolvedConfig.primarySource === "rpc") {
2341
+ return rpcFallback();
2342
+ }
2343
+ return withFallback(
2344
+ () => getTournamentRegistrations(this.resolvedConfig.apiBaseUrl, tournamentId, params, this.apiCtx),
2345
+ rpcFallback,
2346
+ this.connectionStatus
2347
+ );
2348
+ }
2349
+ /**
2350
+ * Fetch prizes for a tournament.
2351
+ * Supports RPC fallback when API is unavailable.
2352
+ */
2353
+ async getTournamentPrizes(tournamentId) {
2354
+ const rpcFallback = async () => {
2355
+ const contract = await this.getViewerContract();
2356
+ return viewerPrizes(contract, tournamentId);
2357
+ };
2358
+ if (this.resolvedConfig.primarySource === "rpc") {
2359
+ return rpcFallback();
2360
+ }
2361
+ return withFallback(
2362
+ () => getTournamentPrizes(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx),
2363
+ rpcFallback,
2364
+ this.connectionStatus
2365
+ );
2366
+ }
2367
+ // ---- Player Queries (API-only, no on-chain equivalent) ----
2368
+ /**
2369
+ * Fetch tournaments that a player has registered for.
2370
+ * API-only — no RPC fallback available.
2371
+ */
2372
+ async getPlayerTournaments(address, params) {
2373
+ return getPlayerTournaments(this.resolvedConfig.apiBaseUrl, address, params, this.apiCtx);
2374
+ }
2375
+ /**
2376
+ * Fetch stats for a player.
2377
+ * API-only — no RPC fallback available.
2378
+ */
2379
+ async getPlayerStats(address) {
2380
+ return getPlayerStats(this.resolvedConfig.apiBaseUrl, address, this.apiCtx);
2381
+ }
2382
+ // ---- Game Queries ----
2383
+ /**
2384
+ * Fetch tournaments for a specific game.
2385
+ * Supports RPC fallback when API is unavailable.
2386
+ */
2387
+ async getGameTournaments(gameAddress, params) {
2388
+ const rpcFallback = async () => {
2389
+ const contract = await this.getViewerContract();
2390
+ const offset = params?.offset ?? 0;
2391
+ const limit = params?.limit ?? 20;
2392
+ const filterResult = await viewerTournamentsByGame(contract, gameAddress, offset, limit);
2393
+ let data = [];
2394
+ if (filterResult.tournamentIds.length > 0) {
2395
+ data = await viewerTournamentsBatch(contract, filterResult.tournamentIds);
2396
+ }
2397
+ return { data, total: filterResult.total, limit, offset };
2398
+ };
2399
+ if (this.resolvedConfig.primarySource === "rpc") {
2400
+ return rpcFallback();
2401
+ }
2402
+ return withFallback(
2403
+ () => getGameTournaments(this.resolvedConfig.apiBaseUrl, gameAddress, params, this.apiCtx),
2404
+ rpcFallback,
2405
+ this.connectionStatus
2406
+ );
2407
+ }
2408
+ /**
2409
+ * Fetch tournament stats for a specific game.
2410
+ * API-only — no RPC fallback available.
2411
+ */
2412
+ async getGameStats(gameAddress) {
2413
+ return getGameStats(this.resolvedConfig.apiBaseUrl, gameAddress, this.apiCtx);
2414
+ }
2415
+ // ---- Reward Claims & Qualifications (API-only) ----
2416
+ /**
2417
+ * Fetch reward claims for a tournament.
2418
+ * API-only -- no RPC fallback available.
2419
+ */
2420
+ async getTournamentRewardClaims(tournamentId, params) {
2421
+ return getTournamentRewardClaims(this.resolvedConfig.apiBaseUrl, tournamentId, params, this.apiCtx);
2422
+ }
2423
+ /**
2424
+ * Fetch reward claims summary for a tournament.
2425
+ * API-only -- no RPC fallback available.
2426
+ */
2427
+ async getTournamentRewardClaimsSummary(tournamentId) {
2428
+ return getTournamentRewardClaimsSummary(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx);
2429
+ }
2430
+ /**
2431
+ * Fetch qualifications for a tournament.
2432
+ * API-only -- no RPC fallback available.
2433
+ */
2434
+ async getTournamentQualifications(tournamentId, params) {
2435
+ return getTournamentQualifications(this.resolvedConfig.apiBaseUrl, tournamentId, params, this.apiCtx);
2436
+ }
2437
+ /**
2438
+ * Fetch prize aggregation for a tournament.
2439
+ * API-only -- no RPC fallback available.
2440
+ */
2441
+ async getTournamentPrizeAggregation(tournamentId) {
2442
+ return getTournamentPrizeAggregation(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx);
2443
+ }
2444
+ // ---- Activity Queries (API-only, activity is indexed) ----
2445
+ /**
2446
+ * Fetch activity events with optional filtering.
2447
+ * API-only — no RPC fallback available.
2448
+ */
2449
+ async getActivity(params) {
2450
+ return getActivity(this.resolvedConfig.apiBaseUrl, params, this.apiCtx);
2451
+ }
2452
+ /**
2453
+ * Fetch platform-wide activity stats.
2454
+ * API-only — no RPC fallback available.
2455
+ */
2456
+ async getActivityStats() {
2457
+ return getActivityStats(this.resolvedConfig.apiBaseUrl, this.apiCtx);
2458
+ }
2459
+ /**
2460
+ * Fetch platform-wide prize stats.
2461
+ * API-only — no RPC fallback available.
2462
+ */
2463
+ async getPrizeStats() {
2464
+ return getPrizeStats(this.resolvedConfig.apiBaseUrl, this.apiCtx);
2465
+ }
2466
+ // ---- WebSocket ----
2467
+ /**
2468
+ * Open a WebSocket connection for real-time updates.
2469
+ */
2470
+ connect() {
2471
+ this.wsManager.connect();
2472
+ }
2473
+ /**
2474
+ * Close the WebSocket connection.
2475
+ */
2476
+ disconnect() {
2477
+ this.wsManager.disconnect();
2478
+ }
2479
+ /**
2480
+ * Stop health monitoring and close all connections.
2481
+ */
2482
+ destroy() {
2483
+ this.connectionStatus.destroy();
2484
+ this.wsManager.disconnect();
2485
+ }
2486
+ /**
2487
+ * Subscribe to WebSocket channels with optional tournament filtering.
2488
+ * Returns an unsubscribe function.
2489
+ */
2490
+ subscribe(channels, handler, tournamentIds) {
2491
+ return this.wsManager.subscribe({ channels, tournamentIds }, handler);
2492
+ }
2493
+ /**
2494
+ * Register a listener for WebSocket connection state changes.
2495
+ * Returns an unsubscribe function.
2496
+ */
2497
+ onWsConnectionChange(listener) {
2498
+ return this.wsManager.onConnectionChange(listener);
2499
+ }
2500
+ };
2501
+ function createBudokanClient(config) {
2502
+ return new BudokanClient(config);
2503
+ }
2504
+ var BudokanContext = createContext(null);
2505
+ function BudokanProvider({ children, config, client: existingClient }) {
2506
+ const client = useMemo(() => {
2507
+ if (existingClient) return existingClient;
2508
+ if (config) return createBudokanClient(config);
2509
+ throw new Error("BudokanProvider requires either 'config' or 'client' prop");
2510
+ }, [existingClient, config]);
2511
+ const clientRef = useRef(client);
2512
+ useEffect(() => {
2513
+ return () => {
2514
+ if (!existingClient && clientRef.current !== client) {
2515
+ clientRef.current.destroy();
2516
+ }
2517
+ };
2518
+ }, [client, existingClient]);
2519
+ useEffect(() => {
2520
+ clientRef.current = client;
2521
+ }, [client]);
2522
+ return /* @__PURE__ */ jsx(BudokanContext.Provider, { value: client, children });
2523
+ }
2524
+ function useBudokanClient() {
2525
+ const client = useContext(BudokanContext);
2526
+ if (!client) {
2527
+ throw new Error("useBudokanClient must be used within a BudokanProvider");
2528
+ }
2529
+ return client;
2530
+ }
2531
+ function useTournaments(params) {
2532
+ const client = useBudokanClient();
2533
+ const [tournaments, setTournaments] = useState(null);
2534
+ const [loading, setLoading] = useState(true);
2535
+ const [error, setError] = useState(null);
2536
+ const paramsKey = JSON.stringify(params);
2537
+ const fetch2 = useCallback(() => {
2538
+ setLoading(true);
2539
+ setError(null);
2540
+ client.getTournaments(params).then(setTournaments).catch(setError).finally(() => setLoading(false));
2541
+ }, [client, paramsKey]);
2542
+ useEffect(() => {
2543
+ fetch2();
2544
+ }, [fetch2]);
2545
+ return { tournaments, loading, error, refetch: fetch2 };
2546
+ }
2547
+ function useTournament(tournamentId) {
2548
+ const client = useBudokanClient();
2549
+ const [tournament, setTournament] = useState(null);
2550
+ const [loading, setLoading] = useState(!!tournamentId);
2551
+ const [error, setError] = useState(null);
2552
+ const fetch2 = useCallback(() => {
2553
+ if (!tournamentId) return;
2554
+ setLoading(true);
2555
+ setError(null);
2556
+ client.getTournament(tournamentId).then(setTournament).catch(setError).finally(() => setLoading(false));
2557
+ }, [client, tournamentId]);
2558
+ useEffect(() => {
2559
+ fetch2();
2560
+ }, [fetch2]);
2561
+ return { tournament, loading, error, refetch: fetch2 };
2562
+ }
2563
+ function useLeaderboard(tournamentId) {
2564
+ const client = useBudokanClient();
2565
+ const [leaderboard, setLeaderboard] = useState(null);
2566
+ const [loading, setLoading] = useState(!!tournamentId);
2567
+ const [error, setError] = useState(null);
2568
+ const fetch2 = useCallback(() => {
2569
+ if (!tournamentId) return;
2570
+ setLoading(true);
2571
+ setError(null);
2572
+ client.getTournamentLeaderboard(tournamentId).then(setLeaderboard).catch(setError).finally(() => setLoading(false));
2573
+ }, [client, tournamentId]);
2574
+ useEffect(() => {
2575
+ fetch2();
2576
+ }, [fetch2]);
2577
+ return { leaderboard, loading, error, refetch: fetch2 };
2578
+ }
2579
+ function usePlayer(address) {
2580
+ const client = useBudokanClient();
2581
+ const [tournaments, setTournaments] = useState(null);
2582
+ const [stats, setStats] = useState(null);
2583
+ const [loading, setLoading] = useState(!!address);
2584
+ const [error, setError] = useState(null);
2585
+ const fetch2 = useCallback(() => {
2586
+ if (!address) return;
2587
+ setLoading(true);
2588
+ setError(null);
2589
+ Promise.all([
2590
+ client.getPlayerTournaments(address),
2591
+ client.getPlayerStats(address)
2592
+ ]).then(([tournamentsResult, statsResult]) => {
2593
+ setTournaments(tournamentsResult);
2594
+ setStats(statsResult);
2595
+ }).catch(setError).finally(() => setLoading(false));
2596
+ }, [client, address]);
2597
+ useEffect(() => {
2598
+ fetch2();
2599
+ }, [fetch2]);
2600
+ return { tournaments, stats, loading, error };
2601
+ }
2602
+ function usePlayerStats(address) {
2603
+ const client = useBudokanClient();
2604
+ const [stats, setStats] = useState(null);
2605
+ const [loading, setLoading] = useState(!!address);
2606
+ const [error, setError] = useState(null);
2607
+ const fetch2 = useCallback(() => {
2608
+ if (!address) return;
2609
+ setLoading(true);
2610
+ setError(null);
2611
+ client.getPlayerStats(address).then(setStats).catch(setError).finally(() => setLoading(false));
2612
+ }, [client, address]);
2613
+ useEffect(() => {
2614
+ fetch2();
2615
+ }, [fetch2]);
2616
+ return { stats, loading, error, refetch: fetch2 };
2617
+ }
2618
+ function usePlayerTournaments(address, params) {
2619
+ const client = useBudokanClient();
2620
+ const [tournaments, setTournaments] = useState(null);
2621
+ const [loading, setLoading] = useState(!!address);
2622
+ const [error, setError] = useState(null);
2623
+ const fetch2 = useCallback(() => {
2624
+ if (!address) return;
2625
+ setLoading(true);
2626
+ setError(null);
2627
+ client.getPlayerTournaments(address, params).then(setTournaments).catch(setError).finally(() => setLoading(false));
2628
+ }, [client, address, params]);
2629
+ useEffect(() => {
2630
+ fetch2();
2631
+ }, [fetch2]);
2632
+ return { tournaments, loading, error, refetch: fetch2 };
2633
+ }
2634
+ function useRewardClaims(tournamentId) {
2635
+ const client = useBudokanClient();
2636
+ const [rewardClaims, setRewardClaims] = useState(null);
2637
+ const [loading, setLoading] = useState(!!tournamentId);
2638
+ const [error, setError] = useState(null);
2639
+ const fetch2 = useCallback(() => {
2640
+ if (!tournamentId) return;
2641
+ setLoading(true);
2642
+ setError(null);
2643
+ client.getTournamentRewardClaims(tournamentId).then(setRewardClaims).catch(setError).finally(() => setLoading(false));
2644
+ }, [client, tournamentId]);
2645
+ useEffect(() => {
2646
+ fetch2();
2647
+ }, [fetch2]);
2648
+ return { rewardClaims, loading, error, refetch: fetch2 };
2649
+ }
2650
+ function useRewardClaimsSummary(tournamentId) {
2651
+ const client = useBudokanClient();
2652
+ const [summary, setSummary] = useState(null);
2653
+ const [loading, setLoading] = useState(!!tournamentId);
2654
+ const [error, setError] = useState(null);
2655
+ const fetch2 = useCallback(() => {
2656
+ if (!tournamentId) return;
2657
+ setLoading(true);
2658
+ setError(null);
2659
+ client.getTournamentRewardClaimsSummary(tournamentId).then(setSummary).catch(setError).finally(() => setLoading(false));
2660
+ }, [client, tournamentId]);
2661
+ useEffect(() => {
2662
+ fetch2();
2663
+ }, [fetch2]);
2664
+ return { summary, loading, error, refetch: fetch2 };
2665
+ }
2666
+ function usePrizes(tournamentId) {
2667
+ const client = useBudokanClient();
2668
+ const [prizes, setPrizes] = useState(null);
2669
+ const [loading, setLoading] = useState(!!tournamentId);
2670
+ const [error, setError] = useState(null);
2671
+ const fetch2 = useCallback(() => {
2672
+ if (!tournamentId) return;
2673
+ setLoading(true);
2674
+ setError(null);
2675
+ client.getTournamentPrizes(tournamentId).then(setPrizes).catch(setError).finally(() => setLoading(false));
2676
+ }, [client, tournamentId]);
2677
+ useEffect(() => {
2678
+ fetch2();
2679
+ }, [fetch2]);
2680
+ return { prizes, loading, error, refetch: fetch2 };
2681
+ }
2682
+ function usePrizeStats() {
2683
+ const client = useBudokanClient();
2684
+ const [prizeStats, setPrizeStats] = useState(null);
2685
+ const [loading, setLoading] = useState(true);
2686
+ const [error, setError] = useState(null);
2687
+ const fetch2 = useCallback(() => {
2688
+ setLoading(true);
2689
+ setError(null);
2690
+ client.getPrizeStats().then(setPrizeStats).catch(setError).finally(() => setLoading(false));
2691
+ }, [client]);
2692
+ useEffect(() => {
2693
+ fetch2();
2694
+ }, [fetch2]);
2695
+ return { prizeStats, loading, error, refetch: fetch2 };
2696
+ }
2697
+ function useQualifications(tournamentId) {
2698
+ const client = useBudokanClient();
2699
+ const [qualifications, setQualifications] = useState(null);
2700
+ const [loading, setLoading] = useState(!!tournamentId);
2701
+ const [error, setError] = useState(null);
2702
+ const fetch2 = useCallback(() => {
2703
+ if (!tournamentId) return;
2704
+ setLoading(true);
2705
+ setError(null);
2706
+ client.getTournamentQualifications(tournamentId).then(setQualifications).catch(setError).finally(() => setLoading(false));
2707
+ }, [client, tournamentId]);
2708
+ useEffect(() => {
2709
+ fetch2();
2710
+ }, [fetch2]);
2711
+ return { qualifications, loading, error, refetch: fetch2 };
2712
+ }
2713
+ function useSubscription(channels, tournamentIds) {
2714
+ const client = useBudokanClient();
2715
+ const [lastMessage, setLastMessage] = useState(null);
2716
+ const [isConnected, setIsConnected] = useState(client.wsConnected);
2717
+ const channelsRef = useRef(channels);
2718
+ const tournamentIdsRef = useRef(tournamentIds);
2719
+ channelsRef.current = channels;
2720
+ tournamentIdsRef.current = tournamentIds;
2721
+ useEffect(() => {
2722
+ if (channels.length === 0) return;
2723
+ client.connect();
2724
+ const unsubscribe = client.subscribe(
2725
+ channels,
2726
+ (message) => setLastMessage(message),
2727
+ tournamentIds
2728
+ );
2729
+ const unsubscribeConnection = client.onWsConnectionChange((connected) => {
2730
+ setIsConnected(connected);
2731
+ });
2732
+ return () => {
2733
+ unsubscribe();
2734
+ unsubscribeConnection();
2735
+ };
2736
+ }, [client, JSON.stringify(channels), JSON.stringify(tournamentIds)]);
2737
+ return { lastMessage, isConnected };
2738
+ }
2739
+ function useConnectionStatus() {
2740
+ const client = useBudokanClient();
2741
+ const [isConnected, setIsConnected] = useState(client.wsConnected);
2742
+ const [datasourceMode, setDatasourceMode] = useState("api");
2743
+ useEffect(() => {
2744
+ const unsubWs = client.onWsConnectionChange((connected) => {
2745
+ setIsConnected(connected);
2746
+ });
2747
+ const unsubDs = client.onConnectionStatusChange((status) => {
2748
+ setDatasourceMode(status.mode);
2749
+ });
2750
+ return () => {
2751
+ unsubWs();
2752
+ unsubDs();
2753
+ };
2754
+ }, [client]);
2755
+ return { isConnected, datasourceMode };
2756
+ }
2757
+
2758
+ export { BudokanProvider, useBudokanClient, useConnectionStatus, useLeaderboard, usePlayer, usePlayerStats, usePlayerTournaments, usePrizeStats, usePrizes, useQualifications, useRewardClaims, useRewardClaimsSummary, useSubscription, useTournament, useTournaments };
2759
+ //# sourceMappingURL=react.js.map
2760
+ //# sourceMappingURL=react.js.map