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