use anchor_lang::prelude::*;
use anchor_spl::token::{Token, TokenAccount};
#[program]
pub mod robot_fight_betting {
use super::*;
// Initialize a new fight
pub fn create_fight(
ctx: Context<CreateFight>,
fight_id: String,
left_fighter: String,
right_fighter: String,
) -> Result<()> {
let fight = &mut ctx.accounts.fight;
fight.fight_id = fight_id;
fight.left_fighter = left_fighter;
fight.right_fighter = right_fighter;
fight.status = FightStatus::Open;
fight.left_pool = 0;
fight.right_pool = 0;
fight.total_bets = 0;
Ok(())
}
// Place a bet
pub fn place_bet(
ctx: Context<PlaceBet>,
amount: u64,
side: Side,
) -> Result<()> {
require!(
ctx.accounts.fight.status == FightStatus::Open,
ErrorCode::FightNotOpen
);
// Transfer SOL from bettor to pool
let transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
&ctx.accounts.bettor.key(),
&ctx.accounts.pool.key(),
amount,
);
anchor_lang::solana_program::program::invoke(
&transfer_ix,
&[
ctx.accounts.bettor.to_account_info(),
ctx.accounts.pool.to_account_info(),
],
)?;
// Record bet
let bet = &mut ctx.accounts.bet;
bet.fight_id = ctx.accounts.fight.key();
bet.bettor = ctx.accounts.bettor.key();
bet.amount = amount;
bet.side = side;
bet.odds = calculate_odds(&ctx.accounts.fight, side);
bet.timestamp = Clock::get()?.unix_timestamp;
// Update fight pools
let fight = &mut ctx.accounts.fight;
match side {
Side::Left => fight.left_pool += amount,
Side::Right => fight.right_pool += amount,
}
fight.total_bets += 1;
Ok(())
}
// Close fight and declare winner
pub fn close_fight(
ctx: Context<CloseFight>,
winner: Side,
) -> Result<()> {
require!(
ctx.accounts.fight.status == FightStatus::Open,
ErrorCode::FightAlreadyClosed
);
let fight = &mut ctx.accounts.fight;
fight.status = FightStatus::Closed;
fight.winner = Some(winner);
Ok(())
}
// Claim payout
pub fn claim_payout(ctx: Context<ClaimPayout>) -> Result<()> {
let bet = &ctx.accounts.bet;
let fight = &ctx.accounts.fight;
require!(
fight.status == FightStatus::Closed,
ErrorCode::FightNotClosed
);
require!(
bet.side == fight.winner.unwrap(),
ErrorCode::BetLost
);
require!(
!bet.claimed,
ErrorCode::AlreadyClaimed
);
// Calculate payout
let total_pool = fight.left_pool + fight.right_pool;
let winning_pool = match bet.side {
Side::Left => fight.left_pool,
Side::Right => fight.right_pool,
};
let house_edge = 0.05;
let payout = (bet.amount * total_pool / winning_pool)
* (1.0 - house_edge) as u64;
// Transfer payout
**ctx.accounts.pool.to_account_info().try_borrow_mut_lamports()? -= payout;
**ctx.accounts.bettor.to_account_info().try_borrow_mut_lamports()? += payout;
// Mark as claimed
let bet = &mut ctx.accounts.bet;
bet.claimed = true;
bet.payout = payout;
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateFight<'info> {
#[account(init, payer = admin, space = 8 + 256)]
pub fight: Account<'info, Fight>,
#[account(mut)]
pub admin: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct PlaceBet<'info> {
#[account(init, payer = bettor, space = 8 + 128)]
pub bet: Account<'info, Bet>,
#[account(mut)]
pub fight: Account<'info, Fight>,
#[account(mut)]
pub bettor: Signer<'info>,
#[account(mut)]
pub pool: SystemAccount<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct Fight {
pub fight_id: String,
pub left_fighter: String,
pub right_fighter: String,
pub status: FightStatus,
pub winner: Option<Side>,
pub left_pool: u64,
pub right_pool: u64,
pub total_bets: u32,
}
#[account]
pub struct Bet {
pub fight_id: Pubkey,
pub bettor: Pubkey,
pub amount: u64,
pub side: Side,
pub odds: f64,
pub payout: u64,
pub claimed: bool,
pub timestamp: i64,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
pub enum FightStatus {
Open,
Live,
Closed,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Copy)]
pub enum Side {
Left,
Right,
}
#[error_code]
pub enum ErrorCode {
#[msg("Fight is not open for betting")]
FightNotOpen,
#[msg("Fight is already closed")]
FightAlreadyClosed,
#[msg("Fight is not closed yet")]
FightNotClosed,
#[msg("Bet lost")]
BetLost,
#[msg("Payout already claimed")]
AlreadyClaimed,
}