@dubsdotapp/expo 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.
- package/README.md +158 -0
- package/dist/index.d.mts +577 -0
- package/dist/index.d.ts +577 -0
- package/dist/index.js +1364 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1318 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +57 -0
- package/src/client.ts +355 -0
- package/src/constants.ts +3 -0
- package/src/errors.ts +127 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useAuth.ts +180 -0
- package/src/hooks/useClaim.ts +64 -0
- package/src/hooks/useCreateGame.ts +78 -0
- package/src/hooks/useEvents.ts +34 -0
- package/src/hooks/useGame.ts +30 -0
- package/src/hooks/useGames.ts +31 -0
- package/src/hooks/useJoinGame.ts +78 -0
- package/src/hooks/useNetworkGames.ts +31 -0
- package/src/index.ts +83 -0
- package/src/provider.tsx +39 -0
- package/src/types.ts +340 -0
- package/src/ui/ConnectWalletScreen.tsx +145 -0
- package/src/ui/SettingsSheet.tsx +173 -0
- package/src/ui/UserProfileCard.tsx +90 -0
- package/src/ui/index.ts +8 -0
- package/src/ui/theme.ts +57 -0
- package/src/utils/transaction.ts +67 -0
- package/src/wallet/index.ts +3 -0
- package/src/wallet/mwa-adapter.ts +156 -0
- package/src/wallet/types.ts +30 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/client.ts","../src/provider.tsx","../src/wallet/mwa-adapter.ts","../src/hooks/useEvents.ts","../src/hooks/useGame.ts","../src/hooks/useGames.ts","../src/hooks/useNetworkGames.ts","../src/hooks/useCreateGame.ts","../src/utils/transaction.ts","../src/hooks/useJoinGame.ts","../src/hooks/useClaim.ts","../src/hooks/useAuth.ts","../src/ui/ConnectWalletScreen.tsx","../src/ui/theme.ts","../src/ui/UserProfileCard.tsx","../src/ui/SettingsSheet.tsx"],"sourcesContent":["export const DEFAULT_BASE_URL = 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1';\n\nexport const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';\n","import type { ParsedError, SolanaErrorCode } from './types';\n\nexport class DubsApiError extends Error {\n public readonly code: string;\n public readonly httpStatus: number;\n\n constructor(code: string, message: string, httpStatus: number) {\n super(message);\n this.name = 'DubsApiError';\n this.code = code;\n this.httpStatus = httpStatus;\n }\n}\n\n/** Custom error codes from dubs_solana_program (6000-6049) */\nexport const SOLANA_PROGRAM_ERRORS: Record<number, SolanaErrorCode> = {\n 6000: { code: 'invalid_amount', message: 'Amount must be greater than 0' },\n 6001: { code: 'invalid_max_players', message: 'Max players must be between 1 and 20' },\n 6002: { code: 'game_not_active', message: 'Game is not active' },\n 6003: { code: 'game_full', message: 'Game is full' },\n 6004: { code: 'player_already_joined', message: 'Player has already joined this game' },\n 6005: { code: 'insufficient_funds', message: 'Insufficient funds in pot' },\n 6006: { code: 'unauthorized', message: 'Only the game creator can perform this action' },\n 6007: { code: 'invalid_winner_index', message: 'Invalid winner index' },\n 6008: { code: 'game_still_active', message: 'Game is still active — must close before resetting' },\n 6009: { code: 'pot_not_empty', message: 'Pot must be empty before resetting — distribute winnings first' },\n 6010: { code: 'no_winners_specified', message: 'At least one winner must be specified' },\n 6011: { code: 'mismatched_winners_percentages', message: 'Number of winners must match number of percentages' },\n 6012: { code: 'percentages_invalid', message: 'Percentages must sum to exactly 100' },\n 6013: { code: 'winner_account_mismatch', message: 'Winner account does not match expected player' },\n 6014: { code: 'invalid_operator_fee', message: 'Operator fee must be between 0 and 100' },\n 6015: { code: 'operator_wallet_mismatch', message: 'Operator wallet does not match game settings' },\n 6016: { code: 'winner_not_in_game', message: 'Winner address is not a player in this game' },\n 6017: { code: 'voting_not_enabled', message: 'Voting is not enabled for this game' },\n 6018: { code: 'voter_not_in_game', message: 'Voter is not a player in this game' },\n 6019: { code: 'voted_for_not_in_game', message: 'Cannot vote for someone who is not in the game' },\n 6020: { code: 'voting_incomplete', message: 'Voting incomplete — need majority of players to vote' },\n 6021: { code: 'invalid_referee_commission', message: 'Referee commission must be between 0 and 100' },\n 6022: { code: 'total_fees_exceed_100', message: 'Total fees (operator + referee) cannot exceed 100%' },\n 6023: { code: 'referee_mode_requires_referee', message: 'Referee mode requires a referee address' },\n 6024: { code: 'referee_must_earn_commission', message: 'Referee must earn commission' },\n 6025: { code: 'referee_cannot_be_player', message: 'Referee cannot join as a player (conflict of interest)' },\n 6026: { code: 'only_referee_can_vote', message: 'Only the referee can vote in Referee mode' },\n 6027: { code: 'referee_account_mismatch', message: 'Referee account does not match game settings' },\n 6028: { code: 'invalid_lock_time', message: 'Lock time must be in the future' },\n 6029: { code: 'game_locked', message: 'Game is locked — no more players can join' },\n 6030: { code: 'lock_time_passed', message: 'Lock time has passed — game is now locked' },\n 6031: { code: 'unauthorized_oracle', message: 'Only authorized oracle can resolve this game' },\n 6032: { code: 'game_not_locked', message: 'Game must be locked before it can be resolved' },\n 6033: { code: 'already_resolved', message: 'Game has already been resolved' },\n 6034: { code: 'game_not_resolved', message: 'Game has not been resolved yet' },\n 6035: { code: 'player_not_in_game', message: 'Player is not in this game' },\n 6036: { code: 'not_a_winner', message: 'Player did not win this game' },\n 6037: { code: 'invalid_game_mode', message: 'Invalid game mode for this operation' },\n 6038: { code: 'already_claimed', message: 'Player has already claimed their winnings' },\n 6039: { code: 'lock_time_too_soon', message: 'Lock time must be at least 2 minutes in the future' },\n 6040: { code: 'operator_account_missing', message: 'Operator account must be provided' },\n 6041: { code: 'no_winners_to_distribute', message: 'No winners to distribute funds to' },\n 6042: { code: 'insufficient_funds_for_rent', message: 'Insufficient funds to maintain rent exemption' },\n 6043: { code: 'cannot_resolve_before_lock', message: 'Cannot resolve game before lock time has passed' },\n 6044: { code: 'emergency_refund_not_available', message: 'Emergency refund not available yet — must wait 7 days after lock time' },\n 6045: { code: 'sponsor_account_missing', message: 'Sponsor account must be provided for tie refund' },\n 6046: { code: 'sponsor_wallet_mismatch', message: 'Sponsor wallet does not match stored sponsor' },\n 6047: { code: 'invalid_bet_amount', message: 'Bet amount must be greater than zero' },\n 6048: { code: 'no_survivors_to_distribute', message: 'No survivors to distribute winnings to' },\n 6049: { code: 'too_many_survivors', message: 'Too many survivors (max 50 per batch)' },\n};\n\n/** Known Solana built-in instruction errors */\nconst SOLANA_BUILTIN_ERRORS: Record<number, SolanaErrorCode> = {\n 0: { code: 'generic_error', message: 'Generic instruction error' },\n 1: { code: 'invalid_argument', message: 'Invalid argument passed to program' },\n 2: { code: 'invalid_instruction_data', message: 'Invalid instruction data' },\n 3: { code: 'invalid_account_data', message: 'Invalid account data' },\n 4: { code: 'account_data_too_small', message: 'Account data too small' },\n 5: { code: 'insufficient_funds', message: 'Insufficient funds for transaction' },\n 6: { code: 'incorrect_program_id', message: 'Incorrect program ID' },\n 7: { code: 'missing_required_signature', message: 'Missing required signature' },\n 8: { code: 'account_already_initialized', message: 'Account already initialized' },\n 9: { code: 'uninitialized_account', message: 'Attempt to operate on uninitialized account' },\n};\n\n/**\n * Parse a raw Solana transaction error into a human-readable { code, message }.\n * Handles: { InstructionError: [idx, { Custom: N }] }, string errors, etc.\n */\nexport function parseSolanaError(err: unknown): ParsedError {\n if (!err) return { code: 'unknown_error', message: 'Unknown transaction error' };\n\n let parsed = err;\n\n if (typeof parsed === 'string') {\n try {\n const jsonMatch = parsed.match(/\\{.*\\}/s);\n if (jsonMatch) parsed = JSON.parse(jsonMatch[0]);\n else return { code: 'transaction_failed', message: parsed };\n } catch {\n return { code: 'transaction_failed', message: parsed as string };\n }\n }\n\n if (typeof parsed === 'object' && parsed !== null && 'InstructionError' in parsed) {\n const [ixIndex, details] = (parsed as { InstructionError: [number, unknown] }).InstructionError;\n\n if (details && typeof details === 'object' && 'Custom' in details) {\n const customCode = (details as { Custom: number }).Custom;\n const known = SOLANA_PROGRAM_ERRORS[customCode];\n if (known) return known;\n return { code: `program_error_${customCode}`, message: `Program error code ${customCode} (instruction ${ixIndex})` };\n }\n\n if (typeof details === 'string') {\n const snake = details.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();\n return { code: snake, message: details.replace(/([a-z])([A-Z])/g, '$1 $2') };\n }\n\n if (typeof details === 'number') {\n const known = SOLANA_BUILTIN_ERRORS[details];\n if (known) return known;\n return { code: `instruction_error_${details}`, message: `Instruction error ${details}` };\n }\n\n return { code: 'instruction_error', message: `Instruction ${ixIndex} failed: ${JSON.stringify(details)}` };\n }\n\n return { code: 'transaction_failed', message: typeof parsed === 'object' ? JSON.stringify(parsed) : String(parsed) };\n}\n","import { DEFAULT_BASE_URL } from './constants';\nimport { DubsApiError, parseSolanaError, SOLANA_PROGRAM_ERRORS } from './errors';\nimport type {\n UnifiedEvent,\n Pagination,\n EsportsMatchDetail,\n ValidateEventResult,\n CreateGameParams,\n CreateGameResult,\n JoinGameParams,\n JoinGameResult,\n ConfirmGameParams,\n ConfirmGameResult,\n BuildClaimParams,\n BuildClaimResult,\n GameDetail,\n GameListItem,\n GetGamesParams,\n GetNetworkGamesParams,\n GetUpcomingEventsParams,\n ParsedError,\n SolanaErrorCode,\n NonceResult,\n AuthenticateParams,\n AuthenticateResult,\n RegisterParams,\n RegisterResult,\n DubsUser,\n DubsPublicUser,\n DubsAppUser,\n CheckUsernameResult,\n} from './types';\n\nexport interface DubsClientConfig {\n apiKey: string;\n baseUrl?: string;\n}\n\nexport class DubsClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private _token: string | null = null;\n\n constructor(config: DubsClientConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\\/+$/, '');\n }\n\n /** Set the user JWT token for authenticated requests */\n setToken(token: string | null): void {\n this._token = token;\n }\n\n /** Get the current user JWT token */\n getToken(): string | null {\n return this._token;\n }\n\n // ── Private HTTP helper ──\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n if (this._token) {\n headers['Authorization'] = `Bearer ${this._token}`;\n }\n\n const res = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const json = await res.json();\n\n if (!json.success) {\n const err = json.error;\n if (typeof err === 'object' && err !== null) {\n throw new DubsApiError(err.code || 'unknown', err.message || 'Unknown error', res.status);\n }\n throw new DubsApiError('unknown', typeof err === 'string' ? err : 'Request failed', res.status);\n }\n\n return json as T;\n }\n\n // ── Events ──\n\n async getUpcomingEvents(params?: GetUpcomingEventsParams): Promise<{ events: UnifiedEvent[]; pagination: Pagination }> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set('type', params.type);\n if (params?.game) qs.set('game', params.game);\n if (params?.page) qs.set('page', String(params.page));\n if (params?.per_page) qs.set('per_page', String(params.per_page));\n const query = qs.toString();\n const res = await this.request<{ success: true; events: UnifiedEvent[]; pagination: Pagination }>(\n 'GET',\n `/events/upcoming${query ? `?${query}` : ''}`,\n );\n return { events: res.events, pagination: res.pagination };\n }\n\n async getSportsEvents(league: string): Promise<UnifiedEvent[]> {\n const res = await this.request<{ success: true; league: string; events: UnifiedEvent[] }>(\n 'GET',\n `/sports/events/${encodeURIComponent(league)}`,\n );\n return res.events;\n }\n\n async getEsportsMatches(videogame?: string): Promise<UnifiedEvent[]> {\n const qs = videogame ? `?videogame=${encodeURIComponent(videogame)}` : '';\n const res = await this.request<{ success: true; matches: UnifiedEvent[] }>(\n 'GET',\n `/esports/matches/upcoming${qs}`,\n );\n return res.matches;\n }\n\n async getEsportsMatchDetail(matchId: string | number): Promise<EsportsMatchDetail> {\n const res = await this.request<{ success: true; match: EsportsMatchDetail }>(\n 'GET',\n `/esports/matches/${encodeURIComponent(String(matchId))}`,\n );\n return res.match;\n }\n\n // ── Game Lifecycle ──\n\n async validateEvent(id: string): Promise<ValidateEventResult> {\n const res = await this.request<{ success: true } & ValidateEventResult>(\n 'POST',\n '/games/validate',\n { id },\n );\n return {\n bettable: res.bettable,\n gameMode: res.gameMode,\n lockTimestamp: res.lockTimestamp,\n startTime: res.startTime,\n status: res.status,\n };\n }\n\n async createGame(params: CreateGameParams): Promise<CreateGameResult> {\n const res = await this.request<{ success: true } & CreateGameResult>(\n 'POST',\n '/games/create',\n params,\n );\n return {\n gameId: res.gameId,\n gameAddress: res.gameAddress,\n transaction: res.transaction,\n lockTimestamp: res.lockTimestamp,\n event: res.event,\n };\n }\n\n async joinGame(params: JoinGameParams): Promise<JoinGameResult> {\n const res = await this.request<{ success: true } & JoinGameResult>(\n 'POST',\n '/games/join',\n params,\n );\n return {\n gameId: res.gameId,\n transaction: res.transaction,\n gameAddress: res.gameAddress,\n };\n }\n\n async confirmGame(params: ConfirmGameParams): Promise<ConfirmGameResult> {\n const res = await this.request<{ success: true } & ConfirmGameResult>(\n 'POST',\n '/games/confirm',\n params,\n );\n return {\n gameId: res.gameId,\n signature: res.signature,\n explorerUrl: res.explorerUrl,\n message: res.message,\n };\n }\n\n async buildClaimTransaction(params: BuildClaimParams): Promise<BuildClaimResult> {\n const res = await this.request<{ success: true } & BuildClaimResult>(\n 'POST',\n '/transactions/build/claim',\n params,\n );\n return {\n transaction: res.transaction,\n gameAddress: res.gameAddress,\n message: res.message,\n };\n }\n\n // ── Game Queries ──\n\n async getGame(gameId: string): Promise<GameDetail> {\n const res = await this.request<{ success: true; game: GameDetail }>(\n 'GET',\n `/games/${encodeURIComponent(gameId)}`,\n );\n return res.game;\n }\n\n async getGames(params?: GetGamesParams): Promise<GameListItem[]> {\n const qs = new URLSearchParams();\n if (params?.wallet) qs.set('wallet', params.wallet);\n if (params?.status) qs.set('status', params.status);\n if (params?.limit != null) qs.set('limit', String(params.limit));\n if (params?.offset != null) qs.set('offset', String(params.offset));\n const query = qs.toString();\n const res = await this.request<{ success: true; games: GameListItem[] }>(\n 'GET',\n `/games${query ? `?${query}` : ''}`,\n );\n return res.games;\n }\n\n // ── Network ──\n\n async getNetworkGames(params?: GetNetworkGamesParams): Promise<{ games: GameListItem[]; pagination: Pagination }> {\n const qs = new URLSearchParams();\n if (params?.league) qs.set('league', params.league);\n if (params?.limit != null) qs.set('limit', String(params.limit));\n if (params?.offset != null) qs.set('offset', String(params.offset));\n const query = qs.toString();\n const res = await this.request<{ success: true; games: GameListItem[]; pagination: Pagination }>(\n 'GET',\n `/network/games${query ? `?${query}` : ''}`,\n );\n return { games: res.games, pagination: res.pagination };\n }\n\n // ── User Auth ──\n\n async getNonce(walletAddress: string): Promise<NonceResult> {\n const res = await this.request<{ success: true; data: NonceResult }>(\n 'POST',\n '/auth/nonce',\n { walletAddress },\n );\n return res.data;\n }\n\n async authenticate(params: AuthenticateParams): Promise<AuthenticateResult> {\n const res = await this.request<{ success: true; data: AuthenticateResult }>(\n 'POST',\n '/auth/authenticate',\n params,\n );\n // Auto-store token on successful auth\n if (res.data.token) {\n this._token = res.data.token;\n }\n return res.data;\n }\n\n async register(params: RegisterParams): Promise<RegisterResult> {\n const res = await this.request<{ success: true; data: RegisterResult }>(\n 'POST',\n '/auth/register',\n params,\n );\n // Auto-store token on successful registration\n if (res.data.token) {\n this._token = res.data.token;\n }\n return res.data;\n }\n\n async getMe(): Promise<DubsUser> {\n const res = await this.request<{ success: true; data: { user: DubsUser } }>(\n 'GET',\n '/auth/me',\n );\n return res.data.user;\n }\n\n async getUser(walletAddress: string): Promise<DubsPublicUser | null> {\n const res = await this.request<{ success: true; data: { user: DubsPublicUser | null } }>(\n 'GET',\n `/users/${encodeURIComponent(walletAddress)}`,\n );\n return res.data.user;\n }\n\n async getAppUsers(params?: { limit?: number; offset?: number }): Promise<{ users: DubsAppUser[]; pagination: { total: number; limit: number; offset: number } }> {\n const qs = new URLSearchParams();\n if (params?.limit != null) qs.set('limit', String(params.limit));\n if (params?.offset != null) qs.set('offset', String(params.offset));\n const query = qs.toString();\n const res = await this.request<{ success: true; data: { users: DubsAppUser[]; pagination: { total: number; limit: number; offset: number } } }>(\n 'GET',\n `/users${query ? `?${query}` : ''}`,\n );\n return res.data;\n }\n\n async checkUsername(username: string): Promise<CheckUsernameResult> {\n const res = await this.request<{ success: true; data: CheckUsernameResult }>(\n 'GET',\n `/auth/check-username/${encodeURIComponent(username)}`,\n );\n return res.data;\n }\n\n async logout(): Promise<void> {\n await this.request<{ success: true; data: { message: string } }>(\n 'POST',\n '/auth/logout',\n );\n this._token = null;\n }\n\n // ── Error Utilities ──\n\n async parseError(error: unknown): Promise<ParsedError> {\n const res = await this.request<{ success: true; error: ParsedError }>(\n 'POST',\n '/errors/parse',\n { error },\n );\n return res.error;\n }\n\n async getErrorCodes(): Promise<Record<number, SolanaErrorCode>> {\n const res = await this.request<{ success: true; errors: Record<number, SolanaErrorCode> }>(\n 'GET',\n '/errors/codes',\n );\n return res.errors;\n }\n\n /**\n * Parse a Solana error locally without an API call.\n * Useful for instant feedback in the UI.\n */\n parseErrorLocal(error: unknown): ParsedError {\n return parseSolanaError(error);\n }\n\n /** Get the full local error code map */\n getErrorCodesLocal(): Record<number, SolanaErrorCode> {\n return { ...SOLANA_PROGRAM_ERRORS };\n }\n}\n","import React, { createContext, useContext, useMemo } from 'react';\nimport { Connection } from '@solana/web3.js';\nimport { DubsClient } from './client';\nimport { DEFAULT_RPC_URL } from './constants';\nimport type { WalletAdapter } from './wallet/types';\n\nexport interface DubsContextValue {\n client: DubsClient;\n wallet: WalletAdapter;\n connection: Connection;\n}\n\nconst DubsContext = createContext<DubsContextValue | null>(null);\n\nexport interface DubsProviderProps {\n apiKey: string;\n baseUrl?: string;\n rpcUrl?: string;\n wallet: WalletAdapter;\n children: React.ReactNode;\n}\n\nexport function DubsProvider({ apiKey, baseUrl, rpcUrl, wallet, children }: DubsProviderProps) {\n const value = useMemo<DubsContextValue>(() => {\n const client = new DubsClient({ apiKey, baseUrl });\n const connection = new Connection(rpcUrl || DEFAULT_RPC_URL, { commitment: 'confirmed' });\n return { client, wallet, connection };\n }, [apiKey, baseUrl, rpcUrl, wallet]);\n\n return <DubsContext.Provider value={value}>{children}</DubsContext.Provider>;\n}\n\nexport function useDubs(): DubsContextValue {\n const ctx = useContext(DubsContext);\n if (!ctx) {\n throw new Error('useDubs must be used within a <DubsProvider>');\n }\n return ctx;\n}\n","import { PublicKey, Transaction } from '@solana/web3.js';\nimport type { WalletAdapter } from './types';\n\n/** Convert an MWA address (base64 string or Uint8Array) to a PublicKey */\nfunction toPublicKey(address: string | Uint8Array): PublicKey {\n if (address instanceof Uint8Array) {\n return new PublicKey(address);\n }\n // MWA protocol returns addresses as base64 — decode to bytes\n const bytes = Uint8Array.from(atob(address), (c) => c.charCodeAt(0));\n return new PublicKey(bytes);\n}\n\n/** The `transact` function signature from @solana-mobile/mobile-wallet-adapter-protocol-web3js */\nexport type MwaTransactFn = (\n callback: (wallet: any) => Promise<any>,\n) => Promise<any>;\n\nexport interface MwaAdapterConfig {\n /** The MWA `transact` function — import it from @solana-mobile/mobile-wallet-adapter-protocol-web3js */\n transact: MwaTransactFn;\n appIdentity: {\n name: string;\n uri?: string;\n icon?: string;\n };\n cluster?: string;\n onAuthTokenChange?: (token: string | null) => void;\n}\n\n/**\n * Mobile Wallet Adapter implementation.\n * Wraps @solana-mobile/mobile-wallet-adapter-protocol-web3js.\n *\n * Usage:\n * ```ts\n * import { transact } from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';\n *\n * const adapter = new MwaWalletAdapter({\n * transact,\n * appIdentity: { name: 'My App' },\n * });\n * ```\n */\nexport class MwaWalletAdapter implements WalletAdapter {\n private _publicKey: PublicKey | null = null;\n private _connected = false;\n private _authToken: string | null = null;\n private readonly config: MwaAdapterConfig;\n private readonly transact: MwaTransactFn;\n\n constructor(config: MwaAdapterConfig) {\n this.config = config;\n this.transact = config.transact;\n }\n\n get publicKey(): PublicKey | null {\n return this._publicKey;\n }\n\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Connect to a mobile wallet. Call this before any signing.\n */\n async connect(): Promise<void> {\n await this.transact(async (wallet) => {\n const authResult = this._authToken\n ? await wallet.reauthorize({ auth_token: this._authToken })\n : await wallet.authorize({\n identity: this.config.appIdentity,\n cluster: this.config.cluster || 'mainnet-beta',\n });\n\n this._publicKey = toPublicKey(authResult.accounts[0].address);\n this._authToken = authResult.auth_token;\n this._connected = true;\n\n this.config.onAuthTokenChange?.(this._authToken);\n });\n }\n\n /**\n * Disconnect and clear auth token.\n */\n disconnect(): void {\n this._publicKey = null;\n this._connected = false;\n this._authToken = null;\n this.config.onAuthTokenChange?.(null);\n }\n\n async signTransaction(transaction: Transaction): Promise<Transaction> {\n if (!this._connected) throw new Error('Wallet not connected');\n\n const signed = await this.transact(async (wallet) => {\n const reauth = await wallet.reauthorize({ auth_token: this._authToken });\n this._authToken = reauth.auth_token;\n this.config.onAuthTokenChange?.(this._authToken);\n\n const result = await wallet.signTransactions({\n transactions: [transaction],\n });\n return result[0];\n });\n\n return signed;\n }\n\n async signMessage(message: Uint8Array): Promise<Uint8Array> {\n if (!this._connected || !this._publicKey) throw new Error('Wallet not connected');\n\n const sig = await this.transact(async (wallet) => {\n const reauth = await wallet.reauthorize({ auth_token: this._authToken });\n this._authToken = reauth.auth_token;\n this.config.onAuthTokenChange?.(this._authToken);\n\n const result = await wallet.signMessages({\n addresses: [this._publicKey!.toBytes()],\n payloads: [message],\n });\n return result[0];\n });\n\n return sig instanceof Uint8Array ? sig : new Uint8Array(sig);\n }\n\n async signAndSendTransaction(transaction: Transaction): Promise<string> {\n if (!this._connected) throw new Error('Wallet not connected');\n\n const signature = await this.transact(async (wallet) => {\n const reauth = await wallet.reauthorize({ auth_token: this._authToken });\n this._authToken = reauth.auth_token;\n this.config.onAuthTokenChange?.(this._authToken);\n\n const result = await wallet.signAndSendTransactions({\n transactions: [transaction],\n });\n return result[0];\n });\n\n // MWA returns Uint8Array signature — convert to base58 string\n if (signature instanceof Uint8Array) {\n const bs58 = await import('@solana/web3.js').then(() => {\n return new PublicKey(signature).toBase58();\n }).catch(() => {\n return Buffer.from(signature).toString('base64');\n });\n return bs58;\n }\n\n return String(signature);\n }\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport type { UnifiedEvent, Pagination, GetUpcomingEventsParams, QueryResult } from '../types';\n\nexport function useEvents(\n params?: GetUpcomingEventsParams,\n): QueryResult<{ events: UnifiedEvent[]; pagination: Pagination }> {\n const { client } = useDubs();\n const [data, setData] = useState<{ events: UnifiedEvent[]; pagination: Pagination } | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Serialize params for dependency comparison\n const paramKey = JSON.stringify(params ?? {});\n\n const fetchData = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await client.getUpcomingEvents(params);\n setData(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setLoading(false);\n }\n }, [client, paramKey]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n return { data, loading, error, refetch: fetchData };\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport type { GameDetail, QueryResult } from '../types';\n\nexport function useGame(gameId: string | null): QueryResult<GameDetail> {\n const { client } = useDubs();\n const [data, setData] = useState<GameDetail | null>(null);\n const [loading, setLoading] = useState(!!gameId);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchData = useCallback(async () => {\n if (!gameId) return;\n setLoading(true);\n setError(null);\n try {\n const game = await client.getGame(gameId);\n setData(game);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setLoading(false);\n }\n }, [client, gameId]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n return { data, loading, error, refetch: fetchData };\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport type { GameListItem, GetGamesParams, QueryResult } from '../types';\n\nexport function useGames(params?: GetGamesParams): QueryResult<GameListItem[]> {\n const { client } = useDubs();\n const [data, setData] = useState<GameListItem[] | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const paramKey = JSON.stringify(params ?? {});\n\n const fetchData = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const games = await client.getGames(params);\n setData(games);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setLoading(false);\n }\n }, [client, paramKey]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n return { data, loading, error, refetch: fetchData };\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport type { GameListItem, Pagination, GetNetworkGamesParams, QueryResult } from '../types';\n\nexport function useNetworkGames(params?: GetNetworkGamesParams): QueryResult<{ games: GameListItem[]; pagination: Pagination }> {\n const { client } = useDubs();\n const [data, setData] = useState<{ games: GameListItem[]; pagination: Pagination } | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const paramKey = JSON.stringify(params ?? {});\n\n const fetchData = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await client.getNetworkGames(params);\n setData(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setLoading(false);\n }\n }, [client, paramKey]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n return { data, loading, error, refetch: fetchData };\n}\n","import { useState, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport { signAndSendBase64Transaction, pollTransactionConfirmation } from '../utils/transaction';\nimport type { CreateGameParams, ConfirmGameResult, MutationStatus } from '../types';\n\nexport interface CreateGameMutationResult {\n gameId: string;\n gameAddress: string;\n signature: string;\n explorerUrl: string;\n}\n\nexport function useCreateGame() {\n const { client, wallet, connection } = useDubs();\n const [status, setStatus] = useState<MutationStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<CreateGameMutationResult | null>(null);\n\n const reset = useCallback(() => {\n setStatus('idle');\n setError(null);\n setData(null);\n }, []);\n\n const execute = useCallback(async (params: CreateGameParams): Promise<CreateGameMutationResult> => {\n setStatus('building');\n setError(null);\n setData(null);\n\n try {\n // 1. Build unsigned transaction\n const createResult = await client.createGame(params);\n\n // 2. Sign transaction\n setStatus('signing');\n const signature = await signAndSendBase64Transaction(\n createResult.transaction,\n wallet,\n connection,\n );\n\n // 3. Poll for confirmation\n setStatus('confirming');\n await pollTransactionConfirmation(signature, connection);\n\n // 4. Confirm with backend\n setStatus('saving');\n const explorerUrl = `https://solscan.io/tx/${signature}`;\n await client.confirmGame({\n gameId: createResult.gameId,\n playerWallet: params.playerWallet,\n signature,\n teamChoice: params.teamChoice,\n wagerAmount: params.wagerAmount,\n role: 'creator',\n gameAddress: createResult.gameAddress,\n });\n\n const result: CreateGameMutationResult = {\n gameId: createResult.gameId,\n gameAddress: createResult.gameAddress,\n signature,\n explorerUrl,\n };\n\n setData(result);\n setStatus('success');\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setStatus('error');\n throw error;\n }\n }, [client, wallet, connection]);\n\n return { execute, status, error, data, reset };\n}\n","import { Connection, Transaction } from '@solana/web3.js';\nimport type { TransactionConfirmationStatus } from '@solana/web3.js';\nimport type { WalletAdapter } from '../wallet/types';\n\n/**\n * Deserialize a base64-encoded transaction, sign via wallet adapter, send to Solana.\n * Returns the transaction signature.\n */\nexport async function signAndSendBase64Transaction(\n base64Tx: string,\n wallet: WalletAdapter,\n connection: Connection,\n): Promise<string> {\n if (!wallet.publicKey) throw new Error('Wallet not connected');\n\n const txBuffer = Buffer.from(base64Tx, 'base64');\n const transaction = Transaction.from(txBuffer);\n\n // If the wallet supports signAndSend in one step, prefer that\n if (wallet.signAndSendTransaction) {\n return wallet.signAndSendTransaction(transaction);\n }\n\n // Otherwise: sign, then send via RPC\n const signed = await wallet.signTransaction(transaction);\n const signature = await connection.sendRawTransaction(signed.serialize(), {\n skipPreflight: true,\n });\n\n return signature;\n}\n\n/**\n * Poll for transaction confirmation using getSignatureStatuses.\n * Uses polling instead of deprecated WebSocket-based confirmTransaction.\n */\nexport async function pollTransactionConfirmation(\n signature: string,\n connection: Connection,\n commitment: TransactionConfirmationStatus = 'confirmed',\n timeout: number = 60000,\n interval: number = 1500,\n): Promise<void> {\n const start = Date.now();\n const confirmationOrder: TransactionConfirmationStatus[] = ['processed', 'confirmed', 'finalized'];\n const targetIndex = confirmationOrder.indexOf(commitment);\n\n while (Date.now() - start < timeout) {\n const statuses = await connection.getSignatureStatuses([signature]);\n const status = statuses?.value?.[0];\n\n if (status?.err) {\n throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);\n }\n\n if (status?.confirmationStatus) {\n const currentIndex = confirmationOrder.indexOf(status.confirmationStatus);\n if (currentIndex >= targetIndex) {\n return;\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, interval));\n }\n\n throw new Error(`Transaction confirmation timeout after ${timeout}ms`);\n}\n","import { useState, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport { signAndSendBase64Transaction, pollTransactionConfirmation } from '../utils/transaction';\nimport type { JoinGameParams, MutationStatus } from '../types';\n\nexport interface JoinGameMutationResult {\n gameId: string;\n gameAddress: string;\n signature: string;\n explorerUrl: string;\n}\n\nexport function useJoinGame() {\n const { client, wallet, connection } = useDubs();\n const [status, setStatus] = useState<MutationStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<JoinGameMutationResult | null>(null);\n\n const reset = useCallback(() => {\n setStatus('idle');\n setError(null);\n setData(null);\n }, []);\n\n const execute = useCallback(async (params: JoinGameParams): Promise<JoinGameMutationResult> => {\n setStatus('building');\n setError(null);\n setData(null);\n\n try {\n // 1. Build unsigned transaction\n const joinResult = await client.joinGame(params);\n\n // 2. Sign transaction\n setStatus('signing');\n const signature = await signAndSendBase64Transaction(\n joinResult.transaction,\n wallet,\n connection,\n );\n\n // 3. Poll for confirmation\n setStatus('confirming');\n await pollTransactionConfirmation(signature, connection);\n\n // 4. Confirm with backend\n setStatus('saving');\n const explorerUrl = `https://solscan.io/tx/${signature}`;\n await client.confirmGame({\n gameId: params.gameId,\n playerWallet: params.playerWallet,\n signature,\n teamChoice: params.teamChoice,\n wagerAmount: params.amount,\n role: 'joiner',\n gameAddress: joinResult.gameAddress,\n });\n\n const result: JoinGameMutationResult = {\n gameId: params.gameId,\n gameAddress: joinResult.gameAddress,\n signature,\n explorerUrl,\n };\n\n setData(result);\n setStatus('success');\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setStatus('error');\n throw error;\n }\n }, [client, wallet, connection]);\n\n return { execute, status, error, data, reset };\n}\n","import { useState, useCallback } from 'react';\nimport { useDubs } from '../provider';\nimport { signAndSendBase64Transaction, pollTransactionConfirmation } from '../utils/transaction';\nimport type { BuildClaimParams, MutationStatus } from '../types';\n\nexport interface ClaimMutationResult {\n gameId: string;\n signature: string;\n explorerUrl: string;\n}\n\nexport function useClaim() {\n const { client, wallet, connection } = useDubs();\n const [status, setStatus] = useState<MutationStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<ClaimMutationResult | null>(null);\n\n const reset = useCallback(() => {\n setStatus('idle');\n setError(null);\n setData(null);\n }, []);\n\n const execute = useCallback(async (params: BuildClaimParams): Promise<ClaimMutationResult> => {\n setStatus('building');\n setError(null);\n setData(null);\n\n try {\n // 1. Build unsigned claim transaction\n const claimResult = await client.buildClaimTransaction(params);\n\n // 2. Sign transaction\n setStatus('signing');\n const signature = await signAndSendBase64Transaction(\n claimResult.transaction,\n wallet,\n connection,\n );\n\n // 3. Poll for confirmation (no backend confirm step for claims)\n setStatus('confirming');\n await pollTransactionConfirmation(signature, connection);\n\n const explorerUrl = `https://solscan.io/tx/${signature}`;\n const result: ClaimMutationResult = {\n gameId: params.gameId,\n signature,\n explorerUrl,\n };\n\n setData(result);\n setStatus('success');\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setStatus('error');\n throw error;\n }\n }, [client, wallet, connection]);\n\n return { execute, status, error, data, reset };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport bs58 from 'bs58';\nimport { useDubs } from '../provider';\nimport type { AuthStatus, DubsUser } from '../types';\n\nexport interface UseAuthResult {\n /** Current auth status */\n status: AuthStatus;\n /** Authenticated user profile, or null */\n user: DubsUser | null;\n /** JWT token, or null */\n token: string | null;\n /** Convenience boolean */\n isAuthenticated: boolean;\n /** Error from the last operation, or null */\n error: Error | null;\n\n /**\n * Full nonce → sign → verify flow.\n * If the wallet is already registered, resolves to authenticated state.\n * If new wallet, resolves to needsRegistration state — call register() next.\n */\n authenticate: () => Promise<void>;\n\n /**\n * Register a new user after authenticate() returned needsRegistration.\n * Reuses the nonce+signature from authenticate() — no second signing prompt.\n */\n register: (username: string, referralCode?: string) => Promise<void>;\n\n /** Log out and clear state */\n logout: () => Promise<void>;\n\n /**\n * Restore a session from a saved token (e.g. AsyncStorage on app restart).\n * Calls GET /auth/me to validate. Returns true if valid, false otherwise.\n */\n restoreSession: (token: string) => Promise<boolean>;\n\n /** Reset to idle state, clearing errors and user */\n reset: () => void;\n}\n\nexport function useAuth(): UseAuthResult {\n const { client, wallet } = useDubs();\n const [status, setStatus] = useState<AuthStatus>('idle');\n const [user, setUser] = useState<DubsUser | null>(null);\n const [token, setToken] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n // Stash nonce+signature between authenticate → register (single-sign flow)\n const pendingAuth = useRef<{\n walletAddress: string;\n nonce: string;\n signature: string;\n } | null>(null);\n\n const reset = useCallback(() => {\n setStatus('idle');\n setUser(null);\n setToken(null);\n setError(null);\n pendingAuth.current = null;\n client.setToken(null);\n }, [client]);\n\n const authenticate = useCallback(async () => {\n try {\n if (!wallet.publicKey) {\n throw new Error('Wallet not connected');\n }\n if (!wallet.signMessage) {\n throw new Error('Wallet does not support signMessage');\n }\n\n const walletAddress = wallet.publicKey.toBase58();\n setStatus('authenticating');\n setError(null);\n\n // 1. Get nonce\n const { nonce, message } = await client.getNonce(walletAddress);\n\n // 2. Sign message\n setStatus('signing');\n const messageBytes = new TextEncoder().encode(message);\n const signatureBytes = await wallet.signMessage(messageBytes);\n const signature = bs58.encode(signatureBytes);\n\n // 3. Verify with server\n setStatus('verifying');\n const result = await client.authenticate({ walletAddress, signature, nonce });\n\n if (result.needsRegistration) {\n // Stash credentials for register() — nonce is NOT consumed\n pendingAuth.current = { walletAddress, nonce, signature };\n setStatus('needsRegistration');\n return;\n }\n\n // Existing user — fully authenticated\n setUser(result.user!);\n setToken(result.token!);\n setStatus('authenticated');\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus('error');\n }\n }, [client, wallet]);\n\n const register = useCallback(async (username: string, referralCode?: string) => {\n try {\n const pending = pendingAuth.current;\n if (!pending) {\n throw new Error('No pending authentication — call authenticate() first');\n }\n\n setStatus('registering');\n setError(null);\n\n const result = await client.register({\n walletAddress: pending.walletAddress,\n signature: pending.signature,\n nonce: pending.nonce,\n username,\n referralCode,\n });\n\n pendingAuth.current = null;\n setUser(result.user);\n setToken(result.token);\n setStatus('authenticated');\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setStatus('error');\n }\n }, [client]);\n\n const logout = useCallback(async () => {\n try {\n await client.logout();\n } catch {\n // Ignore logout errors — clear state regardless\n }\n setUser(null);\n setToken(null);\n setStatus('idle');\n setError(null);\n pendingAuth.current = null;\n }, [client]);\n\n const restoreSession = useCallback(async (savedToken: string): Promise<boolean> => {\n try {\n client.setToken(savedToken);\n const me = await client.getMe();\n setUser(me);\n setToken(savedToken);\n setStatus('authenticated');\n return true;\n } catch {\n client.setToken(null);\n setUser(null);\n setToken(null);\n setStatus('idle');\n return false;\n }\n }, [client]);\n\n return {\n status,\n user,\n token,\n isAuthenticated: status === 'authenticated',\n error,\n authenticate,\n register,\n logout,\n restoreSession,\n reset,\n };\n}\n","import React from 'react';\nimport {\n View,\n Text,\n TouchableOpacity,\n ActivityIndicator,\n StyleSheet,\n} from 'react-native';\nimport { useDubsTheme } from './theme';\n\nexport interface ConnectWalletScreenProps {\n /** Called when the user taps Connect Wallet */\n onConnect: () => void | Promise<void>;\n /** Show a loading spinner on the button while connecting */\n connecting?: boolean;\n /** Error message to display (e.g. \"User rejected the request\") */\n error?: string | null;\n /** App name shown in the header. Defaults to \"Dubs\" */\n appName?: string;\n}\n\nexport function ConnectWalletScreen({\n onConnect,\n connecting = false,\n error = null,\n appName = 'Dubs',\n}: ConnectWalletScreenProps) {\n const t = useDubsTheme();\n\n return (\n <View style={[styles.container, { backgroundColor: t.background }]}>\n <View style={styles.content}>\n {/* Branding */}\n <View style={styles.brandingSection}>\n <View style={[styles.logoCircle, { backgroundColor: t.accent }]}>\n <Text style={styles.logoText}>D</Text>\n </View>\n <Text style={[styles.appName, { color: t.text }]}>{appName}</Text>\n <Text style={[styles.subtitle, { color: t.textMuted }]}>\n Connect your Solana wallet to get started\n </Text>\n </View>\n\n {/* Action */}\n <View style={styles.actionSection}>\n {error ? (\n <View\n style={[\n styles.errorBox,\n { backgroundColor: t.errorBg, borderColor: t.errorBorder },\n ]}\n >\n <Text style={[styles.errorText, { color: t.errorText }]}>{error}</Text>\n </View>\n ) : null}\n\n <TouchableOpacity\n style={[styles.connectButton, { backgroundColor: t.accent }]}\n onPress={onConnect}\n disabled={connecting}\n activeOpacity={0.8}\n >\n {connecting ? (\n <ActivityIndicator color=\"#FFFFFF\" size=\"small\" />\n ) : (\n <Text style={styles.connectButtonText}>Connect Wallet</Text>\n )}\n </TouchableOpacity>\n\n <Text style={[styles.hint, { color: t.textDim }]}>\n Phantom, Solflare, or any Solana wallet\n </Text>\n </View>\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'center',\n },\n content: {\n flex: 1,\n justifyContent: 'space-between',\n paddingHorizontal: 32,\n paddingTop: 120,\n paddingBottom: 80,\n },\n brandingSection: {\n alignItems: 'center',\n gap: 12,\n },\n logoCircle: {\n width: 80,\n height: 80,\n borderRadius: 40,\n justifyContent: 'center',\n alignItems: 'center',\n marginBottom: 8,\n },\n logoText: {\n fontSize: 36,\n fontWeight: '800',\n color: '#FFFFFF',\n },\n appName: {\n fontSize: 32,\n fontWeight: '800',\n },\n subtitle: {\n fontSize: 16,\n textAlign: 'center',\n lineHeight: 22,\n },\n actionSection: {\n gap: 16,\n },\n errorBox: {\n borderWidth: 1,\n borderRadius: 12,\n paddingHorizontal: 16,\n paddingVertical: 12,\n },\n errorText: {\n fontSize: 14,\n textAlign: 'center',\n },\n connectButton: {\n height: 56,\n borderRadius: 16,\n justifyContent: 'center',\n alignItems: 'center',\n },\n connectButtonText: {\n color: '#FFFFFF',\n fontSize: 18,\n fontWeight: '700',\n },\n hint: {\n fontSize: 13,\n textAlign: 'center',\n },\n});\n","import { useColorScheme } from 'react-native';\n\nexport interface DubsTheme {\n background: string;\n surface: string;\n surfaceActive: string;\n border: string;\n text: string;\n textSecondary: string;\n textMuted: string;\n textDim: string;\n accent: string;\n success: string;\n live: string;\n errorText: string;\n errorBg: string;\n errorBorder: string;\n}\n\nconst dark: DubsTheme = {\n background: '#08080D',\n surface: '#111118',\n surfaceActive: '#7C3AED',\n border: '#1A1A24',\n text: '#FFFFFF',\n textSecondary: '#E0E0EE',\n textMuted: '#666666',\n textDim: '#555555',\n accent: '#7C3AED',\n success: '#22C55E',\n live: '#EF4444',\n errorText: '#F87171',\n errorBg: '#1A0A0A',\n errorBorder: '#3A1515',\n};\n\nconst light: DubsTheme = {\n background: '#FFFFFF',\n surface: '#F0F0F5',\n surfaceActive: '#7C3AED',\n border: '#E0E0E8',\n text: '#111118',\n textSecondary: '#333333',\n textMuted: '#888888',\n textDim: '#999999',\n accent: '#7C3AED',\n success: '#16A34A',\n live: '#DC2626',\n errorText: '#DC2626',\n errorBg: '#FEF2F2',\n errorBorder: '#FECACA',\n};\n\nexport function useDubsTheme(): DubsTheme {\n const scheme = useColorScheme();\n return scheme === 'light' ? light : dark;\n}\n","import React, { useMemo } from 'react';\nimport { View, Text, Image, StyleSheet } from 'react-native';\nimport { useDubsTheme } from './theme';\n\nexport interface UserProfileCardProps {\n walletAddress: string;\n username?: string;\n avatarUrl?: string | null;\n memberSince?: string | null;\n}\n\nfunction truncateAddress(address: string, chars = 4): string {\n if (address.length <= chars * 2 + 3) return address;\n return `${address.slice(0, chars)}...${address.slice(-chars)}`;\n}\n\nfunction formatMemberSince(iso: string): string {\n const date = new Date(iso);\n const month = date.toLocaleString('en-US', { month: 'short' });\n const year = date.getFullYear();\n return `Member since ${month} ${year}`;\n}\n\nexport function UserProfileCard({\n walletAddress,\n username,\n avatarUrl,\n memberSince,\n}: UserProfileCardProps) {\n const t = useDubsTheme();\n\n const imageUri = useMemo(\n () =>\n avatarUrl ||\n `https://api.dicebear.com/9.x/avataaars/png?seed=${walletAddress}&size=128`,\n [avatarUrl, walletAddress],\n );\n\n return (\n <View style={[styles.card, { backgroundColor: t.surface, borderColor: t.border }]}>\n <Image source={{ uri: imageUri }} style={styles.avatar} />\n <View style={styles.info}>\n {username ? (\n <Text style={[styles.username, { color: t.text }]}>{username}</Text>\n ) : null}\n <Text style={[styles.address, { color: t.textMuted }]}>\n {truncateAddress(walletAddress)}\n </Text>\n {memberSince ? (\n <Text style={[styles.memberSince, { color: t.textDim }]}>\n {formatMemberSince(memberSince)}\n </Text>\n ) : null}\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n card: {\n flexDirection: 'row',\n alignItems: 'center',\n padding: 16,\n borderRadius: 16,\n borderWidth: 1,\n gap: 14,\n },\n avatar: {\n width: 64,\n height: 64,\n borderRadius: 32,\n backgroundColor: '#1A1A24',\n },\n info: {\n flex: 1,\n gap: 2,\n },\n username: {\n fontSize: 18,\n fontWeight: '700',\n },\n address: {\n fontSize: 14,\n fontFamily: 'monospace',\n },\n memberSince: {\n fontSize: 12,\n marginTop: 2,\n },\n});\n","import React from 'react';\nimport {\n View,\n Text,\n ScrollView,\n TouchableOpacity,\n ActivityIndicator,\n StyleSheet,\n} from 'react-native';\nimport { useDubsTheme } from './theme';\nimport { UserProfileCard } from './UserProfileCard';\n\nexport interface SettingsSheetProps {\n walletAddress: string;\n username?: string;\n avatarUrl?: string | null;\n memberSince?: string | null;\n appVersion?: string;\n onCopyAddress?: () => void;\n onSupport?: () => void;\n onLogout: () => void | Promise<void>;\n loggingOut?: boolean;\n}\n\nfunction truncateAddress(address: string, chars = 4): string {\n if (address.length <= chars * 2 + 3) return address;\n return `${address.slice(0, chars)}...${address.slice(-chars)}`;\n}\n\nexport function SettingsSheet({\n walletAddress,\n username,\n avatarUrl,\n memberSince,\n appVersion,\n onCopyAddress,\n onSupport,\n onLogout,\n loggingOut = false,\n}: SettingsSheetProps) {\n const t = useDubsTheme();\n\n return (\n <ScrollView\n style={[styles.container, { backgroundColor: t.background }]}\n contentContainerStyle={styles.content}\n >\n {/* Profile card */}\n <UserProfileCard\n walletAddress={walletAddress}\n username={username}\n avatarUrl={avatarUrl}\n memberSince={memberSince}\n />\n\n {/* Action rows */}\n <View style={[styles.actionsCard, { backgroundColor: t.surface, borderColor: t.border }]}>\n {onCopyAddress ? (\n <TouchableOpacity\n style={styles.actionRow}\n onPress={onCopyAddress}\n activeOpacity={0.7}\n >\n <View style={styles.actionRowLeft}>\n <Text style={[styles.actionLabel, { color: t.text }]}>Wallet Address</Text>\n <Text style={[styles.actionValue, { color: t.textMuted }]}>\n {truncateAddress(walletAddress)}\n </Text>\n </View>\n <Text style={[styles.copyLabel, { color: t.accent }]}>Copy</Text>\n </TouchableOpacity>\n ) : null}\n\n {onSupport ? (\n <>\n {onCopyAddress ? (\n <View style={[styles.separator, { backgroundColor: t.border }]} />\n ) : null}\n <TouchableOpacity\n style={styles.actionRow}\n onPress={onSupport}\n activeOpacity={0.7}\n >\n <Text style={[styles.actionLabel, { color: t.text }]}>Help & Support</Text>\n <Text style={[styles.chevron, { color: t.textMuted }]}>{'\\u203A'}</Text>\n </TouchableOpacity>\n </>\n ) : null}\n </View>\n\n {/* Logout button */}\n <TouchableOpacity\n style={[styles.logoutButton, { borderColor: t.live }]}\n onPress={onLogout}\n disabled={loggingOut}\n activeOpacity={0.7}\n >\n {loggingOut ? (\n <ActivityIndicator color={t.live} size=\"small\" />\n ) : (\n <Text style={[styles.logoutText, { color: t.live }]}>Log Out</Text>\n )}\n </TouchableOpacity>\n\n {/* App version */}\n {appVersion ? (\n <Text style={[styles.version, { color: t.textDim }]}>v{appVersion}</Text>\n ) : null}\n </ScrollView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n },\n content: {\n padding: 20,\n paddingTop: 24,\n gap: 20,\n },\n actionsCard: {\n borderRadius: 16,\n borderWidth: 1,\n overflow: 'hidden',\n },\n actionRow: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-between',\n paddingHorizontal: 16,\n paddingVertical: 14,\n },\n actionRowLeft: {\n flex: 1,\n gap: 2,\n },\n actionLabel: {\n fontSize: 15,\n fontWeight: '600',\n },\n actionValue: {\n fontSize: 13,\n fontFamily: 'monospace',\n },\n copyLabel: {\n fontSize: 14,\n fontWeight: '600',\n },\n chevron: {\n fontSize: 22,\n fontWeight: '300',\n },\n separator: {\n height: 1,\n marginHorizontal: 16,\n },\n logoutButton: {\n height: 52,\n borderRadius: 16,\n borderWidth: 1.5,\n justifyContent: 'center',\n alignItems: 'center',\n },\n logoutText: {\n fontSize: 16,\n fontWeight: '700',\n },\n version: {\n fontSize: 12,\n textAlign: 'center',\n },\n});\n"],"mappings":"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","names":["bs58","useState","useEffect","useCallback","useState","useCallback","useEffect","useState","useEffect","useCallback","useState","useCallback","useEffect","useState","useEffect","useCallback","useState","useCallback","useEffect","useState","useCallback","Transaction","useState","useCallback","error","useState","useCallback","useState","useCallback","error","useState","useCallback","useState","useCallback","error","useState","useCallback","useState","useCallback","jsx","useMemo","View","Text","StyleSheet","jsx","jsxs","useMemo","View","styles","Text","StyleSheet","View","Text","TouchableOpacity","ActivityIndicator","StyleSheet","jsx","jsxs","truncateAddress","styles","View","TouchableOpacity","Text","ActivityIndicator","StyleSheet"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dubsdotapp/expo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React Native SDK for the Dubs betting platform",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"clean": "rm -rf dist"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"bs58": "^5.0.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@solana/web3.js": "^1.90.0",
|
|
30
|
+
"react": ">=18.0.0",
|
|
31
|
+
"react-native": ">=0.72.0"
|
|
32
|
+
},
|
|
33
|
+
"optionalDependencies": {
|
|
34
|
+
"@solana-mobile/mobile-wallet-adapter-protocol-web3js": "^2.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@solana/web3.js": "^1.95.0",
|
|
38
|
+
"@types/react": "^18.2.0",
|
|
39
|
+
"react": "^18.2.0",
|
|
40
|
+
"react-native": "^0.73.0",
|
|
41
|
+
"tsup": "^8.0.0",
|
|
42
|
+
"typescript": "^5.3.0"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"solana",
|
|
46
|
+
"betting",
|
|
47
|
+
"react-native",
|
|
48
|
+
"expo",
|
|
49
|
+
"dubs",
|
|
50
|
+
"web3"
|
|
51
|
+
],
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/iheartsolana/dubs-expo"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { DEFAULT_BASE_URL } from './constants';
|
|
2
|
+
import { DubsApiError, parseSolanaError, SOLANA_PROGRAM_ERRORS } from './errors';
|
|
3
|
+
import type {
|
|
4
|
+
UnifiedEvent,
|
|
5
|
+
Pagination,
|
|
6
|
+
EsportsMatchDetail,
|
|
7
|
+
ValidateEventResult,
|
|
8
|
+
CreateGameParams,
|
|
9
|
+
CreateGameResult,
|
|
10
|
+
JoinGameParams,
|
|
11
|
+
JoinGameResult,
|
|
12
|
+
ConfirmGameParams,
|
|
13
|
+
ConfirmGameResult,
|
|
14
|
+
BuildClaimParams,
|
|
15
|
+
BuildClaimResult,
|
|
16
|
+
GameDetail,
|
|
17
|
+
GameListItem,
|
|
18
|
+
GetGamesParams,
|
|
19
|
+
GetNetworkGamesParams,
|
|
20
|
+
GetUpcomingEventsParams,
|
|
21
|
+
ParsedError,
|
|
22
|
+
SolanaErrorCode,
|
|
23
|
+
NonceResult,
|
|
24
|
+
AuthenticateParams,
|
|
25
|
+
AuthenticateResult,
|
|
26
|
+
RegisterParams,
|
|
27
|
+
RegisterResult,
|
|
28
|
+
DubsUser,
|
|
29
|
+
DubsPublicUser,
|
|
30
|
+
DubsAppUser,
|
|
31
|
+
CheckUsernameResult,
|
|
32
|
+
} from './types';
|
|
33
|
+
|
|
34
|
+
export interface DubsClientConfig {
|
|
35
|
+
apiKey: string;
|
|
36
|
+
baseUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class DubsClient {
|
|
40
|
+
private readonly apiKey: string;
|
|
41
|
+
private readonly baseUrl: string;
|
|
42
|
+
private _token: string | null = null;
|
|
43
|
+
|
|
44
|
+
constructor(config: DubsClientConfig) {
|
|
45
|
+
this.apiKey = config.apiKey;
|
|
46
|
+
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Set the user JWT token for authenticated requests */
|
|
50
|
+
setToken(token: string | null): void {
|
|
51
|
+
this._token = token;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Get the current user JWT token */
|
|
55
|
+
getToken(): string | null {
|
|
56
|
+
return this._token;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── Private HTTP helper ──
|
|
60
|
+
|
|
61
|
+
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
62
|
+
const url = `${this.baseUrl}${path}`;
|
|
63
|
+
const headers: Record<string, string> = {
|
|
64
|
+
'X-API-Key': this.apiKey,
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (this._token) {
|
|
69
|
+
headers['Authorization'] = `Bearer ${this._token}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const res = await fetch(url, {
|
|
73
|
+
method,
|
|
74
|
+
headers,
|
|
75
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const json = await res.json();
|
|
79
|
+
|
|
80
|
+
if (!json.success) {
|
|
81
|
+
const err = json.error;
|
|
82
|
+
if (typeof err === 'object' && err !== null) {
|
|
83
|
+
throw new DubsApiError(err.code || 'unknown', err.message || 'Unknown error', res.status);
|
|
84
|
+
}
|
|
85
|
+
throw new DubsApiError('unknown', typeof err === 'string' ? err : 'Request failed', res.status);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return json as T;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── Events ──
|
|
92
|
+
|
|
93
|
+
async getUpcomingEvents(params?: GetUpcomingEventsParams): Promise<{ events: UnifiedEvent[]; pagination: Pagination }> {
|
|
94
|
+
const qs = new URLSearchParams();
|
|
95
|
+
if (params?.type) qs.set('type', params.type);
|
|
96
|
+
if (params?.game) qs.set('game', params.game);
|
|
97
|
+
if (params?.page) qs.set('page', String(params.page));
|
|
98
|
+
if (params?.per_page) qs.set('per_page', String(params.per_page));
|
|
99
|
+
const query = qs.toString();
|
|
100
|
+
const res = await this.request<{ success: true; events: UnifiedEvent[]; pagination: Pagination }>(
|
|
101
|
+
'GET',
|
|
102
|
+
`/events/upcoming${query ? `?${query}` : ''}`,
|
|
103
|
+
);
|
|
104
|
+
return { events: res.events, pagination: res.pagination };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async getSportsEvents(league: string): Promise<UnifiedEvent[]> {
|
|
108
|
+
const res = await this.request<{ success: true; league: string; events: UnifiedEvent[] }>(
|
|
109
|
+
'GET',
|
|
110
|
+
`/sports/events/${encodeURIComponent(league)}`,
|
|
111
|
+
);
|
|
112
|
+
return res.events;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getEsportsMatches(videogame?: string): Promise<UnifiedEvent[]> {
|
|
116
|
+
const qs = videogame ? `?videogame=${encodeURIComponent(videogame)}` : '';
|
|
117
|
+
const res = await this.request<{ success: true; matches: UnifiedEvent[] }>(
|
|
118
|
+
'GET',
|
|
119
|
+
`/esports/matches/upcoming${qs}`,
|
|
120
|
+
);
|
|
121
|
+
return res.matches;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async getEsportsMatchDetail(matchId: string | number): Promise<EsportsMatchDetail> {
|
|
125
|
+
const res = await this.request<{ success: true; match: EsportsMatchDetail }>(
|
|
126
|
+
'GET',
|
|
127
|
+
`/esports/matches/${encodeURIComponent(String(matchId))}`,
|
|
128
|
+
);
|
|
129
|
+
return res.match;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── Game Lifecycle ──
|
|
133
|
+
|
|
134
|
+
async validateEvent(id: string): Promise<ValidateEventResult> {
|
|
135
|
+
const res = await this.request<{ success: true } & ValidateEventResult>(
|
|
136
|
+
'POST',
|
|
137
|
+
'/games/validate',
|
|
138
|
+
{ id },
|
|
139
|
+
);
|
|
140
|
+
return {
|
|
141
|
+
bettable: res.bettable,
|
|
142
|
+
gameMode: res.gameMode,
|
|
143
|
+
lockTimestamp: res.lockTimestamp,
|
|
144
|
+
startTime: res.startTime,
|
|
145
|
+
status: res.status,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async createGame(params: CreateGameParams): Promise<CreateGameResult> {
|
|
150
|
+
const res = await this.request<{ success: true } & CreateGameResult>(
|
|
151
|
+
'POST',
|
|
152
|
+
'/games/create',
|
|
153
|
+
params,
|
|
154
|
+
);
|
|
155
|
+
return {
|
|
156
|
+
gameId: res.gameId,
|
|
157
|
+
gameAddress: res.gameAddress,
|
|
158
|
+
transaction: res.transaction,
|
|
159
|
+
lockTimestamp: res.lockTimestamp,
|
|
160
|
+
event: res.event,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async joinGame(params: JoinGameParams): Promise<JoinGameResult> {
|
|
165
|
+
const res = await this.request<{ success: true } & JoinGameResult>(
|
|
166
|
+
'POST',
|
|
167
|
+
'/games/join',
|
|
168
|
+
params,
|
|
169
|
+
);
|
|
170
|
+
return {
|
|
171
|
+
gameId: res.gameId,
|
|
172
|
+
transaction: res.transaction,
|
|
173
|
+
gameAddress: res.gameAddress,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async confirmGame(params: ConfirmGameParams): Promise<ConfirmGameResult> {
|
|
178
|
+
const res = await this.request<{ success: true } & ConfirmGameResult>(
|
|
179
|
+
'POST',
|
|
180
|
+
'/games/confirm',
|
|
181
|
+
params,
|
|
182
|
+
);
|
|
183
|
+
return {
|
|
184
|
+
gameId: res.gameId,
|
|
185
|
+
signature: res.signature,
|
|
186
|
+
explorerUrl: res.explorerUrl,
|
|
187
|
+
message: res.message,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async buildClaimTransaction(params: BuildClaimParams): Promise<BuildClaimResult> {
|
|
192
|
+
const res = await this.request<{ success: true } & BuildClaimResult>(
|
|
193
|
+
'POST',
|
|
194
|
+
'/transactions/build/claim',
|
|
195
|
+
params,
|
|
196
|
+
);
|
|
197
|
+
return {
|
|
198
|
+
transaction: res.transaction,
|
|
199
|
+
gameAddress: res.gameAddress,
|
|
200
|
+
message: res.message,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ── Game Queries ──
|
|
205
|
+
|
|
206
|
+
async getGame(gameId: string): Promise<GameDetail> {
|
|
207
|
+
const res = await this.request<{ success: true; game: GameDetail }>(
|
|
208
|
+
'GET',
|
|
209
|
+
`/games/${encodeURIComponent(gameId)}`,
|
|
210
|
+
);
|
|
211
|
+
return res.game;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async getGames(params?: GetGamesParams): Promise<GameListItem[]> {
|
|
215
|
+
const qs = new URLSearchParams();
|
|
216
|
+
if (params?.wallet) qs.set('wallet', params.wallet);
|
|
217
|
+
if (params?.status) qs.set('status', params.status);
|
|
218
|
+
if (params?.limit != null) qs.set('limit', String(params.limit));
|
|
219
|
+
if (params?.offset != null) qs.set('offset', String(params.offset));
|
|
220
|
+
const query = qs.toString();
|
|
221
|
+
const res = await this.request<{ success: true; games: GameListItem[] }>(
|
|
222
|
+
'GET',
|
|
223
|
+
`/games${query ? `?${query}` : ''}`,
|
|
224
|
+
);
|
|
225
|
+
return res.games;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ── Network ──
|
|
229
|
+
|
|
230
|
+
async getNetworkGames(params?: GetNetworkGamesParams): Promise<{ games: GameListItem[]; pagination: Pagination }> {
|
|
231
|
+
const qs = new URLSearchParams();
|
|
232
|
+
if (params?.league) qs.set('league', params.league);
|
|
233
|
+
if (params?.limit != null) qs.set('limit', String(params.limit));
|
|
234
|
+
if (params?.offset != null) qs.set('offset', String(params.offset));
|
|
235
|
+
const query = qs.toString();
|
|
236
|
+
const res = await this.request<{ success: true; games: GameListItem[]; pagination: Pagination }>(
|
|
237
|
+
'GET',
|
|
238
|
+
`/network/games${query ? `?${query}` : ''}`,
|
|
239
|
+
);
|
|
240
|
+
return { games: res.games, pagination: res.pagination };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ── User Auth ──
|
|
244
|
+
|
|
245
|
+
async getNonce(walletAddress: string): Promise<NonceResult> {
|
|
246
|
+
const res = await this.request<{ success: true; data: NonceResult }>(
|
|
247
|
+
'POST',
|
|
248
|
+
'/auth/nonce',
|
|
249
|
+
{ walletAddress },
|
|
250
|
+
);
|
|
251
|
+
return res.data;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async authenticate(params: AuthenticateParams): Promise<AuthenticateResult> {
|
|
255
|
+
const res = await this.request<{ success: true; data: AuthenticateResult }>(
|
|
256
|
+
'POST',
|
|
257
|
+
'/auth/authenticate',
|
|
258
|
+
params,
|
|
259
|
+
);
|
|
260
|
+
// Auto-store token on successful auth
|
|
261
|
+
if (res.data.token) {
|
|
262
|
+
this._token = res.data.token;
|
|
263
|
+
}
|
|
264
|
+
return res.data;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async register(params: RegisterParams): Promise<RegisterResult> {
|
|
268
|
+
const res = await this.request<{ success: true; data: RegisterResult }>(
|
|
269
|
+
'POST',
|
|
270
|
+
'/auth/register',
|
|
271
|
+
params,
|
|
272
|
+
);
|
|
273
|
+
// Auto-store token on successful registration
|
|
274
|
+
if (res.data.token) {
|
|
275
|
+
this._token = res.data.token;
|
|
276
|
+
}
|
|
277
|
+
return res.data;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async getMe(): Promise<DubsUser> {
|
|
281
|
+
const res = await this.request<{ success: true; data: { user: DubsUser } }>(
|
|
282
|
+
'GET',
|
|
283
|
+
'/auth/me',
|
|
284
|
+
);
|
|
285
|
+
return res.data.user;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async getUser(walletAddress: string): Promise<DubsPublicUser | null> {
|
|
289
|
+
const res = await this.request<{ success: true; data: { user: DubsPublicUser | null } }>(
|
|
290
|
+
'GET',
|
|
291
|
+
`/users/${encodeURIComponent(walletAddress)}`,
|
|
292
|
+
);
|
|
293
|
+
return res.data.user;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async getAppUsers(params?: { limit?: number; offset?: number }): Promise<{ users: DubsAppUser[]; pagination: { total: number; limit: number; offset: number } }> {
|
|
297
|
+
const qs = new URLSearchParams();
|
|
298
|
+
if (params?.limit != null) qs.set('limit', String(params.limit));
|
|
299
|
+
if (params?.offset != null) qs.set('offset', String(params.offset));
|
|
300
|
+
const query = qs.toString();
|
|
301
|
+
const res = await this.request<{ success: true; data: { users: DubsAppUser[]; pagination: { total: number; limit: number; offset: number } } }>(
|
|
302
|
+
'GET',
|
|
303
|
+
`/users${query ? `?${query}` : ''}`,
|
|
304
|
+
);
|
|
305
|
+
return res.data;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async checkUsername(username: string): Promise<CheckUsernameResult> {
|
|
309
|
+
const res = await this.request<{ success: true; data: CheckUsernameResult }>(
|
|
310
|
+
'GET',
|
|
311
|
+
`/auth/check-username/${encodeURIComponent(username)}`,
|
|
312
|
+
);
|
|
313
|
+
return res.data;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async logout(): Promise<void> {
|
|
317
|
+
await this.request<{ success: true; data: { message: string } }>(
|
|
318
|
+
'POST',
|
|
319
|
+
'/auth/logout',
|
|
320
|
+
);
|
|
321
|
+
this._token = null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Error Utilities ──
|
|
325
|
+
|
|
326
|
+
async parseError(error: unknown): Promise<ParsedError> {
|
|
327
|
+
const res = await this.request<{ success: true; error: ParsedError }>(
|
|
328
|
+
'POST',
|
|
329
|
+
'/errors/parse',
|
|
330
|
+
{ error },
|
|
331
|
+
);
|
|
332
|
+
return res.error;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async getErrorCodes(): Promise<Record<number, SolanaErrorCode>> {
|
|
336
|
+
const res = await this.request<{ success: true; errors: Record<number, SolanaErrorCode> }>(
|
|
337
|
+
'GET',
|
|
338
|
+
'/errors/codes',
|
|
339
|
+
);
|
|
340
|
+
return res.errors;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Parse a Solana error locally without an API call.
|
|
345
|
+
* Useful for instant feedback in the UI.
|
|
346
|
+
*/
|
|
347
|
+
parseErrorLocal(error: unknown): ParsedError {
|
|
348
|
+
return parseSolanaError(error);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** Get the full local error code map */
|
|
352
|
+
getErrorCodesLocal(): Record<number, SolanaErrorCode> {
|
|
353
|
+
return { ...SOLANA_PROGRAM_ERRORS };
|
|
354
|
+
}
|
|
355
|
+
}
|
package/src/constants.ts
ADDED