Source Code

(define-non-fungible-token role-nft uint)

(define-map token-roles
    { token-id: uint, role: (string-ascii 32) }
    { 
        account: principal,
        expires-at: uint,
        revocable: bool,
        data: (buff 256)
    }
)

(define-map role-approvals
    { token-id: uint, grantor: principal, role: (string-ascii 32) }
    principal
)

(define-map token-owner uint principal)

(define-data-var last-token-id uint u0)

(define-constant err-not-owner (err u100))
(define-constant err-not-found (err u101))
(define-constant err-role-expired (err u102))
(define-constant err-not-revocable (err u103))
(define-constant err-not-approved (err u104))

(define-read-only (get-role-data (token-id uint) (role (string-ascii 32)))
    (ok (map-get? token-roles { token-id: token-id, role: role }))
)

(define-read-only (has-role (token-id uint) (role (string-ascii 32)) (account principal))
    (match (map-get? token-roles { token-id: token-id, role: role })
        role-data 
            (ok (and 
                (is-eq (get account role-data) account)
                (> (get expires-at role-data) stacks-block-time)
            ))
        (ok false)
    )
)

(define-read-only (get-role-expiration (token-id uint) (role (string-ascii 32)))
    (match (map-get? token-roles { token-id: token-id, role: role })
        role-data (ok (get expires-at role-data))
        err-not-found
    )
)

(define-public (mint (recipient principal))
    (let
        (
            (token-id (+ (var-get last-token-id) u1))
        )
        (try! (nft-mint? role-nft token-id recipient))
        (map-set token-owner token-id recipient)
        (var-set last-token-id token-id)
        (ok token-id)
    )
)

(define-public (grant-role 
    (token-id uint)
    (role (string-ascii 32))
    (account principal)
    (expires-at uint)
    (revocable bool)
    (data (buff 256))
)
    (let
        (
            (owner (unwrap! (map-get? token-owner token-id) err-not-found))
        )
        (asserts! (is-eq tx-sender owner) err-not-owner)
        (map-set token-roles { token-id: token-id, role: role }
            {
                account: account,
                expires-at: expires-at,
                revocable: revocable,
                data: data
            }
        )
        (ok true)
    )
)

(define-public (revoke-role (token-id uint) (role (string-ascii 32)))
    (let
        (
            (owner (unwrap! (map-get? token-owner token-id) err-not-found))
            (role-data (unwrap! (map-get? token-roles { token-id: token-id, role: role }) err-not-found))
        )
        (asserts! (is-eq tx-sender owner) err-not-owner)
        (asserts! (get revocable role-data) err-not-revocable)
        (map-delete token-roles { token-id: token-id, role: role })
        (ok true)
    )
)

(define-public (approve-role (token-id uint) (role (string-ascii 32)) (operator principal))
    (let
        (
            (owner (unwrap! (map-get? token-owner token-id) err-not-found))
        )
        (asserts! (is-eq tx-sender owner) err-not-owner)
        (map-set role-approvals { token-id: token-id, grantor: tx-sender, role: role } operator)
        (ok true)
    )
)

Functions (7)

FunctionAccessArgs
get-role-dataread-onlytoken-id: uint, role: (string-ascii 32
has-roleread-onlytoken-id: uint, role: (string-ascii 32
get-role-expirationread-onlytoken-id: uint, role: (string-ascii 32
mintpublicrecipient: principal
grant-rolepublictoken-id: uint, role: (string-ascii 32
revoke-rolepublictoken-id: uint, role: (string-ascii 32
approve-rolepublictoken-id: uint, role: (string-ascii 32