-
-
Save kopcho/6bbbc508b31997877ca3624e12d2dba7 to your computer and use it in GitHub Desktop.
Convex Lisp Dutch Auction Smart Contract
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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