Source Code

;; Voting Analytics - Participation & Reputation Tracking (Clarity 4)
;; This contract tracks voter participation, reputation, and generates analytics

;; Constants
(define-constant CONTRACT-OWNER tx-sender)

;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u701))
(define-constant ERR-VOTER-NOT-FOUND (err u702))
(define-constant ERR-INVALID-REPUTATION (err u703))

;; Data maps

;; Voter participation stats with Clarity 4 timestamps
(define-map voter-stats
    { voter: principal }
    {
        total-votes-cast: uint,
        proposals-created: uint,
        first-vote: uint,                    ;; Clarity 4: Unix timestamp
        last-vote: uint,                     ;; Clarity 4: Unix timestamp
        reputation-score: uint,
        active-streak: uint
    }
)

;; Proposal participation with Clarity 4 timestamps
(define-map proposal-participation
    { proposal-id: uint }
    {
        total-voters: uint,
        participation-rate: uint,             ;; In basis points (10000 = 100%)
        created-at: uint,                     ;; Clarity 4: Unix timestamp
        voting-ended-at: (optional uint),     ;; Clarity 4: Unix timestamp
        avg-time-to-vote: uint                ;; In seconds
    }
)

;; Voter reputation history with Clarity 4 timestamps
(define-map reputation-history
    { voter: principal, index: uint }
    {
        previous-score: uint,
        new-score: uint,
        reason: (string-ascii 50),
        timestamp: uint                       ;; Clarity 4: Unix timestamp
    }
)

(define-map reputation-count { voter: principal } uint)

;; Global analytics
(define-data-var total-votes-cast uint u0)
(define-data-var total-unique-voters uint u0)
(define-data-var total-proposals uint u0)

;; Leaderboard tracking
(define-map leaderboard-entry
    { rank: uint }
    {
        voter: principal,
        score: uint,
        last-updated: uint                    ;; Clarity 4: Unix timestamp
    }
)

(define-data-var leaderboard-size uint u0)

;; Read-only functions

;; Get voter stats
(define-read-only (get-voter-stats (voter principal))
    (ok (map-get? voter-stats { voter: voter }))
)

;; Get proposal participation
(define-read-only (get-proposal-participation (proposal-id uint))
    (ok (map-get? proposal-participation { proposal-id: proposal-id }))
)

;; Get voter reputation history
(define-read-only (get-reputation-history (voter principal) (index uint))
    (ok (map-get? reputation-history { voter: voter, index: index }))
)

;; Get global stats
(define-read-only (get-global-stats)
    (ok {
        total-votes: (var-get total-votes-cast),
        unique-voters: (var-get total-unique-voters),
        total-proposals: (var-get total-proposals)
    })
)

;; Get leaderboard entry
(define-read-only (get-leaderboard-entry (rank uint))
    (ok (map-get? leaderboard-entry { rank: rank }))
)

;; Get leaderboard size
(define-read-only (get-leaderboard-size)
    (ok (var-get leaderboard-size))
)

;; Calculate participation rate
(define-read-only (calculate-participation-rate (voters uint) (total uint))
    (if (is-eq total u0)
        (ok u0)
        (ok (/ (* voters u10000) total))
    )
)

;; Public functions

;; Record vote (should be called by voting contract)
(define-public (record-vote (voter principal) (proposal-id uint))
    (begin
        (match (map-get? voter-stats { voter: voter })
            existing-stats
                ;; Update existing voter stats
                (map-set voter-stats
                    { voter: voter }
                    {
                        total-votes-cast: (+ (get total-votes-cast existing-stats) u1),
                        proposals-created: (get proposals-created existing-stats),
                        first-vote: (get first-vote existing-stats),
                        last-vote: stacks-block-time,        ;; Clarity 4: Unix timestamp
                        reputation-score: (+ (get reputation-score existing-stats) u1),
                        active-streak: (+ (get active-streak existing-stats) u1)
                    }
                )
            ;; Create new voter stats
            (begin
                (map-set voter-stats
                    { voter: voter }
                    {
                        total-votes-cast: u1,
                        proposals-created: u0,
                        first-vote: stacks-block-time,       ;; Clarity 4: Unix timestamp
                        last-vote: stacks-block-time,        ;; Clarity 4: Unix timestamp
                        reputation-score: u1,
                        active-streak: u1
                    }
                )
                (var-set total-unique-voters (+ (var-get total-unique-voters) u1))
            )
        )

        ;; Increment global vote count
        (var-set total-votes-cast (+ (var-get total-votes-cast) u1))

        ;; Update proposal participation
        (match (map-get? proposal-participation { proposal-id: proposal-id })
            participation
                (map-set proposal-participation
                    { proposal-id: proposal-id }
                    {
                        total-voters: (+ (get total-voters participation) u1),
                        participation-rate: (get participation-rate participation),
                        created-at: (get created-at participation),
                        voting-ended-at: (get voting-ended-at participation),
                        avg-time-to-vote: (get avg-time-to-vote participation)
                    }
                )
            ;; Initialize proposal participation
            (map-set proposal-participation
                { proposal-id: proposal-id }
                {
                    total-voters: u1,
                    participation-rate: u0,
                    created-at: stacks-block-time,           ;; Clarity 4: Unix timestamp
                    voting-ended-at: none,
                    avg-time-to-vote: u0
                }
            )
        )

        (print {
            event: "vote-recorded",
            voter: voter,
            proposal-id: proposal-id,
            timestamp: stacks-block-time
        })
        (ok true)
    )
)

;; Record proposal creation
(define-public (record-proposal-creation (proposer principal) (proposal-id uint))
    (begin
        (match (map-get? voter-stats { voter: proposer })
            existing-stats
                (map-set voter-stats
                    { voter: proposer }
                    {
                        total-votes-cast: (get total-votes-cast existing-stats),
                        proposals-created: (+ (get proposals-created existing-stats) u1),
                        first-vote: (get first-vote existing-stats),
                        last-vote: (get last-vote existing-stats),
                        reputation-score: (+ (get reputation-score existing-stats) u5),
                        active-streak: (get active-streak existing-stats)
                    }
                )
            ;; Create new voter stats for proposer
            (begin
                (map-set voter-stats
                    { voter: proposer }
                    {
                        total-votes-cast: u0,
                        proposals-created: u1,
                        first-vote: stacks-block-time,
                        last-vote: stacks-block-time,
                        reputation-score: u5,
                        active-streak: u0
                    }
                )
                (var-set total-unique-voters (+ (var-get total-unique-voters) u1))
            )
        )

        ;; Increment global proposal count
        (var-set total-proposals (+ (var-get total-proposals) u1))

        ;; Record reputation change
        (let
            (
                (history-index (default-to u0 (map-get? reputation-count { voter: proposer })))
                (current-stats (unwrap-panic (map-get? voter-stats { voter: proposer })))
                (current-score (get reputation-score current-stats))
            )
            (map-set reputation-history
                { voter: proposer, index: history-index }
                {
                    previous-score: (if (> current-score u5) (- current-score u5) u0),
                    new-score: current-score,
                    reason: "proposal-created",
                    timestamp: stacks-block-time             ;; Clarity 4: Unix timestamp
                }
            )
            (map-set reputation-count { voter: proposer } (+ history-index u1))
        )

        (print {
            event: "proposal-created-recorded",
            proposer: proposer,
            proposal-id: proposal-id,
            timestamp: stacks-block-time
        })
        (ok true)
    )
)

;; Update voter reputation (admin function)
(define-public (update-reputation (voter principal) (score-change int) (reason (string-ascii 50)))
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)

        (let
            (
                (stats (unwrap! (map-get? voter-stats { voter: voter }) ERR-VOTER-NOT-FOUND))
                (current-score (get reputation-score stats))
                (new-score (if (< score-change 0)
                    (if (> (to-uint (- score-change)) current-score)
                        u0
                        (- current-score (to-uint (- score-change)))
                    )
                    (+ current-score (to-uint score-change))
                ))
                (history-index (default-to u0 (map-get? reputation-count { voter: voter })))
            )
            (map-set voter-stats
                { voter: voter }
                (merge stats { reputation-score: new-score })
            )

            (map-set reputation-history
                { voter: voter, index: history-index }
                {
                    previous-score: current-score,
                    new-score: new-score,
                    reason: reason,
                    timestamp: stacks-block-time             ;; Clarity 4: Unix timestamp
                }
            )

            (map-set reputation-count { voter: voter } (+ history-index u1))

            (print {
                event: "reputation-updated",
                voter: voter,
                previous-score: current-score,
                new-score: new-score,
                reason: reason,
                timestamp: stacks-block-time
            })
            (ok true)
        )
    )
)

;; End proposal voting and calculate final participation
(define-public (finalize-proposal-participation (proposal-id uint) (total-eligible-voters uint))
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)

        (let
            (
                (participation (unwrap! (map-get? proposal-participation { proposal-id: proposal-id }) ERR-VOTER-NOT-FOUND))
                (participation-rate (unwrap! (calculate-participation-rate (get total-voters participation) total-eligible-voters) ERR-INVALID-REPUTATION))
            )
            (map-set proposal-participation
                { proposal-id: proposal-id }
                (merge participation {
                    participation-rate: participation-rate,
                    voting-ended-at: (some stacks-block-time)
                })
            )

            (print {
                event: "participation-finalized",
                proposal-id: proposal-id,
                total-voters: (get total-voters participation),
                participation-rate: participation-rate,
                timestamp: stacks-block-time
            })
            (ok true)
        )
    )
)

Functions (11)

FunctionAccessArgs
get-voter-statsread-onlyvoter: principal
get-proposal-participationread-onlyproposal-id: uint
get-reputation-historyread-onlyvoter: principal, index: uint
get-global-statsread-only
get-leaderboard-entryread-onlyrank: uint
get-leaderboard-sizeread-only
calculate-participation-rateread-onlyvoters: uint, total: uint
record-votepublicvoter: principal, proposal-id: uint
record-proposal-creationpublicproposer: principal, proposal-id: uint
update-reputationpublicvoter: principal, score-change: int, reason: (string-ascii 50
finalize-proposal-participationpublicproposal-id: uint, total-eligible-voters: uint