Dungeon Master

Charisma protocol's core DAO governance controller — ExecutorDAO pattern with extension-based access control and proposal execution

ContractSP2D5BGGJ956A635JG7CJQ59FTRFRB0893514EZPJ.dungeon-master
ProtocolCharisma — gamified DeFi on Stacks
SourceVerified on-chain via Hiro API (/v2/contracts/source)
Clarity VersionClarity 1 (pre-Clarity 4 — no as-contract? asset allowances)
Publish Height98,098
Lines of Code~75
Audit Date2026-02-25
Confidence🟢 HIGH — compact contract, fully reviewed; security heavily depends on extension/proposal governance (out of scope)
0
Critical
1
High
2
Medium
1
Low
2
Info

Overview

The Dungeon Master is the core governance contract for the Charisma protocol, implementing the ExecutorDAO pattern. It serves as the central authority through which all governance actions flow — proposals are executed under the contract's identity, and extensions (trusted sub-contracts) can modify state or trigger further actions.

The contract provides five key capabilities:

The trust model is straightforward: only the contract itself (via proposals) or registered extensions can call privileged functions. The deployer's power is revoked after the initial construct call.

Documented Limitations / Design Notes

Findings

HIGH

H-01: Blanket as-contract grants unlimited asset authority to proposals

Location: execute function

Description: The execute function runs proposals under as-contract, which in Clarity 1 grants the proposal code full authority to transfer any asset the DAO contract holds — STX, fungible tokens, and NFTs. There is no way to scope or limit what a proposal can do once it executes.

(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))
  )
)

Impact: Any proposal or extension that calls execute with a malicious proposal contract can drain all assets held by the DAO. The entire security of the system depends on governance gatekeeping (e.g., voting extensions) never approving a malicious proposal. A compromised or buggy extension is equivalent to a complete DAO takeover.

Recommendation: Migrate to Clarity 4 and use as-contract? with explicit asset allowances (with-ft, with-nft, with-stx) for proposal execution. This would enforce at the language level which assets a proposal can move, rather than relying solely on governance review. Alternatively, implement a proposal type system with different execution contexts (e.g., admin proposals vs. treasury proposals with spending caps).

MEDIUM

M-01: Extension callback allows code execution under DAO identity

Location: request-extension-callback function

Description: The request-extension-callback function lets any registered extension trigger its own callback function with the DAO as tx-sender (via as-contract). While the function validates that contract-caller matches the extension being called, the callback executes under the DAO's 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))
  )
)

Impact: A registered extension's callback function runs as the DAO principal. If the extension's callback has any asset-moving logic, it could be abused. The double-check (is-extension + contract-caller == contract-of extension) prevents impersonation, but any enabled extension can self-invoke callbacks with arbitrary memos.

Recommendation: Document the security invariant that extension callback implementations must be carefully audited since they run under DAO identity. With Clarity 4, use as-contract? with scoped allowances for callbacks.

MEDIUM

M-02: is-self-or-extension uses tx-sender for self-check instead of contract-caller

Location: is-self-or-extension private function

Description: The authorization check uses (is-eq tx-sender (as-contract tx-sender)) for the "self" branch. This checks whether the original transaction sender is the contract itself, which only holds true when the contract is calling itself through as-contract. However, the extension branch checks contract-caller, which is the more appropriate check for inter-contract calls.

(define-private (is-self-or-extension)
  (ok (asserts! (or (is-eq tx-sender (as-contract tx-sender))
                    (is-extension contract-caller)) err-unauthorized))
)

Impact: The self-check via tx-sender means the DAO can only call itself when it's the original transaction initiator (i.e., within an as-contract block). This is the intended behavior for the ExecutorDAO pattern — proposals executing under as-contract can call back into the DAO. However, mixing tx-sender and contract-caller checks in the same authorization function is a subtle pattern that could confuse auditors or lead to unexpected behavior if the call graph changes.

Recommendation: Consider documenting why tx-sender is used for the self-check. For maximum clarity, the self-check could use contract-caller instead: (is-eq contract-caller (as-contract tx-sender)) — though this would change the semantics slightly (only direct callers, not transitive ones, would pass).

LOW

L-01: No mechanism to revoke or rotate extensions in bulk emergencies

Location: set-extensions function

Description: While set-extensions supports batch operations (up to 200), disabling a compromised extension requires either the DAO itself or another extension to act. If a malicious extension is discovered, the response time depends on governance speed.

Impact: In an emergency where an extension is compromised, there is no circuit breaker or emergency shutdown mechanism. The DAO must go through normal governance flow to disable the extension.

Recommendation: Consider implementing a guardian/emergency multisig pattern that can freeze extensions without a full governance vote. This is a common pattern in DeFi DAOs for rapid incident response.

INFO

I-01: Clarity 1 — missing modern safety features

Description: This contract uses Clarity 1 (deployed at block 98,098, well before Clarity 4 at Bitcoin block 923,222). It lacks access to critical safety builtins:

Recommendation: When upgrading the DAO (deploying a new version), use Clarity 4 to benefit from language-level asset safety. The as-contract? builtin would be particularly valuable for scoping proposal and callback execution authority.

INFO

I-02: sender parameter in execute is caller-supplied, not verified

Description: The execute function accepts a sender parameter that is passed directly to the proposal's execute function. This parameter is not verified against the actual tx-sender — any extension can supply any principal as the sender.

(define-public (execute (proposal <proposal-trait>) (sender principal))
  (begin
    (try! (is-self-or-extension))
    ...
    (as-contract (contract-call? proposal execute sender))
  )
)

Impact: This is by design — extensions (like voting contracts) track who initiated a proposal and pass that context through. However, proposal contracts must not use this sender parameter for authorization decisions without additional verification, as it can be spoofed by any registered extension.

Recommendation: Proposal implementations should treat the sender parameter as informational context, not as an authentication credential. Document this trust boundary clearly.

Architecture Analysis

The Dungeon Master implements a clean ExecutorDAO pattern with a minimal attack surface:

FunctionAccess ControlPurpose
constructDeployer (one-time)Bootstrap DAO with initial proposal
executeSelf or extensionRun proposal under DAO authority
set-extension(s)Self or extensionManage trusted extension registry
request-extension-callbackRegistered extension (self-only)Extension self-callback under DAO identity

Trust Model

The contract's security is a delegation chain:

  1. Deployer bootstraps the DAO via construct (one-time, irrevocable)
  2. Bootstrap proposal registers initial extensions (e.g., voting, treasury)
  3. Extensions gate future proposal execution (e.g., require token-weighted votes)
  4. Proposals execute arbitrary logic under DAO authority

The weakest link is always the extension that gates proposal execution. A poorly designed voting extension (e.g., one that allows flash-loan voting) would compromise the entire DAO.

Positive Patterns

Conclusion

The Dungeon Master is a well-structured ExecutorDAO implementation with a minimal codebase and clear access control boundaries. The primary risk is inherent to the pattern: proposals execute with full DAO authority, and security depends entirely on the extensions that gate execution. The use of Clarity 1 means the contract cannot leverage modern asset-scoping features that would significantly reduce the blast radius of a compromised proposal.

For a protocol managing significant DeFi assets, the recommended upgrade path is to redeploy on Clarity 4 with as-contract? allowances and consider adding an emergency guardian mechanism for rapid extension revocation.