Source Code

;; fractional-data-nft - Clarity 4
;; Fractionalized ownership of genomic data NFTs

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-NFT-NOT-FOUND (err u101))
(define-constant ERR-ALREADY-FRACTIONALIZED (err u102))
(define-constant ERR-INVALID-SHARES (err u103))
(define-constant ERR-INSUFFICIENT-SHARES (err u104))
(define-constant ERR-NOT-FRACTIONALIZED (err u105))

(define-constant MAX-SHARES u1000000) ;; Maximum fractional shares

(define-map fractionalized-nfts uint
  {
    original-nft-id: uint,
    original-owner: principal,
    total-shares: uint,
    shares-sold: uint,
    share-price: uint,
    fractionalized-at: uint,
    is-active: bool
  }
)

(define-map shareholder-balances { nft-id: uint, holder: principal }
  { shares: uint, acquired-at: uint }
)

(define-map share-transfer-history uint
  {
    nft-id: uint,
    from: principal,
    to: principal,
    shares: uint,
    price: uint,
    timestamp: uint
  }
)

(define-map voting-rights { nft-id: uint, holder: principal }
  { voting-power: uint, has-voted: bool }
)

(define-data-var fraction-counter uint u0)
(define-data-var transfer-counter uint u0)

;; Fractionalize NFT into shares
(define-public (fractionalize-nft
    (original-nft-id uint)
    (total-shares uint)
    (share-price uint))
  (let ((fraction-id (+ (var-get fraction-counter) u1)))
    (asserts! (> total-shares u0) ERR-INVALID-SHARES)
    (asserts! (<= total-shares MAX-SHARES) ERR-INVALID-SHARES)
    (asserts! (> share-price u0) ERR-INVALID-SHARES)
    (map-set fractionalized-nfts fraction-id
      {
        original-nft-id: original-nft-id,
        original-owner: tx-sender,
        total-shares: total-shares,
        shares-sold: u0,
        share-price: share-price,
        fractionalized-at: stacks-block-time,
        is-active: true
      })
    (map-set shareholder-balances { nft-id: fraction-id, holder: tx-sender }
      { shares: total-shares, acquired-at: stacks-block-time })
    (var-set fraction-counter fraction-id)
    (ok fraction-id)))

;; Buy fractional shares
(define-public (buy-shares (nft-id uint) (shares-to-buy uint))
  (let ((fraction (unwrap! (map-get? fractionalized-nfts nft-id) ERR-NFT-NOT-FOUND))
        (owner-balance (unwrap! (map-get? shareholder-balances
                                         { nft-id: nft-id, holder: (get original-owner fraction) })
                               ERR-NOT-AUTHORIZED)))
    (asserts! (get is-active fraction) ERR-NOT-FRACTIONALIZED)
    (asserts! (>= (get shares owner-balance) shares-to-buy) ERR-INSUFFICIENT-SHARES)
    (let ((buyer-balance (default-to { shares: u0, acquired-at: u0 }
                                     (map-get? shareholder-balances { nft-id: nft-id, holder: tx-sender }))))
      ;; Update seller balance
      (map-set shareholder-balances { nft-id: nft-id, holder: (get original-owner fraction) }
        (merge owner-balance { shares: (- (get shares owner-balance) shares-to-buy) }))
      ;; Update buyer balance
      (map-set shareholder-balances { nft-id: nft-id, holder: tx-sender }
        { shares: (+ (get shares buyer-balance) shares-to-buy),
          acquired-at: stacks-block-time })
      ;; Record transfer
      (record-transfer nft-id (get original-owner fraction) tx-sender shares-to-buy (get share-price fraction))
      (ok shares-to-buy))))

;; Transfer shares between holders
(define-public (transfer-shares
    (nft-id uint)
    (recipient principal)
    (shares uint))
  (let ((sender-balance (unwrap! (map-get? shareholder-balances { nft-id: nft-id, holder: tx-sender })
                                ERR-INSUFFICIENT-SHARES))
        (recipient-balance (default-to { shares: u0, acquired-at: u0 }
                                      (map-get? shareholder-balances { nft-id: nft-id, holder: recipient }))))
    (asserts! (>= (get shares sender-balance) shares) ERR-INSUFFICIENT-SHARES)
    (map-set shareholder-balances { nft-id: nft-id, holder: tx-sender }
      (merge sender-balance { shares: (- (get shares sender-balance) shares) }))
    (map-set shareholder-balances { nft-id: nft-id, holder: recipient }
      { shares: (+ (get shares recipient-balance) shares),
        acquired-at: stacks-block-time })
    (record-transfer nft-id tx-sender recipient shares u0)
    (ok true)))

;; Reunify NFT (buy back all shares)
(define-public (reunify-nft (nft-id uint))
  (let ((fraction (unwrap! (map-get? fractionalized-nfts nft-id) ERR-NFT-NOT-FOUND))
        (owner-balance (unwrap! (map-get? shareholder-balances
                                         { nft-id: nft-id, holder: tx-sender })
                               ERR-NOT-AUTHORIZED)))
    (asserts! (is-eq tx-sender (get original-owner fraction)) ERR-NOT-AUTHORIZED)
    (asserts! (is-eq (get shares owner-balance) (get total-shares fraction)) ERR-INSUFFICIENT-SHARES)
    (ok (map-set fractionalized-nfts nft-id (merge fraction { is-active: false })))))

;; Record share transfer
(define-private (record-transfer
    (nft-id uint)
    (from principal)
    (to principal)
    (shares uint)
    (price uint))
  (let ((transfer-id (+ (var-get transfer-counter) u1)))
    (map-set share-transfer-history transfer-id
      {
        nft-id: nft-id,
        from: from,
        to: to,
        shares: shares,
        price: price,
        timestamp: stacks-block-time
      })
    (var-set transfer-counter transfer-id)
    true))

;; Read-only functions
(define-read-only (get-fractionalized-nft (nft-id uint))
  (ok (map-get? fractionalized-nfts nft-id)))

(define-read-only (get-shareholder-balance (nft-id uint) (holder principal))
  (ok (map-get? shareholder-balances { nft-id: nft-id, holder: holder })))

(define-read-only (get-transfer-history (transfer-id uint))
  (ok (map-get? share-transfer-history transfer-id)))

(define-read-only (calculate-ownership-percentage (nft-id uint) (holder principal))
  (let ((fraction (unwrap! (map-get? fractionalized-nfts nft-id) ERR-NFT-NOT-FOUND))
        (balance (unwrap! (map-get? shareholder-balances { nft-id: nft-id, holder: holder })
                         ERR-INSUFFICIENT-SHARES)))
    (ok (/ (* (get shares balance) u100) (get total-shares fraction)))))

;; Clarity 4: principal-destruct?
(define-read-only (validate-holder (holder principal))
  (principal-destruct? holder))

;; Clarity 4: int-to-ascii
(define-read-only (format-nft-id (nft-id uint))
  (ok (int-to-ascii nft-id)))

;; Clarity 4: string-to-uint?
(define-read-only (parse-nft-id (id-str (string-ascii 20)))
  (string-to-uint? id-str))

;; Clarity 4: burn-block-height
(define-read-only (get-bitcoin-block)
  (ok burn-block-height))

Functions (13)

FunctionAccessArgs
fractionalize-nftpublicoriginal-nft-id: uint, total-shares: uint, share-price: uint
buy-sharespublicnft-id: uint, shares-to-buy: uint
transfer-sharespublicnft-id: uint, recipient: principal, shares: uint
reunify-nftpublicnft-id: uint
record-transferprivatenft-id: uint, from: principal, to: principal, shares: uint, price: uint
get-fractionalized-nftread-onlynft-id: uint
get-shareholder-balanceread-onlynft-id: uint, holder: principal
get-transfer-historyread-onlytransfer-id: uint
calculate-ownership-percentageread-onlynft-id: uint, holder: principal
validate-holderread-onlyholder: principal
format-nft-idread-onlynft-id: uint
parse-nft-idread-onlyid-str: (string-ascii 20
get-bitcoin-blockread-only