Source Code


;; dao-governance.clar
;; A simple DAO governance contract for submitting and voting on proposals.
;; Clarity 4

(define-constant err-not-member (err u400))
(define-constant err-proposal-not-found (err u401))
(define-constant err-proposal-expired (err u402))
(define-constant err-already-voted (err u403))
(define-constant err-insufficient-votes (err u404))
(define-constant voting-period u144) ;; ~1 day in blocks

(define-map members principal bool)
(define-map proposals 
    uint 
    {
        proposer: principal,
        title: (string-utf8 50),
        description: (string-utf8 256),
        start-block: uint,
        end-block: uint,
        yes-votes: uint,
        no-votes: uint,
        executed: bool
    }
)

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

(define-data-var proposal-count uint u0)

(define-public (add-member (new-member principal))
    (begin
        ;; In a real DAO, this would be restricted to execute by the DAO contract itself
        ;; For this example, we assume deployer can add initial members
        (map-set members new-member true)
        (ok true)
    )
)

(define-public (submit-proposal (title (string-utf8 50)) (description (string-utf8 256)))
    (let
        (
            (new-id (+ (var-get proposal-count) u1))
        )
        (asserts! (default-to false (map-get? members tx-sender)) err-not-member)
        
        (map-set proposals new-id {
            proposer: tx-sender,
            title: title,
            description: description,
            start-block: burn-block-height,
            end-block: (+ burn-block-height voting-period),
            yes-votes: u0,
            no-votes: u0,
            executed: false
        })
        (var-set proposal-count new-id)
        (ok new-id)
    )
)

(define-public (vote (proposal-id uint) (vote-for bool))
    (let
        (
            (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
        )
        (asserts! (default-to false (map-get? members tx-sender)) err-not-member)
        (asserts! (< burn-block-height (get end-block proposal)) err-proposal-expired)
        (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 } true)
        (if vote-for
            (map-set proposals proposal-id (merge proposal { yes-votes: (+ (get yes-votes proposal) u1) }))
            (map-set proposals proposal-id (merge proposal { no-votes: (+ (get no-votes proposal) u1) }))
        )
        (ok true)
    )
)

(define-public (execute-proposal (proposal-id uint))
    (let
        (
            (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
        )
        (asserts! (>= burn-block-height (get end-block proposal)) err-proposal-expired) ;; Wait until voting ends
        (asserts! (> (get yes-votes proposal) (get no-votes proposal)) err-insufficient-votes)
        (asserts! (not (get executed proposal)) (err u405))
        
        ;; Execution logic would go here (e.g. contract-calls)
        
        (map-set proposals proposal-id (merge proposal { executed: true }))
        (ok true)
    )
)

Functions (4)

FunctionAccessArgs
add-memberpublicnew-member: principal
submit-proposalpublictitle: (string-utf8 50
votepublicproposal-id: uint, vote-for: bool
execute-proposalpublicproposal-id: uint