Test case: <struct> storage
Solidity tightly packs struct storage words starting from the right-hand side.
This test ensures that relative offsets are computed properly for a struct
with a few small fields (struct Record { uint8 x; uint8 y; bytes4 salt; }).
Test source
const structStorageTest: ObserveTraceTest<{
x: number;
y: number;
salt: string;
}> = {
pointer: findExamplePointer("packed-field"),
compileOptions: singleSourceCompilation({
path: "StructStorage.sol",
contractName: "StructStorage",
content: `contract StructStorage {
Record record;
uint8 step;
constructor() {
record = Record({
x: 5,
y: 8,
salt: 0xdeadbeef
});
// trick the optimizer maybe (otherwise the first record assignment
// will get optimized out)
//
// compiler might be smarter in the future and cause this test to fail
step = 1;
record = Record({
x: 1,
y: 2,
salt: 0xfeedface
});
step = 2;
}
}
struct Record {
uint8 x;
uint8 y;
bytes4 salt;
}
`,
}),
expectedValues: [
{ x: 0, y: 0, salt: "0x" },
{ x: 5, y: 8, salt: "0xdeadbeef" },
{ x: 1, y: 2, salt: "0xfeedface" },
],
async observe({ regions, read }) {
const x = Number((await read(regions.lookup["x"])).asUint());
const y = Number((await read(regions.lookup["y"])).asUint());
const salt = (await read(regions.lookup["salt"])).toHex();
return { x, y, salt };
},
equals(a, b) {
return a.x === b.x && a.y === b.y && a.salt === b.salt;
},
};
Tested pointer
# example `struct Record { uint8 x; uint8 y; bytes4 salt; }` in storage
#
# this example defines the "packed-field" template inline and demonstrates
# how templates can be reused with `yields` to rename regions.
# each field is placed by packing right-to-left from the previous offset.
templates:
packed-field:
expect:
- "struct-storage-contract-variable-slot"
- "previous"
- "size"
for:
name: "field"
location: storage
slot: "struct-storage-contract-variable-slot"
offset:
$difference: [ "previous", "size" ]
length: "size"
in:
define:
"struct-storage-contract-variable-slot": 0
in:
group:
# sentinel region marking where packing begins (end of word)
- name: "packing-begin"
location: storage
slot: "struct-storage-contract-variable-slot"
offset: $wordsize
length: 0
- define: { previous: { .offset: "packing-begin" }, size: 1 }
in:
template: "packed-field"
yields: { "field": "x" }
- define: { previous: { .offset: "x" }, size: 1 }
in:
template: "packed-field"
yields: { "field": "y" }
- define: { previous: { .offset: "y" }, size: 4 }
in:
template: "packed-field"
yields: { "field": "salt" }