This reposity contains the codebase of the Taiko DAO, along with its 3 plugins and helper contracts.
The DAO contract is an Aragon DAO, on which an Optimistic Token Voting plugin has the permission to execute proposals. Proposals on this plugin can only be created by two distinct multisig plugins, belonging to Taiko's Security Council.
The Security Council has a standard multisig plugin and an emergency variant. The former one is intended to be used most of the time, if not always. A certain majority of Security Council members need to approve it for it to make it to the community vote. The latter, is meant to only be used in exceptional situations, i.e. when a security vulnerability needs to be addressed immediately.
See Deploying the DAO below and check out the latest deployments.
This plugin is an adapted version of Aragon's Optimistic Token Voting plugin.
Only addresses that have been granted PROPOSER_PERMISSION_ID
on the plugin can create proposals. These adresses belong to the two multisig's belonging to the Security Council.
Proposals can only be executed when a certain amount of vetoes hasn't emerged after a given period of time.
The governance settings need to be defined when the plugin is installed but the DAO can update them at any time.
- Only proposers can create proposals on the plugin
- The plugin can execute actions on the DAO
- The DAO can update the plugin settings
- The DAO can upgrade the plugin
Implements a list of addresses, where proposals can only be relayed to the Optimistic Token Voting plugin after being approved.
- Only members can create proposals
- Only members can approve
- The plugin can only create proposals on the Optimistic Token Voting plugin provided that the
duration
is equal or greater than the minimum defined
Same as before, it implements a list of addresses, where proposals can only be relayed to the Optimistic Token Voting plugin after being approved.
There are two key differences:
- The proposal's metadata and the actions to execute are encrypted, only available to the Security Council members
- When approved by a super majority, the proposal can be executed on the DAO immediately, going through the Optimistic Token Voting plugin.
This is taken care by the TaikoDAOFactory
contract. Is is invoked by scripts/Deploy.s.sol
and it creates an immutable DAO deployment, given a certain settings. To create a DAO with different settings, a new factory needs to be deployed.
Plugin changes need a proposal to be passed when the DAO already exists.
- Calling
pluginSetupProcessor.prepareInstallation()
- A new plugin instance is deployed with the desired settings
- The call requests a set of permissions to be applied by the DAO
- Editors pass a proposal to make the DAO call
applyInstallation()
on the PluginSetupProcessor- This applies the requested permissions and the plugin becomes installed
See OptimisticTokenVotingPluginSetup
.
Learn more about plugin setup's and preparing installations.
OSx DAO's are designed to hold all the assets and rights by themselves, while plugins are custom, opt-in pieces of logic that can perform any type of actions governed by the DAO's permission database.
The DAO contract can be deployed by using Aragon's DAOFactory
contract. This will deploy a new DAO with the desired plugins and settings.
An Aragon DAO is a set of permissions that are used to restrict who can do what and where.
A permission looks like:
- An address
who
holdsMY_PERMISSION_ID
on a target contractwhere
Brand new DAO's are deployed with a ROOT_PERMISSION
assigned to its creator, but the DAO will typically deployed by the DAO factory, which will install all the requested plugins and drop the ROOT permission after the set up is done.
Managing permissions is made via two functions that are called on the DAO:
function grant(address _where, address _who, bytes32 _permissionId);
function revoke(address _where, address _who, bytes32 _permissionId);
For the cases where an unrestricted permission is not derisable, a Permission Condition can be used.
Conditional permissions look like this:
- An address
who
holdsMY_PERMISSION_ID
on a target contractwhere
, onlywhen
the condition contract approves it
Conditional permissions are granted like this:
function grantWithCondition(
address _where,
address _who,
bytes32 _permissionId,
IPermissionCondition _condition
);
See the condition contract boilerplate. It provides the plumbing to easily restrict what the different multisig plugins can propose on the OptimisticVotingPlugin.
Learn more about OSx permissions
Below are all the permissions that a PluginSetup contract may want to request:
EXECUTE_PERMISSION
is required to make the DAOexecute
a set of actions- Only governance plugins should have this permission
ROOT_PERMISSION
is required to make the DAOgrant
orrevoke
permissions- The DAO needs to be ROOT on itself (it is by default)
- Nobody else should be ROOT on the DAO
UPGRADE_PLUGIN_PERMISSION
is required for an address to be able to upgrade a plugin to a newer version published by the developer- Typically called by the DAO via proposal
- Optionally granted to an additional address for convenience
PROPOSER_PERMISSION
is required to be able to create optimistic proposals on the governance pluginUPDATE_MULTISIG_SETTINGS_PERMISSION_ID
is used by the DAO to update the settings of a multisig plugin, if the community approvesUPDATE_OPTIMISTIC_GOVERNANCE_SETTINGS_PERMISSION_ID
is used by the DAO to update the settings of the optimistic voting plugin, if the community approves
Other DAO specific permissions:
UPGRADE_DAO_PERMISSION
SET_METADATA_PERMISSION
SET_TRUSTED_FORWARDER_PERMISSION
SET_SIGNATURE_VALIDATOR_PERMISSION
REGISTER_STANDARD_CALLBACK_PERMISSION
Making calls to the DAO is straightforward, however making execute arbitrary actions requires them to be encoded, stored on chain and be approved before they can be executed.
To this end, the DAO has a struct called Action { to, value, data }
, which will make the DAO call the to
address, with value
ether and call the given calldata (if any). Such calldata is an ABI encoded array of bytes with the function to call and the parameters it needs.
- Never grant
ROOT_PERMISSION
unless you are just trying things out - Never uninstall all plugins, as this would brick your DAO
- Ensure that there is at least always one plugin with
EXECUTE_PERMISSION
on the DAO - Ensure that the DAO is ROOT on itself
- Use the
_gap[]
variable for upgradeable plugins, as a way to reserve storage slots for future plugin implementations- Decrement the
_gap
number for every new variable you add in the future
- Decrement the
By default, only the DAO can upgrade plugins to newer versions. This requires passing a proposal.
Learn more about plugin upgrades
To work with the repository you need to install Foundry on your operating system.
$ forge build
$ forge test
- Edit
script/multisig-members.json
with the list of addresses to set as signers - Run
forge build
- Copy
.env.example
into.env
and define the settings - Run
source .env
to load them - Set the RPC URL and run the deployment script
RPC_URL="https://eth-holesky.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
forge script --chain "$NETWORK" script/Deploy.s.sol:Deploy --rpc-url "$RPC_URL" --broadcast --verify --via-ir
If you get the error Failed to get EIP-1559 fees
, add --legacy
to the last command:
forge script --chain "$NETWORK" script/Deploy.s.sol:Deploy --rpc-url "$RPC_URL" --broadcast --verify --via-ir --legacy
If a some contracts fail to verify on Etherscan, retry with this command:
forge script --chain "$NETWORK" script/Deploy.s.sol:Deploy --rpc-url "$RPC_URL" --verify --via-ir --legacy --private-key "$DEPLOYMENT_PRIVATE_KEY" --resume
$ forge fmt
$ forge snapshot
$ anvil
$ cast <subcommand>