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