# Understanding Function Injection in Solidity

By [Jumaru](https://paragraph.com/@jumaru) · 2024-09-29

---

### Introduction

In Solidity, you can pass functions as arguments to other functions or store them in variables. This technique is often referred to as **function injection** or **function pointers**. It allows for greater flexibility, as you can dynamically choose which function to call based on certain conditions.

For beginners and intermediate developers, this can seem confusing at first because the syntax is a bit different from how we usually call functions. In this article, we'll break it down step-by-step with clear examples to help you understand how **function injection** works in Solidity.

* * *

### What Is Function Injection?

Function injection in Solidity refers to the concept of passing a function as a parameter or storing a function in a variable so that it can be called dynamically later. This gives your code more flexibility, as you can choose which function to call based on different conditions.

In Solidity, you can declare a **function type** as a variable, and later assign it a specific function that matches the same signature (input parameters and return types). Once assigned, you can invoke that function just like any other function call.

* * *

### Basic Example of Function Injection

Let's start with a simple example to understand how it works.

#### Example: Calculator with Dynamic Operations

Imagine you are building a basic calculator contract, and you want to dynamically choose between different arithmetic operations like addition, subtraction, multiplication, and division based on user input.

Here’s how you could do that using function injection.

#### Step 1: Define Basic Arithmetic Functions

We’ll start by defining internal functions that perform the basic arithmetic operations (`add`, `subtract`, `multiply`, `divide`).

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    contract Calculator {
    
        // Basic arithmetic functions
        function add(uint a, uint b) internal pure returns (uint) {
            return a + b;
        }
    
        function subtract(uint a, uint b) internal pure returns (uint) {
            return a - b;
        }
    
        function multiply(uint a, uint b) internal pure returns (uint) {
            return a * b;
        }
    
        function divide(uint a, uint b) internal pure returns (uint) {
            require(b > 0, "Cannot divide by zero");
            return a / b;
        }
    }
    

Each function takes two numbers (`a` and `b`) and returns the result of the respective operation.

#### Step 2: Define a Function Type Variable

Next, we'll create a **function type variable** to store a reference to any of the above functions. This variable will allow us to dynamically choose which operation to perform.

    function (uint, uint) internal pure returns (uint) operation;
    

This line of code declares a variable `operation` that can hold any function matching the following signature:

*   Takes two `uint` parameters.
    
*   Returns a `uint`.
    
*   Is an `internal` and `pure` function.
    

#### Step 3: Inject the Function and Execute It

Now, let's create a function that accepts an operation type (addition, subtraction, etc.) and uses function injection to call the correct function dynamically.

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    contract Calculator {
    
        // Define basic arithmetic functions
        function add(uint a, uint b) internal pure returns (uint) {
            return a + b;
        }
    
        function subtract(uint a, uint b) internal pure returns (uint) {
            return a - b;
        }
    
        function multiply(uint a, uint b) internal pure returns (uint) {
            return a * b;
        }
    
        function divide(uint a, uint b) internal pure returns (uint) {
            require(b > 0, "Cannot divide by zero");
            return a / b;
        }
    
        // Use function injection to perform the desired operation
        function calculate(uint a, uint b, string memory operationType) public pure returns (uint result) {
            function (uint, uint) internal pure returns (uint) operation;
    
            // Dynamically assign the correct operation based on input
            if (keccak256(bytes(operationType)) == keccak256(bytes("add"))) {
                operation = add;
            } else if (keccak256(bytes(operationType)) == keccak256(bytes("subtract"))) {
                operation = subtract;
            } else if (keccak256(bytes(operationType)) == keccak256(bytes("multiply"))) {
                operation = multiply;
            } else if (keccak256(bytes(operationType)) == keccak256(bytes("divide"))) {
                operation = divide;
            } else {
                revert("Unknown operation type");
            }
    
            // Call the injected function and return the result
            return operation(a, b);
        }
    }
    

### How It Works:

*   `operation`: This is the function type variable that can hold any internal function with the signature `(uint, uint) -> uint`.
    
*   **String Matching**: Based on the `operationType` string passed to the `calculate` function, the correct function (`add`, `subtract`, `multiply`, or `divide`) is assigned to `operation`.
    
*   **Function Call**: Once the function is assigned to `operation`, we can call it using `operation(a, b)`, and it will return the result of the appropriate arithmetic operation.
    

### Example Usage:

1.  **Calling** `calculate` to Add:
    
        calculate(10, 5, "add") // returns 15
        
    
2.  **Calling** `calculate` to Multiply:
    
        calculate(10, 5, "multiply") // returns 50
        
    
3.  **Calling** `calculate` to Divide:
    
        calculate(10, 2, "divide") // returns 5
        
    

### Detailed Explanation:

#### Function Type Variables

In Solidity, you can create variables that can store references to functions. These variables allow you to dynamically decide which function to call at runtime. In our example, `operation` is a variable that can hold any function that:

*   Is **internal** (i.e., can only be called inside the contract).
    
*   Is **pure** (i.e., it doesn’t read or modify the contract’s state).
    
*   Takes two `uint` parameters and returns a `uint`.
    

#### Assigning a Function to a Variable

    if (keccak256(bytes(operationType)) == keccak256(bytes("add"))) {
        operation = add;
    }
    

Here, we're checking the string passed in (`"add"`, `"subtract"`, etc.), and based on the result, we assign the corresponding function (`add`, `subtract`, `multiply`, or `divide`) to the `operation` variable.

#### Calling the Function

Once we assign a function to the variable `operation`, we can call it like a normal function:

    return operation(a, b);
    

This dynamically calls the correct function based on the input. If `operation` points to `add`, it will perform the addition. If it points to `divide`, it will perform the division, and so on.

* * *

### Another Example Using Function Injection with a Different Syntax

Solidity allows you to pass function types as parameters to other functions. In the previous example, we stored a function reference in a variable. Now, we will demonstrate how to **pass functions directly as arguments** using this syntax:

    function foo(function () internal pure returns (uint) bar) internal pure returns (uint) {
        return bar();
    }
    

In this case, `foo` is a function that accepts another function (`bar`) as its argument. This function (`bar`) must match the signature: **no parameters and returning a** `uint`. Inside `foo`, we can call `bar()` directly.

### Rewriting the Calculator Example Using This Syntax

We will now rewrite the previous dynamic calculator example where instead of assigning a function to a variable, we pass the function directly as an argument to another function.

### Updated Calculator Example with Direct Function Passing:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    contract Calculator {
    
        // Define basic arithmetic functions
        function add(uint a, uint b) internal pure returns (uint) {
            return a + b;
        }
    
        function subtract(uint a, uint b) internal pure returns (uint) {
            return a - b;
        }
    
        function multiply(uint a, uint b) internal pure returns (uint) {
            return a * b;
        }
    
        function divide(uint a, uint b) internal pure returns (uint) {
            require(b > 0, "Cannot divide by zero");
            return a / b;
        }
    
        // Function that accepts an operation function and calls it
        function executeOperation(
            uint a, 
            uint b, 
            function(uint, uint) internal pure returns (uint) operation
        ) internal pure returns (uint) {
            return operation(a, b);
        }
    
        // Main function to handle different operations
        function calculate(uint a, uint b, string memory operationType) public pure returns (uint result) {
            // Pass the correct function directly to executeOperation
            if (keccak256(bytes(operationType)) == keccak256(bytes("add"))) {
                return executeOperation(a, b, add);
            } else if (keccak256(bytes(operationType)) == keccak256(bytes("subtract"))) {
                return executeOperation(a, b, subtract);
            } else if (keccak256(bytes(operationType)) == keccak256(bytes("multiply"))) {
                return executeOperation(a, b, multiply);
            } else if (keccak256(bytes(operationType)) == keccak256(bytes("divide"))) {
                return executeOperation(a, b, divide);
            } else {
                revert("Unknown operation type");
            }
        }
    }
    

### Explanation of the Updated Example:

*   `executeOperation` Function: This function accepts two numbers (`a`, `b`) and a function (`operation`) that performs an arithmetic operation. The function signature is:
    

    function(uint, uint) internal pure returns (uint)
    

*   This means the function passed must accept two `uint` parameters and return a `uint`. Inside `executeOperation`, we simply call the passed function (`operation(a, b)`).
    
*   **Passing Functions Directly**: In the `calculate` function, instead of assigning a function to a variable, we pass the function directly to `executeOperation`:
    

    return executeOperation(a, b, add);
    

*   Here, `add`, `subtract`, `multiply`, or `divide` are passed directly as arguments.
    
*   **Dynamic Execution**: Based on the `operationType` string, the appropriate function is passed to `executeOperation`. This function is then called with the provided inputs (`a`, `b`).
    

* * *

### How Does This Syntax Work?

In this syntax:

    function foo(function () internal pure returns (uint) bar) internal pure returns (uint) {
        return bar();
    }
    

*   `foo` is a function that takes another function `bar` as an argument.
    
*   The parameter `bar` must be a function with the signature `() internal pure returns (uint)`, meaning:
    
    *   It takes no arguments.
        
    *   It returns a `uint`.
        
    *   It is an `internal` and `pure` function.
        
*   Inside `foo`, you simply call the function `bar()`.
    

This syntax is very powerful because it allows you to write generic code where behavior (in the form of a function) can be injected and executed at runtime. It simplifies logic by removing the need for conditional function assignments and allows direct function execution.

### Key Benefits of Passing Functions Directly:

*   **Code Simplification**: By passing functions directly as arguments, you reduce the need for temporary variables to store function references. It makes the code cleaner and more concise.
    
*   **Flexibility**: This pattern allows you to inject different logic dynamically into a function, making your code more modular and reusable.
    
*   **Encapsulation**: The logic for calling the function is encapsulated in the higher-order function (like `executeOperation`), making it easier to maintain and extend.
    

* * *

### Why Use Function Injection?

Function injection is useful when you need to write flexible, reusable code that can handle different logic paths without repeating code. Instead of writing separate logic for each operation or duplicating code, you can:

1.  **Dynamically choose** a function at runtime.
    
2.  **Pass functions** as parameters, making your code more modular and reusable.
    
3.  **Reduce code duplication**, since you only need to write the core logic once.
    

* * *

### Conclusion

In Solidity, function injection is a powerful tool for dynamically selecting and calling functions. By using function type variables, you can build flexible and modular contracts that adapt to different inputs and conditions. In our example, we saw how you can implement a dynamic calculator that switches between different arithmetic operations using this technique.

The key takeaway is that **function type variables** allow you to inject different behaviors into your code, reducing duplication and increasing flexibility. This technique is particularly useful in scenarios where your contract needs to adapt to different behaviors at runtime.

* * *

Feel free to experiment with this pattern to make your smart contracts more flexible and adaptable!

---

*Originally published on [Jumaru](https://paragraph.com/@jumaru/understanding-function-injection-in-solidity)*
