Skip to content

Instantly share code, notes, and snippets.

@kopcho
Last active January 11, 2025 00:40
Show Gist options
  • Save kopcho/6bbbc508b31997877ca3624e12d2dba7 to your computer and use it in GitHub Desktop.
Save kopcho/6bbbc508b31997877ca3624e12d2dba7 to your computer and use it in GitHub Desktop.
Convex Lisp Dutch Auction Smart Contract
; vim: syntax=clojure
;Updated based on much feedback on proper smart contract logic construction with Gemini 2.0 Flash Experimental
(defn create-dutch-auction []
(deploy
`(do
(import convex.asset :as asset)
(import asset.nft.tokens :as nft)
(def auctions {})
(def AUCTION-FEE-PERCENTAGE 0.001)
(def EXTENSION-WINDOW 300000)
(def EXTENSION-TIME 60000)
(defn validate-ownership [nft-id]
(let [owner (asset/owner nft-id)]
(when (not (= *caller* owner))
(fail :NOT-NFT-OWNER (str "Caller: " *caller* " Owner: " owner)))))
(defn calculate-current-price [auction]
(let [elapsed (- *timestamp* (:start-at auction))
duration (:duration auction)]
(if (>= elapsed duration)
(:ending-price auction)
(let [price-drop (/ (* (- (:starting-price auction) (:ending-price auction)) elapsed) duration)]
(- (:starting-price auction) price-drop)))))
(defn ^:callable create [nft-id starting-price ending-price duration reserve-price metadata-cid]
(validate-ownership nft-id)
(when (not (>= starting-price ending-price)) (fail :INVALID-PRICE-RANGE))
(when (not (> duration 0)) (fail :INVALID-DURATION))
(when (not (>= starting-price reserve-price)) (fail :INVALID-RESERVE-PRICE))
(let [transfer-result (asset/transfer *caller* *address* nft-id)]
(when (:error transfer-result) (fail :NFT-TRANSFER-FAILED transfer-result))
(let [auction-id (hash (str *timestamp* *caller* (rand)))
auction {:id auction-id :nft-id nft-id :seller *caller*
:starting-price starting-price :ending-price ending-price
:duration duration :start-at *timestamp* :active true
:reserve-price reserve-price :metadata-cid metadata-cid}]
(set! auctions (assoc auctions auction-id auction))
(log :AUCTION-CREATED auction-id *caller* nft-id starting-price ending-price duration reserve-price metadata-cid)
auction-id)))
(defn ^:callable get-price [auction-id]
(let [auction (get auctions auction-id)]
(when (not auction) (fail :AUCTION-NOT-FOUND))
(when (not (:active auction)) (fail :AUCTION-ENDED))
(calculate-current-price auction)))
(defn ^:callable buy [auction-id]
(let [auction (get auctions auction-id)]
(when (not auction) (fail :AUCTION-NOT-FOUND))
(when (not (:active auction)) (fail :AUCTION-ENDED))
(when (not (< *timestamp* (+ (:start-at auction) (:duration auction)))) (fail :AUCTION-EXPIRED))
(let [price (calculate-current-price auction) offer *offer*]
(when (< price (:reserve-price auction)) (fail :RESERVE-NOT-MET))
(when (not (>= offer price)) (fail :INSUFFICIENT-PAYMENT price))
(let [time-remaining (- (+ (:start-at auction) (:duration auction)) *timestamp*)]
(when (< time-remaining EXTENSION-WINDOW)
(set! auctions (assoc auctions auction-id (assoc auction :duration (+ (:duration auction) EXTENSION-TIME)))))
(let [fee (* price AUCTION-FEE-PERCENTAGE)
seller-payout (- price fee)
accept-result (asset/accept *address* *caller* (:nft-id auction))]
(when (:error accept-result) (fail :NFT-ACCEPT-FAILED accept-result))
(set! auctions (assoc auctions auction-id (assoc auction :active false :final-price price :winner *caller*)))
(accept price)
(when (> offer price) (transfer *caller* (- offer price)))
(transfer (:seller auction) seller-payout)
(transfer *address* fee)
(log :AUCTION-ENDED auction-id *caller* price (:nft-id auction))
true)))))
(defn ^:callable cancel [auction-id]
(let [auction (get auctions auction-id)]
(when (not auction) (fail :AUCTION-NOT-FOUND))
(when (not (:active auction)) (fail :AUCTION-ENDED))
(when (not (= *caller* (:seller auction))) (fail :NOT-SELLER))
(let [accept-result (asset/accept *address* *caller* (:nft-id auction))]
(when (:error accept-result) (fail :NFT-ACCEPT-FAILED accept-result))
(set! auctions (assoc auctions auction-id (assoc auction :active false)))
(log :AUCTION-CANCELLED auction-id *caller* (:nft-id auction))
true))) ; Corrected: Three closing parentheses here
)))
; this version deploys to success, yay!!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment