Source Code

(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-unauthorized (err u102))
(define-constant err-invalid-params (err u103))
(define-constant err-already-voted (err u104))

(define-map members
  {member: principal}
  {voting-power: uint, active: bool}
)

(define-map proposals
  {proposal-id: uint}
  {
    title: (string-ascii 256),
    description: (string-ascii 512),
    proposer: principal,
    votes-for: uint,
    votes-against: uint,
    status: (string-ascii 16),
    created-at: uint,
    ends-at: uint,
    proposal-type: (string-ascii 64)
  }
)

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

(define-data-var proposal-nonce uint u0)
(define-data-var total-voting-power uint u0)

(define-read-only (get-member (member principal))
  (map-get? members {member: member})
)

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

(define-public (add-member (member principal) (voting-power uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set members {member: member}
      {voting-power: voting-power, active: true}
    )
    (var-set total-voting-power (+ (var-get total-voting-power) voting-power))
    (ok true)
  )
)

(define-public (create-proposal
  (title (string-ascii 256))
  (description (string-ascii 512))
  (proposal-type (string-ascii 64))
  (duration uint)
)
  (let (
    (member (unwrap! (map-get? members {member: tx-sender}) err-unauthorized))
    (proposal-id (var-get proposal-nonce))
  )
    (asserts! (get active member) err-unauthorized)
    (map-set proposals {proposal-id: proposal-id}
      {
        title: title,
        description: description,
        proposer: tx-sender,
        votes-for: u0,
        votes-against: u0,
        status: "active",
        created-at: stacks-block-height,
        ends-at: (+ stacks-block-height duration),
        proposal-type: proposal-type
      }
    )
    (var-set proposal-nonce (+ proposal-id u1))
    (ok proposal-id)
  )
)

(define-public (vote (proposal-id uint) (vote-for bool))
  (let (
    (proposal (unwrap! (map-get? proposals {proposal-id: proposal-id}) err-not-found))
    (member (unwrap! (map-get? members {member: tx-sender}) err-unauthorized))
  )
    (asserts! (get active member) err-unauthorized)
    (asserts! (is-eq (get status proposal) "active") err-invalid-params)
    (asserts! (< stacks-block-height (get ends-at proposal)) err-invalid-params)
    (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}
      {vote: vote-for, power: (get voting-power member)}
    )
    (ok (map-set proposals {proposal-id: proposal-id}
      (merge proposal {
        votes-for: (if vote-for (+ (get votes-for proposal) (get voting-power member)) (get votes-for proposal)),
        votes-against: (if vote-for (get votes-against proposal) (+ (get votes-against proposal) (get voting-power member)))
      })
    ))
  )
)

(define-public (finalize-proposal (proposal-id uint))
  (let ((proposal (unwrap! (map-get? proposals {proposal-id: proposal-id}) err-not-found)))
    (asserts! (>= stacks-block-height (get ends-at proposal)) err-invalid-params)
    (ok (map-set proposals {proposal-id: proposal-id}
      (merge proposal {
        status: (if (> (get votes-for proposal) (get votes-against proposal)) "passed" "rejected")
      })
    ))
  )
)

Functions (6)

FunctionAccessArgs
get-memberread-onlymember: principal
get-proposalread-onlyproposal-id: uint
add-memberpublicmember: principal, voting-power: uint
create-proposalpublictitle: (string-ascii 256
votepublicproposal-id: uint, vote-for: bool
finalize-proposalpublicproposal-id: uint