Source Code

;; stx10 marketplace https://stx10.com
(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)
(define-trait Wstx10-trait
  (
    (transfer (uint principal principal (optional (buff 34))) (response bool uint))
    (get-name () (response (string-ascii 32) uint))
    (get-symbol () (response (string-ascii 32) uint))
    (get-decimals () (response uint uint))
    (get-balance (principal) (response uint uint))
    (get-total-supply () (response uint uint))
    (get-token-uri () (response (optional (string-utf8 256)) uint))
    (wrap (uint) (response bool uint))
    (unwrap (uint) (response bool uint))
  )
)

(define-constant ERR_NO_AUTHORITY u1001)
(define-constant ERR_INVALID_PRICE u1002)
(define-constant ERR_INVALID_STATE u1003)
(define-constant ERR_INVALID_TOKEN u1004)
(define-constant ERR_INVALID_ORDER u1005)
(define-constant ERR_ORDER_MISMATCH u1006)

(define-constant MIN_PRICE u1000000)
(define-constant MAX_PRICE u1000000000000)
(define-constant MAX_PRICE_PER_TOKEN u1000000000)
(define-constant ORDER_MODE_WTS u1)
(define-constant ORDER_MODE_WTB u2)

(define-data-var m_admin principal tx-sender)
(define-data-var m_fee uint u15) ;; 1.5%
(define-data-var m_last_order_id uint u0)

(define-map map_valid_tokens principal bool)

(define-map map_orders
  uint                  ;; order_id
  {
    mode: uint,         ;; ORDER_MODE_[WTS|WTB]
    state: uint,        ;; 1 valid, 2 cancelled, 3 successful
    block: uint,        ;; update-block
    token: principal,
    offer: principal,
    amount: uint,
    price: uint,
    taker: (optional principal),
  }
)

(define-public (wrap_and_create_order (token <Wstx10-trait>) (amount uint) (price uint))
  (begin
    (try! (contract-call? token wrap amount))
    (create_order ORDER_MODE_WTS token amount price)
  )
)

(define-public (create_order (mode uint) (token <sip-010-trait>) (amount uint) (price uint))
  (let
    (
      (offer tx-sender)
      (order_id (+ (var-get m_last_order_id) u1))
    )
    (asserts! (and (>= mode ORDER_MODE_WTS) (<= mode ORDER_MODE_WTB)) (err ERR_INVALID_ORDER))
    (asserts! (default-to false (map-get? map_valid_tokens (contract-of token))) (err ERR_INVALID_TOKEN))
    (asserts! (and (>= price MIN_PRICE) (<= price MAX_PRICE) (<= (/ price amount) MAX_PRICE_PER_TOKEN)) (err ERR_INVALID_PRICE))
    (if (is-eq mode ORDER_MODE_WTS)
      (try! (contract-call? token transfer (get_real_amount token amount) offer (as-contract tx-sender) none))
      (try! (stx-transfer? price offer (as-contract tx-sender)))
    )
    (map-set map_orders order_id {
      mode: mode,
      state: u1,
      block: block-height,
      token: (contract-of token),
      offer: offer,
      amount: amount,
      price: price,
      taker: none,
    })
    (var-set m_last_order_id order_id)
    (print {
      type: "create_order",
      mode: mode,
      token: (contract-of token),
      amount: amount,
      price: price,
      order_id: order_id,
    })
    (ok true)
  )
)

(define-public (change_price (order_id uint) (token <sip-010-trait>) (amount uint) (new_price uint))
  (let
    (
      (order_info (unwrap! (map-get? map_orders order_id) (err ERR_INVALID_ORDER)))
      (price (get price order_info))
    )
    (try! (check_order order_id token amount none))
    (asserts! (is-eq tx-sender (get offer order_info)) (err ERR_ORDER_MISMATCH))
    (asserts! (and (>= new_price MIN_PRICE) (<= new_price MAX_PRICE) (<= (/ new_price amount) MAX_PRICE_PER_TOKEN)) (err ERR_INVALID_PRICE))
    (and
      (is-eq (get mode order_info) ORDER_MODE_WTB)
      (if (> new_price price)
        (try! (stx-transfer? (- new_price price) tx-sender (as-contract tx-sender)))
        (try! (as-contract (stx-transfer? (- price new_price) tx-sender (get offer order_info)))) ;; fail if equal
      )
    )
    (map-set map_orders order_id (merge order_info {
      block: block-height,
      price: new_price,
    }))
    (print {
      type: "change_price",
      id: order_id,
      new_price: new_price,
    })
    (ok true)
  )
)

(define-public (cancel (order_id uint) (token <sip-010-trait>) (amount uint) (price uint))
  (let
    (
      (order_info (unwrap! (map-get? map_orders order_id) (err ERR_INVALID_ORDER)))
      (offer (get offer order_info))
    )
    (try! (check_order order_id token amount (some price)))
    (asserts! (or (is-eq offer tx-sender) (is-eq tx-sender (var-get m_admin))) (err ERR_NO_AUTHORITY))
    (map-set map_orders order_id (merge order_info {
      state: u2,
      block: block-height,
    }))
    (if (is-eq (get mode order_info) ORDER_MODE_WTS)
      (try! (as-contract (contract-call? token transfer (get_real_amount token amount) tx-sender offer none)))
      (try! (as-contract (stx-transfer? (get price order_info) tx-sender offer)))
    )
    (print {
      type: "cancel",
      id: order_id,
    })
    (ok true)
  )
)

(define-public (bulk_cancel (orders (list 100 { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint })))
  (ok (asserts! (is-eq (len (filter bc orders)) u0) (err ERR_INVALID_STATE)))
)

(define-public (take (order_id uint) (token <sip-010-trait>) (amount uint) (price uint))
  (let
    (
      (taker tx-sender)
      (order_info (unwrap! (map-get? map_orders order_id) (err ERR_INVALID_ORDER)))
      (fee (/ (* (var-get m_fee) price) u1000))
      (remain_stx (- price fee))
    )
    (try! (check_order order_id token amount (some price)))
    ;; (asserts! (not (is-eq taker (get offer order_info))) (err ERR_INVALID_SENDER))
    (asserts! (is-none (get name (unwrap-panic (principal-destruct? contract-caller)))) (err ERR_NO_AUTHORITY))
    (map-set map_orders order_id (merge order_info {
      state: u3,
      block: block-height,
      taker: (some taker),
    }))
    (if (is-eq (get mode order_info) ORDER_MODE_WTS)
      (begin
        (try! (stx-transfer? remain_stx taker (get offer order_info)))
        (try! (stx-transfer? fee taker .stx10-marketplace-fee-collector))
        (try! (as-contract (contract-call? token transfer (get_real_amount token amount) tx-sender taker none)))
      )
      (begin
        (try! (contract-call? token transfer (get_real_amount token amount) taker (get offer order_info) none))
        (try! (as-contract (stx-transfer? fee tx-sender .stx10-marketplace-fee-collector)))
        (try! (as-contract (stx-transfer? remain_stx tx-sender taker)))
      )
    )
    (print {
      type: "take",
      id: order_id,
      taker: taker,
    })
    (ok true)
  )
)

(define-public (bulk_take (orders (list 100 { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint })))
  (ok (map bt orders))
)

(define-public (cancel_and_take (token <sip-010-trait>) (cancel_order_id uint) (cancel_amount uint) (cancel_price uint) (take_order_id uint) (take_amount uint) (take_price uint))
  (begin
    (try! (cancel cancel_order_id token cancel_amount cancel_price))
    (take take_order_id token take_amount take_price)
  )
)

(define-public (set_valid_token (token principal) (valid bool))
  (ok (and (is-eq tx-sender (var-get m_admin)) (map-set map_valid_tokens token valid)))
)

(define-public (set_config (admin principal) (fee uint))
  (ok (and (is-eq tx-sender (var-get m_admin)) (var-set m_admin admin) (var-set m_fee fee)))
)

(define-read-only (get_summary)
  {
    height: block-height,
    admin: (var-get m_admin),
    fee: (var-get m_fee),
    order_id: (var-get m_last_order_id),
  }
)

(define-read-only (is_token_valid (token principal))
  (default-to false (map-get? map_valid_tokens token))
)

(define-read-only (get_order (order_id uint))
  (map-get? map_orders order_id)
)

(define-read-only (get_orders (order_ids (list 25 uint)))
  (map get_order order_ids)
)

(define-private (bc (order { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint }))
  (is-err (cancel (get order_id order) (get token order) (get amount order) (get price order)))
)

(define-private (bt (order { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint }))
  (is-ok (take (get order_id order) (get token order) (get amount order) (get price order)))
)

(define-private (get_real_amount (token <sip-010-trait>) (amount uint))
  (* amount (pow u10 (unwrap-panic (contract-call? token get-decimals))))
)

(define-private (check_order (order_id uint) (token <sip-010-trait>) (amount uint) (price (optional uint)))
  (match (map-get? map_orders order_id) order_info
    (ok (begin
      (asserts! (is-eq (get state order_info) u1) (err ERR_INVALID_STATE))
      (asserts! (is-eq (contract-of token) (get token order_info)) (err ERR_ORDER_MISMATCH))
      (asserts! (is-eq amount (get amount order_info)) (err ERR_ORDER_MISMATCH))
      (asserts! (or (is-none price) (is-eq (unwrap-panic price) (get price order_info))) (err ERR_ORDER_MISMATCH))
    ))
    (err ERR_INVALID_ORDER)
  )
)

Functions (18)

FunctionAccessArgs
wrap_and_create_orderpublictoken: <Wstx10-trait>, amount: uint, price: uint
create_orderpublicmode: uint, token: <sip-010-trait>, amount: uint, price: uint
change_pricepublicorder_id: uint, token: <sip-010-trait>, amount: uint, new_price: uint
cancelpublicorder_id: uint, token: <sip-010-trait>, amount: uint, price: uint
bulk_cancelpublicorders: (list 100 { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint }
takepublicorder_id: uint, token: <sip-010-trait>, amount: uint, price: uint
bulk_takepublicorders: (list 100 { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint }
cancel_and_takepublictoken: <sip-010-trait>, cancel_order_id: uint, cancel_amount: uint, cancel_price: uint, take_order_id: uint, take_amount: uint, take_price: uint
set_valid_tokenpublictoken: principal, valid: bool
set_configpublicadmin: principal, fee: uint
get_summaryread-only
is_token_validread-onlytoken: principal
get_orderread-onlyorder_id: uint
get_ordersread-onlyorder_ids: (list 25 uint
bcprivateorder: { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint }
btprivateorder: { order_id: uint, token: <sip-010-trait>, amount: uint, price: uint }
get_real_amountprivatetoken: <sip-010-trait>, amount: uint
check_orderprivateorder_id: uint, token: <sip-010-trait>, amount: uint, price: (optional uint