├── .prettierignore ├── programs └── prediction-market │ ├── Xargo.toml │ ├── src │ ├── state │ │ ├── mod.rs │ │ ├── whitelist.rs │ │ ├── config.rs │ │ ├── global.rs │ │ └── market.rs │ ├── instructions │ │ ├── mod.rs │ │ ├── admin │ │ │ ├── mod.rs │ │ │ ├── nominate_authority.rs │ │ │ ├── accept_authority.rs │ │ │ └── configure.rs │ │ └── market │ │ │ ├── mod.rs │ │ │ ├── resolution.rs │ │ │ ├── add_liquidity.rs │ │ │ ├── withdraw_liquidity.rs │ │ │ ├── create_market.rs │ │ │ ├── mint_no_token.rs │ │ │ └── swap.rs │ ├── constants.rs │ ├── events.rs │ ├── lib.rs │ ├── errors.rs │ └── utils.rs │ └── Cargo.toml ├── .gitignore ├── tsconfig.json ├── Cargo.toml ├── Anchor.toml ├── package.json ├── README.md ├── cli ├── command.ts └── scripts.ts ├── yarn.lock └── Cargo.lock /.prettierignore: -------------------------------------------------------------------------------- 1 | .anchor 2 | .DS_Store 3 | target 4 | node_modules 5 | dist 6 | build 7 | test-ledger 8 | -------------------------------------------------------------------------------- /programs/prediction-market/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /programs/prediction-market/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod global; 3 | pub mod market; 4 | pub mod whitelist; 5 | -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod admin; 2 | pub use admin::*; 3 | pub mod market; 4 | pub use market::*; 5 | -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/admin/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod accept_authority; 2 | pub mod configure; 3 | pub mod nominate_authority; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .anchor 2 | .DS_Store 3 | target 4 | **/*.rs.bk 5 | node_modules 6 | test-ledger 7 | .yarn 8 | .lock 9 | tests 10 | target 11 | migrations 12 | app 13 | keys -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/market/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod add_liquidity; 2 | pub mod create_market; 3 | pub mod mint_no_token; 4 | pub mod resolution; 5 | pub mod swap; 6 | pub mod withdraw_liquidity; 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["mocha", "chai"], 4 | "typeRoots": ["./node_modules/@types"], 5 | "lib": ["es2015"], 6 | "module": "commonjs", 7 | "target": "es6", 8 | "esModuleInterop": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "programs/*" 4 | ] 5 | resolver = "2" 6 | 7 | [profile.release] 8 | overflow-checks = true 9 | lto = "fat" 10 | codegen-units = 1 11 | [profile.release.build-override] 12 | opt-level = 3 13 | incremental = false 14 | codegen-units = 1 15 | -------------------------------------------------------------------------------- /programs/prediction-market/src/state/whitelist.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | 3 | #[account] 4 | #[derive(InitSpace, Debug, Default)] 5 | pub struct Whitelist { 6 | pub creator: Pubkey, 7 | } 8 | 9 | impl Whitelist { 10 | pub const SEED_PREFIX: &'static str = "wl-seed"; 11 | } 12 | -------------------------------------------------------------------------------- /programs/prediction-market/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const CONFIG: &str = "config"; 2 | pub const GLOBAL: &str = "global"; 3 | pub const MARKET: &str = "market"; 4 | pub const USERINFO: &str = "userinfo"; 5 | pub const METADATA: &str = "metadata"; 6 | pub const YES_NAME: &str = "agree"; 7 | pub const NO_NAME: &str = "disagree"; 8 | 9 | pub const MAX_START_SLOT_DELAY: u64 = 1_512_000; // ~1 week in slots (400ms each) 10 | 11 | -------------------------------------------------------------------------------- /Anchor.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | anchor_version = "0.30.1" 3 | 4 | [features] 5 | resolution = true 6 | skip-lint = true 7 | 8 | [programs.devnet] 9 | prediction_market = "5q1C8N47AYvLu7w6LKngwXhLjrZCZ5izMB8nbziZhYEV" 10 | 11 | [registry] 12 | url = "https://api.apr.dev" 13 | 14 | [provider] 15 | cluster = "https://api.devnet.solana.com" 16 | wallet = "./keys/admin.json" 17 | 18 | [scripts] 19 | build = "rm -rf target && anchor build && mkdir -p target/deploy && cp ./keys/program/3uHJMHzeiqdqQ3LNc5bNVxuCp224HGtStPkv1JUEcabr.json ./target/deploy/prediction_market-keypair.json" 20 | test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" 21 | -------------------------------------------------------------------------------- /programs/prediction-market/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prediction-market" 3 | version = "0.1.0" 4 | description = "Created with Anchor" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "prediction_market" 10 | 11 | [features] 12 | default = [] 13 | cpi = ["no-entrypoint"] 14 | no-entrypoint = [] 15 | no-idl = [] 16 | no-log-ix-name = [] 17 | idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] 18 | 19 | [dependencies] 20 | anchor-lang = { version = "0.30.1", features = ["init-if-needed","event-cpi"] } 21 | anchor-spl = { version = "0.30.1", features = ["metadata"] } 22 | solana-program = "1.18.18" 23 | spl-token = "=4.0.3" 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "ISC", 3 | "scripts": { 4 | "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", 5 | "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check", 6 | "test": "mocha -r ts-node/register tests/**/*.ts", 7 | "script": "ts-node ./cli/command.ts" 8 | }, 9 | "dependencies": { 10 | "@coral-xyz/anchor": "^0.30.1", 11 | "@solana/spl-token": "^0.4.8", 12 | "@solana/web3.js": "^1.68.0", 13 | "commander": "^13.0.0", 14 | "dotenv": "^16.4.5" 15 | }, 16 | "devDependencies": { 17 | "chai": "^4.3.4", 18 | "mocha": "^9.0.3", 19 | "ts-mocha": "^10.0.0", 20 | "@types/bn.js": "^5.1.0", 21 | "@types/chai": "^4.3.0", 22 | "@types/mocha": "^9.0.0", 23 | "typescript": "^4.3.5", 24 | "prettier": "^2.6.2" 25 | } 26 | } -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/admin/nominate_authority.rs: -------------------------------------------------------------------------------- 1 | use constants::CONFIG; 2 | use errors::PredictionMarketError; 3 | // use state::config::*; 4 | 5 | use crate::*; 6 | 7 | #[derive(Accounts)] 8 | pub struct NominateAuthority<'info> { 9 | // Current admin 10 | #[account( 11 | mut, 12 | constraint = global_config.authority == *admin.key @PredictionMarketError::IncorrectAuthority 13 | )] 14 | pub admin: Signer<'info>, 15 | 16 | // Stores admin address 17 | #[account( 18 | mut, 19 | seeds = [CONFIG.as_bytes()], 20 | bump, 21 | )] 22 | global_config: Box>, 23 | } 24 | 25 | impl NominateAuthority<'_> { 26 | pub fn process(&mut self, new_admin: Pubkey) -> Result<()> { 27 | self.global_config.pending_authority = new_admin; 28 | Ok(()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/admin/accept_authority.rs: -------------------------------------------------------------------------------- 1 | use constants::CONFIG; 2 | use errors::PredictionMarketError; 3 | 4 | use crate::*; 5 | 6 | #[derive(Accounts)] 7 | pub struct AcceptAuthority<'info> { 8 | // Pending admin 9 | #[account( 10 | mut, 11 | constraint = global_config.pending_authority == new_admin.key() @PredictionMarketError::IncorrectAuthority 12 | )] 13 | pub new_admin: Signer<'info>, 14 | 15 | // Stores admin address 16 | #[account( 17 | mut, 18 | seeds = [CONFIG.as_bytes()], 19 | bump, 20 | )] 21 | global_config: Box>, 22 | } 23 | 24 | impl AcceptAuthority<'_> { 25 | pub fn process(&mut self) -> Result<()> { 26 | self.global_config.authority = self.new_admin.key(); 27 | self.global_config.pending_authority = Pubkey::default(); 28 | 29 | Ok(()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /programs/prediction-market/src/events.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | 3 | #[event] 4 | pub struct GlobalUpdateEvent { 5 | pub global_authority: Pubkey, 6 | pub initial_real_token_reserves: u64, 7 | pub token_total_supply: u64, 8 | pub mint_decimals: u8, 9 | } 10 | 11 | #[event] 12 | pub struct CreateEvent { 13 | pub creator: Pubkey, 14 | pub market: Pubkey, 15 | 16 | pub token_yes: Pubkey, 17 | pub metadata_yes: Pubkey, 18 | pub token_yes_total_supply: u64, 19 | pub real_yes_sol_reserves: u64, 20 | 21 | pub token_no: Pubkey, 22 | pub metadata_no: Pubkey, 23 | pub token_no_total_supply: u64, 24 | pub real_no_sol_reserves: u64, 25 | 26 | pub start_slot: u64, 27 | pub ending_slot: u64, 28 | } 29 | 30 | #[event] 31 | pub struct WithdrawEvent { 32 | pub withdraw_authority: Pubkey, 33 | pub mint: Pubkey, 34 | pub fee_vault: Pubkey, 35 | 36 | pub withdrawn: u64, 37 | pub total_withdrawn: u64, 38 | 39 | pub withdraw_time: i64, 40 | } 41 | 42 | #[event] 43 | pub struct TradeEvent { 44 | pub user: Pubkey, 45 | pub token_yes: Pubkey, 46 | pub token_no: Pubkey, 47 | pub market_info: Pubkey, 48 | 49 | pub sol_amount: u64, 50 | pub token_amount: u64, 51 | pub fee_lamports: u64, 52 | pub is_buy: bool, 53 | pub is_yes_no: bool, 54 | 55 | pub real_sol_reserves: u64, 56 | pub real_token_yes_reserves: u64, 57 | pub real_token_no_reserves: u64, 58 | 59 | pub timestamp: i64, 60 | } 61 | 62 | #[event] 63 | pub struct CompleteEvent { 64 | pub user: Pubkey, 65 | pub mint: Pubkey, 66 | pub virtual_sol_reserves: u64, 67 | pub virtual_token_reserves: u64, 68 | pub real_sol_reserves: u64, 69 | pub real_token_reserves: u64, 70 | pub timestamp: i64, 71 | } 72 | 73 | pub trait IntoEvent { 74 | fn into_event(&self) -> T; 75 | } 76 | -------------------------------------------------------------------------------- /programs/prediction-market/src/state/config.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::*; 2 | use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize}; 3 | use core::fmt::Debug; 4 | 5 | #[account] 6 | #[derive(Debug)] 7 | pub struct Config { 8 | pub authority: Pubkey, 9 | // use this for 2 step ownership transfer 10 | pub pending_authority: Pubkey, 11 | 12 | pub team_wallet: Pubkey, 13 | 14 | // platform fee percentage 15 | pub platform_buy_fee: u64, 16 | pub platform_sell_fee: u64, 17 | 18 | // lp fee percentage 19 | pub lp_buy_fee: u64, 20 | pub lp_sell_fee: u64, 21 | 22 | pub token_supply_config: u64, 23 | pub token_decimals_config: u8, 24 | 25 | pub initial_real_token_reserves_config: u64, 26 | 27 | pub min_sol_liquidity: u64, 28 | 29 | pub initialized: bool, 30 | } 31 | 32 | #[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq, Debug)] 33 | pub enum AmountConfig { 34 | Range { min: Option, max: Option }, 35 | Enum(Vec), 36 | } 37 | 38 | impl AmountConfig { 39 | pub fn validate(&self, value: &T) -> Result<()> { 40 | match self { 41 | Self::Range { min, max } => { 42 | if let Some(min) = min { 43 | if value < min { 44 | // msg!("value {value:?} too small, expected at least {min:?}"); 45 | return Err(ValueTooSmall.into()); 46 | } 47 | } 48 | if let Some(max) = max { 49 | if value > max { 50 | // msg!("value {value:?} too large, expected at most {max:?}"); 51 | return Err(ValueTooLarge.into()); 52 | } 53 | } 54 | 55 | Ok(()) 56 | } 57 | Self::Enum(options) => { 58 | if options.contains(value) { 59 | Ok(()) 60 | } else { 61 | // msg!("invalid value {value:?}, expected one of: {options:?}"); 62 | Err(ValueInvalid.into()) 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/market/resolution.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::{CONFIG, GLOBAL, MARKET, USERINFO}, 3 | errors::PredictionMarketError, 4 | state::{config::*, market::*}, 5 | }; 6 | use anchor_lang::{prelude::*, system_program}; 7 | use anchor_spl::{ 8 | associated_token::{self, AssociatedToken}, 9 | token::{self, Mint, Token}, 10 | }; 11 | 12 | #[derive(Accounts)] 13 | pub struct Resolution<'info> { 14 | #[account( 15 | mut, 16 | seeds = [CONFIG.as_bytes()], 17 | bump, 18 | )] 19 | global_config: Box>, 20 | 21 | #[account( 22 | mut, 23 | seeds = [MARKET.as_bytes(), &yes_token.key().to_bytes(), &no_token.key().to_bytes()], 24 | bump 25 | )] 26 | market: Account<'info, Market>, 27 | 28 | /// CHECK: global vault pda which stores SOL 29 | #[account( 30 | mut, 31 | seeds = [GLOBAL.as_bytes()], 32 | bump, 33 | )] 34 | pub global_vault: AccountInfo<'info>, 35 | 36 | pub yes_token: Box>, 37 | pub no_token: Box>, 38 | 39 | #[account( 40 | mut, 41 | seeds = [USERINFO.as_bytes(), &user.key().to_bytes(), &market.key().to_bytes()], 42 | bump 43 | )] 44 | pub user_info: Box>, 45 | 46 | #[account(mut)] 47 | pub user: AccountInfo<'info>, 48 | 49 | #[account(mut)] 50 | pub authority: Signer<'info>, 51 | 52 | #[account(address = system_program::ID)] 53 | pub system_program: Program<'info, System>, 54 | 55 | #[account(address = token::ID)] 56 | pub token_program: Program<'info, Token>, 57 | 58 | #[account(address = associated_token::ID)] 59 | pub associated_token_program: Program<'info, AssociatedToken>, 60 | } 61 | 62 | impl<'info> Resolution<'info>{ 63 | pub fn handler(&mut self, yes_amount: u64, no_amount: u64 ,token_type: u8, is_completed: bool ,global_vault_bump:u8)-> Result<()>{ 64 | require!( 65 | self.authority.key() == self.global_config.authority.key(), 66 | PredictionMarketError::InvalidMigrationAuthority 67 | ); 68 | 69 | //A decentralized prediction market platform built on Solana blockchain, inspired by Polymarket. This project enables users to create markets, trade positions, and resolve outcomes based on real-world events. 70 | 71 | 72 | Ok(()) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/market/add_liquidity.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::{CONFIG, GLOBAL, MARKET, USERINFO}, 3 | errors::PredictionMarketError, 4 | state::{config::*, market::*}, 5 | }; 6 | use anchor_lang::{prelude::*, system_program}; 7 | use anchor_spl::{ 8 | associated_token::{self, AssociatedToken}, 9 | token::{self, Mint, Token}, 10 | }; 11 | 12 | #[derive(Accounts)] 13 | pub struct AddLiquidity<'info> { 14 | #[account( 15 | mut, 16 | seeds = [CONFIG.as_bytes()], 17 | bump, 18 | )] 19 | global_config: Box>, 20 | 21 | // team wallet 22 | /// CHECK: should be same with the address in the global_config 23 | #[account( 24 | mut, 25 | constraint = global_config.team_wallet == team_wallet.key() @PredictionMarketError::IncorrectAuthority 26 | )] 27 | pub team_wallet: AccountInfo<'info>, 28 | 29 | #[account( 30 | mut, 31 | seeds = [MARKET.as_bytes(), &yes_token.key().to_bytes(), &no_token.key().to_bytes()], 32 | bump 33 | )] 34 | market: Account<'info, Market>, 35 | 36 | /// CHECK: global vault pda which stores SOL 37 | #[account( 38 | mut, 39 | seeds = [GLOBAL.as_bytes()], 40 | bump, 41 | )] 42 | pub global_vault: AccountInfo<'info>, 43 | 44 | pub yes_token: Box>, 45 | pub no_token: Box>, 46 | 47 | #[account( 48 | init_if_needed, 49 | payer = user, 50 | space = 8 + std::mem::size_of::(), 51 | seeds = [USERINFO.as_bytes(), &user.key().to_bytes(), &market.key().to_bytes()], 52 | bump 53 | )] 54 | pub user_info: Box>, 55 | 56 | #[account(mut)] 57 | pub user: Signer<'info>, 58 | 59 | #[account(address = system_program::ID)] 60 | pub system_program: Program<'info, System>, 61 | 62 | #[account(address = token::ID)] 63 | pub token_program: Program<'info, Token>, 64 | 65 | #[account(address = associated_token::ID)] 66 | pub associated_token_program: Program<'info, AssociatedToken>, 67 | } 68 | 69 | impl<'info> AddLiquidity<'info> { 70 | pub fn handler(&mut self, amount: u64) -> Result<()> { 71 | //A decentralized prediction market platform built on Solana blockchain, inspired by Polymarket. This project enables users to create markets, trade positions, and resolve outcomes based on real-world events. 72 | 73 | Ok(()) 74 | } 75 | } -------------------------------------------------------------------------------- /programs/prediction-market/src/instructions/market/withdraw_liquidity.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::{CONFIG, GLOBAL, MARKET, USERINFO}, 3 | errors::PredictionMarketError, 4 | state::{config::*, market::*}, 5 | }; 6 | use anchor_lang::{prelude::*, system_program}; 7 | use anchor_spl::{ 8 | associated_token::{self, AssociatedToken}, 9 | token::{self, Mint, Token}, 10 | }; 11 | 12 | #[derive(Accounts)] 13 | pub struct WithdrawLiquidity<'info> { 14 | #[account( 15 | mut, 16 | seeds = [CONFIG.as_bytes()], 17 | bump, 18 | )] 19 | global_config: Box>, 20 | 21 | // team wallet 22 | /// CHECK: should be same with the address in the global_config 23 | #[account( 24 | mut, 25 | constraint = global_config.team_wallet == team_wallet.key() @PredictionMarketError::IncorrectAuthority 26 | )] 27 | pub team_wallet: AccountInfo<'info>, 28 | 29 | #[account( 30 | mut, 31 | seeds = [MARKET.as_bytes(), &yes_token.key().to_bytes(), &no_token.key().to_bytes()], 32 | bump 33 | )] 34 | market: Account<'info, Market>, 35 | 36 | /// CHECK: global vault pda which stores SOL 37 | #[account( 38 | mut, 39 | seeds = [GLOBAL.as_bytes()], 40 | bump, 41 | )] 42 | pub global_vault: AccountInfo<'info>, 43 | 44 | pub yes_token: Box>, 45 | pub no_token: Box>, 46 | 47 | #[account( 48 | mut, 49 | seeds = [USERINFO.as_bytes(), &user.key().to_bytes(), &market.key().to_bytes()], 50 | bump 51 | )] 52 | pub user_info: Box>, 53 | 54 | #[account(mut)] 55 | pub user: Signer<'info>, 56 | 57 | #[account(address = system_program::ID)] 58 | pub system_program: Program<'info, System>, 59 | 60 | #[account(address = token::ID)] 61 | pub token_program: Program<'info, Token>, 62 | 63 | #[account(address = associated_token::ID)] 64 | pub associated_token_program: Program<'info, AssociatedToken>, 65 | } 66 | 67 | impl<'info> WithdrawLiquidity<'info> { 68 | pub fn handler(&mut self, amount: u64, global_vault_bump:u8) -> Result<()> { 69 | //validate user is lp 70 | require!(self.user_info.is_lp == true, PredictionMarketError::WITHDRAWNOTLPERROR); 71 | 72 | //A decentralized prediction market platform built on Solana blockchain, inspired by Polymarket. This project enables users to create markets, trade positions, and resolve outcomes based on real-world events. 73 | 74 | 75 | Ok(()) 76 | } 77 | } -------------------------------------------------------------------------------- /programs/prediction-market/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | pub mod constants; 3 | pub mod errors; 4 | pub mod events; 5 | pub mod instructions; 6 | pub mod state; 7 | pub mod utils; 8 | 9 | use instructions::{ 10 | accept_authority::*, add_liquidity::*, configure::*, create_market::*, mint_no_token::*, 11 | nominate_authority::*, resolution::*, swap::*, withdraw_liquidity::*, 12 | }; 13 | 14 | use state::config::*; 15 | use state::market::*; 16 | 17 | declare_id!("5q1C8N47AYvLu7w6LKngwXhLjrZCZ5izMB8nbziZhYEV"); 18 | 19 | #[program] 20 | pub mod prediction_market { 21 | // use crate::{instruction::AddLiquidity, instructions::resolution::Resolution}; 22 | 23 | use super::*; 24 | 25 | // called by admin to set global config 26 | // need to check the signer is authority 27 | pub fn configure(ctx: Context, new_config: Config) -> Result<()> { 28 | msg!("configure: {:#?}", new_config); 29 | ctx.accounts.handler(new_config, ctx.bumps.config) 30 | } 31 | 32 | // Admin can hand over admin role 33 | pub fn nominate_authority(ctx: Context, new_admin: Pubkey) -> Result<()> { 34 | ctx.accounts.process(new_admin) 35 | } 36 | 37 | // Pending admin should accept the admin role 38 | pub fn accept_authority(ctx: Context) -> Result<()> { 39 | ctx.accounts.process() 40 | } 41 | 42 | pub fn mint_no_token( 43 | ctx: Context, 44 | // metadata 45 | no_symbol: String, 46 | no_uri: String, 47 | ) -> Result<()> { 48 | ctx.accounts 49 | .handler(no_symbol, no_uri, ctx.bumps.global_vault) 50 | } 51 | 52 | pub fn create_market(ctx: Context, params: CreateMarketParams) -> Result<()> { 53 | ctx.accounts.handler(params, ctx.bumps.global_vault) 54 | } 55 | 56 | pub fn swap( 57 | ctx: Context, 58 | amount: u64, 59 | direction: u8, 60 | token_type: u8, 61 | minimum_receive_amount: u64, 62 | ) -> Result<()> { 63 | ctx.accounts.handler( 64 | amount, 65 | direction, 66 | token_type, 67 | minimum_receive_amount, 68 | ctx.bumps.global_vault, 69 | ) 70 | } 71 | 72 | pub fn resolution( 73 | ctx: Context, 74 | yes_amount: u64, 75 | no_amount: u64, 76 | token_type: u8, 77 | is_completed: bool, 78 | ) -> Result<()> { 79 | ctx.accounts.handler( 80 | yes_amount, 81 | no_amount, 82 | token_type, 83 | is_completed, 84 | ctx.bumps.global_vault, 85 | ) 86 | } 87 | 88 | pub fn add_liquidity(ctx: Context, amount: u64) -> Result<()> { 89 | ctx.accounts.handler(amount) 90 | } 91 | 92 | pub fn withdraw_liquidity(ctx: Context, amount: u64) -> Result<()> { 93 | ctx.accounts.handler(amount, ctx.bumps.global_vault) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /programs/prediction-market/src/errors.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | 3 | pub use PredictionMarketError::*; 4 | 5 | #[error_code] 6 | pub enum PredictionMarketError { 7 | #[msg("ValueTooSmall")] 8 | ValueTooSmall, 9 | 10 | #[msg("ValueTooLarge")] 11 | ValueTooLarge, 12 | 13 | #[msg("ValueInvalid")] 14 | ValueInvalid, 15 | 16 | #[msg("IncorrectConfigAccount")] 17 | IncorrectConfigAccount, 18 | 19 | #[msg("IncorrectAuthority")] 20 | IncorrectAuthority, 21 | 22 | #[msg("Overflow or underflow occured")] 23 | OverflowOrUnderflowOccurred, 24 | 25 | #[msg("Amount is invalid")] 26 | InvalidAmount, 27 | 28 | #[msg("Incorrect team wallet address")] 29 | IncorrectTeamWallet, 30 | 31 | #[msg("Curve is not completed")] 32 | CurveNotCompleted, 33 | 34 | #[msg("Can not swap after the curve is completed")] 35 | CurveAlreadyCompleted, 36 | 37 | #[msg("Mint authority should be revoked")] 38 | MintAuthorityEnabled, 39 | 40 | #[msg("Freeze authority should be revoked")] 41 | FreezeAuthorityEnabled, 42 | 43 | #[msg("Return amount is too small compared to the minimum received amount")] 44 | ReturnAmountTooSmall, 45 | 46 | #[msg("AMM is already exist")] 47 | AmmAlreadyExists, 48 | 49 | #[msg("Global Not Initialized")] 50 | NotInitialized, 51 | 52 | #[msg("Invalid Global Authority")] 53 | InvalidGlobalAuthority, 54 | 55 | #[msg("This creator is not in whitelist")] 56 | NotWhiteList, 57 | 58 | #[msg("IncorrectLaunchPhase")] 59 | IncorrectLaunchPhase, 60 | 61 | #[msg("Not enough tokens to complete the sell order.")] 62 | InsufficientTokens, 63 | 64 | #[msg("Not enough SOL received to be valid.")] 65 | InsufficientSol, 66 | 67 | #[msg("Sell Failed")] 68 | SellFailed, 69 | 70 | #[msg("Buy Failed")] 71 | BuyFailed, 72 | 73 | #[msg("This token is not a bonding curve token")] 74 | NotBondingCurveMint, 75 | 76 | #[msg("Not quote mint")] 77 | NotSOL, 78 | 79 | #[msg("Invalid Migration Authority")] 80 | InvalidMigrationAuthority, 81 | 82 | #[msg("Bonding curve is not completed")] 83 | NotCompleted, 84 | 85 | #[msg("Invalid Meteora Program")] 86 | InvalidMeteoraProgram, 87 | 88 | #[msg("Arithmetic Error")] 89 | ArithmeticError, 90 | 91 | #[msg("Invalid Parameter")] 92 | InvalidParameter, 93 | 94 | #[msg("Start time is in the past")] 95 | InvalidStartTime, 96 | 97 | #[msg("End time is in the past")] 98 | InvalidEndTime, 99 | 100 | #[msg("Global Already Initialized")] 101 | AlreadyInitialized, 102 | 103 | #[msg("Invalid Authority")] 104 | InvalidAuthority, 105 | 106 | #[msg("Invalid Argument")] 107 | InvalidArgument, 108 | 109 | #[msg("The market has already ended.")] 110 | MarketNotCompleted, 111 | 112 | #[msg("The market already ended.")] 113 | MarketIsCompleted, 114 | 115 | #[msg("The winner token type error.")] 116 | RESOLUTIONTOKEYTYPEERROR, 117 | 118 | #[msg("The winner yes token amount error.")] 119 | RESOLUTIONYESAMOUNTERROR, 120 | 121 | #[msg("The winner no token amount error.")] 122 | RESOLUTIONNOAMOUNTERROR, 123 | 124 | #[msg("The withdraw sol amount error.")] 125 | WITHDRAWLIQUIDITYSOLAMOUNTERROR, 126 | 127 | #[msg("The withdraw: not lp error.")] 128 | WITHDRAWNOTLPERROR, 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solana Prediction Market Smart Contract 2 | 3 | A decentralized prediction market platform built on Solana blockchain, inspired by Polymarket. This project enables users to create markets, trade positions, and resolve outcomes based on real-world events. 4 | 5 | ## Features 6 | 7 | - **Market Creation**: Create prediction markets for any event 8 | - **Liquidity Provision**: Add and withdraw liquidity to markets 9 | - **Trading**: Trade positions using Yes/No tokens 10 | - **Market Resolution**: Automatic resolution based on final outcomes 11 | - **Fee Structure**: Platform and LP fees for sustainable operations 12 | 13 | ## Contact 14 | 15 | If you wanna build prediction market project like this, plz contact here: [Telegram](https://t.me/Rust0x_726) 16 | 17 | ## Architecture 18 | 19 | The project is built using: 20 | 21 | - Solana Web3.js 22 | - Anchor Framework 23 | - SPL Token Program 24 | - Associated Token Program 25 | 26 | ## Getting Started 27 | 28 | ### Prerequisites 29 | 30 | - Node.js 31 | - Yarn 32 | - Solana CLI 33 | - Anchor Framework 34 | 35 | ### Installation 36 | 37 | 1. Build the program: 38 | 39 | ```bash 40 | anchor build 41 | ``` 42 | 43 | 2. Deploy the program: 44 | 45 | ```bash 46 | anchor deploy 47 | ``` 48 | 49 | ### Configuration 50 | 51 | Configure your project settings: 52 | 53 | ```bash 54 | yarn script config -e devnet -k -r 55 | ``` 56 | 57 | ### Usage Examples 58 | 59 | 1. Create a new market: 60 | 61 | ```bash 62 | yarn script market -e devnet -k -r 63 | ``` 64 | 65 | 2. Add liquidity to a market: 66 | 67 | ```bash 68 | yarn script addlp -y -n -a -e devnet -k -r 69 | ``` 70 | 71 | 3. Trade positions: 72 | 73 | ```bash 74 | yarn script swap -y -n -a -s