Deploy your first DAO
Deploy a DAO with a Multisig plugin so that, from block one, only a passed multisig proposal can make it act and no external address retains control. In A hands-on tour of OSx, you made a bare DAO and held EXECUTE_PERMISSION_ID as an EOA. That suits a demo; this deployment gives execution authority to the organization's governance.
It's still one transaction. DAOFactory.createDao creates the DAO, installs the plugin through the PSP's temporary-ROOT window, and hands EXECUTE to the plugin, atomically.
What you need
The one-time Setup, with the multisig plugin's remapping enabled. This guide reads the DAOFactory and Multisig PluginRepo (MULTISIG_PLUGIN_REPO_ADDRESS) addresses, and installs a specific plugin version (a release/build, see release vs build).
Step 1, the skeleton
Fork the network and wire the two addresses. Everything else happens in one test:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {Test} from "forge-std/Test.sol";
import {DAO} from "@aragon/osx/core/dao/DAO.sol";
import {DAOFactory} from "@aragon/osx/framework/dao/DAOFactory.sol";
import {PluginRepo} from "@aragon/osx/framework/plugin/repo/PluginRepo.sol";
import {PluginSetupRef} from "@aragon/osx/framework/plugin/setup/PluginSetupProcessorHelpers.sol";
import {IPlugin} from "@aragon/osx/common/plugin/IPlugin.sol";
import {Multisig} from "@aragon/multisig-plugin/Multisig.sol"; // path follows your installed dependency
contract DeployWithMultisig is Test {
DAOFactory factory = DAOFactory(vm.envAddress("DAO_FACTORY_ADDRESS"));
PluginRepo multisigRepo = PluginRepo(vm.envAddress("MULTISIG_PLUGIN_REPO_ADDRESS"));
function setUp() public {
vm.createSelectFork(vm.envString("RPC_URL"));
}
}Step 2, describe the DAO and the plugin install
A DAO created with plugins takes a PluginSettings[]: each entry names a plugin version (its repo + a (release, build) tag) and the install data the plugin's setup expects. Multisig's MultisigSetup decodes its data as (address[] members, MultisigSettings, TargetConfig, bytes metadata):
function test_deployWithMultisig() public {
// Who is on the multisig, and its rule (2-of-3, members only).
address[] memory members = new address[](3);
members[0] = address(0xA11CE);
members[1] = address(0xB0B);
members[2] = address(0xCa201);
Multisig.MultisigSettings memory settings =
Multisig.MultisigSettings({onlyListed: true, minApprovals: 2});
// Where the plugin executes. target == address(0) is the sentinel for "my own DAO":
// at runtime the plugin resolves it to TargetConfig(dao(), Call). This is what lets you
// install in the same tx that creates the DAO, the DAO address doesn't exist yet here.
IPlugin.TargetConfig memory targetConfig =
IPlugin.TargetConfig({target: address(0), operation: IPlugin.Operation.Call});
bytes memory installData = abi.encode(members, settings, targetConfig, bytes(""));
// Point at the plugin version to install.
PluginRepo.Tag memory versionTag = PluginRepo.Tag({release: 1, build: 3});
DAOFactory.PluginSettings[] memory pluginSettings = new DAOFactory.PluginSettings[](1);
pluginSettings[0] = DAOFactory.PluginSettings({
pluginSetupRef: PluginSetupRef({versionTag: versionTag, pluginSetupRepo: multisigRepo}),
data: installData
});
// ... createDao in Step 3 (same function)
}The installData layout is dictated by the plugin's setup, not by OSx; every plugin publishes the ABI for its data in its build metadata. Get it wrong and prepareInstallation reverts on abi.decode.
Step 3, create the DAO with the plugin
Continue the same function. createDao returns the DAO and the installed plugin(s):
DAOFactory.DAOSettings memory daoSettings = DAOFactory.DAOSettings({
trustedForwarder: address(0),
daoURI: "",
subdomain: "",
metadata: ""
});
(DAO dao, DAOFactory.InstalledPlugin[] memory installed) =
factory.createDao(daoSettings, pluginSettings);
address multisig = installed[0].plugin;The factory ran the temporary-ROOT sequence: it prepared and applied the multisig setup, whose permission list granted the plugin EXECUTE_PERMISSION_ID on the DAO. Because you passed a plugin, the factory did not grant you EXECUTE.
Step 4, verify governance is in control
Confirm the plugin can make the DAO act and you cannot:
bytes32 EXECUTE = dao.EXECUTE_PERMISSION_ID();
assertTrue(dao.hasPermission(address(dao), multisig, EXECUTE, "")); // the plugin can
assertFalse(dao.hasPermission(address(dao), address(this), EXECUTE, "")); // you cannot
}The DAO now acts only when a multisig proposal passes and calls dao.execute. No EOA holds execution authority, and the DAO holds ROOT over itself, so changing those permissions is also a governance decision.
What you just saw
- Specify the plugin's version + install data, and the factory prepares and applies its installation, including the temporary ROOT grants.
- The setup configures the permissions: here,
EXECUTEgoes to the plugin. See multisig's setup. TargetConfig.target = address(0)means "my own DAO", which makes same-transaction installation possible.
Next
- Launch a governance token with your DAO, the same flow with Token Voting, whose install data also specifies the token choice.
- Create, vote, and execute a proposal, now drive the multisig you just installed.
- Installing admin instead is the same shape with simpler data (a single admin address); installing into an already-running DAO is Install a plugin.