BDS
smart-contracts, security-audits, ethereum-solidity

Upgrading Smart Contracts Through Proxy Patterns: A Comprehensive Guide

February 23, 2026
12 min
c
Proxy pattern architecture showing interaction between proxy contract, implementation contract, and user interfaces

Introduction

Smart contracts are programmed to be permanent and unchangeable after they are deployed on a blockchain network. This immutability is a source of security and trust, but it also presents challenges when developers need to fix bugs or add new features.

When modifications are required, developers have to release a completely new contract with a different address. While this permanence is great for security, it can be limiting in situations when things go wrong or need to be improved.

History has shown that smart contract vulnerabilities can result in catastrophic financial losses. One particular example is a major incident which saw millions of dollars lost because of an exploitable flaw.

These incidents point out the importance of being able to address security issues and bugs after deployment. Making contracts upgradeable allows for a mechanism to correct problems without losing the existing state and user experience, which is a key aspect of smart contract upgradeability.

This ability can be important in order to prevent significant losses and preserve the integrity of the system.

There are a variety of ways to implement upgradeable smart contracts, such as smart contract proxy patterns and data separation techniques. This article is specifically about how to implement upgradeability by using proxy patterns which is a popular way to solve this in the blockchain development space.

Understanding the Proxy Pattern

The proxy pattern is a structural design pattern where one contract implements an interface for another contract. This architecture has two major parts: the proxy contract and the implementation contract.

Rather than the user directly communicating with the implementation contract, the user will communicate with the proxy contract, which will then pass the requests on accordingly.

In this setup, both the proxy contract and the implementation contract are not altered after being deployed. However, upgradeability is accomplished by allowing the proxy to refer to different implementation contracts over time.

This means that the users can continue to use the same address and interface while the functionality of the underlying is updated. From the user's point of view, the application keeps on running perfectly even though the backend logic changes.

The proxy contract is responsible for user interactions and storing all data. It maintains the address of the implementation contract in a special storage place.

When users are calling functions on the proxy contract, a fallback function is called. The following function takes a delegatecall ethereum mechanism to invoke the code from the implementation contract but any state changes are saved in the storage of the proxy contract.

Major Proxy Pattern Implementations

Transparent Proxy Pattern

The Transparent Proxy Pattern puts the upgrade functionality in the proxy contract itself. The proxy has an upgradeTo method which is used to update the address pointing to the implementation contract.

This presents a potential problem: if both the proxy and implementation contracts contain an upgradeTo method, it is not clear which one should be called when a user invokes this function.

To solve this ambiguity, a solution has been devised in which the decision about which one to delegate to is based on who is calling the contract.

If the caller is the proxy administrator, calls are handled by the proxy contract itself. Otherwise, calls are delegated to implementation contract. This approach ensures that functions are executed clearly and that there is no confusion between administrative and regular user operations.

Universal Upgradeable Proxy Standard

The Universal Upgradeable Proxy Standard has a different approach by putting the upgrade functionality in the implementation contract, not the proxy. The proxy delegates all calls to the implementation contract, which has the upgradeTo method to point to newer versions.

This pattern gives developers more control of the upgrade path. If a developer chooses to no longer include the upgradeTo method in a future version, the contract is now permanently immutable and cannot be upgraded any further.

This can be helpful when a contract has been well tested and the development team wants to freeze it in its final state.

Since the upgrade logic is in the implementation contract, there is no need for the proxy's fallback function to check if the caller is an administrator before delegating calls.

This makes the pattern more gas efficient than the Transparent Proxy Pattern. The potential naming conflict is also eliminated since the upgradeTo method only exists in the implementation contract.

Beacon Proxy Pattern

The Beacon Proxy Pattern introduces a three component architecture: the proxy contract, a beacon contract, and the implementation contract. Instead of saving the address of the implementation, the proxy saves the address of the beacon contract.

The beacon contract in turn holds the address of the implementation contract.

When a user calls the proxy, first it will get the address of the beacon contract, and then it will call the beacon to get the address of the implementation contract. Finally, it leaves the call to the implementation contract.

This extra layer of indirection is for a very specific purpose.

This pattern is especially useful when several proxy contracts must use the same implementation. In simpler patterns, to update the implementation, one would have to update the address in each proxy contract individually. With beacon pattern, only the beacon contract needs to be updated and all proxies associated with it automatically point to the new implementation.

Major Proxy Pattern Implementations

Diamond Proxy Pattern

The Diamond Proxy Pattern solves the size limitation problem of smart contracts, which are usually limited to about 24 kilobytes. This pattern separates functionality into several smaller contracts known as facets.

The proxy contract keeps a mapping between function selectors and the address of the facets containing those functions.

When a function is called on the proxy, it uses the function selector to look up the facet that contains the function and delegates the function call to the appropriate facet.

Upgrades are done by modifying the facet addresses in the proxy. This pattern permits much greater total functionality by distributing the functionality across multiple contracts, with each facet remaining below the size limit.

Comparing Proxy Pattern Approaches

Each proxy pattern has different characteristics that make it suitable for different use cases. All Four Patterns Need Proxy Contract and Use Delegation to Forward Calls to Implementation Contracts.

Transparent Proxy Pattern Locates Upgrade Functions in Proxy Contract and UUPS Locates Upgrade Functions in Implementation Contract. The Beacon pattern puts the upgrade capability in its own beacon contract and the Diamond pattern typically puts upgrade capability in the implementation contracts but not strictly specified.

As far as immutability is concerned, UUPS and Diamond patterns can make contracts permanently immutable by removing the upgrade function from future versions. Transparent and Beacon patterns do not provide this flexibility.

When dealing with multiple proxies, the Beacon pattern has a definite advantage. With Transparent, UUPS, and Diamond patterns, each proxy has to be changed individually when implementing a new implementation.

With the Beacon pattern, only the beacon contract must be updated, and all proxies will automatically use the new implementation.

Gas efficiency is different for each pattern. The Transparent pattern requires that you check whether the caller is an administrator before every delegation so it is more expensive.

UUPS is more efficient as it does not require this check. All patterns except Diamond have extra gas costs of the extra lookup operations.

All patterns except Diamond are restricted to 24 kilobytes per contract. The Diamond pattern permits each facet to be as large as 24 kilobytes, which makes it possible to have much larger total functionality across multiple facets.

Master Smart Contract Security

Learn advanced techniques to build secure upgradeable contracts with our expert-led courses.

Important Considerations and Risks

Storage Collision Issues

Storage collisions are a serious risk when implementing proxy patterns. Contract variables are stored in specific storage slots and if the variables of the proxy contract and the variables of the implementation contract are stored in the same storage slots, they will interfere with each other.

Additionally, if the order of variables in the implementation contract changes from one version to the next, the storage slots may be re-assigned, causing data corruption.

Uninitialized Implementation Contracts

Implementation contracts must be initialized exactly once via an initialization function, which is a similar concept to a constructor in traditional contracts.

Developers also forget to initialize the implementation, or fail to include protections to prevent the initialization function from being called more than once.

When this occurs, attackers can invoke the initialization function themselves and potentially take control of the contract or manipulate its state.

Real-World Security Incidents

A bug bounty program found a critical vulnerability in vault proxy contracts where the implementation contracts were not initialized correctly. This flaw could have been used by an attacker to destroy the implementation contract making the associated proxy contracts useless. In another incident of a certain time period of July, smart contracts were hacked due to vulnerability in the initialization code, in which the initialize function could be called multiple times.

Alternative Approaches to Upgradeability

Data Separation Pattern

An alternative to the proxy patterns is the data separation approach. This approach is based on the use of separate contracts for storage and logic. The logic contract communicates with the storage contract to read or update data.

While it is possible to replace the logic contract with new versions, the storage contract is fixed and unchangeable. This offers a different model for upgradeability which may be appropriate in some circumstances.

Verification Layers

None of the proxy patterns commonly used have mechanisms for verifying that a new implementation contract is valid before the upgrade is made.

It is a must that new versions must include all the necessary business logic, fallback functions, and other essential components.

This can be achieved by introducing a layer of verification that is alongside the proxy layer, the business logic layer, and the storage layer. This extra layer ensures that upgrades adhere to some criteria before they are permitted to carry out.

Best Practices for Upgradeable Contracts

Developers who are designing upgradeable contracts should follow some key guidelines to ensure security and reliability:

  • Instead, rely on well-tested implementations from well-tested libraries which have been thoroughly reviewed and audited
  • Always make sure to initialize the implementation contracts properly and make sure that initialization functions can only be called once
  • Never initialize state variables at their declaration or in a constructor, use the initialization function for all state setup
  • Do not change the order or types of state variables when making new versions of implementation contracts
  • If new variables are required, then they should be added after all existing variables
  • For Diamond pattern implementations, use special storage methods that are optimized for that pattern
  • UUPS method is preferred over Transparent Proxy Pattern when possible, because it requires less gas for routine operations
  • Make sure that the proxy administrator account is highly secure because this account controls the upgrade process, and it is a critical security point
  • Finally, have all contracts professionally audited by experienced smart contract security professionals before being deployed

Final Thoughts

Proxy patterns provide a powerful mechanism for upgrading smart contracts, while maintaining the same address and state. The proxy contract uses delegateCall to pass the execution to implementation contracts so that the underlying logic can be changed without modifying the user interface.

However, if upgradeable contracts are not properly implemented, they can provide serious security vulnerabilities.

Developers need to carefully consider the tradeoffs between different types of proxy patterns, adhere to established best practices, and ensure that proper security audits are conducted to build reliable and secure upgradeable smart contracts.

Comparison of Proxy Patterns

When comparing which proxy pattern to use, there are several factors that should be taken into account:

Proxy Pattern Comparison

FeatureTransparentUUPSBeaconDiamond
Upgrade LocationProxy ContractImplementation ContractBeacon ContractImplementation Contracts
Permanent ImmutabilityNoYesNoYes
Multiple ProxiesIndividual UpdatesIndividual UpdatesSingle Beacon UpdateIndividual Updates
Gas EfficiencyHigher Cost (Admin Check)Lower CostMedium (Extra Lookup)Medium (Facet Lookup)
Contract Size Limit24 KB24 KB24 KB24 KB per Facet
Implementation ComplexityMediumMediumMediumMedium

Pattern Selection Considerations

Key considerations for pattern selection:

  • All patterns need a proxy contract and use delegation to pass on calls
  • The Transparent Proxy Pattern is used to store the upgrade functions in the proxy contract itself
  • UUPS puts the upgrade functions in the implementation contract
  • The Beacon pattern makes use of a separate beacon contract for upgrades
  • The Diamond pattern usually places upgrade functions in implementation contracts, but the specification does not require that they do so
  • Only UUPS and Diamond patterns have the option of making contracts permanently immutable by dropping the upgrade function from future versions
  • The Beacon pattern is ideal if you have multiple proxies to upgrade at the same time because the beacon only needs to be upgraded and not each proxy
  • Maximum contract size is 24 kilobytes for Transparent, UUPS and Beacon patterns
  • The Diamond pattern supports each facet to be as large as 24 kilobytes, so much larger total functionality can be supported
  • All patterns have medium implementation complexity and well established libraries exist for Transparent, UUPS and Beacon patterns

FAQ

#proxy patterns
#smart contract upgradeability
#blockchain security
BDS

Pioneering the future of blockchain technology with innovative solutions that empower businesses and individuals worldwide.

+1 929 560 3730 (USA)
+44 2045 771515 (UK)
+372 603 92 65 (Estonia)
Harju maakond, Tallinn, Lasnamäe linnaosa, Katusepapi tn 6-502, 11412, Estonia

Stay Updated

Get the latest blockchain news and updates delivered to your inbox.

© 2026 BDS, part of Idealogic Group. All rights reserved.