Untitled
unknown
plain_text
a year ago
27 kB
15
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