Source Code

;; TradeMint - Decentralized Trading Platform
;; A platform for trading digital assets with secure escrow capabilities

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-not-authorized (err u100))
(define-constant err-listing-not-found (err u101))
(define-constant err-invalid-status (err u102))
(define-constant err-insufficient-balance (err u103))
(define-constant err-no-active-offer (err u104))
(define-constant err-listing-expired (err u105))
(define-constant err-invalid-amount (err u106))
(define-constant err-offer-not-found (err u107))
(define-constant err-listing-closed (err u108))
(define-constant err-already-has-offer (err u109))

;; Data Variables
(define-map listings
  uint
  {
    seller: principal,
    asset: (string-ascii 32),
    price: uint,
    status: (string-ascii 10),
    expiry: uint,
    created-at: uint,
  }
)

(define-map offers
  {
    listing-id: uint,
    buyer: principal,
  }
  {
    amount: uint,
    status: (string-ascii 10),
    created-at: uint,
  }
)

(define-data-var listing-nonce uint u0)

;; Events
(define-data-var event-id uint u0)

(define-private (emit-event
    (event-type (string-ascii 20))
    (listing-id uint)
    (principal-data (optional principal))
  )
  (begin
    (var-set event-id (+ (var-get event-id) u1))
    (print {
      event-id: (var-get event-id),
      event-type: event-type,
      listing-id: listing-id,
      principal: principal-data,
    })
  )
)

;; Private Functions
(define-private (increment-nonce)
  (begin
    (var-set listing-nonce (+ (var-get listing-nonce) u1))
    (ok (var-get listing-nonce))
  )
)

(define-private (is-listing-active (listing {
  seller: principal,
  asset: (string-ascii 32),
  price: uint,
  status: (string-ascii 10),
  expiry: uint,
  created-at: uint,
}))
  (and
    (is-eq (get status listing) "active")
    (< stacks-block-time (get expiry listing))
  )
)

;; Public Functions  
(define-public (create-listing
    (asset (string-ascii 32))
    (price uint)
    (expiry uint)
  )
  (let (
      (current-block stacks-block-time)
      (new-id (var-get listing-nonce))
    )
    ;; Increment the nonce first
    (var-set listing-nonce (+ new-id u1))

    ;; Validate parameters
    (asserts! (> expiry current-block) (err err-invalid-status))
    (asserts! (> price u0) (err err-invalid-amount))

    ;; Create the listing
    (map-insert listings new-id {
      seller: tx-sender,
      asset: asset,
      price: price,
      status: "active",
      expiry: expiry,
      created-at: current-block,
    })
    (emit-event "listing-created" new-id none)
    (ok new-id)
  )
)

(define-public (cancel-listing (listing-id uint))
  (let ((listing (unwrap! (map-get? listings listing-id) (err err-listing-not-found))))
    (asserts! (is-eq (get seller listing) tx-sender) (err err-not-authorized))
    (asserts! (is-eq (get status listing) "active") (err err-invalid-status))

    (map-set listings listing-id (merge listing { status: "cancelled" }))
    (emit-event "listing-cancelled" listing-id none)
    (ok true)
  )
)

;; Admin function to recover expired listings and refund related offers
(define-public (cleanup-expired-listing (listing-id uint))
  (let ((listing (unwrap! (map-get? listings listing-id) (err err-listing-not-found))))
    ;; Verify listing is expired and still active
    (asserts!
      (and
        (>= stacks-block-time (get expiry listing))
        (is-eq (get status listing) "active")
      )
      (err err-invalid-status)
    )

    ;; Mark listing as expired
    (map-set listings listing-id (merge listing { status: "expired" }))

    (emit-event "listing-expired" listing-id none)
    (ok true)
  )
)

;; Read-only functions
(define-read-only (get-listing (listing-id uint))
  (map-get? listings listing-id)
)

(define-read-only (get-offer
    (listing-id uint)
    (buyer principal)
  )
  (map-get? offers {
    listing-id: listing-id,
    buyer: buyer,
  })
)

(define-read-only (get-listing-count)
  (var-get listing-nonce)
)

(define-read-only (is-expired (listing-id uint))
  (match (map-get? listings listing-id)
    listing (>= stacks-block-time (get expiry listing))
    false
  )
)

Functions (8)

FunctionAccessArgs
emit-eventprivateevent-type: (string-ascii 20
increment-nonceprivate
create-listingpublicasset: (string-ascii 32
cancel-listingpubliclisting-id: uint
cleanup-expired-listingpubliclisting-id: uint
get-listingread-onlylisting-id: uint
get-listing-countread-only
is-expiredread-onlylisting-id: uint