EIP-1967: Standard Proxy Storage Slots

Today I stumbled upon an interesting issue with this standard:

While testing some contracts, I ended up with this intended call chain:

MainContract -delegate-> ProxyObject
    ProxyObject -staticcall-> beacon.implementation()   [reverts here]
    ProxyObject -delegatecall-> implementation

beacon is simply a beacon with implementation() (and other things) while ProxyObject accepts a beacon in the constructor, stores it in the beacon storage slot, and declares a fallback function that reads that storage slot, calls implementation() then delegate-calls to it.

This would always revert due to function call to a non-contract account when doing beacon.implementation(), even though manually doing that method or even manually calling ProxyObject worked fine. Somehow ProxyObject being delegate-called by MainContract produced this weird error.

After a while, I figured out that since ProxyObject is delegate-called and not called regularly (or using STATICCALL), it is using the storage of MainContract which doesn’t contain the (right) beacon storage slot.

I “solved” this by storing the beacon address in an immutable field and defaulting to that if the storage slot is empty. Obviously not the best solution, but I can’t think of any other solution besides either:

  • Storing the ProxyObject's address in an immutable field during construction, and making it query itself for the beacon implementation. This would mean that ProxyObject requires a regular (non-fallback) function for this. Perhaps it could use some magic parameter and check msg.sender to detect when it’s a “request from itself” versus a regular proxy call it should delegate, but quite complex and counter-intuitive. Quite a fundamental (and complex) change though.
  • Simply not use EIP-1967 as it seems that it never anticipated the ProxyObject (the proxy contract with the beacon storage slot) to be delegate-called.

Is my reasoning in all this wrong, or did the EIP actually overlook this issue? I don’t see anything about this restriction (“proxy scripts that use beacon storage slots can’t be delegate-called”) in the EIP. I If that’s the case, perhaps adding a warning about this might not be a bad idea?