In the first example, I tried to change the second slot of the struct but failed
mapping(uint256 => MyStruct) public myStruct;
struct MyStruct {
uint128 slot1;
uint128 slot2; //fixed 32-bytes, 256bit
}
function writeTest(uint256 key) external {
assembly {
mstore(0x0, key)
mstore(0x20, myStruct.slot)
let hash := keccak256(0, 0x40)
let s:= sload(hash)
sstore(
hash,
or(
and(s, not(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
and(12, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
)
) // clear first 128 bit and write new 128 bit (12)
let slot2 := div(
and(
s,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000
),
0x100000000000000000000000000000000
)
// or
slot2 := shr(128, s)
// how i can clean and rewrite bit in slot 2
}
}
In the second example, I tried to create a 32-byte struct in memory and then save it in storage, in this case only the first slot of the struct changes.
function writeTest2(uint256 key) external {
MyStruct memory test2 = MyStruct(10, 20); // 32 bytes
assembly {
mstore(0x0, key)
mstore(0x20, myStruct.slot)
let hash := keccak256(0, 0x40)
sstore(hash, mload(test2))
// check map and get me wrong value, just slot 1 changed
}
}
how I can write, read or shift a,b,c in mstore
for one 32-byte pack
function testPackingMemory(uint64 a,uint64 b,uint128 c) external {
assembly {
mstore(0x40,some_value) // how i can write or read a,b,c in mstore for one 32byte
}
}
Best Answer
First example
If you intend to update both fields, there is no need to do an
sstore
just after updating the first field, it's better to do all the required updates locally (in stack / memory) and issue only a singlesstore
as it will cost less gas.Your code is unnecessarily complicated for such operations and it seems that you are using
div
for bit shifting... I'd advise against it (even if they are equivalent with powers of 2) because it makes the code less readable. If you are dealing with bits, use bit operators and functions. If you ever need a reminder for bit setting / clearing / etc... you can check this answer.Here is a commented example for your first question :
Of course, if you are updating all the bits from the 32 bytes storage variable, you can also completely skip the read step since you will update everything anyway but at least with this example you can see the whole process.
Second example
Your idea is good, but there are some caveats, as you can see in the documentation. There is no in memory packing in solidity (except for bytes and string) so you need to pack the values yourself before writing it with
sstore
.Here is a commented example for your second question :
Third example
The issue is similar to your second question. The values are once again encoded on 32 bytes as per the abi encoding. You also need to do the packing yourself...
Here is a commented example for your third question, returning a
bytes32
: just for display.For any of the example, just make sure to modify the shift values accordingly to your underlying types if you were to change them.
I hope this answers your questions, and don't hesitate to ask for more precision if anything is left unclear.