Source Code

;; title: vault-governance
;; version: 1.0.0
;; summary: DAO governance for vault parameters
;; description: Governance system for vault configuration and upgrades - Clarity 4

;; Constants
(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-UNAUTHORIZED (err u300))
(define-constant ERR-PROPOSAL-NOT-FOUND (err u301))
(define-constant ERR-ALREADY-VOTED (err u302))
(define-constant ERR-VOTING-ENDED (err u303))
(define-constant ERR-VOTING-ACTIVE (err u304))
(define-constant ERR-QUORUM-NOT-MET (err u305))
(define-constant ERR-PROPOSAL-FAILED (err u306))

;; Proposal Types
(define-constant PROPOSAL-TYPE-FEE-CHANGE u1)
(define-constant PROPOSAL-TYPE-PARAMETER-CHANGE u2)
(define-constant PROPOSAL-TYPE-UPGRADE u3)
(define-constant PROPOSAL-TYPE-EMERGENCY u4)

;; Voting Parameters
(define-constant VOTING-PERIOD u1008) ;; ~1 week in blocks (assuming 10min blocks)
(define-constant QUORUM-PERCENT u30) ;; 30% quorum required
(define-constant APPROVAL-THRESHOLD u60) ;; 60% approval required

;; Data Variables
(define-data-var next-proposal-id uint u1)
(define-data-var total-voting-power uint u0)
(define-data-var governance-paused bool false)

;; Data Maps - Using stacks-block-time for Clarity 4
(define-map proposals uint {
  proposer: principal,
  proposal-type: uint,
  title: (string-ascii 100),
  description: (string-ascii 500),
  target-contract: principal,
  proposed-value: uint,
  votes-for: uint,
  votes-against: uint,
  start-time: uint,  ;; Clarity 4: Unix timestamp
  end-time: uint,    ;; Clarity 4: Unix timestamp
  executed: bool,
  passed: bool
})

(define-map votes { proposal-id: uint, voter: principal } {
  vote: bool,  ;; true = for, false = against
  voting-power: uint,
  voted-at: uint  ;; Clarity 4: Unix timestamp
})

(define-map voting-power principal uint)

(define-map delegations principal principal) ;; delegator -> delegate

;; Public Functions

;; Create new proposal
(define-public (create-proposal
  (proposal-type uint)
  (title (string-ascii 100))
  (description (string-ascii 500))
  (target-contract principal)
  (proposed-value uint))
  (let (
    (proposal-id (var-get next-proposal-id))
    (proposer-power (get-voting-power tx-sender))
  )
    (asserts! (not (var-get governance-paused)) ERR-UNAUTHORIZED)
    (asserts! (> proposer-power u0) ERR-UNAUTHORIZED) ;; Must have voting power

    ;; Create proposal
    (map-set proposals proposal-id {
      proposer: tx-sender,
      proposal-type: proposal-type,
      title: title,
      description: description,
      target-contract: target-contract,
      proposed-value: proposed-value,
      votes-for: u0,
      votes-against: u0,
      start-time: stacks-block-time,
      end-time: (+ stacks-block-time (* VOTING-PERIOD u600)), ;; ~1 week (600 sec/block estimate)
      executed: false,
      passed: false
    })

    ;; Increment proposal ID
    (var-set next-proposal-id (+ proposal-id u1))

    ;; Emit event
    (print {
      event: "proposal-created",
      proposal-id: proposal-id,
      proposer: tx-sender,
      type: proposal-type,
      title: title,
      timestamp: stacks-block-time
    })

    (ok proposal-id)
  )
)

;; Cast vote
(define-public (vote (proposal-id uint) (vote-for bool))
  (let (
    (proposal (unwrap! (map-get? proposals proposal-id) ERR-PROPOSAL-NOT-FOUND))
    (voter-power (get-voting-power tx-sender))
    (vote-key { proposal-id: proposal-id, voter: tx-sender })
  )
    (asserts! (> voter-power u0) ERR-UNAUTHORIZED)
    (asserts! (is-none (map-get? votes vote-key)) ERR-ALREADY-VOTED)
    (asserts! (< stacks-block-time (get end-time proposal)) ERR-VOTING-ENDED)

    ;; Record vote
    (map-set votes vote-key {
      vote: vote-for,
      voting-power: voter-power,
      voted-at: stacks-block-time
    })

    ;; 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))
      }))

    ;; Emit event
    (print {
      event: "vote-cast",
      proposal-id: proposal-id,
      voter: tx-sender,
      vote-for: vote-for,
      voting-power: voter-power,
      timestamp: stacks-block-time
    })

    (ok true)
  )
)

;; Execute proposal after voting ends
(define-public (execute-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-power (var-get total-voting-power))
  )
    (asserts! (>= stacks-block-time (get end-time proposal)) ERR-VOTING-ACTIVE)
    (asserts! (not (get executed proposal)) ERR-UNAUTHORIZED)

    ;; Check quorum
    (asserts! (>= (* total-votes u100) (* total-power QUORUM-PERCENT)) ERR-QUORUM-NOT-MET)

    ;; Check if proposal passed
    (let (
      (approval-rate (if (> total-votes u0)
        (/ (* (get votes-for proposal) u100) total-votes)
        u0))
      (passed (>= approval-rate APPROVAL-THRESHOLD))
    )
      ;; Mark as executed
      (map-set proposals proposal-id
        (merge proposal {
          executed: true,
          passed: passed
        }))

      ;; Emit event
      (print {
        event: "proposal-executed",
        proposal-id: proposal-id,
        passed: passed,
        votes-for: (get votes-for proposal),
        votes-against: (get votes-against proposal),
        timestamp: stacks-block-time
      })

      (if passed
        (ok true)
        ERR-PROPOSAL-FAILED)
    )
  )
)

;; Delegate voting power
(define-public (delegate-vote (delegate principal))
  (begin
    (map-set delegations tx-sender delegate)
    (print {
      event: "vote-delegated",
      delegator: tx-sender,
      delegate: delegate,
      timestamp: stacks-block-time
    })
    (ok true)
  )
)

;; Remove delegation
(define-public (remove-delegation)
  (begin
    (map-delete delegations tx-sender)
    (print {
      event: "delegation-removed",
      delegator: tx-sender,
      timestamp: stacks-block-time
    })
    (ok true)
  )
)

;; Set voting power (called by staking contract)
(define-public (set-voting-power (user principal) (power uint))
  (begin
    ;; In production, verify caller is authorized staking contract
    (let (
      (old-power (default-to u0 (map-get? voting-power user)))
      (power-delta (if (> power old-power)
        (- power old-power)
        (- old-power power)))
    )
      (map-set voting-power user power)

      ;; Update total voting power
      (if (> power old-power)
        (var-set total-voting-power (+ (var-get total-voting-power) power-delta))
        (var-set total-voting-power (- (var-get total-voting-power) power-delta))
      )

      (ok true)
    )
  )
)

;; Admin Functions

(define-public (pause-governance)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-UNAUTHORIZED)
    (var-set governance-paused true)
    (ok true)
  )
)

(define-public (resume-governance)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-UNAUTHORIZED)
    (var-set governance-paused false)
    (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? votes { proposal-id: proposal-id, voter: voter })
)

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

(define-read-only (get-delegation (user principal))
  (map-get? delegations user)
)

(define-read-only (get-total-voting-power)
  (var-get total-voting-power)
)

(define-read-only (is-governance-paused)
  (var-get governance-paused)
)

(define-read-only (can-execute (proposal-id uint))
  (match (map-get? proposals proposal-id)
    proposal (and
      (>= stacks-block-time (get end-time proposal))
      (not (get executed proposal)))
    false
  )
)

(define-read-only (get-proposal-status (proposal-id uint))
  (match (map-get? proposals proposal-id)
    proposal {
      is-active: (< stacks-block-time (get end-time proposal)),
      is-executed: (get executed proposal),
      is-passed: (get passed proposal),
      votes-for: (get votes-for proposal),
      votes-against: (get votes-against proposal)
    }
    {
      is-active: false,
      is-executed: false,
      is-passed: false,
      votes-for: u0,
      votes-against: u0
    }
  )
)

Functions (16)

FunctionAccessArgs
get-total-voting-powerread-only
create-proposalpublicproposal-type: uint, title: (string-ascii 100
votepublicproposal-id: uint, vote-for: bool
execute-proposalpublicproposal-id: uint
delegate-votepublicdelegate: principal
remove-delegationpublic
set-voting-powerpublicuser: principal, power: uint
pause-governancepublic
resume-governancepublic
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
get-voting-powerread-onlyuser: principal
get-delegationread-onlyuser: principal
is-governance-pausedread-only
can-executeread-onlyproposal-id: uint
get-proposal-statusread-onlyproposal-id: uint