Charisma Rulebook v0

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

ContractSP2ZNGJ85ENDY6QRHQ5P2D4FXKGZWCKTB2T0Z55KS.charisma-rulebook-v0
ProtocolCharisma โ€” gamified DeFi on Stacks
SourceVerified on-chain via Hiro API (/v2/contracts/source)
Clarity VersionPre-Clarity 4 (no as-contract? asset allowances)
Block Height256,537
Lines of Code275
SHA-256b6f694d1a30e61e3bc7474ddda7cf0a0802a4391082d768cd724251383913a02
Audit Date2026-02-25
Confidence๐ŸŸก MEDIUM โ€” external dependencies (.status-effects-v0, .experience, .energy, governance token) not reviewed; security heavily depends on status effects contract
0
Critical
1
High
2
Medium
2
Low
2
Info

Overview

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:

Documented Limitations

Findings

HIGH

H-01: Status Effects Contract Has Unrestricted Power to Manipulate All Operations

Location: 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.

MEDIUM

M-01: No Multi-Sig โ€” Single Owner Has Full Control Despite Documentation Claims

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:

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.

MEDIUM

M-02: Status Effects Execute Before Authorization โ€” Unverified Callers Trigger External Calls

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.

LOW

L-01: Inconsistent Principal Check in remove-contract-owner

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

LOW

L-02: No Minimum Owner Count โ€” All Owners Can Be Removed

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.

INFO

I-01: No Zero-Amount Validation on Token Operations

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.

INFO

I-02: Pre-Clarity 4 โ€” Consider Migration for Stronger Asset Safety

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.

Architecture Analysis

Strengths

Trust Assumptions

Priority Score

MetricScoreWeightWeighted
Financial Risk3 โ€” transfers/mints/burns governance tokens39
Deployment Likelihood3 โ€” deployed on mainnet26
Code Complexity2 โ€” 275 lines, multi-contract integration24
User Exposure3 โ€” Charisma is a well-known Stacks protocol1.54.5
Novelty2 โ€” GameFi rulebook/orchestration pattern1.53
Total (pre-penalty)2.65 / 3.0
Clarity version penalty (pre-Clarity 4)โˆ’0.5
Final Score2.15 / 3.0 โœ… AUDIT