1

Solana has the invoke function that can be used to send a transaction from one Solana program to another. Looking at the documentation for this method here, we see:

pub fn invoke(
    instruction: &Instruction, 
    account_infos: &[AccountInfo<'_>]
) -> ProgramResult

It basically contains the instructions and the accounts that the instruction will touch. What I am missing now is, how is this call signed? Because I believe any call to a Solana program must come in the form of a signed transaction, but in this case, it is not clear who is actually signing the transactions that gets sent when invoke is called

1 Answer 1

1

The invoke function does a CPI to an on-chain instruction of an already deployed Program. It takes as argument an Instruction and Account_infos. The invoked instruction is signed through the passed in AccountInfos. When Instruction 1 does a CPI, the AccountInfos passed into the CPI must come from Instruction 1. The CPI expects a particular AccountInfo(s) to sign the instruction(Order is important). So when passing the AccountInfo(s) for the CPI, make sure that you follow the correct order and also make sure that the necessary AccountInfo(s) that will sign for the CPI were passed in as a Signer in Instruction 1. Hence the CPI would fail if you passed the AccountInfo(s) in the correct order but the AccountInfo(s) expected to sign the CPI was not passed as a Signer in Instruction 1.

Let me demonstrate with an example

   #[derive(Accounts)]
    pub struct InstructionSuccess<'info>{

    #[account(mut)]
    pub alice: Signer<'info>,// alice is passed as a signer so that it can sign the invoked instruction

    #[account(mut)]
    pub bob: AccountInfo<'info>,

    #[account(mut)]
    pub payer: Signer<'info>,// Signs this instruction
}
#[derive(Accounts)]
pub struct InstructionFailure<'info>{

    #[account(mut)]
    pub alice: AccountInfo<'info>,// alice is not passed as a signer here

    #[account(mut)]
    pub bob: AccountInfo<'info>,// bob is also not passed as a signer hence no signer available to sign the invoked instruction

    #[account(mut)]
    pub payer: Signer<'info>,// Signs this instruction
}

#[derive(Accounts)]
pub struct InstructionAnotherFailure<'info>{

    #[account(mut)]
    pub alice: AccountInfo<'info>,

    #[account(mut)]
    pub bob: Signer<'info>,// the receiving account signs the instruction but is not a valid signer

    #[account(mut)]
    pub payer: Signer<'info>,// Signs this instruction
}


   pub fn instruction_is_successful(ctx: Context<InstructionSuccess>, amount: u64) -> Result<()> {
       // Does some other stuff here

    let sol_transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
        ctx.accounts.alice.key,
        ctx.accounts.bob.key,
        amount
    );

    invoke(
        &sol_transfer_ix,
        &[
            ctx.accounts.alice.to_account_info(),
            ctx.accounts.bob.to_account_info(),
            ctx.accounts.system_program.to_account_info(),
        ]
    )?;

}


   pub fn instruction_is_not_successful(ctx: Context<InstructionFailure>, amount: u64) -> Result<()> {
       // Does some other stuff here

    let sol_transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
        ctx.accounts.alice.key,
        ctx.accounts.bob.key,
        amount
    );

    invoke(
        &sol_transfer_ix,
        &[
            ctx.accounts.alice.to_account_info(),
            ctx.accounts.bob.to_account_info(),
            ctx.accounts.system_program.to_account_info(),
        ]
    )?;

}

   pub fn instruction_is_still_not_successful(ctx: Context<InstructionAnotherFailure>, amount: u64) -> Result<()> {
       // Does some other stuff here

    let sol_transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
        ctx.accounts.alice.key,
        ctx.accounts.bob.key,
        amount
    );

    invoke(
        &sol_transfer_ix,
        &[
            ctx.accounts.alice.to_account_info(),
            ctx.accounts.bob.to_account_info(),
            ctx.accounts.system_program.to_account_info(),
        ]
    )?;

}

This code shows three similar instructions which invoke the Solana transfer instruction in which the payer is a third party that pays for each instruction. The Solana transfer instruction expects two accounts in the exact order in which the sending account(Alice) is the first and the receiving account(Bob) is the second. The first Account(alice) must be a Signer because only the first account can sign to reduce lamports of it's account, hence the solana runtime will check that the alice always signs the transaction.

However the instruction_is_successful, instruction_is_not_successful and instruction_is_still_not_successful instructions are signed respectively by the payer. But when the Solana transfer is invoked, the payer is not passed as an account, instead alice and bob accounts are passed in as the accounts for the invoked instruction.

The instruction_is_successful will succeed because alice was passed as a Signer for the instruction_is_successful instruction and consequently alice will also be passed as a Signer in the invoked solana transfer instruction(alice will sign the invoked solana transfer instruction while payer signs the instruction_is_successful instruction).

The Second scenario is the case for the instruction_is_not_successful instruction, the payer will sign the instruction_is_not_successful instruction but when the Solana transfer instruction is invoked, alice and bob will be passed as the necessary accounts and since neither alice nor bob was passed as a Signer in the instruction_is_not_successful instruction, the Solana transfer instruction will not have a Signer and hence the invoke call would fail because the invoked Solana transfer instruction does not have a Signer(the invoked call fails because neither alice nor bob is passed as a signer).

The third Scenario is a mix of the previous scenarios, the payer signs the instruction_is_still_not_successful instruction. When the Solana transfer instruction is invoked, alice and bob are passed as accounts but since bob was passed as a Signer in the instruction_is_still_not_successful instruction, hence the invoked instruction has a Signer(bob will sign the invoked solana transfer instruction). However, the invoked Solana transfer instruction still fails because bob is not a Valid Signer for the instruction to be successful, the solana transfer instruction checks that the sending account(alice) must sign the instruction.(Bob signs the invoked instruction but the instruction fails because the invoked instruction expects alice to be a Signer)

3
  • Your answer is not clear. so who signs the transaction that invoke sends to another Solana program? Also is there a special order to how the accountInto needs to be passed when doing CPI? Commented Sep 21, 2022 at 6:59
  • you wrote "When Instruction 1 does a CPI" but instruction do not do CPIs, programs make CPI calls. What do you mean by "When Instruction 1 does a CPI" Commented Sep 21, 2022 at 7:23
  • I have edited my answer to explain more in-depth using some code alongside it. My statement "When Instruction 1 does a CPI" refers to when an instruction in a program calls an instruction of another program Commented Sep 21, 2022 at 21:04

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.