Source Code

;; Access Control - Role-Based Permissions (Clarity 4)
;; This contract manages roles and permissions for the governance system

;; Constants
(define-constant CONTRACT-OWNER tx-sender)

;; Role constants
(define-constant ROLE-ADMIN u1)
(define-constant ROLE-MODERATOR u2)
(define-constant ROLE-PROPOSER u3)
(define-constant ROLE-VOTER u4)

;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u501))
(define-constant ERR-INVALID-ROLE (err u502))
(define-constant ERR-ROLE-NOT-FOUND (err u503))
(define-constant ERR-USER-NOT-FOUND (err u504))
(define-constant ERR-ALREADY-HAS-ROLE (err u505))

;; Data maps

;; Track user roles with Clarity 4 timestamps
(define-map user-roles
    { user: principal, role: uint }
    {
        granted-at: uint,                    ;; Clarity 4: Unix timestamp
        granted-by: principal,
        active: bool
    }
)

;; Role metadata
(define-map roles
    { role-id: uint }
    {
        name: (string-ascii 50),
        description: (string-utf8 256),
        created-at: uint,                    ;; Clarity 4: Unix timestamp
        active: bool
    }
)

;; Track role counts per user
(define-map user-role-count
    { user: principal }
    uint
)

;; Initialize default roles
(map-set roles
    { role-id: ROLE-ADMIN }
    {
        name: "Admin",
        description: u"Full system access and permissions",
        created-at: stacks-block-time,
        active: true
    }
)

(map-set roles
    { role-id: ROLE-MODERATOR }
    {
        name: "Moderator",
        description: u"Can moderate proposals and users",
        created-at: stacks-block-time,
        active: true
    }
)

(map-set roles
    { role-id: ROLE-PROPOSER }
    {
        name: "Proposer",
        description: u"Can create new proposals",
        created-at: stacks-block-time,
        active: true
    }
)

(map-set roles
    { role-id: ROLE-VOTER }
    {
        name: "Voter",
        description: u"Can vote on proposals",
        created-at: stacks-block-time,
        active: true
    }
)

;; Grant admin role to contract owner
(map-set user-roles
    { user: CONTRACT-OWNER, role: ROLE-ADMIN }
    {
        granted-at: stacks-block-time,
        granted-by: CONTRACT-OWNER,
        active: true
    }
)

;; Read-only functions

;; Check if user has a specific role
(define-read-only (has-role (user principal) (role uint))
    (match (map-get? user-roles { user: user, role: role })
        role-data (ok (get active role-data))
        (ok false)
    )
)

;; Check if user is admin
(define-read-only (is-admin (user principal))
    (has-role user ROLE-ADMIN)
)

;; Check if user is moderator
(define-read-only (is-moderator (user principal))
    (has-role user ROLE-MODERATOR)
)

;; Check if user can create proposals
(define-read-only (can-create-proposal (user principal))
    (ok (or
        (unwrap-panic (has-role user ROLE-ADMIN))
        (unwrap-panic (has-role user ROLE-PROPOSER))
    ))
)

;; Check if user can vote
(define-read-only (can-vote (user principal))
    (ok (or
        (unwrap-panic (has-role user ROLE-ADMIN))
        (or
            (unwrap-panic (has-role user ROLE-VOTER))
            (unwrap-panic (has-role user ROLE-PROPOSER))
        )
    ))
)

;; Get role information
(define-read-only (get-role (role-id uint))
    (ok (map-get? roles { role-id: role-id }))
)

;; Get user role details
(define-read-only (get-user-role (user principal) (role uint))
    (ok (map-get? user-roles { user: user, role: role }))
)

;; Get user role count
(define-read-only (get-user-role-count (user principal))
    (ok (default-to u0 (map-get? user-role-count { user: user })))
)

;; Public functions

;; Grant role to user (only admins)
(define-public (grant-role (user principal) (role uint))
    (begin
        (asserts! (unwrap! (is-admin tx-sender) ERR-NOT-AUTHORIZED) ERR-NOT-AUTHORIZED)
        (asserts! (is-some (map-get? roles { role-id: role })) ERR-INVALID-ROLE)

        ;; Check if user already has role
        (match (map-get? user-roles { user: user, role: role })
            existing-role
                (if (get active existing-role)
                    ERR-ALREADY-HAS-ROLE
                    (begin
                        ;; Reactivate role
                        (map-set user-roles
                            { user: user, role: role }
                            {
                                granted-at: stacks-block-time,   ;; Clarity 4: Unix timestamp
                                granted-by: tx-sender,
                                active: true
                            }
                        )
                        (print {
                            event: "role-granted",
                            user: user,
                            role: role,
                            granted-by: tx-sender,
                            timestamp: stacks-block-time
                        })
                        (ok true)
                    )
                )
            ;; Grant new role
            (begin
                (map-set user-roles
                    { user: user, role: role }
                    {
                        granted-at: stacks-block-time,           ;; Clarity 4: Unix timestamp
                        granted-by: tx-sender,
                        active: true
                    }
                )
                (map-set user-role-count
                    { user: user }
                    (+ (default-to u0 (map-get? user-role-count { user: user })) u1)
                )
                (print {
                    event: "role-granted",
                    user: user,
                    role: role,
                    granted-by: tx-sender,
                    timestamp: stacks-block-time
                })
                (ok true)
            )
        )
    )
)

;; Revoke role from user (only admins)
(define-public (revoke-role (user principal) (role uint))
    (begin
        (asserts! (unwrap! (is-admin tx-sender) ERR-NOT-AUTHORIZED) ERR-NOT-AUTHORIZED)

        (match (map-get? user-roles { user: user, role: role })
            role-data
                (begin
                    (map-set user-roles
                        { user: user, role: role }
                        (merge role-data { active: false })
                    )
                    (print {
                        event: "role-revoked",
                        user: user,
                        role: role,
                        revoked-by: tx-sender,
                        timestamp: stacks-block-time
                    })
                    (ok true)
                )
            ERR-ROLE-NOT-FOUND
        )
    )
)

;; Batch grant roles (only admins)
(define-public (batch-grant-roles (users (list 50 principal)) (role uint))
    (begin
        (asserts! (unwrap! (is-admin tx-sender) ERR-NOT-AUTHORIZED) ERR-NOT-AUTHORIZED)
        (asserts! (is-some (map-get? roles { role-id: role })) ERR-INVALID-ROLE)

        (ok (map grant-role-helper users))
    )
)

;; Helper function for batch granting
(define-private (grant-role-helper (user principal))
    (begin
        (map-set user-roles
            { user: user, role: ROLE-VOTER }
            {
                granted-at: stacks-block-time,
                granted-by: tx-sender,
                active: true
            }
        )
        true
    )
)

Functions (12)

FunctionAccessArgs
has-roleread-onlyuser: principal, role: uint
is-adminread-onlyuser: principal
is-moderatorread-onlyuser: principal
can-create-proposalread-onlyuser: principal
can-voteread-onlyuser: principal
get-roleread-onlyrole-id: uint
get-user-roleread-onlyuser: principal, role: uint
get-user-role-countread-onlyuser: principal
grant-rolepublicuser: principal, role: uint
revoke-rolepublicuser: principal, role: uint
batch-grant-rolespublicusers: (list 50 principal
grant-role-helperprivateuser: principal