@provable-games/metagame-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +251 -0
  2. package/package.json +94 -0
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # @provable-games/metagame-sdk
2
+
3
+ Shared types, utilities, and headless React hooks for Provable Games metagame UIs — powering [Budokan](https://github.com/Provable-Games/budokan) (tournaments) and [Bokendo](https://github.com/Provable-Games/bokendo) (quests).
4
+
5
+ ## Features
6
+
7
+ - **Unified types** — `Token`, `Prize`, `EntryFee`, `Participant`, `StatusTimestamps`
8
+ - **Utilities** — Address formatting, number display, prize aggregation, status computation, entry fee calculations, qualification evaluation, proof building, extension config parsing
9
+ - **Headless hooks** — Pagination, search, token selection, status indicators, score tables, prize tables, entry fee preview, entry/extension qualification
10
+ - **RPC utilities** — On-chain extension and fee/prize config reading via Starknet RPC
11
+ - **Tree-shakeable** — Main entry has zero React dependency; hooks and RPC are separate entry points
12
+ - **ESM + CJS** — Dual build with full TypeScript declarations
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @provable-games/metagame-sdk
18
+ # or
19
+ bun add @provable-games/metagame-sdk
20
+ ```
21
+
22
+ **Optional peer dependencies** (install only what you use):
23
+
24
+ ```bash
25
+ npm install react # For hooks (/react entry)
26
+ npm install starknet # For RPC utilities (/rpc entry)
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Utilities (no React needed)
32
+
33
+ ```ts
34
+ import {
35
+ indexAddress,
36
+ formatNumber,
37
+ formatPrizeAmount,
38
+ computeStatus,
39
+ groupPrizesByToken,
40
+ getOrdinalSuffix,
41
+ } from "@provable-games/metagame-sdk";
42
+
43
+ // Normalize a Starknet address
44
+ indexAddress("0x00000abc"); // "0xabc"
45
+
46
+ // Format numbers with smart suffixes
47
+ formatNumber(1500000); // "1.5m"
48
+ formatNumber(2500); // "2.5k"
49
+
50
+ // Compute tournament/quest status from timestamps
51
+ const status = computeStatus({
52
+ registrationStart: 1000,
53
+ registrationEnd: 2000,
54
+ start: 3000,
55
+ end: 4000,
56
+ }, Date.now() / 1000);
57
+ // { label: "Live", variant: "active", isActive: true, countdown: { ... } }
58
+
59
+ // Group prizes by token
60
+ const grouped = groupPrizesByToken(prizes);
61
+ // { "0xabc": { type: "erc20", address: "0xabc", totalAmount: "150" } }
62
+
63
+ // Ordinal suffixes
64
+ getOrdinalSuffix(1); // "st"
65
+ getOrdinalSuffix(22); // "nd"
66
+ ```
67
+
68
+ ### Entry Fee & Prize Calculations
69
+
70
+ ```ts
71
+ import {
72
+ calculateEntryFeeBreakdown,
73
+ distributePool,
74
+ buildEntryFeePrizes,
75
+ calculateTotalPrizeValueUSD,
76
+ calculatePaidPlaces,
77
+ } from "@provable-games/metagame-sdk";
78
+
79
+ // Break down entry fee into creator/protocol/prize shares
80
+ const breakdown = calculateEntryFeeBreakdown(entryFee, participantCount);
81
+
82
+ // Calculate prize pool distribution
83
+ const distribution = distributePool(totalPool, positions, weight);
84
+ ```
85
+
86
+ ### Qualification & Proofs
87
+
88
+ ```ts
89
+ import {
90
+ evaluateQualification,
91
+ buildQualificationProof,
92
+ identifyExtensionType,
93
+ parseTournamentValidatorConfig,
94
+ } from "@provable-games/metagame-sdk";
95
+
96
+ // Check if a user qualifies for entry
97
+ const result = evaluateQualification(requirement, userState);
98
+
99
+ // Build proof for on-chain verification
100
+ const proof = buildQualificationProof(qualificationData);
101
+
102
+ // Parse extension configs from on-chain data
103
+ const config = parseTournamentValidatorConfig(rawConfig);
104
+ ```
105
+
106
+ ### React Hooks
107
+
108
+ ```tsx
109
+ import { useStatusIndicator, useTokenSelector, usePagination } from "@provable-games/metagame-sdk/react";
110
+
111
+ // Auto-refreshing status indicator
112
+ function StatusBadge({ timestamps }) {
113
+ const status = useStatusIndicator(timestamps); // refreshes every second
114
+ return <span className={status.variant}>{status.label}</span>;
115
+ }
116
+
117
+ // Token selector with search + pagination
118
+ function TokenPicker({ tokens }) {
119
+ const { search, setSearch, filteredTokens, select, pagination, getTokenProps } =
120
+ useTokenSelector({ tokens, tokenType: "erc20", pageSize: 10 });
121
+
122
+ return (
123
+ <div>
124
+ <input value={search} onChange={(e) => setSearch(e.target.value)} />
125
+ {filteredTokens.map((token) => (
126
+ <div key={token.address} {...getTokenProps(token)}>
127
+ {token.symbol}
128
+ </div>
129
+ ))}
130
+ <button onClick={pagination.prev} disabled={!pagination.hasPrev}>Prev</button>
131
+ <button onClick={pagination.next} disabled={!pagination.hasNext}>Next</button>
132
+ </div>
133
+ );
134
+ }
135
+
136
+ // Entry qualification check
137
+ import { useEntryQualification } from "@provable-games/metagame-sdk/react";
138
+
139
+ function EntryGate({ requirement, userAddress }) {
140
+ const { qualified, reason, loading } = useEntryQualification({ requirement, userAddress });
141
+ if (loading) return <span>Checking...</span>;
142
+ return qualified ? <button>Enter</button> : <span>{reason}</span>;
143
+ }
144
+ ```
145
+
146
+ ### RPC Utilities
147
+
148
+ ```ts
149
+ import { readExtensionConfig, readFeeAndPrizeExtensions } from "@provable-games/metagame-sdk/rpc";
150
+
151
+ // Read extension configuration from on-chain contracts
152
+ const config = await readExtensionConfig(provider, extensionAddress);
153
+ ```
154
+
155
+ ## API Reference
156
+
157
+ ### Types
158
+
159
+ | Type | Fields |
160
+ |------|--------|
161
+ | `Token` | `address`, `name`, `symbol`, `tokenType`, `decimals?`, `logoUrl?` |
162
+ | `Prize` | `id`, `position`, `tokenAddress`, `tokenType`, `amount`, `sponsorAddress` |
163
+ | `EntryFee` | `tokenAddress`, `amount`, `creatorShare?`, `refundShare?` |
164
+ | `EntryRequirement` | `requirementType`, `tokenAddress?`, `entryLimit?` |
165
+ | `Participant` | `address`, `rank?`, `score?`, `name?`, `entryNumber?` |
166
+ | `StatusTimestamps` | `start`, `end`, `registrationStart?`, `registrationEnd?`, `submissionEnd?`, `completed?`, `unlocked?` |
167
+ | `StatusResult` | `label`, `variant`, `isActive`, `countdown` |
168
+
169
+ ### Utilities
170
+
171
+ | Function | Description |
172
+ |----------|-------------|
173
+ | `indexAddress(address)` | Strip leading zeros from hex address |
174
+ | `padAddress(address)` | Pad address to 66 characters |
175
+ | `displayAddress(address)` | Truncate to `0x1234...abcd` |
176
+ | `bigintToHex(value)` | Convert bigint/number/string to hex |
177
+ | `formatNumber(num)` | Smart formatting with k/m suffixes |
178
+ | `formatPrizeAmount(num)` | Precision-aware prize display |
179
+ | `formatUsdValue(value)` | USD display with `<0.01` handling |
180
+ | `formatScore(num)` | Score display formatting |
181
+ | `formatTime(seconds)` | Human-readable duration ("2 Days", "3 Hours") |
182
+ | `getOrdinalSuffix(n)` | Returns "st", "nd", "rd", "th" |
183
+ | `calculatePayouts(places, weight)` | Weighted payout percentages summing to 100 |
184
+ | `calculateDistribution(positions, weight, ...)` | Basis-point distribution matching contract logic |
185
+ | `groupPrizesByToken(prizes)` | Group prizes by token address |
186
+ | `calculatePrizeValue(amount, decimals, price)` | Compute USD value of a prize |
187
+ | `computeStatus(timestamps, now?)` | Derive status label, variant, and countdown |
188
+ | `isBefore(date1, date2)` | Date comparison |
189
+ | `calculateEntryFeeBreakdown(fee, count)` | Break down entry fee into shares |
190
+ | `distributePool(pool, positions, weight)` | Distribute prize pool across positions |
191
+ | `aggregatePrizesByPosition(prizes)` | Group prizes by leaderboard position |
192
+ | `aggregatePrizesBySponsor(prizes)` | Group prizes by sponsor address |
193
+ | `filterClaimablePrizes(prizes, ...)` | Filter prizes claimable by a participant |
194
+ | `filterZeroPrizes(prizes)` | Remove zero-amount prizes |
195
+ | `parseNFTBulkInput(input)` | Parse bulk NFT token ID input |
196
+ | `getExtensionAddresses(chain)` | Get known extension contract addresses |
197
+ | `identifyExtensionType(address)` | Identify extension type from address |
198
+ | `parseTournamentValidatorConfig(config)` | Parse tournament validator config |
199
+ | `parseERC20BalanceValidatorConfig(config)` | Parse ERC20 balance validator config |
200
+ | `parseOpusTrovesValidatorConfig(config)` | Parse Opus Troves validator config |
201
+ | `evaluateQualification(requirement, state)` | Evaluate entry qualification |
202
+ | `buildQualificationProof(data)` | Build proof for on-chain verification |
203
+ | `buildEntryFeePrizes(config)` | Build prize list from entry fee config |
204
+ | `calculateTotalPrizeValueUSD(prizes, ...)` | Calculate total prize pool USD value |
205
+ | `calculatePaidPlaces(prizes)` | Count positions with non-zero prizes |
206
+ | `buildParticipationMap(registrations)` | Map addresses to participation counts |
207
+ | `buildWinMap(leaderboards)` | Map addresses to win counts |
208
+ | `resolveTournamentQualifications(input)` | Resolve tournament validator qualifications |
209
+ | `calculateOpusTrovesEntries(troves)` | Calculate entries from Opus Troves |
210
+ | `findBannableEntries(entries, ...)` | Find entries eligible for banning |
211
+
212
+ ### React Hooks (`/react` entry)
213
+
214
+ | Hook | Description |
215
+ |------|-------------|
216
+ | `useDebounce(value, delay)` | Debounce any value |
217
+ | `usePagination({ totalItems, pageSize? })` | Page state with next/prev/reset |
218
+ | `useSearchFilter({ items, searchFields, debounceMs? })` | Filtered list with debounced search |
219
+ | `useTokenSelector({ tokens, tokenType?, pageSize? })` | Token search + selection + pagination |
220
+ | `useStatusIndicator(timestamps, refreshMs?)` | Auto-refreshing status computation |
221
+ | `useScoreTable({ participants, pageSize?, sortField? })` | Sorted, searchable, paginated score table |
222
+ | `usePrizeTable({ prizes, ... })` | Prize display with grouping and pagination |
223
+ | `useEntryFeePreview({ entryFee, ... })` | Entry fee breakdown preview |
224
+ | `useEntryQualification({ requirement, ... })` | Entry qualification check |
225
+ | `useExtensionQualification({ extension, ... })` | Extension-based qualification evaluation |
226
+ | `useOpusTrovesBannableEntries({ entries, ... })` | Identify bannable Opus Troves entries |
227
+
228
+ ### RPC Utilities (`/rpc` entry)
229
+
230
+ | Function | Description |
231
+ |----------|-------------|
232
+ | `readExtensionConfig(provider, address)` | Read extension config from chain |
233
+ | `readFeeAndPrizeExtensions(provider, ...)` | Read fee and prize extension data |
234
+
235
+ ## Development
236
+
237
+ ```bash
238
+ npm install
239
+ npm run build # ESM + CJS to dist/
240
+ npm run typecheck # TypeScript validation
241
+ npm test # Unit tests
242
+ npm run dev # Watch mode
243
+ ```
244
+
245
+ ## Publishing
246
+
247
+ Automated via GitHub Actions — create a GitHub release to trigger npm publish.
248
+
249
+ ## License
250
+
251
+ MIT
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@provable-games/metagame-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Shared types, utilities, and headless hooks for Provable Games metagame SDKs",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "./react": {
21
+ "import": {
22
+ "types": "./dist/react.d.ts",
23
+ "default": "./dist/react.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/react.d.cts",
27
+ "default": "./dist/react.cjs"
28
+ }
29
+ },
30
+ "./rpc": {
31
+ "import": {
32
+ "types": "./dist/rpc.d.ts",
33
+ "default": "./dist/rpc.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/rpc.d.cts",
37
+ "default": "./dist/rpc.cjs"
38
+ }
39
+ }
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "sideEffects": false,
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "test": "vitest run",
49
+ "test:watch": "vitest",
50
+ "typecheck": "tsc --noEmit",
51
+ "clean": "rm -rf dist"
52
+ },
53
+ "keywords": [
54
+ "starknet",
55
+ "metagame",
56
+ "gaming",
57
+ "ui",
58
+ "hooks",
59
+ "sdk"
60
+ ],
61
+ "author": "Provable Games",
62
+ "license": "MIT",
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "devDependencies": {
67
+ "@testing-library/react": "^16.0.0",
68
+ "@types/node": "^22.0.0",
69
+ "@types/react": "^19.0.0",
70
+ "react": "^19.0.0",
71
+ "starknet": "^6.24.1",
72
+ "tsup": "^8.4.0",
73
+ "typescript": "^5.7.0",
74
+ "vitest": "^3.0.0"
75
+ },
76
+ "peerDependencies": {
77
+ "react": ">=18.0.0",
78
+ "starknet": ">=6.0.0"
79
+ },
80
+ "peerDependenciesMeta": {
81
+ "react": {
82
+ "optional": true
83
+ },
84
+ "starknet": {
85
+ "optional": true
86
+ }
87
+ },
88
+ "engines": {
89
+ "node": ">=22.0.0"
90
+ },
91
+ "dependencies": {
92
+ "@rollup/rollup-linux-arm64-gnu": "^4.59.0"
93
+ }
94
+ }