šŸŒˆ

Advanced Solidity and Yul

Types in Yul

function getNumber() external pure returns (uint256) {
    uint256 x;
    assembly {
        x := 10
    }
    return x;
}

function getString() external pure returns(bytes32) {
    bytes32 myString;
    assembly {
        myString := "hello world"
    }
    return myString;
}

Basic operations

function isPrime(uint256 x) external pure returns(bool p) {
    p = true;
    assembly{
        let halfX := add(div(x, 2), 1)
        for {let i:= 2} lt(x, halfX) {i := add(x, 1)} {
            if iszero(mod(x, i)) {
                p := 0
                break
            }
        }
    }
}

Storage Slots

uint256 x;
function getXYul() external view returns(uint256 ret) {
    assembly {
        ret := x
    }
}
uint256 x;

function getXYul() external view returns(uint256 ret) {
    assembly {
        ret := sload(x.slot)
    }
}
uint256 x;
function setVarYul(uint256 value) external  {
    assembly {
        sstore(x.slot, value)
    }
}
uint128 a = 1;
uint128 b = 2;

function getASlot() external pure returns(uint256 slot) {
    assembly {
        slot := a.slot
    }
}

function getBSlot() external pure returns(uint256 slot) {
    assembly {
        slot := b.slot
    }
}

function getA() external view returns (bytes32 value) {
    assembly {
        value := sload(a.slot)
    }
}

function getB() external view returns (bytes32 value) {
    assembly {
        value := sload(b.slot)
    }
}
uint128 C = 4;
uint96 D = 6;
uint16 E = 8;
uint8 F = 1;
function getOffsetE() external view returns(uint256 slot, uint256 offset) {
    assembly {
        slot := E.slot
        offset := E.offset
    }
}
function readE() external view returns(uint16 e) {
    assembly {
        let value := sload(E.slot)
        // value = 0x0001000800000000000000000000000600000000000000000000000000000004

        let shifted := shr(mul(E.offset, 8), value) // multiply by 8 because 1 byte = 8 bits and shr takes bits not bytes
        // shifted = 0x0000000000000000000000000000000000000000000000000000000000010008

        e := and(0xffff, shifted) // mask the last 4 bytes to 1 and the rest to 0
    }
}
function writeToE(uint16 newE) external {
  assembly {
    let currentVal := sload(E.slot)
                            // 0x0001000800000000000000000000000600000000000000000000000000000004
    let clearedE := and(currentVal, 0xffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff)

    // mask =       0xffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    // currentVal = 0x0001000800000000000000000000000600000000000000000000000000000004
    // clearedE =   0x0001000000000000000000000000000600000000000000000000000000000004

    let shiftedNewE := shl(mul(E.offset, 8), newE)
    let newVal := or(clearedE, shiftedNewE)
    // shiftedNewE = 0x0000000a00000000000000000000000000000000000000000000000000000000
    // clearedE =    0x0001000000000000000000000000000600000000000000000000000000000004
    // newCal =      0x0001000a00000000000000000000000600000000000000000000000000000004
    sstore(E.slot, newVal)

  }
}

Storage for Arrays and Mapping

uint256[3] fixedArray;
uint256[] dynamicArray;
uint8[] smallArray;

mapping(uint256 => uint256) public myMapping;
mapping(uint256 => mapping(uint256 => uint256)) nestedMapping;
mapping(address => uint256[]) addressToList;

constructor() {
  fixedArray = [99, 100, 101];
  dynamicArray = [50, 20, 45];
  smallArray = [1, 2, 3];

  myMapping[10] = 5;
  myMapping[11] = 6;
  nestedMapping[2][4] = 7;

  addressToList[0x5B38Da6a701c568545dCfcB03FcB875f56beddC4] = [
      42,
      1337,
      777
  ];
    
}

Memory in Solidity

How Solidity uses Memory

struct Points {
    uint256 a;
    uint256 b;
}

event MemoryPointer(bytes32);
event MemoryPointerMsize(bytes32, bytes32);

function memoryPointer() external {
    bytes32 x40;
    assembly {
        x40 := mload(0x40)
    }
    
    emit MemoryPointer(x40);

    Points memory p = Points({
        a: uint256(10),
        b: uint256(20)
    });

    assembly {
        x40 := mload(0x40)
    }

    emit MemoryPointer(x40);
}
function memoryPointerV2() external {
    bytes32 x40;
    bytes32 _msize;
    assembly {
        x40 := mload(0x40)
        _msize := msize()
    }
    
    emit MemoryPointerMsize(x40, _msize);

    Points memory p = Points({
        a: uint256(10),
        b: uint256(20)
    });

    assembly {
        x40 := mload(0x40)
        _msize := msize()
    }

    emit MemoryPointerMsize(x40, _msize);
    
    assembly {
        pop(mload(0xff))
        x40 := mload(0x40)
        _msize := msize()
    }

    emit MemoryPointerMsize(x40, _msize);
}
function fixedArray() external {
    bytes32 x40;
    assembly {
        x40 := mload(0x40)
    }
    emit MemoryPointer(x40);
    uint256[2] memory arr = [uint256(5), uint256(6)];
    assembly {
        x40 := mload(0x40)
    }
    emit MemoryPointer(x40);
}
  function abiEncode() external {
      bytes32 x40;
      assembly {
          x40 := mload(0x40)
      }
      emit MemoryPointer(x40);
      abi.encode(uint256(5), uint256(19));
      assembly {
          x40 := mload(0x40)
      }
      emit MemoryPointer(x40);
  }
function abiEncodePacked() external {
    bytes32 x40;
    assembly {
        x40 := mload(0x40)
    }
    emit MemoryPointer(x40);
    abi.encodePacked(uint256(5), uint128(19));
    assembly {
        x40 := mload(0x40)
    }
    emit MemoryPointer(x40);
}