Source Code


;; bitgov.clar
;; 
;; ============================================
;; title: BitGov
;; summary: A comprehensive DAO governance suite with proposal management, voting, and treasury.
;; ============================================

;; --- Constants & Error Codes ---

(define-constant ERR_UNAUTHORIZED (err u100))
(define-constant ERR_ALREADY_EXISTS (err u101))
(define-constant ERR_NOT_FOUND (err u102))
(define-constant ERR_PROPOSAL_EXPIRED (err u103))
(define-constant ERR_PROPOSAL_NOT_ACTIVE (err u104))
(define-constant ERR_ALREADY_VOTED (err u105))
(define-constant ERR_INSUFFICIENT_FUNDS (err u106))
(define-constant ERR_INVALID_PARAMETER (err u107))
(define-constant ERR_EXECUTION_DELAY_NOT_MET (err u108))

;; Governance Parameters
(define-constant VOTING_PERIOD u144) ;; ~1 day in blocks (assuming 10 min blocks)
(define-constant EXECUTION_DELAY u144) ;; ~1 day

;; --- Data Variables ---

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

;; --- Data Maps ---

;; Proposals
(define-map proposals uint {
  proposer: principal,
  title: (string-ascii 50),
  description: (string-utf8 500),
  start-block: uint,
  end-block: uint,
  votes-for: uint,
  votes-against: uint,
  status: (string-ascii 20), ;; "active", "passed", "failed", "executed"
  executed: bool
})

;; Votes: proposal-id -> voter -> { outcome, amount }
(define-map votes { proposal-id: uint, voter: principal } {
  outcome: bool, ;; true = for, false = against
  amount: uint
})

;; Members (Simple whitelist for now, can be expanded)
(define-map members principal {
  reputation: uint,
  joined-at: uint
})

;; --- Public Functions (Governance) ---

;; 1. Create Proposal
(define-public (create-proposal (title (string-ascii 50)) (description (string-utf8 500)))
  (let
    (
      (current-count (var-get proposal-count))
      (start-height burn-block-height)
      (end-height (+ start-height VOTING_PERIOD))
      (new-proposal {
        proposer: tx-sender,
        title: title,
        description: description,
        start-block: start-height,
        end-block: end-height,
        votes-for: u0,
        votes-against: u0,
        status: "active",
        executed: false
      })
    )
    (begin
      (map-set proposals current-count new-proposal)
      (var-set proposal-count (+ current-count u1))
      
      ;; EVENT: Proposal Created
      (print {
        event: "proposal-created",
        proposal-id: current-count,
        proposer: tx-sender,
        title: title
      })
      (ok current-count)
    )
  )
)

;; 2. Vote
(define-public (vote (proposal-id uint) (vote-for bool) (amount uint))
  (let
    (
      (proposal (unwrap! (map-get? proposals proposal-id) ERR_NOT_FOUND))
      (voter-participation (map-get? votes { proposal-id: proposal-id, voter: tx-sender }))
    )
    ;; Checks
    (asserts! (is-eq (get status proposal) "active") ERR_PROPOSAL_NOT_ACTIVE)
    (asserts! (<= burn-block-height (get end-block proposal)) ERR_PROPOSAL_EXPIRED)
    (asserts! (is-none voter-participation) ERR_ALREADY_VOTED)
    
    ;; Update Proposal
    (let
      (
        (current-votes-for (get votes-for proposal))
        (current-votes-against (get votes-against proposal))
        (new-votes-for (if vote-for (+ current-votes-for amount) current-votes-for))
        (new-votes-against (if (not vote-for) (+ current-votes-against amount) current-votes-against))
      )
      (map-set proposals proposal-id (merge proposal {
        votes-for: new-votes-for,
        votes-against: new-votes-against
      }))
      
      ;; Record Vote
      (map-set votes { proposal-id: proposal-id, voter: tx-sender } {
        outcome: vote-for,
        amount: amount
      })
      
      ;; EVENT: Vote Cast
      (print {
        event: "vote-cast",
        proposal-id: proposal-id,
        voter: tx-sender,
        outcome: vote-for,
        amount: amount
      })
      
      (ok true)
    )
  )
)

;; 3. Execute Proposal (Simplified)
;; Checks if voting period ended and if votes-for > votes-against
(define-public (execute-proposal (proposal-id uint))
  (let
    (
      (proposal (unwrap! (map-get? proposals proposal-id) ERR_NOT_FOUND))
    )
    (asserts! (> burn-block-height (get end-block proposal)) ERR_EXECUTION_DELAY_NOT_MET)
    (asserts! (is-eq (get status proposal) "active") ERR_PROPOSAL_NOT_ACTIVE)
    
    (let
      (
        (passed (> (get votes-for proposal) (get votes-against proposal)))
        (new-status (if passed "passed" "failed"))
      )
      (map-set proposals proposal-id (merge proposal {
        status: new-status,
        executed: passed ;; In a real scenario, this would trigger external logic
      }))
      
      ;; EVENT: Proposal Concluded
      (print {
        event: "proposal-concluded",
        proposal-id: proposal-id,
        status: new-status,
        votes-for: (get votes-for proposal),
        votes-against: (get votes-against proposal)
      })
      
      (ok passed)
    )
  )
)

;; --- Utility Functions (Counter Logic) ---

(define-public (increment)
  (let ((new-val (+ (var-get general-counter) u1)))
    (begin
      (var-set general-counter new-val)
      ;; EVENT: Counter Incremented
      (print {
        event: "counter-increment",
        caller: tx-sender,
        new-value: new-val
      })
      (ok new-val)
    )
  )
)

(define-public (decrement)
  (let ((current-val (var-get general-counter)))
    (begin
      (asserts! (> current-val u0) (err u109)) ;; Underflow protection
      (let ((new-val (- current-val u1)))
        (var-set general-counter new-val)
        ;; EVENT: Counter Decremented
        (print {
          event: "counter-decrement",
          caller: tx-sender,
          new-value: new-val
        })
        (ok new-val)
      )
    )
  )
)

;; --- Read-Only Functions ---

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

(define-read-only (get-signature-count) ;; Kept name for compatibility/checking, but maps to proposal-count
  (var-get proposal-count)
)

(define-read-only (get-general-counter)
  (var-get general-counter)
)

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

Functions (9)

FunctionAccessArgs
create-proposalpublictitle: (string-ascii 50
votepublicproposal-id: uint, vote-for: bool, amount: uint
execute-proposalpublicproposal-id: uint
incrementpublic
decrementpublic
get-proposalread-onlyproposal-id: uint
get-signature-countread-only
get-general-counterread-only
get-voteread-onlyproposal-id: uint, voter: principal