Source Code

;; auction-house.clar
;; Simple auction house with bidding and refunds

;; Constants
(define-constant CONTRACT-ADDRESS .auction-house)
(define-constant ERR-AUCTION-NOT-FOUND (err u100))
(define-constant ERR-NOT-SELLER (err u101))
(define-constant ERR-AUCTION-NOT-ACTIVE (err u102))
(define-constant ERR-AUCTION-STILL-ACTIVE (err u103))
(define-constant ERR-BID-TOO-LOW (err u104))
(define-constant ERR-INVALID-DURATION (err u105))
(define-constant ERR-EMPTY-TITLE (err u106))
(define-constant ERR-NO-FUNDS-TO-WITHDRAW (err u109))

;; Status
(define-constant STATUS-ACTIVE u0)
(define-constant STATUS-ENDED u1)

;; Data Variables
(define-data-var auction-counter uint u0)

;; Maps
(define-map auctions uint 
  {
    seller: principal,
    title: (string-ascii 64),
    description: (string-ascii 256),
    starting-price: uint,
    current-bid: uint,
    current-bidder: (optional principal),
    start-time: uint,
    end-time: uint,
    status: uint,
    bid-count: uint,
    claimed: bool
  }
)

;; Public Functions
(define-public (create-auction (title (string-ascii 64)) (description (string-ascii 256)) (starting-price uint) (duration-hours uint))
  (let (
    (auction-id (+ (var-get auction-counter) u1))
    (end-time (+ stacks-block-time (* duration-hours u3600)))
  )
    (begin
      (asserts! (> (len title) u0) ERR-EMPTY-TITLE)
      (asserts! (> (len description) u0) (err u110))
      (asserts! (> starting-price u0) (err u111))
      (asserts! (> duration-hours u0) ERR-INVALID-DURATION)
      
      (map-set auctions auction-id {
        seller: tx-sender,
        title: title,
        description: description,
        starting-price: starting-price,
        current-bid: u0,
        current-bidder: none,
        start-time: stacks-block-time,
        end-time: end-time,
        status: STATUS-ACTIVE,
        bid-count: u0,
        claimed: false
      })
      
      (var-set auction-counter auction-id)
      (print { event: "auction-created", id: auction-id, seller: tx-sender, end-time: end-time })
      (ok auction-id)
    )
  )
)

(define-public (place-bid (auction-id uint) (bid-amount uint))
  (let (
    (auction (unwrap! (map-get? auctions auction-id) ERR-AUCTION-NOT-FOUND))
    (min-bid (if (> (get current-bid auction) u0) 
                 (+ (get current-bid auction) u1) 
                 (get starting-price auction)))
  )
    (begin
      (asserts! (is-eq (get status auction) STATUS-ACTIVE) ERR-AUCTION-NOT-ACTIVE)
      (asserts! (< stacks-block-time (get end-time auction)) ERR-AUCTION-STILL-ACTIVE)
      (asserts! (>= bid-amount min-bid) ERR-BID-TOO-LOW)
      
      ;; In Clarity, we handle STX transfers explicitly
      (unwrap-panic (stx-transfer? bid-amount tx-sender CONTRACT-ADDRESS))
      
      ;; Refund previous bidder
      (match (get current-bidder auction)
        prev-bidder (try! (stx-transfer? (get current-bid auction) CONTRACT-ADDRESS prev-bidder))
        true
      )
      
      (map-set auctions auction-id (merge auction {
        current-bid: bid-amount,
        current-bidder: (some tx-sender),
        bid-count: (+ (get bid-count auction) u1)
      }))
      
      (print { event: "bid-placed", id: auction-id, bidder: tx-sender, amount: bid-amount })
      (ok true)
    )
  )
)

(define-public (end-auction (auction-id uint))
  (let ((auction (unwrap! (map-get? auctions auction-id) ERR-AUCTION-NOT-FOUND)))
    (begin
      (asserts! (is-eq (get status auction) STATUS-ACTIVE) ERR-AUCTION-NOT-ACTIVE)
      (asserts! (>= stacks-block-time (get end-time auction)) ERR-AUCTION-STILL-ACTIVE)
      
      (map-set auctions auction-id (merge auction { status: STATUS-ENDED }))
      (print { event: "auction-ended", id: auction-id, winner: (get current-bidder auction), price: (get current-bid auction) })
      (ok true)
    )
  )
)

(define-public (withdraw-funds (auction-id uint))
  (let ((auction (unwrap! (map-get? auctions auction-id) ERR-AUCTION-NOT-FOUND)))
    (begin
      (asserts! (is-eq (get status auction) STATUS-ENDED) ERR-AUCTION-NOT-ACTIVE)
      (asserts! (> (get current-bid auction) u0) ERR-NO-FUNDS-TO-WITHDRAW)
      
      (let ((amount (get current-bid auction)))
        (begin
          (map-set auctions auction-id (merge auction { current-bid: u0 }))
          (try! (stx-transfer? amount CONTRACT-ADDRESS (get seller auction)))
          (print { event: "funds-withdrawn", id: auction-id, seller: (get seller auction), amount: amount })
          (ok true)
        )
      )
    )
  )
)

;; Read-only
(define-read-only (get-auction (auction-id uint))
  (map-get? auctions auction-id)
)

;; CLARITY 4 FEATURE: to-ascii? for auction status
(define-read-only (get-auction-status-ascii (auction-id uint))
  (match (map-get? auctions auction-id)
    auction (let (
      (price-ascii (match (to-ascii? (get current-bid auction)) ok-val ok-val err "0"))
      (bid-count-ascii (match (to-ascii? (get bid-count auction)) ok-val ok-val err "0"))
      (status-val (get status auction))
      (status-str (if (is-eq status-val STATUS-ACTIVE) "ACTIVE"
        (if (is-eq status-val STATUS-ENDED) "ENDED"
        "CANCELLED")))
    )
      (ok {
        title: (get title auction),
        current-bid: price-ascii,
        bid-count: bid-count-ascii,
        status: status-str
      })
    )
    (ok {
        title: "Auction not found",
        current-bid: "0",
        bid-count: "0",
        status: "NOT_FOUND"
    })
  )
)

Functions (6)

FunctionAccessArgs
create-auctionpublictitle: (string-ascii 64
place-bidpublicauction-id: uint, bid-amount: uint
end-auctionpublicauction-id: uint
withdraw-fundspublicauction-id: uint
get-auctionread-onlyauction-id: uint
get-auction-status-asciiread-onlyauction-id: uint