;; 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
)
)