文件 1 的 4:IStorageV1.sol
pragma solidity 0.7.6;
interface IStorageV1 {
function governance() external view returns(address);
function treasury() external view returns(address);
function isAdmin(address _target) external view returns(bool);
function isOperator(address _target) external view returns(bool);
}
文件 2 的 4:Proxy.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Proxy {
function _delegate(address implementation) internal {
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
function _implementation() internal virtual view returns (address);
function _fallback() internal {
_beforeFallback();
_delegate(_implementation());
}
fallback () external payable {
_fallback();
}
receive () external payable {
_fallback();
}
function _beforeFallback() internal virtual {
}
}
文件 3 的 4:TimelockProxyStorageCentered.sol
pragma solidity 0.7.6;
import "@openzeppelin/contracts/proxy/Proxy.sol";
import "./utilities/UnstructuredStorageWithTimelock.sol";
import "./interface/IStorageV1.sol";
contract TimelockProxyStorageCentered is Proxy {
using UnstructuredStorageWithTimelock for bytes32;
bytes32 private constant _SYSTEM_STORAGE_SLOT =
0xf7ce9e33978bd6e766998cbee51134930bc6e39dc5dcd8f992c5b743b1c6d698;
bytes32 private constant _TIMELOCK_SLOT =
0xc6fb23975d74c7743b6d6d0c1ad9dc3911bc8a4a970ec5723a30579b45472009;
bytes32 private constant _IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
event UpgradeScheduled(address indexed implementation, uint256 activeTime);
event Upgraded(address indexed implementation);
event TimelockUpdateScheduled(uint256 newTimelock, uint256 activeTime);
event TimelockUpdated(uint256 newTimelock);
constructor(
address _logic,
address _storage,
uint256 _timelock,
bytes memory _data
) {
assert(
_SYSTEM_STORAGE_SLOT ==
bytes32(uint256(keccak256("eip1967.proxy.systemStorage")) - 1)
);
assert(
_TIMELOCK_SLOT ==
bytes32(uint256(keccak256("eip1967.proxy.timelock")) - 1)
);
assert(
_IMPLEMENTATION_SLOT ==
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
);
_SYSTEM_STORAGE_SLOT.setAddress(_storage);
_TIMELOCK_SLOT.setUint256(_timelock);
_IMPLEMENTATION_SLOT.setAddress(_logic);
if (_data.length > 0) {
(bool success, ) = _logic.delegatecall(_data);
require(success);
}
}
modifier adminPriviledged() {
require(
msg.sender == IStorageV1(_systemStorage()).governance() ||
IStorageV1(_systemStorage()).isAdmin(msg.sender),
"msg.sender is not adminPriviledged"
);
_;
}
modifier requireTimelockPassed(bytes32 _slot) {
require(
block.timestamp >= _slot.scheduledTime(),
"Timelock has not passed yet"
);
_;
}
function proxyScheduleImplementationUpdate(address targetAddress)
public
adminPriviledged
{
bytes32 _slot = _IMPLEMENTATION_SLOT;
uint256 activeTime = block.timestamp + _TIMELOCK_SLOT.fetchUint256();
(_slot.scheduledContentSlot()).setAddress(targetAddress);
(_slot.scheduledTimeSlot()).setUint256(activeTime);
emit UpgradeScheduled(targetAddress, activeTime);
}
function proxyScheduleTimelockUpdate(uint256 newTimelock) public adminPriviledged {
uint256 activeTime = block.timestamp + _TIMELOCK_SLOT.fetchUint256();
(_TIMELOCK_SLOT.scheduledContentSlot()).setUint256(newTimelock);
(_TIMELOCK_SLOT.scheduledTimeSlot()).setUint256(activeTime);
emit TimelockUpdateScheduled(newTimelock, activeTime);
}
function proxyUpgradeTimelock()
public
adminPriviledged
requireTimelockPassed(_TIMELOCK_SLOT)
{
uint256 newTimelock =
(_TIMELOCK_SLOT.scheduledContentSlot()).fetchUint256();
_TIMELOCK_SLOT.setUint256(newTimelock);
emit TimelockUpdated(newTimelock);
}
function proxyUpgradeImplementation()
public
adminPriviledged
requireTimelockPassed(_IMPLEMENTATION_SLOT)
{
address newImplementation =
(_IMPLEMENTATION_SLOT.scheduledContentSlot()).fetchAddress();
_IMPLEMENTATION_SLOT.setAddress(newImplementation);
emit Upgraded(newImplementation);
}
function _implementation() internal view override returns (address impl) {
bytes32 slot = _IMPLEMENTATION_SLOT;
assembly {
impl := sload(slot)
}
}
function _systemStorage() internal view returns (address systemStorage) {
bytes32 slot = _SYSTEM_STORAGE_SLOT;
assembly {
systemStorage := sload(slot)
}
}
}
文件 4 的 4:UnstructuredStorageWithTimelock.sol
pragma solidity 0.7.6;
library UnstructuredStorageWithTimelock {
uint256 private constant SCHEDULED_SIGNATURE = 0x111;
uint256 private constant TIMESLOT_SIGNATURE = 0xAAA;
function updateAddressWithTimelock(bytes32 _slot) internal {
require(
scheduledTime(_slot) > block.timestamp,
"Timelock has not passed"
);
setAddress(_slot, scheduledAddress(_slot));
}
function updateUint256WithTimelock(bytes32 _slot) internal {
require(
scheduledTime(_slot) > block.timestamp,
"Timelock has not passed"
);
setUint256(_slot, scheduledUint256(_slot));
}
function setAddress(bytes32 _slot, address _target) internal {
assembly {
sstore(_slot, _target)
}
}
function fetchAddress(bytes32 _slot)
internal
view
returns (address result)
{
assembly {
result := sload(_slot)
}
}
function scheduledAddress(bytes32 _slot)
internal
view
returns (address result)
{
result = fetchAddress(scheduledContentSlot(_slot));
}
function scheduledUint256(bytes32 _slot)
internal
view
returns (uint256 result)
{
result = fetchUint256(scheduledContentSlot(_slot));
}
function setUint256(bytes32 _slot, uint256 _target) internal {
assembly {
sstore(_slot, _target)
}
}
function fetchUint256(bytes32 _slot)
internal
view
returns (uint256 result)
{
assembly {
result := sload(_slot)
}
}
function scheduledContentSlot(bytes32 _slot)
internal
pure
returns (bytes32)
{
return
bytes32(
uint256(keccak256(abi.encodePacked(_slot, SCHEDULED_SIGNATURE)))
);
}
function scheduledTime(bytes32 _slot) internal view returns (uint256) {
return fetchUint256(scheduledTimeSlot(_slot));
}
function scheduledTimeSlot(bytes32 _slot) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encodePacked(_slot, TIMESLOT_SIGNATURE)))
);
}
}
{
"compilationTarget": {
"contracts/TimelockProxyStorageCentered.sol": "TimelockProxyStorageCentered"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_logic","type":"address"},{"internalType":"address","name":"_storage","type":"address"},{"internalType":"uint256","name":"_timelock","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newTimelock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"activeTime","type":"uint256"}],"name":"TimelockUpdateScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newTimelock","type":"uint256"}],"name":"TimelockUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"uint256","name":"activeTime","type":"uint256"}],"name":"UpgradeScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"targetAddress","type":"address"}],"name":"proxyScheduleImplementationUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTimelock","type":"uint256"}],"name":"proxyScheduleTimelockUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxyUpgradeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxyUpgradeTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]