1

I'm currently reading https://blog.openzeppelin.com/upgradeability-using-unstructured-storage which has an example with an OwnedUpgradeabilityProxy smart contract which inherits from a base Proxy contract. I've come across a situation in the code where I'm unsure about the relationship between two storage slot references, namely proxyOwnerPosition and ownerPosition.

Here's a snippet of the code for context:

contract OwnedUpgradeabilityProxy is Proxy { 
    bytes32 private constant ownerPosition = keccak256("org.zeppelinos.proxy.owner"); 
    bytes32 private constant implementationPosition = keccak256("org.zeppelinos.proxy.implementation"); 

    // ... other functions and code ...

    function proxyOwner() public view returns(address owner) {   
        bytes32 position = proxyOwnerPosition;   
        assembly {
            owner := sload(position)
        } 
    } 

    // ... other functions and code ...
}

My question is: Given that OwnedUpgradeabilityProxy is an extension of the Proxy contract, does the use of proxyOwnerPosition in the proxyOwner function refer to the same storage slot as ownerPosition? In other words, are these two variables pointing to the same location in the contract's storage, or are they distinct? Thank in advance.

1 Answer 1

2
  1. I think there is a mistake in the provided code from article (the article is pretty outdated though, and there are other typos): proxyOwnerPostion should be ownerPosition as seen from this description:

The sload and sstore opcodes let us manage the storage to read and write from fixed slots referenced by custom pointers (in our example, keccak256 of org.zeppelinos.proxy.owner and org.zeppelinos.proxy.implementation).

  1. Since ownerPosition has constant modifier, it's not stored in the contract's storage. I don't know which exact version of Solidity is used in this code, but here is an excerpt from current Solidity version doc:

The compiler does not reserve a storage slot for these variables, and every occurrence is replaced by the respective value.

  1. Even if there was proxyOwnerPosition state variable(not constant) defined in Proxy, and ownerPosition was not a constant, usage of proxyOwnerPosition in OwnedUpgradeabilityProxy references it correctly from Proxy. It's because storage collision not related to inheritance, it's specifically related to proxy pattern(see below)

are these two variables pointing to the same location in the contract's storage, or are they distinct?

This situation (storage collision) happens only when a proxy contract calls implementation(logic) contract(BasicTokenis in this case) through delegatecall, and they have different storage layouts.

This problem solved in the article by using dedicated contract to define common storage - UpgradeabilityStorage.

So a storage collision only possible between a proxy contract (OwnedUpgradeabilityProxy) and implementation contract (BasicTokenis), e.g. if you remove UpgradeabilityStorage and define OwnedUpgradeabilityProxy as following (not a working example, just main idea):

contract OwnedUpgradeabilityProxy is Proxy, UpgradeabilityStorage {
    address internal _proxyOwner;
    address internal _implementation;


    function upgradeTo(address newImplementation) public onlyProxyOwner {
        address currentImplementation = implementation();
        setImplementation(newImplementation);
    }
    ...
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.