(define-non-fungible-token nestable-nft uint)
(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-PARENT (err u103))
(define-constant ERR-PENDING-LIMIT (err u104))
(define-constant ERR-NOT-PENDING (err u105))
(define-constant ERR-IS-EQUIPPED (err u106))
(define-constant ERR-CIRCULAR-REF (err u107))
(define-data-var token-id-nonce uint u0)
(define-data-var max-pending-children uint u128)
(define-map token-parent uint {parent-id: uint, parent-contract: (optional principal)})
(define-map token-children uint (list 200 {child-id: uint, child-contract: principal}))
(define-map pending-children uint (list 128 {child-id: uint, child-contract: principal}))
(define-map token-uri uint (string-ascii 256))
(define-read-only (get-last-token-id)
(ok (var-get token-id-nonce))
)
(define-read-only (get-token-uri (token uint))
(ok (map-get? token-uri token))
)
(define-read-only (get-owner (token uint))
(ok (nft-get-owner? nestable-nft token))
)
(define-read-only (get-direct-owner (token uint))
(let ((parent-data (default-to {parent-id: u0, parent-contract: none} (map-get? token-parent token))))
(ok {
parent-id: (get parent-id parent-data),
parent-contract: (get parent-contract parent-data),
is-nft: (if (> (get parent-id parent-data) u0) true false)
})
)
)
(define-read-only (get-root-owner (token uint))
(let ((parent-data (unwrap! (map-get? token-parent token) (ok tx-sender))))
(if (is-eq (get parent-id parent-data) u0)
(ok (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND))
(ok (unwrap! (nft-get-owner? nestable-nft (get parent-id parent-data)) ERR-NOT-FOUND))
)
)
)
(define-read-only (get-children (token uint))
(ok (default-to (list) (map-get? token-children token)))
)
(define-read-only (get-pending-children (token uint))
(ok (default-to (list) (map-get? pending-children token)))
)
(define-read-only (is-root (token uint))
(let ((parent-data (default-to {parent-id: u0, parent-contract: none} (map-get? token-parent token))))
(ok (is-eq (get parent-id parent-data) u0))
)
)
(define-read-only (is-leaf (token uint))
(let ((children (default-to (list) (map-get? token-children token))))
(ok (is-eq (len children) u0))
)
)
(define-public (mint (recipient principal) (uri (string-ascii 256)))
(let ((new-id (+ (var-get token-id-nonce) u1)))
(try! (nft-mint? nestable-nft new-id recipient))
(map-set token-uri new-id uri)
(map-set token-parent new-id {parent-id: u0, parent-contract: none})
(var-set token-id-nonce new-id)
(ok new-id)
)
)
(define-public (nest-mint (parent-id uint) (recipient principal) (uri (string-ascii 256)))
(let (
(new-id (+ (var-get token-id-nonce) u1))
(parent-owner (unwrap! (nft-get-owner? nestable-nft parent-id) ERR-NOT-FOUND))
)
(asserts! (is-eq tx-sender parent-owner) ERR-NOT-AUTHORIZED)
(try! (nft-mint? nestable-nft new-id recipient))
(map-set token-uri new-id uri)
(map-set token-parent new-id {parent-id: parent-id, parent-contract: (some tx-sender)})
(var-set token-id-nonce new-id)
(try! (add-pending-child parent-id new-id tx-sender))
(ok new-id)
)
)
(define-public (transfer (token uint) (sender principal) (recipient principal))
(let ((owner (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND)))
(asserts! (or (is-eq tx-sender owner) (is-eq tx-sender sender)) ERR-NOT-AUTHORIZED)
(try! (nft-transfer? nestable-nft token sender recipient))
(ok true)
)
)
(define-public (nest-transfer (token uint) (sender principal) (parent-id uint))
(let ((owner (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND)))
(asserts! (or (is-eq tx-sender owner) (is-eq tx-sender sender)) ERR-NOT-AUTHORIZED)
(asserts! (not (is-eq token parent-id)) ERR-CIRCULAR-REF)
(map-set token-parent token {parent-id: parent-id, parent-contract: (some tx-sender)})
(try! (add-pending-child parent-id token tx-sender))
(ok true)
)
)
(define-public (accept-child (parent-id uint) (child-index uint) (child-id uint) (child-contract principal))
(let (
(parent-owner (unwrap! (nft-get-owner? nestable-nft parent-id) ERR-NOT-FOUND))
(pending (unwrap! (map-get? pending-children parent-id) ERR-NOT-PENDING))
(child-data (unwrap! (element-at pending child-index) ERR-NOT-FOUND))
(current-children (default-to (list) (map-get? token-children parent-id)))
)
(asserts! (is-eq tx-sender parent-owner) ERR-NOT-AUTHORIZED)
(asserts! (is-eq (get child-id child-data) child-id) ERR-NOT-FOUND)
(asserts! (is-eq (get child-contract child-data) child-contract) ERR-NOT-FOUND)
(let (
(before (if (> child-index u0)
(unwrap! (slice? pending u0 child-index) ERR-NOT-FOUND)
(list)))
(after (if (< (+ child-index u1) (len pending))
(unwrap! (slice? pending (+ child-index u1) (len pending)) ERR-NOT-FOUND)
(list)))
(new-pending (unwrap! (as-max-len? (concat before after) u128) ERR-NOT-FOUND))
)
(map-set pending-children parent-id new-pending)
(map-set token-children parent-id (unwrap! (as-max-len? (append current-children child-data) u200) ERR-NOT-FOUND))
(ok true)
)
)
)
(define-public (reject-all-children (parent-id uint) (max-rejections uint))
(let ((parent-owner (unwrap! (nft-get-owner? nestable-nft parent-id) ERR-NOT-FOUND)))
(asserts! (is-eq tx-sender parent-owner) ERR-NOT-AUTHORIZED)
(map-delete pending-children parent-id)
(ok true)
)
)
(define-public (burn (token uint) (max-recursive-burns uint))
(let (
(owner (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND))
(children (default-to (list) (map-get? token-children token)))
)
(asserts! (is-eq tx-sender owner) ERR-NOT-AUTHORIZED)
(asserts! (is-eq (len children) u0) ERR-IS-EQUIPPED)
(try! (nft-burn? nestable-nft token owner))
(map-delete token-parent token)
(map-delete token-uri token)
(ok u0)
)
)
(define-private (add-pending-child (parent-id uint) (child-id uint) (child-contract principal))
(let (
(current-pending (default-to (list) (map-get? pending-children parent-id)))
(new-child {child-id: child-id, child-contract: child-contract})
)
(asserts! (< (len current-pending) (var-get max-pending-children)) ERR-PENDING-LIMIT)
(map-set pending-children parent-id (unwrap! (as-max-len? (append current-pending new-child) u128) ERR-PENDING-LIMIT))
(ok true)
)
)