@project-gamma/react-sdk
v1.0.0
Published
React SDK for Project Gamma - Decentralized Prediction Markets
Maintainers
Readme
Project Gamma React SDK
A simple, React-focused SDK for integrating Project Gamma's decentralized prediction markets into React applications.
Features
- ✅ Market Discovery: Search, filter, and query markets
- ✅ Trading: Buy/sell outcome tokens with slippage protection
- ✅ Liquidity: Add/remove liquidity, track LP positions
- ✅ Resolution: Propose, dispute, and finalize market outcomes
- ✅ Oracle Integration: Request AI resolution via public API
- ✅ Wallet Integration: Built on Wagmi for wallet connections
- ✅ Real-Time Updates: Automatic data refetching and caching
- ✅ TypeScript: Full TypeScript support with comprehensive types
Installation
npm install @project-gamma/react-sdk
# or
yarn add @project-gamma/react-sdk
# or
pnpm add @project-gamma/react-sdkPeer Dependencies
The SDK requires the following peer dependencies:
npm install react react-dom wagmi viem @tanstack/react-queryQuick Start
1. Setup Wagmi Provider
The SDK integrates with Wagmi for wallet management. Set up your Wagmi configuration:
import { WagmiProvider, createConfig, http } from 'wagmi';
import { bsc, bscTestnet } from 'wagmi/chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const config = createConfig({
chains: [bsc, bscTestnet],
transports: {
[bsc.id]: http(),
[bscTestnet.id]: http(),
},
});
const queryClient = new QueryClient();
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
{/* Your app */}
</QueryClientProvider>
</WagmiProvider>
);
}2. Setup SDK Provider
Wrap your app with the SDK provider:
import { GammaProvider } from '@project-gamma/react-sdk';
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<GammaProvider
chainId={56}
oracleApiUrl="https://api.projectgamma.io"
pinataJwt="your-pinata-jwt-token" // Optional: for IPFS storage
>
{/* Your app */}
</GammaProvider>
</QueryClientProvider>
</WagmiProvider>
);
}Note: The pinataJwt prop is optional. If provided, it will be used for IPFS uploads when creating markets. You can also configure it via environment variables or pass it directly to hooks.
3. Use SDK Hooks
import { useMarkets, useBuy, usePrices } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
function MarketList() {
const { data: markets, isLoading } = useMarkets({
status: MarketStatus.Active,
});
if (isLoading) return <div>Loading...</div>;
return (
<div>
{markets?.map(market => (
<MarketCard key={market.id} market={market} />
))}
</div>
);
}
function MarketCard({ market }: { market: Market }) {
const { data: prices } = usePrices(market.id);
const { write: buyYes, isLoading } = useBuy(market.id);
const handleBuy = () => {
buyYes({
outcomeId: 0, // YES
amount: parseUnits('100', 6), // 100 USDC
slippage: 0.5, // 0.5%
});
};
return (
<div>
<h3>{market.category}</h3>
{prices && (
<div>
YES: {(Number(prices.yesPrice) / 1e18 * 100).toFixed(1)}¢
NO: {(Number(prices.noPrice) / 1e18 * 100).toFixed(1)}¢
</div>
)}
<button onClick={handleBuy} disabled={isLoading}>
{isLoading ? 'Buying...' : 'Buy YES'}
</button>
</div>
);
}API Reference
React Hooks
Market Hooks
useMarkets(filters?)
Fetch markets with optional filters.
import { useMarkets, MarketStatus } from '@project-gamma/react-sdk';
const { data: markets, isLoading } = useMarkets({
category: 'sports',
status: MarketStatus.Active,
limit: 10,
});useMarket(marketId)
Fetch single market information.
import { useMarket } from '@project-gamma/react-sdk';
const { data: market, isLoading } = useMarket(1);useCreateMarket()
Create a new prediction market.
import { useCreateMarket } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: createMarket, isLoading } = useCreateMarket();
createMarket({
collateralToken: '0x...', // USDC address
category: 'sports',
metadataURI: 'ipfs://...',
closeTime: BigInt(Math.floor(Date.now() / 1000) + 86400 * 30),
creatorStake: parseUnits('1000', 18), // 1000 HORIZON tokens
});Trading Hooks
useBuy(marketId)
Buy outcome tokens.
import { useBuy } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: buyYes, isLoading, hash } = useBuy(1);
buyYes({
outcomeId: 0, // 0 = YES, 1 = NO
amount: parseUnits('100', 6), // 100 USDC
slippage: 0.5, // 0.5% slippage tolerance
});useSell(marketId)
Sell outcome tokens.
import { useSell } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: sellYes, isLoading } = useSell(1);
sellYes({
outcomeId: 0,
amount: parseUnits('50', 18), // 50 YES tokens
slippage: 0.5,
});useQuote(marketId, params)
Get price quote for a trade.
import { useQuote } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { data: quote } = useQuote({
marketId: 1,
outcomeId: 0,
amount: parseUnits('100', 6),
isBuy: true,
});
// quote: { tokensOut, fee, priceImpact }usePrices(marketId)
Get current market prices.
import { usePrices } from '@project-gamma/react-sdk';
const { data: prices } = usePrices(1);
// prices: { yesPrice, noPrice }Liquidity Hooks
useAddLiquidity(marketId)
Add liquidity to a market.
import { useAddLiquidity } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: addLiquidity, isLoading } = useAddLiquidity(1);
addLiquidity({
amount: parseUnits('1000', 6), // 1000 USDC
});useRemoveLiquidity(marketId)
Remove liquidity from a market.
import { useRemoveLiquidity } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: removeLiquidity, isLoading } = useRemoveLiquidity(1);
removeLiquidity({
lpTokens: parseUnits('100', 18),
});useLPPosition(marketId)
Get LP position for a market.
import { useLPPosition } from '@project-gamma/react-sdk';
const { data: position } = useLPPosition(1);
// position: { lpTokens, value, yesReserve, noReserve }Resolution Hooks
useResolution(marketId)
Get resolution state for a market.
import { useResolution } from '@project-gamma/react-sdk';
const { data: resolution } = useResolution(1);useProposeResolution(marketId)
Propose a resolution for a market.
import { useProposeResolution } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: proposeResolution, isLoading } = useProposeResolution(1);
proposeResolution({
outcomeId: 0, // YES
evidenceHash: '0x...',
signature: '0x...',
bondAmount: parseUnits('1000', 18),
evidenceURIs: ['ipfs://...'],
});useDispute(marketId)
Dispute a proposed resolution.
import { useDispute } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: dispute, isLoading } = useDispute(1);
dispute({
bondAmount: parseUnits('1000', 18),
});useFinalize(marketId)
Finalize a resolution after dispute window.
import { useFinalize } from '@project-gamma/react-sdk';
const { write: finalize, isLoading } = useFinalize(1);
finalize();Oracle Hooks
useRequestResolution()
Request AI resolution via API.
import { useRequestResolution } from '@project-gamma/react-sdk';
const { mutate: requestResolution, data: request } = useRequestResolution();
requestResolution({
marketId: 1,
metadata: {
question: 'Market question',
description: 'Details',
},
});useOracleStatus(requestId, options?)
Poll oracle request status.
import { useOracleStatus } from '@project-gamma/react-sdk';
const { data: status } = useOracleStatus(request?.requestId, {
refetchInterval: 5000, // Poll every 5 seconds
});useOracleResult(requestId, options?)
Get completed oracle result.
import { useOracleResult } from '@project-gamma/react-sdk';
const { data: result } = useOracleResult(request?.requestId, {
enabled: status?.status === 'completed',
});
// result: { outcomeId, confidence, reasoning, sources, evidenceUrl }useOracleHistory(marketId)
Get oracle request history for a market.
import { useOracleHistory } from '@project-gamma/react-sdk';
const { data: history } = useOracleHistory(1);Token Hooks
useBalance(tokenAddress, userAddress?)
Get ERC20 token balance.
import { useBalance } from '@project-gamma/react-sdk';
const { data: balance } = useBalance('0x...'); // USDC addressuseOutcomeBalance(marketId, outcomeId, userAddress?)
Get outcome token balance.
import { useOutcomeBalance } from '@project-gamma/react-sdk';
const { data: yesBalance } = useOutcomeBalance(1, 0); // YES tokens
const { data: noBalance } = useOutcomeBalance(1, 1); // NO tokensuseApprove(tokenAddress)
Approve token spending.
import { useApprove } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const { write: approve, isLoading } = useApprove('0x...'); // USDC address
approve({
spender: '0x...', // Market AMM address
amount: parseUnits('10000', 6), // Approve 10000 USDC
});useRedeem(marketId)
Redeem winning outcome tokens.
import { useRedeem } from '@project-gamma/react-sdk';
const { write: redeem, isLoading } = useRedeem(1);
redeem();Configuration
GammaProvider Props
<GammaProvider
chainId={56} // Required: BNB Chain Mainnet (56) or Testnet (97)
oracleApiUrl="https://api.projectgamma.io" // Optional: Oracle API URL
marketFactoryAddress="0x..." // Optional: Override default address
horizonTokenAddress="0x..." // Optional: Override default address
outcomeTokenAddress="0x..." // Optional: Override default address
// ... other contract addresses
>Default Contract Addresses (BNB Chain Mainnet)
- MarketFactory:
0x22Cc806047BB825aa26b766Af737E92B1866E8A6 - HorizonToken:
0x5b2bA38272125bd1dcDE41f1a88d98C2F5c14444 - OutcomeToken:
0x17B322784265c105a94e4c3d00aF1E5f46a5F311 - HorizonPerks:
0x71Ff73A5a43B479a2D549a34dE7d3eadB9A1E22C - FeeSplitter:
0x275017E98adF33051BbF477fe1DD197F681d4eF1 - ResolutionModule:
0xF0CF4C741910cB48AC596F620a0AE892Cd247838 - AIOracleAdapter:
0x8773B8C5a55390DAbAD33dB46a13cd59Fb05cF93
Utility Functions
Token Amount Formatting
import { formatTokenAmount, parseTokenAmount } from '@project-gamma/react-sdk';
// Format bigint to readable string
const formatted = formatTokenAmount(1000000000000000000n, 18, 4); // "1.0000"
// Parse string to bigint
const parsed = parseTokenAmount('1.5', 18); // 1500000000000000000nPrice Calculations
import { calculatePrice, calculateMarketPrices } from '@project-gamma/react-sdk';
// Calculate price for an outcome
const price = calculatePrice(yesLiquidity, noLiquidity);
// Calculate prices for both outcomes
const prices = calculateMarketPrices(yesLiquidity, noLiquidity);Address Utilities
import { isValidAddress, shortenAddress } from '@project-gamma/react-sdk';
const isValid = isValidAddress('0x...');
const shortened = shortenAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'); // "0x742d...bEb"Token ID Utilities
import { getOutcomeTokenId, getMarketIdFromTokenId, getOutcomeFromTokenId } from '@project-gamma/react-sdk';
const tokenId = getOutcomeTokenId(1n, 0); // Market 1, YES outcome (0)
const marketId = getMarketIdFromTokenId(tokenId); // 1n
const outcome = getOutcomeFromTokenId(tokenId); // 0 (YES)Error Handling
The SDK uses custom error classes for better error handling:
import { SDKError, ContractError, TradeError } from '@project-gamma/react-sdk';
try {
buyYes({ outcomeId: 0, amount: parseUnits('100', 6) });
} catch (error) {
if (error instanceof SDKError) {
console.error('SDK Error:', error.message, error.code);
} else if (error instanceof ContractError) {
console.error('Contract Error:', error.message, error.contractAddress);
} else if (error instanceof TradeError) {
console.error('Trade Error:', error.message, error.marketId);
}
}Complete Example
import { WagmiProvider, createConfig, http } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { bsc } from 'wagmi/chains';
import { GammaProvider, useMarkets, useBuy, usePrices, MarketStatus } from '@project-gamma/react-sdk';
import { parseUnits } from 'viem';
const config = createConfig({
chains: [bsc],
transports: {
[bsc.id]: http(),
},
});
const queryClient = new QueryClient();
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<GammaProvider chainId={56} oracleApiUrl="https://api.projectgamma.io">
<MarketList />
</GammaProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
function MarketList() {
const { data: markets, isLoading } = useMarkets({
status: MarketStatus.Active,
});
if (isLoading) return <div>Loading markets...</div>;
return (
<div>
{markets?.map(market => (
<MarketCard key={market.id} market={market} />
))}
</div>
);
}
function MarketCard({ market }: { market: Market }) {
const { data: prices } = usePrices(market.id);
const { write: buyYes, isLoading } = useBuy(market.id);
const handleBuy = () => {
buyYes({
outcomeId: 0,
amount: parseUnits('100', 6),
slippage: 0.5,
});
};
return (
<div>
<h3>{market.category}</h3>
{prices && (
<div>
YES: {(Number(prices.yesPrice) / 1e18 * 100).toFixed(1)}¢
NO: {(Number(prices.noPrice) / 1e18 * 100).toFixed(1)}¢
</div>
)}
<button onClick={handleBuy} disabled={isLoading}>
{isLoading ? 'Buying...' : 'Buy YES'}
</button>
</div>
);
}TypeScript Support
The SDK is fully typed. All types are exported:
import type {
Market,
MarketOutcome,
MarketStatus,
CreateMarketParams,
TradeQuote,
MarketPrices,
ResolutionProposal,
OracleRequest,
OracleResult,
SDKError,
ContractError,
TradeError,
} from '@project-gamma/react-sdk';Testing
The SDK includes comprehensive tests. Run tests with:
npm testContributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
License
MIT
Support
For support, please open an issue on GitHub or contact the Horizon Oracles team.
Links
- Website: horizonoracles.com
- GitHub: github.com/HorizonOracles/Project_Gamma
- Contract Address:
0x5b2ba38272125bd1dcde41f1a88d98c2f5c14444
