Admin Plugin
The Admin Plugin gives one address the power to make the DAO execute any actions immediately, with no vote and no waiting. Use it to bootstrap a DAO before handing control to a governance plugin for ongoing operation.
Use it when a DAO is, deliberately, under a single trusted controller: a solo founder, a migration script, or a Multisig/Safe acting as that one address. The common case is bootstrapping: run a DAO under Admin while it's being set up, then install a governance plugin for ongoing operation and remove Admin (which the admin can do itself; see migrating away). If you only need the DAO to start correctly configured, prefer the DAO Launchpad's one-shot factory, which deploys it atomically and leaves a verifiable onchain record. Use Admin when you want to operate under a single controller for a while, beyond deployment.
Why a plugin, not just a permission?
You could skip Admin entirely: grant a trusted address EXECUTE_PERMISSION_ID on the DAO directly, and it could call dao.execute(...) itself. So why does this plugin exist? Two practical reasons:
- Disposable setup. Admin is a plugin with the standard install/uninstall lifecycle: install it to run a DAO under one controller while you bootstrap it, then replace it with ongoing governance and remove it. This lets you use the setup and teardown operations instead of manually granting and later revoking a raw permission.
- Every action becomes an auditable proposal. Because actions flow through
createProposal/executeProposal, each one emits the standardProposalCreated/ProposalExecutedevents and enters the DAO's proposal history alongside proposals from other governance plugins. The same UIs and indexers can review them after the fact. Direct DAO execution still has its own transaction and execution events, but does not create that proposal record. Admin records an unrestricted operator's actions as proposals.
Admin provides direct execution with the accountability and lifecycle of a plugin, even though a raw permission grant would allow the same execution.
The zero-width proposal lifecycle
Every OSx governance plugin implements the shared IProposal interface, which was designed for plugins that store proposals and decide over time. Admin implements it too, but completes the whole lifecycle in a single atomic call:
createProposal and executeProposal are the same call. Creating a proposal is executing it; nothing is ever written to storage. In one transaction, executeProposal:
- computes a
proposalId(the same deterministic hash other plugins use, here only for event traceability, never as a storage key), - runs the actions immediately against the live execution target,
- emits
ProposalCreatedand thenProposalExecuted, back-to-back.
Those two events exist so indexers and tooling built for the normal proposal lifecycle keep working: a subgraph watching for ProposalCreated → ProposalExecuted sees a proposal that's created and instantly passes, with no Admin-specific code path. The startDate/endDate parameters of the standard createProposal signature are ignored; there is no time window at all.
Because nothing persists, there's no proposal storage, no existence check, and no "already exists" guard: the same actions can be executed through Admin any number of times (contrast Multisig, where a proposal id is a storage key and identical proposals collide).
The two entry points differ only in how they carry allowFailureMap (the failsafe action map): executeProposal takes it as a typed uint256 argument, while the generic createProposal expects it abi-encoded in its trailing data (as customProposalParamsABI advertises).
canExecutereturnstrue, butexecute(id)always reverts. ForIProposalcompatibility Admin has to exposecanExecute,hasSucceeded, andexecute(uint256). The first two ignore their argument and always returntrue;execute(uint256)always reverts withFunctionNotSupported(it exists only so the contract compiles againstIProposal). Generic tooling that does "ifcanExecutethenexecute" will get a revert on Admin. The execution entry points arecreateProposal/executeProposal, which execute atomically; there is never a separateexecute()step to call.
Who is "the admin"
There is no dedicated admin role. Being the admin is defined as holding EXECUTE_PROPOSAL_PERMISSION_ID on the plugin, isMember(addr) is literally "does addr hold that permission?". Two consequences:
- Singularity is a convention, not an invariant. The setup grants the permission to exactly one address, but nothing stops the DAO later granting it to more, turning Admin into an "any of N" controller with no code change.
- Rotating the admin is a permission change, done through the DAO's permission system. There's no plugin-specific "transfer adminship" function.
Installation
AdminSetup (a plugin setup) deploys the plugin and wires three permissions:
| Permission | On (where) | Granted to (who) | Condition | Gates |
|---|---|---|---|---|
EXECUTE_PROPOSAL_PERMISSION_ID | plugin | the admin address | none | createProposal, executeProposal |
EXECUTE_PERMISSION_ID | DAO | plugin | none | the plugin calling dao.execute |
SET_TARGET_CONFIG_PERMISSION_ID | plugin | DAO | none | setTargetConfig |
No conditions, no helper contracts, every grant is a direct 1:1 relationship (contrast Multisig's ListedCheckCondition). The admin address is passed in the install data (a zero address reverts).
Admin is a PluginCloneable, not UUPS. It holds essentially no per-instance state and is typically short-lived, so a cheap minimal-proxy clone fits better than the heavier UUPS machinery, at the cost that an installed instance can't be upgraded in place (you'd install a new build instead). A separate zkSync variant deploys the full contract per install rather than cloning, purely a deployment-mechanics difference for that chain, the governance behavior is identical.
Migrating away
Because the admin can make the DAO execute any action, and a DAO holds ROOT over itself, the admin can hand power over without any special function: submit an action batch that installs a governance plugin for ongoing operation (grant it its permissions) and then removes Admin. That's a typical end state for a bootstrapped DAO: use Admin during setup, then transfer control.
Uninstalling Admin leaves one dangling grant.
prepareUninstallationrevokes the two DAO-side grants (EXECUTE_PERMISSION_IDandSET_TARGET_CONFIG_PERMISSION_ID) but not the admin'sEXECUTE_PROPOSAL_PERMISSION_ID(the setup can't reliably enumerate every address it was granted to). It's inert: withoutEXECUTE_PERMISSION_IDthe orphaned plugin can no longer reach the DAO. The grant remains as onchain state to account for when auditing permissions after an Admin removal.
Keep in mind
- Never call
execute(id)on Admin. It always reverts; execution happens insidecreateProposal/executeProposal.canExecutereturningtrueis meaningless here. - Single point of control, by design. No timelock, no quorum, no veto: whoever holds
EXECUTE_PROPOSAL_PERMISSION_IDcan drain or reconfigure the DAO in one transaction. Admin adds no safety net of its own, its safety is entirely the safety of that one key or contract. - Nothing enforces one admin. "Single admin" is how it's installed, not a rule the code guarantees.
See also
- Multisig Plugin and Token Voting Plugin — governance plugins that do store proposals and decide over time; the contrast is the clearest way to understand what Admin strips away.
- Proposals — the
IProposalinterface Admin implements in its most minimal form (a zero-width create-and-execute). - The permission system — how "the admin" is defined by a grant, and how the admin can grant its own successor.