Untitled
unknown
plain_text
a year ago
27 kB
8
Indexable
use anchor_lang::prelude::*; use anchor_spl::token::{self, Token, TokenAccount, Transfer}; // Declare the program ID declare_id!("GTwvccoDjKRUB936wqrF3jvAdpwEJGZWZLShEy4PAPxs"); #[program] mod multi_token { use super::*; /// Initializes the escrow account with zero balances for all tokens and sets the authority and mint addresses. pub fn initialize_escrow( ctx: Context<InitializeEscrow>, usdc_mint: Pubkey, bonk_mint: Pubkey, send_mint: Pubkey, ) -> Result<()> { let escrow_account = &mut ctx.accounts.escrow_account; // Initialize balances to 0 escrow_account.sol_balance = 0; escrow_account.usdc_balance = 0; escrow_account.bonk_balance = 0; escrow_account.send_balance = 0; // Set the authority and mint addresses escrow_account.authority = ctx.accounts.owner.key(); escrow_account.usdc_mint = usdc_mint; escrow_account.bonk_mint = bonk_mint; escrow_account.send_mint = send_mint; Ok(()) } /// Allows a user to participate in a challenge by transferring the specified token amount to the escrow account. pub fn participate<'info>( ctx: Context<'_, '_, '_, 'info, Participate<'info>>, token: String, amount: u64, challenge_id: u64, player_id: Option<u64>, participation_type: ParticipationType, ) -> Result<()> { // Determine the number of decimals for the token and adjust the amount accordingly let adjusted_amount = match token.as_str() { "SOL" => amount, // Use the amount directly for SOL as it is already in lamports "USDC" => amount .checked_mul(10_u64.pow(6)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust USDC amount "BONK" => amount .checked_mul(10_u64.pow(5)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust BONK amount "SEND" => amount .checked_mul(10_u64.pow(6)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust SEND amount _ => return Err(ErrorCode::UnsupportedCurrency.into()), }; match token.as_str() { "SOL" => { // Transfer SOL to the escrow account let sol_transfer = anchor_lang::solana_program::system_instruction::transfer( &ctx.accounts.user.key(), &ctx.accounts.escrow_account.key(), adjusted_amount, ); anchor_lang::solana_program::program::invoke( &sol_transfer, &[ ctx.accounts.user.to_account_info().clone(), ctx.accounts.escrow_account.to_account_info().clone(), ctx.accounts.system_program.to_account_info().clone(), ], )?; // Update the SOL balance in the escrow account let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.sol_balance += adjusted_amount; } "USDC" => { // Transfer USDC to the escrow account using CPI let cpi_accounts = Transfer { from: ctx.accounts.user_token_account.to_account_info(), to: ctx.accounts.escrow_token_account.to_account_info(), authority: ctx.accounts.user.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; // Update the USDC balance in the escrow account let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.usdc_balance += adjusted_amount; } "BONK" => { // Transfer BONK to the escrow account using CPI let cpi_accounts = Transfer { from: ctx.accounts.user_token_account.to_account_info(), to: ctx.accounts.escrow_token_account.to_account_info(), authority: ctx.accounts.user.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; // Update the BONK balance in the escrow account let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.bonk_balance += adjusted_amount; } "SEND" => { // Transfer SEND to the escrow account using CPI let cpi_accounts = Transfer { from: ctx.accounts.user_token_account.to_account_info(), to: ctx.accounts.escrow_token_account.to_account_info(), authority: ctx.accounts.user.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; // Update the SEND balance in the escrow account let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.send_balance += adjusted_amount; } _ => return Err(ErrorCode::UnsupportedCurrency.into()), } // Emit an event for the participation emit!(ParticipateEvent { user: ctx.accounts.user.key(), token: token.clone(), amount, challenge_id, player_id, participation_type: participation_type.clone(), }); Ok(()) } /// Handles false settlements by transferring the specified token amount from the escrow account to the user account. pub fn false_settlement( ctx: Context<FalseSettlement>, token: String, amount: u64, txn_id: String, ) -> Result<()> { // Adjust the amount based on the token's decimals let adjusted_amount = match token.as_str() { "SOL" => amount, // Use the amount directly for SOL as it is already in lamports "USDC" => amount .checked_mul(10_u64.pow(6)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust USDC amount "BONK" => amount .checked_mul(10_u64.pow(5)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust BONK amount "SEND" => amount .checked_mul(10_u64.pow(6)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust SEND amount _ => return Err(ErrorCode::UnsupportedCurrency.into()), }; // Ensure the authority is valid require!( ctx.accounts.escrow_account.authority == *ctx.accounts.authority.key, ErrorCode::Unauthorized ); // Get the rent exemption amount for an account with 0 bytes let rent_exemption_amount = Rent::get()?.minimum_balance(0); match token.as_str() { "SOL" => { let escrow_balance = **ctx .accounts .escrow_account .to_account_info() .lamports .borrow(); msg!( "Escrow account SOL balance: {} adjustedAmount {} rentExemption {}", escrow_balance, adjusted_amount, rent_exemption_amount ); // Check if there are enough SOL funds in the escrow account including rent exemption if escrow_balance < adjusted_amount + rent_exemption_amount { return Err(ErrorCode::InsufficientFunds.into()); } // Perform SOL transfer from escrow to user account **ctx .accounts .escrow_account .to_account_info() .try_borrow_mut_lamports()? -= adjusted_amount; **ctx .accounts .user_account .to_account_info() .try_borrow_mut_lamports()? += adjusted_amount; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.sol_balance -= adjusted_amount; // Emit an event for the false settlement emit!(FalseSettlementEvent { user: ctx.accounts.user_account.key(), token: "SOL".to_string(), amount, txn_id }); } "USDC" => { // Perform USDC transfer from escrow to user account using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info(), to: ctx.accounts.user_token_account.to_account_info(), authority: ctx.accounts.escrow_account.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.usdc_balance -= adjusted_amount; // Emit an event for the false settlement emit!(FalseSettlementEvent { user: ctx.accounts.user_account.key(), token: token.clone(), amount, txn_id }); } "BONK" => { // Perform BONK transfer from escrow to user account using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info(), to: ctx.accounts.user_token_account.to_account_info(), authority: ctx.accounts.escrow_account.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.bonk_balance -= adjusted_amount; // Emit an event for the false settlement emit!(FalseSettlementEvent { user: ctx.accounts.user_account.key(), token: token.clone(), amount, txn_id }); } "SEND" => { // Perform SEND transfer from escrow to user account using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info(), to: ctx.accounts.user_token_account.to_account_info(), authority: ctx.accounts.escrow_account.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.send_balance -= adjusted_amount; // Emit an event for the false settlement emit!(FalseSettlementEvent { user: ctx.accounts.user_account.key(), token: token.clone(), amount, txn_id }); } _ => return Err(ErrorCode::UnsupportedCurrency.into()), } Ok(()) } /// Sends tokens from the escrow account to the admin account. pub fn send(ctx: Context<Send>, token: String, amount: u64) -> Result<()> { let adjusted_amount = match token.as_str() { "SOL" => amount, // Use the amount directly for SOL as it is already in lamports "USDC" => amount .checked_mul(10_u64.pow(6)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust USDC amount "BONK" => amount .checked_mul(10_u64.pow(5)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust BONK amount "SEND" => amount .checked_mul(10_u64.pow(6)) .ok_or(ErrorCode::InvalidAmount)?, // Adjust SEND amount _ => return Err(ErrorCode::UnsupportedCurrency.into()), }; // Ensure the authority is valid require!( ctx.accounts.escrow_account.authority == *ctx.accounts.authority.key, ErrorCode::Unauthorized ); match token.as_str() { "SOL" => { // Check if there are enough SOL funds in the escrow account if **ctx .accounts .escrow_account .to_account_info() .lamports .borrow() < adjusted_amount { return Err(ErrorCode::InsufficientFunds.into()); } // Perform SOL transfer from escrow to admin account **ctx .accounts .escrow_account .to_account_info() .try_borrow_mut_lamports()? -= adjusted_amount; **ctx .accounts .admin_account .to_account_info() .try_borrow_mut_lamports()? += adjusted_amount; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.sol_balance -= adjusted_amount; // Emit an event for the send operation emit!(SendEvent { to: ctx.accounts.escrow_account.authority, token: "SOL".to_string(), amount, }); } "USDC" => { // Perform USDC transfer from escrow to admin account using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info(), to: ctx.accounts.admin_token_account.to_account_info(), authority: ctx.accounts.escrow_account.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.usdc_balance -= adjusted_amount; // Emit an event for the send operation emit!(SendEvent { to: ctx.accounts.escrow_account.authority, token: token.clone(), amount, }); } "BONK" => { // Perform BONK transfer from escrow to admin account using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info(), to: ctx.accounts.admin_token_account.to_account_info(), authority: ctx.accounts.escrow_account.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.bonk_balance -= adjusted_amount; // Emit an event for the send operation emit!(SendEvent { to: ctx.accounts.escrow_account.authority, token: token.clone(), amount, }); } "SEND" => { // Perform SEND transfer from escrow to admin account using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info(), to: ctx.accounts.admin_token_account.to_account_info(), authority: ctx.accounts.escrow_account.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.send_balance -= adjusted_amount; // Emit an event for the send operation emit!(SendEvent { to: ctx.accounts.escrow_account.authority, token: token.clone(), amount, }); } _ => return Err(ErrorCode::UnsupportedCurrency.into()), } Ok(()) } /// Settles a challenge by transferring the specified token amounts from the escrow account to the winners. pub fn settle_challenge<'a, 'b>( ctx: Context<'_, '_, '_, 'b, SettleChallenge<'b>>, token: String, amounts: Vec<u64>, challenge_id: u64, ) -> Result<()> { // Determine the number of decimals for the token let decimals = match token.as_str() { "SOL" => 9, "USDC" => 6, "BONK" => 5, "SEND" => 6, _ => return Err(ErrorCode::UnsupportedCurrency.into()), }; // Ensure the authority is valid require!( ctx.accounts.escrow_account.authority == *ctx.accounts.authority.key, ErrorCode::Unauthorized ); // Iterate through each winner and transfer the respective amount for (winner, &amount) in ctx.remaining_accounts.iter().zip(amounts.iter()) { let adjusted_amount = if token.as_str() == "SOL" { amount } else { amount .checked_mul(10_u64.pow(decimals as u32)) .ok_or(ErrorCode::InvalidAmount)? }; match token.as_str() { "SOL" => { // Check if there are enough SOL funds in the escrow account if **ctx .accounts .escrow_account .to_account_info() .lamports .borrow() < adjusted_amount { return Err(ErrorCode::InsufficientFunds.into()); } // Perform SOL transfer from escrow to winner **ctx .accounts .escrow_account .to_account_info() .try_borrow_mut_lamports()? -= adjusted_amount; **winner.try_borrow_mut_lamports()? += adjusted_amount; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.sol_balance -= adjusted_amount; } "USDC" => { // Perform USDC transfer from escrow to winner using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info().clone(), to: winner.clone(), authority: ctx.accounts.escrow_account.to_account_info().clone(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.usdc_balance -= adjusted_amount; } "BONK" => { // Perform BONK transfer from escrow to winner using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info().clone(), to: winner.clone(), authority: ctx.accounts.escrow_account.to_account_info().clone(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.bonk_balance -= adjusted_amount; } "SEND" => { // Perform SEND transfer from escrow to winner using CPI let cpi_accounts = Transfer { from: ctx.accounts.escrow_token_account.to_account_info().clone(), to: winner.clone(), authority: ctx.accounts.escrow_account.to_account_info().clone(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, adjusted_amount)?; let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.send_balance -= adjusted_amount; } _ => return Err(ErrorCode::UnsupportedCurrency.into()), } } // Emit an event for the challenge settlement emit!(SettleChallengeEvent { token, amounts, challenge_id, }); Ok(()) } } // Define the account structures and constraints #[derive(Accounts)] pub struct InitializeEscrow<'info> { #[account(init, payer = owner, space = 8 + 8 + 8 + 8 + 8 + 32 + 32 + 32 + 32)] pub escrow_account: Account<'info, EscrowAccount>, #[account(mut)] pub owner: Signer<'info>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct Participate<'info> { #[account(mut)] pub user: Signer<'info>, #[account(mut)] pub user_token_account: Account<'info, TokenAccount>, #[account(mut)] pub escrow_token_account: Account<'info, TokenAccount>, #[account(mut)] pub escrow_account: Account<'info, EscrowAccount>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, } #[derive(Accounts)] pub struct FalseSettlement<'info> { #[account(mut, signer)] pub escrow_account: Account<'info, EscrowAccount>, #[account(mut)] pub escrow_token_account: Account<'info, TokenAccount>, #[account(mut)] pub user_account: AccountInfo<'info>, #[account(mut)] pub user_token_account: Account<'info, TokenAccount>, pub authority: Signer<'info>, pub token_program: Program<'info, Token>, } #[derive(Accounts)] pub struct Send<'info> { #[account(mut, signer)] pub escrow_account: Account<'info, EscrowAccount>, #[account(mut)] pub escrow_token_account: Account<'info, TokenAccount>, #[account(mut)] pub admin_account: AccountInfo<'info>, #[account(mut)] pub admin_token_account: Account<'info, TokenAccount>, pub authority: Signer<'info>, pub token_program: Program<'info, Token>, } #[derive(Accounts)] pub struct SettleChallenge<'info> { #[account(mut, signer)] pub escrow_account: Account<'info, EscrowAccount>, #[account(mut)] pub escrow_token_account: Account<'info, TokenAccount>, #[account(mut)] pub authority: Signer<'info>, pub token_program: Program<'info, Token>, } // Define the escrow account structure #[account] pub struct EscrowAccount { pub sol_balance: u64, pub usdc_balance: u64, pub bonk_balance: u64, pub send_balance: u64, pub authority: Pubkey, pub usdc_mint: Pubkey, pub bonk_mint: Pubkey, pub send_mint: Pubkey, } // Define participation types #[derive(AnchorSerialize, AnchorDeserialize, Clone)] pub enum ParticipationType { SideBet, JoinChallenge, } // Define events for logging #[event] pub struct ParticipateEvent { pub user: Pubkey, pub token: String, pub amount: u64, pub challenge_id: u64, pub player_id: Option<u64>, pub participation_type: ParticipationType, } #[event] pub struct SettleChallengeEvent { pub token: String, pub amounts: Vec<u64>, pub challenge_id: u64, } #[event] pub struct FalseSettlementEvent { pub user: Pubkey, pub token: String, pub amount: u64, pub txn_id: String, } #[event] pub struct SendEvent { pub to: Pubkey, pub token: String, pub amount: u64, } // Define custom error codes #[error_code] pub enum ErrorCode { #[msg("The requested operation is not authorized.")] Unauthorized, #[msg("Insufficient funds to complete the operation.")] InsufficientFunds, #[msg("The specified currency is not supported.")] UnsupportedCurrency, #[msg("Failed to perform the transfer.")] TransferFailed, #[msg("Invalid input provided.")] InvalidInput, #[msg("Invalid amount provided.")] InvalidAmount, #[msg("Failed to get current time.")] TimeError, #[msg("Invalid admin wallet public key.")] InvalidAdminWallet, #[msg("Invalid mint address provided.")] InvalidMint, }
Editor is loading...
Leave a Comment