7Block Labs
Blockchain Technology

ByAUJay

Summary: Decentralized proposals only matter if you can ship them safely. Here’s how to automate proposal execution end‑to‑end with Safe Modules, Zodiac’s Reality-based executors, and onchain Guards and Module Guards—plus concrete configurations, failure modes, and audit-ready controls you can ship today.

decentralized proposals: Automating Execution with Safe Modules and Onchain Guards

For many DAOs and enterprise treasuries, the longest mile is the last one: turning an approved proposal into a transaction that actually settles onchain—without waiting for a human multisig to wake up or risking a misconfigured module draining funds. This post shows how to wire proposal execution that is both automated and secure by combining:

  • Execution Modules (e.g., Zodiac Reality/“SafeSnap”) to translate off‑chain votes into onchain transactions.
  • Onchain Guards and Module Guards to constrain what can be executed and when.
  • Modifiers like Roles and Delay/Timelock to enforce least‑privilege and reaction windows.

We’ll use fresh details from the latest Safe releases and the Zodiac tooling ecosystem, with concrete parameters you can lift into production.


The building blocks you’ll use (and what’s changed recently)

  • Safe Modules: Plug‑in contracts that can initiate transactions from your Safe without owner signatures, via execTransactionFromModule. This is how you enable automation. Use them sparingly and only when audited—they can execute arbitrary calls, including delegatecall. (docs.safe.global)
  • Safe Guards: Contracts that run pre‑ and post‑transaction checks for normal Safe transactions (owner‑signed flow). Classic Guards don’t apply to module‑initiated transactions. (docs.safe.global)
  • Module Guards (newer): Safe v1.5.0 adds a dedicated guard hook for module‑initiated transactions, letting you enforce policy on automation paths as well. If you haven’t upgraded your Safe, plan for it. (docs.safe.global)
  • Other v1.5.0 improvements that matter operationally: extensible fallback handler for composability, updated EIP‑1271 signature validation, and better revert propagation—useful for diagnosing failed proposal executions. (github.com)
  • Safe Shield: an example of guard‑backed, policy‑driven protection offered in the Safe product stack—useful as a mental model even if you’ll ship your own guard. (safe.global)

Key takeaway for decision‑makers: your automation surface is modules; your risk brakes are guards and module guards. Starting with Safe v1.5.0, you can apply brakes to both execution paths.


Pattern 1: Off‑chain voting, trustless onchain execution (Snapshot + Zodiac Reality)

Zodiac’s Reality Module (used by the SafeSnap plugin) lets you define a batch of transactions off‑chain, vote on them in Snapshot, then execute them from your Safe once the Reality.eth oracle finalizes that the proposal passed and matches the payload. Anyone can press “execute” once conditions are met—no signer bottleneck. (zodiac.wiki)

What actually happens onchain

  • A proposal includes:
    • proposalId (e.g., an IPFS hash) and
    • an array of EIP‑712 typed transaction hashes (txHashes). Each hash uniquely encodes to, value, data, operation, and a nonce equal to its position in the array. (github.com)
  • The module creates a Reality.eth question keyed by that proposalId + txHashes. Once Reality resolves “yes,” and a cooldown elapses, the module invokes execTransactionFromModule on the Safe to execute the queued calls in order. (github.com)
  • Safety levers you control on the module:
    • minimum bond (discourages low‑effort answers),
    • arbitrator (e.g., Kleros),
    • timeout (answer finalization window),
    • cooldown (grace period between answer finalization and execution),
    • optional answer expiration to prevent stuck executions from being runnable forever. (github.com)

Under the hood, Reality.eth is an optimistic oracle: answers require a bond; challenges must double it; disputes can escalate to an arbitrator (like Kleros). For DAO use, that combination balances responsiveness with recourse. (docs.kleros.io)

Operator‑level checklist to configure SafeSnap well

  • Timeout: 24–72 hours depending on community vigilance. Short timeouts improve velocity; longer ones give room for challenges. (realityeth.github.io)
  • Cooldown: 24–48 hours gives signers time to react if a malicious or erroneous answer slips through before execution. (docs.kleros.io)
  • Minimum bond: set a floor high enough to make griefing costly in your token units; Reality supports native and ERC‑20 deployments—use your governance token if your Snapshot space does. (realityeth.github.io)
  • Arbitrator: select Kleros for contested questions; confirm chain coverage and costs ahead of time. (docs.kleros.io)
  • Answer expiration: enable and run markProposalWithExpiredAnswerAsInvalid as a janitorial process to prevent surprise late executions. (github.com)

Governance UX notes

  • Your Snapshot proposal includes a “Transactions” section that encodes batched multisend payloads. After voting, “Request Execution” triggers the oracle flow; once resolved and cooldown passes, “Execute” is available to anyone. (zodiac.wiki)

Pattern 2: Add guardrails—Guards vs Module Guards (and how to combine them)

Without constraints, a module can do almost anything from your Safe—including a delegatecall. That’s power and risk. Use these controls:

  • Guard (classic): Validates owner‑signed Safe transactions. Implement checks like “block delegatecall” or “block transfers to non‑allowlisted receivers.” (docs.safe.global)
  • Module Guard: Validates module‑initiated transactions. Use it to ensure Reality‑driven executions can only touch allowlisted contracts and functions, respect amount caps, or disallow delegatecall entirely. You set it with setModuleGuard(address). (docs.safe.global)

Minimum viable policy pack we see working in practice

  • NoDelegateCall: revert if operation == DELEGATECALL. This closes the class of storage‑clobbering attacks and reduces blast radius. (docs.safe.global)
  • Allowlist selector scope: restrict module‑initiated calls to specific target addresses and function selectors that correspond to your treasury policy (e.g., ERC‑20 transfer/approve to known venues). Combine with per‑tx value caps.
  • Emergency blocklist: kill‑switch for known‑bad destinations.
  • Log and monitor: emit granular reasons on reverts and index events to observability.

Why it matters in 2025

  • As of v1.5.0, you can apply policy to both owner‑signed and module‑initiated flows. If you previously relied on a Guard alone, add a Module Guard to avoid a policy gap on automation paths. (docs.safe.global)

Pattern 3: Compose modifiers for least‑privilege and reaction time

Zodiac Modifiers are drop‑in controls you can place between modules and your Safe to add rate limits, roles, or delays.

  • Roles Modifier: a role‑based permission system that grants tightly scoped call rights: allowlisted targets, selectors, and parameter constraints; works with an SDK and a subgraph for auditability. Use it to ensure only a designated executor (e.g., your proposals module) can call only the functions you permit. (zodiac.wiki)
  • Delay Modifier: queue module transactions and enforce a cooldown/expiry; anyone can execute the next eligible tx after the window. This gives your team time to cancel or upgrade policy before funds move. (github.com)
  • SaferSafes (Optimism): production‑hardened combo of a liveness module (fallback owner if signers are unresponsive) and a timelock guard for scheduled execution. If you operate on OP chains with Safe 1.4.1, this is a pattern worth emulating; enforce invariants like livenessResponsePeriod ≥ 2× timelockDelay. (github.com)
  • Governor Module: if you prefer fully onchain voting (OpenZeppelin Governor) instead of Snapshot+Reality, run Governor as a Safe module and apply the same guard stack to it. (github.com)

Reference architecture: Automated proposal execution with layered controls

  1. Governance surface
  1. Execution engine
  • Zodiac Reality Module configured with:
    • arbitrator = Kleros
    • timeout = 48h
    • cooldown = 24h
    • minimumBond = governance token value approximating $5k
    • answerExpiration = 7 days
    • executor = your Safe These settings discourage spam, give reaction time, and prevent stale executions. (github.com)
  1. Policy layer
  • Module Guard that:
    • blocks delegatecall
    • allowlists ERC‑20 transfers to specified counterparties and stablecoins
    • enforces per‑transaction value caps and per‑day net spend for the module
  • Classic Guard that mirrors key constraints for owner‑signed flows (belt‑and‑suspenders). (docs.safe.global)
  1. Optional modifiers
  • Roles Modifier in front of the module to enforce target/function/param scoping in addition to guard checks (defense‑in‑depth).
  • Delay Modifier with 24h cooldown to serialize execution and offer a last‑chance cancel window. (zodiac.wiki)
  1. Monitoring and response
  • Watch these events and act:
    • Reality question created/answered/finalized (min bond escalations, arbitration triggers).
    • ExecutionFromModuleSuccess/Failure (investigate failures; upgrade guard if necessary).
    • ChangedGuard/ChangedModuleGuard and (en|dis)abled modules for config drift.
  • Use the Zodiac Roles subgraph and your SIEM for policy diffs over time. (docs.safe.global)

Implementation details you’ll want in your runbooks

  • Enable/disable modules safely: enabling is via enableModule(); disabling requires the previous module pointer (linked list) and the module address—document these so your team can remove a compromised module quickly. (docs.safe.global)
  • Module execution paths: modules call execTransactionFromModule or execTransactionFromModuleReturnData; index both success/failure events and include return data to ease debugging. (docs.safe.global)
  • Setting guards:
    • setGuard(address) for owner‑signed transaction policy.
    • setModuleGuard(address) for module policy. Both require Safe transactions; build break‑glass procedures to disable a bad guard if it bricks the Safe. (docs.safe.global)
  • Safe versioning: confirm you’re on ≥1.5.0 if you plan to use Module Guards and the extensible fallback handler. Track the Safe singleton address per network and verify in Settings before executing updates. (github.com)
  • Reality module specifics to avoid edge cases:
    • nonce/index rule: each transaction’s nonce equals its index in txHashes, ensuring uniqueness even if to/value/data repeat.
    • markProposalInvalid and answer expiration: use them actively to prevent “zombie” proposals from executing much later than intended. (github.com)
  • Arbitration economics: document arbitrator, fee schedule, and who is authorized to pay arbitration fees on behalf of the DAO. Remember: challenges must double the previous bond; plan treasury approvals accordingly. (reality.eth.limo)

Example: A sane configuration for a USDC‑denominated treasury

Context: A growth DAO approves spend from a Safe on Ethereum mainnet. They want one‑click execution after votes, but with strict spending policy.

  • Safe: 4‑of‑7 owners, Safe v1.5.0.
  • Modules:
    • Reality Module (arbitrator = Kleros, timeout = 48h, cooldown = 24h, minimumBond = 5,000 USDC equivalent, answerExpiration = 7d).
  • Modifiers:
    • Roles Modifier: role “proposal_executor” assigned only to the Reality module; allowlist:
      • USDC.transfer, USDC.approve to approved counterparties,
      • Multisend from Safe Appstore multisend addr,
      • No DEX approvals beyond 1% of treasury per 24h.
    • Delay Modifier: cooldown = 24h; expiration = 7d.
  • Guards:
    • Guard: no delegatecall; block unknown ERC‑20 approvals; cap owner‑signed transfers to ≤$50k unless proposal tag “emergency.”
    • Module Guard: enforce same constraints for module‑initiated txs; additionally reject any call with operation == DELEGATECALL. (docs.safe.global)
  • Monitoring:
    • Alerts on Reality question creation; bond escalations above $10k; arbitration requests; any ExecutionFromModuleFailure; any ChangedGuard/ChangedModuleGuard; any module enable/disable. (docs.safe.global)

Result: Proposals that pass are executable by anyone, but only within scoped permissions, after a time buffer, with controls that apply equally to human and automated paths.


Common failure modes (and how to avoid them)

  • “We added a module; it bypassed our guard.” Use Safe v1.5.0 and set a Module Guard; Guards alone don’t constrain module transactions. (docs.safe.global)
  • “A malicious module drained funds via delegatecall.” Block delegatecall in both Guard and Module Guard. Audit any module thoroughly; modules can execute arbitrary calls. (docs.safe.global)
  • “A proposal stayed executable for months and ran later.” Enable answer expiration and mark expired proposals invalid as part of your ops routine. (github.com)
  • “Nobody could transact; our guard bricked the Safe.” Treat guards like production firewalls: staged rollout on a test Safe, bounded blast‑radius (time‑boxed timelock), and a documented break‑glass plan to disable setGuard/setModuleGuard with an emergency owner set. (docs.safe.global)
  • “We needed to execute on Optimism with timelocks.” Use the SaferSafes timelock guard (Safe 1.4.1 compatible) and ensure livenessResponsePeriod ≥ 2× timelockDelay. (github.com)

Emerging best practices we recommend in 2025

  • Enforce policy on both transaction planes. Always pair a Guard with a Module Guard. Verify both are active after any Safe upgrade. (docs.safe.global)
  • Separate duties with Roles. Put proposal execution behind a distinct “executor” role, scoped to target/selector/params, then require all modules (Reality, Governor) to route through it. (zodiac.wiki)
  • Serialize with a timelock or delay. Even a 24‑hour delay significantly reduces tail risk and gives you time to push an emergency patch or disable a module before funds move. (github.com)
  • Instrument everything. Index Safe events like ExecutionFromModuleSuccess/Failure and Changed(Guard|ModuleGuard). Use dashboards to spot config drift instantly. (docs.safe.global)
  • Prefer v1.5.0 Safe deployments for new systems. Extensible fallback handlers and improved EIP‑1271 support reduce footguns and make integrations cleaner. (github.com)
  • Snapshot + Reality for broad participation, Governor for high‑assurance onchain voting. Both can be secured identically with module guards and modifiers—choose based on latency and user experience needs. (docs.snapshot.box)

Implementation crib notes (copy/paste to your internal wiki)

  • Enable Reality:
    • Deploy/attach the Zodiac Reality Module to your Safe.
    • In Snapshot, add the SafeSnap (Reality) plugin and set module address, timeout, cooldown, arbitrator, and min bond. (zodiac.wiki)
  • Add policy:
    • Submit Safe tx to setGuard(guardAddress) and setModuleGuard(moduleGuardAddress).
    • Roll out first on a staging Safe with identical owners/threshold; run a fire‑drill to disable a guard and remove a module. (docs.safe.global)
  • Tighten permissions:
    • Insert Zodiac Roles in front of modules; author a “proposal_executor” role with target/selector/param scopes; manage via the Roles SDK and app. (zodiac.wiki)
  • Add a delay:
    • Insert Zodiac Delay with cooldown and optional expiration; require executeNextTx for liveness. (github.com)
  • Validate:
    • Confirm Safe version in Settings; for ≥1.5.0 you’ll see module guard support.
    • Dry‑run a proposal with a tiny onchain transfer (e.g., 1 USDC) to exercise the full path and your monitoring. (github.com)

What to monitor in production

  • Reality flows: question->answer bonds->finalization; arbitration requests; cooldown expiry. This is your “proposal control plane.” (realitio.github.io)
  • Safe security posture: ChangedGuard/ChangedModuleGuard, module enable/disable, owner changes, threshold changes. Treat any unexpected change as a P1. (docs.safe.global)
  • Execution health: ExecutionFromModuleSuccess/Failure spikes usually mean a guard mismatch or a selector/param that isn’t allowlisted. (docs.safe.global)

Closing: Make your proposals self‑executing—safely

Automation should not mean giving up control. With Safe v1.5.0’s Module Guards, Zodiac’s Reality executor, and composable modifiers like Roles and Delay, you can achieve “approve once, execute trustlessly” while keeping granular, audit‑ready control over what can move, when, and to whom. If you’re designing a DAO treasury or enterprise crypto operations function, this stack is the current best of breed.

If you’d like an architecture review, guard policy authoring, or an implementation sprint, 7Block Labs can help you ship this pattern with confidence.


References and further reading

  • Safe Modules, execTransactionFromModule, enable/disable module APIs and behavior. (docs.safe.global)
  • Safe Guards vs Module Guards, setGuard and setModuleGuard; Safe concepts and v1.5.0 release notes. (docs.safe.global)
  • Tutorial: Build a no‑delegatecall Guard. (docs.safe.global)
  • Safe Shield overview for guard‑style policy management. (safe.global)
  • Zodiac Reality Module docs and README (proposalId/txHashes, bonds, cooldown, expiration). (zodiac.wiki)
  • Snapshot SafeSnap (Reality) plugin overview. (docs.snapshot.box)
  • Reality.eth docs and Kleros arbitration. (realitio.github.io)
  • Zodiac Roles Modifier (RBAC), Delay Modifier (queue and cooldown). (zodiac.wiki)
  • SaferSafes (OP): combined liveness module + timelock guard and its invariants. (github.com)

Like what you're reading? Let's build together.

Get a free 30‑minute consultation with our engineering team.

Related Posts

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2025 7BlockLabs. All rights reserved.