How are delegatecall and staticcall in Solidity different?
The functions staticcall and delegatecall are two low-level functions in Solidity that are used to interact with other contracts deployed on the Ethereum blockchain. Low-level functions in Solidity allow for direct interaction with the Ethereum Virtual Machine (EVM) and are normally used for advanced operations and optimizations. Although both functions seem very similar, they have their distinct features.
Apart from the very commonly used call function, both staticcall and delegatecall are also used to call a function in another contract and execute it. However, the difference between staticcall and delegatecall lies in the way the function is executed.
How are delegatecall and staticcall different?
The staticcall function allows a contract to read data from another contract but does not allow the contract to modify its state. It is called in a read-only context, which means that the called function cannot modify any storage variables and can only read from them. When a contract needs to retrieve information from another contract without altering any state variables, this function tends to be particularly useful.
On the other hand, delegatecall allows a contract to delegate some functionality to another contract while preserving its state. It is used to call a function in another contract and execute it in the context of the current contract. This means that any storage variables accessed or modified during the execution of the called function will be from the current contract. The purpose of delegatecall is typically to delegate some functionality to another contract while preserving the state of the current contract.
Let's explore the difference between the two through the use of examples.
The staticcall function code example
In the code example below, we can see how staticcall is utilized by contract class_B to access the public variable N in contract class_A. The staticcall function only reads the variable value and stores it into data:
pragma solidity ^0.5.0;/// defining contract Class_Acontract Class_A {/// declaring a public variable N to be read by Class_Buint256 public N = 25;}/// defining contract Class_Bcontract Class_B {/// creating a parameter in the read function that passes the address of Class_Afunction read_N(address Class_A_address) public view returns (uint256) {/// calling staticcall() to read the data(bool success, bytes memory data) = Class_A_address.staticcall(/// encoding the function signature of Nabi.encodeWithSignature("N()"));/// ensuring the success of staticcall() using require()require(success, "staticcall() failed.");/// returning the decoded numberreturn abi.decode(data, (uint256));}}
Explanation
Line 1: We use
pragmato specify the version of Solidity. We want to compile the code inpragma.Lines 4–9: We define
Class_A, in which we declare a public numberNto be equal to25.Line 15: We define a
read_Nfunction inClass_Bthat reads the value ofNfromClass_A, and we define a pointer address toClass_AnamedClass_A_address.Lines 18–22: We use the
staticcall()function to read the dataNfrom the contractClass_Ausing theClass_A_addressvariable as input and encode the function signature ofNusing theencodeWithSignature()function.Line 25: We use the
require()function to ensure the success of thestaticcall()function.Line 28: We return the decoded data using the
decode()function.
The delegatecall function code example
In the code example below, we can see how delegatecall is utilized by contract class_B to calculate the sum of N1 and N2 using the find_Sum function in contract class_A. The answer is decoded and stored in final_answer:
pragma solidity ^0.5.0;/// defining contract Class_Acontract Class_A {/// declaring a number to store the sumuint256 public answer;/// declaring the sum functionfunction find_Sum(uint256 N1, uint256 N2) public {answer = N1 + N2;}}/// defining contract Class_Bcontract Class_B {/// declaring an address that points to Class_Aaddress public class_A_address;/// declaring a variable to store the final resultuint256 public final_answer;/// defining a function that uses delegatecall() to call find_Sum() functionfunction find_Sum_caller(uint256 N1, uint256 N2) public {/// using delegatecall()(bool success, bytes memory data) = class_A_address.delegatecall(/// encoding the function signature of find_Sum()abi.encodeWithSignature("find_Sum(uint256,uint256)", N1, N2));/// ensuring the success of delegatecall()require(success, "delegatecall() failed.");/// storing the decoded final answerfinal_answer = abi.decode(data, (uint256));}}
Explanation
Line 1: We use
pragmato specify the version of Solidity. We want to compile the code inpragma.Lines 10–12: We define the
find_Sum()function in the contractClass_A, in which we pass the numbersN1andN2to be added.Line 19: We define a pointer address to
Class_Anamedclass_A_address.Lines 25–28: We use the
delegatecall()function to call the functionfind_Sum()from the contractClass_Ausing theclass_A_addressvariable as input and encode the function signature offind_Sum()using theencodeWithSignature()function along the inputsN1andN2.Line 35: We use the
require()function to ensure the execution of thedelegatecall()function.Line 38: We store the decoded result of addition using the
decode()function in thefinal_answervariable.
Summary
Both staticcall and delegatecall are important low-level functions in Solidity that allow contracts to interact with other contracts in different ways. The staticcall function is used to read data from another contract without modifying its state, while delegatecall is used to delegate some functionality to another contract while preserving the state of the current contract. Understanding the differences between these functions is crucial for developing efficient and secure smart contracts on the Ethereum blockchain.
Free Resources