Smart Contract Upgrades: How EIP-1967 standardises the approach to tracking changes to implementation and admin slots in upgradeable contracts
With IRL beacon proxy example
EIP-1967 essentially defines storage slots for any proxy that uses delegatecall, proposing a set of standard slots for storing proxy information.
A user will call a function on the proxy, which maintains storage and balance, which in turn uses delegatecall to forward the function data to the current implementation contract, which handles logic.
Why was this standard created?
Prior to EIP-1967 there was no consistent way to obtain the implementation contract address for a given proxy. Many proxies already had ways to read the implementation (via custom admin functions or manually reading storage), but not in a standard storage location.
tl;dr: This EIP isn’t an upgradeability pattern in itself. It is a storage slot standard for proxies.
How to use the storage slots
Logic contract
The logic contract address should be stored at slot
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
given by
bytes32(uint256(keccak256(’eip1967.proxy.implementation’)) - 1)with the corresponding event
event Upgraded(address indexed implementation);Beacon contract
The beacon contract address slot, which should be empty if the logic contract address is used, and should be used only if the logic address is empty, is
0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50
given by
bytes32(uint256(keccak256(’eip1967.proxy.beacon’)) - 1)and has the corresponding event
event BeaconUpgraded(address indexed beacon);Since Beacons are used for keeping the logic address for multiple proxies in a single location, allowing the upgrade of multiple proxies by modifying a single storage slot, there must also be a function implementation to point to this logic.
function implementation() returns (address)Admin address
A third storage slot is included in this EIP for an optional admin address that is allowed to upgrade the logic contract address for this proxy,
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
given by
bytes32(uint256(keccak256(’eip1967.proxy.admin’)) - 1)with the corresponding event
event AdminChanged(address previousAdmin, address newAdmin);Why standardisation matters
Standardising the way that storage is implemented has various benefits, not least preventing storage collisions and other errors.
By eliminating ad-hoc slot choices, with different magic constants, this EIP provides a stable minimal basis for upgradeability frameworks to build on (UUPS, Transparent, Beacon, Diamond helpers and so on). The predictability also helps debuggers, block explorers (e.g. Etherscan) and auditors to reliably read implementation / admin addresses.
How frameworks use it
OpenZeppelin’s Transparent Upgradeable Proxy and UUPS contracts both use this EIP for variable storage. The Solady library also provides a gas-efficient UUPS proxy implementation that uses it.
OpenZeppelin also provides options for beacon storage, via their Beacon Proxy. Beacons seem to be used less in practice than other patterns these days, but prove very useful for specific use cases such as managing multiple (or even many) NFT collections.
For example: to facilitate easy deployment of new collections (using an NFT-Factory with a default NFT template) and easy upgrade of existing ones.
Beacon storage slot IRL example
The Sandbox’s Avatar collection provides one such example where EIP-1967 is used by a beacon proxy to manage AVATAR collections (collections of ERC-721 tokens).
The purpose is to support having an admin that can change the beacon logic to which the proxy for each collection points.
The CollectionProxy.sol contract inherits OpenZeppelin’s BeaconProxy
contract CollectionProxy is BeaconProxyand initially the beacon proxy (logic) implementation address is set via the constructor:
where beacon_ is the address of the new beacon and data_ is an optional encoded function call to be forwarded to the new beacon implementation address via delegatecall.
The BeaconProxy passes these arguments in its constructor to _upgradeBeaconToAndCall(beacon, data, false);
Once the beacon has been set, any future changes to the beacon can only be made by admin.
Beacon based proxy contracts use the beacon contract slot to store the address of the beacon they are attached to. Hence, we see that the required function is also included in CollectionProxy.sol:
I hope you enjoyed the article, thanks for reading!





