Proxy Contracts Inheritance Hierarchy

Hi everyone,

I trust everyone is well.

I’ve been studying the proxy contracts to get a better understanding of how they work. I’ve come across a few things that I don’t understand along the way. I’ve listed some questions below.

  • The BaseAdminUpgradeabilityProxy contract inherits the BaseUpgradeabilityProxy contract, but only imports the UpgradeabilityProxy.sol file. Why does this work? Does this work because the UpgradeabilityProxy contract imports and inherits the BaseUpgradeabilityProxy? Why wouldn’t you just import the BaseUpgradeabilityProxy contract into the BaseAdminUpgradeabilityProxy contract?

BaseAdminUpgradeabilityProxy Inheritance Issue

  • Similarly, the AdminUpgradeabilityProxy contract inherits the UpgradeabilityProxy contract and BaseAdminUpgradeabilityProxy contract, but only imports the BaseAdminUpgradeabilityProxy.sol file. Does it have access to UpgradeabilityProxy.sol through the BaseAdminUpgradeabilityProxy contract?

  • There is a _willFallback() function in the BaseAdminUpgradeabilityProxy contract. In that function, it invokes super._willFallback(). I thought that super only accessed functions from the immediate parent contract level, but the _willFallback() function is found in the Proxy contract which is two levels above. If you use super, does it just keep going up levels until it finds the referenced function? Is there a limit to how my inheritance levels it will go up?

The rectangles mean inherited contracts and the ovals mean imported, but not inherited.

I would appreciate any advice or comments. Cheers.


1 Like

Hi @cjd9s,

You may want to look at the proxy contracts that were migrated to OpenZeppelin Contracts 3.x which could be simpler to follow the hierarchy:

Currently the OpenZeppelin Upgrades Plugins use contracts in Upgrades Core (though this will change to use the proxy contracts in OpenZeppelin Contracts). So recommend studying these versions as this is what you would use when deploying an upgradeable project today with the Upgrades Plugins.

The proxy contracts used in the OpenZeppelin CLI, as you found, have a more complex hierarchy.
Please see: Building for interoperability: why we’re focusing on Upgrades Plugins as we are focusing our upgradeability efforts on the Upgrades Plugins exclusively.

I assume this works because an import of a parent allows inheriting from a grand parent.
It would be simpler if the imports matched the inheritance.

I assume the same applies.

I assume that super calls up the inheritance tree:

I suggest trying it out with some simple contracts to see it for yourself.

Hi @cjd9s,

Let me know if you need more information.

Hi Andrew @abcoathup,

If you ever stop hearing from me, it’s because I’m dead. I will never not need more information.

I prefer to download and inherit the proxy contracts directly. I have gone through and mostly understand what is going on. It has been interesting to learn Yul. I do have a few questions, however.

  1. My previous post had a question about super. I noted that the _willFallback() function in BaseAdminUpgradeabilityProxy contract invokes super._willFallback(). However, there is no _willFallback() in the immediate parent contract. It’s actually two levels up in the Proxy contract.

As you suggested, I created some simple contracts in Remix. And, lo, even though there was no function in the parent contract, there was one in the grandparent contract and it worked.

However, you copied a link to Solidity docs about inheritance. I noted the following:
“It is possible to call functions further up in the inheritance hierarchy internally by … using super.functionName() if you want to call the function one level higher up in the flattened inheritance hierarchy (see below).”

So, if I’m reading this right, it says that super can be used to call a function one level higher up in the inheritance hierarchy. However, it seems to work at least two levels up. Am I reading this wrong, do I not understand the specific order in the directed acyclic graph (DAG) of base classes, or is Solidity Docs wrong?

  1. In the ProxyFactory contract there is a deployMinimal() function.

// store next available memory address in clone

let clone := mload(0x40)

// Set the bytes of memory from the next available memory address (clone) to clone+32
// equal to
// 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
// which is the constructor of the clone contract

mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)

// Set the bytes of memory from clone+20 to clone+52 equal to targetBytes
// which is the address of the implementation (logic) contract

mstore(add(clone, 0x14), targetBytes)

// Set the bytes of memory from clone+40 to clone+72 is set to equal
// 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000

mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)

So, am I correct that the first store operation is the clone constructor? What is the signifiance of the final (third) store operation?

  1. If I want to use the Proxy Factory contract, do I need to inherit the InitializableAdminUpgradeabilityProxy into my own contract and then change the import in the Proxy Contract from InitializableAdminUpgradeabilityProxy to my contract? And then replace the InitializableAdminUpgradeabilityProxy reference to my contract in the constructor function?

As always, any insight you could provide would be great. Cheers.