Source Code

;; title: stx-identity
;; version:
;; summary:
;; description:

;; title: stacks-blacklist
;; version:
;; summary:
;; description:
;; Blacklist Manager Contract

;; Error Codes
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-ALREADY-BLACKLISTED (err u101))
(define-constant ERR-NOT-BLACKLISTED (err u102))
(define-constant ERR-INVALID-ARGUMENT (err u103))
(define-constant ERR-BATCH-OPERATION-FAILED (err u104))
(define-constant ERR-ADMIN-ONLY (err u105))
(define-constant ERR-CANNOT-BLACKLIST-ADMIN (err u106))
(define-constant ERR-INVALID-TIME (err u107))
(define-constant ERR-EXPIRED-BLACKLIST (err u108))

;; Data Variables
(define-data-var contract-admin principal tx-sender)
(define-data-var backup-admin principal tx-sender)
(define-data-var blacklisted-address-count uint u0)
(define-data-var is-contract-active bool true)
(define-data-var last-updated uint stacks-block-height)

;; Maps
(define-map blacklisted-addresses
  principal
  {
    is-blacklisted: bool,
    timestamp: uint,
    expiry: uint,
    blacklist-level: uint,
  }
)
(define-map blacklisted-address-reasons
  principal
  (string-utf8 500)
)
(define-map admins
  principal
  bool
)
(define-map blacklist-appeals
  principal
  {
    status: (string-utf8 20),
    appeal-timestamp: uint,
    appeal-reason: (string-utf8 500),
  }
)

;; Read-Only Functions
(define-read-only (is-address-blacklisted (address principal))
  (match (map-get? blacklisted-addresses address)
    entry (and
      (get is-blacklisted entry)
      (> (get expiry entry) stacks-block-height)
    )
    false
  )
)

(define-read-only (get-blacklist-details (address principal))
  (map-get? blacklisted-addresses address)
)

(define-read-only (get-blacklist-reason-for-address (address principal))
  (default-to u"" (map-get? blacklisted-address-reasons address))
)

(define-read-only (get-total-blacklisted-address-count)
  (var-get blacklisted-address-count)
)

(define-read-only (is-admin (address principal))
  (default-to false (map-get? admins address))
)

(define-read-only (get-appeal-status (address principal))
  (map-get? blacklist-appeals address)
)

(define-read-only (get-contract-status)
  {
    is-active: (var-get is-contract-active),
    last-updated: (var-get last-updated),
    total-blacklisted: (var-get blacklisted-address-count),
  }
)

;; Private Functions
(define-private (is-authorized)
  (or
    (is-eq tx-sender (var-get contract-admin))
    (is-eq tx-sender (var-get backup-admin))
    (is-admin tx-sender)
  )
)

(define-private (calculate-expiry (expiry-blocks (optional uint)))
  (default-to (+ stacks-block-height u1000) expiry-blocks)
)

;; Public Functions
(define-public (set-contract-admin (new-admin principal))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-admin)) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-eq new-admin (var-get contract-admin)))
      ERR-INVALID-ARGUMENT
    )
    (var-set contract-admin new-admin)
    (map-set admins new-admin true)
    (ok true)
  )
)

(define-public (set-backup-admin (new-backup-admin principal))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-admin)) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-eq new-backup-admin (var-get backup-admin)))
      ERR-INVALID-ARGUMENT
    )
    (var-set backup-admin new-backup-admin)
    (map-set admins new-backup-admin true)
    (ok true)
  )
)

(define-public (add-admin (address principal))
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-admin address)) ERR-ALREADY-BLACKLISTED)
    (map-set admins address true)
    (ok true)
  )
)

(define-public (remove-admin (address principal))
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-eq address (var-get contract-admin))) ERR-NOT-AUTHORIZED)
    (map-delete admins address)
    (ok true)
  )
)

(define-public (add-address-to-blacklist
    (address principal)
    (reason (string-utf8 500))
    (blacklist-level uint)
    (expiry-blocks (optional uint))
  )
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-admin address)) ERR-CANNOT-BLACKLIST-ADMIN)
    (asserts! (not (is-address-blacklisted address)) ERR-ALREADY-BLACKLISTED)
    (asserts! (> (len reason) u0) ERR-INVALID-ARGUMENT)
    (asserts! (and (>= blacklist-level u1) (<= blacklist-level u10))
      ERR-INVALID-ARGUMENT
    )
    (let ((safe-expiry (calculate-expiry expiry-blocks)))
      (asserts! (> safe-expiry stacks-block-height) ERR-INVALID-TIME)
      (map-set blacklisted-addresses address {
        is-blacklisted: true,
        timestamp: stacks-block-height,
        expiry: safe-expiry,
        blacklist-level: blacklist-level,
      })
      (map-set blacklisted-address-reasons address reason)
      (var-set blacklisted-address-count
        (+ (var-get blacklisted-address-count) u1)
      )
      (var-set last-updated stacks-block-height)
      (ok true)
    )
  )
)

(define-public (remove-address-from-blacklist (address principal))
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (asserts! (is-address-blacklisted address) ERR-NOT-BLACKLISTED)
    (map-delete blacklisted-addresses address)
    (map-delete blacklisted-address-reasons address)
    (var-set blacklisted-address-count (- (var-get blacklisted-address-count) u1))
    (var-set last-updated stacks-block-height)
    (ok true)
  )
)

(define-public (submit-appeal (reason (string-utf8 500)))
  (begin
    (asserts! (is-address-blacklisted tx-sender) ERR-NOT-BLACKLISTED)
    (asserts! (> (len reason) u0) ERR-INVALID-ARGUMENT)
    (map-set blacklist-appeals tx-sender {
      status: u"pending",
      appeal-timestamp: stacks-block-height,
      appeal-reason: reason,
    })
    (ok true)
  )
)

(define-public (process-appeal
    (address principal)
    (approved bool)
  )
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (asserts! (is-address-blacklisted address) ERR-NOT-BLACKLISTED)
    (let ((appeal-data (unwrap! (map-get? blacklist-appeals address) ERR-NOT-BLACKLISTED)))
      (map-set blacklist-appeals address
        (merge appeal-data { status: (if approved
          u"approved"
          u"rejected"
        ) }
        ))
      (if approved
        (remove-address-from-blacklist address)
        (ok true)
      )
    )
  )
)

(define-public (toggle-contract-status)
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (var-set is-contract-active (not (var-get is-contract-active)))
    (ok true)
  )
)

(define-public (update-blacklist-expiry
    (address principal)
    (new-expiry uint)
  )
  (begin
    (asserts! (is-authorized) ERR-NOT-AUTHORIZED)
    (asserts! (> new-expiry stacks-block-height) ERR-INVALID-TIME)
    (asserts! (is-address-blacklisted address) ERR-NOT-BLACKLISTED)
    (match (map-get? blacklisted-addresses address)
      entry (begin
        (map-set blacklisted-addresses address
          (merge entry { expiry: new-expiry })
        )
        (ok true)
      )
      ERR-NOT-BLACKLISTED
    )
  )
)

;; Prevent STX transfer to the contract
(define-public (receive-stx)
  (err ERR-INVALID-ARGUMENT)
)

Functions (18)

FunctionAccessArgs
is-address-blacklistedread-onlyaddress: principal
get-blacklist-detailsread-onlyaddress: principal
get-blacklist-reason-for-addressread-onlyaddress: principal
get-total-blacklisted-address-countread-only
is-adminread-onlyaddress: principal
get-appeal-statusread-onlyaddress: principal
get-contract-statusread-only
is-authorizedprivate
calculate-expiryprivateexpiry-blocks: (optional uint
set-contract-adminpublicnew-admin: principal
set-backup-adminpublicnew-backup-admin: principal
add-adminpublicaddress: principal
remove-adminpublicaddress: principal
add-address-to-blacklistpublicaddress: principal, reason: (string-utf8 500
remove-address-from-blacklistpublicaddress: principal
submit-appealpublicreason: (string-utf8 500
toggle-contract-statuspublic
receive-stxpublic