1

I'm trying to load varios bytes from a struct in assembly. I found a good explanation here for uint. While this is working for uint and the whole memory slot i cant figure out how to get a bytes1 or bytes12 variable from the struct.

contract Storage {

mapping(uint256 => turmite) public turmites;

struct turmite {
    bytes1 state;
    bytes12 baserule;
}

bytes1 state = hex"02";
constructor(){
    turmites[1] = turmite(
    state,
    0xff0801ff0201ff0000000001
    );
}

function retrieve(uint id) public{
    turmite storage data = turmites[id];
    bytes12 b;
    bytes1 a;
    bytes32 completeslot;
    assembly {
        let w := sload(data.slot)
        completeslot := w
        a := and(w, 0xff)
}   
}
}

1 Answer 1

1

Bytes & Strings are left aligned, while numbers (i.e., uintX) are right aligned.

  • bytes32(bytes1(0xFF)) -> 0xFF0...00
  • uint256(uint8(0xFF)) -> 0x00...0FF

Leading to such behavior :

function test() public view returns (bytes1) {
    uint256 value = 0xFF00000000000000000000000000000000000000000000000000000000000000;
    bytes1 rvalue;

    assembly {
        rvalue := value
    }

    return rvalue; // 0xFF
}

SLOAD returns a uint256, when trying to put this value in a bytes1 you are actually getting the 32th byte of the uint (Throughout this answer, byte 32 is far left, byte 0 is far right), not the 1st. With bytes12, you get the range 32 to 20 instead of 12 to 0.

This is exactly what happens with :

// clears bytes 32 to 1 and puts byte 32 into a
// because a is byte1 : left aligned
a := and(w, 0xff)

You could work directly on ints and only convert before returning outside the assembly block like so :

function retrieve(uint id) public view returns (bytes1, bytes12) {
        turmite storage data = turmites[id];
        uint96 b;
        uint8 a;

        assembly {
                let w := sload(data.slot)
                a := w
                b := shr(8, w)
        }

        return (bytes1(a), bytes12(b));   
}

Or stick with bytesX and shift your values left as this is where they will be picked up :

  function retrieve(uint id) public view returns (bytes1, bytes12) {
        turmite storage data = turmites[id];
        bytes12 b;
        bytes1 a;

        assembly {
                let w := sload(data.slot)

                // Shift 152 bits (19 bytes) left to bring data.baserule at byte 32
                b := shl(152, w)

                // shift 248 bits (31 bytes) left to bring data.state at byte 32
                a := shl(248, w)
        }

        return (a, b);  
    }
1
  • Thanks for the summary - really helpfull! Mar 28, 2022 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.