I am wondering however what is the point of specifying the input argument in a function as calldata, if you know that it will only be called from a contract (hence no gas savings of using the real calldata space which is only used EOA-> Contract calls).
That is not true, calldata is present for both EOA to CA and CA to CA. In the EVM implementation, this is the input
.
Typically, executing a transaction runs the interpreter the same way the call opcode implementation does.
Doing a call
, delegatecall
, staticcall
from one contract to another will put the data passed along the call in calldata.
You can also see gas consumption gains even if the initial transaction is the same :
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Caller {
Callee immutable public callee;
constructor(Callee _callee) {
callee = _callee;
}
function testCallerCalldata(bytes calldata data) external {
callee.testCalleeCalldata(data);
}
function testCallerMemory(bytes calldata data) external {
callee.testCalleeMemory(data);
}
}
contract Callee {
function testCalleeCalldata(bytes calldata data) external {
}
function testCalleeMemory(bytes memory data) external {
}
}
Calling testCallerCalldata
costs less than testCallerMemory
because the Callee
contract won't have to copy any calldata to memory.