Source Code

(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_NOT_AUTHORIZED (err u1600))
(define-constant ERR_TOKEN_NOT_FOUND (err u1601))
(define-constant ERR_CANNOT_TRANSFER (err u1602))

(define-non-fungible-token permanent-abt uint)

(define-data-var next-token-id uint u1)

(define-map token-metadata
  uint
  {
    holder: principal,
    issuer: principal,
    credential-type: (string-ascii 128),
    issued-at: uint,
    revoked: bool,
    attributes: (buff 512)
  }
)

(define-map holder-tokens
  principal
  (list 50 uint)
)

(define-read-only (get-contract-hash)
  (contract-hash? .permanent-abt)
)

(define-read-only (get-token-owner (token-id uint))
  (ok (nft-get-owner? permanent-abt token-id))
)

(define-read-only (get-token-metadata (token-id uint))
  (ok (unwrap! (map-get? token-metadata token-id) ERR_TOKEN_NOT_FOUND))
)

(define-read-only (get-holder-tokens (holder principal))
  (ok (default-to (list) (map-get? holder-tokens holder)))
)

(define-public (mint-abt 
  (to principal)
  (credential-type (string-ascii 128))
  (attributes (buff 512))
)
  (let
    (
      (token-id (var-get next-token-id))
    )
    (try! (nft-mint? permanent-abt token-id to))
    (map-set token-metadata token-id {
      holder: to,
      issuer: tx-sender,
      credential-type: credential-type,
      issued-at: stacks-block-time,
      revoked: false,
      attributes: attributes
    })
    (let
      (
        (current-tokens (default-to (list) (map-get? holder-tokens to)))
      )
      (map-set holder-tokens to (unwrap-panic (as-max-len? (append current-tokens token-id) u50)))
    )
    (var-set next-token-id (+ token-id u1))
    (ok token-id)
  )
)

(define-public (revoke-abt (token-id uint))
  (let
    (
      (token-data (unwrap! (map-get? token-metadata token-id) ERR_TOKEN_NOT_FOUND))
    )
    (asserts! (is-eq (get issuer token-data) tx-sender) ERR_NOT_AUTHORIZED)
    (map-set token-metadata token-id (merge token-data {revoked: true}))
    (ok true)
  )
)

(define-public (update-attributes (token-id uint) (new-attributes (buff 512)))
  (let
    (
      (token-data (unwrap! (map-get? token-metadata token-id) ERR_TOKEN_NOT_FOUND))
    )
    (asserts! (is-eq (get issuer token-data) tx-sender) ERR_NOT_AUTHORIZED)
    (map-set token-metadata token-id (merge token-data {attributes: new-attributes}))
    (ok true)
  )
)

(define-public (transfer (token-id uint) (sender principal) (recipient principal))
  (begin
    (err ERR_CANNOT_TRANSFER)
  )
)

(define-read-only (verify-credential (token-id uint))
  (let
    (
      (token-data (unwrap! (map-get? token-metadata token-id) ERR_TOKEN_NOT_FOUND))
    )
    (ok (and (not (get revoked token-data)) (is-some (nft-get-owner? permanent-abt token-id))))
  )
)

(define-read-only (verify-r1-signature (message (buff 32)) (signature (buff 64)) (public-key (buff 33)))
  (ok (secp256r1-verify message signature public-key))
)

(define-read-only (get-time)
  stacks-block-time
)

(define-read-only (check-restrictions)
  (ok (is-ok (contract-hash? .permanent-abt)))
)

Functions (12)

FunctionAccessArgs
get-contract-hashread-only
get-token-ownerread-onlytoken-id: uint
get-token-metadataread-onlytoken-id: uint
get-holder-tokensread-onlyholder: principal
mint-abtpublicto: principal, credential-type: (string-ascii 128
revoke-abtpublictoken-id: uint
update-attributespublictoken-id: uint, new-attributes: (buff 512
transferpublictoken-id: uint, sender: principal, recipient: principal
verify-credentialread-onlytoken-id: uint
verify-r1-signatureread-onlymessage: (buff 32
get-timeread-only
check-restrictionsread-only