Gist #2 below provides a more concise explanation of what I believe is going wrong and causing decoding to fail. I am leaving gist #1 up, but would recommend readers to read Gist #2 first to get a better understanding of the problem.
Breaking code:
let primaryStruct = await program.account.primaryStruct.fetch(stateAccount);
The error:
Error: Invalid option undefined
at OptionLayout.decode (node_modules/@project-serum/borsh/src/index.ts:146:11)
at Sequence.decode (node_modules/buffer-layout/lib/Layout.js:1090:34)
at Structure.decode (node_modules/buffer-layout/lib/Layout.js:1234:32)
at WrappedLayout.decode (node_modules/@project-serum/borsh/src/index.ts:100:37)
at Structure.decode (node_modules/buffer-layout/lib/Layout.js:1234:32)
at AccountsCoder.decode (node_modules/@project-serum/anchor/src/coder/accounts.ts:53:19)
at AccountClient.fetchNullable (node_modules/@project-serum/anchor/src/program/namespace/account.ts:153:33)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at AccountClient.fetch (node_modules/@project-serum/anchor/src/program/namespace/account.ts:165:18)
Example code that breaks
#[account(zero_copy)]
pub struct PrimaryStruct {
pub owner: Pubkey,
pub is_initialized: bool,
pub example_arr: [Option<FakeStruct>; 10],
pub some_bump: u8,
pub some_bump_2: u8,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy)]
pub struct FakeStruct {
pub aU8: u8,
pub anOptionalU8: Option<u8>,
}
Example code that works (removed pub anOptionalU8: Option<u8>
from the FakeStruct
)
#[account(zero_copy)]
pub struct PrimaryStruct {
pub owner: Pubkey,
pub is_initialized: bool,
pub example_arr: [Option<FakeStruct>; 10],
pub some_bump: u8,
pub some_bump_2: u8,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy)]
pub struct FakeStruct {
pub aU8: u8,
}
Example code that also works (keep the FakeStruct optional, but set example_arr
to not be optional:
#[account(zero_copy)]
pub struct PrimaryStruct {
pub owner: Pubkey,
pub is_initialized: bool,
pub example_arr: [FakeStruct; 10],
pub some_bump: u8,
pub some_bump_2: u8,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy)]
pub struct FakeStruct {
pub aU8: u8,
pub anOptionalU8: Option<u8>,
}
Why this is a problem: I cannot currently fetch my program's state account data due to this deserialization bug.
This is the bit of code in node_modules/@project-serum/borsh/src/index.ts
that is failing. I threw in some logs above the error:
decode(b, offset = 0) {
const discriminator = this.discriminator.decode(b, offset);
if (discriminator === 0) {
return null;
}
else if (discriminator === 1) {
return this.layout.decode(b, offset + 1);
}
console.log(this);
console.log(this.discriminator.decode(b, offset));
throw new Error('Invalid option ' + this.property);
}
and got the following:
OptionLayout {
span: -1,
property: undefined,
layout: Structure {
span: 20,
property: undefined,
fields: [
[UInt], [UInt],
[UInt], [WrappedLayout],
[WrappedLayout], [WrappedLayout],
[WrappedLayout], [WrappedLayout],
[WrappedLayout], [WrappedLayout],
[UInt], [UInt],
[BNLayout]
],
decodePrefixes: false
},
discriminator: UInt { span: 1, property: undefined }
}
second log:
2
It appears this problem isn't only related to nested Option
types. I've removed all Option
types from my non-example code in an attempt to circumvent this error, yet still receive the error. My non-example code currently contains 5 u8
s, 7 bool
s, and a u64
, yet is still failing to fetch with the same error.