Skip to content

Clarify the direction of offset in storage regions #307

Closed

Description

@djolertrk

The segment schema says offset: 0 means the segment "begins at the start of the specified slot", but not whether that's the most or the least significant byte.

The reference implementation (packages/pointers/src/read.ts) slices the word from byte 0, i.e. the most significant byte, and a length that runs past the slot continues into the next one, which also only makes sense that way.

Solidity's storage layout counts from the other end (packed values are "lower-order aligned"), so solc has to convert its offsets when emitting
pointers. This came up in review of that code because the schema text doesn't settle it. It would help if the description said explicitly that offset counts from the most significant byte, ideally with a packed example like a uint8 in the last byte at "offset": 31.

See this for the context: argotorg/solidity#16990 (comment)

cc @cameel @gnidan

Activity

  1. gnidan commented on Sep 30, 2026

    @gnidan
    Member

    Thanks for pointing this out! I'm declaring the reference implementation behavior to be the official format spec.

    See #309 for some examples on how to be lazy about the disparity vs. solc in the implementation ;)

  2. gnidan commented on Sep 30, 2026

    @gnidan
    Member

    Now that the above is merged, see this example for a packed address a; uint32 u; respectively at ethdebug offsets 12 and 8 and solc offsets 0 and 20:

    - slot: 2
    offset:
    $difference:
    - $wordsize
    - .length: $this
    length: 20
    - slot: 2
    offset:
    $difference:
    - $wordsize
    - $sum:
    - 20
    - .length: $this
    length: 4

    Of course, you can get more creative and have a generalized $difference: [$wordsize, $sum: [<offset>, .length: $this]] or whatever variation you'd like for parameterizing that. Just in case you want to preserve the presentation of solc's own internal layout logic.

  3. cameel commented on Oct 1, 2026

    @cameel
    Contributor

    What is the motivation for counting from the most significant byte though? Is it to be consistent with memory being big endian?

    The problem is that counting from the least significant byte is the prevalent convention so this may be an unnecessary footgun. Fe follows Solidity's convention when specifying offsets. Vyper does not do packing at all AFAIK.

    And Solidity's convention is not technically wrong. Storage is slot oriented and you can only read/write whole slots, so it does not have any inherent endianness.

  4. gnidan commented on Oct 1, 2026

    @gnidan
    Member

    What is the motivation for counting from the most significant byte though? Is it to be consistent with memory being big endian?

    Not specifically, although this does help with consistency. The decision is somewhat arbitrary, but mostly it's because I was focusing on string storage semantics, which go the other direction vs. packed slots. Also, since #284, I made it so that offset/length values can extend beyond [0,255] and the format defines that to extend to the next lot naturally... by starting MSB at offset 0, it allows these semantics to work like normal left-to-right reading order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions