community-governance-voting-dao

SP3BXJENEWVNCFYGJF75DFS478H1BZJXNZPT84EAD

Source Code

;; title: governance-dao
;; version: 1.0.0
;; summary: Decentralized platform governance
;; description: Proposal system, voting, parameter changes, treasury management, and community governance

;; constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-unauthorized (err u102))
(define-constant err-voting-closed (err u103))
(define-constant err-already-voted (err u104))
(define-constant err-proposal-active (err u105))
(define-constant err-quorum-not-met (err u106))
(define-constant err-insufficient-voting-power (err u107))

;; Proposal types
(define-constant PROPOSAL-FEE-CHANGE u1)
(define-constant PROPOSAL-PARAMETER-CHANGE u2)
(define-constant PROPOSAL-TREASURY-SPEND u3)
(define-constant PROPOSAL-EMERGENCY-PAUSE u4)

;; Proposal status
(define-constant STATUS-ACTIVE u1)
(define-constant STATUS-PASSED u2)
(define-constant STATUS-REJECTED u3)
(define-constant STATUS-EXECUTED u4)

;; data vars
(define-data-var proposal-nonce uint u0)
(define-data-var quorum-threshold uint u1000) ;; Minimum votes required
(define-data-var voting-duration uint u2880) ;; ~20 days
(define-data-var min-voting-power uint u100)
(define-data-var treasury-balance uint u0)

;; data maps
(define-map proposals
    { proposal-id: uint }
    {
        proposer: principal,
        proposal-type: uint,
        title: (string-utf8 100),
        description: (string-utf8 500),
        status: uint,
        votes-for: uint,
        votes-against: uint,
        start-time: uint,
        end-time: uint,
        execution-delay: uint,
        executed: bool
    }
)

(define-map votes
    { proposal-id: uint, voter: principal }
    {
        voting-power: uint,
        vote-for: bool,
        timestamp: uint
    }
)

(define-map voting-power
    { user: principal }
    {
        power: uint,
        delegated-to: (optional principal),
        last-updated: uint
    }
)

(define-map delegations
    { delegator: principal, delegate: principal }
    {
        power: uint,
        active: bool
    }
)

(define-map treasury-transactions
    { tx-id: uint }
    {
        amount: uint,
        recipient: principal,
        reason: (string-utf8 200),
        proposal-id: uint,
        executed-at: uint
    }
)

(define-map proposal-parameters
    { proposal-id: uint }
    {
        parameter-name: (string-ascii 50),
        new-value: uint
    }
)

;; private functions
(define-private (has-quorum (votes-for uint) (votes-against uint))
    (>= (+ votes-for votes-against) (var-get quorum-threshold))
)

(define-private (is-passed (votes-for uint) (votes-against uint))
    (> votes-for votes-against)
)

;; read only functions
(define-read-only (get-proposal (proposal-id uint))
    (map-get? proposals { proposal-id: proposal-id })
)

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

(define-read-only (get-voting-power (user principal))
    (default-to { power: u0, delegated-to: none, last-updated: u0 }
        (map-get? voting-power { user: user })
    )
)

(define-read-only (get-delegation (delegator principal) (delegate principal))
    (map-get? delegations { delegator: delegator, delegate: delegate })
)

(define-read-only (get-treasury-balance)
    (var-get treasury-balance)
)

(define-read-only (can-vote (proposal-id uint) (voter principal))
    (let
        (
            (proposal (unwrap! (map-get? proposals { proposal-id: proposal-id }) false))
            (user-power (get power (get-voting-power voter)))
            (has-voted (is-some (map-get? votes { proposal-id: proposal-id, voter: voter })))
        )
        (and
            (is-eq (get status proposal) STATUS-ACTIVE)
            (< stacks-block-time (get end-time proposal))
            (>= user-power (var-get min-voting-power))
            (not has-voted)
        )
    )
)

;; public functions
(define-public (create-proposal
    (proposal-type uint)
    (title (string-utf8 100))
    (description (string-utf8 500))
    (execution-delay uint)
)
    (let
        (
            (proposal-id (+ (var-get proposal-nonce) u1))
            (user-power (get power (get-voting-power tx-sender)))
        )
        (asserts! (>= user-power (var-get min-voting-power)) err-insufficient-voting-power)

        (map-set proposals
            { proposal-id: proposal-id }
            {
                proposer: tx-sender,
                proposal-type: proposal-type,
                title: title,
                description: description,
                status: STATUS-ACTIVE,
                votes-for: u0,
                votes-against: u0,
                start-time: stacks-block-time,
                end-time: (+ stacks-block-time (var-get voting-duration)),
                execution-delay: execution-delay,
                executed: false
            }
        )

        (var-set proposal-nonce proposal-id)
        (ok proposal-id)
    )
)

(define-public (vote-on-proposal (proposal-id uint) (vote-for bool))
    (let
        (
            (proposal (unwrap! (map-get? proposals { proposal-id: proposal-id }) err-not-found))
            (user-power (get power (get-voting-power tx-sender)))
        )
        (asserts! (is-eq (get status proposal) STATUS-ACTIVE) err-voting-closed)
        (asserts! (< stacks-block-time (get end-time proposal)) err-voting-closed)
        (asserts! (>= user-power (var-get min-voting-power)) err-insufficient-voting-power)
        (asserts! (is-none (map-get? votes { proposal-id: proposal-id, voter: tx-sender })) err-already-voted)

        (map-set votes
            { proposal-id: proposal-id, voter: tx-sender }
            {
                voting-power: user-power,
                vote-for: vote-for,
                timestamp: stacks-block-time
            }
        )

        (map-set proposals
            { proposal-id: proposal-id }
            (merge proposal {
                votes-for: (if vote-for (+ (get votes-for proposal) user-power) (get votes-for proposal)),
                votes-against: (if vote-for (get votes-against proposal) (+ (get votes-against proposal) user-power))
            })
        )

        (ok true)
    )
)

(define-public (execute-proposal (proposal-id uint))
    (let
        (
            (proposal (unwrap! (map-get? proposals { proposal-id: proposal-id }) err-not-found))
        )
        (asserts! (is-eq (get status proposal) STATUS-ACTIVE) err-voting-closed)
        (asserts! (>= stacks-block-time (get end-time proposal)) err-proposal-active)
        (asserts! (not (get executed proposal)) err-unauthorized)
        (asserts! (has-quorum (get votes-for proposal) (get votes-against proposal)) err-quorum-not-met)

        (let
            (
                (passed (is-passed (get votes-for proposal) (get votes-against proposal)))
            )
            (map-set proposals
                { proposal-id: proposal-id }
                (merge proposal {
                    status: (if passed STATUS-PASSED STATUS-REJECTED),
                    executed: true
                })
            )

            (ok passed)
        )
    )
)

(define-public (delegate-voting-power (delegate principal) (amount uint))
    (let
        (
            (delegator-power (get-voting-power tx-sender))
        )
        (asserts! (>= (get power delegator-power) amount) err-insufficient-voting-power)

        (map-set delegations
            { delegator: tx-sender, delegate: delegate }
            {
                power: amount,
                active: true
            }
        )

        (map-set voting-power
            { user: tx-sender }
            (merge delegator-power {
                delegated-to: (some delegate),
                power: (- (get power delegator-power) amount)
            })
        )

        (let
            (
                (delegate-power (get-voting-power delegate))
            )
            (map-set voting-power
                { user: delegate }
                (merge delegate-power {
                    power: (+ (get power delegate-power) amount),
                    last-updated: stacks-block-time
                })
            )
        )

        (ok true)
    )
)

(define-public (revoke-delegation (delegate principal))
    (let
        (
            (delegation (unwrap! (map-get? delegations { delegator: tx-sender, delegate: delegate }) err-not-found))
            (delegator-power (get-voting-power tx-sender))
            (delegate-power (get-voting-power delegate))
        )
        (asserts! (get active delegation) err-unauthorized)

        (map-set delegations
            { delegator: tx-sender, delegate: delegate }
            (merge delegation { active: false })
        )

        (map-set voting-power
            { user: tx-sender }
            (merge delegator-power {
                power: (+ (get power delegator-power) (get power delegation)),
                delegated-to: none
            })
        )

        (map-set voting-power
            { user: delegate }
            (merge delegate-power {
                power: (- (get power delegate-power) (get power delegation))
            })
        )

        (ok true)
    )
)

(define-public (update-voting-power (user principal) (new-power uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)

        (map-set voting-power
            { user: user }
            {
                power: new-power,
                delegated-to: none,
                last-updated: stacks-block-time
            }
        )

        (ok true)
    )
)

(define-public (treasury-spend
    (proposal-id uint)
    (recipient principal)
    (amount uint)
    (reason (string-utf8 200))
)
    (let
        (
            (proposal (unwrap! (map-get? proposals { proposal-id: proposal-id }) err-not-found))
            (tx-id (+ (var-get proposal-nonce) u1))
        )
        (asserts! (is-eq (get status proposal) STATUS-PASSED) err-unauthorized)
        (asserts! (>= (var-get treasury-balance) amount) err-unauthorized)

        ;; In production, transfer from treasury
        ;; (try! (as-contract (stx-transfer? amount tx-sender recipient)))

        (map-set treasury-transactions
            { tx-id: tx-id }
            {
                amount: amount,
                recipient: recipient,
                reason: reason,
                proposal-id: proposal-id,
                executed-at: stacks-block-time
            }
        )

        (var-set treasury-balance (- (var-get treasury-balance) amount))
        (ok true)
    )
)

;; Admin functions
(define-public (set-quorum-threshold (new-threshold uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set quorum-threshold new-threshold)
        (ok true)
    )
)

(define-public (set-voting-duration (new-duration uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set voting-duration new-duration)
        (ok true)
    )
)

(define-public (deposit-to-treasury (amount uint))
    (begin
        ;; In production, transfer to contract
        ;; (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))

        (var-set treasury-balance (+ (var-get treasury-balance) amount))
        (ok true)
    )
)

Functions (18)

FunctionAccessArgs
has-quorumprivatevotes-for: uint, votes-against: uint
is-passedprivatevotes-for: uint, votes-against: uint
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
get-voting-powerread-onlyuser: principal
get-delegationread-onlydelegator: principal, delegate: principal
get-treasury-balanceread-only
can-voteread-onlyproposal-id: uint, voter: principal
create-proposalpublicproposal-type: uint, title: (string-utf8 100
vote-on-proposalpublicproposal-id: uint, vote-for: bool
execute-proposalpublicproposal-id: uint
delegate-voting-powerpublicdelegate: principal, amount: uint
revoke-delegationpublicdelegate: principal
update-voting-powerpublicuser: principal, new-power: uint
treasury-spendpublicproposal-id: uint, recipient: principal, amount: uint, reason: (string-utf8 200
set-quorum-thresholdpublicnew-threshold: uint
set-voting-durationpublicnew-duration: uint
deposit-to-treasurypublicamount: uint