Source Code

;; token-voting.clar
;; Token-weighted voting system with delegation and quadratic option
;; Clarity 4 / Epoch 3.3

;; -----------------------------------------------
;; Constants
;; -----------------------------------------------
(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u300))
(define-constant ERR-PROPOSAL-NOT-FOUND (err u301))
(define-constant ERR-ALREADY-VOTED (err u302))
(define-constant ERR-VOTING-CLOSED (err u303))
(define-constant ERR-NO-VOTING-POWER (err u304))
(define-constant ERR-PROPOSAL-NOT-PASSED (err u305))
(define-constant ERR-ALREADY-EXECUTED (err u306))
(define-constant ERR-INVALID-INPUT (err u307))
(define-constant ERR-SELF-DELEGATE (err u308))
(define-constant ERR-VOTING-ACTIVE (err u309))

;; -----------------------------------------------
;; Data Variables
;; -----------------------------------------------
(define-data-var proposal-nonce uint u0)
(define-data-var quadratic-enabled bool false)
(define-data-var default-voting-period uint u144)

;; -----------------------------------------------
;; Data Maps
;; -----------------------------------------------
(define-map proposals
  uint
  {
    title: (string-ascii 64),
    description: (string-ascii 256),
    proposer: principal,
    start-block: uint,
    end-block: uint,
    yes-votes: uint,
    no-votes: uint,
    executed: bool,
    total-voters: uint
  }
)

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

(define-map token-balances
  principal
  uint
)

(define-map delegations
  principal
  principal
)

(define-map vote-history
  principal
  (list 20 uint)
)

;; -----------------------------------------------
;; Private Functions
;; -----------------------------------------------
(define-private (sqrt-approx (n uint))
  (if (<= n u1)
    n
    (let (
      (x1 (/ n u2))
      (x2 (/ (+ x1 (/ n x1)) u2))
      (x3 (/ (+ x2 (/ n x2)) u2))
    )
      x3
    )
  )
)

(define-private (get-voting-power (voter principal))
  (let (
    (own-balance (default-to u0 (map-get? token-balances voter)))
  )
    (if (var-get quadratic-enabled)
      (sqrt-approx own-balance)
      own-balance
    )
  )
)

(define-private (append-to-history (who principal) (pid uint))
  (let (
    (current (default-to (list) (map-get? vote-history who)))
  )
    (match (as-max-len? (append current pid) u20)
      updated (begin (map-set vote-history who updated) true)
      false
    )
  )
)

;; -----------------------------------------------
;; Public Functions
;; -----------------------------------------------

;; Assign token balance to a voter (admin only)
(define-public (set-token-balance (who principal) (amount uint))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (map-set token-balances who amount)
    (ok true)
  )
)

;; Create a new proposal
(define-public (create-proposal
    (title (string-ascii 64))
    (description (string-ascii 256))
    (duration uint))
  (let (
    (pid (var-get proposal-nonce))
    (vote-period (if (> duration u0) duration (var-get default-voting-period)))
  )
    (asserts! (> (get-voting-power tx-sender) u0) ERR-NO-VOTING-POWER)
    (map-set proposals pid
      {
        title: title,
        description: description,
        proposer: tx-sender,
        start-block: tenure-height,
        end-block: (+ tenure-height vote-period),
        yes-votes: u0,
        no-votes: u0,
        executed: false,
        total-voters: u0
      })
    (var-set proposal-nonce (+ pid u1))
    (ok pid)
  )
)

;; Cast a vote on a proposal
(define-public (cast-vote (proposal-id uint) (vote-for bool))
  (let (
    (proposal (unwrap! (map-get? proposals proposal-id)
                       ERR-PROPOSAL-NOT-FOUND))
    (effective-voter (default-to tx-sender
      (map-get? delegations tx-sender)))
    (power (get-voting-power effective-voter))
  )
    (asserts! (<= tenure-height (get end-block proposal)) ERR-VOTING-CLOSED)
    (asserts! (> power u0) ERR-NO-VOTING-POWER)
    (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 }
      { amount: power, vote-for: vote-for, block: tenure-height })
    (if vote-for
      (map-set proposals proposal-id
        (merge proposal {
          yes-votes: (+ (get yes-votes proposal) power),
          total-voters: (+ (get total-voters proposal) u1) }))
      (map-set proposals proposal-id
        (merge proposal {
          no-votes: (+ (get no-votes proposal) power),
          total-voters: (+ (get total-voters proposal) u1) }))
    )
    (append-to-history tx-sender proposal-id)
    (ok power)
  )
)

;; Delegate voting power to another address
(define-public (delegate-to (delegate principal))
  (begin
    (asserts! (not (is-eq tx-sender delegate)) ERR-SELF-DELEGATE)
    (map-set delegations tx-sender delegate)
    (ok true)
  )
)

;; Remove delegation
(define-public (revoke-delegation)
  (begin
    (map-delete delegations tx-sender)
    (ok true)
  )
)

;; Toggle quadratic voting
(define-public (set-quadratic-voting (enabled bool))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (var-set quadratic-enabled enabled)
    (ok true)
  )
)

;; Execute a passed proposal
(define-public (execute-proposal (proposal-id uint))
  (let (
    (proposal (unwrap! (map-get? proposals proposal-id)
                       ERR-PROPOSAL-NOT-FOUND))
  )
    (asserts! (> tenure-height (get end-block proposal)) ERR-VOTING-ACTIVE)
    (asserts! (not (get executed proposal)) ERR-ALREADY-EXECUTED)
    (asserts! (> (get yes-votes proposal) (get no-votes proposal))
              ERR-PROPOSAL-NOT-PASSED)
    (map-set proposals proposal-id
      (merge proposal { executed: true }))
    (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-voter-power (who principal))
  (get-voting-power who)
)

(define-read-only (get-vote-history (who principal))
  (default-to (list) (map-get? vote-history who))
)

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

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

(define-read-only (is-quadratic-enabled)
  (var-get quadratic-enabled)
)

Functions (17)

FunctionAccessArgs
sqrt-approxprivaten: uint
get-voting-powerprivatevoter: principal
append-to-historyprivatewho: principal, pid: uint
set-token-balancepublicwho: principal, amount: uint
create-proposalpublictitle: (string-ascii 64
cast-votepublicproposal-id: uint, vote-for: bool
delegate-topublicdelegate: principal
revoke-delegationpublic
set-quadratic-votingpublicenabled: bool
execute-proposalpublicproposal-id: uint
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
get-voter-powerread-onlywho: principal
get-vote-historyread-onlywho: principal
get-delegationread-onlywho: principal
get-proposal-countread-only
is-quadratic-enabledread-only