Source Code

;; StackSusu Governance v6
;; Enhanced governance with quadratic voting, delegation, and timelock

(define-constant CONTRACT-OWNER tx-sender)

;; Proposal types
(define-constant PROPOSAL-EXTEND-DEADLINE u1)
(define-constant PROPOSAL-CHANGE-INTERVAL u2)
(define-constant PROPOSAL-PAUSE-CIRCLE u3)
(define-constant PROPOSAL-RESUME-CIRCLE u4)
(define-constant PROPOSAL-REMOVE-MEMBER u5)
(define-constant PROPOSAL-CHANGE-ORDER u6)
(define-constant PROPOSAL-UPDATE-FEE u7)        ;; NEW: Fee changes
(define-constant PROPOSAL-EMERGENCY-ACTION u8)  ;; NEW: Emergency proposals
(define-constant PROPOSAL-UPGRADE-CONTRACT u9)  ;; NEW: Contract upgrades

;; Proposal status
(define-constant STATUS-ACTIVE u0)
(define-constant STATUS-PASSED u1)
(define-constant STATUS-REJECTED u2)
(define-constant STATUS-EXECUTED u3)
(define-constant STATUS-EXPIRED u4)
(define-constant STATUS-QUEUED u5)      ;; NEW: Waiting in timelock
(define-constant STATUS-CANCELLED u6)   ;; NEW: Cancelled before execution

;; Error constants
(define-constant ERR-NOT-AUTHORIZED (err u4000))
(define-constant ERR-NOT-MEMBER (err u4001))
(define-constant ERR-PROPOSAL-NOT-FOUND (err u4002))
(define-constant ERR-ALREADY-VOTED (err u4003))
(define-constant ERR-PROPOSAL-EXPIRED (err u4004))
(define-constant ERR-PROPOSAL-NOT-PASSED (err u4005))
(define-constant ERR-INVALID-PROPOSAL-TYPE (err u4006))
(define-constant ERR-CIRCLE-NOT-FOUND (err u4007))
(define-constant ERR-PAUSED (err u4008))
(define-constant ERR-QUORUM-NOT-MET (err u4009))
(define-constant ERR-TIMELOCK-NOT-EXPIRED (err u4010))
(define-constant ERR-ALREADY-DELEGATED (err u4011))
(define-constant ERR-NOT-DELEGATED (err u4012))
(define-constant ERR-SELF-DELEGATION (err u4013))
(define-constant ERR-DELEGATION-LOOP (err u4014))

;; Governance parameters
(define-constant VOTING-PERIOD-BLOCKS u432)     ;; ~3 days
(define-constant EXECUTION-DELAY-BLOCKS u144)   ;; ~1 day delay after passing
(define-constant QUORUM-PERCENT u50)            ;; 50% of members must vote
(define-constant TIMELOCK-BLOCKS u288)          ;; ~2 days timelock for execution
(define-constant EMERGENCY-QUORUM-PERCENT u75)  ;; 75% for emergency proposals

;; Proposal counter
(define-data-var proposal-counter uint u0)

;; Proposals
(define-map proposals
  uint
  {
    circle-id: uint,
    proposer: principal,
    proposal-type: uint,
    title: (string-ascii 100),
    description: (string-ascii 500),
    value: uint,
    target-member: (optional principal),
    votes-for: uint,
    votes-against: uint,
    total-voters: uint,
    total-voting-power: uint,
    status: uint,
    created-at: uint,
    expires-at: uint,
    queued-at: uint,
    executed-at: uint,
    use-quadratic: bool
  }
)

;; Vote records with weight
(define-map votes
  { proposal-id: uint, voter: principal }
  { vote: bool, voted-at: uint, weight: uint, delegated-from: (optional principal) }
)

;; Voting power (based on reputation and stake)
(define-map voting-power
  { circle-id: uint, member: principal }
  uint
)

;; Vote delegation
(define-map vote-delegations
  { circle-id: uint, delegator: principal }
  { delegate: principal, delegated-at: uint }
)

;; Delegated power tracking
(define-map delegated-power
  { circle-id: uint, delegate: principal }
  { total-power: uint, delegator-count: uint }
)

;; Circle proposal tracking
(define-map circle-proposals
  uint
  (list 50 uint)
)

;; Execution queue (timelock)
(define-map execution-queue
  uint
  { execute-after: uint, execute-before: uint }
)

;; Authorized executors
(define-map authorized-executors principal bool)

;; Member status for governance (simplified check)
(define-map governance-members
  { circle-id: uint, member: principal }
  bool
)


;; ============================================
;; Integer Square Root (Non-recursive)
;; ============================================

;; Newton's method with fixed iterations (no recursion)
(define-private (sqrt-int (n uint))
  (if (<= n u1)
    n
    (let
      (
        (x0 (/ n u2))
        (x1 (/ (+ x0 (/ n x0)) u2))
        (x2 (/ (+ x1 (/ n x1)) u2))
        (x3 (/ (+ x2 (/ n x2)) u2))
        (x4 (/ (+ x3 (/ n x3)) u2))
      )
      ;; After 4-5 iterations, we have good precision for typical voting power values
      x4
    )
  )
)


;; ============================================
;; Authorization
;; ============================================

(define-public (authorize-executor (executor principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (ok (map-set authorized-executors executor true))
  )
)

(define-public (revoke-executor (executor principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (ok (map-delete authorized-executors executor))
  )
)

(define-public (register-governance-member (circle-id uint) (member principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (ok (map-set governance-members { circle-id: circle-id, member: member } true))
  )
)

(define-read-only (is-governance-member (circle-id uint) (member principal))
  (default-to false (map-get? governance-members { circle-id: circle-id, member: member }))
)


;; ============================================
;; Voting Power Management
;; ============================================

(define-public (set-voting-power (circle-id uint) (member principal) (power uint))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (map-set voting-power { circle-id: circle-id, member: member } power)
    (ok power)
  )
)

(define-read-only (get-voting-power (circle-id uint) (member principal))
  (ok (default-to u100 (map-get? voting-power { circle-id: circle-id, member: member })))
)

(define-read-only (get-effective-voting-power (circle-id uint) (member principal))
  (let
    (
      (own-power (default-to u100 (map-get? voting-power { circle-id: circle-id, member: member })))
      (delegated (default-to { total-power: u0, delegator-count: u0 }
                   (map-get? delegated-power { circle-id: circle-id, delegate: member })))
    )
    (+ own-power (get total-power delegated))
  )
)


;; ============================================
;; Vote Delegation (NEW in v6)
;; ============================================

;; Check delegation at specific level (non-recursive)
(define-private (get-delegate-at-level (circle-id uint) (start principal) (level uint))
  (let
    (
      (l1 (map-get? vote-delegations { circle-id: circle-id, delegator: start }))
    )
    (if (is-eq level u1)
      (match l1 d (some (get delegate d)) none)
      (if (is-eq level u2)
        (match l1 
          d1 
            (match (map-get? vote-delegations { circle-id: circle-id, delegator: (get delegate d1) })
              d2 (some (get delegate d2))
              none)
          none)
        (if (is-eq level u3)
          (match l1 
            d1 
              (match (map-get? vote-delegations { circle-id: circle-id, delegator: (get delegate d1) })
                d2 
                  (match (map-get? vote-delegations { circle-id: circle-id, delegator: (get delegate d2) })
                    d3 (some (get delegate d3))
                    none)
                none)
            none)
          none
        )
      )
    )
  )
)

;; Check for delegation loop (non-recursive, checks 3 levels)
(define-private (has-delegation-loop-check (circle-id uint) (delegate principal) (target principal))
  (let
    (
      (l1 (get-delegate-at-level circle-id delegate u1))
      (l2 (get-delegate-at-level circle-id delegate u2))
      (l3 (get-delegate-at-level circle-id delegate u3))
    )
    (or
      (is-eq (some target) l1)
      (is-eq (some target) l2)
      (is-eq (some target) l3)
    )
  )
)

(define-public (delegate-votes (circle-id uint) (delegate principal))
  (let
    (
      (delegator tx-sender)
      (delegator-power (default-to u100 (map-get? voting-power { circle-id: circle-id, member: delegator })))
      (existing-delegation (map-get? vote-delegations { circle-id: circle-id, delegator: delegator }))
      (delegate-delegated (map-get? delegated-power { circle-id: circle-id, delegate: delegate }))
    )
    (asserts! (is-governance-member circle-id delegator) ERR-NOT-MEMBER)
    (asserts! (is-governance-member circle-id delegate) ERR-NOT-MEMBER)
    (asserts! (not (is-eq delegator delegate)) ERR-SELF-DELEGATION)
    (asserts! (is-none existing-delegation) ERR-ALREADY-DELEGATED)
    
    ;; Check for delegation loops
    (asserts! (not (has-delegation-loop-check circle-id delegate delegator)) ERR-DELEGATION-LOOP)
    
    ;; Record delegation
    (map-set vote-delegations { circle-id: circle-id, delegator: delegator }
      { delegate: delegate, delegated-at: block-height })
    
    ;; Update delegate's power
    (map-set delegated-power { circle-id: circle-id, delegate: delegate }
      {
        total-power: (+ (default-to u0 (match delegate-delegated d (some (get total-power d)) none)) delegator-power),
        delegator-count: (+ (default-to u0 (match delegate-delegated d (some (get delegator-count d)) none)) u1)
      })
    
    (ok true)
  )
)

(define-public (revoke-delegation (circle-id uint))
  (let
    (
      (delegator tx-sender)
      (delegation (unwrap! (map-get? vote-delegations { circle-id: circle-id, delegator: delegator }) 
                           ERR-NOT-DELEGATED))
      (delegate (get delegate delegation))
      (delegator-power (default-to u100 (map-get? voting-power { circle-id: circle-id, member: delegator })))
      (delegate-data (unwrap! (map-get? delegated-power { circle-id: circle-id, delegate: delegate })
                              ERR-NOT-DELEGATED))
    )
    (map-delete vote-delegations { circle-id: circle-id, delegator: delegator })
    
    ;; Update delegate's power
    (map-set delegated-power { circle-id: circle-id, delegate: delegate }
      {
        total-power: (- (get total-power delegate-data) delegator-power),
        delegator-count: (- (get delegator-count delegate-data) u1)
      })
    
    (ok true)
  )
)


;; ============================================
;; Create Proposal
;; ============================================

(define-public (create-proposal 
    (circle-id uint) 
    (proposal-type uint)
    (title (string-ascii 100))
    (description (string-ascii 500))
    (value uint)
    (target-member (optional principal))
    (use-quadratic bool))
  (let
    (
      (proposer tx-sender)
      (proposal-id (+ (var-get proposal-counter) u1))
      (proposer-power (get-effective-voting-power circle-id proposer))
      (current-proposals (default-to (list) (map-get? circle-proposals circle-id)))
    )
    (asserts! (not (contract-call? .stacksusu-admin-v6 is-paused)) ERR-PAUSED)
    (asserts! (is-governance-member circle-id proposer) ERR-NOT-MEMBER)
    (asserts! (and (>= proposal-type PROPOSAL-EXTEND-DEADLINE) 
                   (<= proposal-type PROPOSAL-UPGRADE-CONTRACT))
              ERR-INVALID-PROPOSAL-TYPE)
    
    ;; Create proposal
    (map-set proposals proposal-id
      {
        circle-id: circle-id,
        proposer: proposer,
        proposal-type: proposal-type,
        title: title,
        description: description,
        value: value,
        target-member: target-member,
        votes-for: proposer-power,
        votes-against: u0,
        total-voters: u1,
        total-voting-power: proposer-power,
        status: STATUS-ACTIVE,
        created-at: block-height,
        expires-at: (+ block-height VOTING-PERIOD-BLOCKS),
        queued-at: u0,
        executed-at: u0,
        use-quadratic: use-quadratic
      }
    )
    
    ;; Record proposer's vote
    (map-set votes 
      { proposal-id: proposal-id, voter: proposer }
      { vote: true, voted-at: block-height, weight: proposer-power, delegated-from: none }
    )
    
    ;; Track proposal in circle
    (map-set circle-proposals circle-id
      (unwrap! (as-max-len? (append current-proposals proposal-id) u50) ERR-NOT-AUTHORIZED)
    )
    
    (var-set proposal-counter proposal-id)
    (ok proposal-id)
  )
)


;; ============================================
;; Voting
;; ============================================

(define-public (vote (proposal-id uint) (support bool))
  (let
    (
      (voter tx-sender)
      (proposal (unwrap! (map-get? proposals proposal-id) ERR-PROPOSAL-NOT-FOUND))
      (circle-id (get circle-id proposal))
      (existing-vote (map-get? votes { proposal-id: proposal-id, voter: voter }))
      (raw-power (get-effective-voting-power circle-id voter))
      (vote-weight (if (get use-quadratic proposal)
                     (sqrt-int raw-power)
                     raw-power))
    )
    (asserts! (not (contract-call? .stacksusu-admin-v6 is-paused)) ERR-PAUSED)
    (asserts! (is-governance-member circle-id voter) ERR-NOT-MEMBER)
    (asserts! (is-none existing-vote) ERR-ALREADY-VOTED)
    (asserts! (is-eq (get status proposal) STATUS-ACTIVE) ERR-PROPOSAL-EXPIRED)
    (asserts! (< block-height (get expires-at proposal)) ERR-PROPOSAL-EXPIRED)
    
    ;; Record vote
    (map-set votes 
      { proposal-id: proposal-id, voter: voter }
      { vote: support, voted-at: block-height, weight: vote-weight, delegated-from: none }
    )
    
    ;; Update proposal
    (map-set proposals proposal-id
      (merge proposal {
        votes-for: (if support (+ (get votes-for proposal) vote-weight) (get votes-for proposal)),
        votes-against: (if support (get votes-against proposal) (+ (get votes-against proposal) vote-weight)),
        total-voters: (+ (get total-voters proposal) u1),
        total-voting-power: (+ (get total-voting-power proposal) vote-weight)
      })
    )
    
    (ok vote-weight)
  )
)


;; ============================================
;; Proposal Finalization
;; ============================================

(define-public (finalize-proposal (proposal-id uint) (member-count uint))
  (let
    (
      (proposal (unwrap! (map-get? proposals proposal-id) ERR-PROPOSAL-NOT-FOUND))
      (quorum-required (if (is-eq (get proposal-type proposal) PROPOSAL-EMERGENCY-ACTION)
                         (/ (* member-count EMERGENCY-QUORUM-PERCENT) u100)
                         (/ (* member-count QUORUM-PERCENT) u100)))
      (votes-for (get votes-for proposal))
      (votes-against (get votes-against proposal))
      (total-voters (get total-voters proposal))
    )
    (asserts! (is-eq (get status proposal) STATUS-ACTIVE) ERR-PROPOSAL-NOT-FOUND)
    (asserts! (>= block-height (get expires-at proposal)) ERR-NOT-AUTHORIZED)
    
    (if (< total-voters quorum-required)
      ;; Quorum not met - reject
      (begin
        (map-set proposals proposal-id (merge proposal { status: STATUS-REJECTED }))
        (ok STATUS-REJECTED)
      )
      ;; Check if passed
      (if (> votes-for votes-against)
        ;; Passed - add to timelock queue
        (begin
          (map-set proposals proposal-id (merge proposal { 
            status: STATUS-QUEUED,
            queued-at: block-height
          }))
          (map-set execution-queue proposal-id {
            execute-after: (+ block-height TIMELOCK-BLOCKS),
            execute-before: (+ block-height TIMELOCK-BLOCKS VOTING-PERIOD-BLOCKS)
          })
          (ok STATUS-QUEUED)
        )
        ;; Rejected
        (begin
          (map-set proposals proposal-id (merge proposal { status: STATUS-REJECTED }))
          (ok STATUS-REJECTED)
        )
      )
    )
  )
)


;; ============================================
;; Execution
;; ============================================

(define-public (execute-proposal (proposal-id uint))
  (let
    (
      (proposal (unwrap! (map-get? proposals proposal-id) ERR-PROPOSAL-NOT-FOUND))
      (queue-info (unwrap! (map-get? execution-queue proposal-id) ERR-PROPOSAL-NOT-FOUND))
    )
    (asserts! (is-eq (get status proposal) STATUS-QUEUED) ERR-PROPOSAL-NOT-PASSED)
    (asserts! (>= block-height (get execute-after queue-info)) ERR-TIMELOCK-NOT-EXPIRED)
    (asserts! (< block-height (get execute-before queue-info)) ERR-PROPOSAL-EXPIRED)
    
    ;; Execute based on proposal type
    ;; (Actual execution logic would call other contracts)
    
    (map-set proposals proposal-id (merge proposal {
      status: STATUS-EXECUTED,
      executed-at: block-height
    }))
    
    (map-delete execution-queue proposal-id)
    
    (ok true)
  )
)

(define-public (cancel-proposal (proposal-id uint))
  (let
    (
      (proposal (unwrap! (map-get? proposals proposal-id) ERR-PROPOSAL-NOT-FOUND))
    )
    (asserts! (or (is-eq tx-sender (get proposer proposal))
                  (is-eq tx-sender CONTRACT-OWNER))
              ERR-NOT-AUTHORIZED)
    (asserts! (or (is-eq (get status proposal) STATUS-ACTIVE)
                  (is-eq (get status proposal) STATUS-QUEUED))
              ERR-NOT-AUTHORIZED)
    
    (map-set proposals proposal-id (merge proposal { status: STATUS-CANCELLED }))
    (map-delete execution-queue proposal-id)
    
    (ok true)
  )
)


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

(define-read-only (get-proposal (proposal-id uint))
  (ok (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 (has-voted (proposal-id uint) (voter principal))
  (is-some (map-get? votes { proposal-id: proposal-id, voter: voter }))
)

(define-read-only (get-circle-proposals (circle-id uint))
  (default-to (list) (map-get? circle-proposals circle-id))
)

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

(define-read-only (get-delegation (circle-id uint) (delegator principal))
  (map-get? vote-delegations { circle-id: circle-id, delegator: delegator })
)

(define-read-only (get-delegated-power-info (circle-id uint) (delegate principal))
  (map-get? delegated-power { circle-id: circle-id, delegate: delegate })
)

(define-read-only (get-execution-info (proposal-id uint))
  (map-get? execution-queue proposal-id)
)

(define-read-only (is-proposal-executable (proposal-id uint))
  (match (map-get? execution-queue proposal-id)
    queue-info
      (and 
        (>= block-height (get execute-after queue-info))
        (< block-height (get execute-before queue-info))
      )
    false
  )
)

Functions (26)

FunctionAccessArgs
sqrt-intprivaten: uint
authorize-executorpublicexecutor: principal
revoke-executorpublicexecutor: principal
register-governance-memberpubliccircle-id: uint, member: principal
is-governance-memberread-onlycircle-id: uint, member: principal
set-voting-powerpubliccircle-id: uint, member: principal, power: uint
get-voting-powerread-onlycircle-id: uint, member: principal
get-effective-voting-powerread-onlycircle-id: uint, member: principal
get-delegate-at-levelprivatecircle-id: uint, start: principal, level: uint
has-delegation-loop-checkprivatecircle-id: uint, delegate: principal, target: principal
delegate-votespubliccircle-id: uint, delegate: principal
revoke-delegationpubliccircle-id: uint
create-proposalpubliccircle-id: uint, proposal-type: uint, title: (string-ascii 100
votepublicproposal-id: uint, support: bool
finalize-proposalpublicproposal-id: uint, member-count: uint
execute-proposalpublicproposal-id: uint
cancel-proposalpublicproposal-id: uint
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
has-votedread-onlyproposal-id: uint, voter: principal
get-circle-proposalsread-onlycircle-id: uint
get-proposal-countread-only
get-delegationread-onlycircle-id: uint, delegator: principal
get-delegated-power-inforead-onlycircle-id: uint, delegate: principal
get-execution-inforead-onlyproposal-id: uint
is-proposal-executableread-onlyproposal-id: uint