real-time-bidding-marketplace

SP3BXJENEWVNCFYGJF75DFS478H1BZJXNZPT84EAD

Source Code

;; title: auction-system
;; version: 1.0.0
;; summary: Real-time bidding for premium placements
;; description: Ad slot auctions, bidding system, instant buyout, and automated winner selection

;; constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-auction-ended (err u102))
(define-constant err-auction-active (err u103))
(define-constant err-bid-too-low (err u104))
(define-constant err-not-winner (err u105))
(define-constant err-already-claimed (err u106))

;; Auction status
(define-constant STATUS-ACTIVE u1)
(define-constant STATUS-ENDED u2)
(define-constant STATUS-CLAIMED u3)
(define-constant STATUS-CANCELLED u4)

;; data vars
(define-data-var auction-nonce uint u0)
(define-data-var min-bid-increment uint u10000) ;; 0.01 STX
(define-data-var platform-auction-fee uint u2) ;; 2%

;; data maps
(define-map auctions
    { auction-id: uint }
    {
        slot-id: uint,
        publisher: principal,
        start-time: uint,
        end-time: uint,
        reserve-price: uint,
        buyout-price: uint,
        current-high-bid: uint,
        high-bidder: (optional principal),
        status: uint,
        total-bids: uint
    }
)

(define-map bids
    { auction-id: uint, bid-id: uint }
    {
        bidder: principal,
        amount: uint,
        timestamp: uint,
        auto-bid-limit: (optional uint)
    }
)

(define-map bid-count
    { auction-id: uint }
    {
        total: uint
    }
)

(define-map slot-inventory
    { slot-id: uint }
    {
        publisher: principal,
        ad-format: (string-ascii 30),
        estimated-impressions: uint,
        reserve-price: uint,
        available: bool
    }
)

(define-map auction-results
    { auction-id: uint }
    {
        winner: principal,
        final-price: uint,
        second-price: uint,
        settled-at: uint,
        claimed: bool
    }
)

(define-map bidder-stats
    { bidder: principal }
    {
        total-bids: uint,
        won-auctions: uint,
        total-spent: uint
    }
)

;; private functions
(define-private (calculate-auction-fee (amount uint))
    (/ (* amount (var-get platform-auction-fee)) u100)
)

;; read only functions
(define-read-only (get-auction (auction-id uint))
    (map-get? auctions { auction-id: auction-id })
)

(define-read-only (get-bid (auction-id uint) (bid-id uint))
    (map-get? bids { auction-id: auction-id, bid-id: bid-id })
)

(define-read-only (get-bid-count (auction-id uint))
    (default-to { total: u0 } (map-get? bid-count { auction-id: auction-id }))
)

(define-read-only (get-slot-inventory (slot-id uint))
    (map-get? slot-inventory { slot-id: slot-id })
)

(define-read-only (get-auction-result (auction-id uint))
    (map-get? auction-results { auction-id: auction-id })
)

(define-read-only (get-bidder-stats (bidder principal))
    (map-get? bidder-stats { bidder: bidder })
)

(define-read-only (is-auction-ended (auction-id uint))
    (match (map-get? auctions { auction-id: auction-id })
        auction (>= stacks-block-time (get end-time auction))
        false
    )
)

;; public functions
(define-public (create-ad-slot-auction
    (slot-id uint)
    (duration uint)
    (reserve-price uint)
    (buyout-price uint)
)
    (let
        (
            (auction-id (+ (var-get auction-nonce) u1))
            (slot (unwrap! (map-get? slot-inventory { slot-id: slot-id }) err-not-found))
        )
        (asserts! (is-eq tx-sender (get publisher slot)) err-owner-only)
        (asserts! (get available slot) err-auction-active)

        (map-set auctions
            { auction-id: auction-id }
            {
                slot-id: slot-id,
                publisher: tx-sender,
                start-time: stacks-block-time,
                end-time: (+ stacks-block-time duration),
                reserve-price: reserve-price,
                buyout-price: buyout-price,
                current-high-bid: u0,
                high-bidder: none,
                status: STATUS-ACTIVE,
                total-bids: u0
            }
        )

        (map-set bid-count { auction-id: auction-id } { total: u0 })
        (map-set slot-inventory
            { slot-id: slot-id }
            (merge slot { available: false })
        )

        (var-set auction-nonce auction-id)
        (ok auction-id)
    )
)

(define-public (place-bid
    (auction-id uint)
    (bid-amount uint)
)
    (let
        (
            (auction (unwrap! (map-get? auctions { auction-id: auction-id }) err-not-found))
            (count-data (get-bid-count auction-id))
            (bid-id (+ (get total count-data) u1))
            (min-bid (+ (get current-high-bid auction) (var-get min-bid-increment)))
        )
        (asserts! (is-eq (get status auction) STATUS-ACTIVE) err-auction-ended)
        (asserts! (< stacks-block-time (get end-time auction)) err-auction-ended)
        (asserts! (>= bid-amount min-bid) err-bid-too-low)
        (asserts! (>= bid-amount (get reserve-price auction)) err-bid-too-low)

        ;; In production, escrow bid amount
        ;; (try! (stx-transfer? bid-amount tx-sender (as-contract tx-sender)))

        (map-set bids
            { auction-id: auction-id, bid-id: bid-id }
            {
                bidder: tx-sender,
                amount: bid-amount,
                timestamp: stacks-block-time,
                auto-bid-limit: none
            }
        )

        (map-set auctions
            { auction-id: auction-id }
            (merge auction {
                current-high-bid: bid-amount,
                high-bidder: (some tx-sender),
                total-bids: (+ (get total-bids auction) u1)
            })
        )

        (map-set bid-count { auction-id: auction-id } { total: bid-id })

        (let
            (
                (bidder-data (default-to
                    { total-bids: u0, won-auctions: u0, total-spent: u0 }
                    (map-get? bidder-stats { bidder: tx-sender })
                ))
            )
            (map-set bidder-stats
                { bidder: tx-sender }
                (merge bidder-data {
                    total-bids: (+ (get total-bids bidder-data) u1)
                })
            )
        )

        (ok bid-id)
    )
)

(define-public (instant-buyout (auction-id uint))
    (let
        (
            (auction (unwrap! (map-get? auctions { auction-id: auction-id }) err-not-found))
        )
        (asserts! (is-eq (get status auction) STATUS-ACTIVE) err-auction-ended)

        ;; In production, transfer buyout price
        ;; (try! (stx-transfer? (get buyout-price auction) tx-sender (get publisher auction)))

        (map-set auctions
            { auction-id: auction-id }
            (merge auction {
                current-high-bid: (get buyout-price auction),
                high-bidder: (some tx-sender),
                status: STATUS-ENDED
            })
        )

        (map-set auction-results
            { auction-id: auction-id }
            {
                winner: tx-sender,
                final-price: (get buyout-price auction),
                second-price: (get current-high-bid auction),
                settled-at: stacks-block-time,
                claimed: false
            }
        )

        (ok true)
    )
)

(define-public (execute-auction (auction-id uint))
    (let
        (
            (auction (unwrap! (map-get? auctions { auction-id: auction-id }) err-not-found))
            (winner (unwrap! (get high-bidder auction) err-not-found))
            (count-data (get-bid-count auction-id))
            (second-highest-bid (if (> (get total count-data) u1)
                (get-second-highest-bid auction-id (get current-high-bid auction))
                (get reserve-price auction)
            ))
            (final-price (if (< second-highest-bid (get current-high-bid auction))
                (+ second-highest-bid (var-get min-bid-increment))
                (get current-high-bid auction)
            ))
        )
        (asserts! (is-eq (get status auction) STATUS-ACTIVE) err-auction-ended)
        (asserts! (>= stacks-block-time (get end-time auction)) err-auction-active)

        (map-set auctions
            { auction-id: auction-id }
            (merge auction { status: STATUS-ENDED })
        )

        (map-set auction-results
            { auction-id: auction-id }
            {
                winner: winner,
                final-price: final-price,
                second-price: second-highest-bid,
                settled-at: stacks-block-time,
                claimed: false
            }
        )

        (let
            (
                (bidder-data (unwrap! (map-get? bidder-stats { bidder: winner }) err-not-found))
            )
            (map-set bidder-stats
                { bidder: winner }
                (merge bidder-data {
                    won-auctions: (+ (get won-auctions bidder-data) u1),
                    total-spent: (+ (get total-spent bidder-data) final-price)
                })
            )
        )

        (ok final-price)
    )
)

(define-public (claim-auction-slot (auction-id uint))
    (let
        (
            (result (unwrap! (map-get? auction-results { auction-id: auction-id }) err-not-found))
            (auction (unwrap! (map-get? auctions { auction-id: auction-id }) err-not-found))
        )
        (asserts! (is-eq tx-sender (get winner result)) err-not-winner)
        (asserts! (not (get claimed result)) err-already-claimed)

        (map-set auction-results
            { auction-id: auction-id }
            (merge result { claimed: true })
        )

        (map-set auctions
            { auction-id: auction-id }
            (merge auction { status: STATUS-CLAIMED })
        )

        (ok true)
    )
)

(define-public (register-ad-slot
    (slot-id uint)
    (ad-format (string-ascii 30))
    (estimated-impressions uint)
    (reserve-price uint)
)
    (begin
        (map-set slot-inventory
            { slot-id: slot-id }
            {
                publisher: tx-sender,
                ad-format: ad-format,
                estimated-impressions: estimated-impressions,
                reserve-price: reserve-price,
                available: true
            }
        )
        (ok true)
    )
)

;; Helper function to get second highest bid (simplified)
(define-private (get-second-highest-bid (auction-id uint) (highest uint))
    u0 ;; Simplified - would iterate through bids
)

;; Admin functions
(define-public (set-min-bid-increment (new-increment uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set min-bid-increment new-increment)
        (ok true)
    )
)

(define-public (cancel-auction (auction-id uint))
    (let
        (
            (auction (unwrap! (map-get? auctions { auction-id: auction-id }) err-not-found))
        )
        (asserts! (is-eq tx-sender (get publisher auction)) err-owner-only)
        (asserts! (is-eq (get status auction) STATUS-ACTIVE) err-auction-ended)

        (map-set auctions
            { auction-id: auction-id }
            (merge auction { status: STATUS-CANCELLED })
        )

        (ok true)
    )
)

Functions (15)

FunctionAccessArgs
calculate-auction-feeprivateamount: uint
get-auctionread-onlyauction-id: uint
get-bidread-onlyauction-id: uint, bid-id: uint
get-bid-countread-onlyauction-id: uint
get-slot-inventoryread-onlyslot-id: uint
get-auction-resultread-onlyauction-id: uint
get-bidder-statsread-onlybidder: principal
is-auction-endedread-onlyauction-id: uint
instant-buyoutpublicauction-id: uint
execute-auctionpublicauction-id: uint
claim-auction-slotpublicauction-id: uint
register-ad-slotpublicslot-id: uint, ad-format: (string-ascii 30
get-second-highest-bidprivateauction-id: uint, highest: uint
set-min-bid-incrementpublicnew-increment: uint
cancel-auctionpublicauction-id: uint