Program ID
F1pb…E5Wd
mainnet-beta
SDK version
0.1.0
MIT
Bundle size
9.2 KB
gzipped, tree-shaken
Runtime
any
Node · Deno · Bun · browser
npm install @flipbet/sdk @solana/web3.js# orpnpm add @flipbet/sdk @solana/web3.js# orbun add @flipbet/sdk @solana/web3.jsBuild a transaction that creates a 0.1 SOL match in three lines:
import { Connection, Keypair, Transaction } from "@solana/web3.js";import { createMatchSolIx, BN } from "@flipbet/sdk";import { randomBytes } from "node:crypto"; const conn = new Connection("https://api.mainnet-beta.solana.com");const me = Keypair.generate(); // your wallet adapter signer const { ix, matchAccount } = createMatchSolIx({ creator: me.publicKey, nonce: new BN(Date.now()), // any unique value wagerLamports: new BN(0.1 * 1e9), pick: "heads", creatorSeed: randomBytes(32), // commit; revealed at settle}); const tx = new Transaction().add(ix);tx.feePayer = me.publicKey;tx.recentBlockhash = (await conn.getLatestBlockhash()).blockhash;const sig = await conn.sendTransaction(tx, [me]);console.log("match created:", matchAccount.toBase58(), sig);matchAccount returned is a PDA derived from [b"match", creator.key(), nonce.to_le_bytes()]. Anyone can fetch the same address knowing the creator + nonce — no IDs to coordinate.Every program instruction has a typed builder that returns a ready-to-sign TransactionInstruction. Builders never throw on missing accounts; they fill PDAs deterministically.
| Builder | Caller |
|---|---|
| createMatchSolIx | Player A |
| joinMatchSolIx | Player B |
| requestRandomnessIx | Either |
| settleIx | Anyone |
| claimWinningsIx | Winner / anyone |
| offerDorIx | Winner |
| acceptDorIx | Loser |
| declineDorIx | Loser / anyone |
| cancelOpenIx | Creator |
On-chain Match data is decoded with strict types — no runtime casts. Use these from any environment that can read account data:
import { fetchMatch, listOpenMatches, deriveOutcome } from "@flipbet/sdk"; const match = await fetchMatch(conn, matchPubkey);if (!match) throw new Error("match not found"); console.log(match.status); // "open" | "joined" | ...console.log(match.pot.toString()); // Recompute the outcome client-side and compare to on-chain result.const { side } = deriveOutcome({ vrfValue: match.lastEntropy, // already SHA-256'd by the program creatorSeed: match.creatorSeed, opponentSeed: match.opponentSeed,});console.log("verified", side === match.lastResult); // Fetch all open matches for a lobby render. Heavy on RPC — prefer the// indexer WebSocket (below) for live updates.const open = await listOpenMatches(conn);Subscribe to every program-touching transaction by pointing a Helius Enhanced Transactions webhook at our flipbet-indexer Worker. The Worker decodes Anchor events and writes into a Lobby Durable Object that web clients subscribe to.
{ "webhookURL": "https://flipbet-indexer.your-workers.dev/webhook", "transactionTypes": ["ANY"], "accountAddresses": ["F1pbet4Hjk9zqsZTPKqNPQ8YEfeZCJdrvZB56dbqE5Wd"], "webhookType": "enhanced", "authHeader": "shared-secret-here"}Each event carries the same shape across the SDK and indexer Worker. All fields are bytes/numbers, never trusted strings.
MatchCreatedlobbyMatchJoinedlobbyRandomnessRequestedlifecycleMatchSettledlifecycleDorOffereddorDorAccepteddorDorDeclineddorWinningsClaimedlifecycleMatchCancelledlifecycleCommunitySwepttreasuryFor live lobby updates, connect to the indexer Worker's WebSocket. It's a single global connection — one stream per origin — and the Cloudflare hibernation API means idle DOs cost nothing.
const ws = new WebSocket("wss://flipbet-indexer.your-workers.dev/lobby/connect"); ws.onmessage = (e) => { const msg = JSON.parse(e.data); switch (msg.kind) { case "snapshot": { // Full initial state: open[], recent[] hydrateLobby(msg.open); break; } case "match_added": addToLobby(msg.match); break; case "match_removed": removeFromLobby(msg.matchPda); break; case "settled": prependRecentFlip(msg.flip); break; }};Auth-less by design
If you're building in a language without the SDK, talk to the program directly via JSON-RPC. The instruction layout matches Anchor conventions — discriminator + Borsh args. The Rust source is the canonical reference; an IDL JSON is published on every release.
curl https://api.mainnet-beta.solana.com -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0","id":1,"method":"getProgramAccounts", "params":[ "F1pbet4Hjk9zqsZTPKqNPQ8YEfeZCJdrvZB56dbqE5Wd", {"encoding":"base64","filters":[{"memcmp":{"offset":0,"bytes":"<MATCH_DISC>"}}]} ] }'- Program calls: limited only by Solana itself. Compute budget per instruction is well under the 1.4M cap.
- Switchboard randomness: ~0.0005 SOL per request, paid by whichever wallet calls
request_randomness. - Indexer WebSocket: free, no key required. ~50 msg/s per connection, ~10k concurrent connections per Worker region.
- Chat WebSocket: 30 messages/min per wallet, enforced by the ChatRoom DO with persistent SQLite-backed counters.