Strange behaviour with assembly and storage slot

Hello,

I'm rewriting uniswap v2 in inline assembly, i'm trying to update reserve0 and reserve1 info :

   uint112 private reserve0;           // uses single storage slot, accessible via getReserves
    uint112 private reserve1;           // uses single storage slot, accessible via getReserves
    uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves



 function _testSave(uint256 balance0, uint256 balance1) private {
        uint256 slot0;
        uint256 slot1;
        assembly {
            //  require(balance0 <= type(uint112).max && balance1 <= type(uint112).max,"UniswapV2: OVERFLOW");
            if gt(balance0, 0xfffffffffffffffffffffffffff) {
                mstore(0x80, shl(229, 4594637))
                mstore(0x84, 32)
                mstore(0xA4, 30)
                mstore(0xC4, "OVE")
                revert(0x80, 0x64)
            }
            if gt(balance1, 0xfffffffffffffffffffffffffff) {
                mstore(0x80, shl(229, 4594637))
                mstore(0x84, 32)
                mstore(0xA4, 30)
                mstore(0xC4, "OVE")
                revert(0x80, 0x64)
            }

            //reserve0 = uint112(balance0);
            sstore(reserve0.slot, balance0)
            sstore(reserve1.slot, balance1)
           
            slot0 := sload(reserve0.slot)
            slot1 := sload(reserve1.slot)
        }

        console.log("balance 0 %s balance 1 %s", balance0, balance1);
        console.log("reserve 0 %s reserve 1 %s", slot0, slot1);

        // reserve0 = uint112(balance0);
        // reserve1 = uint112(balance1);
        //reserve.blockTimestampLast = blockTimestamp;

        assembly {
            // emit Sync(uint112(balance0), uint112(balance1));
            mstore(0x100, balance0)
            mstore(add(0x100, 0x20), balance1)
            log1(0x100, 0x40, syncTopic)
        }
    }

My log show

balance 0 150000000000000000000000 balance 1 500000000000000000000
reserve 0 500000000000000000000 reserve 1 500000000000000000000

I think it's because these data share the same storage slot but how correctly update it in assembly ?

Unless there are other state variables in this contract, these 3 should all be within the same slot.

Yes, but how do I update each setting?

you need to retrieve the value in the slot, then use logical operations to change the bits corresponding to each variable and finally write it back to the slot

Can you write me an exemple in code please

my yul is a bit rusty, but i think it would look something like this:

to set reserve0

masked := and(sload(n_slot), sub(exp(2, 144), 1))
new_v := add(shl(new_r0, 144), masked)
sstore(n_slot, new_v)

to set reserve1:

masked := and(sload(n_slot), add(shl(sub(exp(2, 112), 1), 144), sub(exp(2, 32), 1)))
new_v := add(shl(new_r1, 32), masked)
sstore(n_slot, new_v)

to set blockTimestampLast:

masked := and(sload(n_slot), sub(exp(2, 32), 1))
new_v := add(new_btl, masked)
sstore(n_slot, new_v)

thanks, I think I'm doing some shit the reserve seems to equal block timestamp :

            let masked := and(sload(reserve0.slot), sub(exp(2, 144), 1))
            let new_v := add(shl(balance0, 144), masked)
            sstore(reserve0.slot, new_v)

            masked := and(
                sload(reserve1.slot),
                add(shl(sub(exp(2, 112), 1), 144), sub(exp(2, 32), 1))
            )
            new_v := add(shl(balance1, 32), masked)
            sstore(reserve1.slot, new_v)

            masked := and(sload(blockTimestampLast.slot), sub(exp(2, 32), 1))
            new_v := add(timestamp(), masked)
            sstore(blockTimestampLast.slot, new_v)

FYI, you can just compile the original code with --asm, and then look into the output .asm file.

--asm outputs some sort of assembly, --opcodes outputs the opcodes and --ir outputs ir/yul code. @youtpout is writing code in Yul.

sorry. was writing this out of the top of my head and didn't do a second check. the masking for blockTimestampLast is wrong.
The idea is to AND the value in the slot with a mask to set the variable to all 0's and then slh() then new value and ADD them together.

masked := and(sload(blockTimestampLast.slot), sub(exp(2, 32), 1)) does the opposite of the intended (144 0's and 32 1's)

it should be : masked := and(sload(blockTimestampLast.slot), shl(sub(exp(2, 144), 1), 32)) 144 1's and 32 0's

Thanks I compile to yul, analyse the result and update my code to match and it's work

        assembly {
            // update reserve0
            let value := sload(reserve0.slot)
            let mask := 0xffffffffffffffffffffffffffff
            // uint256 to uint112
            let bal0 := and(balance0, mask)
            let bal1 := and(balance1, mask)

            value := and(value, not(mask))
            let result := or(value, and(bal0, mask))
            sstore(reserve0.slot, result)

            // update reserve1 (the slot remains the same)
            value := sload(reserve0.slot)
            mask := 0xffffffffffffffffffffffffffff0000000000000000000000000000
            let toInsert := shl(112, bal1)
            value := and(value, not(mask))
            result := or(value, and(toInsert, mask))
            sstore(reserve1.slot, result)
        }