Source Code

;; DAO Proposal System
;; Manages proposal creation, voting, and execution

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u200))
(define-constant err-proposal-not-found (err u201))
(define-constant err-already-voted (err u202))
(define-constant err-voting-closed (err u203))
(define-constant err-proposal-not-passed (err u204))
(define-constant err-proposal-already-executed (err u205))
(define-constant err-insufficient-voting-power (err u206))
(define-constant err-voting-still-active (err u207))
(define-constant err-quorum-not-met (err u208))

;; Governance parameters
(define-constant quorum-percentage u15) ;; 15%
(define-constant passing-threshold u51) ;; 51%
(define-constant voting-period u1008) ;; ~7 days in blocks (assuming 10 min blocks)

;; Data variables
(define-data-var proposal-count uint u0)
(define-data-var governance-token-contract principal .governance-token)

;; Proposal status
(define-constant status-active u1)
(define-constant status-passed u2)
(define-constant status-failed u3)
(define-constant status-executed u4)
(define-constant status-cancelled u5)

;; Data maps
(define-map proposals
    uint
    {
        proposer: principal,
        title: (string-ascii 100),
        description: (string-utf8 500),
        votes-for: uint,
        votes-against: uint,
        start-block: uint,
        end-block: uint,
        status: uint,
        execution-delay: uint,
        target-contract: (optional principal),
        target-function: (optional (string-ascii 50))
    }
)

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

;; Create a new proposal
(define-public (create-proposal 
    (title (string-ascii 100))
    (description (string-utf8 500))
    (execution-delay uint)
    (target-contract (optional principal))
    (target-function (optional (string-ascii 50)))
)
    (let (
        (proposal-id (+ (var-get proposal-count) u1))
        (start-block stacks-block-height)
        (end-block (+ stacks-block-height voting-period))
    )
        (map-set proposals proposal-id {
            proposer: tx-sender,
            title: title,
            description: description,
            votes-for: u0,
            votes-against: u0,
            start-block: start-block,
            end-block: end-block,
            status: status-active,
            execution-delay: execution-delay,
            target-contract: target-contract,
            target-function: target-function
        })
        (var-set proposal-count proposal-id)
        (print { event: "proposal-created", proposal-id: proposal-id, proposer: tx-sender })
        (ok proposal-id)
    )
)

;; Cast a vote on a proposal
(define-public (vote (proposal-id uint) (vote-for bool) (amount uint))
    (let (
        (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
        (voter-power amount)
    )
        ;; Validate voting conditions
        (asserts! (is-none (map-get? votes { proposal-id: proposal-id, voter: tx-sender })) err-already-voted)
        (asserts! (< stacks-block-height (get end-block proposal)) err-voting-closed)
        (asserts! (is-eq (get status proposal) status-active) err-voting-closed)
        (asserts! (> voter-power u0) err-insufficient-voting-power)
        
        ;; Record the vote
        (map-set votes 
            { proposal-id: proposal-id, voter: tx-sender }
            { amount: voter-power, vote-for: vote-for }
        )
        
        ;; Update proposal vote counts
        (map-set proposals proposal-id
            (merge proposal {
                votes-for: (if vote-for 
                    (+ (get votes-for proposal) voter-power)
                    (get votes-for proposal)
                ),
                votes-against: (if vote-for
                    (get votes-against proposal)
                    (+ (get votes-against proposal) voter-power)
                )
            })
        )
        
        (print { 
            event: "vote-cast", 
            proposal-id: proposal-id, 
            voter: tx-sender, 
            vote-for: vote-for,
            amount: voter-power
        })
        (ok true)
    )
)

;; Finalize proposal after voting period
(define-public (finalize-proposal (proposal-id uint))
    (let (
        (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
        (total-votes (+ (get votes-for proposal) (get votes-against proposal)))
        (total-supply u1000000000000) ;; Should query from governance token
        (quorum-required (/ (* total-supply quorum-percentage) u100))
        (votes-for (get votes-for proposal))
        (votes-against (get votes-against proposal))
    )
        (asserts! (>= stacks-block-height (get end-block proposal)) err-voting-still-active)
        (asserts! (is-eq (get status proposal) status-active) err-voting-closed)
        
        ;; Check quorum and passing threshold
        (if (and 
                (>= total-votes quorum-required)
                (>= (* votes-for u100) (* (+ votes-for votes-against) passing-threshold))
            )
            (begin
                (map-set proposals proposal-id (merge proposal { status: status-passed }))
                (print { event: "proposal-passed", proposal-id: proposal-id })
                (ok status-passed)
            )
            (begin
                (map-set proposals proposal-id (merge proposal { status: status-failed }))
                (print { event: "proposal-failed", proposal-id: proposal-id })
                (ok status-failed)
            )
        )
    )
)

;; Execute a passed proposal
(define-public (execute-proposal (proposal-id uint))
    (let (
        (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
    )
        (asserts! (is-eq (get status proposal) status-passed) err-proposal-not-passed)
        (asserts! (>= stacks-block-height (+ (get end-block proposal) (get execution-delay proposal))) err-voting-still-active)
        
        (map-set proposals proposal-id (merge proposal { status: status-executed }))
        (print { event: "proposal-executed", proposal-id: proposal-id })
        (ok true)
    )
)

;; Cancel a proposal (only by proposer or owner)
(define-public (cancel-proposal (proposal-id uint))
    (let (
        (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-found))
    )
        (asserts! 
            (or 
                (is-eq tx-sender (get proposer proposal))
                (is-eq tx-sender contract-owner)
            )
            err-owner-only
        )
        (asserts! (is-eq (get status proposal) status-active) err-proposal-already-executed)
        
        (map-set proposals proposal-id (merge proposal { status: status-cancelled }))
        (print { event: "proposal-cancelled", proposal-id: proposal-id })
        (ok true)
    )
)

;; Read-only functions

(define-read-only (get-proposal (proposal-id uint))
    (ok (map-get? proposals proposal-id))
)

(define-read-only (get-proposal-count)
    (ok (var-get proposal-count))
)

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

(define-read-only (has-voted (proposal-id uint) (voter principal))
    (ok (is-some (map-get? votes { proposal-id: proposal-id, voter: voter })))
)

(define-read-only (get-proposal-status (proposal-id uint))
    (ok (get status (unwrap! (map-get? proposals proposal-id) err-proposal-not-found)))
)

(define-read-only (is-voting-active (proposal-id uint))
    (match (map-get? proposals proposal-id)
        proposal
            (ok (and 
                (< stacks-block-height (get end-block proposal))
                (is-eq (get status proposal) status-active)
            ))
        err-proposal-not-found
    )
)

(define-read-only (get-voting-results (proposal-id uint))
    (match (map-get? proposals proposal-id)
        proposal
            (ok {
                votes-for: (get votes-for proposal),
                votes-against: (get votes-against proposal),
                total-votes: (+ (get votes-for proposal) (get votes-against proposal)),
                status: (get status proposal)
            })
        err-proposal-not-found
    )
)

Functions (12)

FunctionAccessArgs
create-proposalpublictitle: (string-ascii 100
votepublicproposal-id: uint, vote-for: bool, amount: uint
finalize-proposalpublicproposal-id: uint
execute-proposalpublicproposal-id: uint
cancel-proposalpublicproposal-id: uint
get-proposalread-onlyproposal-id: uint
get-proposal-countread-only
get-voteread-onlyproposal-id: uint, voter: principal
has-votedread-onlyproposal-id: uint, voter: principal
get-proposal-statusread-onlyproposal-id: uint
is-voting-activeread-onlyproposal-id: uint
get-voting-resultsread-onlyproposal-id: uint