Revert on unmatched function with upgradeable contract?

:computer: Environment
@openzeppelin/cli: 2.8.0
@openzeppelin/contracts-ethereum-package: 2.5.0
@openzeppelin/upgrades: 2.8.0

:memo:Details

I’m using the OpenZeppelin Proxy Upgradeability pattern faciliated through the OpenZeppelin CLI.

Everything is working well - my one question is wondering if it’s possible to revert() a transaction when the underlying “logic” contract (the one the developer writes when using this pattern) doesn’t have a function matching the transaction encoded data.

Example: My contract has a payable function signature of buyStamp(). However, Bob calls buyStamp(5) on my contract - and sends 5 ETH. The signature buyStamp(uint256) doesn’t exist so the proxy forwards it to my contract, and nothing happens - Bob doesn’t buy a stamp. However:

  1. To Bob it originally appears the transaction was successful (it didn’t fail).
  2. Bob just gave away 5 ETH for nothing in return.

I’m wondering if it’s possible the transaction fails instead of the current behavior.

I think generally the best practice is something like below, where you’d check the msg.data.length is empty (ie. they weren’t trying to call a function). I believe this would be added to the “logic” contract (not OpenZeppelin’s Proxy contract).

function() payable { require(msg.data.length == 0); }

However, I’m not sure if that will mess with the OpenZeppelin proxy pattern functionality.

Thanks for any help!

1 Like

Hi @Dyno,

My understanding is that if the function signature doesn’t exist then calling the non-existant function via the proxy will revert unless there is a fall back function in the logic contract. So I am not sure if I can see how we would get to your scenario.

Are you able to put together an example?

Hi @abcoathup

Thanks for the response.

Ah, ok that’s good news - yes, in the contract in question there is a fallback function. Currently it is defined as:

function() external payable {}

I’m guessing that I need to either remove it, or require the msg.data.length == 0. I need the fallback definition so I can receive Ether transfers - therefore I’m guessing the solution is to do the latter.

From what you’ve said, the proxy will still forward the logic contract the non-existent function - but now it will revert as the msg.data.length > 0. Also, my understanding is that the proxy contract doesn’t forward all function calls to the logic contract’s fallback function - only ones where a matching function signature is missing.

Is this correct?

Yes exactly.

This is not exactly correct. You can read more about this in Transparent Proxies and Function Clashes in the docs.

2 Likes

Awesome, thanks for the clarification @frangio

1 Like