Source Code

;; Gamin Gaming Platform Smart Contract
;; Handles in-game asset ownership and trading functionality


;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-not-authorized (err u102))
(define-constant err-invalid-input (err u103))
(define-constant err-invalid-price (err u104))
(define-constant max-level u100)
(define-constant max-experience u10000)
(define-constant max-metadata-length u256)
(define-constant max-batch-size u10)  ;; Limit batch operations to prevent potential gas issues

;; Data Variables
(define-map assets 
    { asset-id: uint }
    { owner: principal, metadata-uri: (string-utf8 256), transferable: bool })

(define-map asset-prices
    { asset-id: uint }
    { price: uint })

(define-map player-stats
    { player: principal }
    { experience: uint, level: uint })

(define-map marketplace-listings
    { asset-id: uint }
    { seller: principal, price: uint, listed-at: uint })

;; Asset Counter
(define-data-var asset-counter uint u0)

;; Helper Functions

;; Validate asset exists and return asset data
(define-private (get-asset-checked (asset-id uint))
    (let ((asset (map-get? assets { asset-id: asset-id })))
        (asserts! (and 
                (is-some asset)
                (<= asset-id (var-get asset-counter)))
            err-not-found)
        (ok (unwrap-panic asset))))

;; Validate metadata URI length
(define-private (validate-metadata-uri (uri (string-utf8 256)))
    (let ((uri-length (len uri)))
        (and 
            (> uri-length u0)
            (<= uri-length max-metadata-length))))

;; Public Functions

;; Batch Mint new gaming assets
(define-public (batch-mint-assets 
    (metadata-uris (list 10 (string-utf8 256))) 
    (transferable-list (list 10 bool)))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (and 
            (> (len metadata-uris) u0)
            (<= (len metadata-uris) max-batch-size)
            (is-eq (len metadata-uris) (len transferable-list))) 
            err-invalid-input)
        (let ((minted-assets 
            (map mint-single-asset 
                metadata-uris 
                transferable-list)))
            (ok minted-assets))))

;; Helper function for batch minting
(define-private (mint-single-asset 
    (uri (string-utf8 256))
    (transferable bool))
    (let 
        ((asset-id (+ (var-get asset-counter) u1)))
        (asserts! (validate-metadata-uri uri) err-invalid-input)
        (map-set assets
            { asset-id: asset-id }
            { owner: contract-owner,
              metadata-uri: uri,
              transferable: transferable })
        (var-set asset-counter asset-id)
        (ok asset-id)))

;; Batch Transfer assets
(define-public (batch-transfer-assets 
    (asset-ids (list 10 uint)) 
    (recipients (list 10 principal)))
    (begin
        (asserts! (and 
            (> (len asset-ids) u0)
            (<= (len asset-ids) max-batch-size)
            (is-eq (len asset-ids) (len recipients))) 
            err-invalid-input)
        (let ((transfers 
            (map transfer-single-asset 
                asset-ids 
                recipients)))
            (ok transfers))))

;; Helper function for batch transfer
(define-private (transfer-single-asset 
    (asset-id uint)
    (recipient principal))
    (let 
        ((asset (unwrap-panic (get-asset-checked asset-id))))
        (asserts! (and
                (is-eq (get owner asset) tx-sender)
                (get transferable asset)
                (not (is-eq recipient tx-sender)))  ;; Prevent self-transfers
            err-not-authorized)
        (map-set assets
            { asset-id: asset-id }
            { owner: recipient,
              metadata-uri: (get metadata-uri asset),
              transferable: (get transferable asset) })
        (ok true)))  ;; Changed to return (ok true)


;; Mint single asset
(define-public (mint-asset (metadata-uri (string-utf8 256)) (transferable bool))
    (let
        ((asset-id (+ (var-get asset-counter) u1)))
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (validate-metadata-uri metadata-uri) err-invalid-input)
        (map-set assets
            { asset-id: asset-id }
            { owner: tx-sender,
              metadata-uri: metadata-uri,
              transferable: transferable })
        (var-set asset-counter asset-id)
        (ok asset-id)))

;; Transfer asset ownership
(define-public (transfer-asset (asset-id uint) (recipient principal))
    (begin
        (asserts! (<= asset-id (var-get asset-counter)) err-invalid-input)
        (let ((asset (try! (get-asset-checked asset-id))))
            (asserts! (and
                    (is-eq (get owner asset) tx-sender)
                    (get transferable asset)
                    (not (is-eq recipient tx-sender)))  ;; Prevent self-transfers
                err-not-authorized)
            (map-set assets
                { asset-id: asset-id }
                { owner: recipient,
                  metadata-uri: (get metadata-uri asset),
                  transferable: (get transferable asset) })
            (ok true))))

;; List asset for sale with enhanced marketplace listing
(define-public (list-asset-for-sale (asset-id uint) (price uint))
    (begin
        (asserts! (<= asset-id (var-get asset-counter)) err-invalid-input)
        (let ((asset (try! (get-asset-checked asset-id))))
            (asserts! (and 
                    (is-eq (get owner asset) tx-sender)
                    (> price u0)
                    (get transferable asset))  ;; Ensure asset is transferable
                err-invalid-price)
            (map-set marketplace-listings
                { asset-id: asset-id }
                { seller: tx-sender, 
                  price: price, 
                  listed-at: stacks-block-height })
            (ok true))))

;; Purchase listed asset with enhanced marketplace mechanics
(define-public (purchase-asset (asset-id uint))
    (begin
        (asserts! (<= asset-id (var-get asset-counter)) err-invalid-input)
        (let
            ((asset (try! (get-asset-checked asset-id)))
             (listing (unwrap! (map-get? marketplace-listings { asset-id: asset-id }) err-not-found)))
            (asserts! (and
                    (not (is-eq (get seller listing) tx-sender))
                    (get transferable asset))
                err-not-authorized)
            (try! (stx-transfer? (get price listing) tx-sender (get seller listing)))
            (map-set assets
                { asset-id: asset-id }
                { owner: tx-sender,
                  metadata-uri: (get metadata-uri asset),
                  transferable: (get transferable asset) })
            (map-delete marketplace-listings { asset-id: asset-id })
            (ok true))))

;; Remove asset from marketplace listing
(define-public (delist-asset (asset-id uint))
    (begin
        ;; Validate asset-id is within the range of minted assets
        (asserts! (<= asset-id (var-get asset-counter)) err-invalid-input)
        
        ;; Try to get the listing, return error if not found
        (let ((listing (unwrap! (map-get? marketplace-listings { asset-id: asset-id }) err-not-found)))
            ;; Ensure only the seller can delist
            (asserts! (is-eq tx-sender (get seller listing)) err-not-authorized)
            
            ;; Delete the marketplace listing
            (map-delete marketplace-listings { asset-id: asset-id })
            
            ;; Return success
            (ok true))))

;; Update player stats with validation
(define-public (update-player-stats (experience uint) (level uint))
    (begin
        (asserts! (<= experience max-experience) err-invalid-input)
        (asserts! (<= level max-level) err-invalid-input)
        (map-set player-stats
            { player: tx-sender }
            { experience: experience, level: level })
        (ok true)))

;; Read-only Functions

;; Get asset details
(define-read-only (get-asset-details (asset-id uint))
    (if (<= asset-id (var-get asset-counter))
        (map-get? assets { asset-id: asset-id })
        none))

;; Get marketplace listing details
(define-read-only (get-marketplace-listing (asset-id uint))
    (map-get? marketplace-listings { asset-id: asset-id }))

;; Get player stats
(define-read-only (get-player-stats (player principal))
    (map-get? player-stats { player: player }))

;; Get total assets minted
(define-read-only (get-total-assets)
    (var-get asset-counter))

Functions (16)

FunctionAccessArgs
get-asset-checkedprivateasset-id: uint
validate-metadata-uriprivateuri: (string-utf8 256
batch-mint-assetspublicmetadata-uris: (list 10 (string-utf8 256
mint-single-assetprivateuri: (string-utf8 256
batch-transfer-assetspublicasset-ids: (list 10 uint
transfer-single-assetprivateasset-id: uint, recipient: principal
mint-assetpublicmetadata-uri: (string-utf8 256
transfer-assetpublicasset-id: uint, recipient: principal
list-asset-for-salepublicasset-id: uint, price: uint
purchase-assetpublicasset-id: uint
delist-assetpublicasset-id: uint
update-player-statspublicexperience: uint, level: uint
get-asset-detailsread-onlyasset-id: uint
get-marketplace-listingread-onlyasset-id: uint
get-player-statsread-onlyplayer: principal
get-total-assetsread-only