2

Assume that I have the below program and I want to restrict the caller of hello function pay at least X number of lamports to be able to call it.

declare_id!("some-id");

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

    pub fn hello(ctx: Context<Hello>) -> Result<()> {
        Ok(())
    }
}

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

As far as I'm familiar with anchor, I think I have to set constraints on Hello account. But I don't know how to set such constraint for amount of the receiving transaction which is invoking our function.

3 Answers 3

3

The simplest way to enforce a "fee" to execute the instruction would be to add a simple system program transfer to it, like the following:

system_program::transfer(
        CpiContext::new(
            ctx.accounts.system_program.to_account_info(),
            system_program::Transfer {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.receiver.to_account_info(),
            },
        ),
        fee,
    )?;

This will not stop someone from calling the instruction persay, but will require that they pay the fee for the instruction to be executed successfully.

For example: you want to force someone to pay 10 lamports to log "Hello!"

declare_id!("some-id");

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

    pub fn hello(ctx: Context<Hello>) -> Result<()> {
    
        system_program::transfer(
          CpiContext::new(
            ctx.accounts.system_program.to_account_info(),
              system_program::Transfer {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.receiver.to_account_info(),
              },
           ),
          10,
        )?;

        msg!("Hello!");
    
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Hello {
   #[account(mut)]
   pub receiver: Account<'info, (some account struct)> (or AccountInfo<'info>)
   #[account(mut)]
   pub payer: Signer<'info>,
   pub system_program: Program<'info, System>,
}

If you want to get closer to the desired effect, you can encapsulate the core logic of the program in a separate utility function, and have the execution of that function be contingent on the transfer occurring.

3

You're not actually making a constraint specifying the lamport transfer, so much as you are telling your instruction to send the lamports from one account to another.

Your Hello context will require that you specify the sender of the lamports (as a Signer) and a receiver of the lamports. Both will need a mut constraint since we are modifying them. You'll also need to include the system_program which will handle the transfer. Here's an example:

use anchor_lang::prelude::*;
use solana_program::system_instruction;
// ...

#[derive(Accounts)]
pub struct Hello<'info> {
    #[account(mut)]
    pub from: Signer<'info>,
    #[account(mut)]
    pub to: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}


pub fn hello(ctx: Context<Hello>, amount: u64) -> Result<()> {
    let from_account = &ctx.accounts.from;
    let to_account = &ctx.accounts.to;

    // Create the transfer instruction
    let transfer_instruction = system_instruction::transfer(from_account.key, to_account.key, amount);

    // Invoke the transfer instruction
    anchor_lang::solana_program::program::invoke_signed(
        &transfer_instruction,
        &[
            from_account.to_account_info(),
            to_account.clone(),
            ctx.accounts.system_program.to_account_info(),
        ],
        &[],
    )?;

    Ok(())
}

Here's a guide that walks through SOL and Token transfers that could be help.

3

The answers above are correct, but you dont need to use this invoke instructions. Anchor is a beautiful framework that came to help us. Below is an example of cpi transfer.

A transfer cpi without signer seeds:

    let accounts = Transfer {
        from: self.from.to_account_info(),
        to: self.to.to_account_info()
    };

    let ctx = CpiContext::new(
        self.system_program.to_account_info(),
        accounts
    );

    transfer(ctx, fee)
    }

make sure you import:

  use anchor_lang::{prelude::*, system_program::{Transfer, transfer}};

When you are importing this, Under the hood the transfer will do that invoke instruction for you:

pub fn transfer<'info>(
ctx: CpiContext<'_, '_, '_, 'info, Transfer<'info>>,
lamports: u64,
 ) -> Result<()> {
let ix = crate::solana_program::system_instruction::transfer(
    ctx.accounts.from.key,
    ctx.accounts.to.key,
    lamports,
);
crate::solana_program::program::invoke_signed(
    &ix,
    &[ctx.accounts.from, ctx.accounts.to],
    ctx.signer_seeds,
)
.map_err(Into::into)
}

I believe that your code will become significantly cleaner and more understandable.

Have a great day.

2
  • I don't want a way to transfer SOL, I need to check how much SOL the caller of my instruction is paying to my instruction. Commented Jan 11 at 10:12
  • As you can see on of the arguments of the transfer cpi is lamports. "pub fn transfer<'info>(ctx: CpiContext<', ', '_, 'info, Transfer<'info>>, lamports: u64)" This lamports is the amount that the caller will pay to call your instruction. You can define this anywhere, directly on the function, or in a struct. Think about this like, in order to call your instruction the user will have to make a deposit(your fee). This will fix your issue " function pay at least X number of lamports to be able to call it." Commented Jan 11 at 23:40

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.