3

I am a newbie studying Uniswap V3's github code. I came up with noDelegateCall.sol and I found out the way to prevent a contract to be delegatecalled from another cotract. As I tried to implement this, I caught up with the problem.

pragma solidity >0.8.0;

contract Receiver {
    string greeting = "Hello";
    address private immutable original;
    event Greeting(string greeting, address original, address addressThis);

    constructor() {
        original = address(this);
    }

    function checkNotDelegateCall() private view {
        require(address(this) == original);
    }

    modifier noDelegateCall() {
        checkNotDelegateCall();
        _;
    }
    
    function greet() external noDelegateCall {
        emit Greeting(greeting, original, address(this));
    }
}

contract Sender {
    string greeting = "Hi";
    
    function delegatedGreeting(address _contract) external {
        (bool success,) = _contract.delegatecall(
            abi.encodeWithSignature("greet()")
        );
    }
}

If I call function delegatedGreeting, I expect the function to be reverted because variables original and address(this) differs. However, although it emits an empty event, it still doesn't reverts. Why does this happen?

Jasperan
  • 2,154
  • 1
  • 16
  • 40
SungJunEun
  • 53
  • 3

1 Answers1

2

When the low-level delegatecall reverts, it doesn't automatically revert the main transaction. Instead, it returns false as the first return value of the delegatecall() function.

So you need to check the return value (in your case bool success) and validate that.

function delegatedGreeting(address _contract) external {
    (bool success,) = _contract.delegatecall(
        abi.encodeWithSignature("greet()")
    );
    require(success == true, "delegatecall failed");
}
Petr Hejda
  • 40,554
  • 8
  • 72
  • 100