Source Code

;; Marketplace Contract
;; Peer-to-peer marketplace for goods and services
;; Halal - legitimate trade (tijara)
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-LISTING-NOT-FOUND (err u404))
(define-constant ERR-LISTING-SOLD (err u405))
(define-constant ERR-SELF-BUY (err u406))

(define-data-var listing-count uint u0)
(define-data-var total-sales uint u0)

(define-map listings uint {
  seller: principal,
  title: (string-utf8 100),
  price: uint,
  sold: bool,
  buyer: (optional principal),
  created-at: uint
})
(define-map seller-listings principal (list 20 uint))
(define-map seller-revenue principal uint)

(define-public (create-listing (title (string-utf8 100)) (price uint))
  (let ((id (+ (var-get listing-count) u1)))
    (map-set listings id {
      seller: tx-sender, title: title, price: price,
      sold: false, buyer: none, created-at: stacks-block-height
    })
    (var-set listing-count id)
    (ok id)))

(define-public (buy-listing (id uint))
  (let ((listing (unwrap! (map-get? listings id) ERR-LISTING-NOT-FOUND))
        (prev-rev (default-to u0 (map-get? seller-revenue (get seller listing)))))
    (asserts! (not (get sold listing)) ERR-LISTING-SOLD)
    (asserts! (not (is-eq tx-sender (get seller listing))) ERR-SELF-BUY)
    (try! (stx-transfer? (get price listing) tx-sender (get seller listing)))
    (map-set listings id (merge listing { sold: true, buyer: (some tx-sender) }))
    (map-set seller-revenue (get seller listing) (+ prev-rev (get price listing)))
    (var-set total-sales (+ (var-get total-sales) u1))
    (print { event: "sale", id: id, buyer: tx-sender, price: (get price listing) })
    (ok true)))

(define-public (cancel-listing (id uint))
  (let ((listing (unwrap! (map-get? listings id) ERR-LISTING-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get seller listing)) ERR-NOT-AUTHORIZED)
    (asserts! (not (get sold listing)) ERR-LISTING-SOLD)
    (map-set listings id (merge listing { sold: true, buyer: none }))
    (ok true)))

(define-public (update-price (id uint) (new-price uint))
  (let ((listing (unwrap! (map-get? listings id) ERR-LISTING-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get seller listing)) ERR-NOT-AUTHORIZED)
    (asserts! (not (get sold listing)) ERR-LISTING-SOLD)
    (map-set listings id (merge listing { price: new-price }))
    (ok true)))

(define-read-only (get-listing (id uint)) (map-get? listings id))
(define-read-only (get-listing-count) (ok (var-get listing-count)))
(define-read-only (get-total-sales) (ok (var-get total-sales)))
(define-read-only (get-seller-revenue (seller principal))
  (ok (default-to u0 (map-get? seller-revenue seller))))

Functions (8)

FunctionAccessArgs
create-listingpublictitle: (string-utf8 100
buy-listingpublicid: uint
cancel-listingpublicid: uint
update-pricepublicid: uint, new-price: uint
get-listingread-onlyid: uint
get-listing-countread-only
get-total-salesread-only
get-seller-revenueread-onlyseller: principal