Source Code

;; title: KycVault
;; version:
;; summary:
;; description:

;; NFT for access control
(define-non-fungible-token kyc-access uint)

;; Error constants
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-NOT-FOUND (err u101))
(define-constant ERR-ALREADY-EXISTS (err u102))
(define-constant ERR-INVALID-TOKEN (err u103))

;; Data variables
(define-data-var next-token-id uint u1)

;; Maps
(define-map kyc-vault
  principal
  (buff 1024)
)
(define-map access-grants
  uint
  principal
)
(define-map token-owners
  uint
  principal
)
(define-map token-expiry
  uint
  uint
)
(define-map token-metadata
  uint
  {
    created-at: uint,
    purpose: (string-ascii 50),
  }
)
(define-map access-history
  principal
  (list 50 {
    token-id: uint,
    accessed-at: uint,
    accessor: principal,
  })
)

;; Store encrypted KYC data
(define-public (store-kyc-data (encrypted-data (buff 1024)))
  (begin
    (map-set kyc-vault tx-sender encrypted-data)
    (ok true)
  )
)

;; Mint access NFT and grant access to specific principal
(define-public (grant-access (to principal))
  (let ((token-id (var-get next-token-id)))
    (asserts! (is-some (map-get? kyc-vault tx-sender)) ERR-NOT-FOUND)
    (try! (nft-mint? kyc-access token-id to))
    (map-set access-grants token-id tx-sender)
    (map-set token-owners token-id to)
    (var-set next-token-id (+ token-id u1))
    (ok token-id)
  )
)

;; Grant access with expiration and purpose
(define-public (grant-access-with-expiry
    (to principal)
    (expires-at uint)
    (purpose (string-ascii 50))
  )
  (let ((token-id (var-get next-token-id)))
    (asserts! (is-some (map-get? kyc-vault tx-sender)) ERR-NOT-FOUND)
    (asserts! (> expires-at stacks-block-height) ERR-NOT-AUTHORIZED)
    (try! (nft-mint? kyc-access token-id to))
    (map-set access-grants token-id tx-sender)
    (map-set token-owners token-id to)
    (map-set token-expiry token-id expires-at)
    (map-set token-metadata token-id {
      created-at: stacks-block-height,
      purpose: purpose,
    })
    (var-set next-token-id (+ token-id u1))
    (ok token-id)
  )
)

;; Revoke access by burning NFT
(define-public (revoke-access (token-id uint))
  (let (
      (owner (unwrap! (map-get? token-owners token-id) ERR-INVALID-TOKEN))
      (data-owner (unwrap! (map-get? access-grants token-id) ERR-INVALID-TOKEN))
    )
    (asserts! (is-eq tx-sender data-owner) ERR-NOT-AUTHORIZED)
    (try! (nft-burn? kyc-access token-id owner))
    (map-delete access-grants token-id)
    (map-delete token-owners token-id)
    (ok true)
  )
)

;; Access KYC data using NFT (checks expiration)
(define-read-only (get-kyc-data (token-id uint))
  (let (
      (data-owner (unwrap! (map-get? access-grants token-id) ERR-INVALID-TOKEN))
      (expiry (map-get? token-expiry token-id))
    )
    (asserts! (is-eq (some tx-sender) (nft-get-owner? kyc-access token-id))
      ERR-NOT-AUTHORIZED
    )
    (match expiry
      some-expiry (asserts! (< stacks-block-height some-expiry) ERR-NOT-AUTHORIZED)
      true
    )
    (ok (map-get? kyc-vault data-owner))
  )
)

;; Check if user has KYC data stored
(define-read-only (has-kyc-data (user principal))
  (is-some (map-get? kyc-vault user))
)

;; Get NFT owner
(define-read-only (get-token-owner (token-id uint))
  (ok (nft-get-owner? kyc-access token-id))
)

;; Get data owner for a token
(define-read-only (get-data-owner (token-id uint))
  (ok (map-get? access-grants token-id))
)

;; Get token metadata
(define-read-only (get-token-metadata (token-id uint))
  (ok (map-get? token-metadata token-id))
)

;; Get token expiry
(define-read-only (get-token-expiry (token-id uint))
  (ok (map-get? token-expiry token-id))
)

;; Check if token is expired
(define-read-only (is-token-expired (token-id uint))
  (match (map-get? token-expiry token-id)
    some-expiry (ok (>= stacks-block-height some-expiry))
    (ok false)
  )
)

;; Update KYC data (only by data owner)
(define-public (update-kyc-data (encrypted-data (buff 1024)))
  (begin
    (asserts! (is-some (map-get? kyc-vault tx-sender)) ERR-NOT-FOUND)
    (map-set kyc-vault tx-sender encrypted-data)
    (ok true)
  )
)

;; Delete KYC data and revoke all access tokens
(define-public (delete-kyc-data)
  (begin
    (asserts! (is-some (map-get? kyc-vault tx-sender)) ERR-NOT-FOUND)
    (map-delete kyc-vault tx-sender)
    (ok true)
  )
)

;; Transfer access token to another principal
(define-public (transfer-access
    (token-id uint)
    (to principal)
  )
  (let ((owner (unwrap! (nft-get-owner? kyc-access token-id) ERR-INVALID-TOKEN)))
    (asserts! (is-eq tx-sender owner) ERR-NOT-AUTHORIZED)
    (try! (nft-transfer? kyc-access token-id tx-sender to))
    (map-set token-owners token-id to)
    (ok true)
  )
)

;; Batch grant access to multiple principals
(define-public (batch-grant-access (recipients (list 10 principal)))
  (begin
    (asserts! (is-some (map-get? kyc-vault tx-sender)) ERR-NOT-FOUND)
    (ok (map grant-access-helper recipients))
  )
)

(define-private (grant-access-helper (recipient principal))
  (let ((token-id (var-get next-token-id)))
    (match (nft-mint? kyc-access token-id recipient)
      success (begin
        (map-set access-grants token-id tx-sender)
        (map-set token-owners token-id recipient)
        (var-set next-token-id (+ token-id u1))
        token-id
      )
      error u0
    )
  )
)

;; Check if principal has access to specific user's data
(define-read-only (has-access-to
    (data-owner principal)
    (accessor principal)
  )
  (or
    (check-token-access-for u1 data-owner accessor)
    (check-token-access-for u2 data-owner accessor)
    (check-token-access-for u3 data-owner accessor)
    (check-token-access-for u4 data-owner accessor)
    (check-token-access-for u5 data-owner accessor)
  )
)

(define-private (check-token-access-for
    (token-id uint)
    (data-owner principal)
    (accessor principal)
  )
  (and
    (is-eq (map-get? access-grants token-id) (some data-owner))
    (is-eq (nft-get-owner? kyc-access token-id) (some accessor))
  )
)

;; Get total number of access tokens issued
(define-read-only (get-total-tokens)
  (ok (- (var-get next-token-id) u1))
)

;; Emergency pause function (can be extended with admin controls)
(define-data-var contract-paused bool false)

(define-read-only (is-contract-paused)
  (ok (var-get contract-paused))
)

;; Check if user can store KYC data (contract not paused)
(define-read-only (can-store-data)
  (ok (not (var-get contract-paused)))
)

;; Get access history for a user's data
(define-read-only (get-access-history (data-owner principal))
  (ok (map-get? access-history data-owner))
)

;; Extend token expiry (only by data owner)
(define-public (extend-token-expiry
    (token-id uint)
    (new-expiry uint)
  )
  (let ((data-owner (unwrap! (map-get? access-grants token-id) ERR-INVALID-TOKEN)))
    (asserts! (is-eq tx-sender data-owner) ERR-NOT-AUTHORIZED)
    (asserts! (> new-expiry stacks-block-height) ERR-NOT-AUTHORIZED)
    (map-set token-expiry token-id new-expiry)
    (ok true)
  )
)

;; Bulk revoke multiple tokens
(define-public (bulk-revoke-tokens (token-ids (list 10 uint)))
  (ok (map revoke-token-helper token-ids))
)

(define-private (revoke-token-helper (token-id uint))
  (match (revoke-access token-id)
    success true
    error false
  )
)

;; Simple token count by owner (checks first 10 tokens)
(define-read-only (count-tokens-by-owner (owner principal))
  (ok (fold count-token-helper (list u1 u2 u3 u4 u5 u6 u7 u8 u9 u10) {
    owner: owner,
    count: u0,
  }))
)

(define-private (count-token-helper
    (token-id uint)
    (acc {
      owner: principal,
      count: uint,
    })
  )
  (if (is-eq (nft-get-owner? kyc-access token-id) (some (get owner acc)))
    {
      owner: (get owner acc),
      count: (+ (get count acc) u1),
    }
    acc
  )
)

Functions (22)

FunctionAccessArgs
grant-access-with-expirypublicto: principal, expires-at: uint, purpose: (string-ascii 50
revoke-accesspublictoken-id: uint
get-kyc-dataread-onlytoken-id: uint
has-kyc-dataread-onlyuser: principal
get-token-ownerread-onlytoken-id: uint
get-data-ownerread-onlytoken-id: uint
get-token-metadataread-onlytoken-id: uint
get-token-expiryread-onlytoken-id: uint
is-token-expiredread-onlytoken-id: uint
update-kyc-datapublicencrypted-data: (buff 1024
delete-kyc-datapublic
batch-grant-accesspublicrecipients: (list 10 principal
grant-access-helperprivaterecipient: principal
get-total-tokensread-only
is-contract-pausedread-only
can-store-dataread-only
get-access-historyread-onlydata-owner: principal
bulk-revoke-tokenspublictoken-ids: (list 10 uint
revoke-token-helperprivatetoken-id: uint
count-tokens-by-ownerread-onlyowner: principal
grant-accesspublicto: principal
store-kyc-datapublicencrypted-data: (buff 1024