Solidity Internal Functions – Calling Internal Functions Using Function Selector

function-selectorsolidity

I have the following contract:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;
contract Selectors {
    
    bool public called;
    function callOtherFunc() public {
        bytes4 func = this.otherFunc.selector;
        (bool success, ) = address(this).call(abi.encodeWithSelector(func, true));
    }
    
    function otherFunc(bool _called) internal {
        called =_called; 
    }
}

However, I'm running into the error:

TypeError: Member "otherFunc" not found or not visible after argument-dependent lookup in contract Selectors.

But if I make otherFunc public, it compiles fine.

Additionally, the other global functions don't seem to work

(bool success, ) = address(this).call(abi.encodeWithSignature("otherFunc(bool)", true));

or

(bool success, ) = address(this).call(abi.encodeWithSelector(bytes4(keccak256(bytes("otherFunc(bool)"))), true));

Is this just the way it is? Is there another way to call an internal function using the selector?

Best Answer

There is no way to directly call an internal function through function selector.

The entry point of a contract is basically a jump table with additional checks, looking for a matching function selector between the first 4 bytes of calldata and the exposed functions of the contract.

Marking your function internal excludes it from that jump table, making it inaccessible from an external call.

Take the following contract :

contract SelectorsPublic {
    
    bool public called;
    
    function otherFunc(bool _called) public {
        called =_called; 
    }
}

I deployed it here. You can check the decompiled bytecode here.

With an explicit check for the function selector in the main function body (As a reminder: keccak256("otherFunc(bool)") == 0x64feed76...) :

else if (var0 == 0x64feed76) {
            // Dispatch table entry for 0x64feed76 (unknown)
            var1 = 0x0073;
            var2 = 0x006e;
            var4 = 0x04;
            var3 = var4 + (msg.data.length - var4);
            var2 = func_0109(var3, var4);
            func_006E(var2);
            stop();
        } 

By opposition, this contract (with internal visibility) :

contract SelectorsInternal {
    
    bool public called;
    
    function otherFunc(bool _called) internal {
        called =_called; 
    }
}

Deployed here, and decompiled here presents no such check. Meaning that there is no path leading to otherFunc from the main entry point of the smart contract.

Only internal calls to it will be allowed, external ones will not find a matching entry and therefore revert (or won't even compile like in your example).

EDIT : If from the decompiled bytecode you are wondering what the check for 0x50f9b6cd are : those are just the same principle applied to the auto generated getter for : bool public called, keccak256("called()") = 0x50f9b6cd....

EDIT 2:

Only external (or public) functions have the following members:

.address: returns the address of the contract of the function.

.selector: returns the ABI function selector

So by using either of these, you are calling the functions "externally".

Related Topic