Skip to content

Condition Library

The Condition Library is a set of ready-made permission conditions for common gating patterns, so a DAO doesn't have to write and audit a bespoke condition contract every time it wants to constrain a permission. You deploy one (via the factory) and attach it with grantWithCondition; from then on the permission is allowed only when the condition says so. All three are audited.

The three conditions

Each is its own tool with its own use case, the first thing to get right is what each one inspects:

  • SelectorCondition — allow only a set of function selectors on the direct call being authorized. "This permission, but only to call functions X and Y."
  • ExecuteSelectorCondition — scope the DAO's EXECUTE_PERMISSION_ID by reaching inside an execute() batch and allow-listing each action's (target, selector). "This plugin may make the DAO execute, but only these calls."
  • SafeOwnerCondition — allow only the current owners of a given Safe, a live bridge from a Safe's membership to a DAO permission.

SelectorCondition gates the direct call's own selector; ExecuteSelectorCondition gates the selectors of the actions inside an execute(). Pick by which call the permission you're guarding actually receives.

The ConditionFactory

ConditionFactory deploys instances of the three conditions (deploySelectorCondition, deployExecuteSelectorCondition, deploySafeOwnerCondition) and emits an event for each. It's plain deployment (no deterministic addresses or clones); the value is a single, audited, discoverable deployment path, one known factory address per network from which every condition instance can be traced via its events.

Using one

The flow is always the same:

  1. Deploy the condition (via the factory) with its initial configuration.
  2. Grant the permission with it attached: dao.grantWithCondition(where, who, permissionId, condition), often with who = ANY_ADDR. What that means depends on the condition: SafeOwnerCondition reads who, so ANY_ADDR + it = "only Safe owners"; the selector conditions ignore who, so ANY_ADDR + them = "anyone, but only these functions/actions" (they gate what, not who).
  3. Manage the allow-list over time (for the selector conditions) via their MANAGE_SELECTORS_PERMISSION_ID, typically held by the DAO.

Keep in mind

  • A selector is not its arguments. These conditions allow-list which function/action may run, not with what arguments; for argument-level rules, write a bespoke condition or use RuledCondition.
  • All conditions fail closed. A malformed call, or a Safe that doesn't answer, resolves to "denied", never a surfaced error.

See also