Solidity Calldata Mastery
Calldata is the read-only area that holds a function's inputs. Calldata inputs cannot be changed by the function, and reading them directly avoids a copy.
A function sums a calldata array directly
The sum reads each element straight from calldata. No copy is made before the loop runs.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract CalldataSum {
function sum(uint256[] calldata values) external pure returns (uint256 total) {
for (uint256 i = 0; i < values.length; i++) {
total += values[i];
}
return total;
}
}
A memory copy gives the same sum as direct calldata reading
Copying the calldata array to memory first still sums the same values. The copy is changeable, while the original calldata is not.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract CopySum {
function sumDirect(uint256[] calldata values) external pure returns (uint256 total) {
for (uint256 i = 0; i < values.length; i++) {
total += values[i];
}
return total;
}
function sumViaMemory(uint256[] calldata values) external pure returns (uint256 total) {
uint256[] memory copy = values;
for (uint256 i = 0; i < copy.length; i++) {
total += copy[i];
}
return total;
}
}
Calldata bytes give their first byte and their length
Calldata bytes are read-only input bytes. The function reads the first byte and counts the bytes without copying them.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract CalldataBytes {
function firstByte(bytes calldata data) external pure returns (bytes1) {
require(data.length > 0, "empty");
return data[0];
}
function byteCount(bytes calldata data) external pure returns (uint256) {
return data.length;
}
}
Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract BatchProcessor {
function sum(uint256[] calldata values) external pure returns (uint256 total) {
for (uint256 i = 0; i < values.length; i++) {
total += values[i];
}
return total;
}
function average(uint256[] calldata values) external pure returns (uint256) {
require(values.length > 0, "empty");
uint256 total = 0;
for (uint256 i = 0; i < values.length; i++) {
total += values[i];
}
return total / values.length;
}
}
Key points
- Calldata: Calldata is the read-only area that holds a function's inputs.
- Read-only: Read-only means the function can read a value but cannot change it.
- Calldata array: A calldata array is a list passed as a function input in calldata.
- Copy to memory: A copy to memory is a changeable duplicate of calldata data.
- Restatement: Calldata holds function inputs in a read-only area that a function can read directly without copying.
In this example, sum reads each calldata element in turn and adds it to a running total. Average does the same loop and then divides the total by the number of elements. The truncated average of 51 over 3 elements is 17, which shows division dropping the remainder.
Note
A function that tries to change a calldata value fails to compile, because calldata is read-only.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Remix IDE
Exercise
What can a function do with a calldata value?