@provable-games/metagame-sdk 0.1.0 → 0.1.2

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.
@@ -0,0 +1,489 @@
1
+ import { P as Prize, S as StatusTimestamps, a as StatusResult, E as ExtensionAddresses, Q as QualifyingModeInfo, b as ExtensionType, c as ERC20BalanceValidatorConfig, T as TournamentValidatorConfig, O as OpusTrovesValidatorConfig, d as SnapshotValidatorConfig, Z as ZkPassportValidatorConfig, G as GovernanceValidatorConfig, e as QualificationResult, f as EntryRequirementVariant, g as QualificationProof } from './prizeAggregation-CHwIJzXr.cjs';
2
+ export { D as DistributionType, h as EntryFeeBreakdown, i as EntryFeeExtensionConfig, j as EntryFeeShares, k as ExtensionCategory, l as Participant, m as PositionPrizeGroup, n as PrizeExtensionConfig, o as QualificationEntry, p as QualifyingMode, q as SponsorContribution, r as StatusVariant, s as Token, t as aggregatePrizesByPosition, u as aggregatePrizesBySponsor, v as calculateDistribution, w as calculateEntryFeeBreakdown, x as calculatePayouts, y as distributePool, z as filterClaimablePrizes, A as filterZeroPrizes, B as formatNumber, C as formatPrizeAmount, F as formatScore, H as formatTime, I as formatUsdValue, J as getOrdinalSuffix } from './prizeAggregation-CHwIJzXr.cjs';
3
+ import { CairoOption, CairoCustomEnum } from 'starknet';
4
+
5
+ interface EntryFee {
6
+ tokenAddress: string;
7
+ amount: string;
8
+ tournamentCreatorShare?: number;
9
+ gameCreatorShare?: number;
10
+ refundShare?: number;
11
+ distribution?: Distribution | null;
12
+ distributionCount?: number;
13
+ }
14
+ interface EntryRequirement {
15
+ requirementType: "token" | "extension";
16
+ tokenAddress?: string;
17
+ entryLimit?: number;
18
+ extensionConfig?: Record<string, unknown>;
19
+ }
20
+ type Distribution = {
21
+ Linear: number;
22
+ } | {
23
+ Exponential: number;
24
+ } | {
25
+ Uniform: Record<string, never>;
26
+ } | {
27
+ Custom: [number, ...number[]];
28
+ };
29
+
30
+ declare function indexAddress(address: string): string;
31
+ declare function padAddress(address: string): string;
32
+ declare function displayAddress(address: string): string;
33
+ declare function bigintToHex(v: bigint | string | number): string;
34
+
35
+ interface GroupedTokenPrize {
36
+ type: "erc20" | "erc721";
37
+ address: string;
38
+ totalAmount: string;
39
+ }
40
+ declare function groupPrizesByToken(prizes: Prize[]): Record<string, GroupedTokenPrize>;
41
+ declare function calculatePrizeValue(amount: string, decimals: number, price: number): number;
42
+
43
+ declare function computeStatus(timestamps: StatusTimestamps, now?: number): StatusResult;
44
+
45
+ declare function isBefore(date1: Date, date2: Date): boolean;
46
+
47
+ /**
48
+ * NFT bulk input parsing for prize creation forms.
49
+ *
50
+ * Metagame UIs often need a textarea where users can paste multiple NFT
51
+ * token IDs with positions. This parser handles two common formats:
52
+ *
53
+ * 1. JSON array: [{ "tokenId": 1, "position": 1 }, ...]
54
+ * 2. Key-value: "tokenId:position" per line or comma-separated
55
+ */
56
+ interface NftPrizeInput {
57
+ tokenId: string;
58
+ position: number;
59
+ }
60
+ interface NftParseResult {
61
+ entries: NftPrizeInput[];
62
+ errors: string[];
63
+ }
64
+ /**
65
+ * Parse bulk NFT input text into structured entries.
66
+ *
67
+ * Supports:
68
+ * - JSON array: `[{"tokenId": 1, "position": 1}]`
69
+ * - Key-value lines: `123:1` (tokenId:position)
70
+ * - Comma-separated: `123:1, 456:2, 789:3`
71
+ *
72
+ * @param input - Raw text from a textarea
73
+ * @returns Parsed entries and any validation errors
74
+ */
75
+ declare function parseNFTBulkInput(input: string): NftParseResult;
76
+
77
+ /**
78
+ * Extension utilities for EGS entry requirements.
79
+ *
80
+ * The EGS ecosystem has shared extension contracts that any metagame can use
81
+ * for gating entry. These utilities handle:
82
+ * - Identifying which extension type an address corresponds to
83
+ * - Parsing the on-chain config arrays into structured objects
84
+ * - Providing deployed addresses per chain
85
+ *
86
+ * The extension contracts are deployed once and shared across metagames.
87
+ * The config format is defined by each extension's Cairo contract — these
88
+ * parsers match that format exactly.
89
+ */
90
+
91
+ /**
92
+ * Get deployed extension addresses for a chain.
93
+ */
94
+ declare function getExtensionAddresses(chainId: string): ExtensionAddresses;
95
+ /**
96
+ * Get supported Opus collateral asset addresses for a chain.
97
+ */
98
+ declare function getOpusSupportedAssets(chainId: string): string[];
99
+ /**
100
+ * Get all whitelisted extension addresses for a chain (normalized).
101
+ */
102
+ declare function getWhitelistedExtensionAddresses(chainId: string): Set<string>;
103
+ /**
104
+ * Check if an extension address is whitelisted for a chain.
105
+ */
106
+ declare function isWhitelistedExtension(extensionAddress: string, chainId: string): boolean;
107
+ /**
108
+ * Identify which extension type a contract address corresponds to.
109
+ *
110
+ * @param extensionAddress - The extension contract address from the entry requirement
111
+ * @param chainId - The chain ID (e.g., "SN_MAIN", "SN_SEPOLIA")
112
+ * @returns The extension type, or "unknown" if not a recognized preset
113
+ */
114
+ declare function identifyExtensionType(extensionAddress: string, chainId: string): ExtensionType;
115
+ /**
116
+ * Parse a tournament validator extension config array.
117
+ *
118
+ * Config format: [qualifier_type, qualifying_mode, top_positions, ...tournament_ids]
119
+ * - qualifier_type: "0" = participated, "1" = won
120
+ * - qualifying_mode: 0-5 (see QualifyingMode enum)
121
+ * - top_positions: 0 = all positions, or N = top N
122
+ * - remaining values: tournament IDs
123
+ */
124
+ declare function parseTournamentValidatorConfig(config: Array<string | number | bigint>): TournamentValidatorConfig | null;
125
+ /**
126
+ * Parse an ERC20 balance validator extension config array.
127
+ *
128
+ * Config format: [token_address, min_low, min_high, max_low, max_high, vpe_low, vpe_high, max_entries]
129
+ * Values are u256 split into low (128-bit) and high (128-bit) parts.
130
+ */
131
+ declare function parseERC20BalanceValidatorConfig(config: Array<string | number | bigint>): ERC20BalanceValidatorConfig | null;
132
+ /**
133
+ * Parse an Opus Troves validator extension config array.
134
+ *
135
+ * Config format: [asset_count, ...asset_addresses, threshold, value_per_entry, max_entries]
136
+ * - asset_count: 0 = wildcard (any collateral), N = specific assets
137
+ * - asset_addresses: N addresses following asset_count
138
+ * - threshold: minimum debt in CASH (18 decimals, 1:1 USD)
139
+ * - value_per_entry: debt per additional entry
140
+ * - max_entries: cap on entries (0 = unlimited)
141
+ */
142
+ declare function parseOpusTrovesValidatorConfig(config: Array<string | number | bigint>): OpusTrovesValidatorConfig | null;
143
+ /**
144
+ * Parse a Snapshot validator extension config array.
145
+ *
146
+ * Config format: [snapshot_space_id]
147
+ */
148
+ declare function parseSnapshotValidatorConfig(config: Array<string | number | bigint>): SnapshotValidatorConfig | null;
149
+ /**
150
+ * Parse a ZK Passport validator extension config array.
151
+ *
152
+ * Config format: [verifier_address, service_scope, subscope, param_commitment, max_proof_age, nullifier_type]
153
+ */
154
+ declare function parseZkPassportValidatorConfig(config: Array<string | number | bigint>): ZkPassportValidatorConfig | null;
155
+ /**
156
+ * Parse a Governance validator extension config array.
157
+ *
158
+ * Config format: [governor_address, governance_token_address, balance_threshold_low, balance_threshold_high,
159
+ * proposal_id_low, proposal_id_high, check_voted, votes_threshold_low, votes_threshold_high,
160
+ * votes_per_entry_low, votes_per_entry_high]
161
+ */
162
+ declare function parseGovernanceValidatorConfig(config: Array<string | number | bigint>): GovernanceValidatorConfig | null;
163
+ /**
164
+ * Parse any extension config by first identifying its type.
165
+ *
166
+ * Returns the parsed config along with the identified type. Useful when
167
+ * you have a raw extension config and need to determine both what it is
168
+ * and what its parameters are in one call.
169
+ */
170
+ declare function parseExtensionConfig(extensionAddress: string, config: Array<string | number | bigint>, chainId: string): {
171
+ type: "tournament";
172
+ config: TournamentValidatorConfig;
173
+ } | {
174
+ type: "erc20Balance";
175
+ config: ERC20BalanceValidatorConfig;
176
+ } | {
177
+ type: "opusTroves";
178
+ config: OpusTrovesValidatorConfig;
179
+ } | {
180
+ type: "snapshot";
181
+ config: SnapshotValidatorConfig;
182
+ } | {
183
+ type: "zkPassport";
184
+ config: ZkPassportValidatorConfig;
185
+ } | {
186
+ type: "governance";
187
+ config: GovernanceValidatorConfig;
188
+ } | {
189
+ type: "unknown";
190
+ config: null;
191
+ } | null;
192
+ /**
193
+ * Get a human-readable label and description for a qualifying mode.
194
+ */
195
+ declare function getQualifyingModeInfo(mode: number): QualifyingModeInfo;
196
+ /**
197
+ * Format a raw token amount (bigint) to a human-readable string with decimals.
198
+ * Removes trailing zeros from the decimal part.
199
+ */
200
+ declare function formatTokenAmount(value: bigint, decimals: number): string;
201
+ /**
202
+ * Format a CASH value (18 decimals, 1:1 USD) to a USD string.
203
+ */
204
+ declare function formatCashToUSD(value: bigint): string;
205
+
206
+ /**
207
+ * Entry qualification evaluation.
208
+ *
209
+ * Pure functions that determine whether a user qualifies to enter a metagame
210
+ * event based on pre-fetched data. Each metagame fetches the data through
211
+ * their own SDK, then passes it here for evaluation.
212
+ *
213
+ * The evaluation logic is the same across metagames because the entry
214
+ * requirement system (token gating, extensions) is shared.
215
+ */
216
+
217
+ interface TokenQualificationInput {
218
+ /** NFT token IDs owned by the player */
219
+ ownedTokenIds: string[];
220
+ /** Number of times each token has been used to enter (tokenId → count) */
221
+ usedEntryCounts: Record<string, number>;
222
+ /** Max entries per token (0 = unlimited) */
223
+ entryLimit: number;
224
+ }
225
+ /**
226
+ * Evaluate token-based entry qualification.
227
+ *
228
+ * Checks if the player owns any qualifying NFTs and how many entries
229
+ * remain for each. Selects the token with the most entries remaining
230
+ * as the best proof.
231
+ */
232
+ declare function evaluateTokenQualification(input: TokenQualificationInput): QualificationResult;
233
+ interface ExtensionQualificationInput {
234
+ /** Results from checking each qualification method against the extension contract */
235
+ checkedQualifications: Array<{
236
+ id: string;
237
+ entriesLeft: number;
238
+ proof: string[];
239
+ label?: string;
240
+ metadata?: Record<string, unknown>;
241
+ }>;
242
+ }
243
+ /**
244
+ * Evaluate extension-based entry qualification from pre-checked results.
245
+ *
246
+ * Each metagame calls the extension contract's `entries_left` function
247
+ * for each potential qualification proof, then passes the results here.
248
+ * This function aggregates and selects the best qualification.
249
+ */
250
+ declare function evaluateExtensionQualification(input: ExtensionQualificationInput): QualificationResult;
251
+ /**
252
+ * Evaluate entry qualification based on requirement variant.
253
+ *
254
+ * This is the top-level function — pass in the requirement type and
255
+ * the appropriate input, get back a unified QualificationResult.
256
+ */
257
+ declare function evaluateQualification(variant: EntryRequirementVariant, input: {
258
+ type: "token";
259
+ data: TokenQualificationInput;
260
+ } | {
261
+ type: "extension";
262
+ data: ExtensionQualificationInput;
263
+ } | {
264
+ type: "none";
265
+ }): QualificationResult;
266
+
267
+ /**
268
+ * Qualification proof construction for EGS entry requirements.
269
+ *
270
+ * Builds the on-chain proof calldata that metagames pass when entering
271
+ * an event. The proof format matches the Cairo `QualificationProof` enum:
272
+ *
273
+ * enum QualificationProof {
274
+ * NFT: NFTQualification, // { token_id: u256 }
275
+ * Extension: Span<felt252>,
276
+ * }
277
+ *
278
+ * Extension-specific data (tournament IDs, positions, addresses, etc.)
279
+ * is encoded into the `Extension` variant's felt252 array by the
280
+ * extension's own logic.
281
+ */
282
+
283
+ /**
284
+ * Build the on-chain qualification proof from a QualificationProof object.
285
+ *
286
+ * Returns a `CairoOption<CairoCustomEnum>` ready to pass to a metagame
287
+ * contract's entry function. Returns `None` if no proof is needed.
288
+ *
289
+ * @param proof - The qualification proof from evaluateQualification()
290
+ * @returns CairoOption wrapping the proof enum, ready for contract call
291
+ */
292
+ declare function buildQualificationProof(proof: QualificationProof | null): CairoOption<CairoCustomEnum>;
293
+ /**
294
+ * Build an NFT ownership proof.
295
+ * Used when entry requires holding a specific NFT collection.
296
+ */
297
+ declare function buildNFTProof(tokenId: string): CairoOption<CairoCustomEnum>;
298
+ /**
299
+ * Build a tournament validator extension proof.
300
+ * Used when entry requires participation/winning in a prior tournament.
301
+ *
302
+ * The proof array is: [tournament_id, token_id, position]
303
+ */
304
+ declare function buildTournamentExtensionProof(tournamentId: string, tokenId: string, position: number): CairoOption<CairoCustomEnum>;
305
+ /**
306
+ * Build a generic extension proof.
307
+ * Used for any extension validator (ERC20 balance, Opus Troves, Snapshot, etc.).
308
+ *
309
+ * @param proofData - Array of felt252 values to pass to the extension contract
310
+ */
311
+ declare function buildExtensionProof(proofData: string[]): CairoOption<CairoCustomEnum>;
312
+
313
+ /**
314
+ * Tournament validator qualification resolution.
315
+ *
316
+ * The tournament validator extension requires players to have participated
317
+ * in (or won) specific prior tournaments. This module resolves which of
318
+ * a player's game tokens can serve as proof of qualification.
319
+ *
320
+ * Every metagame that uses the tournament validator extension needs this
321
+ * same logic — cross-referencing the player's registrations and leaderboard
322
+ * positions against the validator's required tournaments.
323
+ */
324
+
325
+ /** A registration record — player entered a tournament with a game token */
326
+ interface TournamentRegistration {
327
+ tournamentId: string;
328
+ gameTokenId: string;
329
+ }
330
+ /** A leaderboard — ordered list of token IDs for a finalized tournament */
331
+ interface TournamentLeaderboard {
332
+ tournamentId: string;
333
+ /** Token IDs in rank order (index 0 = 1st place) */
334
+ tokenIds: string[];
335
+ /** Whether this tournament has finalized */
336
+ isFinalized: boolean;
337
+ }
338
+ /** A resolved qualification proof for the tournament validator */
339
+ interface TournamentQualificationInput {
340
+ tournamentId: string;
341
+ tokenId: string;
342
+ position: number;
343
+ tournamentName?: string;
344
+ }
345
+ /**
346
+ * Build a map of tournament ID → game token IDs the player used to enter.
347
+ *
348
+ * @param registrations - All registrations for the required tournaments
349
+ * @param requiredTournamentIds - Tournament IDs from the validator config
350
+ * @returns Map of tournamentId → array of game token IDs the player registered with
351
+ */
352
+ declare function buildParticipationMap(registrations: TournamentRegistration[], requiredTournamentIds: string[]): Record<string, string[]>;
353
+ /**
354
+ * Build a map of tournament ID → winning token positions the player holds.
355
+ *
356
+ * Cross-references finalized leaderboards with the player's owned game tokens
357
+ * to find which tokens placed on which leaderboards.
358
+ *
359
+ * @param leaderboards - Leaderboards for the required tournaments
360
+ * @param ownedGameTokenIds - Game token IDs the player currently owns
361
+ * @param topPositions - Only count positions within top N (0 = all positions)
362
+ * @returns Map of tournamentId → array of { tokenId, position }
363
+ */
364
+ declare function buildWinMap(leaderboards: TournamentLeaderboard[], ownedGameTokenIds: string[], topPositions?: number): Record<string, Array<{
365
+ tokenId: string;
366
+ position: number;
367
+ }>>;
368
+ /**
369
+ * Resolve tournament validator qualification inputs from player data.
370
+ *
371
+ * Takes the validator config, the player's registrations/leaderboard positions,
372
+ * and returns the list of proofs to check against the extension contract.
373
+ *
374
+ * @param config - Parsed tournament validator config
375
+ * @param participationMap - From buildParticipationMap()
376
+ * @param winMap - From buildWinMap()
377
+ * @param tournamentNames - Optional map of tournamentId → display name
378
+ * @returns Array of qualification inputs to pass to the extension contract
379
+ */
380
+ declare function resolveTournamentQualifications(config: TournamentValidatorConfig, participationMap: Record<string, string[]>, winMap: Record<string, Array<{
381
+ tokenId: string;
382
+ position: number;
383
+ }>>, tournamentNames?: Record<string, string>): TournamentQualificationInput[];
384
+
385
+ /**
386
+ * Opus Troves entry calculation.
387
+ *
388
+ * The Opus Troves extension gates entry based on the player's CASH debt
389
+ * in the Opus Protocol. This module contains the pure math for determining
390
+ * how many entries a given debt level allows, and which entries are bannable
391
+ * if a player's debt drops below the threshold.
392
+ *
393
+ * The trove debt fetching (getUserTroveIds, getTroveHealth) is RPC-specific
394
+ * and stays in each app. This module handles the calculation once you have
395
+ * the debt value.
396
+ */
397
+
398
+ /**
399
+ * Calculate how many entries a player's total debt allows.
400
+ *
401
+ * Two modes based on config:
402
+ * - Proportional (valuePerEntry > 0): entries = (debt - threshold) / valuePerEntry
403
+ * - Fixed (valuePerEntry == 0): if debt >= threshold → maxEntries
404
+ *
405
+ * @param debt - Player's total CASH debt across all troves (18 decimals)
406
+ * @param config - Parsed Opus Troves validator config
407
+ * @returns Number of entries allowed (0 if below threshold)
408
+ */
409
+ declare function calculateOpusTrovesEntries(debt: bigint, config: OpusTrovesValidatorConfig): number;
410
+ /**
411
+ * Determine which entries should be banned based on debt vs allowed entries.
412
+ *
413
+ * When a player's debt drops below what their entries require, the excess
414
+ * entries (sorted by token ID ascending — oldest first) become bannable.
415
+ *
416
+ * @param registeredTokenIds - Token IDs the player registered with
417
+ * @param entriesAllowed - From calculateOpusTrovesEntries()
418
+ * @returns Set of token IDs that should be banned
419
+ */
420
+ declare function findBannableEntries(registeredTokenIds: string[], entriesAllowed: number): Set<string>;
421
+ /**
422
+ * Batch calculate bannable entries for multiple players.
423
+ *
424
+ * Takes a map of player → { debt, registeredTokenIds } and returns
425
+ * the set of all bannable token IDs across all players.
426
+ *
427
+ * @param players - Map of player address → their debt and registered entries
428
+ * @param config - Parsed Opus Troves validator config
429
+ * @returns Set of all bannable token IDs
430
+ */
431
+ declare function findAllBannableEntries(players: Map<string, {
432
+ debt: bigint;
433
+ registeredTokenIds: string[];
434
+ }>, config: OpusTrovesValidatorConfig): Set<string>;
435
+
436
+ /**
437
+ * Entry fee configuration as provided by metagame contracts (e.g. Budokan SDK).
438
+ */
439
+ interface EntryFeeConfig {
440
+ tokenAddress: string;
441
+ amount: string;
442
+ tournamentCreatorShare: number;
443
+ gameCreatorShare: number;
444
+ refundShare: number;
445
+ distribution: unknown;
446
+ distributionCount: number;
447
+ }
448
+ /**
449
+ * Build Prize[] from entry fee configuration and entry count.
450
+ *
451
+ * Calculates the total entry fee pool, subtracts creator/game/refund shares,
452
+ * then distributes the remaining prize pool across positions using the
453
+ * configured distribution curve.
454
+ *
455
+ * @param entryFee - Entry fee configuration from the tournament
456
+ * @param entryCount - Number of entries in the tournament
457
+ * @returns Array of Prize objects representing entry fee prizes per position
458
+ */
459
+ declare function buildEntryFeePrizes(entryFee: EntryFeeConfig, entryCount: number): Prize[];
460
+ /**
461
+ * Token price lookup: normalized address → USD price.
462
+ */
463
+ type TokenPriceLookup = Record<string, number>;
464
+ /**
465
+ * Token decimals lookup: normalized address → decimals.
466
+ */
467
+ type TokenDecimalsLookup = Record<string, number>;
468
+ /**
469
+ * Calculate total USD value of a set of prizes.
470
+ *
471
+ * @param prizes - Array of Prize objects (sponsored + entry fee)
472
+ * @param prices - Token price lookup (normalized address → USD price)
473
+ * @param decimals - Token decimals lookup (normalized address → decimals, defaults to 18)
474
+ * @param normalizeAddress - Function to normalize addresses for lookup (e.g. indexAddress)
475
+ * @returns Total USD value, or 0 if any required price is missing
476
+ */
477
+ declare function calculateTotalPrizeValueUSD(prizes: Prize[], prices: TokenPriceLookup, decimals: TokenDecimalsLookup, normalizeAddress?: (addr: string) => string): number;
478
+ /**
479
+ * Calculate the number of paid positions (places that receive prizes).
480
+ *
481
+ * Takes the maximum of sponsored prize positions and entry fee distribution count.
482
+ *
483
+ * @param sponsoredPrizes - Array of sponsored Prize objects
484
+ * @param entryFeeDistributionCount - Number of positions from entry fee distribution
485
+ * @returns Number of paid places
486
+ */
487
+ declare function calculatePaidPlaces(sponsoredPrizes: Prize[], entryFeeDistributionCount: number): number;
488
+
489
+ export { type Distribution, ERC20BalanceValidatorConfig, type EntryFee, type EntryFeeConfig, type EntryRequirement, EntryRequirementVariant, ExtensionAddresses, type ExtensionQualificationInput, ExtensionType, GovernanceValidatorConfig, type GroupedTokenPrize, type NftParseResult, type NftPrizeInput, OpusTrovesValidatorConfig, Prize, QualificationProof, QualificationResult, QualifyingModeInfo, SnapshotValidatorConfig, StatusResult, StatusTimestamps, type TokenDecimalsLookup, type TokenPriceLookup, type TokenQualificationInput, type TournamentLeaderboard, type TournamentQualificationInput, type TournamentRegistration, TournamentValidatorConfig, ZkPassportValidatorConfig, bigintToHex, buildEntryFeePrizes, buildExtensionProof, buildNFTProof, buildParticipationMap, buildQualificationProof, buildTournamentExtensionProof, buildWinMap, calculateOpusTrovesEntries, calculatePaidPlaces, calculatePrizeValue, calculateTotalPrizeValueUSD, computeStatus, displayAddress, evaluateExtensionQualification, evaluateQualification, evaluateTokenQualification, findAllBannableEntries, findBannableEntries, formatCashToUSD, formatTokenAmount, getExtensionAddresses, getOpusSupportedAssets, getQualifyingModeInfo, getWhitelistedExtensionAddresses, groupPrizesByToken, identifyExtensionType, indexAddress, isBefore, isWhitelistedExtension, padAddress, parseERC20BalanceValidatorConfig, parseExtensionConfig, parseGovernanceValidatorConfig, parseNFTBulkInput, parseOpusTrovesValidatorConfig, parseSnapshotValidatorConfig, parseTournamentValidatorConfig, parseZkPassportValidatorConfig, resolveTournamentQualifications };