0

I've been racking my brain with this for some time, and have been unable to increment and decrement specific fields inside nested structs as per the example below.

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

contract CalendarGasTest {
    struct ExampleNested_1 {
        uint88 e0;
        uint80 e1;
        uint80 e2;
    }

    struct ExampleNested_2 {
        uint80 e3;
        uint72 e4;
        uint72 e5;
    }

    struct Example {
        ExampleNested_1 nested1;
        ExampleNested_2 nested2;
    }

    Example public _example;


    constructor() {}

    function increment() external {
        assembly {
            let slot := _example.slot
            // how do i increment a specific field in my packed structs?
        }
    }

    function decrement() external {
        assembly {
            let slot := _example.slot
            // how do i decrement a specific field in my packed structs?
        }
    }
}

I am able to get the first field of each nested struct to increment/decrement, with something like this:

    function testFirstFieldFirstNestedStruct() external {
      assembly {
        let slot := _example.slot

        sstore(slot, add(sload(slot), 1))
      }
    }

    function testFirstFieldSecondNestedStruct() external {
      assembly {
        let slot := _example.slot

        sstore(add(slot, 0x1), add(sload(add(slot, 0x1)), 1))
      }
    }

which I'm pretty sure is not a good implementation as I was just messing around (not sure why 0x1, 0x2, 0x3...0xn correspond to the nth nested struct).

Regardless of what I do I can't get the behavior I want which is to be able to increment and decrement any field within the nested structs.

1 Answer 1

0

The code below shows an implementation for incrementing:

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

contract CalendarGasTest {
    struct ExampleNested_1 {
        uint88 e0;
        uint80 e1;
        uint80 e2;
    }

    struct ExampleNested_2 {
        uint80 e3;
        uint72 e4;
        uint72 e5;
    }

    struct Example {
        ExampleNested_1 nested1;
        ExampleNested_2 nested2;
    }

    Example public _example;

    constructor() {}

    function incMinerPopulation(uint256 order) external {
        if (order ==  1) { ++_example.nested1.e1; }
    }

    function incMinerPopulationOptimized(uint256 order) external {
        assembly {
            switch order
                case 1 {
                    let e2Offset := 88
                    let e2Mask := 0x00000000000000000000ffffffffffffffffffff0000000000000000000000
                    let slot := _example.slot
                    let slotInMem := sload(slot)
                    let tempE2 := and(slotInMem, e2Mask)
                    let rShift := shr(e2Offset, tempE2)
                    let inc := add(rShift, 1)
                    let newE2 := shl(e2Offset, inc)
                    sstore(slot, or(newE2, and(slotInMem, not(e2Mask))))
                }
        }
    }
}

In general, its about shifting the desired value to the right-most bit, masking the other packed fields, doing the addition, shifting it back into its right location and then finally masking this new value with the persisted values for the rest of the nested struct.

Are there any assembly optimizations I could do to make this more performant?

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.