Skip to content

Assembly Coding - Calldata

Posted on:February 10, 2023 at 04:20 AM

The input data is stored in the calldata that means a read only mode in the memory space. Using assembly code allows developers to perform low-level operations on the virtual machine, including accessing and manipulating data stored in memory, storage, or the calldata space. However, bugs or errors can easily result in security vulnerabilities or contract failures, so it’s important to understand how to use assembly code. A function needs space to store all the info that it will use in the memory space, and each of these memory slots is 32 bytes in size, and they start off completely empty when the function starts running.

It can be used to determine the size of the data being passed to a function, allowing you to perform operations based on that data size, such as parsing and processing inputs, or ensuring that inputs meet certain size requirements. Calldatasize is an opcode that returns the input data’s size in bytes that is sent with a message call. This can be useful in different ways, such as to validate the size of the input data or to extract the data itself.

function getDataSize() public pure returns (uint256 size) {
    assembly {
        size := calldatasize()
        // The result of the size := 4,
        // It is the first 4 slots are reserved for the function
    }
}

In the next code block, the getInputDataSize() has an input of uint256 which stores values up to 2^(256-1). The size in the function input is 32 bytes or 256 bits, so it required to store a uint256 value in memory and the size will be 36 bytes instead of 4 bytes. Let’s use an example to to load data or read from memory location using the mload opcode.

function getInputDataSize(uint256 _x) public pure returns (uint256 size) {
    assembly {
        let a := mload(_x)
        size := calldatasize()
        // The result of the size := 36
    }
}

In the next code block, the values are stored in memory starting with the 0x40 location instead of 0x0. This spot tells us where the next available free memory is which is adding 0x40 and 0x20, so it’s always safe to use for custom memory related variables. The return opcode sends 32 bytes back to the caller, starting at the 0x60 memory location.

function mulUsingAssembly() public pure returns (uint256){
    assembly {

        let _0_mul := mul(10,50) // 10 * 50 = 500
        mstore(0x40, _0_mul) // store _0_mul at the 0x40 memory location
        let loadVlaueof0x40 := mload(0x40)

        let _1_mul := mul(loadVlaueof0x40, 2)  // _0_mul * 2 = 1000
        let _add_space := add(0x40, 0x20) // The next available free memory 0x60
        mstore(_add_space, _1_mul)

        return(_add_space, 32)
        // The function returns 1000 which is on
    }
}