Full lesson
solana_prediction_market
What it does
From the project README:
You can use any normal anchor commands. Either move to the `anchor` directory and run the `anchor` command or prefix the command with `npm run`, eg: `npm run anchor`.
- Original
- chappie1998/solana_prediction_market
- SuperStack fork
- Blockchains/solana_prediction_market @
2508aa7 - Stars
- 1
- Last push (original)
- 2024-10-08
- Languages
- TypeScript, Rust, JavaScript, CSS
- Detected stack
- AnchorNext.jsweb3.js
- Categories
- Other
- Licence
- MIT
Architecture
47 source files. By extension: .ts ×21, .tsx ×20, .js ×5, .rs ×1.
| Top-level directory | Source files |
|---|---|
web | 38 |
anchor | 7 |
(root) | 2 |
On-chain programs: anchor/programs/prediction-market
Dapp: deploying
This fork is queued for its own live dapp on Sol.College, with a Devnet/Mainnet switch. It appears here, at /dapps/solana-prediction-market/, once it is deployed and checked on devnet. Until then, run it locally with the commands below.
Key code, explained
Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.
anchor/programs/prediction-market/src/lib.rs L49–72 @ 2508aa7 · MIT · original // let yes_compressed_token_ = create_create_token_pool_instruction(ctx.accounts.owner.key, &pool.yes_token_mint); // let no_compressed_token_ = create_create_token_pool_instruction(ctx.accounts.owner.key, &pool.yes_token_mint); // let cpi_program = ctx.accounts.compressed_token_program.to_account_info(); // let seeds = &[ // pool.to_account_info().key.as_ref(), // &[pool.bump], // ]; // let signer = &[&seeds[..]]; // let cpi_ctx = CpiContext::new_with_signer(cpi_program, yes_compressed_token_.accounts, signer); Ok(()) } /// Vote in a prediction pool /// Users can vote YES or NO by transferring USDT and receiving corresponding tokens pub fn vote(ctx: Context<Vote>, amount: u64, vote_yes: bool) -> Result<()> { let pool = &mut ctx.accounts.pool; let current_timestamp = Clock::get()?.unix_timestamp; // Check if the pool is active and within the voting timeframe require!(current_timestamp <= pool.end_time, PredictionMarketError::PoolEnded); require!(current_timestamp >= pool.start_time, PredictionMarketError::PoolNotStarted); require!(pool.status == PoolStatus::Active, PredictionMarketError::PoolNotActive); // Verify the pool signer- Line 52
// let seeds = &[: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 54
// &[pool.bump],: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 57
// let cpi_ctx = CpiContext::new_with_signer(cpi_program, yes_compressed_token_.accounts,: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address. - Line 66
let current_timestamp = Clock::get()?.unix_timestamp;: Reads on-chain time from the Clock sysvar. - Line 68
require!(current_timestamp <= pool.end_time, PredictionMarketError::PoolEnded);: An explicit validation that aborts the instruction with an error.
web/app/api/createPool.ts L13–36 @ 2508aa7 · MIT · original export const createPool = async ( program: Program<PredictionMarket>, payer: WalletContextState, predictionMarketPubkey: PublicKey // startTime: number, // endTime: number): Promise<string> => { const startTime = Math.floor(Date.now() / 1000); // Current time in seconds const endTime = startTime + 60; // End time 1 hour from now if (!payer.publicKey) { throw new Error('Wallet not connected'); } // Generate a new pool ID const poolId = Buffer.from(Keypair.generate().publicKey.toBytes()).toJSON() .data; // const [poolPubkey] = await findProgramAddress( // [Buffer.from('pool'), Buffer.from(poolId)], // program.programId // ); const [poolPubkey, bump] = await findProgramAddress(- Line 31
// const [poolPubkey] = await findProgramAddress(: Derives a PDA from seeds and the program ID and returns the address with its canonical bump. - Line 36
const [poolPubkey, bump] = await findProgramAddress(: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
web/app/api/vote.ts L25–48 @ 2508aa7 · MIT · original ); const poolUsdtAccount = await getAssociatedTokenAddress( usdtMint, poolPubkey, true ); const userYesTokenAccount = await getAssociatedTokenAddress( new PublicKey(pool.yesTokenMint), payer.publicKey ); const userNoTokenAccount = await getAssociatedTokenAddress( new PublicKey(pool.noTokenMint), payer.publicKey ); const [poolSigner] = await findProgramAddress( [Buffer.from("pool"), Buffer.from(pool.id)], PREDICTION_MARKET_PROGRAM_ID ); // , Buffer.from([pool.bump]) // Check if accounts exist and create them if they don't const accountsToCheck = [ { address: poolUsdtAccount, mint: usdtMint, owner: poolPubkey },- Line 40
const [poolSigner] = await findProgramAddress(: Derives a PDA from seeds and the program ID and returns the address with its canonical bump. - Line 44
// , Buffer.from([pool.bump]): Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
How to run, build and test
These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.
| Directory | Command | Runs |
|---|---|---|
. | npm run dev | nx serve web |
. | npm run build | nx build web |
anchor | anchor build | compile programs and generate IDLs |
anchor | anchor test | local validator + test suite |
anchor | anchor run test | ../node_modules/.bin/nx run anchor:jest |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)