Borrow Checker Issue with Mutable and Immutable Access to Account, RUST

Hi everyone I’m developing a Solana smart contract/Program written in Rust for a lottery application. I’m encountering a borrow checker issue specifically in the claim_prize function. The error indicates that ctx.accounts.lottery is being borrowed mutably and immutably at the same time.

Here’s the relevant code snippet:

pub fn claim_prize(ctx: Context<ClaimPrize>, _lottery_id: u32, _ticket_id: u32) -> Result<()> {
    // Immutable borrow for checks
    let ticket = &ctx.accounts.ticket;
    let ticket_id = ticket.id;

    let lottery = &ctx.accounts.lottery;

    // Check if the prize has already been claimed
    if lottery.claimed {
        return Err(LotteryError::AlreadyClaimed.into());
    }

    // Check if the provided ticket is a valid winner
    match lottery.winner_id {
        Some(winner_id) => {
            if winner_id != ticket_id {
                return Err(LotteryError::InvalidWinner.into());
            }
        }
        None => return Err(LotteryError::WinnerNotChosen.into()),
    };

    // Calculate the prize amount
    let prize = lottery
        .ticket_price
        .checked_mul(lottery.last_ticket_id.into())
        .ok_or(LotteryError::InvalidWinner)?;

    // Now that we've completed the immutable borrow, we can mutably borrow the lottery account
    let mut lottery = &mut ctx.accounts.lottery;

    // Update the lottery state
    lottery.claimed = true;

    // Perform the token transfer
    let cpi_accounts = Transfer {
        from: ctx.accounts.lottery_token_account.to_account_info(),
        to: ctx.accounts.winner_token_account.to_account_info(),
        authority: ctx.accounts.lottery.to_account_info(), // No mutable borrow here
    };
    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
    token::transfer(cpi_ctx, prize)?;

    msg!(
        "{} claimed {} lamports from lottery id {} with ticket id {}",
        ctx.accounts.authority.key(),
        prize,
        lottery.id,
        ticket_id
    );

    Ok(())
}

this is the error :

    Building...
error[E0502]: cannot borrow `ctx.accounts.lottery` as immutable because it is also borrowed as mutable
   --> src/lib.rs:151:20
    |
142 |     let mut lottery = &mut ctx.accounts.lottery;
    |                       ------------------------- mutable borrow occurs here
...
error[E0502]: cannot borrow `ctx.accounts.lottery` as immutable because it is also borrowed as mutable
   --> src/lib.rs:153:20
    |
144 |     let mut lottery = &mut ctx.accounts.lottery;
    |                       ------------------------- mutable borrow occurs here
...
153 |         authority: ctx.accounts.lottery.to_account_info(), // No mutable borrow here
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
...
163 |         lottery.id,
    |         ------- mutable borrow later used here
 
warning: `Lottery` (lib) generated 2 warnings

I’ve been trying to fix it, but I haven’t been able to, even with ChatGPT. I feel like I’ve been going in circles

I’m trying to “Build” and “Deploy” the Program in the Solana Blockchain, but it’s not “Building” because of the Mutable and InMutable error.

this is the complete code :

use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, Transfer};
use solana_program::hash::hash;
use std::convert::TryFrom;

declare_id!("---EXAMPLE-ID_REPLACE_BY_PROGRAM-ID---");

mod constants {
    pub const MASTER_SEED: &str = "master";
    pub const LOTTERY_SEED: &str = "lottery";
    pub const TICKET_SEED: &str = "ticket";
}

mod error {
    use anchor_lang::prelude::*;

    #[error_code]
    pub enum LotteryError {
        #[msg("The winner has already been chosen.")]
        WinnerAlreadyExists,
        #[msg("No tickets have been purchased.")]
        NoTickets,
        #[msg("The prize has already been claimed.")]
        AlreadyClaimed,
        #[msg("The winner ID is invalid.")]
        InvalidWinner,
        #[msg("The winner has not been chosen yet.")]
        WinnerNotChosen,
    }
}

use constants::*;
use error::LotteryError;

#[program]
pub mod lottery_program {
    use super::*;

    pub fn init_master(ctx: Context<InitMaster>) -> Result<()> {
        Ok(())
    }

    pub fn create_lottery(ctx: Context<CreateLottery>, ticket_price: u64) -> Result<()> {
        let master = &mut ctx.accounts.master;
        let lottery = &mut ctx.accounts.lottery;

        master.last_id += 1;

        lottery.id = master.last_id;
        lottery.authority = ctx.accounts.authority.key();
        lottery.ticket_price = ticket_price;

        msg!("Created lottery: {}", lottery.id);
        msg!("Authority: {}", lottery.authority);
        msg!("Ticket price: {}", lottery.ticket_price);

        Ok(())
    }

    pub fn buy_ticket(ctx: Context<BuyTicket>, lottery_id: u32) -> Result<()> {
        let lottery = &mut ctx.accounts.lottery;
        let ticket = &mut ctx.accounts.ticket;
        let buyer = &ctx.accounts.buyer;

        if lottery.winner_id.is_some() {
            return Err(LotteryError::WinnerAlreadyExists.into());
        }

        let cpi_accounts = Transfer {
            from: ctx.accounts.buyer_token_account.to_account_info(),
            to: ctx.accounts.lottery_token_account.to_account_info(),
            authority: ctx.accounts.buyer.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, lottery.ticket_price)?;

        lottery.last_ticket_id += 1;

        ticket.id = lottery.last_ticket_id;
        ticket.lottery_id = lottery_id;
        ticket.authority = buyer.key();

        msg!("Ticket id: {}", ticket.id);
        msg!("Ticket authority: {}", ticket.authority);

        Ok(())
    }

    pub fn pick_winner(ctx: Context<PickWinner>, _lottery_id: u32) -> Result<()> {
        let lottery = &mut ctx.accounts.lottery;

        if lottery.winner_id.is_some() {
            return Err(LotteryError::WinnerAlreadyExists.into());
        }
        if lottery.last_ticket_id == 0 {
            return Err(LotteryError::NoTickets.into());
        }

        let clock = Clock::get()?;
        let pseudo_random_number = ((u64::from_le_bytes(
            <[u8; 8]>::try_from(&hash(&clock.unix_timestamp.to_be_bytes()).to_bytes()[..8])
                .unwrap(),
        ) * clock.slot)
            % u32::MAX as u64) as u32;

        let winner_id = (pseudo_random_number % lottery.last_ticket_id) + 1;
        lottery.winner_id = Some(winner_id);

        msg!("Winner id: {}", winner_id);

        Ok(())
    }

pub fn claim_prize(ctx: Context<ClaimPrize>, _lottery_id: u32, _ticket_id: u32) -> Result<()> {
    let lottery = &mut ctx.accounts.lottery; // Mutable borrow
    let ticket = &ctx.accounts.ticket; // Immutable borrow

    if lottery.claimed {
        return Err(LotteryError::AlreadyClaimed.into());
    }

    let ticket_id = ticket.id;

    match lottery.winner_id {
        Some(winner_id) => {
            if winner_id != ticket_id {
                return Err(LotteryError::InvalidWinner.into());
            }
        }
        None => return Err(LotteryError::WinnerNotChosen.into()),
    }

    let prize = lottery
        .ticket_price
        .checked_mul(lottery.last_ticket_id.into())
        .ok_or(LotteryError::InvalidWinner)?;

    // Update lottery before creating CPI context
    lottery.claimed = true;

    // Create CPI context
    let cpi_accounts = Transfer {
        from: ctx.accounts.lottery_token_account.to_account_info(),
        to: ctx.accounts.winner_token_account.to_account_info(),
        authority: ctx.accounts.lottery.to_account_info(),
    };
    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

    // Call the CPI function
    token::transfer(cpi_ctx, prize)?;

    msg!(
        "{} claimed {} lamports from lottery id {} with ticket id {}",
        ctx.accounts.authority.key(),
        prize,
        lottery.id,
        ticket_id
    );

    Ok(())
}




}

#[derive(Accounts)]
pub struct InitMaster<'info> {
    #[account(
        init,
        payer = payer,
        space = 8 + 4,
        seeds = [MASTER_SEED.as_bytes()],
        bump,
    )]
    pub master: Account<'info, Master>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct CreateLottery<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + 4 + 32 + 8 + 4 + 1 + 4 + 1,
        seeds = [LOTTERY_SEED.as_bytes(), &(master.last_id + 1).to_le_bytes()],
        bump,
    )]
    pub lottery: Account<'info, Lottery>,
    #[account(
        mut,
        seeds = [MASTER_SEED.as_bytes()],
        bump,
    )]
    pub master: Account<'info, Master>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(lottery_id: u32)]
pub struct BuyTicket<'info> {
    #[account(
        mut,
        seeds = [LOTTERY_SEED.as_bytes(), &lottery_id.to_le_bytes()],
        bump,
    )]
    pub lottery: Account<'info, Lottery>,
    #[account(
        init,
        payer = buyer,
        space = 8 + 4 + 4 + 32,
        seeds = [
            TICKET_SEED.as_bytes(),
            lottery.key().as_ref(),
            &(lottery.last_ticket_id + 1).to_le_bytes()
        ],
        bump,
    )]
    pub ticket: Account<'info, Ticket>,
    #[account(mut)]
    pub buyer: Signer<'info>,
    #[account(mut, constraint = buyer_token_account.owner == buyer.key())]
    pub buyer_token_account: Account<'info, TokenAccount>,
    #[account(mut)]
    pub lottery_token_account: Account<'info, TokenAccount>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
}

#[derive(Accounts)]
#[instruction(lottery_id: u32)]
pub struct PickWinner<'info> {
    #[account(
        mut,
        seeds = [LOTTERY_SEED.as_bytes(), &lottery_id.to_le_bytes()],
        bump,
        has_one = authority,
    )]
    pub lottery: Account<'info, Lottery>,
    pub authority: Signer<'info>,
}

#[derive(Accounts)]
#[instruction(lottery_id: u32, ticket_id: u32)]
pub struct ClaimPrize<'info> {
    #[account(
        mut,
        seeds = [LOTTERY_SEED.as_bytes(), &lottery_id.to_le_bytes()],
        bump,
        has_one = authority,
    )]
    pub lottery: Account<'info, Lottery>,
    #[account(
        seeds = [TICKET_SEED.as_bytes(), lottery.key().as_ref(), &ticket_id.to_le_bytes()],
        bump,
        has_one = authority,
    )]
    pub ticket: Account<'info, Ticket>,
    #[account(mut)]
    pub authority: Signer<'info>,
    #[account(mut, constraint = lottery_token_account.owner == authority.key())]
    pub lottery_token_account: Account<'info, TokenAccount>,
    #[account(mut)]
    pub winner_token_account: Account<'info, TokenAccount>,
    pub token_program: Program<'info, Token>,
}

#[account]
pub struct Master {
    pub last_id: u32,
}

#[account]
pub struct Lottery {
    pub id: u32,
    pub authority: Pubkey,
    pub ticket_price: u64,
    pub last_ticket_id: u32,
    pub winner_id: Option<u32>,
    pub claimed: bool,
}

#[account]
pub struct Ticket {
    pub id: u32,
    pub lottery_id: u32,
    pub authority: Pubkey,
}

i also have a Error.rs file that look like this :

use anchor_lang::prelude::*;

#[error_code]
pub enum LotteryError {
    #[msg("The winner has already been chosen.")]
    WinnerAlreadyExists,
    #[msg("No tickets have been purchased.")]
    NoTickets,
    #[msg("The prize has already been claimed.")]
    AlreadyClaimed,
    #[msg("The winner ID is invalid.")]
    InvalidWinner,
    #[msg("The winner has not been chosen yet.")]
    WinnerNotChosen,
}

and a constants.rs file that look like this :

pub const MASTER_SEED: &str = "master";
pub const LOTTERY_SEED: &str = "lottery";
pub const TICKET_SEED: &str = "ticket";
pub const MEME_COIN_MINT: &str = "Example-Contract-Adress-MEMEcoin"; // replace Address

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật