ByAUJay
“ExecutionFromModuleSuccess” and “ExecutionFromModuleFailure” in DAO‑Multisig: What the Alerts Really Mean
Description: These two Safe (formerly Gnosis Safe) events are not generic “success/fail” logs — they’re precise signals that a whitelisted module executed (or attempted to execute) a transaction from your Safe account. This guide explains exactly what they indicate on L1 vs L2, how to investigate them, and how to harden your setup against the risky cases.
TL;DR for decision‑makers
- In Safe‑based DAO multisigs, modules can bypass the normal n‑of‑m signature flow to automate actions. When a module fires, the Safe emits ExecutionFromModuleSuccess or ExecutionFromModuleFailure. That’s your audit trail that “automation touched the treasury.” (docs.safe.global)
- On L2 networks (and on SafeL2 deployments), you also get a rich SafeModuleTransaction event with target, value, and operation, making incident response much easier; on L1, you’ll typically need traces or the Safe Transaction Service API to reconstruct the call. (scrollscan.com)
Why your SIEM keeps flagging these two events
Safe’s ModuleManager contract exposes execTransactionFromModule(...). Any enabled “module” (automation plug‑in) can call it to make the Safe perform a call or delegatecall without collecting owner signatures. The Safe emits one of two events afterward:
- event ExecutionFromModuleSuccess(address indexed module)
- event ExecutionFromModuleFailure(address indexed module)
These events are baked into the Safe contracts and fire immediately after the low‑level call returns true/false. They log only one thing: which module initiated the execution. (gnosisscan.io)
Key point for executives: a Success means “a whitelisted module just moved or changed something on behalf of your treasury,” not merely “a UI action succeeded.” A Failure means the module tried and the call returned false (often benign, sometimes a warning). (gnosisscan.io)
Where these alerts come from in the code
Inside ModuleManager.execTransactionFromModule, Safe checks that msg.sender is an enabled module, then executes the requested operation and emits one of the two events:
- success = execute(to, value, data, operation, gasleft());
if (success) emit ExecutionFromModuleSuccess(msg.sender);
else emit ExecutionFromModuleFailure(msg.sender);
That’s straight from verified Safe sources (Safe v1.3.x/1.4.x family); it’s the canonical behavior across networks. (gnosisscan.io)
On SafeL2 (the event‑rich variant used on most L2s), the contract also emits SafeModuleTransaction(module, to, value, data, operation) before the success/failure event so indexers can easily reconstruct what happened. (scrollscan.com)
L1 vs L2: why your forensics experience differs
- L1 (Safe.sol): Minimal events to save gas. You get ExecutionFromModuleSuccess/Failure but not the to/value/data; to reconstruct you usually need transaction tracing or the Safe Transaction Service (STS). (docs.safe.global)
- L2 (SafeL2.sol): Emits extra events for indexing, notably SafeModuleTransaction, which includes the details you need for quick triage. This is why many dashboards feel “instantly informative” on Arbitrum/Optimism/Base/Scroll vs slower on L1 unless they run traces. (docs.safe.global)
The Safe Transaction Service explicitly documents this: it indexes Safe activity with traces on L1 and with events on L2. It exposes module transaction endpoints that normalize “isSuccessful,” module address, target, value, data, and operation. (docs.safe.global)
Real on‑chain examples to recognize
- You’ll often see ExecutionFromModuleSuccess with modules like the ERC‑4337 Safe adapter (e.g., Safe7579), indicating a module‑driven execution in account‑abstraction flows. Etherscan receipts show the Success event from the Safe and an adjacent SafeModuleTransaction on L2. (etherscan.io)
- Topic 0 (event signature) for ExecutionFromModuleSuccess is keccak256("ExecutionFromModuleSuccess(address)"), which you’ll see as 0x6895…becb8 in logs. Use this topic in filters if your tooling doesn’t support ABI‑based decoding. (etherscan.io)
Remember: the event only guarantees “a module did something.” To learn what, correlate with SafeModuleTransaction (L2) or fetch the module transaction from the STS API or node traces (L1). (scrollscan.com)
What actually triggers “Failure”?
Failure means the inner call/delegatecall returned false. Common benign reasons:
- The target call intentionally reverts on a “dry run” by a module probing capabilities.
- A rate‑limit/roles modifier rejected an action (e.g., Roles/Delay modifiers in Zodiac setups).
- A dependency wasn’t ready (allowance missing; target paused).
Since execTransactionFromModule returns a bool and emits events instead of bubbling the revert, you’ll record a Failure event without necessarily reverting the whole outer tx. This is by design for modules to implement their own control flow. (gnosisscan.io)
Your alerting should therefore treat isolated Failures as “investigate context,” not automatically “breach.” Look for repeated failures from a new or unexpected module, or failures paired with config changes. (docs.safe.global)
Mapping alerts back to real actions (step‑by‑step)
Use this three‑step playbook whenever you catch ExecutionFromModuleSuccess/Failure:
- Identify the module and check it’s still enabled
- Pull the Safe’s module registry (on‑chain via getModulesPaginated or via API) to confirm the module is authorized. If it’s not enabled, escalate immediately (possible spoofed event or misconfigured indexer). (gnosisscan.io)
- Reconstruct the call details
- L2: Parse the SafeModuleTransaction event in the same transaction for module, to, value, data, and operation, then decode the calldata. (scrollscan.com)
- L1: Query the Safe Transaction Service “Get Module Transaction” by tx hash or list module transactions for the Safe; STS uses traces to rebuild “to/value/data/operation” and “isSuccessful.” (docs.safe.global)
- Classify the operation risk
- operation == 1 (DELEGATE_CALL) is high risk because it mutates the Safe’s storage. Prefer to disallow it for modules unless absolutely necessary. Use a Module Guard or a Roles/Delay chain to block or protect these. (scrollscan.com)
Practical examples you can mirror
- A DAO’s “automation” module executing a token transfer on L2
- You’ll see SafeModuleTransaction(module, to=token, value=0, data=transfer(...), operation=CALL) followed by ExecutionFromModuleSuccess. Confirm the token address and recipient against your policy allowlist. (scrollscan.com)
- An ERC‑4337 userOp routed through a Safe module
- The receipt will show EntryPoint events, the Safe’s ExecutionFromModuleSuccess, and often the module address of a 7579 adapter. This is expected for AA‑powered Safe accounts; whitelist these modules in your monitoring. (etherscan.io)
- A Failure from a Roles+Delay pipeline (Zodiac)
- Delay Modifier enqueues, then later executes; a Failure could indicate the queued call expired or was invalidated. Check your Delay queue and the Roles ruleset before escalating. (github.com)
The most common misunderstandings (and how to avoid them)
- “Success means owners approved it.” Wrong — it means a module executed without owner signatures. That’s the whole purpose of modules. Confirm module legitimacy, then decode the action. (docs.safe.global)
- “Failure means we were attacked.” Not necessarily. Many modules probe or guard themselves with revert‑on‑unexpected‑state patterns. Correlate with repeated attempts, new modules, or sensitive operations (delegatecall, owner/threshold changes). (gnosisscan.io)
- “Events look different across networks.” Yes. On L1 the base Safe emits fewer events (cheaper); on L2, SafeL2 surfaces more indexing events. Plan your monitoring accordingly. (docs.safe.global)
Monitoring recipes your team can deploy this week
- Minimal event filter (all chains)
Filter for ExecutionFromModuleSuccess/Failure by topic0 and restrict address to your Safe(s):
// ethers.js v6 import { ethers } from 'ethers'; const iface = new ethers.Interface([ 'event ExecutionFromModuleSuccess(address indexed module)', 'event ExecutionFromModuleFailure(address indexed module)' ]); const filter = { address: SAFE_ADDRESS, // your Safe topics: [ // topic0 = either Success or Failure signature [iface.getEvent('ExecutionFromModuleSuccess').topicHash, iface.getEvent('ExecutionFromModuleFailure').topicHash] ] }; provider.on(filter, async (log) => { const parsed = iface.parseLog(log); const module = parsed.args.module; // Step 1: verify module is enabled; Step 2: fetch details (see below) });
For custom pipelines that operate by topic hash, ExecutionFromModuleSuccess has topic0 0x6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb8. Compute others via keccak256("EventName(types)") if needed. (etherscan.io)
- Auto‑enrichment for details
- L2: parse SafeModuleTransaction in the same receipt; decode “data” to a function and parameters. (scrollscan.com)
- L1: call Safe Transaction Service: GET /api/v1/module-transactions?safe=0x… to list, or GET /api/v1/module-transactions/{id} if you know the moduleTransactionId. Use isSuccessful and dataDecoded fields for instant clarity. (docs.safe.global)
- Detection rules that actually surface risk
- High priority: Success from a module not on your allowlist; Success with operation == DELEGATE_CALL; any Success immediately following ChangedModuleGuard/EnabledModule events. (docs.safe.global)
- Medium priority: Repeated Failures from a new module; Failures correlated with guard/roles changes. (docs.safe.global)
Governance and security guardrails that reduce alert noise
- Always pair modules with a Module Guard. Module Guards pre‑check module‑initiated calls and can enforce allowlisted targets/selectors, value caps, and “no delegatecall” policies. Configure via setModuleGuard(address). (docs.safe.global)
- Add Zodiac modifiers in front of your Safe:
- Roles Modifier: granular, selector‑level permissions for modules; encode “only these functions on these contracts.” (github.com)
- Delay/Timelock Modifier: enforce a cooldown so you can react before funds move; transactions are enqueued and executed later. (github.com)
- Treat modules as superusers. Safe docs warn that modules can take over a Safe; enable only audited, least‑privilege modules and review them like you would a privileged production microservice. (zkevm.polygonscan.com)
How to triage a “Failure” like a pro (10‑minute drill)
- Confirm the module is still enabled on the Safe. If not, investigate indexing drift. (gnosisscan.io)
- Reconstruct the target/action:
- L2: read SafeModuleTransaction event.
- L1: fetch module transaction from STS; if unavailable, run a trace on the tx. (docs.safe.global)
- Classify: harmless probe vs policy block vs real issue (e.g., repeated attempts to change owner set).
- If suspicious: temporarily disable the module (owners sign a standard Safe tx) and/or raise threshold while you investigate.
- Post‑mortem: tighten the Module Guard or Roles policy so the same failure pattern won’t spam alerts.
Notes on ERC‑4337 and modules (why you’ll see more of these)
Account‑abstraction adapters for Safe (e.g., Safe7579) run as modules and invoke execTransactionFromModule during user operation validation/execution. If you’re adopting AA, expect regular ExecutionFromModuleSuccess logs; allowlist the adapter module and focus your detections on target/selector and delegatecall usage, not on the mere presence of the event. (github.com)
The difference from owner‑signed multisig executions
Owner‑signed multisig transactions emit ExecutionSuccess/ExecutionFailure (different events), not the module events. In your SIEM, keep these streams separate so you can distinguish “human‑approved” activity from “automation‑initiated” activity at a glance. (gist.github.com)
Implementation snippet: enrich an alert with STS (TypeScript)
import SafeApiKit from '@safe-global/api-kit'; const api = new SafeApiKit({ chainId: 1n }); // mainnet, adjust per chain async function enrichModuleTx(txHash: string) { // List module txs for this Safe, filter by hash const list = await api.getModuleTransactions({ safe: SAFE_ADDRESS, transactionHash: txHash }); if (list.results.length === 0) return null; const t = list.results[0]; return { isSuccessful: t.isSuccessful, module: t.module, to: t.to, value: t.value, operation: t.operation, // 0=CALL, 1=DELEGATE_CALL, 2=CREATE dataDecoded: t.dataDecoded }; }
STS flattens the hard bits for you (especially on L1 where you don’t get the rich L2 events). Build this enrichment into your alert pipeline. (docs.safe.global)
Emerging best practices we recommend in 2026
- Enforce policy on both planes: use a classic Guard for owner‑signed tx and a Module Guard for module‑initiated tx. Verify both after any Safe upgrade. (docs.safe.global)
- Default‑deny DELEGATE_CALL from modules unless there’s a documented need. Review any Success with operation==1. (scrollscan.com)
- Add a 24–72h Delay modifier in front of treasury‑impacting module actions; serialize high‑risk executions so you can respond. (github.com)
- Instrument configuration drift: alert on EnabledModule/DisabledModule and ChangedModuleGuard events, not just executions. This catches supply‑chain changes before money moves. (docs.safe.global)
- Choose SafeL2 where available for event‑rich monitoring; if you must stay on L1, run STS with tracing enabled and treat it as your source of truth. (docs.safe.global)
Executive summary to share with your board
- ExecutionFromModuleSuccess/Failure are the Safe contract’s way of telling you “automation touched the treasury.” They’re emitted by design whenever an enabled module executes a transaction. On L2 you can read the full call details from events; on L1 you’ll use traces or the Safe Transaction Service. Put Module Guards, Roles, and Delay in front of modules, and alert on policy‑breaking patterns (unknown module, delegatecall, config drift) instead of spamming on every Failure. (gnosisscan.io)
Appendix: exact references you can hand to your engineers
- Module events in Safe source and behavior of execTransactionFromModule (emits Success/Failure). (gnosisscan.io)
- SafeL2 emits SafeModuleTransaction (richer L2 indexing). (scrollscan.com)
- Docs for execTransactionFromModule and execTransactionFromModuleReturnData. (docs.safe.global)
- STS indexing model (L1 traces vs L2 events) and module transaction APIs. (docs.safe.global)
- Modules can bypass signatures; treat them as high‑privilege components. (docs.safe.global)
If you want our team to review your current Safe configuration and write the Module Guard/Delay/Roles policies tailored to your treasury operations, 7Block Labs can do this in a week‑long engagement and hand you a monitoring ruleset that only fires on what matters.
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

