23

Can you pass a function as a parameter in Solidity?

My guess: There is the concept of address in Solidity, but they represent contracts. Contracts can have fallback functions, but I don't think you can give them parameters. Thinking about passing the function as a parameter by address like you would do in C.

Is there a legit way to pass functions as parameters, or if not, is there a hacky way?

If there is, how? And if there isn't, why not?

7
  • Solidity is too costly for functional programming
    – niksmac
    Apr 25, 2016 at 17:53
  • I understand that a lot of functional programming concepts like functional composition might be too expensive, but I'm curious specifically if passing functions is too expensive. Passing a pointer to a function should be pretty cheap. Apr 25, 2016 at 17:57
  • Both are different function as a parameter and functional programming right?
    – niksmac
    Apr 25, 2016 at 17:59
  • Yeah they are different. Passing functions as a parameter is just a helpful technique that functional programming languages utilize. For instance, you can pass a function as a parameter in C, but you wouldn't really call C a functional programming language. Apr 25, 2016 at 18:03
  • 2
    @NikhilM : " Solidity is too costly for functional programming " Just curious can you explain more why it's too costly?
    – dbryson
    Apr 25, 2016 at 18:19

5 Answers 5

17

Functions (aka Methods) are specified by the ABI, and have a Method ID, which is the first 4 bytes of the sha3 (Keccak-256) of the method's signature.

Here's an example of invoking someFunction on contract: contract.call(bytes4(sha3("someFunction()")))

Here is a tested function with passing a methodId as a parameter:

contract C1 {
    uint public _n;  // public just for easy inspection in Solidity Browser

    function foo(uint n) returns(uint) {
        _n = n;
        return _n;
    }

    function invoke(bytes4 methodId, uint n) returns(bool) {
        return this.call(methodId, n);
    }
}

Test it in Solidity Browser by using "0x2fbebd38", 9 as the parameters to invoke, then see that _n equals 9.

Notes:

  • 0x2fbebd38 is the result of bytes4(sha3("foo(uint256)")) (don't forget the need to use canonical types, in this case uint256, per the ABI.)

  • Return values from call and callcode are boolean, either the call succeeded or the call failed. It is not possible to return a value from call since that would require the contract to know the return type beforehand.

3
  • 1
    Of course, that's an external function - internal ones exist too, and passing a pointer to one would be a different matter entirely... Apr 26, 2016 at 14:09
  • It seems you can see this technique being used here: github.com/nexusdev/dappsys/blob/… Apr 28, 2016 at 1:26
  • 3
    This method has two drabacks: 1) costs way more gas, 2) is treated as call from "external" and hence non-accessible if target function is marked "internal"
    – SCBuergel
    May 1, 2017 at 16:15
9

To add on to Nick Johnson's answer, function types in recent versions of solidity allow you to describe function pointers now:

http://solidity.readthedocs.io/en/latest/types.html#function-types

Function types are the types of functions. Variables of function type can be assigned from functions and function parameters of function type can be used to pass functions to and return functions from function calls. Function types come in two flavours - internal and external functions

1
  • What s the abi in that case? Jan 27, 2021 at 14:43
6

eth's answer applies to external function calls (between contracts, or by using the external interface to call your own contract); here I'll attempt to answer for internal function calls.

Solidity currently provides no syntax for describing the type of a function pointer, so you can't take them as arguments or return values. However, functions are first-class and can be assigned to variables using var; here's the example from the Solidity user manual:

contract FunctionSelector {
  function select(bool useB, uint x) returns (uint z) {
    var f = a;
    if (useB) f = b;
    return f(x);
  }
  function a(uint x) returns (uint z) {
    return x * x;
  }
  function b(uint x) returns (uint z) {
    return 2 * x;
  }
}
2
  • 1
    Thanks for helpful answer :) I thought there might be other angles and I didn't internalize that part of the docs to remember it.
    – eth
    Apr 26, 2016 at 18:43
  • Not sure when that changed, but this is not correct anymore, as it seems at least as early as 0.4.16 solidity does provide syntax for describing the type of functions, so you can pass them as arguments (solidity.readthedocs.io/en/v0.4.24/types.html), see Michael's answer Aug 22, 2018 at 12:45
1

For anyone coming from google looking for a quick example of how to pass a function as a parameter, check out the following code excerpt (source https://docs.soliditylang.org/en/latest/types.html#function-types)

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

library ArrayUtils {
    // internal functions can be used in internal library functions because
    // they will be part of the same code context
    function map(uint[] memory self, function (uint) pure returns (uint) f)
        internal
        pure
        returns (uint[] memory r)
    {
        r = new uint[](self.length);
        for (uint i = 0; i < self.length; i++) {
            r[i] = f(self[i]);
        }
    }

    function reduce(
        uint[] memory self,
        function (uint, uint) pure returns (uint) f
    )
        internal
        pure
        returns (uint r)
    {
        r = self[0];
        for (uint i = 1; i < self.length; i++) {
            r = f(r, self[i]);
        }
    }

    // more methods...
}

0

As noted in other answers, there are distinct answers to your question depending on whether you are talking about calling the function "externally" or calling it "internally."

Within the same contract, recent Solidity versions include language features to pass around a "function pointer" (it's not really a pointer per-se but it behaves like one). In other words, there exist variables of type "function." This is what I mean by "internally" passing around a function and calling it.

For "external" calls, i.e., function calls to an on-chain address, you would use Solidity address type member "call()" and/or Yul (assembly) etc to emulate a contract call as Solidity would compile such a function call when expressed as "someAddr.someFunction()" ... and this amounts to encoding the function signature as a selector (selectors are the first 4 bytes of the hash of the function signature) plus the arguments you want to pass, and executing the necessary EVM bytecodes (assembly) using those data.

In other words, for "external" functions, the "function pointer" is the tuple (address,selector).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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