We know smart contracts are lines of code on the blockchain that execute transactions automatically. These contracts are known for their immutable nature, and once they’re set in action, they stay that way forever.
On the other hand, we’ve got something called upgradable contracts. It means developers can tweak the code if needed. Bugs show up? They can patch ’em up without starting from scratch. It’s pretty handy for keeping things smooth and efficient.
Concerns arise around who controls these changes. But having the power to upgrade isn’t automatically bad. It’s about using it responsibly and having a contract that can adapt to the evolving needs.
Inspite of the debates and arguments about the upgradable smart contracts, why are they still proving indispensable?
Let’s explore in our article today the advantages one can have with upgradability, what is the working behind the upgradable smart contract and how to practically implement them.
What are smart contract upgrades?
Smart contracts themselves are inherently immutable once deployed. So, rather than modifying the code of an already deployed smart contract, upgrading revolves around implementing a new smart contract to replace an existing one.
This process of substitution allows the introduction of new functionalities or modifications without altering the original code directly.
Essentially, it involves swapping an old contract for a new one while the older versions remain unchanged and immutable on the blockchain.
This ensures that end users of the dApp experience a seamless transition without having to adjust their interaction with the application.
What’s the need for upgradable contracts?
Adapting to dynamic business needs: Upgradability ensures that contracts can evolve, accommodating new functionalities or modifications demanded by evolving markets or user needs.
Security and Bug Fixes: Upgradable contracts enable quick fixes to identified issues, enhancing security and mitigating risks promptly.
Unlike immutable contracts, upgradable ones allow developers to create new versions and update contracts, swiftly resolving any identified vulnerabilities.
Efficiency: Upgrading streamlines the process of introducing improvements, ensuring enhanced efficiency without the need for complete redevelopment.
Scope for continuous improvement: These contracts facilitate continuous development, allowing for ongoing improvements in functionality, security, and performance.
Inclusive Governance: The system of upgradable contracts encourages community participation in shaping the contract’s evolution.
Community involvement ensures that the contract evolves in a transparent manner, aligning with the collective interests and needs of the stakeholders.
Approach to upgrading smart contract
Having highlighted the importance of upgradable smart contracts in addressing the limitations of immutable contracts, let’s find out how to do it.
The earliest was the data-separation pattern, which separated smart contracts into logic and storage entities, storing and managing the state respectively.
However, constant interactions between logic and storage incurred considerable gas costs, rendering this approach inefficient.
That’s why modern upgradability smart contracts rely on proxies to manage upgradability efficiently.
A fixed proxy contract maintains the system’s state while holding a changeable logic address. Unlike conventional contracts, users interact with the proxy, directing their calls to the business logic contract.
This setup ensures that logic contracts don’t store user data, simplifying upgrades. When an upgrade is necessary, deploying a new logic contract involves only updating the address in the proxy contract.
Proxy Patterns and Their Variants
The three common variants of the proxy pattern are:
Simple Proxy Pattern
The simple proxy involves a fixed proxy contract and an execution contract (logic contract). The proxy maintains its execution context, storage, and ether balance.
Using delegatecall, the proxy invokes the logic contract’s code within its execution context, allowing the logic contract to alter the proxy contract’s state while keeping the application’s state intact.
Transparent Proxy Pattern
In this pattern, the end-user calls directly access the logic contract, except for control functions managed by the proxy for upgradability. This approach mitigates function selector clashes but incurs additional gas costs due to loading logic contract addresses for each delegatecall.
Universal Upgradable Proxy Standard (UUPS) Pattern
The Universal Upgradable Proxy Standard (UUPS) establishes a standardized method for creating proxy contracts with universal compatibility across all contracts. Unlike traditional proxy patterns, UUPS alters the upgrade process by shifting the upgrade functionality to the logic contract itself.
This is achieved through a specific design where the logic contract inherits from a designated “proxiable” parent contract that holds the upgrade functionality. By integrating the upgrade logic into a parent contract, this pattern allows the compiler to detect potential clashes within the same contract, reducing the likelihood of clashes between different contract components.
However, the implementation and maintenance of smart contracts using the UUPS pattern might pose some challenges compared to other proxy patterns due to their specific structure and inheritance requirements.
Action steps for implementing upgradable contracts
Here’s a step-by-step guide to creating and upgrading a smart contract. This process allows you to create, deploy, and upgrade your smart contracts using Hardhat and OpenZeppelin‘s upgradable contract libraries.
Ensure you have Node.js (version 12 or higher) and Hardhat (version 2 or higher) installed on your system.
Step 1: Setting Up the Project
1. Create a new project directory and navigate to it.
2. Initialize a new Node.js project:
3. Install necessary packages:
4. Set up your Hardhat project:
Step 2: Writing the Initial Contract
Create a new file named ‘MyContract.sol’ in the contracts directory and add the initial contract code. For example:
Step 3: Deploying the Initial Contract
Create a new file, ‘my-contract.js’ in the task folder and add the deployment code:
Step 4: Preparing the Upgraded Contract
Update ‘MyContract.sol’ to reflect the upgraded contract:
Step 5: Upgrading the Contract
Add an upgrade task in ‘my-contract.js’ to upgrade the contract:
Ensure to set up configurations in ‘.env’ and ‘hardhat.config.js’ and then execute the deployment and upgrade commands with Hardhat, adjusting the network flag as needed.
Talking of the upgradable smart contracts, security risks lurk in various forms, from missing initialization calls and potential storage clashes to unauthorized upgrades and denial-of-service vulnerabilities post-upgrade.
If left unchecked, these risks can cascade into damaging exploits, posing a threat to both user funds and data integrity.
To protect these contracts against such vulnerabilities, a meticulous smart contract audit from reputed firms like QuillAudits becomes imperative.
In the quest for the agility and flexibility that upgradable contracts offer, security must stand as an unwavering cornerstone. An audited and secure contract ensures that these contracts are void of errors or loopholes that could be exploited.