Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(genesis): create3 and timelock #242

Merged
merged 9 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ To generate this first state:
1. Add a .env file in `contracts/.env`

```
ADMIN_ADDRESS=0x123...
UPGRADE_ADMIN_ADDRESS=0x234...
ADMIN_ADDRESS=0x...
TIMELOCK_EXECUTOR_ADDRESS=0x...
TIMELOCK_GUARDIAN_ADDRESS=0x...
```
- `ADMIN_ADDRESS` will be the owner of `IPTokenStaking` and `UpgradeEntryPoint`, able to execute admin methods.
- `UPGRADE_ADMIN_ADDRESS` will be the owner of the [ProxyAdmin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/transparent/ProxyAdmin.sol) for each upgradeable predeploy.
- `ADMIN_ADDRESS` will be the owner of the `TimelockController` contract. Will be able to propose transactions to the timelock, and cancel them.
- `TIMELOCK_EXECUTOR_ADDRESS` address allowed to execute the scheduled actions once the timelock matures.
- `TIMELOCK_GUARDIAN_ADDRESS` address allowed to cancel proposals

2. Run
1. Run
Ramarti marked this conversation as resolved.
Show resolved Hide resolved
```
forge script script/GenerateAlloc.s.sol -vvvv --chain-id <DESIRED_CHAIN_ID>
```
Expand Down
138 changes: 120 additions & 18 deletions contracts/script/GenerateAlloc.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pragma solidity ^0.8.23;
import { Script } from "forge-std/Script.sol";
import { console2 } from "forge-std/console2.sol";
import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import { TimelockController } from "@openzeppelin/contracts/governance/TimelockController.sol";
import { IIPTokenStaking } from "../src/interfaces/IIPTokenStaking.sol";
import { IPTokenStaking } from "../src/protocol/IPTokenStaking.sol";
import { UpgradeEntrypoint } from "../src/protocol/UpgradeEntrypoint.sol";
Expand All @@ -15,7 +15,7 @@ import { UBIPool } from "../src/protocol/UBIPool.sol";
import { EIP1967Helper } from "./utils/EIP1967Helper.sol";
import { InitializableHelper } from "./utils/InitializableHelper.sol";
import { Predeploys } from "../src/libraries/Predeploys.sol";

import { Create3 } from "../src/deploy/Create3.sol";
/**
* @title GenerateAlloc
* @dev A script to generate the alloc section of EL genesis
Expand All @@ -33,11 +33,15 @@ contract GenerateAlloc is Script {
*/
address internal deployer = 0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd;

// Upgrade admin controls upgradeability (by being Owner of each ProxyAdmin),
// protocol admin is Owner of precompiles (admin/governance methods).
// To disable upgradeability, we transfer ProxyAdmin ownership to a dead address
address internal upgradeAdmin;
// TimelockController
address internal timelock;
// Governance multi-sig
address internal protocolAdmin;
// Executor of scheduled operations
address internal timelockExecutor;
// Guardian of timelock
address internal timelockGuardian;

string internal dumpPath = getDumpPath();
bool public saveState = true;
uint256 public constant MAINNET_CHAIN_ID = 0; // TBD
Expand All @@ -49,10 +53,11 @@ contract GenerateAlloc is Script {
}

/// @notice call from Test.sol only
function setAdminAddresses(address upgrade, address protocol) external {
function setAdminAddresses(address protocol, address executor, address guardian) external {
require(block.chainid == 31337, "Only for local tests");
upgradeAdmin = upgrade;
protocolAdmin = protocol;
timelockExecutor = executor;
timelockGuardian = guardian;
}

/// @notice path where alloc file will be stored
Expand All @@ -74,18 +79,70 @@ contract GenerateAlloc is Script {
}
}

/// @notice main script method
function run() public {
if (upgradeAdmin == address(0)) {
upgradeAdmin = vm.envAddress("UPGRADE_ADMIN_ADDRESS");
/// @notice Get the minimum delay for the timelock
function getTimelockMinDelay() internal view returns (uint256) {
if (block.chainid == 1513) {
// Iliad
return 1 days;
} else if (block.chainid == 1512) {
// Mininet
return 10 seconds;
} else if (block.chainid == 1315) {
// Odyssey devnet
return 10 seconds;
} else if (block.chainid == 1516) {
// Odyssey testnet
return 1 days;
} else if (block.chainid == 31337) {
// Local
return 10 seconds;
} else {
revert("Unsupported chain id");
}
require(upgradeAdmin != address(0), "upgradeAdmin not set");
}

function getTimelockControllers()
internal
view
returns (address[] memory proposers, address[] memory executors, address canceller)
{
proposers = new address[](1);
executors = new address[](1);
if (block.chainid == 1513) {
Ramarti marked this conversation as resolved.
Show resolved Hide resolved
// Iliad
proposers[0] = protocolAdmin;
executors[0] = protocolAdmin;
canceller = protocolAdmin;
return (proposers, executors, protocolAdmin);
} else if (block.chainid == 1512 || block.chainid == 1315 || block.chainid == 31337 || block.chainid == 1516) {
// Mininet, Odyssey devnet, Local, Odyssey testnet
proposers[0] = protocolAdmin;
executors[0] = timelockExecutor;
canceller = timelockGuardian;
return (proposers, executors, protocolAdmin);
} else {
revert("Unsupported chain id");
}
}

/// @notice main script method
function run() public {
// Tests should set these addresses first
if (protocolAdmin == address(0)) {
protocolAdmin = vm.envAddress("ADMIN_ADDRESS");
}
require(protocolAdmin != address(0), "protocolAdmin not set");

if (timelockExecutor == address(0)) {
timelockExecutor = vm.envAddress("TIMELOCK_EXECUTOR_ADDRESS");
}
require(timelockExecutor != address(0), "executor not set");

if (timelockGuardian == address(0)) {
timelockGuardian = vm.envAddress("TIMELOCK_GUARDIAN_ADDRESS");
}
require(timelockGuardian != address(0), "canceller not set");

vm.startPrank(deployer);

setPredeploys();
Expand All @@ -111,6 +168,11 @@ contract GenerateAlloc is Script {
}

function setPredeploys() internal {
// predeploys that are not upgradable
setCreate3();
deployTimelock();

// predeploys that are upgradable
setProxy(Predeploys.Staking);
setProxy(Predeploys.Upgrades);
setProxy(Predeploys.UBIPool);
Expand All @@ -120,6 +182,32 @@ contract GenerateAlloc is Script {
setUBIPool();
}

/// @notice Deploy TimelockController
function deployTimelock() internal {
// We deploy this with Create3 because we can't set storage variables in constructor with vm.etch

uint256 minDelay = getTimelockMinDelay();
(address[] memory proposers, address[] memory executors, address canceller) = getTimelockControllers();

bytes memory creationCode = abi.encodePacked(
type(TimelockController).creationCode,
abi.encode(minDelay, proposers, executors, deployer)
);
bytes32 salt = keccak256("STORY_TIMELOCK_CONTROLLER");
timelock = Create3(Predeploys.Create3).deploy(salt, creationCode);

TimelockController(payable(timelock)).grantRole(
TimelockController(payable(timelock)).CANCELLER_ROLE(),
canceller
);
TimelockController(payable(timelock)).renounceRole(
TimelockController(payable(timelock)).DEFAULT_ADMIN_ROLE(),
deployer
);

console2.log("TimelockController deployed at:", timelock);
}

function setProxy(address proxyAddr) internal {
address impl = Predeploys.getImplAddress(proxyAddr);

Expand All @@ -129,7 +217,7 @@ contract GenerateAlloc is Script {
vm.etch(impl, "00");

// use new, so that the immutable variable the holds the ProxyAdmin proxyAddr is set in properly in bytecode
address tmp = address(new TransparentUpgradeableProxy(impl, upgradeAdmin, ""));
address tmp = address(new TransparentUpgradeableProxy(impl, timelock, ""));
vm.etch(proxyAddr, tmp.code);

// set implempentation storage manually
Expand Down Expand Up @@ -170,7 +258,7 @@ contract GenerateAlloc is Script {

InitializableHelper.disableInitializers(impl);
IIPTokenStaking.InitializerArgs memory args = IIPTokenStaking.InitializerArgs({
owner: protocolAdmin,
owner: timelock,
minStakeAmount: 1024 ether,
minUnstakeAmount: 1024 ether,
minCommissionRate: 5_00, // 5% in basis points
Expand Down Expand Up @@ -201,7 +289,7 @@ contract GenerateAlloc is Script {
vm.resetNonce(tmp);

InitializableHelper.disableInitializers(impl);
UpgradeEntrypoint(Predeploys.Upgrades).initialize(protocolAdmin);
UpgradeEntrypoint(Predeploys.Upgrades).initialize(timelock);

console2.log("UpgradeEntrypoint proxy deployed at:", Predeploys.Upgrades);
console2.log("UpgradeEntrypoint ProxyAdmin deployed at:", EIP1967Helper.getAdmin(Predeploys.Upgrades));
Expand All @@ -220,17 +308,30 @@ contract GenerateAlloc is Script {
vm.resetNonce(tmp);

InitializableHelper.disableInitializers(impl);
UBIPool(Predeploys.UBIPool).initialize(protocolAdmin);
UBIPool(Predeploys.UBIPool).initialize(timelock);

console2.log("UBIPool proxy deployed at:", Predeploys.UBIPool);
console2.log("UBIPool ProxyAdmin deployed at:", EIP1967Helper.getAdmin(Predeploys.UBIPool));
console2.log("UBIPool impl at:", EIP1967Helper.getImplementation(Predeploys.UBIPool));
console2.log("UBIPool owner:", UBIPool(Predeploys.UBIPool).owner());
}

function setCreate3() internal {
address tmp = address(new Create3());
vm.etch(Predeploys.Create3, tmp.code);

// reset tmp
vm.etch(tmp, "");
vm.store(tmp, 0, "0x");
vm.resetNonce(tmp);

vm.deal(Predeploys.Create3, 1);
console2.log("Create3 deployed at:", Predeploys.Create3);
}

function setAllocations() internal {
// EL Predeploys
vm.deal(0x0000000000000000000000000000000000000001, 1);
// Geth precompiles
vm.deal(0x0000000000000000000000000000000000000001, 1);
vm.deal(0x0000000000000000000000000000000000000002, 1);
vm.deal(0x0000000000000000000000000000000000000003, 1);
Expand All @@ -240,6 +341,7 @@ contract GenerateAlloc is Script {
vm.deal(0x0000000000000000000000000000000000000007, 1);
vm.deal(0x0000000000000000000000000000000000000008, 1);
vm.deal(0x0000000000000000000000000000000000000009, 1);
// Story's IPGraph precompile
vm.deal(0x000000000000000000000000000000000000001a, 1);
// Allocation
if (block.chainid == MAINNET_CHAIN_ID) {
Expand Down
82 changes: 0 additions & 82 deletions contracts/script/TestPrecompileUpgrades.s.sol

This file was deleted.

4 changes: 4 additions & 0 deletions contracts/src/libraries/Predeploys.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ library Predeploys {
address internal constant UBIPool = 0xCccCCC0000000000000000000000000000000002;
address internal constant Upgrades = 0xccCCcc0000000000000000000000000000000003;

/// @notice Create3 factory address
/// @dev We maximize compatibility with the contracts deployed by ZeframLou
address internal constant Create3 = 0x9fBB3DF7C40Da2e5A0dE984fFE2CCB7C47cd0ABf;

/// @notice Return true if `addr` is not proxied
function notProxied(address addr) internal pure returns (bool) {
return addr == WIP;
Expand Down
8 changes: 1 addition & 7 deletions contracts/test/deploy/Create3.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@ pragma solidity ^0.8.23;
/// NOTE: pragma allowlist-secret must be inline (same line as the pubkey hex string) to avoid false positive
/// flag "Hex High Entropy String" in CI run detect-secrets

import { Test } from "forge-std/Test.sol";
import { Test } from "../utils/Test.sol";

import { Create3 } from "../../src/deploy/Create3.sol";

contract Create3Test is Test {
Create3 private create3;

function setUp() public {
create3 = new Create3();
}

function testCreate3_deploy() public {
// deploy and getDeployed should return same address when deployed by the same deployer and with same salt.
bytes32 salt = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef;
Expand Down
Loading
Loading