Source Code

;; CoreMarketPlace Contract
;; This contract manages the creation, updating, and purchasing of listings in the Strade decentralized marketplace.
;; It handles the core logic for marketplace interactions, including listing management and purchase fulfillment.

;; --- Constants ---
;; Defines immutable values used throughout the contract for error handling and configuration.

(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
(define-constant ERR_LISTING_NOT_FOUND (err u101)) ;; Error when a listing cannot be found.
(define-constant ERR_INVALID_PRICE (err u102)) ;; Error for invalid listing prices (e.g., zero or negative).
(define-constant ERR_INVALID_SELLER (err u103)) ;; Error for invalid seller principals.
(define-constant ERR_INSUFFICIENT_BALANCE (err u104)) ;; Error when a buyer has insufficient funds.
(define-constant ERR_LISTING_EXPIRED (err u105)) ;; Error for expired listings.
(define-constant ERR_INVALID_STATUS (err u106)) ;; Error for invalid listing statuses.
(define-constant ERR_NOT_SELLER (err u107)) ;; Error when a user is not the seller of a listing.
(define-constant ERR_ALREADY_PURCHASED (err u108)) ;; Error for listings that have already been sold.
(define-constant ERR_INVALID_INPUT (err u109)) ;; Error for invalid input parameters.
(define-constant ERR_INVALID_DURATION (err u110)) ;; Error for invalid listing durations.
(define-constant ERR_INVALID_LISTING_ID (err u111)) ;; Error for invalid listing IDs.
(define-constant MAX_LISTING_DURATION u52560) ;; Maximum duration of a listing (approximately 1 year).

;; --- Data Maps ---
;; Defines the data structures used to store marketplace information.

(define-map Listings
  { listing-id: uint }
  {
    seller: principal, ;; The principal of the seller.
    name: (string-utf8 64), ;; The name of the listing.
    description: (string-utf8 256), ;; A description of the item.
    price: uint, ;; The price of the listing in micro-STX.
    status: (string-ascii 20), ;; The current status of the listing (e.g., "active", "sold", "cancelled").
    created-at: uint, ;; The block height at which the listing was created.
    expires-at: uint ;; The block height at which the listing expires.
  }
)

;; --- Variables ---
;; Defines mutable variables for tracking the contract's state.

(define-data-var last-listing-id uint u0) ;; Tracks the ID of the last created listing.

;; --- Private Functions ---
;; Helper functions intended for internal use by the contract.

;; Checks if a given price is valid (greater than zero).
(define-private (is-valid-price (price uint))
  (> price u0)
)

;; Checks if a seller principal is valid (not the sender or the contract itself).
(define-private (is-valid-seller (seller principal))
  (and 
    (not (is-eq seller tx-sender))
    (not (is-eq seller (as-contract tx-sender)))
  )
)

;; Checks if a string is within the specified length constraints.
(define-private (is-valid-string (str (string-utf8 256)) (max-len uint))
  (and (>= (len str) u1) (<= (len str) max-len))
)

;; Checks if a listing duration is valid.
(define-private (is-valid-duration (duration uint))
  (and (> duration u0) (<= duration MAX_LISTING_DURATION))
)

;; Checks if a listing ID is valid.
(define-private (is-valid-listing-id (id uint))
  (<= id (var-get last-listing-id))
)

;; Increments and returns the next listing ID.
(define-private (increment-listing-id)
  (let 
    (
      (current-id (var-get last-listing-id))
    )
    (var-set last-listing-id (+ current-id u1))
    (var-get last-listing-id)
  )
)

;; --- Public Functions ---
;; Functions that can be called by any user.

;; Creates a new listing in the marketplace.
;; @param name: The name of the listing.
;; @param description: A description of the item.
;; @param price: The price of the listing in micro-STX.
;; @param duration: The duration of the listing in blocks.
;; @returns (ok uint): The ID of the newly created listing.
(define-public (create-listing (name (string-utf8 64)) (description (string-utf8 256)) (price uint) (duration uint))
  (let
    (
      (listing-id (increment-listing-id))
      (expires-at (+ stacks-block-height duration))
    )
    (asserts! (is-valid-price price) (err ERR_INVALID_PRICE))
    (asserts! (is-valid-string name u64) (err ERR_INVALID_INPUT))
    (asserts! (is-valid-string description u256) (err ERR_INVALID_INPUT))
    (asserts! (is-valid-duration duration) (err ERR_INVALID_DURATION))
    (map-set Listings
      { listing-id: listing-id }
      {
        seller: tx-sender,
        name: name,
        description: description,
        price: price,
        status: "active",
        created-at: stacks-block-height,
        expires-at: expires-at
      }
    )
    (print { event: "listing_created", listing-id: listing-id, seller: tx-sender })
    (ok listing-id)
  )
)

;; Updates an existing listing.
;; @param listing-id: The ID of the listing to update.
;; @param new-price: The new price for the listing.
;; @param new-description: The new description for the listing.
;; @returns (ok bool): True if the update is successful.
(define-public (update-listing (listing-id uint) (new-price uint) (new-description (string-utf8 256)))
  (begin
    (asserts! (is-valid-listing-id listing-id) (err ERR_INVALID_LISTING_ID))
    (let
      (
        (listing (unwrap! (map-get? Listings { listing-id: listing-id }) (err ERR_LISTING_NOT_FOUND)))
      )
      (asserts! (is-eq (get seller listing) tx-sender) (err ERR_NOT_SELLER))
      (asserts! (is-eq (get status listing) "active") (err ERR_INVALID_STATUS))
      (asserts! (is-valid-price new-price) (err ERR_INVALID_PRICE))
      (asserts! (is-valid-string new-description u256) (err ERR_INVALID_INPUT))
      (map-set Listings
        { listing-id: listing-id }
        (merge listing 
          {
            price: new-price,
            description: new-description
          }
        )
      )
      (print { event: "listing_updated", listing-id: listing-id, seller: tx-sender })
      (ok true)
    )
  )
)

;; Cancels a listing.
;; @param listing-id: The ID of the listing to cancel.
;; @returns (ok bool): True if the cancellation is successful.
(define-public (cancel-listing (listing-id uint))
  (begin
    (asserts! (is-valid-listing-id listing-id) (err ERR_INVALID_LISTING_ID))
    (let
      (
        (listing (unwrap! (map-get? Listings { listing-id: listing-id }) (err ERR_LISTING_NOT_FOUND)))
      )
      (asserts! (is-eq (get seller listing) tx-sender) (err ERR_NOT_SELLER))
      (asserts! (is-eq (get status listing) "active") (err ERR_INVALID_STATUS))
      (map-set Listings
        { listing-id: listing-id }
        (merge listing { status: "cancelled" })
      )
      (print { event: "listing_cancelled", listing-id: listing-id, seller: tx-sender })
      (ok true)
    )
  )
)

;; Purchases a listing.
;; @param listing-id: The ID of the listing to purchase.
;; @returns (ok bool): True if the purchase is successful.
(define-public (purchase-listing (listing-id uint))
  (begin
    (asserts! (is-valid-listing-id listing-id) (err ERR_INVALID_LISTING_ID))
    (let
      (
        (listing (unwrap! (map-get? Listings { listing-id: listing-id }) (err ERR_LISTING_NOT_FOUND)))
        (price (get price listing))
        (seller (get seller listing))
      )
      (asserts! (is-eq (get status listing) "active") (err ERR_INVALID_STATUS))
      (asserts! (<= stacks-block-height (get expires-at listing)) (err ERR_LISTING_EXPIRED))
      (asserts! (is-valid-seller seller) (err ERR_INVALID_SELLER))
      (match (stx-transfer? price tx-sender seller)
        success (begin
          (map-set Listings
            { listing-id: listing-id }
            (merge listing { status: "sold" })
          )
          (print { event: "listing_purchased", listing-id: listing-id, buyer: tx-sender, seller: seller, price: price })
          (ok true))
        error (err ERR_INSUFFICIENT_BALANCE))
    )
  )
)

;; --- Read-Only Functions ---
;; Functions for retrieving data from the contract without making state changes.

;; Retrieves a listing by its ID.
;; @param listing-id: The ID of the listing to retrieve.
;; @returns (optional {<listing-data>}): The listing data or none if not found.
(define-read-only (get-listing (listing-id uint))
  (if (is-valid-listing-id listing-id)
    (map-get? Listings { listing-id: listing-id })
    none
  )
)

;; Retrieves the ID of the last created listing.
;; @returns (ok uint): The last listing ID.
(define-read-only (get-last-listing-id)
  (ok (var-get last-listing-id))
)

;; --- Contract Initialization ---
;; Initializes the contract upon deployment.
(begin
  (print "CoreMarketPlace contract initialized")
  (ok true)
)

Functions (12)

FunctionAccessArgs
is-valid-priceprivateprice: uint
is-valid-sellerprivateseller: principal
is-valid-stringprivatestr: (string-utf8 256
is-valid-durationprivateduration: uint
is-valid-listing-idprivateid: uint
increment-listing-idprivate
create-listingpublicname: (string-utf8 64
update-listingpubliclisting-id: uint, new-price: uint, new-description: (string-utf8 256
cancel-listingpubliclisting-id: uint
purchase-listingpubliclisting-id: uint
get-listingread-onlylisting-id: uint
get-last-listing-idread-only