ExecutorDAO

The foundational modular DAO framework for Stacks — proposals as smart contracts, extensions as plugins, single-address ownership via sending context. Widely forked across the ecosystem.

Contracts executor-dao.clar (core) + dao-traits-v4 (trait definitions)
On-chain traits: SP2ZNGJ85ENDY6QRHQ5P2D4FXKGZWCKTB2T0Z55KS.dao-traits-v4
AuthorMarvin Janssen
SourceGitHub commit 4676718 (2022-07-11) · Traits verified on-chain via Hiro API
Clarity VersionPre-Clarity 4 (Clarity 1 — no as-contract? asset allowances)
Lines of Code68 (core) + 86 (traits) = 154 total
Audit Date2026-02-25
Confidence🟢 HIGH — compact codebase fully reviewed; all findings verified against source; no ambiguous external dependencies in core
0
Critical
1
High
2
Medium
1
Low
3
Info

Overview

ExecutorDAO is the most influential DAO framework on Stacks, forked and deployed by ALEX, Charisma, Megapont, aibtcdev, and dozens of other projects. The design is elegantly minimal: a single core contract that executes proposals (which are themselves smart contracts) and manages a registry of extensions. Extensions are plugin contracts that add governance tokens, voting, treasuries, etc.

The core contract is only 68 lines — a testament to Clarity's expressiveness. The three key design principles are:

The traits contract (dao-traits-v4) defines 10 traits covering SIP-010 tokens, proposals, extensions, governance tokens, NFTs, liquid FTs, CDK/EDK (land game mechanics), and fee-sharing.

Documented Limitations

Findings

HIGH

H-01: Blanket as-contract Grants Unlimited Asset Authority to Proposals and Extensions

Location: execute (line 52), construct (line 60), request-extension-callback (line 67)

Description: The core contract uses as-contract in three places to execute proposals, bootstrap, and handle extension callbacks. In pre-Clarity 4, as-contract grants blanket authority over all assets held by the DAO contract — STX, fungible tokens, NFTs, everything. Any proposal or extension that runs under as-contract can move any asset the DAO holds, with no language-level restriction on which assets it can touch.

;; Proposal gets blanket asset authority via as-contract
(define-public (execute (proposal <proposal-trait>) (sender principal))
  (begin
    (try! (is-self-or-extension))
    (asserts! (map-insert executed-proposals (contract-of proposal) block-height) err-already-executed)
    (print {event: "execute", proposal: proposal})
    (as-contract (contract-call? proposal execute sender))  ;; <-- blanket authority
  )
)

;; Extension callback also gets blanket authority
(define-public (request-extension-callback (extension <extension-trait>) (memo (buff 34)))
  (let ((sender tx-sender))
    (asserts! (is-extension contract-caller) err-invalid-extension)
    (asserts! (is-eq contract-caller (contract-of extension)) err-invalid-extension)
    (as-contract (contract-call? extension callback sender memo))  ;; <-- blanket authority
  )
)

Impact: A malicious or compromised proposal/extension can drain the entire DAO treasury in a single transaction. Since proposals are arbitrary smart contracts, a governance attack (e.g., flash-loan voting in a fork that uses token-weighted voting) could pass a proposal that steals all funds. The risk is amplified by the fact that most forks hold significant treasury assets.

Recommendation: Migrate to Clarity 4 and replace as-contract with as-contract? using explicit asset allowances. For example, a treasury management proposal should use (as-contract? (with-stx (with-ft .governance-token) ...)) to limit which assets the proposal can move. This provides defense-in-depth even if governance is compromised — a voting proposal can't touch treasury funds if it only has with-ft .governance-token allowance.

MEDIUM

M-01: No Emergency Shutdown or Pause Mechanism

Location: Entire contract architecture

Description: Once bootstrapped, the only way to modify the DAO is through proposals executed by extensions. If a critical vulnerability is discovered in an extension, there is no way to pause the DAO, disable all extensions atomically, or prevent malicious proposals from being executed while a fix is prepared. The only recourse is to rush a counter-proposal through whatever voting mechanism the fork has implemented.

Impact: In an active exploit scenario, the time between detection and a fix proposal being voted on and executed could allow an attacker to drain the DAO. Many DAOs have multi-day voting periods, during which the exploit would be unstoppable.

Recommendation: Add an emergency guardian pattern: a designated principal (multisig or timelock) that can pause all proposal execution and extension calls. The guardian should be removable by governance to prevent centralization. Example: (define-data-var paused bool false) checked at the top of execute and set-extension.

MEDIUM

M-02: sender Parameter in execute Is Caller-Controlled and Unverified

Location: execute function (line 49)

Description: The execute function takes a sender parameter that is passed to the proposal's execute function. This parameter is provided by the calling extension and is not validated against tx-sender or contract-caller. A malicious or buggy extension could pass an arbitrary principal as the sender, potentially impersonating another user to a proposal.

;; sender is caller-supplied, not verified
(define-public (execute (proposal <proposal-trait>) (sender principal))
  (begin
    (try! (is-self-or-extension))
    (asserts! (map-insert executed-proposals (contract-of proposal) block-height) err-already-executed)
    (print {event: "execute", proposal: proposal})
    (as-contract (contract-call? proposal execute sender))  ;; proposal trusts sender
  )
)

Impact: If a proposal uses the sender parameter for authorization decisions (e.g., "only admin X can trigger this proposal"), a compromised extension could spoof that identity. The severity depends on how downstream proposals use the sender value — in many forks, it's used purely for logging, making this low-impact. But in forks where proposals check sender identity, this is exploitable.

Recommendation: Either validate that sender equals tx-sender (or contract-caller) inside the core, or document clearly that proposals must NOT trust the sender parameter for authorization. The construct function correctly captures tx-sender in a local binding — execute should do the same.

LOW

L-01: construct Has No Replay Protection Across Forks

Location: construct function (line 56)

Description: The construct function checks that tx-sender equals the executive variable (set to the deployer at deploy time). After execution, it sets executive to the contract's own principal, preventing re-invocation. However, if the same deployer key is used across multiple chain forks or testnet deployments, the bootstrap proposal could be front-run or replayed if the deployer's key is compromised before construct is called.

(define-public (construct (proposal <proposal-trait>))
  (let ((sender tx-sender))
    (asserts! (is-eq sender (var-get executive)) err-unauthorised)
    (var-set executive (as-contract tx-sender))  ;; one-time lock
    (as-contract (execute proposal sender))
  )
)

Impact: Low — requires compromising the deployer key in the window between deployment and construct call. In practice, deployers typically call construct in the same block or shortly after deployment.

Recommendation: Call construct in the same transaction as deployment when possible. Document that the deployer key should be rotated or secured after bootstrap.

INFO

I-01: Pre-Clarity 4 — Migrate to as-contract? for Language-Level Asset Safety

Location: Entire contract

Description: This contract was written for Clarity 1 (epoch 2.05). Clarity 4 (epoch 3.3, activated at Bitcoin block 923,222) introduced as-contract? with explicit asset allowances. This is the single most impactful improvement available to ExecutorDAO forks. Instead of granting blanket authority, each as-contract? call explicitly declares which assets (STX, specific FTs, specific NFTs) the enclosed code can move. This directly mitigates H-01.

Recommendation: All new forks should use Clarity 4. The core execute function should use as-contract? with the minimum required asset allowances per proposal type. Consider a proposal categorization system where treasury proposals get with-stx/with-ft while governance proposals get no asset allowances.

INFO

I-02: Traits Contract Bundles Unrelated Concerns

Location: dao-traits-v4

Description: The traits contract defines 10 traits in a single file, mixing core DAO concerns (proposal-trait, extension-trait, governance-token-trait) with domain-specific traits (cdk-trait, edk-trait for land games, liquid-ft-trait, share-fee-to-trait). This creates an unnecessary coupling: any contract that needs the proposal trait must deploy a contract that also contains game mechanics traits.

Impact: No security impact. This is a code organization concern that increases deployment costs and reduces modularity.

Recommendation: Split traits into separate contracts: core DAO traits (proposal, extension, governance-token), token traits (sip010, nft, liquid-ft, ft-plus), and domain-specific traits (cdk, edk, share-fee-to).

INFO

I-03: governance-token-trait Uses Non-Standard dmg- Prefixed Functions

Location: dao-traits-v4governance-token-trait

Description: The governance token trait defines functions with a dmg- prefix (e.g., dmg-get-balance, dmg-transfer, dmg-mint). "DMG" appears to be a specific token name (possibly "Dungeon Master's Gold" from the Megapont ecosystem). This naming couples the generic DAO framework to a specific project's naming convention, making it confusing for forks that use different governance token names.

Impact: No security impact. Forks must implement functions named dmg-* even if their token has nothing to do with DMG.

Recommendation: Use generic names: gov-get-balance, gov-transfer, gov-mint, gov-burn, gov-lock, gov-unlock, gov-get-locked, gov-has-percentage-balance.

Architecture Analysis

Strengths

Trust Assumptions

Fork Guidance

ExecutorDAO's security is only as strong as its extensions. Forks should:

Priority Score

MetricScoreWeightWeighted
Financial Risk2 — manages extensions that control assets36
Deployment Likelihood3 — deployed on mainnet, widely forked26
Code Complexity2 — 154 lines, multi-contract framework24
User Exposure3 — most forked DAO framework on Stacks1.54.5
Novelty3 — foundational pattern, first audit of original1.54.5
Total (pre-penalty)2.5 / 3.0
Clarity version penalty (pre-Clarity 4)−0.5
Final Score2.0 / 3.0 ✅ AUDIT