5

I am using a delegate call inside a view function to a view function in the implementation contract

The compiler throws an error meaning that the delegate call can change the state, therefore, can not be declared as "view".

I am using an upgradeable contract, so I prefer not to use the fallback() function. What would then be the alternative to preserve state visibility when using a delegate.call to a view function? Thank you very much!

5
  • 2
    I think the following is a relevant question: ethereum.stackexchange.com/questions/82342/… Commented Jan 10, 2023 at 20:51
  • Could you share an example of that view function ??
    – Sky
    Commented Jan 11, 2023 at 20:53
  • It is not relevant, this behavior happens with any view function
    – donoso.eth
    Commented Jan 12, 2023 at 5:35
  • 1
    you want to preserve that on solidity level (like your function would be ok to be used by other view functions ?) if that's the case I dont think it's possible
    – minhhn2910
    Commented Jan 14, 2023 at 8:44
  • @minhhn2910, yes exactly, this is what the question is about
    – donoso.eth
    Commented Jan 15, 2023 at 7:18

2 Answers 2

3
+50

Easiest way to resolve this issue would be writing a fallback() method considering first 4 bytes of calldata as a selector and matching it with necessary view functions. And you can freely use delegatecall inside fallback():

fallback() external {
    bytes4 selector = bytes4(msg.data);
    if (selector == IActor.power.selector) {
        // ...
    }
    if (selector == IActor.height.selector) {
        // ...
    }
    // ...
}

One more solution:

interface IDelegatedFoo {
    function delegatedPower() external view returns(uint256);
}

contract Foo {
    address impl;
   
    function power() external view returns(uint256) {
        return IDelegatedFoo(address(this)).delegatedPower();
    }

    function delegatedPower() external /* view */ returns(uint256) {
        (bool success, bytes memory res) = impl.delegatecall(abi.encodePacked(this.power.selector, msg.data[4:]));
        require(success, "Failed delegatecall");
        return abi.decode(res, (uint256));
    }
}

5
  • Hey, thks, but the actually as stated in the question, I am asking for a solution without using fallback()
    – donoso.eth
    Commented Jan 11, 2023 at 8:13
  • nops, I need to preserve the context and staticcall doesn't
    – donoso.eth
    Commented Jan 11, 2023 at 8:14
  • @donoso.eth added one more solution to the answer.
    – k06a
    Commented Jan 12, 2023 at 12:43
  • Great! this works. Thank you very much
    – donoso.eth
    Commented Jan 17, 2023 at 9:46
  • @donoso.eth provided code should also work with any possible arguments.
    – k06a
    Commented Jan 17, 2023 at 16:45
0

When calling another smart contract using the standard way by specifying the interface of the smart contract being called and then calling that function as follows: contract.functionName(params), the Solidity compiler checks if the target function is marked as "view" or "pure" in its interface. However, when using delegatecall, Solidity does not have knowledge of the exact function being called or whether it modifies the state or not.

A possible workaround would be to use the staticcall function instead of delegatecall. This would work as long as staticcall is not invoking a function where the calling context (storage/state-variables, msg.sender, msg.value, etc..) needs to be preserved. Since delegatecall preserves the calling context, while staticcall doesn't. staticcall works exactly the same as call with only difference that it cannot modify state of the contract being called.

3
  • This trick would not work for any functions touching storage - moreover it would return wrong result.
    – k06a
    Commented Jan 11, 2023 at 1:45
  • thks, not applicable in this case,as I need to preserve the context from the caller
    – donoso.eth
    Commented Jan 11, 2023 at 8:11
  • @k06a Yes, that's true since the storage is also part of the calling context. I'll update the answer to make that clearer. Commented Jan 11, 2023 at 20:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.