4

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 3

3
+50

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:

  1. 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();
    }
}
  1. Compile it using the following command:

solc --optimize --ir-optimized -o out ./Foo.sol --overwrite

  1. 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 :)

5
  • 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 the external_fun_anotherFunction function is done like that: external_fun_anotherFunction() // call to private function
    – BonisTech
    Commented Dec 19, 2023 at 20:55
  • The private function privateFunction from the solidity code is compiled to external_fun_anotherFunction by the compiler in this case. But you can also write such private functions manually.
    – BonisTech
    Commented Dec 19, 2023 at 20:58
  • 1
    You are right. The prefix external_fun_ throw me off.
    – Ismael
    Commented Dec 19, 2023 at 23:08
  • 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 that external_fun_anotherFunction in Yul was the privateFunction in Solidity.
    – BonisTech
    Commented Dec 20, 2023 at 6:24
2

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.

2

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.