-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: deploy multisig wallets and timelock contract to address centra…
…lization related risks (#92) * feat: use script to deploy multisig wallet on exocore * forge install: safe-smart-account v1.3.0-libs.0 * fix: remapping * feat: add deployment script for exocore multisig * chore: add utils directory * feat: add circuit breaker role to customtimelockcontroller * fix: solhint * test: add fuzzing test for governance utilizing multisig and timelock * doc: add governance doc
- Loading branch information
Showing
22 changed files
with
618 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Governance | ||
|
||
## Overview | ||
|
||
The contract has privileged functions accessible only to the contract owner, excluding most user-facing functionalities. These functions are utilized for: | ||
|
||
- **Configuration**: Setting key contract parameters, such as the whitelist for stakeable assets. | ||
- **Pause/Unpause**: Temporarily halting the contract in emergencies and resuming it once resolved. | ||
- **Enable Messaging**: Activating or deactivating LayerZero messaging capabilities. | ||
- **Upgradeability**: Transitioning the contract to a new implementation. | ||
|
||
The contract owner is effectively the protocol's governor, with governance primarily operating through the owner's proposal and execution of tasks. To facilitate governance and mitigate centralization risks, a two-tier governance structure is implemented for the contract owner: | ||
|
||
1. **CustomTimelockController**: A custom timelock controller contract that owns the business contract, allowing tasks to be proposed and executed through it. This custom controller features a circuit breaker role, enabling emergency contract pauses without waiting for the timelock period. | ||
2. **Multisig**: Utilizing Safe Multisig wallets as the proposer/canceler, executor, circuit breaker, and even the admin of the custom timelock controller contract to avoid single point of failure. | ||
|
||
## `CustomTimelockController` | ||
|
||
`CustomTimelockController` is a custom timelock controller contract that inherits from the OpenZeppelin `TimelockController` contract. It has a special role named circuit breaker, which can be used to pause the contract in case of emergency without waiting for the timelock period(not applied to unpause). The main roles for the `CustomTimelockController` are: | ||
|
||
- **Proposer/Canceler**: Propose a new task or cancel a proposed task. | ||
- **Executor**: Execute a task after the timelock period. | ||
- **Circuit Breaker**: Pause the contract in case of emergency. | ||
- **Admin**: Set the roles for the `CustomTimelockController`. | ||
|
||
## Safe Multisig | ||
|
||
We use the Safe Multisig wallets as the proposer/canceler, executor, circuit breaker and even the admin of the `CustomTimelockController`. For chains like Ethereum, we create the Safe Multisig wallets from deployed Safe proxy factory contract, and set the implementation as the deployed Safe. And for Exocore specifically, we deploy the set of Safe contracts, especially for the `GnosisSafeProxyFactory` and the `GnosisSafe`, `GnosisSafeL2` singletons. The deployed Safe contracts address can be found in the [deployment json file](../script/safe_contracts_on_exocore.json). | ||
|
||
## Governance Test | ||
|
||
We have some fuzzing tests to make sure governance works as expected. Please refer to the [governance test](../test/foundry/Governance.t.sol) for more details. | ||
|
||
## Governance in Production | ||
|
||
When the protocol is ready for production, we will set the Safe Multisig wallets as the proposer/canceler, executor, circuit breaker and even the admin of the `CustomTimelockController`, and manage our contracts through the timelock controller. At that time, we will decide the multisig wallet for each role, the threshold for each multisig wallet, the signers for each multisig wallet, and the timelock period for timelock controller. |
Submodule safe-smart-account
added at
767ef3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
pragma solidity ^0.8.13; | ||
|
||
import {BaseScript} from "./BaseScript.sol"; | ||
|
||
import "@safe-contracts/GnosisSafeL2.sol"; | ||
import "@safe-contracts/proxies/GnosisSafeProxyFactory.sol"; | ||
|
||
import "forge-std/Script.sol"; | ||
import "forge-std/StdJson.sol"; | ||
|
||
contract CreateMultisigScript is BaseScript { | ||
|
||
using stdJson for string; | ||
|
||
function setUp() public override { | ||
super.setUp(); | ||
|
||
exocore = vm.createSelectFork(exocoreRPCURL); | ||
_topUpPlayer(exocore, address(0), exocoreGenesis, deployer.addr, 2 ether); | ||
} | ||
|
||
function run() public { | ||
vm.selectFork(exocore); | ||
vm.startBroadcast(deployer.privateKey); | ||
|
||
// Read deployed Safe contracts from JSON file | ||
string memory json = vm.readFile("script/safe_contracts_on_exocore.json"); | ||
|
||
address proxyFactoryAddress = json.readAddress(".GnosisSafeProxyFactory"); | ||
address safeSingletonAddress = json.readAddress(".GnosisSafeL2"); | ||
address fallbackHandlerAddress = json.readAddress(".CompatibilityFallbackHandler"); | ||
|
||
GnosisSafeProxyFactory proxyFactory = GnosisSafeProxyFactory(proxyFactoryAddress); | ||
GnosisSafeL2 safeSingleton = GnosisSafeL2(payable(safeSingletonAddress)); | ||
|
||
// Set up owners | ||
address[] memory owners = new address[](3); | ||
owners[0] = deployer.addr; | ||
owners[1] = exocoreValidatorSet.addr; | ||
owners[2] = relayer.addr; | ||
|
||
// Set up Safe parameters | ||
uint256 threshold = 2; | ||
address to = address(0); | ||
bytes memory data = ""; | ||
address fallbackHandler = fallbackHandlerAddress; | ||
address paymentToken = address(0); | ||
uint256 payment = 0; | ||
address payable paymentReceiver = payable(address(0)); | ||
|
||
// Encode initialization data | ||
bytes memory initializer = abi.encodeWithSelector( | ||
GnosisSafe.setup.selector, | ||
owners, | ||
threshold, | ||
to, | ||
data, | ||
fallbackHandler, | ||
paymentToken, | ||
payment, | ||
paymentReceiver | ||
); | ||
|
||
// Create new Safe proxy | ||
GnosisSafeProxy safeProxy = proxyFactory.createProxy(address(safeSingleton), initializer); | ||
|
||
console.log("New Safe created at:", address(safeProxy)); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"holesky": { | ||
"multisig": "0x9A7b23a0BB29F77BaBB8add484b34c02EfD327BE", | ||
"signers": [ | ||
"0x481E020DB4709e6EdDbf8134D41b866c6Fc8555e", | ||
"0x3583fF95f96b356d716881C871aF7Eb55ea34a93", | ||
"0xA1dfab3234f49e02e04E6C56a021F1a497CD0f82" | ||
] | ||
}, | ||
"exocore": { | ||
"multisig": "0xF27865277D8Cc608F279B3C09719A9Ceaa25A58f", | ||
"signers": [ | ||
"0x481E020DB4709e6EdDbf8134D41b866c6Fc8555e", | ||
"0x3583fF95f96b356d716881C871aF7Eb55ea34a93", | ||
"0xA1dfab3234f49e02e04E6C56a021F1a497CD0f82" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"SimulateTxAccessor": "0xB46E02a8c957892F7a1b7dD019bF7f56cA7830C6", | ||
"GnosisSafeProxyFactory": "0xd92Eb22d59D2736C12ef8e009833b98dB812BC5F", | ||
"DefaultCallbackHandler": "0xA9221e82f099027Da128369d323E249080507b78", | ||
"CompatibilityFallbackHandler": "0x820ed29524601172Fe4aec900Bc48432067CBCDF", | ||
"CreateCall": "0xA2c66e9eD611De51192EEfda6322E3D28b0c380c", | ||
"MultiSend": "0x83Aa234126729346F9e6a33E109244935E521bEC", | ||
"MultiSendCallOnly": "0x32A8c6b3c7D63002E1d230da5D525D6b6391796a", | ||
"SignMessageLib": "0x30fdE0Cc889dEdD87cc11F48798506AbbC7B8c24", | ||
"GnosisSafeL2": "0x9D24ad942d3453F574f3Df9C66504fDE009c14A0", | ||
"GnosisSafe": "0xE28848a95D96dFc200A48f976b32B726253a8e14" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; | ||
|
||
interface IPausable { | ||
|
||
function pause() external; | ||
function unpause() external; | ||
|
||
} | ||
|
||
contract CustomTimelockController is TimelockController { | ||
|
||
bytes32 public constant CIRCUIT_BREAKER_ROLE = keccak256("CIRCUIT_BREAKER_ROLE"); | ||
|
||
constructor( | ||
uint256 minDelay, | ||
address[] memory proposers, | ||
address[] memory executors, | ||
address[] memory circuitBreakers, | ||
address admin | ||
) TimelockController(minDelay, proposers, executors, admin) { | ||
_setRoleAdmin(CIRCUIT_BREAKER_ROLE, TIMELOCK_ADMIN_ROLE); | ||
|
||
// Grant CIRCUIT_BREAKER_ROLE to the specified circuit breakers | ||
for (uint256 i = 0; i < circuitBreakers.length; i++) { | ||
_setupRole(CIRCUIT_BREAKER_ROLE, circuitBreakers[i]); | ||
} | ||
} | ||
|
||
function pause(address target) external onlyRole(CIRCUIT_BREAKER_ROLE) { | ||
require(target != address(0), "CustomTimelockController: invalid target"); | ||
IPausable(target).pause(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.