1

Anymore has some resources or something on how to implement borsh deserialization for Solana instruction data? I'm having the issue that I don't know the length so I get an error when trying to call try_from_slice any way on how to call the try_from_slice_unchecked.

Lets take this as a minimal example:

#[derive(BorshSchema, BorshDeserialize, Debug)]
struct Payload {
    purchase_qty: u64,
    expected_price: u64,
    seller: String,
}

let (&tag, rest) = input.split_first().ok_or(Error::Unexpected(format!("Invalid Instruction")))?;

let data = Payload::try_from_slice(rest).unwrap();

where the last command fails with the following input data 70c23f6334935530e803000000000000ff47030000000000057a4e3d6acf2f5218f24947801d10baadcc78e1abf15d838e0b4f46d6013288

i know there is the function try_from_slice_unchecked but its only available inside the solana-crates can i somehow implement it also or parse the instruction-data more generic?

1
  • If you're trying to deserialize account data then you must first remove the descriminator from the payload.
    – Meow
    Commented Mar 16, 2023 at 19:49

3 Answers 3

4

If that Payload struct is the only input that your program can receive, then this should work:

#[derive(BorshDeserialize, BorshSerialize, Debug)]
struct Payload {
    purchase_qty: u64,
    expected_price: u64,
    seller: String,
}


pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {

    match Payload::try_from_slice(&instruction_data) {
        Ok(payload) => your_fn_here(payload),
        Err(_) => Err(ProgramError::InvalidInstructionData),

}

If you want to leverage multiple instructions, you can use an enum with structs as variant args, like this:

#[derive(BorshDeserialize, BorshSerialize, Debug)]
struct Payload {
    purchase_qty: u64,
    expected_price: u64,
    seller: String,
}

#[derive(BorshDeserialize, BorshSerialize, Debug)]
struct PayloadTwo {
    purchase_qty: u64,
    seller: String,
}

#[derive(BorshDeserialize, BorshSerialize, Debug)]
enum MyInstruction {
    PayloadInstruction(Payload),
    PayloadTwoInstruction(PayloadTwo),

}

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {

    let instruction = MyInstruction::try_from_slice(&instruction_data)?;
    match instruction {
        PayloadInstruction(data1) => some_fn(data1), // Payload struct
        PayloadTwoInstruction(data2) => some_fn(data2), // PayloadTwo struct
    }
}
1

If you want to manually deserialize some struct (without Anchor), I suggest bytemuck, structs are Plain Old Data (POD):

pub fn get_payload_from_bytes(v: &[u8]) -> &Payload{
    bytemuck::from_bytes(v)
}

Note your struct has to implement POD (you may need to replace your String with a fixed-len char array to satisfy POD/Zeroable more easily): #[derive(Pod, Zeroable)]

If these are anchor structs, you can remove the 8-byte discriminator and do the same:

    let acc_data = &ctx.accounts.acc.try_borrow_data()?[..][8..];
    let payload: &Payload = get_payload_from_bytes(acc_data);
0

In the end it was a problem of:

  1. Different instruction lengths/content which i coverd with multiple structs and length checks of the instruction-data

  2. An incorrect mapped type:

seller: String

which should not be a String but a PubKey

represensted in a solana-sdk free project like this:

#[derive(BorshDeserialize, Debug)]
struct Pubkey([u8; 32]);

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.