7Block Labs
Decentralized Finance

ByAUJay

Summary: If your DAO treasury runs on a Gnosis Safe, every ExecutionFromModuleSuccess event is a bypass of owner confirmations by design. This guide shows exactly how to operationalize Forta “findings” for that event using the oz-gnosis-events bot, Forta GraphQL, and SDK workflows—so you can alert, triage, and auto-respond in seconds across L1/L2s.

“DAO Treasury Multisig” Forta GetFindings: Using Forta DAO‑Multisig ExecutionFromModuleSuccess Alerts

Decision-makers are right to worry about modules on a Gnosis Safe. ExecutionFromModuleSuccess signals that some module executed a transaction without gathering the Safe’s usual threshold of owner approvals. That’s frequently legitimate (Zodiac Delay, Reality, Roles, SafeSnap), but it’s also the exact surface adversaries abuse when a malicious module is added or a legitimate module is misconfigured. Forta can turn those raw on-chain events into actionable, low-latency alerts you can route to Slack, PagerDuty, and automated mitigations. (gnosisscan.io)

Below is a hands-on playbook to: (1) subscribe to the right Forta bot, (2) query “findings” via GraphQL and SDK, (3) filter for ExecutionFromModuleSuccess in real time, and (4) wire responses through OpenZeppelin Defender or your SIEM—with exact queries, code, and ops runbooks. (docs.forta.network)


Why ExecutionFromModuleSuccess belongs on your P0 watchlist

  • What it means: a Safe module executed a transaction using execTransactionFromModule; the ModuleManager contract emitted ExecutionFromModuleSuccess(module). No multisig threshold is required for this path. (gnosisscan.io)
  • Why it’s risky: modules can be more powerful than owners; a malicious or backdoored module can transfer assets or reconfigure the Safe. This has been a known class of governance/treasury risk since at least 2020. (blog.openzeppelin.com)
  • What “good” looks like: known modules (e.g., Zodiac Delay, Reality, Roles) performing expected actions to whitelisted targets, at expected cadences and amounts; anything else escalates. (github.com)

The Forta building blocks you’ll use

  • Bot to subscribe to: OpenZeppelin–Gnosis Safe Contract Events (aka oz-gnosis-events). It emits findings for “ALL events” in OpenZeppelin and Gnosis Safe repos—including ExecutionFromModuleSuccess/Failure, EnabledModule, DisabledModule, ExecutionSuccess. Supported on mainnet and major L2s (Polygon, Arbitrum, Optimism, Avalanche). (docs.forta.network)
  • How Forta delivers data: decentralized scan nodes run detection bots and publish alerts (“findings”) you can pull via the Forta GraphQL API or SDK. (docs.forta.network)
  • Access control: generate an API key in Forta App to query alerts or consume alerts inside bots; you can filter by bot IDs, alert name/IDs, chain, date/time, addresses, and scanner confirmations. (docs.forta.network)

Step 1 — Identify the DAO multisig and its modules

Before wiring alerts, inventory your Safe(s):

  • Safe address(es) and chain IDs (e.g., 1 for Ethereum, 137 Polygon, 42161 Arbitrum, 10 Optimism).
  • Expected module allowlist: addresses of Zodiac modules (Delay, Reality, Roles), bridges, guards; expected targets they’re allowed to call.
  • Required routing destinations: Slack/Discord, PagerDuty/Datadog, email, webhooks.

This baseline lets you write alert filters that only escalate on deviations from policy (unknown module, unknown target, abnormal amount/cadence, wrong chain).

Pro tip: also monitor EnabledModule/DisabledModule events on the same Safe to get early signal when module configuration changes. The oz‑gnosis‑events bot emits these events as well. (docs.forta.network)


Step 2 — Subscribe to the oz‑gnosis‑events bot in Forta

  • In Forta App, open the oz‑gnosis‑events bot and click Subscribe. You can add address filters and choose channels (Slack, Discord, Email, Telegram, Webhook). Note: subscription requires the relevant Forta plan. (gate.com)
  • For programmatic access, you’ll query via GraphQL or call the SDK’s getAlerts method (shown below). (docs.forta.network)

Step 3 — Query “findings” (alerts) for ExecutionFromModuleSuccess via GraphQL

Forta’s GraphQL “alerts” query is your primary API. You’ll filter by:

  • bots: the oz‑gnosis‑events bot ID(s)
  • alertName: ExecutionFromModuleSuccess
  • optional: addresses (your Safe), chainId, scanNodeConfirmations, date/blocks

Operation:

query moduleExecAlerts($input: AlertsInput) {
  alerts(input: $input) {
    pageInfo {
      hasNextPage
      endCursor { alertId blockNumber }
    }
    alerts {
      createdAt
      name
      severity
      findingType
      metadata
      addresses
      chainId
      source {
        transactionHash
        block { number chainId timestamp }
        bot { id }
      }
      scanNodeCount
    }
  }
}

Example variables for a mainnet Safe:

{
  "input": {
    "first": 50,
    "bots": ["<OZ_GNOSIS_EVENTS_BOT_ID>"],
    "alertName": "ExecutionFromModuleSuccess",
    "addresses": ["0xYourDaoSafe"],
    "chainId": 1,
    "scanNodeConfirmations": { "gte": 2, "lte": 10 },
    "blockSortDirection": "desc",
    "createdSince": 600000
  }
}
  • createdSince is in ms (600000 = last 10 minutes); scanNodeConfirmations reduces false positives; paginate using the endCursor if hasNextPage. (forta-network.github.io)

Reference: GraphQL AlertsInput supports bots, alertName, alertIds, addresses, chainId, createdSince, block ranges, and scanner confirmations. Endpoint: https://api.forta.network/graphql (Authorization: Bearer API_KEY). (docs.forta.network)


Step 4 — Pull the same data from application code (JS/TS SDK)

When you need to enrich and route alerts inside your own services, use the SDK’s getAlerts:

import { getAlerts, InitializeResponse } from "forta-agent";

// fetch latest module execs for a DAO safe on mainnet
const OZ_GNOSIS_EVENTS = "<OZ_GNOSIS_EVENTS_BOT_ID>";
const DAO_SAFE = "0xYourDaoSafe";

const res = await getAlerts({
  botIds: [OZ_GNOSIS_EVENTS],
  alertId: undefined,                 // use alertName filters post-query
  addresses: [DAO_SAFE],
  chainId: 1,
  createdSince: 10 * 60 * 1000,       // last 10 minutes
  first: 100,
  scanNodeConfirmations: { gte: 2 }   // reliability over speed
});

const moduleExecs = res.alerts.filter(a => a.name === "ExecutionFromModuleSuccess");

Notes:

  • getAlerts requires a Forta API key set in forta.config.json or env; the SDK also exposes getLabels, sendAlerts, and helpers. (docs.forta.network)

Step 5 — Tie Alert → Action with Defender or your incident tooling

Two proven paths:

  • OpenZeppelin Defender “Forta Sentinels” integration: point a monitor to Forta Bot ID(s) and optional Alert ID/Name filters; on match, trigger Actions/Autotasks (e.g., pause, set caps, revoke roles) and send notifications. (forum.openzeppelin.com)
  • Defender Monitor + Actions (or your SIEM): build an HTTP webhook that accepts the Forta alert payload and applies your policy logic; if the module/target is not on allowlist or the amount exceeds thresholds, block downstream, page a responder, or execute an emergency Safe transaction. (docs.openzeppelin.com)

What “good” filtering looks like (practical recipes)

Use these predicates together to minimize noise while catching real issues:

  • Known-module allowlist: pass only if module in {Delay, Reality, Roles, SafeSnap, …}. Otherwise escalate. Enforce chain-specific module addresses. (github.com)
  • Known-target allowlist: for each module, maintain a set of valid target contracts and function selectors; escalate unknown targets.
  • Cadence guards: raise if the same module executes > N times within T minutes for a single Safe (a hallmark of drain or spam).
  • Value guards: add decoded value thresholds; e.g., ERC20 transfers > X, bridge sends > Y, or arbitrary calls with high value.
  • Change correlation: if you saw EnabledModule in the last N blocks and then an ExecutionFromModuleSuccess from that module, escalate harder. (docs.forta.network)
  • Multi-signal combo: combine ExecutionFromModuleSuccess with detectors for exploiter labels, Tornado funding, or Attack Detector to raise priority. (docs.forta.network)

Example: Minimal “GetFindings” pipeline for a DAO Safe

  1. Pull the last minute of ExecutionFromModuleSuccess findings for a Safe:
{
  "input": {
    "first": 25,
    "bots": ["<OZ_GNOSIS_EVENTS_BOT_ID>"],
    "alertName": "ExecutionFromModuleSuccess",
    "addresses": ["0xYourDaoSafe"],
    "chainId": 1,
    "createdSince": 60000,
    "scanNodeConfirmations": { "gte": 2, "lte": 10 }
  }
}
  1. For each alert, decode metadata to extract:
  • module address (emitter), target/to, value, function selector, logs.
  • Cross-check module and target against your allowlists.
  1. If unknown module or target, or abnormal value/cadence:
  • Post to Slack “security” channel
  • Page on-call if value > threshold
  • Kick off Defender Action to “pause” a protocol contract or temporarily disable the module via your Security Council Safe.

All of this can run every 15–30 seconds with very low overhead and sub-minute MTTD. (forta-network.github.io)


Under the hood: why Forta is reliable for this signal

  • Decentralized scan nodes run detection bots per block and publish alerts; you can require scanNodeConfirmations ≥ 2–3 for stronger guarantees (speed vs. certainty trade-off). (docs.forta.network)
  • The oz‑gnosis‑events bot specifically listens for Gnosis Safe core events (ModuleManager: EnabledModule/DisabledModule/ExecutionFromModuleSuccess/Failure) and OpenZeppelin library events, across major EVM chains. That breadth means the same playbook works on L2s where your DAOs also hold funds. (docs.forta.network)

Robustness tips for production

  • Rate limiting and pagination: use first and after with endCursor; don’t assume large backfills will return in a single page. (forta-network.github.io)
  • Time vs block windows: prefer createdSince for near-real-time; for audits and forensic windows use blockDateRange or blockNumberRange. (forta-network.github.io)
  • Bot IDs are required for efficient queries; resolve the oz‑gnosis‑events Bot ID via its Stats page in Forta App. (forta-network.github.io)
  • Auth: set Authorization: Bearer <API_KEY> for GraphQL; store keys securely (vault) and rotate periodically. (docs.forta.network)
  • SDK vs GraphQL: the SDK’s getAlerts wraps the same GraphQL; prefer SDK in app code, GraphQL for quick ad‑hoc analysis and dashboards. (docs.forta.network)
  • Scanner confirmations: for paging alerts, set gte = 2–3; for automated mitigations that must be fast, you may temporarily accept gte = 1 with a confirmation follow-up. (forta-network.github.io)

Emerging best practices we’re seeing across DAOs

  • Split “info” vs “pager” channels: ExecutionFromModuleSuccess with known module and known target goes to an info channel; any deviation goes to PagerDuty with a playbook link.
  • Defense in depth: also watch for ExecutionFromModuleFailure (could indicate probing), EnabledModule/DisabledModule, and owner set changes on your Safe. Roll these into a single “DAO Multisig Posture” dashboard.
  • Combine with Attack Detector feed: if the module execution coincides with funding/prep/exploitation signals (e.g., Tornado-funded account touches your contracts), raise severity. (docs.forta.network)
  • Governance hygiene: treat module adds/removes like constitutional amendments—require explicit, timed votes and broadcast to stakeholders. Use Forta to verify on‑chain that the announced change is what actually executed.
  • Post‑mortem ready logs: persist Forta alert payloads and decoded traces alongside your incident tickets to accelerate IR.

Worked example: Arbitrum DAO Safe, Roles module

Scenario: Your Arbitrum Treasury Safe 0xYourDaoSafe has a Zodiac Roles module at 0xRolesMod authorized to top up a bridged treasury contract and pay auditors.

  • Expected behavior: ExecutionFromModuleSuccess where module = 0xRolesMod, target in {BridgeTreasury, AuditorPayout}, function selectors {deposit(uint256), transfer(address,uint256)}, amounts < 500k USD equivalent per 24h.
  • Forta query: chainId = 42161; alertName = “ExecutionFromModuleSuccess”; addresses = [0xYourDaoSafe]; bots = [oz‑gnosis‑events].
  • Escalate if:
    • module NOT in allowlist, or
    • target NOT in allowlist, or
    • amount > 500k, or
    • 3 executions in 10 minutes, or

    • module was just EnabledModule in last 100 blocks and is now executing. (docs.forta.network)

Going beyond: consuming Forta alerts inside your own bots

You can build a Forta “combiner” bot that subscribes to oz‑gnosis‑events and emits a single, opinionated alert “DAO-MULTISIG-MODULE-EXEC-ANOMALY” only when your policy deems it risky. Use the v2 handleAlert pattern:

  • In initialize, subscribe to oz‑gnosis‑events and filter for alertIds/names you care about (ExecutionFromModuleSuccess/Failure, EnabledModule, DisabledModule).
  • In handleAlert, apply your policy logic; if violated, emit a single high-severity Finding with rich metadata. This improves precision for downstream responders. (docs.forta.network)

Bonus: pull the broader context around a firing

When ExecutionFromModuleSuccess fires, grab the last 50–200 blocks of:

  • Other Safe events (EnabledModule/DisabledModule/ExecutionSuccess)
  • Labels on interacting addresses (exploiter, sanctioned, newly deployed) via Forta labels
  • Any Attack Detector findings for the same address set

All are available via the same Forta API/SDK. Combining them in your incident timeline elevates analyst confidence and compresses MTTR. (docs.forta.network)


Checklist: make “module exec” alerts production-grade in one sprint

  • Forta
  • Policy
    • Compile module and target allowlists per Safe/chain.
    • Define thresholds (amounts/cadence) and escalation paths.
  • Integrations
  • QA
    • Simulate benign module execs to confirm no pages.
    • Simulate a policy breach (unknown target) to validate escalation.
  • Operate
    • Weekly review of module/target allowlists and thresholds.
    • Quarterly game day: practice emergency module disable with the Security Council Safe.

Appendix — Known gotchas

  • Naming vs IDs: Forta supports filtering by alertName and/or alertIds. For oz‑gnosis‑events, using alertName=ExecutionFromModuleSuccess is the most straightforward. If your team standardizes on alertIds, add that too. (docs.forta.network)
  • Chain forks/reorgs: if you react on gte=1 scanner confirmation, accept a small chance of noisy alerts during reorgs; re-check scanNodeCount before acting.
  • Private alerts: some bots can publish private alerts only visible via API when the bot ID is specified; not relevant for oz‑gnosis‑events, but keep in mind for broader programs. (docs.forta.network)

Closing thought

For DAO treasuries, modules are power. ExecutionFromModuleSuccess tells you exactly when that power is exercised—good or bad. Forta gives you the earliest and most consistent signal across chains. With a tight allowlist, scanner confirmations, and an automated response path, you’ll reduce your MTTD to minutes (or less) and your MTTR to a single Safe action.


References and further reading

  • Forta GraphQL and AlertsInput (bots, alertName/Ids, addresses, createdSince, confirmations). (docs.forta.network)
  • Example GraphQL queries (recent alerts, pagination, confirmations). (forta-network.github.io)
  • Forta SDK getAlerts and bot consumption patterns. (docs.forta.network)
  • oz‑gnosis‑events bot (ALL OpenZeppelin/Gnosis Safe events; EnabledModule/DisabledModule/ExecutionFromModuleSuccess/Failure). (docs.forta.network)
  • Gnosis Safe ModuleManager events (ExecutionFromModuleSuccess/Failure). (gnosisscan.io)
  • Why module power matters (backdooring Safe via modules during setup). (blog.openzeppelin.com)
  • Integrating Forta alerts with OpenZeppelin Defender (notifications and actions). (forum.openzeppelin.com)
  • How Forta works (scan nodes, bots, alerts, subscriptions). (docs.forta.network)

7Block Labs can implement this end to end—subscription, queries, filters, incident routing, and automated responses—across your main DAO Safe and any sub‑treasury Safes on L2s.

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.