Code Examples
Practical code examples for working with Prophyt protocol.
Creating a Market
Using Sui CLI
BASHsui client call \ --package <PACKAGE_ID> \ --module prediction_market \ --function create_market \ --type-args "0x2::sui::SUI" \ --args <state_object_id> \ "Will Bitcoin reach $100k by 2025?" \ "Prediction market for Bitcoin price target" \ 86400 \ <clock_object_id> \ --gas-budget 10000000
Using TypeScript SDK
TYPESCRIPTimport { TransactionBlock } from '@mysten/sui/transactions'; import { SuiClient } from '@mysten/sui/client'; async function createMarket( client: SuiClient, signer: Signer, question: string, description: string, duration: number ) { const txb = new TransactionBlock(); txb.moveCall({ target: `${PACKAGE_ID}::prediction_market::create_market`, typeArguments: ['0x2::sui::SUI'], arguments: [ txb.object(STATE_OBJECT_ID), txb.pure(question), txb.pure(description), txb.pure(duration), txb.object(CLOCK_OBJECT_ID), ], }); const result = await signer.signAndExecuteTransactionBlock({ transactionBlock: txb, client, }); return result; } // Usage const result = await createMarket( client, signer, "Will SUI price reach $5 by end of 2024?", "SUI price prediction market", 86400 * 30 // 30 days );
Placing a Bet
TypeScript Example
TYPESCRIPTasync function placeBet( client: SuiClient, signer: Signer, marketId: number, position: boolean, amount: number ) { const txb = new TransactionBlock(); // Split coin for bet const [betCoin] = txb.splitCoins(txb.gas, [amount]); // Generate NFT image (call indexer API) const imageResponse = await fetch('http://localhost:8000/api/bets/generate-bet-image', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ marketId: marketId.toString(), betId: '0', // Will be set by contract position, amount: amount.toString(), }), }); const { imageUrl, blobId, blobAddress } = await imageResponse.json(); txb.moveCall({ target: `${PACKAGE_ID}::prediction_market::place_bet`, typeArguments: ['0x2::sui::SUI'], arguments: [ txb.object(STATE_OBJECT_ID), txb.object(REGISTRY_OBJECT_ID), txb.object(SUILEND_STATE_ID), txb.object(HAEDAL_STATE_ID), txb.object(VOLO_STATE_ID), txb.pure(marketId), txb.pure(position), betCoin, txb.pure(blobAddress), txb.pure(imageUrl), txb.pure(blobId), txb.object(CLOCK_OBJECT_ID), ], }); return await signer.signAndExecuteTransactionBlock({ transactionBlock: txb, client, }); } // Usage await placeBet(client, signer, 1, true, 1000000000); // Bet 1 SUI on Yes
Resolving a Market
Standard Resolution
TYPESCRIPTasync function resolveMarket( client: SuiClient, signer: Signer, marketId: number, outcome: boolean ) { const txb = new TransactionBlock(); txb.moveCall({ target: `${PACKAGE_ID}::prediction_market::resolve_market`, typeArguments: ['0x2::sui::SUI'], arguments: [ txb.object(STATE_OBJECT_ID), txb.object(OWNER_CAP_ID), txb.object(REGISTRY_OBJECT_ID), txb.object(SUILEND_STATE_ID), txb.object(HAEDAL_STATE_ID), txb.object(VOLO_STATE_ID), txb.pure(marketId), txb.pure(outcome), txb.object(CLOCK_OBJECT_ID), ], }); return await signer.signAndExecuteTransactionBlock({ transactionBlock: txb, client, }); }
Nautilus Resolution
TYPESCRIPTasync function resolveMarketWithNautilus( client: SuiClient, signer: Signer, marketId: number ) { // Get market data const marketResponse = await fetch(`http://localhost:8000/api/markets/${marketId}`); const market = await marketResponse.json(); // Request resolution from Nautilus const resolutionResponse = await fetch('http://localhost:8080/resolve', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ market_id: marketId, market_question: market.data.question, market_end_time: Math.floor(new Date(market.data.endDate).getTime() / 1000), data_source_url: market.data.externalLink, image_url: market.data.imageUrl, }), }); const resolution = await resolutionResponse.json(); // Submit to blockchain const txb = new TransactionBlock(); txb.moveCall({ target: `${PACKAGE_ID}::prediction_market::resolve_market_with_nautilus`, typeArguments: ['0x2::sui::SUI'], arguments: [ txb.object(STATE_OBJECT_ID), txb.object(NAUTILUS_REGISTRY_ID), txb.object(REGISTRY_OBJECT_ID), txb.object(SUILEND_STATE_ID), txb.object(HAEDAL_STATE_ID), txb.object(VOLO_STATE_ID), txb.pure(marketId), txb.pure(resolution.outcome), txb.pure(resolution.source_data), txb.pure(Array.from(Buffer.from(resolution.source_data_hash, 'hex'))), txb.pure(resolution.resolution_timestamp), txb.pure(Array.from(Buffer.from(resolution.media_hash, 'hex'))), txb.pure(Array.from(Buffer.from(resolution.signature, 'hex'))), txb.pure(Array.from(Buffer.from(resolution.public_key, 'hex'))), txb.object(CLOCK_OBJECT_ID), ], }); return await signer.signAndExecuteTransactionBlock({ transactionBlock: txb, client, }); }
Claiming Winnings
TYPESCRIPTasync function claimWinnings( client: SuiClient, signer: Signer, marketId: number, betIndex: number ) { // Generate winning NFT image const imageResponse = await fetch('http://localhost:8000/api/bets/generate-winning-image', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ marketId: marketId.toString(), betId: betIndex.toString(), }), }); const { imageUrl, blobId, blobAddress } = await imageResponse.json(); const txb = new TransactionBlock(); txb.moveCall({ target: `${PACKAGE_ID}::prediction_market::claim_winnings`, typeArguments: ['0x2::sui::SUI'], arguments: [ txb.object(STATE_OBJECT_ID), txb.object(REGISTRY_OBJECT_ID), txb.object(SUILEND_STATE_ID), txb.object(HAEDAL_STATE_ID), txb.object(VOLO_STATE_ID), txb.pure(marketId), txb.pure(betIndex), txb.pure(blobAddress), txb.pure(imageUrl), txb.pure(blobId), txb.object(CLOCK_OBJECT_ID), ], }); return await signer.signAndExecuteTransactionBlock({ transactionBlock: txb, client, }); }
Querying Market Data
Using Indexer API
TYPESCRIPT// Get all active markets async function getActiveMarkets() { const response = await fetch('http://localhost:8000/api/markets?status=active&limit=20'); return await response.json(); } // Get market details async function getMarketDetails(marketId: string) { const response = await fetch(`http://localhost:8000/api/markets/${marketId}`); return await response.json(); } // Get user bets async function getUserBets(userAddress: string) { const response = await fetch(`http://localhost:8000/api/bets/user/${userAddress}`); return await response.json(); } // Get market odds async function getMarketOdds(marketId: string) { const market = await getMarketDetails(marketId); const totalYes = BigInt(market.data.totalYesAmount); const totalNo = BigInt(market.data.totalNoAmount); const total = totalYes + totalNo; if (total === 0n) { return { yes: 50, no: 50 }; } const yesOdds = Number((totalYes * 100n) / total); const noOdds = Number((totalNo * 100n) / total); return { yes: yesOdds, no: noOdds }; }
React Hook Example
TYPESCRIPTimport { useState, useEffect } from 'react'; function useMarkets(filters?: { status?: string; limit?: number }) { const [markets, setMarkets] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchMarkets() { try { setLoading(true); const params = new URLSearchParams(); if (filters?.status) params.append('status', filters.status); if (filters?.limit) params.append('limit', filters.limit.toString()); const response = await fetch(`http://localhost:8000/api/markets?${params}`); const data = await response.json(); setMarkets(data.data.markets); } catch (err) { setError(err); } finally { setLoading(false); } } fetchMarkets(); const interval = setInterval(fetchMarkets, 5000); // Poll every 5 seconds return () => clearInterval(interval); }, [filters]); return { markets, loading, error }; } // Usage in component function MarketsList() { const { markets, loading, error } = useMarkets({ status: 'active', limit: 20 }); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> {markets.map(market => ( <div key={market.marketId}> <h3>{market.question}</h3> <p>Yes: {market.totalYesAmount} | No: {market.totalNoAmount}</p> </div> ))} </div> ); }
Event Monitoring
Subscribe to Events
TYPESCRIPTimport { SuiClient, getFullnodeUrl } from '@mysten/sui/client'; const client = new SuiClient({ url: getFullnodeUrl('mainnet'), }); async function subscribeToMarketEvents() { const subscription = await client.subscribeEvent({ filter: { Package: PACKAGE_ID, }, onMessage: (event) => { if (event.type.includes('MarketCreated')) { console.log('New market created:', event); // Handle market creation } else if (event.type.includes('BetPlaced')) { console.log('Bet placed:', event); // Handle bet placement } else if (event.type.includes('MarketResolved')) { console.log('Market resolved:', event); // Handle market resolution } }, }); return subscription; }
Complete Market Lifecycle Example
TYPESCRIPTasync function completeMarketLifecycle() { // 1. Create market const createResult = await createMarket( client, signer, "Will SUI reach $5?", "SUI price prediction", 86400 * 7 // 7 days ); console.log('Market created:', createResult); // 2. Place bets await placeBet(client, signer, 1, true, 1000000000); // Bet 1 SUI on Yes await placeBet(client, signer, 1, false, 500000000); // Bet 0.5 SUI on No // 3. Wait for market to expire... // 4. Resolve market await resolveMarket(client, signer, 1, true); // Resolve as Yes // 5. Claim winnings await claimWinnings(client, signer, 1, 0); // Claim first bet }
Error Handling
TYPESCRIPTasync function safePlaceBet( client: SuiClient, signer: Signer, marketId: number, position: boolean, amount: number ) { try { // Check if market exists and is active const market = await getMarketDetails(marketId.toString()); if (!market.data || market.data.status !== 'active') { throw new Error('Market not found or not active'); } // Check if market has ended const endTime = new Date(market.data.endDate).getTime(); if (Date.now() >= endTime) { throw new Error('Market has already ended'); } // Place bet const result = await placeBet(client, signer, marketId, position, amount); return result; } catch (error) { console.error('Error placing bet:', error); throw error; } }
Testing
Unit Test Example
TYPESCRIPTimport { describe, it, expect } from 'vitest'; describe('Market Operations', () => { it('should create a market', async () => { const result = await createMarket( client, signer, "Test market", "Test description", 86400 ); expect(result.digest).toBeDefined(); }); it('should place a bet', async () => { const result = await placeBet(client, signer, 1, true, 1000000000); expect(result.digest).toBeDefined(); }); });
