GOMTU Crypto
guidePart 26 of 41 in this guide

Ethereum Proxy Contracts Explained: Delegatecall, UUPS, and Upgrade Risks

Learn how Ethereum proxy contracts use delegatecall, how transparent, UUPS, and beacon proxies differ, and how to verify upgrade authority safely.

GOMTU
GOMTU
Crypto Research · August 26, 2026 · 9 min read
Share𝕏in

Last updated

You check a contract on a block explorer, read verified source code, and still may not be looking at the code that handles your transaction. Many Ethereum applications keep one public address while routing calls to replaceable logic. Understanding that routing is an important part of blockchain basics, because an upgradeable address can behave differently tomorrow without users moving tokens to a new address.

That flexibility can fix bugs and add features. It also creates an upgrade authority whose mistake or compromise can change the rules. This guide explains the proxy model, delegatecall, the main proxy families, and a practical verification workflow. It does not assume that upgradeable is automatically unsafe—or that verified source automatically makes an upgrade safe.

What an Ethereum proxy contract is

Advertisement

Think of a proxy like a restaurant with a permanent street address and a replaceable kitchen team. Customers keep arriving at the same door. The kitchen can change, but the restaurant's reservations, inventory, and cash drawer stay at that address.

On Ethereum, the proxy contract is the stable address that holds storage and often assets. The implementation contract, also called the logic contract, contains executable application logic. When a call reaches the proxy, its fallback path forwards the calldata to the implementation using the EVM's DELEGATECALL opcode.

The crucial detail is context. A normal external call executes with the called contract's storage. A delegated call executes implementation code while reading and writing the proxy's storage; the original caller and call value also remain visible to that execution. The implementation acts more like a borrowed instruction manual than a separate account receiving the operation.

This separation is why an application can retain the same token balances, allowances, user records, and integration address after an upgrade. Only the address from which logic is borrowed changes.

How a delegated call flows

A simplified interaction follows this sequence:

  1. Your wallet sends calldata to the proxy address.
  2. The proxy loads an implementation address from a designated storage slot or asks a beacon for it.
  3. The proxy delegates the original calldata to that implementation.
  4. Implementation bytecode runs against the proxy's balance and storage.
  5. Return data or a revert is copied back to your wallet.

The function selector and arguments are still carried in Ethereum calldata. The proxy usually does not decode every application function itself; its fallback forwards the bytes. That is also why block explorers often combine the proxy address with an implementation ABI to display a usable interface.

Why ERC-1967 storage slots matter

If the proxy stored its implementation address in an ordinary early storage slot, implementation variables could overwrite it. ERC-1967 defines specific slots for implementation, beacon, and optional admin information. The implementation slot is derived from keccak256("eip1967.proxy.implementation") - 1, placing it outside storage positions ordinarily allocated by the Solidity compiler.

The standard also recommends emitting Upgraded, BeaconUpgraded, and AdminChanged events when those values change. Events help monitoring systems, but the authoritative current value is storage. A watcher should confirm both the event and the resulting slot rather than relying on a human-readable activity label alone.

Transparent, UUPS, and beacon proxies

These patterns can all preserve a proxy address, but they locate upgrade logic and authority differently.

PatternWhere upgrade mechanism livesUpgrade scopeKey operational question
TransparentProxy and a dedicated admin pathOne proxyWho controls the ProxyAdmin or admin owner?
UUPSImplementation contractOne proxyDoes _authorizeUpgrade enforce the intended access control?
BeaconBeacon contract points many proxies to logicMany beacon followersWho can upgrade the beacon, and which proxies follow it?

Transparent proxy

A transparent proxy separates admin calls from user calls. In the common OpenZeppelin design, the admin manages upgrades and does not use the proxy as an ordinary application user; non-admin calls are delegated to the implementation. The separation reduces ambiguity around functions whose four-byte selectors could collide, but it adds an admin component that must be secured.

Do not infer the effective controller from the proxy address alone. In current tooling, a ProxyAdmin contract may be owned by another account, multisig, or governance system. Trace ownership until you reach the accounts and rules that can actually authorize an upgrade.

UUPS proxy

UUPS puts upgrade functions in the implementation rather than the proxy. The proxy can therefore be smaller, while the implementation's authorization hook decides who may upgrade it. ERC-1822 describes a compatibility mechanism, and widely used implementations pair UUPS behavior with the ERC-1967 implementation slot.

This makes review of upgrade authorization essential. A missing or incorrect access check can expose the upgrade path. An incompatible replacement can also disable future upgrades or break application behavior. OpenZeppelin's current UUPS implementation checks compatibility, but custom logic and governance around it still require review.

Beacon proxy

A beacon proxy does not store its implementation directly in the ERC-1967 implementation slot. It stores a beacon address, and the beacon's implementation() response identifies the logic. Updating one beacon can switch the implementation for many proxies at once.

That is convenient for fleets of similar instances. It also concentrates impact: one authorized beacon upgrade can affect every follower. Verification must include the proxy, beacon, current implementation, beacon owner, and the full set of applications that share it.

Constructors, initializers, and storage layout

Implementation constructors initialize the implementation contract's own storage, not the proxy's storage used during delegated execution. Upgradeable systems therefore commonly expose an initializer called through the proxy during deployment. The initializer must be protected against being called again.

Initialization is not a cosmetic deployment step. An uninitialized proxy or implementation can expose ownership or privileged setup functions to an unintended caller. Mature libraries provide initializer guards, but deployments must still encode and execute initialization correctly.

Storage layout must also remain compatible. Suppose version one stores owner in slot 0 and balance in slot 1. If version two carelessly reorders those variables, the new code interprets old bytes under new meanings. Values are not automatically migrated just because source variable names look sensible. Append-only changes, inheritance order, packed variables, structs, and mappings all need tool-assisted validation against the prior layout.

How to verify a proxy before interacting

1. Confirm the chain and proxy address

Start from a primary project channel and confirm the network. The same-looking interface can point to different contracts across Ethereum and rollups. Bookmarking an explorer page is safer than searching for an address every time, but recheck after migrations.

2. Identify the proxy pattern

Use an explorer's proxy view as a convenience, not the only proof. Read the ERC-1967 implementation, beacon, and admin slots through a trusted RPC endpoint where applicable. For a beacon, call implementation() on the beacon. Custom and minimal proxies may use other layouts, so absence from ERC-1967 slots is not proof that no delegation exists.

3. Verify the current implementation

Check whether implementation source is verified and whether its compiled bytecode matches. Review the implementation ABI for the function you intend to call. A verified proxy shell does not verify replaceable application logic, and an old audited implementation does not describe a newer one.

4. Trace effective upgrade authority

Find the account, multisig, timelock, or governance executor that can change the implementation. Then inspect thresholds, signers, delay, cancellation rights, emergency roles, and whether one account can bypass the normal path. “Governed by a DAO” is incomplete unless the onchain control path supports the claim.

5. Review upgrade history and monitoring

Search for ERC-1967 upgrade events and compare them with storage changes. Read release notes, audit reports for the deployed version, and governance proposals. Ethereum event logs are useful alerts, but chain reorganizations and custom upgrade paths mean monitoring should reconcile logs with canonical state.

6. Simulate the exact transaction

Confirm the destination is the proxy users are meant to call, decode calldata, inspect approvals and asset movements, and simulate against current state. A proxy can upgrade between an earlier review and later execution, so high-consequence actions need a fresh check.

Risks and limitations

Upgrade-key compromise

If an attacker gains effective upgrade authority, they may install logic that transfers assets, changes accounting, or grants permissions. A multisig and timelock can reduce single-key and surprise-upgrade risk, but only if thresholds, signers, delays, and bypass roles are appropriate and actively monitored.

Malicious or buggy implementation

An authorized upgrade can still be unsafe. New logic may introduce reentrancy, broken access control, oracle assumptions, or economic behavior not covered by the previous audit. Audit scope and deployed bytecode version matter.

Storage collision and bad initialization

Incompatible layouts can corrupt persistent state. Incorrect initializer handling can leave privileged roles claimable or reset critical configuration. Automated upgrade validation helps, but it does not prove business logic or governance safety.

Function-selector and interface confusion

EVM dispatch uses the first four bytes of the function signature hash. Proxy administration and implementation functions need careful routing so collisions do not produce unexpected behavior. A displayed ABI may also be stale or attached to the wrong implementation.

Upgradeability can change or disappear

Some systems can renounce, remove, or permanently lock upgrade capability; others claim immutability while retaining an indirect control path. Verify current code and authority rather than relying on branding. Conversely, losing required upgrade authority can leave a bug unfixable.

Warning

A verified proxy address is not enough. Verify its current implementation, storage layout assumptions, initializer state, and the complete authority path that can replace its code.

Practical checklist

  • Confirm chain ID and the intended proxy address
  • Determine transparent, UUPS, beacon, minimal, or custom pattern
  • Read relevant implementation, beacon, and admin state
  • Verify current implementation source and deployed bytecode
  • Trace upgrade control to its effective owners and bypass roles
  • Check timelock delay, multisig threshold, and emergency powers
  • Review upgrade events, proposals, audits, and version release notes
  • Validate storage layout compatibility and initialization status
  • Decode and simulate the exact transaction against current state
  • Recheck before high-value approvals, deposits, or governance actions

FAQ

Does a proxy hold user funds?

Often, yes. Under delegatecall, application state and balances remain at the proxy address. The implementation supplies code, while the proxy commonly remains the address users approve or fund.

Is every proxy upgradeable?

No. Delegation is a mechanism, not proof of a live upgrade path. Minimal clones can delegate to a fixed implementation, and an upgradeable system may later remove or lock authority. Inspect deployed code and state.

Are UUPS proxies safer than transparent proxies?

Neither label guarantees safety. They place upgrade logic differently and create different review surfaces. Security depends on implementation correctness, authorization, storage compatibility, governance, monitoring, and operational discipline.

Can a block explorer always find the implementation?

ERC-1967 makes common patterns discoverable, but custom proxies can use different mechanisms. Explorer labels can also lag state. Confirm important findings through direct storage reads and verified code.

Does an audit cover future upgrades?

Only if the audit scope explicitly covers that version and its upgrade path. Replacing implementation code changes the reviewed system. Check the deployed bytecode and audit commit or release, not just a protocol-level “audited” badge.

Primary sources

Treat the address as a changing system

A proxy is not just a forwarding shell. It is a persistent state container connected to logic and an authority path that may replace that logic. Good verification follows all three: proxy state, current implementation, and effective upgrade controller.

This article is educational and not financial advice. Smart contracts and related assets can fail or lose value. Verify current primary documentation and deployed state, limit exposure to amounts you can afford to lose, and do your own research (DYOR).

Advertisement

Keep learning

Explore related topics

More from GOMTU