@provable-games/budokan-sdk 0.1.3 → 0.1.5

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createContext, useMemo, useRef, useEffect, useContext, useState, useCallback } from 'react';
2
- import { num } from 'starknet';
2
+ import { CairoCustomEnum, num } from 'starknet';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
5
5
  // src/react/context.tsx
@@ -199,12 +199,8 @@ function snakeToCamel(obj) {
199
199
  // src/api/tournaments.ts
200
200
  function normalizeTournament(raw) {
201
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;
202
+ const id = t.id ?? t.tournamentId;
203
+ return { ...t, id, tournamentId: id };
208
204
  }
209
205
  function fetchOpts(ctx) {
210
206
  return {
@@ -337,11 +333,12 @@ async function getPlayerTournaments(baseUrl, address, params, ctx) {
337
333
  game_token_ids: params?.gameTokenIds?.join(",")
338
334
  });
339
335
  const result = await apiFetch(`${baseUrl}/players/${normalized}/tournaments${qs}`, fetchOpts2(ctx));
336
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
340
337
  return {
341
338
  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
339
+ total,
340
+ limit: resLimit,
341
+ offset: resOffset
345
342
  };
346
343
  }
347
344
  async function getPlayerStats(baseUrl, address, ctx) {
@@ -370,11 +367,12 @@ async function getGameTournaments(baseUrl, gameAddress, params, ctx) {
370
367
  offset: params?.offset
371
368
  });
372
369
  const result = await apiFetch(`${baseUrl}/games/${normalized}/tournaments${qs}`, fetchOpts3(ctx));
370
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
373
371
  return {
374
372
  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
373
+ total,
374
+ limit: resLimit,
375
+ offset: resOffset
378
376
  };
379
377
  }
380
378
  async function getGameStats(baseUrl, gameAddress, ctx) {
@@ -403,11 +401,12 @@ async function getActivity(baseUrl, params, ctx) {
403
401
  offset: params?.offset
404
402
  });
405
403
  const result = await apiFetch(`${baseUrl}/activity${qs}`, fetchOpts4(ctx));
404
+ const { total, limit: resLimit, offset: resOffset } = extractPagination(result, { limit: params?.limit, offset: params?.offset });
406
405
  return {
407
406
  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
407
+ total,
408
+ limit: resLimit,
409
+ offset: resOffset
411
410
  };
412
411
  }
413
412
  async function getActivityStats(baseUrl, ctx) {
@@ -583,19 +582,15 @@ var CHAINS = {
583
582
  rpcUrl: "https://api.cartridge.gg/x/starknet/mainnet/rpc/v0_10",
584
583
  apiBaseUrl: "https://budokan-api-production.up.railway.app",
585
584
  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
585
+ budokanAddress: "0x06137ee50f57d08e1d0d758045e45982e2f5ef4826091ed4db136e7afbafecce",
586
+ viewerAddress: "0x075d1b9f1a9751e6b8f8b5a4ca8e721f10c58d87607e703cda062e512a434443"
590
587
  },
591
588
  sepolia: {
592
589
  rpcUrl: "https://starknet-sepolia.public.blastapi.io",
593
590
  apiBaseUrl: "https://budokan-api-sepolia.up.railway.app",
594
591
  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
592
+ budokanAddress: "0x0072a26c29ba5021508bbbb8487663a2a536b8a926acf388d3d772961bd063e0",
593
+ viewerAddress: "0x06bef644110a02c1b075b539953c707cd03b4bb32b42f5f1b0b0090b5139529f"
599
594
  }
600
595
  };
601
596
  function getChainConfig(chain) {
@@ -775,7 +770,6 @@ async function withFallback(primary, fallback, health) {
775
770
 
776
771
  // src/rpc/provider.ts
777
772
  var starknetModule = null;
778
- var useObjectConstructor = null;
779
773
  async function getStarknet() {
780
774
  if (!starknetModule) {
781
775
  starknetModule = await import('starknet');
@@ -793,30 +787,7 @@ async function createContract(abi, address, provider) {
793
787
  resolvedAbi = abi.default;
794
788
  }
795
789
  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
- }
790
+ return new starknet.Contract({ abi: resolvedAbi, address, providerOrAccount: provider });
820
791
  }
821
792
  function wrapRpcCall(fn, contractAddress) {
822
793
  return fn().catch((error) => {
@@ -864,16 +835,22 @@ function decodeByteArray(value) {
864
835
  }
865
836
  return result;
866
837
  }
838
+ var PHASE_VARIANTS = ["Scheduled", "Registration", "Staging", "Live", "Submission", "Finalized"];
839
+ var PHASE_NAME_MAP = {
840
+ scheduled: "Scheduled",
841
+ registration: "Registration",
842
+ staging: "Staging",
843
+ live: "Live",
844
+ submission: "Submission",
845
+ finalized: "Finalized"
846
+ };
867
847
  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]]: {} };
848
+ const activeVariant = PHASE_NAME_MAP[phase];
849
+ const variants = {};
850
+ for (const v of PHASE_VARIANTS) {
851
+ variants[v] = v === activeVariant ? {} : void 0;
852
+ }
853
+ return new CairoCustomEnum(variants);
877
854
  }
878
855
  function parseTournament(raw, entryCount) {
879
856
  const obj = raw;
@@ -891,11 +868,16 @@ function parseTournament(raw, entryCount) {
891
868
  const gameEndDelay = Number(sched?.game_end_delay ?? 0);
892
869
  const submissionDuration = Number(sched?.submission_duration ?? 0);
893
870
  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);
871
+ const regStart = createdAt + registrationStartDelay;
872
+ const regEnd = regStart + registrationEndDelay;
873
+ const gameStart = createdAt + gameStartDelay;
874
+ const gameEnd = gameStart + gameEndDelay;
875
+ const subEnd = gameEnd + submissionDuration;
876
+ const registrationStartTime = String(regStart);
877
+ const registrationEndTime = String(regEnd);
878
+ const gameStartTime = String(gameStart);
879
+ const gameEndTime = String(gameEnd);
880
+ const submissionEndTime = String(subEnd);
899
881
  const gc = obj.game_config;
900
882
  const gameAddress = gc ? num.toHex(gc.game_address) : "";
901
883
  const settingsId = Number(gc?.settings_id ?? 0);
@@ -924,7 +906,7 @@ function parseTournament(raw, entryCount) {
924
906
  distributionCount: Number(ef.distribution_count ?? 0)
925
907
  };
926
908
  }
927
- const entryRequirement = parseOption(obj.entry_requirement) ?? null;
909
+ const entryRequirement = parseOption(obj.entry_requirement);
928
910
  const hasEntryRequirement = entryRequirement !== null;
929
911
  return {
930
912
  id,
@@ -1014,35 +996,30 @@ function parsePrize(raw) {
1014
996
  let distributionCount = null;
1015
997
  let payoutPosition = 0;
1016
998
  if (tokenTypeData) {
1017
- if ("erc20" in tokenTypeData) {
1018
- const erc20 = tokenTypeData.erc20;
999
+ const activeVariant = typeof tokenTypeData.activeVariant === "function" ? tokenTypeData.activeVariant() : tokenTypeData.erc20 !== void 0 ? "erc20" : tokenTypeData.erc721 !== void 0 ? "erc721" : null;
1000
+ const variantData = tokenTypeData.variant;
1001
+ const unwrap = (name) => variantData?.[name] ?? tokenTypeData[name];
1002
+ if (activeVariant === "erc20") {
1003
+ const erc20 = unwrap("erc20");
1019
1004
  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;
1005
+ amount = String(erc20?.amount ?? "0");
1006
+ const distOption = erc20?.distribution;
1007
+ const distInner = distOption?.Some;
1008
+ if (distInner) {
1009
+ const distVariant = distInner.variant ?? distInner;
1010
+ const distType = Object.keys(distVariant).find((k) => distVariant[k] !== void 0);
1011
+ if (distType) {
1012
+ distributionType = distType.toLowerCase();
1013
+ const distValue = distVariant[distType];
1014
+ distributionWeight = distValue != null ? Number(distValue) : null;
1040
1015
  }
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
1016
  }
1017
+ const countOption = erc20?.distribution_count;
1018
+ distributionCount = countOption?.Some != null ? Number(countOption.Some) : null;
1019
+ } else if (activeVariant === "erc721") {
1020
+ const erc721 = unwrap("erc721");
1021
+ tokenType = "erc721";
1022
+ tokenId = String(erc721?.id ?? "0");
1046
1023
  }
1047
1024
  }
1048
1025
  return {
@@ -1106,9 +1083,24 @@ async function viewerTournamentsByCreator(contract, creator, offset, limit) {
1106
1083
  return parseFilterResult(result);
1107
1084
  }, contract.address);
1108
1085
  }
1086
+ var PHASE_GROUPS = {
1087
+ scheduled: ["scheduled", "registration", "staging"],
1088
+ registration: ["registration"],
1089
+ staging: ["staging"],
1090
+ live: ["live", "submission"],
1091
+ submission: ["submission"],
1092
+ finalized: ["finalized"]
1093
+ };
1109
1094
  async function viewerTournamentsByPhase(contract, phase, offset, limit) {
1095
+ const phases = PHASE_GROUPS[phase];
1096
+ if (phases.length === 1) {
1097
+ return wrapRpcCall(async () => {
1098
+ const result = await contract.call("tournaments_by_phase", [phaseToRpcArg(phase), offset, limit]);
1099
+ return parseFilterResult(result);
1100
+ }, contract.address);
1101
+ }
1110
1102
  return wrapRpcCall(async () => {
1111
- const result = await contract.call("tournaments_by_phase", [phaseToRpcArg(phase), offset, limit]);
1103
+ const result = await contract.call("tournaments_by_phases", [phases.map(phaseToRpcArg), offset, limit]);
1112
1104
  return parseFilterResult(result);
1113
1105
  }, contract.address);
1114
1106
  }
@@ -1150,6 +1142,31 @@ async function viewerPrizes(contract, tournamentId) {
1150
1142
  return result.map(parsePrize);
1151
1143
  }, contract.address);
1152
1144
  }
1145
+ async function viewerRewardClaims(contract, tournamentId, offset, limit) {
1146
+ return wrapRpcCall(async () => {
1147
+ const result = await contract.call("tournament_reward_claims", [tournamentId, offset, limit]);
1148
+ const obj = result;
1149
+ const claims = obj.claims?.map((raw) => {
1150
+ const claim = raw;
1151
+ return {
1152
+ rewardType: claim.reward_type,
1153
+ claimed: Boolean(claim.claimed)
1154
+ };
1155
+ }) ?? [];
1156
+ return {
1157
+ claims,
1158
+ total: Number(obj.total ?? 0),
1159
+ totalClaimed: Number(obj.total_claimed ?? 0),
1160
+ totalUnclaimed: Number(obj.total_unclaimed ?? 0)
1161
+ };
1162
+ }, contract.address);
1163
+ }
1164
+ async function viewerPlayerTournaments(contract, playerAddress, offset, limit) {
1165
+ return wrapRpcCall(async () => {
1166
+ const result = await contract.call("player_tournaments", [playerAddress, offset, limit]);
1167
+ return parseFilterResult(result);
1168
+ }, contract.address);
1169
+ }
1153
1170
 
1154
1171
  // src/rpc/abis/budokanViewer.json
1155
1172
  var budokanViewer_default = [
@@ -1435,16 +1452,6 @@ var budokanViewer_default = [
1435
1452
  }
1436
1453
  ]
1437
1454
  },
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
1455
  {
1449
1456
  type: "struct",
1450
1457
  name: "core::array::Span::<core::felt252>",
@@ -1457,7 +1464,7 @@ var budokanViewer_default = [
1457
1464
  },
1458
1465
  {
1459
1466
  type: "struct",
1460
- name: "interfaces::extension::ExtensionConfig",
1467
+ name: "metagame_extensions_interfaces::extension::ExtensionConfig",
1461
1468
  members: [
1462
1469
  {
1463
1470
  name: "address",
@@ -1477,13 +1484,9 @@ var budokanViewer_default = [
1477
1484
  name: "token",
1478
1485
  type: "core::starknet::contract_address::ContractAddress"
1479
1486
  },
1480
- {
1481
- name: "allowlist",
1482
- type: "core::array::Span::<core::starknet::contract_address::ContractAddress>"
1483
- },
1484
1487
  {
1485
1488
  name: "extension",
1486
- type: "interfaces::extension::ExtensionConfig"
1489
+ type: "metagame_extensions_interfaces::extension::ExtensionConfig"
1487
1490
  }
1488
1491
  ]
1489
1492
  },
@@ -1743,6 +1746,92 @@ var budokanViewer_default = [
1743
1746
  }
1744
1747
  ]
1745
1748
  },
1749
+ {
1750
+ type: "enum",
1751
+ name: "game_components_interfaces::prize::PrizeType",
1752
+ variants: [
1753
+ {
1754
+ name: "Single",
1755
+ type: "core::integer::u64"
1756
+ },
1757
+ {
1758
+ name: "Distributed",
1759
+ type: "(core::integer::u64, core::integer::u32)"
1760
+ }
1761
+ ]
1762
+ },
1763
+ {
1764
+ type: "enum",
1765
+ name: "budokan_interfaces::budokan::EntryFeeRewardType",
1766
+ variants: [
1767
+ {
1768
+ name: "Position",
1769
+ type: "core::integer::u32"
1770
+ },
1771
+ {
1772
+ name: "TournamentCreator",
1773
+ type: "()"
1774
+ },
1775
+ {
1776
+ name: "GameCreator",
1777
+ type: "()"
1778
+ },
1779
+ {
1780
+ name: "Refund",
1781
+ type: "core::felt252"
1782
+ }
1783
+ ]
1784
+ },
1785
+ {
1786
+ type: "enum",
1787
+ name: "budokan_interfaces::budokan::RewardType",
1788
+ variants: [
1789
+ {
1790
+ name: "Prize",
1791
+ type: "game_components_interfaces::prize::PrizeType"
1792
+ },
1793
+ {
1794
+ name: "EntryFee",
1795
+ type: "budokan_interfaces::budokan::EntryFeeRewardType"
1796
+ }
1797
+ ]
1798
+ },
1799
+ {
1800
+ type: "struct",
1801
+ name: "budokan_interfaces::viewer::RewardClaimView",
1802
+ members: [
1803
+ {
1804
+ name: "reward_type",
1805
+ type: "budokan_interfaces::budokan::RewardType"
1806
+ },
1807
+ {
1808
+ name: "claimed",
1809
+ type: "core::bool"
1810
+ }
1811
+ ]
1812
+ },
1813
+ {
1814
+ type: "struct",
1815
+ name: "budokan_interfaces::viewer::RewardClaimResult",
1816
+ members: [
1817
+ {
1818
+ name: "claims",
1819
+ type: "core::array::Array::<budokan_interfaces::viewer::RewardClaimView>"
1820
+ },
1821
+ {
1822
+ name: "total",
1823
+ type: "core::integer::u32"
1824
+ },
1825
+ {
1826
+ name: "total_claimed",
1827
+ type: "core::integer::u32"
1828
+ },
1829
+ {
1830
+ name: "total_unclaimed",
1831
+ type: "core::integer::u32"
1832
+ }
1833
+ ]
1834
+ },
1746
1835
  {
1747
1836
  type: "interface",
1748
1837
  name: "budokan_interfaces::viewer::IBudokanViewer",
@@ -1839,6 +1928,30 @@ var budokanViewer_default = [
1839
1928
  ],
1840
1929
  state_mutability: "view"
1841
1930
  },
1931
+ {
1932
+ type: "function",
1933
+ name: "tournaments_by_phases",
1934
+ inputs: [
1935
+ {
1936
+ name: "phases",
1937
+ type: "core::array::Array::<budokan_interfaces::budokan::Phase>"
1938
+ },
1939
+ {
1940
+ name: "offset",
1941
+ type: "core::integer::u64"
1942
+ },
1943
+ {
1944
+ name: "limit",
1945
+ type: "core::integer::u64"
1946
+ }
1947
+ ],
1948
+ outputs: [
1949
+ {
1950
+ type: "budokan_interfaces::viewer::TournamentFilterResult"
1951
+ }
1952
+ ],
1953
+ state_mutability: "view"
1954
+ },
1842
1955
  {
1843
1956
  type: "function",
1844
1957
  name: "count_tournaments",
@@ -1898,6 +2011,22 @@ var budokanViewer_default = [
1898
2011
  ],
1899
2012
  state_mutability: "view"
1900
2013
  },
2014
+ {
2015
+ type: "function",
2016
+ name: "count_tournaments_by_phases",
2017
+ inputs: [
2018
+ {
2019
+ name: "phases",
2020
+ type: "core::array::Array::<budokan_interfaces::budokan::Phase>"
2021
+ }
2022
+ ],
2023
+ outputs: [
2024
+ {
2025
+ type: "core::integer::u64"
2026
+ }
2027
+ ],
2028
+ state_mutability: "view"
2029
+ },
1901
2030
  {
1902
2031
  type: "function",
1903
2032
  name: "tournament_detail",
@@ -1993,6 +2122,54 @@ var budokanViewer_default = [
1993
2122
  }
1994
2123
  ],
1995
2124
  state_mutability: "view"
2125
+ },
2126
+ {
2127
+ type: "function",
2128
+ name: "tournament_reward_claims",
2129
+ inputs: [
2130
+ {
2131
+ name: "tournament_id",
2132
+ type: "core::integer::u64"
2133
+ },
2134
+ {
2135
+ name: "offset",
2136
+ type: "core::integer::u32"
2137
+ },
2138
+ {
2139
+ name: "limit",
2140
+ type: "core::integer::u32"
2141
+ }
2142
+ ],
2143
+ outputs: [
2144
+ {
2145
+ type: "budokan_interfaces::viewer::RewardClaimResult"
2146
+ }
2147
+ ],
2148
+ state_mutability: "view"
2149
+ },
2150
+ {
2151
+ type: "function",
2152
+ name: "player_tournaments",
2153
+ inputs: [
2154
+ {
2155
+ name: "player_address",
2156
+ type: "core::starknet::contract_address::ContractAddress"
2157
+ },
2158
+ {
2159
+ name: "offset",
2160
+ type: "core::integer::u64"
2161
+ },
2162
+ {
2163
+ name: "limit",
2164
+ type: "core::integer::u64"
2165
+ }
2166
+ ],
2167
+ outputs: [
2168
+ {
2169
+ type: "budokan_interfaces::viewer::TournamentFilterResult"
2170
+ }
2171
+ ],
2172
+ state_mutability: "view"
1996
2173
  }
1997
2174
  ]
1998
2175
  },
@@ -2277,6 +2454,41 @@ var BudokanClient = class {
2277
2454
  let data = [];
2278
2455
  if (filterResult.tournamentIds.length > 0) {
2279
2456
  data = await viewerTournamentsBatch(contract, filterResult.tournamentIds);
2457
+ if (params?.includePrizeSummary) {
2458
+ const prizePromises = data.map(
2459
+ (t) => viewerPrizes(contract, t.id).catch(() => [])
2460
+ );
2461
+ const allPrizes = await Promise.all(prizePromises);
2462
+ data = data.map((t, i) => {
2463
+ const prizes = allPrizes[i];
2464
+ if (prizes.length === 0) return t;
2465
+ const tokenMap = /* @__PURE__ */ new Map();
2466
+ for (const p of prizes) {
2467
+ const key = p.tokenAddress;
2468
+ const existing = tokenMap.get(key);
2469
+ if (existing) {
2470
+ existing.totalAmount += BigInt(p.amount ?? "0");
2471
+ if (p.tokenType === "erc721") existing.nftCount += 1;
2472
+ } else {
2473
+ tokenMap.set(key, {
2474
+ tokenAddress: p.tokenAddress,
2475
+ tokenType: p.tokenType,
2476
+ totalAmount: BigInt(p.amount ?? "0"),
2477
+ nftCount: p.tokenType === "erc721" ? 1 : 0
2478
+ });
2479
+ }
2480
+ }
2481
+ return {
2482
+ ...t,
2483
+ prizeAggregation: Array.from(tokenMap.values()).map((v) => ({
2484
+ tokenAddress: v.tokenAddress,
2485
+ tokenType: v.tokenType,
2486
+ totalAmount: v.totalAmount.toString(),
2487
+ nftCount: v.nftCount
2488
+ }))
2489
+ };
2490
+ });
2491
+ }
2280
2492
  }
2281
2493
  return { data, total: filterResult.total, limit, offset };
2282
2494
  };
@@ -2367,10 +2579,32 @@ var BudokanClient = class {
2367
2579
  // ---- Player Queries (API-only, no on-chain equivalent) ----
2368
2580
  /**
2369
2581
  * Fetch tournaments that a player has registered for.
2370
- * API-only — no RPC fallback available.
2582
+ * Supports RPC fallback via viewer contract.
2371
2583
  */
2372
2584
  async getPlayerTournaments(address, params) {
2373
- return getPlayerTournaments(this.resolvedConfig.apiBaseUrl, address, params, this.apiCtx);
2585
+ const rpcFallback = async () => {
2586
+ const contract = await this.getViewerContract();
2587
+ const offset = params?.offset ?? 0;
2588
+ const limit = params?.limit ?? 20;
2589
+ const filterResult = await viewerPlayerTournaments(contract, address, offset, limit);
2590
+ let data = [];
2591
+ if (filterResult.tournamentIds.length > 0) {
2592
+ const tournaments = await viewerTournamentsBatch(contract, filterResult.tournamentIds);
2593
+ data = tournaments.map((t) => ({
2594
+ ...t,
2595
+ tournamentId: t.id
2596
+ }));
2597
+ }
2598
+ return { data, total: filterResult.total, limit, offset };
2599
+ };
2600
+ if (this.resolvedConfig.primarySource === "rpc") {
2601
+ return rpcFallback();
2602
+ }
2603
+ return withFallback(
2604
+ () => getPlayerTournaments(this.resolvedConfig.apiBaseUrl, address, params, this.apiCtx),
2605
+ rpcFallback,
2606
+ this.connectionStatus
2607
+ );
2374
2608
  }
2375
2609
  /**
2376
2610
  * Fetch stats for a player.
@@ -2412,20 +2646,55 @@ var BudokanClient = class {
2412
2646
  async getGameStats(gameAddress) {
2413
2647
  return getGameStats(this.resolvedConfig.apiBaseUrl, gameAddress, this.apiCtx);
2414
2648
  }
2415
- // ---- Reward Claims & Qualifications (API-only) ----
2649
+ // ---- Reward Claims & Qualifications ----
2416
2650
  /**
2417
2651
  * Fetch reward claims for a tournament.
2418
- * API-only -- no RPC fallback available.
2652
+ * Supports RPC fallback via viewer contract.
2419
2653
  */
2420
2654
  async getTournamentRewardClaims(tournamentId, params) {
2421
- return getTournamentRewardClaims(this.resolvedConfig.apiBaseUrl, tournamentId, params, this.apiCtx);
2655
+ const rpcFallback = async () => {
2656
+ const contract = await this.getViewerContract();
2657
+ const offset = params?.offset ?? 0;
2658
+ const limit = params?.limit ?? 100;
2659
+ const result = await viewerRewardClaims(contract, tournamentId, offset, limit);
2660
+ const data = result.claims.map((c) => ({
2661
+ tournamentId,
2662
+ rewardType: c.rewardType,
2663
+ claimed: c.claimed
2664
+ }));
2665
+ return { data, total: result.total, limit, offset };
2666
+ };
2667
+ if (this.resolvedConfig.primarySource === "rpc") {
2668
+ return rpcFallback();
2669
+ }
2670
+ return withFallback(
2671
+ () => getTournamentRewardClaims(this.resolvedConfig.apiBaseUrl, tournamentId, params, this.apiCtx),
2672
+ rpcFallback,
2673
+ this.connectionStatus
2674
+ );
2422
2675
  }
2423
2676
  /**
2424
2677
  * Fetch reward claims summary for a tournament.
2425
- * API-only -- no RPC fallback available.
2678
+ * Supports RPC fallback via viewer contract.
2426
2679
  */
2427
2680
  async getTournamentRewardClaimsSummary(tournamentId) {
2428
- return getTournamentRewardClaimsSummary(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx);
2681
+ const rpcFallback = async () => {
2682
+ const contract = await this.getViewerContract();
2683
+ const result = await viewerRewardClaims(contract, tournamentId, 0, 0);
2684
+ return {
2685
+ totalPrizes: result.total,
2686
+ totalClaimed: result.totalClaimed,
2687
+ totalUnclaimed: result.totalUnclaimed
2688
+ };
2689
+ };
2690
+ if (this.resolvedConfig.primarySource === "rpc") {
2691
+ return rpcFallback();
2692
+ }
2693
+ return withFallback(
2694
+ () => getTournamentRewardClaimsSummary(this.resolvedConfig.apiBaseUrl, tournamentId, this.apiCtx),
2695
+ rpcFallback,
2696
+ this.connectionStatus
2697
+ );
2429
2698
  }
2430
2699
  /**
2431
2700
  * Fetch qualifications for a tournament.
@@ -2528,12 +2797,22 @@ function useBudokanClient() {
2528
2797
  }
2529
2798
  return client;
2530
2799
  }
2800
+ function useResetOnClient(client, ...resetters) {
2801
+ useEffect(() => {
2802
+ for (const reset of resetters) {
2803
+ reset(null);
2804
+ }
2805
+ }, [client]);
2806
+ }
2807
+
2808
+ // src/react/useTournaments.ts
2531
2809
  function useTournaments(params) {
2532
2810
  const client = useBudokanClient();
2533
2811
  const [tournaments, setTournaments] = useState(null);
2534
2812
  const [loading, setLoading] = useState(!!params);
2535
2813
  const [error, setError] = useState(null);
2536
2814
  const paramsKey = JSON.stringify(params);
2815
+ useResetOnClient(client, setTournaments, setError);
2537
2816
  const fetch2 = useCallback(() => {
2538
2817
  if (params === void 0) return;
2539
2818
  setLoading(true);
@@ -2550,6 +2829,7 @@ function useTournament(tournamentId) {
2550
2829
  const [tournament, setTournament] = useState(null);
2551
2830
  const [loading, setLoading] = useState(!!tournamentId);
2552
2831
  const [error, setError] = useState(null);
2832
+ useResetOnClient(client, setTournament, setError);
2553
2833
  const fetch2 = useCallback(() => {
2554
2834
  if (!tournamentId) return;
2555
2835
  setLoading(true);
@@ -2566,6 +2846,7 @@ function useTournamentCount(phase) {
2566
2846
  const [count, setCount] = useState(null);
2567
2847
  const [loading, setLoading] = useState(true);
2568
2848
  const [error, setError] = useState(null);
2849
+ useResetOnClient(client, setCount, setError);
2569
2850
  const fetch2 = useCallback(() => {
2570
2851
  setLoading(true);
2571
2852
  setError(null);
@@ -2581,6 +2862,7 @@ function usePlayerTournamentCount(address, phase) {
2581
2862
  const [count, setCount] = useState(null);
2582
2863
  const [loading, setLoading] = useState(!!address);
2583
2864
  const [error, setError] = useState(null);
2865
+ useResetOnClient(client, setCount, setError);
2584
2866
  const fetch2 = useCallback(() => {
2585
2867
  if (!address) return;
2586
2868
  setLoading(true);
@@ -2597,6 +2879,7 @@ function useLeaderboard(tournamentId) {
2597
2879
  const [leaderboard, setLeaderboard] = useState(null);
2598
2880
  const [loading, setLoading] = useState(!!tournamentId);
2599
2881
  const [error, setError] = useState(null);
2882
+ useResetOnClient(client, setLeaderboard, setError);
2600
2883
  const fetch2 = useCallback(() => {
2601
2884
  if (!tournamentId) return;
2602
2885
  setLoading(true);
@@ -2613,6 +2896,7 @@ function useRegistrations(tournamentId, params) {
2613
2896
  const [registrations, setRegistrations] = useState(null);
2614
2897
  const [loading, setLoading] = useState(!!tournamentId);
2615
2898
  const [error, setError] = useState(null);
2899
+ useResetOnClient(client, setRegistrations, setError);
2616
2900
  const paramsKey = JSON.stringify(params);
2617
2901
  const fetch2 = useCallback(() => {
2618
2902
  if (!tournamentId) return;
@@ -2631,6 +2915,7 @@ function usePlayer(address) {
2631
2915
  const [stats, setStats] = useState(null);
2632
2916
  const [loading, setLoading] = useState(!!address);
2633
2917
  const [error, setError] = useState(null);
2918
+ useResetOnClient(client, setTournaments, setStats, setError);
2634
2919
  const fetch2 = useCallback(() => {
2635
2920
  if (!address) return;
2636
2921
  setLoading(true);
@@ -2653,6 +2938,7 @@ function usePlayerStats(address) {
2653
2938
  const [stats, setStats] = useState(null);
2654
2939
  const [loading, setLoading] = useState(!!address);
2655
2940
  const [error, setError] = useState(null);
2941
+ useResetOnClient(client, setStats, setError);
2656
2942
  const fetch2 = useCallback(() => {
2657
2943
  if (!address) return;
2658
2944
  setLoading(true);
@@ -2669,6 +2955,7 @@ function usePlayerTournaments(address, params) {
2669
2955
  const [tournaments, setTournaments] = useState(null);
2670
2956
  const [loading, setLoading] = useState(!!address);
2671
2957
  const [error, setError] = useState(null);
2958
+ useResetOnClient(client, setTournaments, setError);
2672
2959
  const paramsKey = JSON.stringify(params);
2673
2960
  const fetch2 = useCallback(() => {
2674
2961
  if (!address) return;
@@ -2686,6 +2973,7 @@ function useRewardClaims(tournamentId) {
2686
2973
  const [rewardClaims, setRewardClaims] = useState(null);
2687
2974
  const [loading, setLoading] = useState(!!tournamentId);
2688
2975
  const [error, setError] = useState(null);
2976
+ useResetOnClient(client, setRewardClaims, setError);
2689
2977
  const fetch2 = useCallback(() => {
2690
2978
  if (!tournamentId) return;
2691
2979
  setLoading(true);
@@ -2702,6 +2990,7 @@ function useRewardClaimsSummary(tournamentId) {
2702
2990
  const [summary, setSummary] = useState(null);
2703
2991
  const [loading, setLoading] = useState(!!tournamentId);
2704
2992
  const [error, setError] = useState(null);
2993
+ useResetOnClient(client, setSummary, setError);
2705
2994
  const fetch2 = useCallback(() => {
2706
2995
  if (!tournamentId) return;
2707
2996
  setLoading(true);
@@ -2718,6 +3007,7 @@ function usePrizes(tournamentId) {
2718
3007
  const [prizes, setPrizes] = useState(null);
2719
3008
  const [loading, setLoading] = useState(!!tournamentId);
2720
3009
  const [error, setError] = useState(null);
3010
+ useResetOnClient(client, setPrizes, setError);
2721
3011
  const fetch2 = useCallback(() => {
2722
3012
  if (!tournamentId) return;
2723
3013
  setLoading(true);
@@ -2734,6 +3024,7 @@ function usePrizeStats() {
2734
3024
  const [prizeStats, setPrizeStats] = useState(null);
2735
3025
  const [loading, setLoading] = useState(true);
2736
3026
  const [error, setError] = useState(null);
3027
+ useResetOnClient(client, setPrizeStats, setError);
2737
3028
  const fetch2 = useCallback(() => {
2738
3029
  setLoading(true);
2739
3030
  setError(null);
@@ -2749,6 +3040,7 @@ function usePrizeAggregation(tournamentId) {
2749
3040
  const [prizeAggregation, setPrizeAggregation] = useState(null);
2750
3041
  const [loading, setLoading] = useState(!!tournamentId);
2751
3042
  const [error, setError] = useState(null);
3043
+ useResetOnClient(client, setPrizeAggregation, setError);
2752
3044
  const fetch2 = useCallback(() => {
2753
3045
  if (!tournamentId) return;
2754
3046
  setLoading(true);
@@ -2765,6 +3057,7 @@ function useQualifications(tournamentId) {
2765
3057
  const [qualifications, setQualifications] = useState(null);
2766
3058
  const [loading, setLoading] = useState(!!tournamentId);
2767
3059
  const [error, setError] = useState(null);
3060
+ useResetOnClient(client, setQualifications, setError);
2768
3061
  const fetch2 = useCallback(() => {
2769
3062
  if (!tournamentId) return;
2770
3063
  setLoading(true);
@@ -2781,6 +3074,7 @@ function useActivityStats() {
2781
3074
  const [stats, setStats] = useState(null);
2782
3075
  const [loading, setLoading] = useState(true);
2783
3076
  const [error, setError] = useState(null);
3077
+ useResetOnClient(client, setStats, setError);
2784
3078
  const fetch2 = useCallback(() => {
2785
3079
  setLoading(true);
2786
3080
  setError(null);