DelegateCall- A smart contract relationship

DelegateCall- A smart contract relationship

A function which connects smart contracts

In this blog, we are going to see the .delegatecall() function in solidity. We will dive deeper into the internal workings and use cases of this function.

So devs, get ready for this knowledgeable and techy blog.

Prerequisites - You should know the basics of solidity, EVM, and how blockchain works, you can read my other blogs for these.

What is Delegatecall function?

.delegatecall() function is a low-level function that is used to call a function in a target contract from an original contract, while still preserving its own storage and context.

Simply, if you are working on a contract A and want to use a function from contract B, but, don't have the ABI of B contract, you can use .delegatecall() to call it.

An example to understand it better

Let us consider a situation where you are building a project. Hypothetically, that project involves website development skills, app development skills, blockchain, and AI all in one single project. Damn!

You are responsible for completing this project soon, but the time is limited.

Now, you have 2 options- Learn all these and implement or Just call your friend (who is working on another project right now) who is an expert in that particular skill set and gets the work done.

Now, this calling of another person who is working on his own project too, is what a delegate call does!

East peasy, isn't it?😉

Internal working

Like each of my previous blogs, I like to get in deep. Let us see how .delegatecall() works internally.

Why not call() function?

Call is a low-level function, used to call external functions.

When call() is used, the target contract executes the function in its own context, whereas, delegatecall() executes it in the context of the calling contract's current state as you can see in the diagram above.

This means that any state changes done in the target contract are made to the calling contract's storage. This feature is a crucial one when we upgrade our contracts.

💡
For example, you have to upgrade a contract. You create a new contract with a new upgraded login and code, and then you can use delegatecall() to execute the new contract's code in the context of the original contract's storage. Hence, the original contract's state can be managed by the upgraded contract.

Some points to consider

  • When using the delegatecall, the value of address(this), msg.sender, and msg.value remains same.

    The original message call parameters include msg.sender, which is the address of the account that sent the original message call, and msg.value, which is the amount of Ether sent along with the message call, if any.

  • When a contract is called using delegatecall, the values of msg.senderand msg.value passed to the target contract are not those of the delegate call but instead those of the original message call to the calling contract. This means that the target contract can’t access the actual address of the account that sent the delegatecall or the amount of Ether sent with the delegate call.

  • When a function is executed with delegatecall, reads and writes to state variables do not happen to the contract that loads and executes the function i.e. the target contract. Instead, the reads and writes happen to the contract that defines the function being executed(caller contract). This is because the execution context is that of the target contract, not the calling contract. So any changes made to state variables within the target function will happen to the storage of the target contract, not the calling contract.

Code

A short example. (Like her replies after a few weeks🙈)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// NOTE: Deploy this contract first
contract B {
    // NOTE: storage layout must be the same as contract A
    uint public num;
    address public sender;
    uint public value;

    function setVars(uint _num) public payable {
        num = _num;
        sender = msg.sender;
        value = msg.value;
    }
}

contract A {
    uint public num;
    address public sender;
    uint public value;

    function setVars(address _contract, uint _num) public payable {
        // A's storage is set, B is not modified.
        (bool success, bytes memory data) = _contract.delegatecall(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }
}
// Code from- solidity-by-example

That's all for today!

If you liked the blog, do read more of my interesting in-depth blogs.

Like and Comment about what you think about this blog!

Thanks for reading!🫡

Happy Coding!

Did you find this article valuable?

Support Agrim Sharma by becoming a sponsor. Any amount is appreciated!