diff --git a/contracts/time-auction.clar b/contracts/time-auction.clar index b1e2d0a..a69fca5 100644 --- a/contracts/time-auction.clar +++ b/contracts/time-auction.clar @@ -23,6 +23,8 @@ (define-constant ERR_ALREADY_SETTLED (err u6007)) (define-constant ERR_RESERVE_NOT_MET (err u6008)) (define-constant ERR_INVALID_DURATION (err u6009)) +(define-constant ERR_NO_BID_TO_WITHDRAW (err u6010)) +(define-constant ERR_NOT_HIGHEST_BIDDER (err u6011)) ;; Minimum auction duration: 1 hour (define-constant MIN_DURATION u3600) @@ -316,6 +318,71 @@ ) ) +;; Withdraw bid (only if not the highest bidder) +(define-public (withdraw-non-winning-bid (auction-id uint)) + (let + ( + (auction (unwrap! (map-get? auctions { auction-id: auction-id }) ERR_AUCTION_NOT_FOUND)) + (user tx-sender) + ) + ;; Auction must still be active + (asserts! (is-auction-active auction-id) ERR_AUCTION_ENDED) + + ;; Cannot withdraw if you're the highest bidder + (match (get highest-bidder auction) + highest-bidder (asserts! (not (is-eq user highest-bidder)) ERR_NOT_HIGHEST_BIDDER) + true + ) + + ;; Find and refund user's last bid + (let + ( + (user-last-bid (find-user-last-bid auction-id user)) + ) + (asserts! (is-some user-last-bid) ERR_NO_BID_TO_WITHDRAW) + (let + ( + (bid-amount (get amount user-last-bid)) + ) + (begin + ;; Refund the bid amount + (try! (stx-transfer? bid-amount tx-sender user)) + + ;; Update user stats + (update-user-stats-after-withdrawal user) + + ;; Log event + (log-bid-withdrawn auction-id user bid-amount) + + (ok true) + ) + ) + ) + ) +) + + +;; Check if user can withdraw their bid from an auction +(define-read-only (can-withdraw-bid (auction-id uint) (user principal)) + (match (map-get? auctions { auction-id: auction-id }) + auction + (ok (and + (is-auction-active auction-id) + (not (is-eq user (default-to user (get highest-bidder auction)))) + (is-some (find-user-last-bid auction-id user)) + )) + false + ) +) + +;; Get user's last bid amount in an auction +(define-read-only (get-user-last-bid (auction-id uint) (user principal)) + (match (find-user-last-bid auction-id user) + bid (ok (get amount bid)) + none (ok u0) + ) +) + ;; Settle auction after it ends (define-public (settle-auction (auction-id uint)) (let @@ -414,3 +481,49 @@ (ok true) ) ) + +(define-private (find-user-last-bid (auction-id uint) (user principal)) + (let + ( + (auction (unwrap! (map-get? auctions { auction-id: auction-id }) ERR_AUCTION_NOT_FOUND)) + (bid-count (get bid-count auction)) + ) + (find-user-last-bid-helper auction-id user (- bid-count u1)) + ) +) + +(define-private (find-user-last-bid-helper (auction-id uint) (user principal) (index uint)) + (if (>= index u0) + (match (map-get? bid-history { auction-id: auction-id, bid-index: index }) + bid (if (is-eq (get bidder bid) user) + (some bid) + (find-user-last-bid-helper auction-id user (- index u1))) + (find-user-last-bid-helper auction-id user (- index u1)) + ) + none + ) +) + +;; Helper to update user bid stats when withdrawing +(define-private (update-user-stats-after-withdrawal (user principal)) + (let + ( + (user-stat (default-to { active-bids: u0, total-bids: u0, wins: u0 } + (map-get? user-bids { user: user }))) + ) + (if (> (get active-bids user-stat) u0) + (map-set user-bids + { user: user } + (merge user-stat { + active-bids: (- (get active-bids user-stat) u1) + }) + ) + true + ) + ) +) + +;; Add logging event for bid withdrawal +(define-private (log-bid-withdrawn (auction-id uint) (bidder principal) (amount uint)) + (print { event: "bid-withdrawn", auction-id: auction-id, bidder: bidder, amount: amount, timestamp: stacks-block-time }) +)