I know this question is repeated again, but I look over all the StackExchange to solve the CPI problem, but none is working for me.
I created a smart contract in which the user will toss a coin and x amount of sol will be transferred to the smart contract's well
account, probably, but when I call the distribute_rewards
function, I'm getting an error of "Cross-program invocation with an unauthorised signer or writable account," and I don't know what's the problem and have been stuck on this problem for two days. I'm testing it out in the Solana playground, and if I make the well as signer, then I'm getting an error of Testing 'initialise'...❌ Test 'initialise' failed:
Reason: Error processing Instruction 2.
Code:
use anchor_lang::prelude::*;
use solana_program::sysvar::clock::Clock;
use anchor_lang::system_program::{self, Transfer};
declare_id!("7Z3D7VoA53M4TS1UbgwWyT8AuTRSb9LYziNhaNNXzdGB");
#[program]
pub mod wishing_well {
use super::*;
pub fn initialize(ctx: Context<Initialize>, initial_toss_price: u64) -> Result<()> {
let well = &mut ctx.accounts.well;
well.owner = *ctx.accounts.user.key;
well.total_sol_collected = 0;
well.toss_price = initial_toss_price;
well.last_distribution_time = Clock::get()?.unix_timestamp;
Ok(())
}
pub fn toss_coin(ctx: Context<TossCoin>, amount: u64) -> Result<()> {
// Ensure the amount sent is sufficient
let well_info = ctx.accounts.well.to_account_info(); // Create an immutable reference to the well account
let well = &mut ctx.accounts.well; // Create a mutable reference
require!(amount >= well.toss_price, WishingWellError::InsufficientFunds);
// Transfer SOL from the user to the well account
let cpi_accounts = anchor_lang::system_program::Transfer {
from: ctx.accounts.user.to_account_info(),
to: well_info, // Use the immutable reference here
};
let cpi_program = ctx.accounts.system_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
anchor_lang::system_program::transfer(cpi_context, amount)?;
// Now you can safely use the mutable reference to well
well.total_sol_collected += amount;
//@Dev Storing the time and user adrress in participant vector
let participant = Participant {
key: ctx.accounts.user.key(),
timestamp: Clock::get()?.unix_timestamp,
};
well.participants.push(participant);
// well.participants.push(ctx.accounts.user.key());
Ok(())
}
pub fn distribute_rewards(ctx: Context<DistributeRewards>) -> Result<()> {
let mut fees;
let first_prize_u64:u64;
let second_prize_u64:u64;
let third_prize_u64:u64;
{
let well = &mut ctx.accounts.well;
let current_time = Clock::get()?.unix_timestamp;
require!(
current_time >= well.last_distribution_time + (3 * 60),
WishingWellError::DistributionTimeError
);
require!(well.participants.len() >= 3, WishingWellError::NotEnoughParticipants);
// Calculate fees first
fees = (well.total_sol_collected * 35) / 1000; // 3.5% fees
well.total_sol_collected -= fees;
// Use fixed-point arithmetic for more precise calculations
let total_sol_scaled = well.total_sol_collected * 10000000;
let first_prize = total_sol_scaled * 50 / 100;
let second_prize = total_sol_scaled * 30 / 100;
let third_prize = total_sol_scaled * 20 / 100;
// Convert back to u64
first_prize_u64 = first_prize / 10000000;
second_prize_u64 = second_prize / 10000000;
third_prize_u64 = third_prize / 10000000;
// Ensure total rewards equal total collected minus fees
let total_rewards = first_prize_u64 + second_prize_u64 + third_prize_u64;
require!(
total_rewards <= well.total_sol_collected,
WishingWellError::RewardCalculationError
);
// Calculate remaining sol after fees and rewards
let remaining_sol = well.total_sol_collected - total_rewards;
let fees = (remaining_sol * 35) / 1000; // 3.5% fees
// well.total_sol_collected = remaining_sol - fees;
well.participants.clear();
well.last_distribution_time = current_time;
}
let well_account_info = ctx.accounts.well.to_account_info();
{
// Transfer fees to the owner's account using CPI
let cpi_accounts_fee = Transfer {
from: well_account_info.clone(),
to: ctx.accounts.owner.to_account_info(),
};
let cpi_context_fee = CpiContext::new(ctx.accounts.system_program.to_account_info(), cpi_accounts_fee);
system_program::transfer(cpi_context_fee, fees)?;
}
let cpi_program = ctx.accounts.system_program.to_account_info();
// {
// // Transfer rewards to each winner using CPI
// // First winner transfer
// let cpi_accounts_first = Transfer {
// from: well_account_info.clone(),
// to: ctx.accounts.first_winner.to_account_info(),
// };
// let cpi_context_first = CpiContext::new(cpi_program.clone(), cpi_accounts_first);
// system_program::transfer(cpi_context_first, first_prize_u64)?;
// // Log first winner
// ctx.accounts.well.winners.push(Winner {
// key: ctx.accounts.first_winner.key(),
// amount: first_prize_u64,
// });
// }
// {
// // Second winner transfer
// let cpi_accounts_second = Transfer {
// from: well_account_info.clone(),
// to: ctx.accounts.second_winner.to_account_info(),
// };
// let cpi_context_second = CpiContext::new(cpi_program.clone(), cpi_accounts_second);
// system_program::transfer(cpi_context_second, second_prize_u64)?;
// // Log second winner
// ctx.accounts.well.winners.push(Winner {
// key: ctx.accounts.second_winner.key(),
// amount: second_prize_u64,
// });
// }
// {
// // Third winner transfer
// let cpi_accounts_third = Transfer {
// from: well_account_info.clone(),
// to: ctx.accounts.third_winner.to_account_info(),
// };
// let cpi_context_third = CpiContext::new(cpi_program, cpi_accounts_third);
// system_program::transfer(cpi_context_third, third_prize_u64)?;
// // Log third winner
// ctx.accounts.well.winners.push(Winner {
// key: ctx.accounts.third_winner.key(),
// amount: third_prize_u64,
// });
// }
Ok(())
}
pub fn change_toss_price(ctx: Context<ChangeTossPrice>, new_price: u64) -> Result<()> {
let well = &mut ctx.accounts.well;
well.toss_price = new_price;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 32 + 8 + 8 + 8 + 32 * 100)]
pub well: Account<'info, WishingWell>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct TossCoin<'info> {
#[account(mut)]
pub well: Account<'info, WishingWell>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct DistributeRewards<'info> {
#[account(mut)]
pub well: Account<'info, WishingWell>,
#[account(mut)]
pub first_winner: AccountInfo<'info>,
#[account(mut)]
pub second_winner: AccountInfo<'info>,
#[account(mut)]
pub third_winner: AccountInfo<'info>,
#[account(mut)]
pub owner: Signer<'info>, // Add the owner field here
/// CHECK: Safe because this is the system program
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct ChangeTossPrice<'info> {
#[account(mut, has_one = owner)]
pub well: Account<'info, WishingWell>,
pub owner: Signer<'info>,
}
#[account]
pub struct WishingWell {
pub owner: Pubkey,
pub total_sol_collected: u64,
pub toss_price: u64,
pub last_distribution_time: i64,
// pub participants: Vec<Pubkey>,
pub participants: Vec<Participant>,
pub winners: Vec<Winner>, // New vector for storing winners
}
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct Participant {
pub key: Pubkey,
pub timestamp: i64,
}
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct Winner {
pub key: Pubkey,
pub amount: u64,
}
#[error_code]
pub enum WishingWellError {
#[msg("Insufficient funds sent.")]
InsufficientFunds,
#[msg("Distribution can only happen every 3 days.")]
DistributionTimeError,
#[msg("Not enough participants for a draw.")]
NotEnoughParticipants,
#[msg("Reward calculation error.")]
RewardCalculationError,
}