1

I don't get how to get next element in struct. I can take id value, but what to do next? i assume, i need to add 1 to slot, to get string value but what it return, reference to the string? and what i should do next with that? if i add 1 to slot, and try to sload, it out of gas

    struct Data {
        uint id;
        string name;
        string sername;
        uint8 status;
    }
    mapping (uint => Data) private idToData;
  
function getStructFromMapping(uint n) public view returns(uint r) {
    assembly {
        mstore(0x0, n)
        mstore(0x20, idToData.slot)
        let slot := keccak256(0x0, 0x40)
        r := sload(slot)
      }
}
1
  • Are you still looking for a solution?
    – Mila A
    Commented Mar 5 at 9:02

1 Answer 1

2

This is pure Yul for getting fields of a struct of the Data type at a specific mapping key entry:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Test_Yul {
    struct Data {
        uint id;
        string name;
        string surname;
        uint8 status;
    }
    mapping (uint => Data) private idToData;

    constructor() {
        idToData[0] = Data({
            id: 115792089237316195423570985008687907853269984665640564039457584007913129639935, // type(uint256).max, // testing for real uint256 values, contrary to the ones that fit in a 128-bit slot
            name: "Alice",
            surname: "Lancaster",
            status: 4
        });
    }

    /*
        The storage layout of a Data struct is the following:

        - id is the first 32-bytes slot
        - name is the second 32-bytes slot (because a string is at least 256-bits sized)
        - surname is the third 32-bytes slot (because a string is at least 256-bits sized)
        - status is a part of the fourth 32-bytes slot

        |   Name    |   Type    |   Slot №  | Bits size  |
        |:---------:|:---------:|:---------:|:----------:|
        |    id     |  uint256  |    0      |    256     |
        |   name    |  string   |    1      |    256     |
        |  surname  |  string   |    2      |    256     |
        |  status   |  uint8    |    3      |     8      |
    */

    /*
        * Remember that the struct is split into 32-byte slots in memory, chained one after the other
    */

    function getValues(uint index) public view returns(uint id, string memory name, string memory surname, uint8 status){
        bytes32 nameBytes;
        bytes32 surnameBytes;
        assembly {
           mstore(0x00, index)
           mstore(0x20, idToData.slot)
           let slot := keccak256(0, 0x40)
           id := sload(slot)


           nameBytes := sload(add(slot, 1)) // we first need to store the slots №2 and №3 in bytes32 variables
           surnameBytes := sload(add(slot, 2)) // same as above

           mstore(add(name, 32), nameBytes)

           mstore(add(surname, 32), surnameBytes)
           
           let slot3 := sload(add(slot, 3))

           status := and(slot3, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

    }

    name = bytes32ToString(nameBytes);
    surname = bytes32ToString(surnameBytes);
}

function bytes32ToString(bytes32 source)
    internal
    pure
    returns (string memory result)
{
    uint8 length = 0;
    while (source[length] != 0 && length < 32) {
        length++;
    }
    assembly {
        result := mload(0x40)
        // new "memory end" including padding (the string isn't larger than 32 bytes)
        mstore(0x40, add(result, 0x40))
        // store length in memory
        mstore(result, length)
        // write actual data
        mstore(add(result, 0x20), source)
    }
}
}

Please see the comments in the code for more details.

The result is:

result


The only part that isn't written in Yul is the bytes32ToString conversion function. You could just do something like this, but you'll have empty characters, because the string will be of fixed 32-bytes length:


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Test_Yul {
    struct Data {
        uint id;
        string name;
        string surname;
        uint8 status;
    }
    mapping (uint => Data) private idToData;

    constructor() {
        idToData[0] = Data({
            id: 115792089237316195423570985008687907853269984665640564039457584007913129639935, // type(uint256).max, // testing for real uint256 values, contrary to the ones that fit in a 128-bit slot
            name: "Alice",
            surname: "Lancaster",
            status: 4
        });
    }

    /*
        The storage layout of a Data struct is the following:

        - id is the first 32-bytes slot
        - name is the second 32-bytes slot (because a string is at least 256-bits sized)
        - surname is the third 32-bytes slot (because a string is at least 256-bits sized)
        - status is a part of the fourth 32-bytes slot

        |   Name    |   Type    |   Slot №  | Bits size  |
        |:---------:|:---------:|:---------:|:----------:|
        |    id     |  uint256  |    0      |    256     |
        |   name    |  string   |    1      |    256     |
        |  surname  |  string   |    2      |    256     |
        |  status   |  uint8    |    3      |     8      |
    */

    /*
        * Remember that the struct is split into 32-byte slots in memory, chained one after the other
    */

    function getValues(uint index) public view returns(uint id, string memory name, string memory surname, uint8 status){
        name = new string(32);
        surname = new string(32);
        assembly {
           mstore(0x00, index)
           mstore(0x20, idToData.slot)
           let slot := keccak256(0, 0x40)
           id := sload(slot)


           let nameBytes := sload(add(slot, 1)) // we first need to store the slots №2 and №3 in bytes32 variables
           let surnameBytes := sload(add(slot, 2)) // same as above

           mstore(add(name, 32), nameBytes)

           mstore(add(surname, 32), surnameBytes)
           
           let slot3 := sload(add(slot, 3))

           status := and(slot3, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

    }
}
}

The result is:

result_2

Your Answer

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

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