2

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.

3
  • Briefly looking towards your code, I already found one bug. You define 1 byte less in your Reactie::SPACE. u16 is 2 bytes instead of 1 byte.
    – Omega
    Commented Jul 21, 2022 at 8:33
  • Okay, to be fair, that was indeed an oversight on my part. However, I've fixed it, and the error persists. Commented Jul 21, 2022 at 8:58
  • Also another suggestion, try using the correct TS fields as generated by the IDL. example: cv_url --> cvUrl, reactie_id --> reactieId. Otherwise the IDL does not know how to serialize the data.
    – Omega
    Commented Jul 21, 2022 at 11:16

1 Answer 1

2

As you noted, that -1 span is due to the variable-length fields in your type, the Strings. Instead, you can change your types to encode a set number of bytes in utf-8 encoding, e.g.:

const STRING_LEN: usize = 50;
#[account]
pub struct Reactie {
    pub discord_handle  : [u8; STRING_LEN],
    pub twitter_handle  : [u8; STRING_LEN],
    pub cv_url          : [u8; STRING_LEN],
    pub github_handle   : [u8; STRING_LEN],

    pub id_van_vacature : u32,
    pub eigenaar        : Pubkey,
    pub reactie_id      : u16,
}

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.