Pre-built React components for quickly integrating prediction markets into your application.
Complete reference for all components, hooks, and SDK methods available in PredictKit.
Pre-built React components for quickly integrating prediction markets into your application.
A complete betting interface for a single prediction market.
interface PredictionWidgetProps {
marketAddress: string; // Market contract address
onBetPlaced?: (txHash: string) => void;
onError?: (error: Error) => void;
className?: string;
}import { PredictionWidget } from '@predictkit/react';
function MarketPage() {
return (
<PredictionWidget
marketAddress="0x..."
onBetPlaced={(txHash) => {
console.log('Bet placed:', txHash);
}}
/>
);
}Display a list of prediction markets with filtering and sorting options.
interface MarketListProps {
filter?: 'all' | 'active' | 'resolved'; // Filter by status
sortBy?: 'tvl' | 'recent' | 'ending-soon'; // Sort order
onMarketClick?: (market: MarketInfo) => void;
developerAddress?: string; // Filter by creator
className?: string;
emptyMessage?: string;
}import { MarketList } from '@predictkit/react';
function MarketsPage() {
return (
<MarketList
filter="active"
sortBy="tvl"
onMarketClick={(market) => {
router.push(`/market/${market.address}`);
}}
/>
);
}A card component displaying market info with odds bar, badges, and stats.
interface MarketCardProps {
market: MarketInfo; // Market data object
onClick?: () => void; // Click handler
showOdds?: boolean; // Show odds bar (default: true)
className?: string;
}import { MarketCard, useMarkets } from '@predictkit/react';
function MarketGrid() {
const { markets } = useMarkets();
return (
<div className="grid">
{markets.map((market) => (
<MarketCard
key={market.address}
market={market}
onClick={() => navigate(`/market/${market.address}`)}
/>
))}
</div>
);
}Interface for adding and removing liquidity from market pools.
interface LiquidityManagerProps {
poolAddress: string; // Liquidity pool address
marketAddress: string; // Associated market address
onSuccess?: (txHash: string) => void;
onError?: (error: Error) => void;
className?: string;
}import { LiquidityManager } from '@predictkit/react';
function LiquidityPage({ poolAddress, marketAddress }) {
return (
<LiquidityManager
poolAddress={poolAddress}
marketAddress={marketAddress}
onSuccess={(txHash) => {
console.log('LP operation successful:', txHash);
}}
/>
);
}Low-level hooks for building custom interfaces with full control.
Hook for interacting with a single prediction market.
function useMarket(marketAddress: string): {
marketInfo: MarketInfo | null;
odds: OddsData | null;
userPosition: UserBet | null;
isLoading: boolean;
error: Error | null;
placeBet: (params: PlaceBetParams) => Promise<string>;
claimPayout: () => Promise<string>;
refetch: () => void;
}import { useMarket } from '@predictkit/react';
import { parseEther } from 'viem';
function CustomMarket({ marketAddress }) {
const {
marketInfo,
odds,
placeBet,
isLoading
} = useMarket(marketAddress);
const handleBet = async (side: boolean) => {
try {
const txHash = await placeBet({
side,
amount: parseEther('0.1')
});
console.log('Bet placed:', txHash);
} catch (error) {
console.error('Error:', error);
}
};
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h2>{marketInfo?.question}</h2>
<p>YES: {odds?.yesPercent}%</p>
<p>NO: {odds?.noPercent}%</p>
<button onClick={() => handleBet(true)}>
Bet YES
</button>
<button onClick={() => handleBet(false)}>
Bet NO
</button>
</div>
);
}Hook for fetching and listing multiple markets.
function useMarkets(options?: {
fromBlock?: number;
toBlock?: number | string;
developerAddress?: string;
}): {
markets: MarketInfo[];
activeMarkets: MarketInfo[];
resolvedMarkets: MarketInfo[];
marketsByTVL: MarketInfo[];
isLoading: boolean;
error: Error | null;
refetch: () => void;
}import { useMarkets } from '@predictkit/react';
function MarketsList() {
const { markets, activeMarkets, isLoading } = useMarkets();
if (isLoading) return <div>Loading markets...</div>;
return (
<div>
<h2>Active Markets ({activeMarkets.length})</h2>
{activeMarkets.map((market) => (
<div key={market.address}>
<h3>{market.question}</h3>
<p>TVL: {market.tvl} BNB</p>
</div>
))}
</div>
);
}Hook for managing liquidity pool positions - add/remove liquidity and claim fees.
function useLiquidity(poolAddress: string, marketAddress: string): {
position: LPPosition | null;
totalLiquidity: PoolLiquidity | null;
pendingFees: bigint;
hasPosition: boolean;
canClaimFees: boolean;
addLiquidity: (params, callbacks?) => Promise<void>;
addLiquidityAsync: (params) => Promise<string>;
removeLiquidity: (params, callbacks?) => Promise<void>;
removeLiquidityAsync: (params) => Promise<string>;
claimFees: (params?, callbacks?) => Promise<void>;
claimFeesAsync: () => Promise<string>;
previewAddLiquidity: (yesAmount, noAmount) => Promise<bigint>;
previewRemoveLiquidity: (lpTokens) => Promise<{amountYes, amountNo}>;
isLoadingPosition: boolean;
isAddingLiquidity: boolean;
isRemovingLiquidity: boolean;
isClaimingFees: boolean;
}CRITICAL LP RISK
You deposit BOTH YES and NO. When market resolves, your LOSING SIDE IS COMPLETELY LOST to winners.
Example: Deposit 1 YES + 1 NO. Market resolves YES. Winners claim your 1 NO. You withdraw only 1 YES. Net: 50% loss + fees earned.
Only profitable if: Fees earned > Value of losing side. Best for very high-volume markets.
import { useLiquidity, formatBNB, parseBNB } from '@predictkit/react';
function LiquidityInterface({ poolAddress, marketAddress }) {
const {
position,
totalLiquidity,
pendingFees,
addLiquidityAsync,
removeLiquidityAsync,
claimFeesAsync
} = useLiquidity(poolAddress, marketAddress);
const handleAddLP = async () => {
const txHash = await addLiquidityAsync({
amountYes: parseBNB('0.1'),
amountNo: parseBNB('0.1')
});
console.log('LP added:', txHash);
};
const handleRemoveLP = async () => {
const txHash = await removeLiquidityAsync({
lpTokens: position.lpTokens // Remove all
});
console.log('LP removed:', txHash);
};
const handleClaimFees = async () => {
const txHash = await claimFeesAsync();
console.log('Fees claimed:', txHash);
};
return (
<div>
{/* Pool Stats */}
<div>
<h3>Pool TVL: {formatBNB(totalLiquidity?.tvl)} BNB</h3>
<p>YES: {formatBNB(totalLiquidity?.totalYes)} BNB</p>
<p>NO: {formatBNB(totalLiquidity?.totalNo)} BNB</p>
</div>
{/* Your Position */}
{position && (
<div>
<h3>Your Position</h3>
<p>LP Tokens: {position.lpTokens.toString()}</p>
<p>Share: {(position.shareOfPool / 100).toFixed(2)}%</p>
<p>Pending Fees: {formatBNB(pendingFees)} BNB</p>
</div>
)}
{/* Actions */}
<button onClick={handleAddLP}>Add Liquidity</button>
<button onClick={handleRemoveLP}>Remove Liquidity</button>
{pendingFees > 0n && (
<button onClick={handleClaimFees}>Claim Fees</button>
)}
</div>
);
}Hook for fetching user betting positions and portfolio stats across all markets.
function useUserPortfolio(userAddress: string): {
positions: UserPosition[];
stats: PortfolioStats;
loading: boolean;
error: Error | null;
}Hook for fetching all LP positions for a user across all markets via The Graph subgraph. Tracks liquidity provided, fees earned, and position details.
function useUserLPPositions(): {
positions: LPPositionInfo[];
stats: UserLPStats;
loading: boolean;
error: Error | null;
refetch: () => Promise<void>;
}
interface LPPositionInfo {
marketAddress: string;
poolAddress: string;
question: string;
resolved: boolean;
outcome?: boolean;
lpTokens: bigint;
pendingFees: bigint;
totalFeesEarned: bigint;
poolTotalYes: bigint;
poolTotalNo: bigint;
poolTVL: bigint;
shareOfPool: number; // basis points
}
interface UserLPStats {
totalLiquidityProvided: bigint;
marketsProvidedLiquidity: number;
totalFeesEarned: bigint;
pendingFees: bigint;
activePools: number;
}import { useUserLPPositions, formatBNB } from '@predictkit/react';
function LPDashboard() {
const { positions, stats, loading, error } = useUserLPPositions();
if (loading) return <div>Loading LP positions...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Liquidity Provider Dashboard</h2>
{/* Aggregate Stats */}
<div className="stats-grid">
<div>
<h3>Total Liquidity Provided</h3>
<p>{formatBNB(stats.totalLiquidityProvided)} BNB</p>
</div>
<div>
<h3>Markets Providing Liquidity</h3>
<p>{stats.marketsProvidedLiquidity}</p>
</div>
<div>
<h3>Total Fees Earned</h3>
<p>{formatBNB(stats.totalFeesEarned)} BNB</p>
</div>
<div>
<h3>Pending Fees</h3>
<p>{formatBNB(stats.pendingFees)} BNB</p>
</div>
</div>
{/* Individual Positions */}
<h3>Your LP Positions</h3>
{positions.map((position) => (
<div key={position.marketAddress} className="lp-card">
<h4>{position.question}</h4>
<p>LP Tokens: {formatBNB(position.lpTokens, 4)}</p>
<p>Share of Pool: {(position.shareOfPool / 100).toFixed(2)}%</p>
<p>Pool TVL: {formatBNB(position.poolTVL)} BNB</p>
<p>Pending Fees: {formatBNB(position.pendingFees, 4)} BNB</p>
{position.resolved && (
<span>Resolved: {position.outcome ? 'YES' : 'NO'}</span>
)}
</div>
))}
</div>
);
}This hook requires The Graph subgraph to be deployed. Automatically tracks all LP positions without manual localStorage management.
Hook for fetching recent betting activity on a market with live updates.
function useMarketActivity(marketAddress: string): {
activity: MarketActivity[];
loading: boolean;
error: Error | null;
}Hook for fetching Chainlink oracle data for oracle-based markets. Shows current price, threshold, and expected resolution outcome.
function useOracleInfo(
marketAddress: string,
oracleAdapter: string | undefined | null
): {
oracleInfo: OracleInfo | null;
loading: boolean;
error: Error | null;
}
interface OracleInfo {
feedName: string; // e.g., "BTC/USD"
feedAddress: string; // Chainlink feed address
threshold: number; // Price threshold
operator: ComparisonOperator; // GTE, LTE, GT, LT, EQ
operatorSymbol: string; // e.g., "≥", "<"
currentPrice: number; // Current Chainlink price
lastUpdated: Date; // When price was last updated
expectedOutcome: boolean; // YES if condition met, NO otherwise
isConfigured: boolean; // Whether oracle is configured
}import { useOracleInfo, useMarket } from '@predictkit/react';
function OracleMarketInfo({ marketAddress }) {
const { marketInfo } = useMarket(marketAddress);
const { oracleInfo, loading } = useOracleInfo(
marketAddress,
marketInfo?.oracleAdapter
);
if (loading || !oracleInfo) return null;
return (
<div>
<h3>Oracle Market</h3>
<p>Feed: {oracleInfo.feedName}</p>
<p>Current Price: ${oracleInfo.currentPrice.toLocaleString()}</p>
<p>Threshold: ${oracleInfo.threshold.toLocaleString()}</p>
<p>
Condition: Price {oracleInfo.operatorSymbol}
${oracleInfo.threshold.toLocaleString()}
</p>
<p>
Expected Outcome: {oracleInfo.expectedOutcome ? 'YES' : 'NO'}
</p>
</div>
);
}Access the PredictKit SDK client from context.
function usePredictKit(): {
client: PredictKitClient | null;
isReady: boolean;
}import { usePredictKit } from '@predictkit/react';
function CustomComponent() {
const { client, isReady } = usePredictKit();
if (!isReady) return <div>Connecting...</div>;
// Use client directly for advanced operations
const handleCustomOperation = async () => {
const marketClient = client.getMarketClient('0x...');
const info = await marketClient.getMarketInfo();
console.log(info);
};
return <button onClick={handleCustomOperation}>Get Info</button>;
}Direct SDK methods for non-React applications or advanced use cases.
Create a new prediction market.
async function createMarket(
params: CreateMarketParams
): Promise<{
marketAddress: string;
poolAddress?: string;
txHash: string;
}>
interface CreateMarketParams {
question: string;
resolutionTimestamp: number;
oracleAdapter?: string;
config?: Partial<MarketConfig>;
enableLiquidityPool?: boolean;
}import { PredictKitClient, parseBNB } from '@predictkit/sdk';
const client = new PredictKitClient({
provider,
signer,
chainId: 97,
factoryAddress: '0x...'
});
const result = await client.createMarket({
question: 'Will BNB reach $700 by EOY?',
resolutionTimestamp: Math.floor(Date.now() / 1000) + 86400,
config: {
minBet: parseBNB('0.01'),
maxBetBps: 1000,
feeRateBps: 100
},
enableLiquidityPool: true
});
console.log('Market created:', result.marketAddress);Unlike other prediction market platforms that lock you into one architecture, PredictKit lets YOU choose via enableLiquidityPool:
enableLiquidityPool: false
Best for: Friend bets, community polls, low-volume markets
enableLiquidityPool: true
Best for: High-volume markets, professional platforms
// P2P Mode - Simple friend bet
const p2pMarket = await client.createMarket({
question: "Will it rain tomorrow?",
resolutionTimestamp: Date.now() / 1000 + 86400,
enableLiquidityPool: false // No LP complexity
});
// AMM Mode - Professional high-volume market
const ammMarket = await client.createMarket({
question: "Presidential election outcome?",
resolutionTimestamp: Date.now() / 1000 + 2592000,
enableLiquidityPool: true // Always-on liquidity
});Place a bet on a prediction market.
async function placeBet(
marketAddress: string,
side: boolean,
amount: bigint
): Promise<string>import { parseBNB } from '@predictkit/sdk';
const marketClient = client.getMarketClient('0x...');
// Bet YES with 0.1 BNB
const txHash = await marketClient.placeBet(
true, // true = YES, false = NO
parseBNB('0.1')
);
console.log('Bet placed:', txHash);Resolve a market when the outcome is known.
async function resolveMarket(
marketAddress: string,
outcome: boolean
): Promise<string>const marketClient = client.getMarketClient('0x...');
// Resolve as YES (true) or NO (false)
const txHash = await marketClient.resolveMarket(true);
console.log('Market resolved:', txHash);Only the market creator can resolve the market. Resolution must occur after the resolution timestamp.