Calling new function from proxy fails with `onlyProxy` modifier

I am hitting this weird issue where after upgrading a UUPS upgradeable contract. I deployed a new contract then added some functions to it and upgraded the contract. When I call the new functions the call reverts with the error: Function must be called through delegatecall. This is coming from the onlyProxy modifier in the proxy contract.

modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
execution reverted
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

The function is being called from the proxy and calling functions that existed prior to the upgrade are working as expected. Just the newly added functions fail.

In more concrete terms, I have a contract:

contract MyContract is UUPSUpgradeable {
   function f1() external {
   ...
   }

I then modify MyContract to add an f2() function. Calling f2 on the upgraded throws the error mentioned above. Calling f1 still works, which is odd because I am calling both functions from the proxy and in the exact same way. Also I tried doing a clean install of the contract (not an upgrade) and f2 works as expected on a clean install but fails on the upgraded contract.

The upgrade is being performed by calling prepareUpgrade() on the upgrades plugin, then upgradeTo() on the proxy.

This is part of a larger project so can't easily post the code here but here's an example of a failed transaction and a trace with Tenderly showing the error.

:computer: Environment

Hardhat and deploying to the Optimism Kovan network

1 Like

I'm pretty sure there is an error in Tenderly here. A trace of the transaction I got from QuickNode shows the revert step:

      {
        "pc": 1722,
        "op": "REVERT",
        "gas": 7376717,
        "gasCost": 0,
        "depth": 2,
        "stack": [
          "00000000000000000000000000000000000000000000000000000000ca25aee4",
          "00000000000000000000000000000000000000000000000000000000000001b0",
          "0000000000000000000000003e22e37cb472c872b5de121134cfd1b57ef06560",
          "00000000000000000000000000000000000000000000000000000000000186a0",
          "0000000000000000000000000000000000000000000000000000000000000024",
          "0000000000000000000000000000000000000000000000000000000000000080"
        ],
        "memory": [
          "0000000000000000000000003e22e37cb472c872b5de121134cfd1b57ef06560",
          "0000000000000000000000000000000000000000000000000000000000000101",
          "0000000000000000000000000000000000000000000000000000000000000080",
          "0000000000000000000000000000000000000000000000000000000000000000",
          "316d291d0000000000000000000000003e22e37cb472c872b5de121134cfd1b5",
          "7ef0656000000000000000000000000000000000000000000000000000000000"
        ],
        "storage": {}
      },

trace.json (181.4 KB)

From the above I can see that the revert reason is 316d291d0000000000000000000000003e22e37cb472c872b5de121134cfd1b57ef06560. On samczsun's Ethereum Signature Database we can see that 316d291d is the code for CtrlNotQuoteToken(address). So the revert reason is the custom error CtrlNotQuoteToken(0x3e22e37cb472c872b5de121134cfd1b57ef06560).

You're right the error reported on Tenderly was incorrect. That pointed me in the right direction, it wasn't specifically a problem with the upgrade, but rather a setup issue.