The rules on storage slot packing can be found in the Solidity documentation ("Layout of State Variables in Storage"):
Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
- The first item in a storage slot is stored lower-order aligned.
- Value types use only as many bytes as are necessary to store them.
- If a value type does not fit the remaining part of a storage slot, it is stored in the next storage slot.
- Structs and array data always start a new slot and their items are packed tightly according to these rules.
- Items following struct or array data always start a new storage slot.
"Lower-order aligned" means the first variable (owner
) will be the right-most value in the storage slot, and each subsequent variable is filled from right to left.
The rest of the rules should be self-explanatory.
Visualization of the storage slot:
0x 000000000000000000 001f 01 90416e8285169f15346fcf9e336b6e1443b8c30a
└─────────┬────────┴──┬─┴┬─┴──────────────────┬─────────────────────┘
empty padding │ └isTrue owner
└u16
To decode the data, cast the hexadecimal value from the appropriate place in the storage slot to the desired type. For example, in JavaScript:
const value = '0x000000000000000000001f0190416e8285169f15346fcf9e336b6e1443b8c30a';
const owner = `0x${value.substring(26)}`; // '0x90416e8285169f15346fcf9e336b6e1443b8c30a'
const isTrue = Boolean(Number(value.substring(24, 26))); // true
const u16 = parseInt(value.substring(20, 24), 16); // 31