A decentralized prediction market protocol on Solana where users bet on media ratings β movies, shows, or any scored content. Built with the Anchor framework.
Program ID: GomtSs5546sx4NQFsGaJtwFrDytfqRQPBADkiUr7PcyW
Choose based on what you're building:
| Goal | Resource |
|---|---|
| Building a frontend/app | β See TypeScript SDK section |
| Understanding the protocol | β See How It Works section |
| Running Rust program locally | β See Getting Started section |
| Deploying to Solana | β See Deploy (Localnet) section |
| Full SDK API Reference | β See app/contract/README.md |
The easiest way to interact with Cinefi is through the TypeScript SDK. It provides type-safe wrappers for all 8 program instructions, account fetching, validation, and utility functions.
cinefi/
βββ app/
βββ contract/ β TypeScript SDK is here
βββ README.md β Full SDK documentation (1600+ lines)
βββ index.ts β Main entry point
βββ pdas/
βββ constants/
βββ errors/
βββ types/
βββ utils/
βββ accounts/
βββ instructions/
import CinefiSDK, {
createMarketAndSend,
placeBetAndSend,
} from "./app/contract";
import { Connection, Keypair } from "@solana/web3.js";
import { Wallet } from "@coral-xyz/anchor";
// 1. Initialize connection & SDK
const connection = new Connection("https://api.devnet.solana.com");
const wallet = new Wallet(Keypair.generate());
const sdk = new CinefiSDK({ connection, wallet });
// 2. Create a market
const txId = await createMarketAndSend(sdk.program, creator, {
mediaId: 12345n,
radius: 5,
oracleSet: [oracle1, oracle2, oracle3],
oracleThreshold: 2,
});
// 3. Place a bet
const betTxId = await placeBetAndSend(sdk.program, bettor, {
mediaId: 12345n,
bucket: 75,
amount: 1_000_000_000n, // 1 SOL
});For complete API reference, environment setup, oracle management, constraint documentation, error handling, and advanced workflows, see:
π app/contract/README.md (1600+ lines, covers everything)
SDK Features:
- β Type-safe instruction builders (8 instructions)
- β PDA derivation for all account types (5 PDAs)
- β Account fetching & parsing (6 methods)
- β 27 custom error codes with parsing
- β Validation helpers (bucket, amount, threshold)
- β State checking (betting open? can claim? etc)
- β Conversion utilities (SOL β lamports, time multipliers)
- β Time management (decay multipliers, deadline checks)
- β Complete constraint documentation
// ESM import
import CinefiSDK from "./app/contract";
// or CommonJS
const CinefiSDK = require("./app/contract");
// Environment setup required
import dotenv from "dotenv";
dotenv.config();
// Load keys from .env
const walletSecret = process.env.WALLET_SECRET_KEY;
const oracle1Secret = process.env.ORACLE_1_SECRET_KEY;
const oracle2Secret = process.env.ORACLE_2_SECRET_KEY;
const oracle3Secret = process.env.ORACLE_3_SECRET_KEY;See app/contract/README.md - Environment Configuration for complete setup.
- Cinefi
- π Quick Navigation
- TypeScript SDK
- Table of Contents
- Program Overview
- How It Works
- Program Instructions
- Account Architecture
- PDA Seeds
- Protocol Constants
- Error Codes
- Project Structure
- Getting Started (Development)
- Development: Program vs SDK
- Common Tasks
- π Comprehensive Resource Guide
- π Getting Started Paths
- π‘ Architecture Summary
- π Support & References
- π Checklist for Building an App
- π― Next Steps
- π License
Cinefi lets anyone create a prediction market tied to a media item (identified by media_id). Participants bet SOL on a score bucket between 1 and 100 β representing their prediction of the media's final score. A set of trusted oracles submits the real-world score after the betting window closes. Winners β those who bet within a configurable radius of the final score β share the prize pool, weighted by how close their bucket is to the outcome and how early they placed their bet.
Key Features:
- Decentralized Oracle System - 3 oracles provide consensus on outcomes
- Time-Weighted Betting - Early bettors earn higher multipliers (1.0x β 0.272x over 14 days)
- Proximity-Based Rewards - Winners closer to the actual outcome earn more
- Automatic Fee Distribution - 3% protocol fee, customizable creator fees
- Fallback Mechanism - If no one is in the winning radius, the closest bucket wins
- Permissionless - Anyone can create markets, place bets, or claim rewards
Each market moves through a strict sequential set of phases enforced on-chain:
[Create] β [Betting Open] β [Betting Closed] β [Oracle Window] β [Resolved] β [Claims] β [Reclaimed]
| Phase | Duration | Description |
|---|---|---|
| Betting Open | 14 days | Users place SOL bets on any bucket 1β100 |
| Betting Closed | Day 15β21 | No new bets; oracle window approaches |
| Oracle Window | 1 hour (at Day 21) | Oracles submit their score observations |
| Resolution | After Day 21 | Anyone can trigger resolve_market once oracles finalize |
| Claim Window | 14 days after resolution | Winners claim their SOL payout |
| Reclaim | After claim deadline | Unclaimed SOL is swept to the treasury |
A market is created with an optional betting_starts_after delay, allowing the creator to schedule when betting opens.
The score space is divided into 100 buckets (1β100). Each bucket represents a predicted score. For example:
- Bucket
74= predicting a score of 74/100 - Bucket
50= predicting a score of 50/100
The radius parameter (set at market creation) defines the winning range. If radius = 5 and the final score is 72, then any bettor in buckets 67β77 is a winner.
To incentivize early participation, bets are multiplied by a time-decay multiplier based on when the bet is placed relative to the betting start date.
| Day | Multiplier (Γ1000 scale) | Effective Weight |
|---|---|---|
| 1 | 1000 | 1.000Γ |
| 2 | 904 | 0.904Γ |
| 3 | 818 | 0.818Γ |
| 4 | 740 | 0.740Γ |
| 5 | 670 | 0.670Γ |
| 6 | 606 | 0.606Γ |
| 7 | 548 | 0.548Γ |
| 8 | 496 | 0.496Γ |
| 9 | 449 | 0.449Γ |
| 10 | 406 | 0.406Γ |
| 11 | 367 | 0.367Γ |
| 12 | 332 | 0.332Γ |
| 13 | 301 | 0.301Γ |
| 14 | 272 | 0.272Γ |
The weighted amount is stored alongside the raw amount in both the Market pool arrays and the user's UserPosition. Prize payouts are computed using weighted amounts, so early bettors earn a proportionally larger share.
Formula:
weighted_amount = (amount Γ TIME_MULTIPLIERS[day_index]) / 1000
Each market is created with an oracle_set of exactly 3 oracle public keys and an oracle_threshold (must be 2 or 3).
During the oracle window (starting at the settlement timestamp, open for 1 hour):
- Each oracle in the set calls
submit_scorewith their observed score (1β100). - After
oracle_thresholdsubmissions agree on a score, the report is marked finalized. - If oracles disagree and cannot reach consensus, the report is marked disputed β blocking resolution.
The OracleReport PDA tracks all submissions and their agreement state.
When resolve_market is called:
- A protocol fee of 3% (300 bps) is transferred from the vault to the treasury.
- The remaining
total_prize_poolis distributed across winning buckets using a two-factor weighting:
Factor 1 β Time Weight: Each individual bettor's share within their bucket is proportional to their weighted_amount (early bettors get more).
Factor 2 β Closeness Weight: Each winning bucket's share of the total prize pool is proportional to a closeness function:
closeness_weight(bucket) = 1_000_000 / (|bucket - final_outcome| + 1)
This means a bucket exactly at the final score gets the maximum share, buckets one step away get half, and so on.
Total Bucket Weight (TBW):
TBW = Ξ£ (closeness_weight(bucket_i) Γ weighted_pool[bucket_i]) for all winning buckets i
Bucket Prize:
bucket_prize[i] = (closeness_weight(bucket_i) Γ weighted_pool[i] Γ total_prize_pool) / TBW
Individual Payout:
user_payout = (user.weighted_amount / weighted_pool[user.bucket]) Γ bucket_prize[user.bucket]
If no bets exist within the radius of the final score, the protocol falls back gracefully:
- The winner(s) are determined by finding the minimum distance from the final score across all filled buckets.
- All bettors at that minimum distance are treated as winners.
- The normal closeness-weighted distribution then applies among those fallback buckets.
This guarantees the prize pool is always distributed rather than locked.
Creates the global treasury PDA. This must be called once before any markets can be resolved.
Accounts:
| Account | Type | Description |
|---|---|---|
authority |
Signer (mut) | Pays for account initialization |
treasury |
UncheckedAccount (init) | Global treasury PDA |
system_program |
Program | Solana System Program |
Creates a new prediction market for a media item.
Parameters:
| Parameter | Type | Description |
|---|---|---|
betting_starts_after |
Option<i64> |
Seconds delay before betting opens (default: 0) |
media_id |
u64 |
Unique identifier for the media title |
radius |
u8 |
Number of buckets on each side of the outcome considered a win |
oracle_set |
[Pubkey; 3] |
Exactly 3 oracle public keys |
oracle_threshold |
u8 |
Minimum agreeing oracles to finalize (must be 2 or 3) |
Accounts:
| Account | Type | Description |
|---|---|---|
creator |
Signer (mut) | Market creator; pays for all PDAs |
market |
Account (init) | Market state PDA |
vault |
UncheckedAccount (init) | Lamport vault for this market |
oracle_report |
Account (init) | Oracle submission tracking PDA |
system_program |
Program | Solana System Program |
Timeline set at creation:
betting_starts_at = now + betting_starts_after
betting_closes_at = betting_starts_at + 14 days
settle_at = betting_closes_at + 7 days (day 21 total)
claim_deadline = settle_at + 14 days
Places a SOL bet on a score bucket for a given market.
Parameters:
| Parameter | Type | Description |
|---|---|---|
bucket |
u8 |
Score prediction (1β100) |
amount |
u64 |
Lamports to stake (must be > 0) |
Accounts:
| Account | Type | Description |
|---|---|---|
user |
Signer (mut) | Bettor; lamports source |
market |
Account (mut) | Target market |
user_position |
Account (init_if_needed) | Tracks user's bet for this bucket |
vault |
UncheckedAccount (mut) | Receives the lamports |
system_program |
Program | Solana System Program |
Behavior:
- Transfers
amountlamports from user to the market vault. - Calculates
weighted_amountusing the time-decay multiplier for the current day. - Updates
market.pool[bucket],market.weighted_pool[bucket], andmarket.total_pool. - Initializes or updates the user's
UserPositionfor this market+bucket combination. - A user can add to an existing position in the same bucket by calling
place_betagain; theiramountandweighted_amountaccumulate.
Closes the betting window. Can be called by anyone once betting_closes_at has passed.
Accounts:
| Account | Type | Description |
|---|---|---|
caller |
Signer (mut) | Any signer (permissionless) |
market |
Account (mut) | Target market |
Called by each oracle to submit their observed score. Only executable during the 1-hour oracle window starting at settle_at.
Parameters:
| Parameter | Type | Description |
|---|---|---|
score |
u8 |
Oracle's observed score (1β100) |
Accounts:
| Account | Type | Description |
|---|---|---|
oracle_signer |
Signer (mut) | Must be in the market's oracle_set |
market |
Account | Target market |
oracle_report |
Account (mut) | Oracle submission state |
system_program |
Program | Solana System Program |
Finalization logic:
- If
oracle_thresholdoracles agree on the same score βfinalized = true,agreed_scoreis set. - If all 3 oracles submit but no threshold of agreement is reached β
disputed = true. - A disputed report blocks market resolution.
Resolves the market using the finalized oracle score, distributes the protocol fee, and computes per-bucket prize amounts. Can be called by anyone once settle_at has passed and the oracle report is finalized.
Accounts:
| Account | Type | Description |
|---|---|---|
caller |
Signer (mut) | Any signer (permissionless) |
market |
Account (mut) | Target market |
oracle_report |
Account | Must be finalized and not disputed |
vault |
UncheckedAccount (mut) | Market lamport vault |
treasury |
UncheckedAccount (mut) | Protocol treasury (receives 3% fee) |
system_program |
Program | Solana System Program |
Allows a winning user to claim their SOL payout. Must be called before claim_deadline. The UserPosition account is closed and rent is returned to the user on claim.
Accounts:
| Account | Type | Description |
|---|---|---|
user |
Signer (mut) | Winner claiming their reward |
market |
Account | Resolved market |
user_position |
Account (mut, close=user) | User's bet record (closed on success) |
vault |
UncheckedAccount (mut) | Source of payout lamports |
system_program |
Program | Solana System Program |
Validation:
- Market must be resolved.
- Claim deadline must not have passed.
- User's bucket must be within the winning radius (or fallback set).
- Position must not have been previously claimed.
After the claim_deadline has passed, sweeps all remaining lamports in the vault to the treasury. This handles unclaimed winnings and any dust. Can be called by anyone.
Accounts:
| Account | Type | Description |
|---|---|---|
caller |
Signer (mut) | Any signer (permissionless) |
market |
Account (mut) | Must be resolved; claim deadline passed |
vault |
UncheckedAccount (mut) | Remaining market lamports |
treasury |
UncheckedAccount (mut) | Protocol treasury (receives everything) |
system_program |
Program | Solana System Program |
The core on-chain state for a prediction market. Derived from ["market_seed", media_id_le_bytes].
| Field | Type | Description |
|---|---|---|
media_id |
u64 |
Unique ID of the media item |
creator |
Pubkey |
Market creator's public key |
created_at |
i64 |
Unix timestamp of creation |
betting_starts_at |
i64 |
When betting opens |
betting_closes_at |
i64 |
When betting closes (14 days after open) |
settle_at |
i64 |
Oracle settlement time (day 21) |
claim_deadline |
i64 |
Last date to claim rewards |
radius |
u8 |
Winning bucket radius around final score |
protocol_fee_bps |
u16 |
Protocol fee in basis points (300 = 3%) |
creator_fee_bps |
u16 |
Creator fee in basis points (currently 0) |
oracle_set |
[Pubkey; 3] |
Authorized oracle public keys |
oracle_threshold |
u8 |
Minimum agreeing oracles for finalization |
pool |
[u64; 101] |
Raw lamport totals per bucket (index 1β100) |
weighted_pool |
[u64; 101] |
Time-weighted totals per bucket |
total_pool |
u64 |
Total raw lamports staked |
total_prize_pool |
u64 |
Total after protocol fee deduction |
final_outcome |
u8 |
Resolved score (set after resolution) |
bucket_prize |
[u64; 101] |
Lamport prize allocated to each winning bucket |
fallback_used |
bool |
Whether fallback distribution was applied |
resolved |
bool |
Has the market been resolved |
closed |
bool |
Has the betting window been closed |
reclaimed |
bool |
Has unclaimed pool been swept to treasury |
bump |
u8 |
PDA bump seed |
Tracks oracle submissions for a market. Derived from ["oracle_report_seed", market_pubkey].
| Field | Type | Description |
|---|---|---|
market |
Pubkey |
Parent market public key |
submissions |
[(Pubkey, u8); 3] |
Each oracle's (pubkey, submitted_score) |
submission_count |
u8 |
Number of submissions so far |
agreed_score |
u8 |
Consensus score (set when finalized) |
finalized |
bool |
Threshold consensus reached |
disputed |
bool |
Oracles disagree; cannot resolve |
bump |
u8 |
PDA bump seed |
Records a single user's bet in a specific bucket for a market. Derived from ["position_seed", user_pubkey, market_pubkey, bucket_byte].
| Field | Type | Description |
|---|---|---|
user |
Pubkey |
Bettor's public key |
market |
Pubkey |
Market public key |
bucket |
u8 |
The score bucket they bet on |
amount |
u64 |
Total raw lamports staked in this bucket |
weighted_amount |
u64 |
Total time-weighted lamports |
claimed |
bool |
Whether the reward has been claimed |
bump |
u8 |
PDA bump seed |
A user can hold at most one
UserPositionper(market, bucket)pair but may have positions across different buckets or different markets simultaneously.
A lamport-only account that holds all SOL staked in a market. Derived from ["vault_seed", market_pubkey]. The program uses PDA signer seeds to authorize transfers out of the vault.
The global protocol treasury. Derived from ["treasury_seed"]. Receives:
- 3% protocol fee on every market resolution.
- All unclaimed funds after the claim deadline via
reclaim_pool.
| Account | Seeds |
|---|---|
| Market | b"market_seed" || media_id.to_le_bytes() |
| Vault | b"vault_seed" || market.key() |
| OracleReport | b"oracle_report_seed" || market.key() |
| UserPosition | b"position_seed" || user.key() || market.key() || [bucket] |
| Treasury | b"treasury_seed" |
| Constant | Value | Description |
|---|---|---|
BETTING_DURATION_DAYS |
14 days | Length of the betting window |
SETTLEMENT_DAY |
Day 21 | Oracle settlement deadline |
CLAIM_WINDOW_DAYS |
14 days | Time after resolution to claim |
ORACLE_WINDOWS_START_SECONDS |
0 s | Oracle window starts at settle_at |
ORACLE_WINDOWS_CLOSE_SECONDS |
3600 s (1 hr) | Oracle window closes 1 hour after settle_at |
DEFAULT_PROTOCOL_FEE_BPS |
300 (3%) | Protocol fee on total pool |
DEFAULT_RADIUS |
5 | Default winning radius |
MAX_ORACLE_SIGNER |
3 | Maximum oracles per market |
MAX_BUCKETS |
101 | Total bucket slots (index 0 unused; 1β100 valid) |
MULTIPLIER_SCALE |
1000 | Denominator for time multiplier division |
CLOSENESS_SCALE |
1,000,000 | Numerator for closeness weight computation |
| Error | Description |
|---|---|
MarketAlreadyClosed |
Market is already in closed state |
MarketNotClosed |
Market has not been closed yet |
MarketAlreadyResolved |
Market resolution already happened |
MarketNotResolved |
Market has not been resolved yet |
MarketAlreadyClaimed |
Vault has already been reclaimed |
BettingNotStarted |
Betting window has not opened yet |
BettingClosed |
The betting window has ended |
BettingStillOpen |
Betting window is still active |
SettlementNotReady |
settle_at timestamp not yet reached |
SettlementTimeInvalid |
Invalid settlement time provided |
ClaimDeadlinePassed |
The claim deadline has passed |
ClaimDeadlineNotPassed |
Claim deadline has not passed yet (for reclaim) |
OracleWindowClosed |
Oracle submission window is not currently open |
UnauthorizedOracle |
Signer is not in the market's oracle set |
InvalidOracleThreshold |
Threshold must be 2 or 3 |
OracleAlreadyFinalized |
Oracle report is already finalized |
OracleNotFinalized |
Oracle report has not been finalized |
OracleAlreadySubmitted |
This oracle already submitted a score |
OracleDisputed |
Oracles could not reach consensus |
InvalidBucket |
Bucket must be between 1 and 100 |
InvalidAmount |
Bet amount must be greater than zero |
AlreadyClaimed |
User has already claimed their reward |
InsufficientClaimAmount |
Computed payout is zero |
NotAWinner |
User's bucket is outside the winning range |
Unauthorized |
Signer does not own the position account |
MathOverflow |
Arithmetic overflow during calculation |
cinefi/
βββ Anchor.toml # Anchor workspace config
βββ Cargo.toml # Workspace Cargo manifest
βββ package.json # Node.js dependencies (tests + SDK)
βββ tsconfig.json # TypeScript config
βββ README.md # This file
β
βββ app/
β βββ contract/ # π TypeScript SDK (use this for frontend!)
β βββ README.md # Complete SDK documentation (1600+ lines)
β βββ index.ts # SDK entry point
β βββ pdas/index.ts # 5 PDA derivation functions
β βββ constants/index.ts # Protocol constants
β βββ errors/index.ts # 27 error codes + parsing
β βββ types/index.ts # TypeScript type definitions
β βββ utils/index.ts # Validation, conversion, state functions
β βββ accounts/index.ts # Account fetching methods
β βββ instructions/ # 8 instruction modules
β βββ initialize-treasury.ts
β βββ create-market.ts
β βββ place-bet.ts
β βββ close-market.ts
β βββ submit-score.ts
β βββ resolve-market.ts
β βββ claim-reward.ts
β βββ reclaim-pool.ts
β
βββ programs/
β βββ cinefi/ # Rust Anchor program
β βββ Cargo.toml
β βββ src/
β βββ lib.rs # Program entrypoint & instruction dispatch
β βββ errors/
β β βββ mod.rs # All custom error codes (27 total)
β βββ instructions/
β β βββ mod.rs
β β βββ initialize_treasury.rs
β β βββ create_market.rs
β β βββ place_bet.rs
β β βββ close_market.rs
β β βββ submit_score.rs
β β βββ resolve_market.rs
β β βββ claim_reward.rs
β β βββ reclaim_pool.rs
β βββ states/
β β βββ mod.rs
β β βββ constants.rs # Protocol-wide constants & seeds
β β βββ market.rs # Market account definition
β β βββ oracle_report.rs
β β βββ user_position.rs
β βββ utils/
β βββ mod.rs
β βββ validations.rs # is_winner, oracle checks
β βββ weights.rs # Time multipliers, weights, prize computation
β
βββ tests/
β βββ cinefi.ts # Integration tests
β
βββ migrations/
β βββ deploy.ts
β
βββ target/
βββ idl/
β βββ cinefi.json # Generated IDL
βββ types/
βββ cinefi.ts # Generated TypeScript types
If you want to use Cinefi in your app, use the TypeScript SDK instead (much easier!).
If you want to develop or test the Solana program itself, follow these steps:
- Rust with the
solanatoolchain (seerust-toolchain.toml) - Solana CLI
- Anchor CLI
- Node.js v18+ & Yarn or npm
Verify versions:
rustc --version
solana --version
anchor --version
node --versionNote: For SDK usage only (no program development), you only need Node.js.
# Install Rust/Solana dependencies
rustup update solana
cargo build -p cinefi
# Install Node dependencies
yarn install
# or
npm install# Build all programs
anchor build
# Build specific program
cargo build -p cinefi --releaseOutput:
- Binaries:
target/release/cinefi.so - IDL:
target/idl/cinefi.json - TypeScript types:
target/types/cinefi.ts
Start a local validator and run the test suite:
# Start Solana localnet (in one terminal)
solana-test-validator
# In another terminal, run tests
anchor test
# Or run specific test file
anchor test --skip-local-validatorWhat tests cover:
- β All 8 program instructions
- β Market lifecycle (create β bet β resolve β claim)
- β Oracle consensus logic
- β Prize distribution calculations
- β Error cases and validation
- β Edge cases (fallback, overflow, etc)
# Start local validator
solana-test-validator
# Deploy program (in another terminal)
anchor deploy
# Program deployed to:
# GomtSs5546sx4NQFsGaJtwFrDytfqRQPBADkiUr7PcyWBefore creating markets, initialize treasury:
import { Program, AnchorProvider, Wallet } from "@coral-xyz/anchor";
import { Connection, Keypair } from "@solana/web3.js";
const connection = new Connection("http://localhost:8899");
const wallet = new Wallet(Keypair.generate());
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(IDL, PROGRAM_ID, provider);
await program.methods
.initializeTreasury()
.accounts({ authority: wallet.publicKey })
.rpc();Use the TypeScript SDK (in /app/contract/):
// Simple!
import CinefiSDK, { placeBetAndSend } from "./app/contract";
const sdk = new CinefiSDK({ connection, wallet });
await placeBetAndSend(sdk.program, user, {
mediaId: 123n,
bucket: 75,
amount: 1_000_000_000n,
});Why SDK?
- Type-safe (full TypeScript support)
- Handles serialization & deserialization
- Built-in validation & error parsing
- Utility functions (time multipliers, state checks, etc)
- Complete documentation in app/contract/README.md
- No need to understand Anchor internals
Edit Rust code (in /programs/cinefi/src/):
// Example: Add new instruction or modify existing
pub fn place_bet(ctx: Context<PlaceBet>, bucket: u8, amount: u64) -> Result<()> {
// Your logic here
}Then:
- Build:
anchor build - Tests:
anchor test - Deploy:
anchor deploy - SDK automatically regenerates from IDL
Why modify?
- Changing protocol logic
- Adding new features
- Optimizing on-chain calculations
- Fixing bugs in Rust code
Solution: Use the SDK
- Create Next.js/React app
- Import CinefiSDK from
./app/contract - Follow examples in app/contract/README.md
- Done!
Example:
import CinefiSDK, { fetchMarket, getMarketState } from "./app/contract";
export default function MarketView({ mediaId }: { mediaId: bigint }) {
const [market, setMarket] = useState(null);
const [state, setState] = useState(null);
useEffect(async () => {
const m = await fetchMarket(sdk.program, mediaId);
setMarket(m);
setState(getMarketState(m!));
}, []);
return (
<div>
<h2>Market {mediaId}</h2>
<p>Betting Open: {state?.canBet ? "Yes" : "No"}</p>
<p>Can Claim: {state?.canClaim ? "Yes" : "No"}</p>
</div>
);
}Solution: Use localnet
# Terminal 1: Start local validator
solana-test-validator
# Terminal 2: Deploy program
anchor deploy
# Terminal 3: Run your tests
anchor testSee Run Tests and Deploy (Localnet) sections.
Solution: Read the docs
- Quick overview: This file (README.md)
- How markets work: Market Lifecycle
- Complete details: How It Works
- Using in code: app/contract/README.md
- Program internals: Program Instructions and Account Architecture
| Resource | Purpose | Link |
|---|---|---|
| SDK Setup | Environment config, oracle keys, cluster connection | app/contract/README.md - Setup |
| API Reference | All 8 instructions, 5 PDAs, validation functions | app/contract/README.md - API Reference |
| Complete Workflows | Real market lifecycle examples | app/contract/README.md - Workflows |
| Error Handling | All 27 error codes, recovery patterns | app/contract/README.md - Error Handling |
| Constraint Docs | Time windows, amount limits, bucket rules | app/contract/README.md - Constraints |
| Troubleshooting | Connection issues, wallet errors, debugging | app/contract/README.md - Troubleshooting |
| Resource | Purpose | Link |
|---|---|---|
| Program Overview | Instructions, accounts, logic | Program Overview section (this file) |
| Market Lifecycle | State transitions, timing | Market Lifecycle section (this file) |
| Instructions | Detailed specification of each instruction | Program Instructions section (this file) |
| Accounts | Data structures and field descriptions | Account Architecture section (this file) |
| Constants | Time values, fees, limits | Protocol Constants section (this file) |
| Error Codes | All 27 error codes with descriptions | Error Codes section (this file) |
| Rust Code | Implementation details | /programs/cinefi/src/ directory |
| File/Folder | Purpose |
|---|---|
app/contract/ |
π₯ Start here for frontend - TypeScript SDK |
app/contract/README.md |
π Complete SDK documentation (1600+ lines) |
programs/cinefi/src/lib.rs |
Entry point for Rust program |
programs/cinefi/src/instructions/ |
All 8 instruction implementations |
programs/cinefi/src/states/ |
Account definitions |
tests/cinefi.ts |
Integration tests (shows SDK usage) |
target/idl/cinefi.json |
Generated IDL (shared between Rust & TS) |
1. Read: Quick Navigation β TypeScript SDK section above
2. Setup: app/contract/README.md - Environment Configuration
3. Code: Import CinefiSDK and follow examples
4. Reference: app/contract/README.md - API Reference
5. Deploy: Use TestNet or MainNet
Time: 1-2 hours to basic working app
1. Read: Program Overview (this file)
2. Understand: Market Lifecycle & How It Works (this file)
3. Reference: Program Instructions & Account Architecture (this file)
4. Deep dive: Scroll to Error Codes & Constants (this file)
5. Code: Look at tests/cinefi.ts for real examples
Time: 2-3 hours of reading
1. Setup: Run Prerequisites through Deploy sections above
2. Understand: Program Instructions & Account Architecture (this file)
3. Modify: Edit code in programs/cinefi/src/
4. Build: cargo build -p cinefi
5. Test: anchor test (tests/ folder)
6. Deploy: anchor deploy
7. Regenerate: SDK regenerates from new IDL automatically
Time: Varies by change complexity
1. Copy: app/contract/ folder to your project
2. Install: npm install @coral-xyz/anchor @solana/web3.js bn.js
3. Setup: Create .env with WALLET_SECRET_KEY and ORACLE_*_SECRET_KEY
4. Use: import CinefiSDK from "./app/contract"
5. Reference: app/contract/README.md for all possible operations
Time: 30 minutes
βββββββββββββββββββββββββββββββββββββββββββ
β Your Frontend App (React, Next.js, etc)β
βββββββββββββββββββββββββββββββββββββββββββ€ β Use TypeScript SDK here
β TypeScript SDK (app/contract/) β Type-safe, easy to use
β - Instructions (8) β All examples provided
β - Account Fetching (6 methods) β
β - Validation (15+ helpers) β
β - Error Parsing (27 codes) β
βββββββββββββββββββββββββββββββββββββββββββ€
β Solana Blockchain β β Program runs here
β Cinefi Program (programs/cinefi/) β You don't interact directly
β - 8 Instructions β
β - 5 Account Types β
β - Oracle Consensus Logic β
β - Prize Distribution Math β
βββββββββββββββββββββββββββββββββββββββββββ€
β Solana Network (Devnet/Testnet/Mainnet)β
βββββββββββββββββββββββββββββββββββββββββββ
User Input
β
TypeScript SDK validates
β
SDK calls program instruction
β
Program executes on blockchain
β
Program updates accounts
β
SDK fetches updated accounts
β
Frontend displays results
You only write:
- Frontend code
- SDK usage in frontend
You don't write:
- Blockchain code (already built)
- Transaction serialization (SDK handles)
- Account struct definitions (SDK handles)
- Main Docs:
/README.md(this file) - Protocol overview - SDK Docs:
/app/contract/README.md- Complete API reference & setup - IDL Docs:
/target/idl/cinefi.json- Generated from Rust code
To learn protocol:
programs/cinefi/src/lib.rs- Main logicprograms/cinefi/src/states/- Data structurestests/cinefi.ts- Real usage examples
To build frontend:
app/contract/README.md- Complete guideapp/contract/instructions/- How each instruction worksapp/contract/utils/index.ts- Utility functions
- Read Quick Navigation section
- Read TypeScript SDK section
- Clone/import
/app/contract/to your project - Create
.envfile with wallet and oracle keys (from SDK docs) - Test connection to cluster (from SDK docs)
- Run quick start example (from SDK quick start)
- Study full examples in app/contract/README.md
- Implement your frontend features
- Test on Devnet
- Deploy to Testnet or Mainnet
- Read the Quick Navigation section above
- Check TypeScript SDK section for your use case
- Open app/contract/README.md in your editor
- Set up environment variables (
.envfile) - Create a basic script that connects to SDK
- Run the Quick Start example
- Fetch a market and display its state
- Build core frontend features
- Study complete workflows
- Test on Devnet with real transactions
- Implement error handling
- Comprehensive testing
- Security audit of your app
- Deploy to Testnet
- Deploy to Mainnet
MIT
Questions? Check the comprehensive SDK docs: app/contract/README.md (1600+ lines covering everything)