Upgradeable solidity contract
1. Summary
The basis
- Proxy contracts use
delegatecall
to call the implementation contract - Storage is existed in the proxy contract
- Implementation contract’s address is stored in storage slot
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
(obtained asbytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
), Admin address is stored in storage slot0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
(obtained asbytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
EIP-1967
A few minor caveats
- Always extend storage instead of modifying it
- Make sure your contracts use initializer functions instead of constructors
A proxy contract with OpenZeppelin
Design a contract:
- Upgradeable
- Record address and tokens
eth_getStorageAt
Contracts’ storage layouts
mapping(address => uint256) internal balances;
address public owner;
uint256 public withdrawalFee;
Storage layouts should be
- balances
- 0x43Db1155C06548666E2928f4970694CA21B1835a 0.0002 eth
- 0xcfc247B51f02d415C3490c1652426d9174201a73 0.0003 eth
- owner 0x43Db1155C06548666E2928f4970694CA21B1835a
- withdrawalFee 1
For simple Variables
Stored sequentially in slots.
cast storage 0x00CEDc793A6a48eb9e0bE53D79d9e983ab96A5E8 0x01
We got
0x00000000000000000000000043db1155c06548666e2928f4970694ca21b1835a
For mappings
the storage location is calculated using:
keccak256(key.slotIndex)
We got storage slot for an entry in a mapping
cast index address 0xcfc247B51f02d415C3490c1652426d9174201a73 0
-> 0xe4e0f75d5e229d70c237640257432bc6e8aaeb17d705a1380bbf7b2516008d10
Let’s get the map value
cast storage 0x00CEDc793A6a48eb9e0bE53D79d9e983ab96A5E8 0xe4e0f75d5e229d70c237640257432bc6e8aaeb17d705a1380bbf7b2516008d10
-> 0x000000000000000000000000000000000000000000000000000110d9316ec000
cast to-dec 0x000000000000000000000000000000000000000000000000000110d9316ec000
-> 300000000000000