2

I am facing an issue when using solana-sdk to call a function on my Solana program.

I have the following Program, made using Anchor:

#[program]
pub mod myprogram {
    // does not write on the SomeAccount account
    pub fn do_smth(
        ctx: Context<DoSmth>,
        data: Vec<u8>,
        signatures: Vec<Vec<u8>>,
    ) -> Result<()> {
    ....
    }
}

#[derive(Accounts)]
pub struct DoSmth<'info> {
    // #[account(mut)]
    pub some_accounts: Account<'info, SomeAccounts>,
}

#[account]
pub struct SomeAccounts{
    pub some_accounts: Vec<Vec<u8>>, // 4 + (64 bytes per public key * 20 public keys) = 1284 bytes
}

I can successfully call my "initialize" function that set the SomeAccounts account.

Then, in another Rust crate, I'm doing this in order to build the payload:

#[derive(BorshSerialize, BorshDeserialize)]
struct DoSmth{
    data: Vec<u8>,
    signatures: Vec<Vec<u8>>,
}

fn build_payload(
    data: Vec<u8>,
    some_other_accounts: Vec<Pubkey>,
    program_id: &Pubkey,
    signatures: Vec<Signature>,
    signer: &Pubkey,
) -> Result<Instruction> {

    let some_accounts_pubkey: [u8; 32] = bs58::decode(SOME_ACCOUNT_ADDRESS)
        .into_vec()?
        .try_into()
        .expect("44 caracters long base58 string should be 32 bytes long.");

    let some_accounts_account: AccountMeta = AccountMeta {
        pubkey: Pubkey::from(some_accounts_pubkey),
        is_signer: false,
        is_writable: false,
    };

    let mut data = vec![0, inner_data.len() as u8];
    data.extend_from_slice(&inner_data);
    data.push(signatures.len() as u8);
    for sig in signatures {
        data.extend_from_slice(&sig);
    }

    let do_smth = DoSmth {
        data,
        signatures: signatures.map(|s| s.to_bytes()).collect()
    };
    let instruction = Instruction::new_with_borsh(*program_id, &data, vec![some_other_accounts, AccountMeta::new(*signer, true)]);
    Ok(instruction)

    // Also, I tried this:

    // let mut i_data = vec![0, data.len() as u8];
    // i_data.extend_from_slice(&data);
    // i_data.push(signatures.len() as u8);
    // for sig in signatures {
    //     i_data.extend_from_slice(&sig);
    // }
    // let instruction = Instruction {
    //    program_id: *program_id,
    //    accounts: vec![some_other_accounts, AccountMeta::new(*signer, true)],
    //    i_data
    // };
    // Ok(instruction)
}

Then, I sign and execute the payload this way:

let mut transaction = Transaction::new_with_payer(&[payload], Some(&keypair.pubkey()));
let block_hash = rpc_client.get_latest_blockhash().await.unwrap();
transaction.try_sign(&[&keypair], block_hash).unwrap();
let signature = rpc_client.send_and_confirm_transaction_with_spinner(&transaction).await.unwrap();

Now, when executing this, I encounter the following error:

1: Program ... invoke [1]
2: Program log: AnchorError occurred. Error code: InstructionFallbackNotFound. Error Number; 101. Error Message: Fallback functions are not supported.

From what I've read, it mean the instruction didn't match my do_smth, so it tried to find a fallback function that I did not do. I don't understand what I am missing for my payload to match the right instruction. I verified that the public keys are valid, I verified that my signer account is funded, so i believe the issue lies in my code that uses solana_sdk, but I cannot find the actual issue

1 Answer 1

2

I guess you are missing the 8byte instruction discriminator: Anchor matches these against the available functions. If it does not find any it will end up here:

    // fallback instruction handler as workaround to anchor instruction discriminator check
    pub fn fallback<'info>(
        program_id: &Pubkey,
        accounts: &'info [AccountInfo<'info>],
        data: &[u8],
    ) -> Result<()> {
        let instruction = TransferHookInstruction::unpack(data)?;

        // match instruction discriminator
        match instruction {
// Here could be some of your functions for example
            TransferHookInstruction::Execute { amount } => {
                
            }
            _ => return Err(ProgramError::InvalidInstructionData.into()),
        }
    }

So you can try this to figure out whats happening.

Something like this might help to add the correct data:

pub fn get_function_hash(namespace: &str, name: &str) -> [u8; 8] {
    let preimage = format!("{}:{}", namespace, name);
    let mut sighash = [0u8; 8];
    sighash.copy_from_slice(
        &anchor_lang::solana_program::hash::hash(preimage.as_bytes()).to_bytes()
            [..8],
    );
    sighash
}

See also here: Anchor Instruction Discriminators

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.