Source Code








(define-constant contract-owner tx-sender)

;; Error codes
(define-constant error-unauthorized (err u100))
(define-constant error-proposal-exists (err u101))
(define-constant error-proposal-not-found (err u102))
(define-constant error-proposal-expired (err u103))
(define-constant error-proposal-not-expired (err u104))
(define-constant error-already-voted (err u105))
(define-constant error-insufficient-tokens (err u106))
(define-constant error-not-proposal-creator (err u107))
(define-constant error-proposal-executed (err u108))
(define-constant error-invalid-proposal-duration (err u109))
(define-constant error-invalid-quorum (err u110))
(define-constant error-not-contract-owner (err u111))
(define-constant error-proposal-not-active (err u112))
(define-constant error-proposal-not-passed (err u113))
(define-constant error-invalid-vote-type (err u114))

;; Vote types
(define-constant vote-type-for u1)
(define-constant vote-type-against u2)
(define-constant vote-type-abstain u3)

;; Proposal status
(define-constant status-active u1)
(define-constant status-passed u2)
(define-constant status-rejected u3)
(define-constant status-executed u4)

;; Data structures
(define-map proposals
  { proposal-id: uint }
  {
    creator: principal,
    title: (string-ascii 64),
    description: (string-ascii 512),
    link: (string-ascii 256),
    start-block: uint,
    end-block: uint,
    quorum-threshold: uint,
    vote-for: uint,
    vote-against: uint,
    vote-abstain: uint,
    status: uint,
    executed-at: (optional uint)
  }
)

(define-map votes
  { proposal-id: uint, voter: principal }
  { 
    weight: uint, 
    vote-type: uint,
    time: uint
  }
)

;; Governance token (simulated)
(define-map token-balances principal uint)

;; Total token supply
(define-data-var total-token-supply uint u10000000)

;; Governance parameters
(define-data-var min-proposal-duration uint u144)  ;; Minimum 144 blocks (roughly 1 day)
(define-data-var default-quorum-threshold uint u2000)  ;; 20% of total tokens
(define-data-var proposal-fee uint u100)  ;; Fee to create proposal
(define-data-var next-proposal-id uint u1)

;; Read-only functions

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

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

;; Check if a proposal exists
(define-read-only (proposal-exists (proposal-id uint))
  (is-some (get-proposal proposal-id))
)

;; Check if a proposal is active
(define-read-only (is-proposal-active (proposal-id uint))
  (match (get-proposal proposal-id)
    proposal (and 
              (is-eq (get status proposal) status-active)
              (< stacks-block-height (get end-block proposal))
            )
    false
  )
)

;; Check if a proposal has ended
(define-read-only (has-proposal-ended (proposal-id uint))
  (match (get-proposal proposal-id)
    proposal (>= stacks-block-height (get end-block proposal))
    false
  )
)

;; Get token balance
(define-read-only (get-token-balance (account principal))
  (default-to u0 (map-get? token-balances account))
)

;; Get total token supply
(define-read-only (get-total-supply)
  (var-get total-token-supply)
)

;; Get proposal results
(define-read-only (get-proposal-results (proposal-id uint))
  (match (get-proposal proposal-id)
    proposal {
      vote-for: (get vote-for proposal),
      vote-against: (get vote-against proposal),
      vote-abstain: (get vote-abstain proposal),
      total-votes: (+ (get vote-for proposal) (get vote-against proposal) (get vote-abstain proposal)),
      quorum-reached: (>= (+ (get vote-for proposal) (get vote-against proposal) (get vote-abstain proposal)) (get quorum-threshold proposal)),
      passed: (and 
                (>= (+ (get vote-for proposal) (get vote-against proposal) (get vote-abstain proposal)) (get quorum-threshold proposal))
                (> (get vote-for proposal) (get vote-against proposal))
              )
    }
    { vote-for: u0, vote-against: u0, vote-abstain: u0, total-votes: u0, quorum-reached: false, passed: false }
  )
)

;; Get current proposal ID
(define-read-only (get-current-proposal-id)
  (var-get next-proposal-id)
)

;; Helper functions

;; Check if a vote type is valid
(define-private (is-valid-vote-type (vote-type uint))
  (or (is-eq vote-type vote-type-for)
      (or (is-eq vote-type vote-type-against)
          (is-eq vote-type vote-type-abstain)))
)






;; Calculate proposal status after voting period
(define-private (calculate-proposal-status (proposal-id uint))
  (match (get-proposal proposal-id)
    proposal 
      (let ((total-votes (+ (get vote-for proposal) (get vote-against proposal) (get vote-abstain proposal))))
        (if (>= total-votes (get quorum-threshold proposal))
          (if (> (get vote-for proposal) (get vote-against proposal))
            status-passed
            status-rejected)
          status-rejected))
    status-rejected)
)

;; Public functions

;; Create a new proposal
(define-public (create-proposal 
                (title (string-ascii 64))
                (description (string-ascii 512))
                (link (string-ascii 256))
                (duration uint)
                (quorum-threshold uint))
  (let ((proposal-id (var-get next-proposal-id))
        (start-block stacks-block-height)
        (end-block (+ stacks-block-height duration))
        (creator-balance (get-token-balance tx-sender)))
    (begin
      ;; Validate inputs
      (asserts! (>= duration (var-get min-proposal-duration)) error-invalid-proposal-duration)
      (asserts! (>= quorum-threshold (var-get default-quorum-threshold)) error-invalid-quorum)
      (asserts! (>= creator-balance (var-get proposal-fee)) error-insufficient-tokens)

      ;; Subtract proposal fee from creator
      (map-set token-balances tx-sender (- creator-balance (var-get proposal-fee)))

      ;; Create proposal
      (map-set proposals
        { proposal-id: proposal-id }
        {
          creator: tx-sender,
          title: title,
          description: description,
          link: link,
          start-block: start-block,
          end-block: end-block,
          quorum-threshold: quorum-threshold,
          vote-for: u0,
          vote-against: u0,
          vote-abstain: u0,
          status: status-active,
          executed-at: none
        }
      )

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

      (ok proposal-id)
    )
  )
)

;; Execute a passed proposal
(define-public (execute-proposal (proposal-id uint))
  (let ((proposal (unwrap! (get-proposal proposal-id) error-proposal-not-found)))
    (begin
      ;; Check proposal is passed
      (asserts! (is-eq (get status proposal) status-passed) error-proposal-not-passed)

      ;; Check proposal not already executed
      (asserts! (is-none (get executed-at proposal)) error-proposal-executed)

      ;; Update proposal as executed
      (map-set proposals
        { proposal-id: proposal-id }
        (merge proposal { 
          status: status-executed,
          executed-at: (some stacks-block-height)
        })
      )

      ;; Note: In a real implementation, this would trigger execution logic
      ;; for the specific proposal type, such as treasury transfers, parameter
      ;; updates, etc.

      (ok true)
    )
  )
)

;; Administrative functions

;; Mint governance tokens (for testing/simulation purposes)
(define-public (mint-tokens (recipient principal) (amount uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) error-not-contract-owner)

    (let ((current-balance (get-token-balance recipient))
          (new-balance (+ current-balance amount)))

      ;; Update recipient balance
      (map-set token-balances recipient new-balance)

      ;; Update total supply
      (var-set total-token-supply (+ (var-get total-token-supply) amount))

      (ok new-balance)
    )
  )
)

;; Update proposal fee
(define-public (update-proposal-fee (new-fee uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) error-not-contract-owner)
    (ok (var-set proposal-fee new-fee))
  )
)

;; Update minimum proposal duration
(define-public (update-min-proposal-duration (new-duration uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) error-not-contract-owner)
    (ok (var-set min-proposal-duration new-duration))
  )
)

;; Update default quorum threshold
(define-public (update-default-quorum-threshold (new-threshold uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) error-not-contract-owner)
    (ok (var-set default-quorum-threshold new-threshold))
  )
)







Functions (17)

FunctionAccessArgs
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
proposal-existsread-onlyproposal-id: uint
is-proposal-activeread-onlyproposal-id: uint
has-proposal-endedread-onlyproposal-id: uint
get-token-balanceread-onlyaccount: principal
get-total-supplyread-only
get-proposal-resultsread-onlyproposal-id: uint
get-current-proposal-idread-only
is-valid-vote-typeprivatevote-type: uint
calculate-proposal-statusprivateproposal-id: uint
create-proposalpublictitle: (string-ascii 64
execute-proposalpublicproposal-id: uint
mint-tokenspublicrecipient: principal, amount: uint
update-proposal-feepublicnew-fee: uint
update-min-proposal-durationpublicnew-duration: uint
update-default-quorum-thresholdpublicnew-threshold: uint