facebook

How to Build Upgradable Smart Contracts with Proxies

Posted By : Ashutosh

Apr 27, 2025

Smart contracts are immutable by default. Once deployed, their code cannot be changed. While this ensures trustlessness, it becomes a problem when you need to fix bugs, add features, or adapt to new requirements. 

 

Upgradable smart contracts solve this problem by allowing developers to update contract logic without changing the contract's address.

 

This is where proxies come into play. Proxies act as middlemen, delegating function calls to the latest version of your logic contract. In this guide, we'll explain how proxies work and walk you through building your first upgradable contract. If you are looking to know more about smart contracts, visit our smart contract development services

 

Types of Proxy Patterns

 

  1. Transparent Proxy: Distinguishes between admin and user calls.
  2. UUPS (EIP-1822): Logic upgrades are handled by the implementation contract.
  3. Beacon Proxy: Allows many proxies to share a single upgrade source.

 

For simplicity, we'll focus on the Transparent Proxy pattern in this guide.

 

Also, Check | Creating Cross-Chain Smart Contracts with Polkadot and Substrate

 

Step-by-Step Guide to Building an Upgradable Contract

 

Prerequisites

 

  • Basic knowledge of Solidity and JavaScript.
  • Node.js and npm installed.
  • A code editor (e.g., VS Code).

 

1. Setting Up the Environment

 

Install Hardhat and OpenZeppelin (a library for secure smart contracts):

 

mkdir upgradable-contracts && cd upgradable-contracts
npm init -y
npm install --save-dev hardhat
npx hardhat init # Choose "Create a JavaScript project"
npm install @openzeppelin/contracts @openzeppelin/hardhat-upgrades

 

2. Writing the Implementation Contract

 

Create contracts/StorageV1.sol:

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract StorageV1 {
    uint256 public value;

    // Initialize instead of a constructor
    function initialize(uint256 _value) public {
        value = _value;
    }

    function updateValue(uint256 _value) public {
        value = _value;
    }
}

 

3. Deploying the Proxy

 

Create a deployment script (scripts/deploy.js):

 

const { ethers, upgrades } = require("hardhat");

async function main() {
  // Deploy the implementation (StorageV1) and proxy
  const Storage = await ethers.getContractFactory("StorageV1");
  const proxy = await upgrades.deployProxy(Storage, [100], { 
    initializer: "initialize" 
  });
  await proxy.waitForDeployment();

  console.log("Proxy deployed to:", await proxy.getAddress());
}

main();

 

Run the Script:

 

npx hardhat run scripts/deploy.js --network localhost

 

4. Testing the Setup

 

Create test/Storage.test.js:

 

const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");

describe("Storage (Proxy)", function () {
  it("Should deploy and initialize", async function () {
    const Storage = await ethers.getContractFactory("StorageV1");
    const proxy = await upgrades.deployProxy(Storage, [100]);
    expect(await proxy.value()).to.equal(100);
  });
});

 

Run tests: 

 

npx hardhat test

 

5. Upgrading the Contract

 

Create contracts/StorageV2.sol with a new function:

 

contract StorageV2 is StorageV1 {
    function increment() public {
        value += 1;
    }
}

 

Update the proxy to use StorageV2(scripts/upgrade.js):

 

async function main() {
  const StorageV2 = await ethers.getContractFactory("StorageV2");
  const proxy = await upgrades.upgradeProxy("YOUR_PROXY_ADDRESS", StorageV2);
  console.log("Proxy upgraded to StorageV2");
}

 

You may also like | Optimism Platform: Developing and Implementing Layer 2 Smart Contracts

 

Best Practices and Considerations

 

  1. Storage Layout: Never change the order or type of existing variables in upgrades. Use OpenZeppelin's Storage Gaps for future-proofing.
  2. Security: Transfer proxy admin rights to a multisig wallet or DAO.
  3. Testing: Always test upgrades on a testnet before deploying to mainnet.
  4. Avoid Initial Values in Constructors: Use initialize functions instead.

 

Conclusion

 

Upgradable smart contracts are essential for building flexible, future-proof dApps. By using proxy patterns, you can update your contract's logic while preserving its state and address. Start with the Transparent Proxy pattern, follow best practices, and always prioritize security.

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

May 2, 2025 at 03:40 am

Your comment is awaiting moderation.

bg bg

What's Trending in Tech

bg

Our Offices

India

INDIA

DG-18-009, Tower B,
Emaar Digital Greens, Sector 61,
Gurugram, Haryana
122011.
Unit- 117-120, First Floor,
Welldone Tech Park,
Sector 48, Sohna road,
Gurugram, Haryana
122018.
USA

USA

30N, Gloud St STR E, Sheridan, Wyoming (USA) - 82801
Singapore

SINGAPORE

10 Anson Road, #13-09, International Plaza Singapore 079903.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us