Using the Anchor framework (version 0.25.0 which is the latest at the time of writing) for blockchain programs on Solana, I defined a structure with four String fields. Besides that, it has a BN.js BigNum, a u32 and a u16. When writing an instruction which takes these as input, that structure needs to be serialised using Borsh. However, passing the structure to the function I've written, I get a (seemingly internal) error which looks like the following:
RangeError: The value of "offset" is out of range. It must be >= 0 and <= 996. Received -4
at new NodeError (node:internal/errors:387:5)
at boundsError (node:internal/buffer:86:9)
at checkBounds (node:internal/buffer:55:5)
at checkInt (node:internal/buffer:74:3)
at writeU_Int32LE (node:internal/buffer:692:3)
at Buffer.writeUIntLE (node:internal/buffer:648:12)
at UInt.encode (node_modules/buffer-layout/lib/Layout.js:578:7)
at Structure.encode (node_modules/buffer-layout/lib/Layout.js:1263:26)
at Structure.encode (node_modules/buffer-layout/lib/Layout.js:1263:26)
at BorshInstructionCoder._encode (node_modules/@project-serum/anchor/src/coder/borsh/instruction.ts:92:24)
at BorshInstructionCoder.encode (node_modules/@project-serum/anchor/src/coder/borsh/instruction.ts:75:17)
at /home/simeon/dev/anchor/serialisation-test/node_modules/@project-serum/anchor/src/program/namespace/index.ts:64:43
at ix (node_modules/@project-serum/anchor/src/program/namespace/instruction.ts:60:15)
at txFn (node_modules/@project-serum/anchor/src/program/namespace/transaction.ts:24:14)
at Object.rpc [as maakReactie] (node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:21:18)
at /home/simeon/dev/anchor/serialisation-test/tests/serialisation-test.ts:53:34
at Generator.next (<anonymous>)
at /home/simeon/dev/anchor/serialisation-test/tests/serialisation-test.ts:31:71
at new Promise (<anonymous>)
at __awaiter (tests/serialisation-test.ts:27:12)
at Context.<anonymous> (tests/serialisation-test.ts:40:36)
at processImmediate (node:internal/timers:471:21)
This error had never before occurred to me. After doing some digging, I found that buffer-layout.js is the code responsible for setting the aforementioned offset value for the layout of the buffer. Upon closer inspection, a span value of -1 indicates that the span is indeterminate and needs to be checked with a call to getSpan(). As far as I understand it, this function should normally return the number of bytes that the data field takes up. However, in my case the function still returns -1; because I have four Strings (which are of this 'indeterminate' kind) the four spans get summed to calculate the total offset. Normally, this is functional code because all span values are expected to be strictly positive, but since mine are somehow negative the resulting offset is negative too. So, for some strange reason my specific structure causes some things to fail somewhere, because Strings have never been an issue in Anchor.
Through adding some console.log()s in various places, I managed to get some more debug info. The following pastebin contains this detailed log. The file starts with about 530 lines of errors revealing which fields are still indeterminate by the time the program instruction gets called. After that you see a first structure which does get encoded correctly, and then after that the bane of my existence.
Does anyone have any pointers as to how I could resolve this problem?
For the reference: the error-causing struct looks like this in Rust:
#[account]
pub struct Reactie {
pub discord_handle : String,
pub twitter_handle : String,
pub cv_url : String,
pub github_handle : String,
pub id_van_vacature : u32,
pub eigenaar : Pubkey,
pub reactie_id : u16,
}
and like this in TypeScript:
interface Reactie {
discord: string,
twitter: string,
cv_url: string,
github: string,
idVanVacature: number,
eigenaar: PublicKey,
reactieID: number
}
A MWE can be found in this GitHub repo.
Reactie::SPACE
.u16
is 2 bytes instead of 1 byte.cv_url
-->cvUrl
,reactie_id
-->reactieId
. Otherwise the IDL does not know how to serialize the data.