;; 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)
)