Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Launching a Token with Secure Upgradeable Contract Patterns

A technical guide for deploying social token contracts using upgradeable proxy architectures. Covers Transparent and UUPS patterns, storage management, and security best practices to enable safe contract updates.
Chainscore © 2026
introduction
DEVELOPER GUIDE

Introduction to Upgradeable Token Contracts

Learn how to design and deploy tokens that can be securely upgraded to fix bugs and add features without disrupting your ecosystem.

An upgradeable token contract is a smart contract whose logic can be modified after deployment, while preserving its state and address. This is a critical design pattern for long-lived projects, allowing developers to patch critical security vulnerabilities, implement new token standards like ERC-20 extensions, or adjust economic parameters without requiring a disruptive token migration. The core challenge is executing these upgrades in a way that maintains immutability for users—they must trust the new code—and prevents unauthorized changes.

The most common and secure implementation uses a proxy pattern. In this architecture, users interact with a lightweight Proxy contract that holds the token's storage (balances, allowances, etc.). This Proxy delegates all logic calls to a separate Implementation contract, which contains the executable code. When an upgrade is needed, you deploy a new Implementation contract and point the Proxy to this new address. This preserves all user data and the contract's on-chain identity while swapping the underlying logic. Popular proxy standards include OpenZeppelin's TransparentUpgradeableProxy and the more gas-efficient UUPS (EIP-1822) proxies.

Security is paramount. Upgradeability introduces a central admin or governance mechanism with the power to change the contract's behavior. To mitigate risks, you should: use a timelock contract to delay upgrades, giving users time to react; implement a multi-signature wallet or DAO for upgrade authorization; and thoroughly audit both the initial implementation and all subsequent upgrades. Without these safeguards, the upgrade mechanism itself becomes a single point of failure.

Here's a basic workflow using OpenZeppelin's Upgrades Plugins for Hardhat: First, write your initial token contract, inheriting from upgradeable versions of OpenZeppelin contracts (e.g., ERC20Upgradeable). Deploy it using the plugin's deployProxy function, which handles proxy creation and initializer setup. To upgrade, modify your contract, then run upgradeProxy, providing the proxy address and the new implementation. The plugin validates storage layout compatibility to prevent critical errors. Always test upgrades on a testnet first.

Consider the trade-offs. While upgradeability offers flexibility, it can conflict with the principle of decentralization if controlled by a single entity. For truly decentralized projects, on-chain governance should control the upgrade process. Furthermore, some upgrade patterns, like the Diamond Standard (EIP-2535), allow for modular upgrades of specific functions. Your choice depends on your project's needs for security, gas efficiency, and governance model.

prerequisites
PREREQUISITES AND SETUP

Launching a Token with Secure Upgradeable Contract Patterns

This guide covers the essential tools and knowledge required to build and deploy an upgradeable ERC-20 token using established proxy patterns.

Before writing any code, you need a foundational development environment. Install Node.js (v18 or later) and a package manager like npm or yarn. You will also need a code editor such as VS Code. The core tool is a development framework for compiling, testing, and deploying smart contracts. We recommend Hardhat or Foundry for their robust testing suites and plugin ecosystems. For this guide, we'll use Hardhat. Initialize a new project with npx hardhat init and select the TypeScript template for better type safety.

Understanding the proxy pattern is critical for upgradeable contracts. The core concept separates logic and storage: a Proxy contract holds the state (user balances, allowances), while a Logic contract contains the executable code (transfer, mint, burn). When a user calls the proxy, it delegates the call to the current logic contract via delegatecall. This allows you to deploy a new logic contract and point the proxy to it, upgrading the token's functionality without migrating state. The most common and secure standard is the Transparent Proxy Pattern, which prevents function selector clashes between the proxy admin and the logic contract.

You must install specific libraries to implement this pattern securely. Run npm install @openzeppelin/contracts @openzeppelin/contracts-upgradeable @openzeppelin/hardhat-upgrades. The @openzeppelin/contracts-upgradeable package provides versions of ERC-20 and other standards where the initialization function replaces the constructor, a requirement for proxy compatibility. The hardhat-upgrades plugin provides utilities to deploy, upgrade, and verify proxy contracts safely, handling low-level details like storage layout validation to prevent critical errors during upgrades.

Configure your hardhat.config.ts file to use the plugin and connect to a blockchain network. You'll need an RPC URL for deployment; you can use a local Hardhat network for testing or a testnet like Sepolia. Set up environment variables using a .env file to store your private key and RPC URLs securely (e.g., using the dotenv package). A basic configuration includes importing @nomicfoundation/hardhat-toolbox and @openzeppelin/hardhat-upgrades, and defining your network settings in the networks object.

Finally, set up a basic test structure. Create a test file (e.g., TokenUpgrade.test.ts) to verify your deployment and upgrade path works. Write initial tests to check that the proxy deploys correctly, the token has the right name and symbol, and basic functions like transfer work. Testing upgradeability involves deploying a V2 logic contract with new features and ensuring the proxy seamlessly adopts the new logic while preserving all user balances and approvals from V1. This setup ensures you can proceed to contract development with a verified safety net.

key-concepts-text
SECURE CONTRACT PATTERNS

How Upgradeable Proxies Work

Learn the architecture behind upgradeable smart contracts, a critical pattern for launching future-proof tokens and protocols on Ethereum and EVM chains.

An upgradeable proxy pattern separates a contract's storage and logic into two distinct contracts. The Proxy Contract holds all the state (user balances, owner address, etc.), while the Implementation Contract (or Logic Contract) contains the executable code. The proxy uses a delegatecall to forward all transactions to the current implementation. This means users and applications always interact with the proxy's address, but the logic governing that interaction can be replaced by deploying a new implementation and updating the proxy's pointer.

The delegatecall opcode is the mechanism that enables this. When the proxy receives a call, it executes the code from the implementation contract within the context of the proxy's own storage. This preserves the proxy's state variables and msg.sender. A critical security consideration is storage collision: the layout of state variables in the implementation must remain consistent across upgrades, or new variables could overwrite existing data. Using structured storage patterns, like the unstructured storage pattern from OpenZeppelin, mitigates this risk.

Common proxy standards include Transparent Proxy and UUPS (EIP-1822). A Transparent Proxy uses a ProxyAdmin contract to manage upgrades, preventing the implementation from self-destructing or upgrading itself. UUPS (Universal Upgradeable Proxy Standard) bakes the upgrade logic directly into the implementation contract, making it more gas-efficient for users but requiring upgrade authorization checks in the logic itself. The choice depends on your gas optimization and administrative control requirements.

To launch an upgradeable ERC-20 token, you would use libraries like OpenZeppelin Contracts-Upgradeable. You initialize the contract via an initializer function (replacing the constructor) and deploy using a script that deploys the implementation, proxy admin, and proxy itself. A typical deployment flow with Hardhat and the @openzeppelin/hardhat-upgrades plugin looks like this:

javascript
const TokenV1 = await ethers.getContractFactory("MyToken");
const proxy = await upgrades.deployProxy(TokenV1, ["TokenName", "TKN"], { initializer: 'initialize' });
await proxy.deployed();
console.log("Proxy deployed to:", proxy.address);

Upgrades are not without risk. You must rigorously test upgrades on a testnet, ensuring state persistence and functionality integrity. Always use a timelock controller for production upgrades, which enforces a mandatory delay between proposing and executing an upgrade, giving users time to react. Remember, the proxy's upgradeability can be permanently disabled ("frozen") by renouncing the admin role or using a function like _disableInitializers(), which is a final security measure for mature protocols.

UPGRADE PATTERNS

Transparent vs UUPS Proxy Pattern Comparison

Key architectural and operational differences between the two dominant patterns for upgradeable smart contracts.

FeatureTransparent ProxyUUPS Proxy

Upgrade Logic Location

Separate ProxyAdmin contract

Within the Implementation contract

Proxy Deployment Gas Cost

~1,200,000 gas

~750,000 gas

Proxy Call Overhead

~2,700 gas

~2,200 gas

Implementation Contract Size

Standard

Slightly larger (includes upgrade function)

Upgrade Authorization

ProxyAdmin owner

Implementation contract logic

Risk of Implementation Self-Destruct

Lower (logic is external)

Higher (must be explicitly guarded)

Common Standard

OpenZeppelin TransparentUpgradeableProxy

ERC-1967 / EIP-1822

Recommended Use Case

Simple upgrades, multi-admin scenarios

Gas-optimized deployments, single upgrade authority

step-1-transparent-proxy
UPGRADEABLE CONTRACTS

Step 1: Deploying a Token with Transparent Proxy

Learn how to deploy an ERC-20 token using the Transparent Proxy pattern, separating logic from storage for secure, future-proof upgrades.

The Transparent Proxy pattern is the most widely adopted upgradeability standard for Ethereum smart contracts. It works by separating your application's logic from its data storage. You deploy three core contracts: a Proxy contract that holds the state (user balances, allowances), a Logic contract (your token's ERC-20 implementation), and a ProxyAdmin contract to manage upgrades. When a user interacts with the token, they call the Proxy, which delegates all calls to the current Logic contract using delegatecall. This keeps the storage layout in the Proxy stable while allowing the logic to be replaced.

To begin, you'll need a development environment like Hardhat or Foundry. Install OpenZeppelin's upgradeable contracts library: npm install @openzeppelin/contracts-upgradeable. Your initial token contract must inherit from upgradeable base contracts, such as ERC20Upgradeable and OwnableUpgradeable, and use an initializer function instead of a constructor. For example, your MyToken.sol would start with:

solidity
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
contract MyToken is ERC20Upgradeable, OwnableUpgradeable {
    function initialize(string memory name, string memory symbol) public initializer {
        __ERC20_init(name, symbol);
        __Ownable_init();
        _mint(msg.sender, 1000000 * 10 ** decimals());
    }
}

Deployment is managed through OpenZeppelin's Upgrades Plugins, which handle the complexity. With Hardhat, after writing your script, you run a command like npx hardhat run scripts/deploy.js. The plugin will automatically deploy the Logic contract, the ProxyAdmin, and the Proxy, linking them together. It also performs critical safety checks, like verifying storage layout compatibility to prevent storage collisions during future upgrades. Always deploy to a testnet first. The proxy address returned is your official token address—this is what you will share with users and list on block explorers.

Post-deployment, security management is crucial. The ProxyAdmin contract is the owner of the Proxy and is the only address authorized to upgrade it. You should immediately transfer ownership of the ProxyAdmin to a multisig wallet or a DAO governance contract like Safe or Compound's Governor. Never leave it in an externally owned account (EOA). This ensures that any future upgrade requires multiple signatures, protecting your token from a single point of failure. You can verify all three contracts (Proxy, Logic, ProxyAdmin) on Etherscan using their proxy verification feature so users can trust the implementation.

When planning an upgrade, you must ensure storage compatibility. The new logic contract can add new state variables, but it cannot change the order or types of existing ones declared in previous versions. The Upgrades Plugin will validate this. To upgrade, you deploy the new MyTokenV2.sol contract and then execute an upgrade transaction from the ProxyAdmin, pointing the Proxy to the new logic address. All user balances and data remain intact. This process allows you to fix bugs, add features like minting controls, or adjust for new standards like ERC-20Permit without requiring users to migrate their tokens.

step-2-upgrade-transparent
UPGRADE EXECUTION

Step 2: Performing an Upgrade with Transparent Proxy

This guide walks through the practical steps of deploying a new implementation contract and upgrading your proxy using OpenZeppelin's Transparent Proxy pattern.

After developing and testing your new contract logic (e.g., MyTokenV2.sol), the first step is to deploy it as a new, standalone implementation contract. This contract holds the updated code but has no state. Use a standard deployment script with your preferred tool like Hardhat or Foundry. For example, a Hardhat script would call await ethers.deployContract("MyTokenV2");. The deployed address of this new logic contract is the core argument for the upgrade.

The actual upgrade is performed by calling the upgradeTo(address newImplementation) function on the proxy admin contract. This is a critical security feature: the upgrade call must originate from the admin's address, not the proxy itself. In a typical setup, you interact with the ProxyAdmin contract provided by OpenZeppelin Upgrades Plugins. The call changes the proxy's stored logic address, instantly redirecting all future calls to the new MyTokenV2 code. Crucially, the proxy's storage (user balances, allowances, etc.) remains completely intact.

It is essential to verify that the new implementation contract is compatible with the proxy's existing storage layout. The OpenZeppelin Upgrades Plugins perform automatic checks for common issues, such as:

  • Storage layout collisions: Ensuring new variables don't overwrite existing ones.
  • Initializer functions: Confirming the initializer modifier is used to protect against re-initialization.
  • Parent contract compatibility: Checking for safe inheritance changes. Always run npx hardhat verify or a similar command to publish the source code of your new implementation on a block explorer for transparency.

After the upgrade transaction is confirmed, you should immediately conduct post-upgrade validation. First, confirm the proxy's implementation pointer has updated by calling the implementation() function on the proxy admin. Then, interact with your token contract using its proxy address to test new V2 functions. Ensure all existing state is preserved by checking a user's balance or the total supply. This validation confirms the upgrade was successful and the system is operational with the new logic.

For ongoing maintenance, document every upgrade with the new implementation address, block number, and a brief description of the changes. Consider implementing a timelock contract as the proxy admin for major upgrades, which introduces a mandatory delay between proposing and executing an upgrade. This gives users and stakeholders time to review changes or exit positions if they disagree with the new contract logic, enhancing decentralization and trust in your upgradeable token system.

step-3-uups-implementation
IMPLEMENTATION GUIDE

Step 3: Building a UUPS Upgradeable Token

This guide walks through creating an ERC20 token using the UUPS (Universal Upgradeable Proxy Standard) pattern, enabling secure and gas-efficient contract upgrades.

The UUPS pattern centralizes upgrade logic within the implementation contract itself, unlike the Transparent Proxy pattern which uses a separate ProxyAdmin. This design reduces deployment gas costs and proxy storage overhead. To build a UUPS upgradeable token, you will need three core components: an initial ERC20 implementation, a UUPS-compliant proxy contract, and a proxy deployment script. We'll use OpenZeppelin's Contracts Wizard to generate the base code and Hardhat for deployment and testing.

Start by generating your token's initial logic contract. Use the OpenZeppelin Contracts Wizard, select ERC20, and choose features like mintable and pausable. Crucially, under the 'UPGRADEABILITY' section, select 'UUPS'. This automatically imports and inherits from UUPSUpgradeable and Initializable. The wizard will generate a contract that includes the mandatory _authorizeUpgrade(address newImplementation) function, where you must define your upgrade authorization logic, typically using an onlyOwner modifier.

Your generated contract will look similar to this simplified example:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract MyToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
    function initialize() initializer public {
        __ERC20_init("MyToken", "MTK");
        __Ownable_init();
        __UUPSUpgradeable_init();
    }
    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}

The initialize function acts as the constructor for upgradeable contracts, setting the token's name, symbol, and initializing inherited contracts.

Deploying this system requires a two-step process. First, compile and deploy the logic contract (MyToken). Second, deploy a proxy contract that points to your logic contract's address. Use OpenZeppelin's ERC1967Proxy. In Hardhat, your deployment script will deploy the logic contract, then the proxy, and finally call the initialize function on the proxy. All future interactions should be with the proxy address, which delegates calls to the stored logic. To upgrade, you deploy a new version of MyTokenV2, then call upgradeTo(address(newImplementation)) on the proxy, which executes the authorization check in _authorizeUpgrade.

Key security considerations for UUPS are paramount. Since the upgrade function resides in the implementation, you must ensure _authorizeUpgrade is robustly protected. A compromised logic contract could self-destruct or lock upgrades. Always test upgrades on a testnet first. Use tools like OpenZeppelin's Upgrades Plugins for Hardhat or Foundry to validate storage layout compatibility, a critical requirement to prevent storage collisions during upgrades. Remember, the proxy's admin (the address that can upgrade) is the contract owner defined in your OwnableUpgradeable setup.

The primary advantage of UUPS over other patterns is its gas efficiency, as it avoids the overhead of a separate ProxyAdmin contract. However, it places greater responsibility on the implementation contract's security. This pattern is widely adopted by major protocols and is recommended for developers who want full control over upgrade logic within their main contract codebase, making it an excellent choice for building a future-proof, upgradeable token.

step-4-upgrade-uups
IMPLEMENTING THE UPGRADE

Step 4: Upgrading a UUPS Contract

Learn how to deploy a new implementation and securely upgrade your token's logic using the UUPS proxy pattern.

Once your initial UUPS-compliant token contract is live, its logic can be upgraded by deploying a new implementation contract and pointing the proxy to it. This process is governed by the upgradeTo function in the IERC1822Proxiable and UUPSUpgradeable interfaces. Only an account with the UPGRADER_ROLE (or the default admin, depending on your AccessControl setup) can execute this upgrade, ensuring administrative security. The upgrade transaction must be sent to the proxy contract's address, not the old implementation.

The new implementation contract must satisfy several critical requirements. It must:

  • Inherit from the same initializable base contracts.
  • Include the _authorizeUpgrade function, which contains the access control logic (e.g., onlyRole(UPGRADER_ROLE)).
  • Preserve the storage layout; you cannot change the order or types of existing state variables, though you can append new ones. Failing to maintain storage compatibility is a common source of critical errors and corrupted state.
  • Re-initialize any new initializable base contracts using __[ContractName]_init functions, but it must not call the initializer for the primary contract again, as that would lock the initializer.

Here is a simplified example of a new MyTokenV2 contract that adds a burn function. Note the _authorizeUpgrade override and the use of the initializer modifier on the new constructor-replacement function.

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

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

contract MyTokenV2 is ERC20Upgradeable, UUPSUpgradeable, AccessControlUpgradeable {
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
    // New state variable appended to storage
    uint256 public burnTaxRate;

    // Initializer for V2, sets up new variables
    function initializeV2(uint256 _burnTaxRate) public reinitializer(2) {
        burnTaxRate = _burnTaxRate;
    }

    function burn(uint256 amount) external {
        _burn(msg.sender, amount);
    }

    // Required override for upgrade authorization
    function _authorizeUpgrade(address newImplementation)
        internal
        override
        onlyRole(UPGRADER_ROLE)
    {}
}

To execute the upgrade, you will use a script or command that interacts with the proxy. Using OpenZeppelin's Upgrades Plugins for Hardhat or Foundry is strongly recommended, as they perform critical safety checks. A typical Hardhat script looks like this:

javascript
const { upgrades } = require('hardhat');

async function main() {
  const MyTokenV2 = await ethers.getContractFactory('MyTokenV2');
  const proxyAddress = '0x...'; // Your existing proxy address

  const upgraded = await upgrades.upgradeProxy(proxyAddress, MyTokenV2);
  await upgraded.waitForDeployment();

  console.log('Proxy upgraded to:', await upgraded.getAddress());

  // Initialize new V2 functionality
  const v2Contract = await ethers.getContractAt('MyTokenV2', proxyAddress);
  await v2Contract.initializeV2(50); // Set burnTaxRate to 50 basis points
}

The plugin validates storage layout, checks for initialization safety, and manages the upgrade transaction.

After a successful upgrade, the proxy address remains the same for all users, but the logic execution is routed to the new MyTokenV2 contract. All existing token balances and allowances are preserved because they are stored in the proxy's storage. You should always verify the new implementation on a block explorer and consider implementing a timelock for the UPGRADER_ROLE in production to allow for community review before upgrades take effect, enhancing decentralization and trust.

security-considerations
SECURITY GUIDE

Launching a Token with Secure Upgradeable Contract Patterns

Upgradeable smart contracts introduce significant security risks alongside their flexibility. This guide covers critical considerations for implementing secure upgrade patterns for tokens.

Upgradeable smart contracts allow developers to modify a contract's logic after deployment, which is essential for fixing bugs or adding features. However, this capability fundamentally contradicts the immutable nature of the blockchain, creating a central point of failure: the upgrade mechanism. If compromised, an attacker can alter the contract's behavior to drain funds or lock user assets. The primary security goal is to design an upgrade system that is both transparent and resilient to unauthorized changes, typically managed through a multi-signature wallet or a decentralized autonomous organization (DAO).

The most common pattern for upgradeability is the Proxy Pattern, which uses a proxy contract to delegate calls to a separate logic contract. Users interact with the proxy, which holds the storage, while the executable code resides in the logic contract. When an upgrade is needed, the proxy is pointed to a new logic contract address. Critical risks here include storage collisions, where variable layouts between logic versions mismatch, and function selector clashes that can be exploited. Using established libraries like OpenZeppelin's TransparentUpgradeableProxy or UUPSProxy mitigates these risks by enforcing standardized upgrade procedures and storage slots.

Two main upgrade standards dominate: Transparent Proxy and UUPS (EIP-1822). The Transparent Proxy pattern prevents the admin from accidentally triggering a function in the logic contract by using a proxy admin contract. UUPS (Universal Upgradeable Proxy Standard) bakes the upgrade logic directly into the implementation contract itself, making it more gas-efficient but requiring the upgrade function to be present and secure in every version. The choice depends on your project's needs: UUPS for gas savings and self-contained upgrades, or Transparent Proxy for a clearer separation of concerns and admin roles.

A secure upgrade process requires rigorous access control and timelocks. The upgrade authority should never be a single private key. Use a multi-sig wallet (like Safe) with a threshold of trusted signers. For community-governed tokens, delegate upgrade power to a DAO. Implementing a timelock is non-negotiable for major upgrades; it delays the execution of an upgrade transaction by 24-72 hours, giving users time to review changes or exit the system if they disagree. This creates a critical security window and prevents instantaneous, malicious upgrades.

Before executing any upgrade, you must conduct exhaustive testing and verification. Deploy the new logic contract to a testnet first. Use tools like Hardhat or Foundry to run your full test suite against the new implementation. Perform storage layout checks to ensure no variables have been reordered. Verify the new contract's source code on block explorers like Etherscan. Finally, communicate the upgrade clearly to your users, providing the new contract address, a changelog, and the timelock expiry time. Transparency builds trust and allows for community scrutiny, which is your final layer of defense.

SECURITY RISKS

Common Upgrade Mistakes and Their Impact

Critical errors in upgradeable contract design and their consequences for security, user funds, and protocol governance.

Mistake / VulnerabilityImpact SeverityExample ScenarioMitigation Strategy

Storage Collision

Critical

New variable in V2 overwrites existing V1 data due to incorrect slot calculation.

Use unstructured storage pattern (EIP-1967) or inherit from OpenZeppelin's storage gap contracts.

Function Selector Clash

Critical

V2's upgradeTo(address) function collides with proxy's admin function, locking the proxy.

Use the Transparent Proxy pattern or UUPS with explicit overrides; avoid function selector conflicts.

Initialization Re-entrancy

High

initialize() function makes an external call before state is set, allowing re-initialization.

Use initializer modifiers and the _disableInitializers() function in the constructor.

Unprotected Initialization

High

initialize() function lacks access control, allowing any user to become owner.

Implement explicit access control (e.g., onlyOwner or initializer role) in the initializer.

Incompatible Storage Layout

High

Changing variable order or types between upgrades corrupts all subsequent data.

Append new variables only; use __gap for future variables; never delete or reorder.

Missing Implementation Self-Destruct

Medium

A compromised logic contract self-destructs, bricking all proxies pointing to it.

Use UUPS (proxy delegates upgrade logic to implementation) or a secure proxy admin contract.

Unchecked Delegatecall Return Data

Medium

Failed delegatecall in proxy returns success, causing silent failures in upgrades.

Use Address.functionDelegateCall from OpenZeppelin which reverts on failure.

Governance Delay Bypass

Low-Medium

Upgrade is executed immediately via upgradeTo() without a timelock, allowing rushed malicious code.

Implement a TimelockController as the proxy admin to enforce a mandatory delay for upgrades.

TOKEN LAUNCH

Frequently Asked Questions on Upgradeable Contracts

Common developer questions and solutions for implementing secure, upgradeable token contracts using patterns like the Transparent Proxy and UUPS.

The Transparent Proxy pattern uses a ProxyAdmin contract to manage upgrades, separating the admin logic from the implementation. This prevents function selector clashes but adds gas overhead. The UUPS (EIP-1822) pattern embeds the upgrade logic directly in the implementation contract itself, making it more gas-efficient. However, UUPS requires the implementation to contain the upgrade authorization logic, and if this is removed in a faulty upgrade, the proxy becomes permanently frozen. For new projects, UUPS is often preferred for its lower gas costs, while Transparent Proxies offer a simpler, more battle-tested separation of concerns.

Key differences:

  • Gas Cost: UUPS is cheaper for users as it avoids the extra delegatecall to a ProxyAdmin.
  • Upgrade Mechanism: Transparent uses an external admin; UUPS uses logic within the implementation.
  • Risk: A bug in a UUPS upgrade function can brick the contract permanently.