1

I'm building a Solana Anchor program for a tipping dApp, but I'm encountering an error. Can anyone assist me in troubleshooting and fixing the give_tip function?"

use anchor_lang::{
    prelude::*,
    solana_program::{
        program::invoke, system_instruction::create_account, system_instruction::transfer,
    },
};

// use anchor_lang::system_program::{create_account, CreateAccount};

declare_id!("5bZeT4cdAQH8dN2WW9s2kdzMzxy5oJX4hTProBaSazEn");

pub const PROGRAM_PUBKEY: &str = "program_seed";
pub const TIP_SEED: &str = "tip";

#[error_code]
pub enum TipError {
    #[msg("The tip has already been claimed.")]
    AlreadyClaimed,
}

#[program]
mod tip {
    use super::*;

    pub fn init(_ctx: Context<InitMaster>) -> Result<()> {
        Ok(())
    }

    pub fn give_tip(ctx: Context<GiveTip>, amount: u64) -> Result<()> {
        let tip = &mut ctx.accounts.tip;
        let sender = &ctx.accounts.sender;
        let system_program = &ctx.accounts.system_program;
        let pub_key = &tip.key();

        msg!("Program invoked. Creating a system account...");
        msg!("New public key will be: {}", tip.key().to_string());

        // Ensure the tip hasn't been claimed already
        if tip.amount > 0 {
            return Err(TipError::AlreadyClaimed.into());
        }

        // The minimum lamports for rent exemption
        let lamports = (Rent::get()?).minimum_balance(0);
        msg!("Minimum lamports for rent exemption: {}", lamports);

        invoke(
            &create_account(sender.key, pub_key, lamports, 0, system_program.key),
            &[
                sender.to_account_info(),
                tip.to_account_info(),
                system_program.to_account_info(),
            ],
        )?;

        msg!("Account created successfully.");

        // Transfer SOL to Tip PDA
        invoke(
            &transfer(&sender.key(), &tip.key(), amount),
            &[
                sender.to_account_info(),
                tip.to_account_info(),
                ctx.accounts.system_program.to_account_info(),
            ],
        ).map_err(|e| {
            msg!("Error transferring SOL: {}", e);
            e
        })?;

        tip.amount = amount;
        Ok(())
    }
 }

#[derive(Accounts)]
pub struct InitMaster {}
 

#[derive(Accounts)]
#[instruction(receiver:Pubkey,amount: u64)]
pub struct GiveTip<'info> {
    #[account( 
     init_if_needed,
     payer = sender,
     space = 8 + 32 + 8 + 1,
     seeds = ["program_seed".as_bytes(), &receiver.to_bytes() ,"tip".as_bytes()],
     bump,
 )]
    pub tip: Account<'info, Tip>,
    #[account(mut)]
    pub sender: Signer<'info>,
    pub system_program: Program<'info, System>,
}


#[account]
pub struct Tip {
    pub to: Pubkey,
    pub amount: u64,
}

1 Answer 1

1

you are trying to mix native code with anchor code and I think that is what is causing you the errors.

Using anchor constraints such as init_if_needed will create this account if it's not already initialized and as such, there is no need to invoke the system program create account(tip) in your give_tip instruction.

For the transfer, you could also try using anchor's transfer

Here's the updated code snippet using anchor constraints. Take note that there is a new account to because the accounts that you are interacting with should be provided in the anchor #[derive(Accounts)] context

use anchor_lang::{
    prelude::*,
    system_program::{self, Transfer as SOLTransfer},
};

// --- snipped 

#[program]
mod tip {
    use super::*;

    pub fn give_tip(ctx: Context<GiveTip>, amount: u64) -> Result<()> {
        let tip = &mut ctx.accounts.tip;

        // Ensure the tip hasn't been claimed already
        if tip.amount > 0 {
            return Err(TipError::AlreadyClaimed.into());
        }

        let cpi_program = ctx.accounts.system_program.to_account_info();
        let protocol_cpi_accounts = SOLTransfer {
            from: ctx.accounts.sender.to_account_info(),
            to: ctx.accounts.to.to_account_info(),
        };
        let protocol_cpi_context = CpiContext::new(cpi_program.clone(), protocol_cpi_accounts);
        system_program::transfer(protocol_cpi_context, tip.amount)?;

        tip.amount = amount;

        Ok(())
    }
}

#[derive(Accounts)]
#[instruction(receiver:Pubkey,amount: u64)]
pub struct GiveTip<'info> {
    #[account(
        init_if_needed,
        payer = sender,
        space = 8 + 32 + 8 + 1,
        seeds = ["program_seed".as_bytes(), &receiver.to_bytes(), "tip".as_bytes()],
        bump
    )]
    pub tip: Account<'info, Tip>,

    #[account(mut)]
    pub sender: Signer<'info>,

    /// CHECK: checked by sender
    #[account(mut)]
    pub to: SystemAccount<'info>, // recipient address

    pub system_program: Program<'info, System>,
}

// --- snipped 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.