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 thatProxyObject
requires a regular (non-fallback) function for this. Perhaps it could use some magic parameter and checkmsg.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?