I'm currently learning Yul in Solidity and experimenting with the call() function to call other functions in inline assembly. I've noticed that as soon as a function is set to private, I receive a revert error from the point where the function is called. Is there any way at all to call private functions using inline assembly? Both functions are within the same contract. I'm not trying to figure out any hacking methods, just whether it's possible to call private functions with assembly within the same contract.
3 Answers
tl;dr, to answer your question:
Is there any way at all to call private functions using inline assembly?
Yes, it is possible, but not with the call()
instruction.
Long answer + extras:
If you're purely trying to use the call()
instruction, this will not work because there is no selector created for private functions as explained by the others.
However, it is actually possible to call a private function from within a contract.
Since you're playing around with Yul, what I like to do is write a simple contract in Solidity and compile it using the IR optimizer and look at the output to understand it more. Here's an example:
- Write a contract
Foo.sol
:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
contract Foo {
function privateFunction() private pure returns(uint256) {
return 42;
}
function someFunction() external pure returns(uint256) {
return privateFunction();
}
function anotherFunction() external pure returns(uint256) {
return privateFunction();
}
}
- Compile it using the following command:
solc --optimize --ir-optimized -o out ./Foo.sol --overwrite
- Look at the output file in
./out/Foo_opt.sol
. The output should look like this:
/// @use-src 0:"src/Foo.sol"
object "Foo_28" {
code {
{
/// here goes the constructor code
}
}
/// @use-src 0:"src/Foo.sol"
object "Foo_28_deployed" {
code {
{
// figure out where to which function signature hash to direct the call
/// @src 0:65:376 "contract Foo {..."
mstore(64, memoryguard(0x80))
if iszero(lt(calldatasize(), 4))
{
switch shr(224, calldataload(0))
case 0x0714c622 { // function signature of anotherFunction()
external_fun_anotherFunction() // call to private function
}
case 0x35b09a6e { // function signature of someFunction()
external_fun_anotherFunction() // call to private function
}
}
revert(0, 0)
}
function external_fun_anotherFunction() // the function privateFunction()
{
if callvalue() { revert(0, 0) }
if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) }
let memPos := mload(64)
mstore(memPos, /** @src 0:158:160 "42" */ 0x2a)
/// @src 0:65:376 "contract Foo {..."
return(memPos, 32)
}
}
data // here goes the meta data
}
}
Explanation:
The compiler writes an internal/private external_fun_anotherFunction
function, which is called by the other two functions. You can go through the code line by line, and hopefully learn a bit more :)
-
So.. how do you make the call to a private function from assembly?– Ismael ♦Commented Dec 19, 2023 at 19:40
-
You can call a private function declared in Yul using
functionName(args)
. For example, in my Yul code the call to theexternal_fun_anotherFunction
function is done like that:external_fun_anotherFunction() // call to private function
Commented Dec 19, 2023 at 20:55 -
The private function
privateFunction
from the solidity code is compiled toexternal_fun_anotherFunction
by the compiler in this case. But you can also write such private functions manually. Commented Dec 19, 2023 at 20:58 -
1
-
I agree; the function's name,
external_fun_anotherFunction,
is generated by the compiler based on specific parameters I haven't explored yet. It can be confusing. I should've clarified thatexternal_fun_anotherFunction
in Yul was theprivateFunction
in Solidity. Commented Dec 20, 2023 at 6:24
When compiling internal and private functions, selectors are not created on them. This means that they cannot be called by passing the address of the function selector. Theoretically, you could find the beginning of the function in the code and pass that address instead of the selector. In practice, I have not done this.
This is not possible.
As Alexey said, a function selector is not created for private variables. Whenever someone calls a solidity smart contract, the first thing that contract does is figure out where to "direct" the call, or point the data to the correct function.
They do this by comparing the data of the call to the function selectors the smart contract has. If the function selector doesn't exist, the call reverts.