I have a library
utils.sol
library Utils {
struct UtilType {
unit var1;
bool var2;
}
function addExtra(UtilType storage state, uint extra) public {
state.var1 += extra;
}
}
notice the public
visibility on addExtra
function in utils.sol
library.
and that function is being used as follows:
mycontract.sol
import "utils.sol"
contract MyContract {
using Utils for UtilsType;
UtilsType state;
function foo(uint extra) public {
state.addExtra(extra);
}
}
- will changing
public
toexternal
inmyutils.sol
reduce external function call overhead and save any gas? - if so, does the gas savings only occur due to
uint
variable and not struct type since it's a storage memory?
Best Answer
Declaring a function as
external
rather thanpublic
does not affect gas usage at all, neither in contracts nor in libraries.In earlier versions of Solidity, only external functions could take
calldata
arguments. Making a function public would force you to use the more expensivememory
arguments. This restriction was lifted in Solidity 0.6.9. This is probably where the misconception originally comes from. Using external functions used to make calls cheaper but this was not due to their visibility.Other than that, the choice between them only affects analysis (some things may be forbidden with one or the other), not code generation. You will get the exact same bytecode in both cases.
Example
You can check for yourself that the bytecode is identical.
Let's put this simple library in a file called
lib.sol
:and then run the compiler twice, changing the visibility to
external
before the second run:Even without the optimizer enabled the output is empty, meaning, there are absolutely no differences.
Implementation in the compiler
You can check compiler code to see that it's not just this simple example that behaves this way.
In case of external/public functions, there are two places where you might expect differences:
ContractCompiler::appendFunctionSelector()
.ContractCompiler::visit(FunctionDefinition const&)
.Note how in both cases there are no conditions distinguishing between
public
vsexternal
.The dispatch goes over a list of interface functions which includes both public and external functions. It's constructed by
ContractDefinition::interfaceFunctionList()
, which goes over all functions in a contract and usesFunctionDefinition::isPartOfExternalInterface()
as a filter. This in turn callsDeclaration::isPublic()
, which, despite the name, has a>=
condition on visibility so it matches external functions as well:You could dive deeper into how the body is generated but you won't find any special cases there either.