Eternal Storage Pattern / Upgradable Contracts on Ethereum -1

Updates to the software are a socially accepted phenomenon by the community of applications users and its developers alike. Apps are updated to add new features, fix undesired behaviours, or improve the compatibility of the software on the runtime platforms. Version changes are done by reinstallation or by application of code patches. The flexibility to send patches to the software allows developers to plan and ship a minimum viable product. This level of flexibility is not usually available with Smart Contracts. Any contract deployed on a Blockchain is immutable, and once installed, the program cannot be changed or updated. The contract's deployment and its state are immutable on-chain. A DApp can be composed of single or multiple contracts. Though the components in the DApp are unchangeable, if designed suitably, the child contracts forming the DApp could be replaced for newly deployed ones.

There are multiple identified design approaches to pen updatable DApps. These solutions usually round up around the separation of concerns. This series of posts will explain some commonly used upgradeable patterns with code (non-audited). The first post will explain the Eternal Storage Pattern.

Eternal Storage pattern.

post image

Eternal storage pattern allows users to have on-chain upgrades to logic while keeping the data on-chain unaffected. The pattern implementation proposes having specific and independently deployed contracts for the DApp's business logic and storage. The storage responsibility is delegated and encapsulated in a single Solidity contract. The storage contract is purposed to hold the data of DApp forever, and per this pattern, it cannot have any changes done to it. The other part, the external-facing contract with DApp's logic, is designed for no or minimal state changes. The statelessness makes the logic a replaceable candidate if an update to DApp is required.

Storage Contract

Design: A lot of thought should go into the design of this contract considering it remains outside the scope of upgrade. It should stay simple to survive this constraint. This contract will have all the variables the contract's business requires and would hold the state changes to DApp across its variables (Eg. token minting, change in balance).

State: This contract would have state managed in variables that would be private. The access to state variables would be allowed only by external access methods. The example contract.

Security: The Storage contract has the state of the DApp that must stay secure. Stray apps or other callers should read/edit the current state or else the DApp would be useless. Thus for every invocation, the address of the called is validated. This contract is added with two variables to hold the owner address(_owner) and the current logic address(_caller). These would be utilized in the modifiers written to restrict invocation of access methods. (As usual,) The contract owner address is set from the constructor when the storage contract gets installed. The logic contract's address can be set, later after that gets deployed as the valid caller in the Storage contract. All the state variables' access methods would be accessible only by the logic contract.

post image

Logic Contract

Design: The latest business logic of the DApp would be encapsulated in the Logic contract. Since this contract could be repealed from usage, it should be designed to be completely stateless.

Connection: Logic Contract imports the code of Storage contract, and this by way storage contract's interface made aware. A Contract Can be interacted with in Solidity if its address and interface are known to the caller.

post image

Deployment

  1. Admin or the DApp owner deploys the storage contract and captures the address. The storage contract accepts the deployer address as the owner.

  2. Admin deploys the logic contract with the storage contract's address passed in the constructor. Admin captures the Logic contract address.

  3. Admin (also the storage owner) calls the storage contract's setAllowedCallerAddr with Logic contracts address.

  4. Share the Logic Contract address as Dapp's address to clients.

post image

Upgrade

post image
  1. Deploy the new Logic contract, with the storage address passed in the constructor.

  2. Update the storage contract with the new allowed caller address.

  3. Share the new logic contract address as the DApp's address.

  4. Optional step, destroy the old logic contract.

post image

Pros

  • Simple implementation.

  • Easy and cheap upgrades.

  • No low level calls.

Cons

  • Requires Controlled access to storage.

  • Solid storage strategy required, no updates possible.

DApps also need upgrades, and the Eternal storage pattern is one of the easiest ways to design and deploy upgradeable contracts. A disclaimer, if a firm leaves a window for upgrades without a proper strategy in its decentralized product, adoption and reputation will take a hit.

The next post in series will cover proxy based upgradeable contracts.

I would be sharing more of my experiences on smart contracts, on my Twitter account(@sijeesh). Follow me there, if you wish to learn more. Feel free to DM for questions, if any.