Trust Score v1 โ€” On-Chain Reputation System

SP2CXN6W7WDEHVWDC07BPP1RPY2Z07BR7Z689WAX3.trust-score-v1 ยท Reputation ยท February 24, 2026

1
Contract
~30
Lines
5
Findings
1
Critical

Overview

A minimal on-chain reputation/trust scoring contract deployed on Stacks mainnet. The contract allows reading, increasing, and decreasing reputation scores for any principal, plus a self-reported action tracker.

Source: Fetched on-chain via Hiro API (immutable, canonical).

Clarity version: Not specified (pre-Clarity 4). -0.5 penalty applied.

Audit confidence: ๐ŸŸข HIGH โ€” contract is trivially small and all findings verified directly against source.

Priority Score

MetricScoreWeightWeighted
Financial risk0 (no tokens/STX)30
Deployment likelihood3 (deployed mainnet)26
Code complexity0 (<50 lines)20
User exposure0 (unknown)1.50
Novelty1 (similar to CurateChain)1.51.5
Total0.75 (below 1.8 threshold)

Note: This contract scores below the normal audit threshold (0.75 < 1.8). Audited by explicit request. No financial risk but critical design flaws make the reputation system meaningless.

Full Source

;; Reputation system contract
(define-map reputation principal uint)
(define-map user-history {user: principal, action: (string-ascii 32)} uint)

(define-read-only (get-reputation (user principal))
  (default-to u0 (map-get? reputation user))
)

(define-public (increase-reputation (user principal) (amount uint))
  (let ((current (get-reputation user)))
    (begin
      (asserts! (> amount u0) (err u1))
      (ok (map-set reputation user (+ current amount)))
    )
  )
)

(define-public (decrease-reputation (user principal) (amount uint))
  (let ((current (get-reputation user)))
    (begin
      (asserts! (>= current amount) (err u2))
      (ok (map-set reputation user (- current amount)))
    )
  )
)

(define-public (track-action (action (string-ascii 32)))
  (let ((count (default-to u0 (map-get? user-history {user: tx-sender, action: action}))))
    (ok (map-set user-history {user: tx-sender, action: action} (+ count u1)))
  )
)

Findings Summary

IDSeverityTitle
C-01CRITICALNo access control โ€” anyone can set any user's reputation
H-01HIGHReputation overflow can brick a user's score
M-01MEDIUMtrack-action disconnected from reputation โ€” no on-chain integration
L-01LOWNo event emission for reputation changes
I-01INFOPre-Clarity 4 โ€” no clarity_version specified

Detailed Findings

CRITICAL C-01: No access control โ€” anyone can set any user's reputation

Location: increase-reputation, decrease-reputation

Description: Both increase-reputation and decrease-reputation are fully permissionless. Any caller can arbitrarily inflate or zero out any user's reputation score. There is no owner check, no role-based access, no oracle requirement โ€” nothing prevents a random wallet from calling (increase-reputation 'SPVICTIM u999999999) or (decrease-reputation 'SPVICTIM (get-reputation 'SPVICTIM)).

;; Anyone can call this with any principal and any amount
(define-public (increase-reputation (user principal) (amount uint))
  (let ((current (get-reputation user)))
    (begin
      (asserts! (> amount u0) (err u1))
      (ok (map-set reputation user (+ current amount)))  ;; no access control
    )
  )
)

Impact: The reputation system is completely meaningless. Any user can give themselves max reputation or destroy anyone else's score. If any system relies on these scores for trust decisions, it can be trivially gamed.

Recommendation: Add access control. Options:

Exploit Test

;; Exploit test for C-01: Anyone can manipulate any reputation
(define-public (test-exploit-c01-reputation-manipulation)
  (begin
    ;; Attacker inflates own reputation
    (try! (increase-reputation tx-sender u999999999))
    ;; Attacker zeros out victim
    (let ((victim-rep (get-reputation 'SP000000000000000000002Q6VF78)))
      (if (> victim-rep u0)
        (try! (decrease-reputation 'SP000000000000000000002Q6VF78 victim-rep))
        true
      )
    )
    (asserts! (>= (get-reputation tx-sender) u999999999) (err u100))
    (ok true)
  )
)

HIGH H-01: Reputation overflow can brick a user's score

Location: increase-reputation

Description: The addition (+ current amount) has no upper bound check. While Clarity's uint arithmetic will abort on overflow (exceeding u340282366920938463463374607431768211455), an attacker could repeatedly call increase-reputation to push a user's score so high that any further increase reverts. More practically, since there's no access control (C-01), an attacker can set any value they want โ€” but if C-01 is fixed with rate-limited peer attestation, the lack of bounds becomes relevant.

Impact: Griefing vector โ€” inflate a victim's score to near-max so legitimate updates revert on overflow.

Recommendation: Add a maximum reputation cap: (asserts! (<= (+ current amount) MAX-REPUTATION) (err u3))

MEDIUM M-01: track-action disconnected from reputation

Location: track-action

Description: The track-action function records self-reported action counts per user but has no connection to the reputation system. Actions don't affect reputation, and reputation doesn't depend on actions. The two systems are completely independent, making the action tracker dead weight in a "trust score" contract.

Impact: Misleading architecture โ€” the contract name suggests actions build trust, but they don't. Action counts are self-reported and can be freely inflated by calling track-action repeatedly.

Recommendation: Either integrate actions into reputation scoring (e.g., verified actions earn reputation via an oracle) or remove the function to reduce contract surface area.

LOW L-01: No event emission for reputation changes

Location: increase-reputation, decrease-reputation

Description: Neither function emits a print event when reputation changes. Off-chain indexers and UIs cannot efficiently track reputation updates without scanning every transaction.

Recommendation: Add (print {event: "reputation-change", user: user, old: current, new: (+ current amount), caller: tx-sender}) to both functions.

INFO I-01: Pre-Clarity 4 โ€” no clarity_version specified

Description: The contract does not specify a Clarity version, defaulting to pre-Clarity 4. While this contract has no token transfers (so as-contract? with asset allowances isn't relevant), specifying the Clarity version is best practice for deployment reproducibility.

Architecture Assessment

This is a minimal reputation primitive with a fundamental design flaw: permissionless write access makes the entire scoring system meaningless. The contract is effectively a public key-value store where anyone can write any value for any key.

No funds are at risk (no token transfers of any kind), but any system that relies on these scores for trust or access decisions would be trivially exploitable.

Positive notes: