Source Code

;; Governance Contract (Clarity 4)
;; Protocol governance with proposal creation, voting, and execution
;; Uses stacks-block-time for voting periods and timelock

;; Error Codes
(define-constant ERR_UNAUTHORIZED (err u6001))
(define-constant ERR_NOT_FOUND (err u6002))
(define-constant ERR_INVALID_PARAMS (err u6003))
(define-constant ERR_ALREADY_VOTED (err u6004))
(define-constant ERR_VOTING_CLOSED (err u6005))
(define-constant ERR_PROPOSAL_ACTIVE (err u6006))
(define-constant ERR_QUORUM_NOT_MET (err u6007))
(define-constant ERR_INSUFFICIENT_REPUTATION (err u6008))
(define-constant ERR_TIMELOCK_ACTIVE (err u6009))

;; Configuration
(define-constant CONTRACT_OWNER tx-sender)
(define-constant MIN_PROPOSAL_REPUTATION u100)
(define-constant VOTING_PERIOD u604800) ;; 7 days
(define-constant TIMELOCK_PERIOD u172800) ;; 2 days
(define-constant QUORUM_PERCENTAGE u10) ;; 10%
(define-constant EXECUTION_GRACE_PERIOD u86400) ;; 1 day

;; Proposal States
(define-constant STATE_ACTIVE "active")
(define-constant STATE_PASSED "passed")
(define-constant STATE_FAILED "failed")
(define-constant STATE_EXECUTED "executed")
(define-constant STATE_CANCELLED "cancelled")

;; Data Maps
(define-map proposals
    uint
    {
        proposer: principal,
        title: (string-ascii 100),
        description: (string-ascii 500),
        created-at: uint,
        voting-ends-at: uint,
        execution-available-at: uint,
        yes-votes: uint,
        no-votes: uint,
        total-voters: uint,
        state: (string-ascii 20),
        executed-at: (optional uint),
        proposal-type: (string-ascii 50)
    })

(define-map proposal-votes
    {proposal-id: uint, voter: principal}
    {
        vote: bool,
        weight: uint,
        voted-at: uint
    })

(define-map user-voting-power principal uint)

;; Data vars
(define-data-var next-proposal-id uint u1)
(define-data-var total-proposals uint u0)
(define-data-var total-passed-proposals uint u0)
(define-data-var total-active-voters uint u0)
(define-data-var governance-enabled bool true)

;; Events using Clarity 4 stacks-block-time
(define-private (emit-proposal-created (proposal-id uint) (proposer principal) (title (string-ascii 100)))
    (print {
        event: "proposal-created",
        proposal-id: proposal-id,
        proposer: proposer,
        title: title,
        timestamp: stacks-block-time
    }))

(define-private (emit-vote-cast (proposal-id uint) (voter principal) (vote bool) (weight uint))
    (print {
        event: "vote-cast",
        proposal-id: proposal-id,
        voter: voter,
        vote: vote,
        weight: weight,
        timestamp: stacks-block-time
    }))

(define-private (emit-proposal-executed (proposal-id uint))
    (print {
        event: "proposal-executed",
        proposal-id: proposal-id,
        timestamp: stacks-block-time
    }))

;; Helper Functions
(define-private (calculate-quorum (total-voters uint))
    (/ (* total-voters QUORUM_PERCENTAGE) u100))

(define-private (has-quorum (total-voters uint) (yes-votes uint) (no-votes uint))
    (let ((votes-cast (+ yes-votes no-votes))
          (required-quorum (calculate-quorum total-voters)))
        (>= votes-cast required-quorum)))

;; Public Functions
(define-public (create-proposal
    (title (string-ascii 100))
    (description (string-ascii 500))
    (proposal-type (string-ascii 50)))
    (let (
        (proposal-id (var-get next-proposal-id))
        (voting-ends-at (+ stacks-block-time VOTING_PERIOD))
        (execution-available-at (+ voting-ends-at TIMELOCK_PERIOD))
        (proposer-power (default-to u0 (map-get? user-voting-power tx-sender)))
    )
        (asserts! (var-get governance-enabled) ERR_UNAUTHORIZED)
        (asserts! (>= proposer-power MIN_PROPOSAL_REPUTATION) ERR_INSUFFICIENT_REPUTATION)
        (asserts! (> (len title) u0) ERR_INVALID_PARAMS)
        (asserts! (> (len description) u0) ERR_INVALID_PARAMS)

        (map-set proposals proposal-id {
            proposer: tx-sender,
            title: title,
            description: description,
            created-at: stacks-block-time,
            voting-ends-at: voting-ends-at,
            execution-available-at: execution-available-at,
            yes-votes: u0,
            no-votes: u0,
            total-voters: u0,
            state: STATE_ACTIVE,
            executed-at: none,
            proposal-type: proposal-type
        })

        (var-set next-proposal-id (+ proposal-id u1))
        (var-set total-proposals (+ (var-get total-proposals) u1))

        (emit-proposal-created proposal-id tx-sender title)
        (ok proposal-id)))

(define-public (cast-vote (proposal-id uint) (vote bool))
    (let (
        (proposal (unwrap! (map-get? proposals proposal-id) ERR_NOT_FOUND))
        (voter-power (default-to u1 (map-get? user-voting-power tx-sender)))
    )
        (asserts! (var-get governance-enabled) ERR_UNAUTHORIZED)
        (asserts! (is-eq (get state proposal) STATE_ACTIVE) ERR_VOTING_CLOSED)
        (asserts! (< stacks-block-time (get voting-ends-at proposal)) ERR_VOTING_CLOSED)
        (asserts! (is-none (map-get? proposal-votes {proposal-id: proposal-id, voter: tx-sender})) ERR_ALREADY_VOTED)

        (map-set proposal-votes {proposal-id: proposal-id, voter: tx-sender} {
            vote: vote,
            weight: voter-power,
            voted-at: stacks-block-time
        })

        (let (
            (new-yes-votes (if vote (+ (get yes-votes proposal) voter-power) (get yes-votes proposal)))
            (new-no-votes (if vote (get no-votes proposal) (+ (get no-votes proposal) voter-power)))
            (new-total-voters (+ (get total-voters proposal) u1))
        )
            (map-set proposals proposal-id (merge proposal {
                yes-votes: new-yes-votes,
                no-votes: new-no-votes,
                total-voters: new-total-voters
            }))

            (var-set total-active-voters (+ (var-get total-active-voters) u1))
            (emit-vote-cast proposal-id tx-sender vote voter-power)
            (ok true))))

(define-public (finalize-proposal (proposal-id uint))
    (let ((proposal (unwrap! (map-get? proposals proposal-id) ERR_NOT_FOUND)))
        (asserts! (is-eq (get state proposal) STATE_ACTIVE) ERR_PROPOSAL_ACTIVE)
        (asserts! (>= stacks-block-time (get voting-ends-at proposal)) ERR_VOTING_CLOSED)

        (let (
            (yes-votes (get yes-votes proposal))
            (no-votes (get no-votes proposal))
            (total-voters (get total-voters proposal))
            (quorum-met (has-quorum total-voters yes-votes no-votes))
            (new-state (if (and quorum-met (> yes-votes no-votes)) STATE_PASSED STATE_FAILED))
        )
            (map-set proposals proposal-id (merge proposal {state: new-state}))

            (if (is-eq new-state STATE_PASSED)
                (var-set total-passed-proposals (+ (var-get total-passed-proposals) u1))
                true)

            (print {
                event: "proposal-finalized",
                proposal-id: proposal-id,
                state: new-state,
                yes-votes: yes-votes,
                no-votes: no-votes,
                quorum-met: quorum-met,
                timestamp: stacks-block-time
            })
            (ok new-state))))

(define-public (execute-proposal (proposal-id uint))
    (let ((proposal (unwrap! (map-get? proposals proposal-id) ERR_NOT_FOUND)))
        (asserts! (is-eq (get state proposal) STATE_PASSED) ERR_INVALID_PARAMS)
        (asserts! (>= stacks-block-time (get execution-available-at proposal)) ERR_TIMELOCK_ACTIVE)

        (map-set proposals proposal-id (merge proposal {
            state: STATE_EXECUTED,
            executed-at: (some stacks-block-time)
        }))

        (emit-proposal-executed proposal-id)
        (ok true)))

(define-public (cancel-proposal (proposal-id uint))
    (let ((proposal (unwrap! (map-get? proposals proposal-id) ERR_NOT_FOUND)))
        (asserts! (is-eq tx-sender (get proposer proposal)) ERR_UNAUTHORIZED)
        (asserts! (is-eq (get state proposal) STATE_ACTIVE) ERR_INVALID_PARAMS)

        (map-set proposals proposal-id (merge proposal {state: STATE_CANCELLED}))

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

(define-public (set-voting-power (user principal) (power uint))
    (begin
        (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
        (map-set user-voting-power user power)
        (print {event: "voting-power-updated", user: user, power: power, timestamp: stacks-block-time})
        (ok true)))

(define-public (toggle-governance)
    (begin
        (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
        (var-set governance-enabled (not (var-get governance-enabled)))
        (print {event: "governance-toggled", enabled: (var-get governance-enabled)})
        (ok true)))

;; Read-Only Functions
(define-read-only (get-proposal (proposal-id uint))
    (map-get? proposals proposal-id))

(define-read-only (get-vote (proposal-id uint) (voter principal))
    (map-get? proposal-votes {proposal-id: proposal-id, voter: voter}))

(define-read-only (get-voting-power (user principal))
    (ok (default-to u0 (map-get? user-voting-power user))))

(define-read-only (can-execute-proposal (proposal-id uint))
    (match (map-get? proposals proposal-id)
        proposal (ok (and
            (is-eq (get state proposal) STATE_PASSED)
            (>= stacks-block-time (get execution-available-at proposal))))
        ERR_NOT_FOUND))

(define-read-only (get-governance-stats)
    (ok {
        total-proposals: (var-get total-proposals),
        total-passed-proposals: (var-get total-passed-proposals),
        total-active-voters: (var-get total-active-voters),
        next-proposal-id: (var-get next-proposal-id),
        governance-enabled: (var-get governance-enabled),
        voting-period: VOTING_PERIOD,
        timelock-period: TIMELOCK_PERIOD,
        quorum-percentage: QUORUM_PERCENTAGE
    }))

Functions (17)

FunctionAccessArgs
emit-proposal-createdprivateproposal-id: uint, proposer: principal, title: (string-ascii 100
emit-vote-castprivateproposal-id: uint, voter: principal, vote: bool, weight: uint
emit-proposal-executedprivateproposal-id: uint
calculate-quorumprivatetotal-voters: uint
has-quorumprivatetotal-voters: uint, yes-votes: uint, no-votes: uint
create-proposalpublictitle: (string-ascii 100
cast-votepublicproposal-id: uint, vote: bool
finalize-proposalpublicproposal-id: uint
execute-proposalpublicproposal-id: uint
cancel-proposalpublicproposal-id: uint
set-voting-powerpublicuser: principal, power: uint
toggle-governancepublic
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
get-voting-powerread-onlyuser: principal
can-execute-proposalread-onlyproposal-id: uint
get-governance-statsread-only