Charisma protocol's core DAO governance controller — ExecutorDAO pattern with extension-based access control and proposal execution
| Contract | SP2D5BGGJ956A635JG7CJQ59FTRFRB0893514EZPJ.dungeon-master |
| Protocol | Charisma — gamified DeFi on Stacks |
| Source | Verified on-chain via Hiro API (/v2/contracts/source) |
| Clarity Version | Clarity 1 (pre-Clarity 4 — no as-contract? asset allowances) |
| Publish Height | 98,098 |
| Lines of Code | ~75 |
| Audit Date | 2026-02-25 |
| Confidence | 🟢 HIGH — compact contract, fully reviewed; security heavily depends on extension/proposal governance (out of scope) |
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:
construct — one-time bootstrap by the deployer, initializes the DAO with a bootstrap proposalexecute — runs a proposal contract under as-contract authority (extensions/self only)set-extension / set-extensions — enables or disables trusted extension contractsrequest-extension-callback — allows extensions to invoke callbacks on themselves through the DAOThe 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.
sender parameter in execute is passed through to proposals, allowing extensions to attribute actions to specific usersas-contract grants unlimited asset authority to proposalsLocation: 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).
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.
is-self-or-extension uses tx-sender for self-check instead of contract-callerLocation: 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).
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.
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:
as-contract? with explicit asset allowancesrestrict-assets? expressionscontract-hash? for verifying deployed contract codestacks-block-time for time-based logicRecommendation: 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.
sender parameter in execute is caller-supplied, not verifiedDescription: 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.
The Dungeon Master implements a clean ExecutorDAO pattern with a minimal attack surface:
| Function | Access Control | Purpose |
|---|---|---|
construct | Deployer (one-time) | Bootstrap DAO with initial proposal |
execute | Self or extension | Run proposal under DAO authority |
set-extension(s) | Self or extension | Manage trusted extension registry |
request-extension-callback | Registered extension (self-only) | Extension self-callback under DAO identity |
The contract's security is a delegation chain:
construct (one-time, irrevocable)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.
map-insert (each proposal can only execute once)contract-caller == contract-of extension (prevents impersonation)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.