MediumUnderstanding Smart Contract Immutability and EIP-1967 Upgradeable Contracts | by Ericjo | CoinsBench
Sitemap
CoinsBench

Where blockchain developers share their stories, experiences & ideas.

Understanding Smart Contract Immutability and EIP-1967 Upgradeable Contracts

3 min readFeb 6, 2026

--

Press enter or click to view image in full size

Smart contracts are the backbone of blockchain applications, powering everything from decentralized finance to NFTs. One of their defining features is immutability once deployed, a smart contract’s code cannot be changed. This ensures trust because everyone knows exactly how the contract will behave.

But immutability comes with a downside: if the contract has a bug or vulnerability, it can’t be fixed. Funds can be lost, functions may malfunction, and users are stuck with the original code. This creates a real challenge for developers who want both security and flexibility.

Get Ericjo’s stories in your inbox

Join Medium for free to get updates from this writer.

This is where EIP-1967 comes in. It standardizes where the proxy stores the logic contract address and admin info, preventing accidental overwrites and storage conflicts.

The Upgradeable Contract Solution

To solve this, developers use upgradeable contracts. Instead of deploying a single contract, they deploy a proxy contract that stores the contract’s data and delegates function calls to a logic contract using delegatecall. When updates are needed, a new logic contract is deployed, and the proxy points to it preserving the existing state.

Press enter or click to view image in full size

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

/*
This is the Logic Contract (Implementation)
It contains the actual functions we want to run
*/
contract LogicV1 {
// Storage lives in the proxy
uint256 public value;

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

function getValue() public view returns (uint256) {
return value;
}
}

/*
This is a very simple Proxy Contract using EIP-1967 storage slots
It delegates calls to the logic contract
*/
contract Proxy {
// EIP-1967 implementation slot
bytes32 private constant _IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

constructor(address _logic) {
// Store the logic contract address in the EIP-1967 slot
assembly {
sstore(_IMPLEMENTATION_SLOT, _logic)
}
}

// Delegate all calls to the logic contract
fallback() external payable {
assembly {
let impl := sload(_IMPLEMENTATION_SLOT)
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
let size := returndatasize()
returndatacopy(0, 0, size)
switch result
case 0 { revert(0, size) }
default { return(0, size) }
}
}

receive() external payable {}
}

Key Concepts

  • Delegatecall: The proxy executes code from the logic contract using its own storage and context, allowing upgrades without losing data.
  • EIP-1967 storage slots: Fixed, unique slots store the implementation (logic contract address) and admin, keeping upgrades safe.

Benefits of EIP-1967

With EIP-1967:

  • Safe upgrades without overwriting proxy data
  • Predictable, secure upgrade process
  • Reduced risk of bugs during updates

Without EIP-1967:

  • Proxy storage may accidentally overwrite important data
  • Upgrades are risky and can break contract functionality

Conclusion

Upgradeable contracts combined with EIP-1967 provide a powerful way to overcome the limits of immutability. Developers can fix bugs, add new features, and maintain contract state safely all while keeping the security and trust that make blockchains reliable.

By understanding these patterns, anyone building on Ethereum can create smart contracts that are both secure and flexible.

--

--

CoinsBench
CoinsBench

Published in CoinsBench

Where blockchain developers share their stories, experiences & ideas.