Mistery revealed about delegatecall in Solidity

This time we will talk about the critical lower-level function delegatecall. It is widely used in a proxy pattern, for instance, when using OpenZepplin upgrades pattern. Essentially this function executes code in another contract but uses data from the caller concept. Let's dig deeper and explore more.

Storage clash

When using delegatecall function, it is vital to note that state variables should be in the same order in both contracts. If it isn't, then bizarre things can happen. For instance, data will be overwritten.

Data labyrinth

Let's have a contract, Executor, that will save our lucky number, sender address, and Ether value sent to it.

pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Executor {
  uint256 public luckyNumber;
  address public sender;
  uint256 public value;

  function setLuckyNumber(uint256 _luckyNumber) public payable {
    luckyNumber = _luckyNumber;
    sender = msg.sender;
    value = msg.value;
  }
}

Let's deploy the Executor contract and get the deployed address.

Deployed <code>Executor</code> contract

After that, we can create the Caller smart contract.

Deploy <code>Caller</code> contract
contract Caller {
  uint256 public luckyNumber;
  address public sender;
  uint256 public value;

  function setLuckyNumber(address executor, uint256 _luckyNumber) public payable {
    (bool success, bytes memory data) = executor.delegatecall(
      abi.encodeWithSignature("setLuckyNumber(uint256)", _luckyNumber)
    );

    console.log(success);
  }
}

Now when we interact with the Caller contract and set the lucky number, we can see that data has been changed only in this contract, not in the Executor contract. To verify that, we can get the lucky number from both contracts.

Interaction with the <code>Caller</code> and <code>Executor</code> smart contracts

Potential vulnerability

Using the delegatecall function is a compelling feature in Solidity programming language. With great power comes great responsibility. It can be perilous when misused.

Two major vulnerability attacks are mistakes preserving caller context and storage layout mismatch. We won't go into details this time but will follow up on that in future posts.

TL;DR

The delegatecall is a lower-level function in Solidity programming language. It unlocks the possibility to interact from one smart contract with another by using the caller's context. When using delegatecall we should be super careful because misusing it can cause serious vulnerabilities.

Links