Solidity – Explanation for Accessing Bytes Variable “functionCallData” Multiple Times for Selector

function-selectorsolidity

Why would we get function selector like this and why do we use that bytes variable functionCallData do many times? Thanks in advance!



function getSelectorTwo() public view returns (bytes4 selector) {
        bytes memory functionCallData = abi.encodeWithSignature(
            "transfer(address,uint256)",
            address(this),
            123
        );
        selector = bytes4(
            bytes.concat(
                functionCallData[0],
                functionCallData[1],
                functionCallData[2],
                functionCallData[3]
            )
        );
    }

Best Answer

The bytes type is like an array, where each element in the array is 1 byte.

The getSelectorTwo() function is encoding the transfer(address,uint256) function selector with the values of the parameters it expects (address and uint256).

If you printed functionCallData you would see the following bytes:

0xa9059cbb00000000000000000000000059235ef80c8c63b480734c6888754f464fdb212d000000000000000000000000000000000000000000000000000000000000007b

Notice how the first four bytes are the function selector a9059cbb.

Then the following 32 bytes are the address of that contract (left-padded with zeros):

00000000000000000000000059235ef80c8c63b480734c6888754f464fdb212d

Then the following 32 bytes are the 123 number in hex (left-padded with zeros):

000000000000000000000000000000000000000000000000000000000000007b

7b hex is 123 in decimal.

So, the reason why that code is accessing the functionCallData bytes array 4 times is because it is copying the first 4 bytes of that data, which we know it's the function selector:

functionCallData[0] = a9 functionCallData[1] = 05 functionCallData[2] = 9c functionCallData[3] = bb It concatenates it and returns it.

It cannot do something like functionCallData[0:4] to copy a range from the functionCallData bytes array because the slice operator is only available in calldata bytes array, not on local or storage arrays (yet). So it has to copy it manually, whether in a loop or index by index like it's doing.

Check and try my getTransferFunctionSelectorWithParams function below:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

contract Contract {

   function getTransferFunctionSelectorWithParams() public view returns(bytes memory) {
       bytes memory functionCallData = abi.encodeWithSignature(
            "transfer(address,uint256)",
            address(this),
            123
        );
        return functionCallData;
   }

   function getSelectorTwo() public view returns (bytes4 selector) {
        bytes memory functionCallData = abi.encodeWithSignature(
            "transfer(address,uint256)",
            address(this),
            123
        );
        selector = bytes4(
            bytes.concat(
                functionCallData[0],
                functionCallData[1],
                functionCallData[2],
                functionCallData[3]
            )
        );
    }

}