Solidity Data Retrieval Cost – Why Retrieving Stored Data Costs More Than Calldata

bytescalldatacontract-developmentremixsolidity

I have a function that I need to pass an arbitrary length array of addresses to. In remix, just calling a function that has an address[] costs around 30k gas for an array of 5 addresses:

function fun(address[] calldata addresses) external returns (address[] memory) {
    return addresses;
}

The addresses that I want to call are of a finite set of about 2000 addresses, but it could be any of those addresses when I call my function, so I thought that I could store them in a mapping of uint=>address and call my function with an array of uint16's that I can then look up the addresses with (that I previously stored). Theoretically this should be much cheaper because the calldata is way smaller:

pragma solidity 0.5.17;

contract StoreIDToAddress {
    
    mapping(uint => address) public numToAddress;

    
    function getAddresses(uint16[] calldata  _IDs) external view returns (address[] memory) {
        address[] memory arr = new address[](_IDs.length);
        
        for (uint i; i < _IDs.length; i++) {
            arr[i] = numToAddress[_IDs[i]];
        }
        
        return arr;
    }

    // Other functions to initialize numToAddress etc
}

getAddresses costs 29378 gas with an input of [0, 1, 2, 3, 4].
This alone makes it no cheaper than just including the addresses themselves in the calldata, and when I tested with a 2nd contract that takes in an uint16[] and passes it to getAddresses it costs 34196 gas:

    function testUint16Arr3(uint16[] calldata _arr) external returns (address[] memory) {
        return storeIDToAddress.getAddresses(_arr);
    }

Using bytes and decoding that in to a uint16 to call getAddresses is 35095 gas.

So my question is, why does getAddresses cost so much gas when (AFAIK) the biggest operation is 5 x SLOAD = 1000 gas? How can I reduce the gas cost of getting addresses in to my functions?

Best Answer

The Istanbul hardfork included EIP-1884, which (among other repricings) repriced SLOAD from 200 to 800 gas. That means loading 5 addresses from storage will cost 4000 gas, not including any overhead. The Istanbul hardfork also included EIP-2028, which lowered the cost of non-zero calldata bytes from 68 to 16. These two EIPS are what cause the huge difference in gas costs from what you are expecting.

Also as @goodvibration mentioned in a comment, there is an inbuilt cost of 21000 gas with all transactions. This is the base cost, and any contract interaction you do is on-top of this amount.

Related Topic