Documentation Index
Fetch the complete documentation index at: https://docs.whisky.casino/llms.txt
Use this file to discover all available pages before exploring further.
Whisky React SDK
The Whisky React SDK provides React components and hooks for seamless integration of the Whisky Gaming Protocol into frontend applications.
Overview
The React SDK serves as the UI layer, providing:
- React Hooks: Custom hooks for protocol interaction
- UI Components: Pre-built gaming components
- Wallet Integration: Seamless wallet connectivity
- State Management: Application state handling
Features
- Unified Package: Combines UI components and React hooks in a single package
- Whisky Protocol Integration: Built specifically for the Whisky Gaming Protocol
- TypeScript Support: Full TypeScript support with comprehensive type definitions
- Wallet Integration: Seamless integration with Solana wallet adapters
- Plugin System: Extensible plugin system for custom functionality
- Transaction Management: Built-in transaction handling and state management
Installation
npm install @whisky-gaming/ui
Quick Start
import { WhiskyProvider, useWhisky } from '@whisky-gaming/ui'
function App() {
return (
<WhiskyProvider>
<YourGame />
</WhiskyProvider>
)
}
function YourGame() {
const { play, isPlaying, game } = useWhisky()
const handlePlay = async () => {
await play({
wager: 1000000, // 0.001 SOL
bet: [50, 50], // Equal probability
creator: 'YOUR_CREATOR_ADDRESS',
})
}
return (
<div>
<button onClick={handlePlay} disabled={isPlaying}>
{isPlaying ? 'Playing...' : 'Play'}
</button>
</div>
)
}
Core Components
WhiskyProvider
The main provider that wraps your app and provides access to the Whisky protocol.
import { WhiskyProvider } from '@whisky-gaming/ui'
<WhiskyProvider plugins={[yourCustomPlugin]}>
{children}
</WhiskyProvider>
Props:
interface WhiskyProviderProps {
children: React.ReactNode;
plugins?: WhiskyPlugin[];
config?: WhiskyConfig;
}
Provides platform-specific functionality like token selection and pool management.
import { WhiskyPlatformProvider } from '@whisky-gaming/ui'
<WhiskyPlatformProvider>
{children}
</WhiskyPlatformProvider>
Hooks
useWhisky()
Main hook for interacting with the Whisky protocol.
const { play, result, isPlaying, game, error } = useWhisky()
Returns:
interface WhiskyHookReturn {
play: (params: PlayParams) => Promise<void>;
result: GameResult | null;
isPlaying: boolean;
game: Game | null;
error: Error | null;
reset: () => void;
}
Example:
function GamingComponent() {
const { play, result, isPlaying, game, error } = useWhisky()
const handlePlay = async () => {
try {
await play({
wager: 1000000,
bet: [50, 50],
creator: 'CREATOR_ADDRESS',
clientSeed: 'my_seed_123'
})
} catch (err) {
console.error('Game failed:', err)
}
}
return (
<div>
<button onClick={handlePlay} disabled={isPlaying}>
{isPlaying ? 'Playing...' : 'Play Game'}
</button>
{result && (
<div>
{result.won ? 'You won!' : 'You lost!'}
<p>Payout: {result.payout}</p>
</div>
)}
{error && <div>Error: {error.message}</div>}
</div>
)
}
usePool()
Get pool information for a specific token.
const { pool, loading, error } = usePool(tokenMint, poolAuthority)
Parameters:
tokenMint: Token mint address
poolAuthority: Pool authority address
Returns:
interface PoolHookReturn {
pool: PoolInfo | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
Example:
function PoolInfo({ tokenMint, poolAuthority }) {
const { pool, loading, error } = usePool(tokenMint, poolAuthority)
if (loading) return <div>Loading pool...</div>
if (error) return <div>Error: {error.message}</div>
if (!pool) return <div>Pool not found</div>
return (
<div>
<h3>Pool Information</h3>
<p>Total Liquidity: {pool.totalLiquidity}</p>
<p>Total Plays: {pool.totalPlays}</p>
<p>Min Wager: {pool.minWager}</p>
</div>
)
}
useBalance()
Get balance information for a wallet and token.
const { balance, nativeBalance } = useBalance(wallet, token)
Parameters:
wallet: Wallet public key
token: Token mint address (optional)
Returns:
interface BalanceHookReturn {
balance: number | null;
nativeBalance: number | null;
bonusBalance: number | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
Example:
function BalanceDisplay({ wallet, token }) {
const { balance, nativeBalance, loading } = useBalance(wallet, token)
if (loading) return <div>Loading balance...</div>
return (
<div>
<p>Token Balance: {balance}</p>
<p>SOL Balance: {nativeBalance}</p>
</div>
)
}
useWalletAddress()
Get the current wallet address.
const walletAddress = useWalletAddress()
Returns: string | null
Example:
function WalletInfo() {
const walletAddress = useWalletAddress()
if (!walletAddress) {
return <div>Please connect your wallet</div>
}
return (
<div>
<p>Connected: {walletAddress.substring(0, 8)}...</p>
</div>
)
}
UI Components
TokenValue
Display token values with proper formatting.
import { TokenValue } from '@whisky-gaming/ui'
<TokenValue amount={1000000} token={tokenMeta} />
Props:
interface TokenValueProps {
amount: number;
token: TokenMetadata;
showSymbol?: boolean;
decimals?: number;
className?: string;
}
Example:
function TokenDisplay() {
const usdcToken = {
symbol: 'USDC',
decimals: 6,
name: 'USD Coin'
}
return (
<div>
<TokenValue
amount={1000000}
token={usdcToken}
showSymbol={true}
/>
{/* Displays: "1.00 USDC" */}
</div>
)
}
Canvas
Game canvas component for rendering games.
import { Canvas } from '@whisky-gaming/ui'
<Canvas game={gameBundle} />
Props:
interface CanvasProps {
game: GameBundle;
width?: number;
height?: number;
onGameEnd?: (result: GameResult) => void;
className?: string;
}
Example:
function GameCanvas({ gameBundle }) {
const handleGameEnd = (result) => {
console.log('Game ended:', result)
}
return (
<Canvas
game={gameBundle}
width={800}
height={600}
onGameEnd={handleGameEnd}
/>
)
}
Pre-styled button component for gaming actions.
import { GameButton } from '@whisky-gaming/ui'
<GameButton onClick={handlePlay} disabled={isPlaying}>
Play Game
</GameButton>
Props:
interface GameButtonProps {
children: React.ReactNode;
onClick: () => void;
disabled?: boolean;
variant?: 'primary' | 'secondary' | 'success' | 'danger';
size?: 'small' | 'medium' | 'large';
loading?: boolean;
className?: string;
}
TokenSelector
Component for selecting tokens for gaming.
import { TokenSelector } from '@whisky-gaming/ui'
<TokenSelector
selectedToken={selectedToken}
onTokenSelect={setSelectedToken}
tokens={availableTokens}
/>
Props:
interface TokenSelectorProps {
selectedToken: TokenMetadata | null;
onTokenSelect: (token: TokenMetadata) => void;
tokens: TokenMetadata[];
disabled?: boolean;
className?: string;
}
Plugin System
Create custom plugins to extend functionality:
import { WhiskyPlugin } from '@whisky-gaming/ui'
const myPlugin: WhiskyPlugin = async (input, context) => {
// Your custom logic here
return [] // Return additional transaction instructions
}
<WhiskyProvider plugins={[myPlugin]}>
{children}
</WhiskyProvider>
Plugin Interface:
interface WhiskyPlugin {
name: string;
version: string;
execute: (input: any, context: PluginContext) => Promise<any[]>;
}
Example Plugin:
const referralPlugin: WhiskyPlugin = {
name: 'referral',
version: '1.0.0',
execute: async (input, context) => {
const { referralCode } = input
if (referralCode) {
// Add referral logic
return [referralInstruction]
}
return []
}
}
function App() {
return (
<WhiskyProvider plugins={[referralPlugin]}>
<GamingApp />
</WhiskyProvider>
)
}
Referral System
Built-in referral system support:
import { ReferralProvider, useReferral } from '@whisky-gaming/ui'
<ReferralProvider>
<YourApp />
</ReferralProvider>
// In your component
const { referralCode, setReferralCode } = useReferral()
Referral Hook:
interface ReferralHookReturn {
referralCode: string | null;
setReferralCode: (code: string) => void;
clearReferralCode: () => void;
referralStats: ReferralStats | null;
}
Example:
function ReferralSystem() {
const { referralCode, setReferralCode, referralStats } = useReferral()
const handleReferralInput = (code: string) => {
setReferralCode(code)
}
return (
<div>
<input
placeholder="Enter referral code"
onChange={(e) => handleReferralInput(e.target.value)}
value={referralCode || ''}
/>
{referralStats && (
<div>
<p>Referrals: {referralStats.totalReferrals}</p>
<p>Earnings: {referralStats.totalEarnings}</p>
</div>
)}
</div>
)
}
Advanced Usage Examples
Complete Gaming Application
import React from 'react'
import {
WhiskyProvider,
useWhisky,
usePool,
useBalance,
TokenValue,
GameButton,
TokenSelector
} from '@whisky-gaming/ui'
function GamingApp() {
return (
<WhiskyProvider>
<div className="gaming-app">
<h1>Whisky Gaming</h1>
<GameInterface />
</div>
</WhiskyProvider>
)
}
function GameInterface() {
const { play, isPlaying, result, error } = useWhisky()
const [selectedToken, setSelectedToken] = useState(null)
const [wager, setWager] = useState(1000000)
const [bet, setBet] = useState([50, 50])
const { pool } = usePool(selectedToken?.mint)
const { balance } = useBalance(wallet, selectedToken?.mint)
const handlePlay = async () => {
if (!selectedToken || !pool) return
await play({
poolAddress: pool.address,
wager,
bet,
creator: 'CREATOR_ADDRESS',
clientSeed: `game_${Date.now()}`,
creatorFee: 100,
jackpotFee: 50,
metadata: 'Custom Game'
})
}
return (
<div className="game-interface">
<div className="token-section">
<TokenSelector
selectedToken={selectedToken}
onTokenSelect={setSelectedToken}
tokens={availableTokens}
/>
{balance && (
<div className="balance">
Balance: <TokenValue amount={balance} token={selectedToken} />
</div>
)}
</div>
<div className="game-controls">
<input
type="number"
value={wager}
onChange={(e) => setWager(Number(e.target.value))}
placeholder="Wager amount"
/>
<GameButton
onClick={handlePlay}
disabled={isPlaying || !selectedToken || !pool}
loading={isPlaying}
>
{isPlaying ? 'Playing...' : 'Play Game'}
</GameButton>
</div>
{result && (
<div className="game-result">
<h3>{result.won ? 'You Won!' : 'You Lost!'}</h3>
<p>Payout: <TokenValue amount={result.payout} token={selectedToken} /></p>
<p>Multiplier: {result.multiplier}x</p>
</div>
)}
{error && (
<div className="error">
Error: {error.message}
</div>
)}
</div>
)
}
Multi-Game Interface
function MultiGameInterface() {
const [selectedGame, setSelectedGame] = useState('coinflip')
const games = {
coinflip: {
name: 'Coin Flip',
bet: [50, 50],
description: 'Simple 50/50 chance'
},
dice: {
name: 'Dice Roll',
bet: [16, 16, 16, 16, 16, 20],
description: 'Six-sided dice with slight bias'
},
slots: {
name: 'Slot Machine',
bet: [500, 300, 150, 40, 9, 1],
description: 'Complex slot machine simulation'
}
}
const { play, isPlaying } = useWhisky()
const handleGameSelect = (gameKey) => {
setSelectedGame(gameKey)
}
const handlePlay = async () => {
const game = games[selectedGame]
await play({
wager: 1000000,
bet: game.bet,
creator: 'CREATOR_ADDRESS',
metadata: game.name
})
}
return (
<div className="multi-game">
<div className="game-selector">
{Object.entries(games).map(([key, game]) => (
<button
key={key}
onClick={() => handleGameSelect(key)}
className={selectedGame === key ? 'active' : ''}
>
{game.name}
</button>
))}
</div>
<div className="game-info">
<h3>{games[selectedGame].name}</h3>
<p>{games[selectedGame].description}</p>
</div>
<GameButton onClick={handlePlay} disabled={isPlaying}>
Play {games[selectedGame].name}
</GameButton>
</div>
)
}
Pool Management Interface
function PoolManagement() {
const [selectedPool, setSelectedPool] = useState(null)
const [depositAmount, setDepositAmount] = useState('')
const { pools, loading } = usePools()
const { depositLiquidity, withdrawLiquidity } = usePoolActions()
const handleDeposit = async () => {
if (!selectedPool || !depositAmount) return
await depositLiquidity(selectedPool.address, Number(depositAmount))
setDepositAmount('')
}
const handleWithdraw = async (lpTokenAmount) => {
if (!selectedPool) return
await withdrawLiquidity(selectedPool.address, lpTokenAmount)
}
return (
<div className="pool-management">
<h2>Pool Management</h2>
<div className="pool-list">
{pools.map(pool => (
<div
key={pool.address}
className={`pool-item ${selectedPool?.address === pool.address ? 'selected' : ''}`}
onClick={() => setSelectedPool(pool)}
>
<TokenValue amount={pool.totalLiquidity} token={pool.token} />
<p>Total Plays: {pool.totalPlays}</p>
<p>Fee: {pool.feeBps / 100}%</p>
</div>
))}
</div>
{selectedPool && (
<div className="pool-actions">
<h3>Manage {selectedPool.token.symbol} Pool</h3>
<div className="deposit-section">
<input
type="number"
value={depositAmount}
onChange={(e) => setDepositAmount(e.target.value)}
placeholder="Amount to deposit"
/>
<GameButton onClick={handleDeposit}>
Deposit
</GameButton>
</div>
<div className="withdraw-section">
<p>Your LP Tokens: {selectedPool.userLpTokens}</p>
<GameButton
onClick={() => handleWithdraw(selectedPool.userLpTokens)}
variant="secondary"
>
Withdraw All
</GameButton>
</div>
</div>
)}
</div>
)
}
Styling and Theming
Custom Styling
// Custom CSS
.game-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
color: white;
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.game-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.game-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
Theme Provider
import { WhiskyThemeProvider } from '@whisky-gaming/ui'
const customTheme = {
colors: {
primary: '#667eea',
secondary: '#764ba2',
success: '#48bb78',
danger: '#f56565',
background: '#1a202c',
text: '#ffffff'
},
borderRadius: '8px',
spacing: {
small: '8px',
medium: '16px',
large: '24px'
}
}
<WhiskyThemeProvider theme={customTheme}>
<GamingApp />
</WhiskyThemeProvider>
Error Handling
Global Error Boundary
import { WhiskyErrorBoundary } from '@whisky-gaming/ui'
function App() {
return (
<WhiskyErrorBoundary
fallback={<div>Something went wrong with the gaming system</div>}
>
<WhiskyProvider>
<GamingApp />
</WhiskyProvider>
</WhiskyErrorBoundary>
)
}
Hook Error Handling
function SafeGamingComponent() {
const { play, error, reset } = useWhisky()
useEffect(() => {
if (error) {
// Log error to monitoring service
console.error('Gaming error:', error)
// Show user-friendly message
toast.error('Game failed. Please try again.')
}
}, [error])
const handlePlay = async () => {
try {
await play(gameParams)
} catch (err) {
// Handle specific errors
if (err.code === 'INSUFFICIENT_BALANCE') {
toast.error('Insufficient balance')
} else if (err.code === 'POOL_NOT_FOUND') {
toast.error('Pool not available')
} else {
toast.error('Unexpected error occurred')
}
}
}
return (
<div>
<GameButton onClick={handlePlay}>
Play Game
</GameButton>
{error && (
<button onClick={reset}>
Reset Game State
</button>
)}
</div>
)
}
Memoization
import React, { useMemo, useCallback } from 'react'
function OptimizedGamingComponent() {
const { play, isPlaying } = useWhisky()
// Memoize expensive calculations
const gameConfig = useMemo(() => ({
bet: [25, 25, 25, 25],
creatorFee: 100,
jackpotFee: 50
}), [])
// Memoize callback
const handlePlay = useCallback(async () => {
await play({
wager: 1000000,
...gameConfig,
metadata: 'Optimized Game'
})
}, [play, gameConfig])
return (
<GameButton onClick={handlePlay} disabled={isPlaying}>
Play Optimized Game
</GameButton>
)
}
Lazy Loading
import React, { lazy, Suspense } from 'react'
const GameCanvas = lazy(() => import('./GameCanvas'))
const PoolManagement = lazy(() => import('./PoolManagement'))
function LazyGamingApp() {
return (
<WhiskyProvider>
<Suspense fallback={<div>Loading...</div>}>
<GameCanvas />
<PoolManagement />
</Suspense>
</WhiskyProvider>
)
}
Testing
Component Testing
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { WhiskyProvider } from '@whisky-gaming/ui'
function TestGamingComponent() {
const { play, isPlaying } = useWhisky()
return (
<button onClick={() => play(gameParams)} disabled={isPlaying}>
{isPlaying ? 'Playing...' : 'Play'}
</button>
)
}
test('renders play button', () => {
render(
<WhiskyProvider>
<TestGamingComponent />
</WhiskyProvider>
)
expect(screen.getByText('Play')).toBeInTheDocument()
})
test('handles play action', async () => {
render(
<WhiskyProvider>
<TestGamingComponent />
</WhiskyProvider>
)
const button = screen.getByText('Play')
fireEvent.click(button)
await waitFor(() => {
expect(screen.getByText('Playing...')).toBeInTheDocument()
})
})
Hook Testing
import { renderHook, act } from '@testing-library/react-hooks'
import { useWhisky } from '@whisky-gaming/ui'
test('useWhisky hook', () => {
const { result } = renderHook(() => useWhisky(), {
wrapper: WhiskyProvider
})
expect(result.current.isPlaying).toBe(false)
expect(result.current.result).toBe(null)
act(() => {
result.current.play(gameParams)
})
expect(result.current.isPlaying).toBe(true)
})
Dependencies
This package requires the following peer dependencies:
@whisky-gaming/core: The core Whisky protocol SDK
@solana/wallet-adapter-react: Solana wallet integration
@solana/web3.js: Solana web3 utilities
react: React library
react-dom: React DOM
Development
# Install dependencies
npm install
# Build the package
npm run build
# Development mode with watch
npm run dev
# Type checking
npm run lint
Support
For React SDK support and questions: