Central security and orchestration hub for the Charisma protocol โ manages all token operations (XP, energy, governance) through verified interactions, multi-owner authorization, and dynamic status effects
| Contract | SP2ZNGJ85ENDY6QRHQ5P2D4FXKGZWCKTB2T0Z55KS.charisma-rulebook-v0 |
| Protocol | Charisma โ gamified DeFi on Stacks |
| Source | Verified on-chain via Hiro API (/v2/contracts/source) |
| Clarity Version | Pre-Clarity 4 (no as-contract? asset allowances) |
| Block Height | 256,537 |
| Lines of Code | 275 |
| SHA-256 | b6f694d1a30e61e3bc7474ddda7cf0a0802a4391082d768cd724251383913a02 |
| Audit Date | 2026-02-25 |
| Confidence | ๐ก MEDIUM โ external dependencies (.status-effects-v0, .experience, .energy, governance token) not reviewed; security heavily depends on status effects contract |
The Charisma Rulebook v0 is the central gateway for all token operations in the Charisma protocol. It acts as a permissioned proxy: only whitelisted "verified interaction" contracts can call token operations (reward, punish, energize, exhaust, transfer, mint, burn, lock, unlock). Each operation is subject to configurable maximum limits and passes through a "status effects" modifier that can dynamically adjust amounts and targets before execution.
The contract manages three token types:
reward, burned via punishenergize, burned via exhaustpay-to-play function burns energy and mints XP, requiring the rulebook itself to be a verified interactionLocation: modify-* private functions (lines 235โ252)
Description: Every token operation passes through the .status-effects-v0 contract before authorization checks. The status effects contract can modify both the amount AND the target (recipient) of any operation. This means a compromised or malicious status effects contract could:
The entire protocol's token security hinges on the correctness and integrity of this single external contract.
;; Status effects can return ANY amount/target within limits
(define-private (modify-reward (ctx {amount: uint, target: principal, caller: principal}))
(contract-call? .status-effects-v0 modify-reward ctx))
;; In reward(), the modified values are used directly:
(define-public (reward (amount uint) (target principal))
(let ((modified (modify-reward {amount: amount, target: target, caller: contract-caller})))
(asserts! (is-verified contract-caller) ERR_UNVERIFIED)
(asserts! (<= (get amount modified) (var-get max-reward)) ERR_EXCEEDS_LIMIT)
;; modified target could be anyone
(contract-call? .experience mint (get amount modified) (get target modified))))
Impact: If .status-effects-v0 is compromised, an attacker could drain or manipulate all three token types up to the per-operation limits. Since the status effects contract is hardcoded (not upgradeable within this contract), the risk is bounded but the trust surface is large.
Recommendation: Consider adding bounds-checking on the modified target (e.g., assert target hasn't changed, or only allow target changes for specific operations). Document the trust assumption on .status-effects-v0 explicitly. In a Clarity 4 migration, use as-contract? with explicit asset allowances to limit the blast radius.
Location: Admin functions (lines 99โ164)
Description: The contract documentation states "Owner consensus for critical changes" and "Protection against single point of failure." In reality, any single contract owner can unilaterally:
max-mint from 0)There is no threshold, timelock, or multi-sig mechanism.
;; Any single owner can add new owners
(define-public (add-contract-owner (new-owner principal))
(begin
(asserts! (is-contract-owner) ERR_UNAUTHORIZED)
(ok (map-set contract-owners new-owner true))))
;; Any single owner can raise mint cap from 0 to unlimited
(define-public (set-max-mint (new-max uint))
(begin
(asserts! (is-contract-owner) ERR_UNAUTHORIZED)
(ok (var-set max-mint new-max))))
Impact: A single compromised owner key can take full control: add a malicious verified interaction, raise all limits to max, and drain tokens. The documented "multi-owner" system provides no additional security over a single admin.
Recommendation: Implement a multi-sig pattern requiring N-of-M owner approvals for critical operations (adding owners, changing limits, adding verified interactions). At minimum, add a timelock for limit changes.
Location: All token operation functions (lines 172โ232)
Description: In every token operation, the modify-* call to .status-effects-v0 executes in the let binding before the is-verified assertion. This means any caller โ verified or not โ triggers the external status effects contract call. If the status effects contract has side effects (state changes, events), unverified callers can trigger them.
(define-public (reward (amount uint) (target principal))
;; modify-reward executes FIRST (before auth check)
(let ((modified (modify-reward {amount: amount, target: target, caller: contract-caller})))
;; Auth check happens AFTER the external call
(asserts! (is-verified contract-caller) ERR_UNVERIFIED)
...))
Impact: Unverified contracts can trigger status effect logic. While the overall token operation will abort (Clarity rolls back on error), if the status effects contract performs any read-only tracking or emits events that survive abort, this could be exploited. In practice, since Clarity aborts all state changes on asserts! failure, the direct risk is limited to wasted compute.
Recommendation: Move the is-verified check before the let binding to avoid unnecessary external calls and follow the checks-effects-interactions pattern.
Location: remove-contract-owner (line 104)
Description: The function uses contract-caller for the ownership check but tx-sender for the self-removal guard:
(define-public (remove-contract-owner (owner principal))
(begin
(asserts! (and (is-contract-owner) ;; checks contract-caller
(not (is-eq tx-sender owner))) ;; checks tx-sender
ERR_UNAUTHORIZED)
(ok (map-delete contract-owners owner))))
If an owner is a contract (likely in this architecture), tx-sender will be the original user, not the contract-owner. The self-removal protection would never trigger for contract owners calling directly. Conversely, a user whose address matches the owner parameter would be blocked even if they're calling through a different authorized contract.
Impact: The self-removal guard may not work as intended for contract-based owners. Low severity since the multi-owner map has no minimum count anyway.
Recommendation: Use contract-caller consistently: (not (is-eq contract-caller owner)).
Location: remove-contract-owner (line 104)
Description: There is no check to ensure at least one owner remains. Owner A can remove owner B, then there are zero remaining protections against accidentally (or intentionally) leaving the contract ownerless โ at which point no admin functions can ever be called again.
The self-removal guard prevents removing yourself, but owner A can remove owner B and vice versa, potentially leaving zero owners through coordination or a race condition.
Impact: If all owners are removed, the contract becomes permanently ungovernable. Limits and verified interactions are frozen forever.
Recommendation: Track owner count and assert (> owner-count u1) before deletion.
Location: All token operation functions
Description: Operations don't validate that the amount is greater than zero. Status effects could set an amount to 0, resulting in no-op token calls that still pass all checks. While not directly exploitable, zero-amount operations waste gas and could be used to trigger side effects in downstream contracts without meaningful token movement.
Recommendation: Add (asserts! (> amount u0) ERR_INVALID_AMOUNT) either before or after status effect modification.
Location: Entire contract
Description: This contract was deployed pre-Clarity 4. Clarity 4 introduced as-contract? with explicit asset allowances (with-ft, with-nft, with-stx), which enforce at the language level which assets a contract can move. While this rulebook contract doesn't use as-contract directly (it delegates to token contracts), a v1 migration could leverage restrict-assets? to add language-level bounds on what operations are possible, reducing the trust surface on status effects and verified interactions.
Recommendation: When deploying a v1, use Clarity 4's asset restriction primitives to limit the contract's token movement capabilities at the language level.
contract-caller for auth checks (except L-01) โ resistant to relay/proxy attacks.status-effects-v0 must be non-malicious โ it can manipulate amounts and targets for all operations.experience and .energy token contracts must correctly implement mint/burnSP2D5BGGJ956A635JG7CJQ59FTRFRB0893514EZPJ.dme000-governance-token must correctly implement dmg-* functions.initialize-energy (called in pay-to-play) must be safe to call repeatedly| Metric | Score | Weight | Weighted |
|---|---|---|---|
| Financial Risk | 3 โ transfers/mints/burns governance tokens | 3 | 9 |
| Deployment Likelihood | 3 โ deployed on mainnet | 2 | 6 |
| Code Complexity | 2 โ 275 lines, multi-contract integration | 2 | 4 |
| User Exposure | 3 โ Charisma is a well-known Stacks protocol | 1.5 | 4.5 |
| Novelty | 2 โ GameFi rulebook/orchestration pattern | 1.5 | 3 |
| Total (pre-penalty) | 2.65 / 3.0 | ||
| Clarity version penalty (pre-Clarity 4) | โ0.5 | ||
| Final Score | 2.15 / 3.0 โ AUDIT | ||