Source Code

;; SPoint Market
;; Trustless on-chain escrow service to facilitate SPoint swap among SpaghettiPunk Club members
;;
(define-constant ERR-AMOUNT-TOO-HIGH (err u101))
(define-constant ERR-AMOUNT-TOO-LOW (err u102))
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-SYSTEM-PAUSED (err u402))
(define-constant ERR-MAKET-CAPACITY-EXCEEDED (err u403))
(define-constant MAX-SPOINT-AMOUNT u4200000000)

(define-data-var admin principal tx-sender)
(define-data-var volume-spoint uint u0)
(define-data-var volume-stx uint u0)
(define-data-var order-fee uint u100000)
(define-data-var trade-fee-percent uint u500)
(define-data-var min-spoint-trade-amount uint u1000000)
(define-data-var system-pause bool false)

;; private functions
(define-private (pay-fee) 
  (if (is-eq (var-get admin) tx-sender)
    (ok true)
    (begin
      (try! (stx-transfer? (var-get order-fee) tx-sender (var-get admin)))
      (ok true))))

;; admin functions 
(define-public (change-admin (new-admin principal)) 
    (begin 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (var-set admin new-admin)
        (ok true)))

(define-public (change-order-fee (new-fee uint)) 
    (begin 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (var-set order-fee new-fee)
        (ok true)))

(define-public (modify-order-fee-percent (new-fee uint)) 
    (begin 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (var-set trade-fee-percent new-fee)
        (ok true)))

(define-public (change-min-spoint-trade-amount (new-amount uint)) 
    (begin 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (var-set min-spoint-trade-amount new-amount)
        (ok true)))

(define-public (change-market-capacity (new-capacity uint)) 
    (begin 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (var-set market-capacity new-capacity)
        (ok true)))

(define-public (toggle-pause) 
    (begin 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (var-set system-pause (not (is-paused)))
        (ok true)))

(define-public (admin-cancel-order (spc-id uint)) 
    (let (
        (trade (map-get? orders {spc-id: spc-id}))
        (trade-maker (get maker (unwrap-panic trade)))
        ) 
        (asserts! (is-eq (var-get admin) tx-sender) ERR-NOT-AUTHORIZED)
        (try! (as-contract (contract-call? 'SP1C2K603TGWJGKPT2Z3WWHA0ARM66D352385TTWH.spaghettipunk-club transfer spc-id (as-contract tx-sender) trade-maker)))
        (map-delete orders {spc-id: spc-id})
        (unwrap-panic (delete-order spc-id trade-maker))
        (ok true)))

;; public functions
(define-public (place-order (spc-id uint) (amount uint) (price uint)) 
    (let (
        (balance (unwrap-panic (contract-call? 'SP217FZ8AZYTGPKMERWZ6FYRAK4ZZ6YHMJ7XQXGEV.spoints get-balance spc-id)))
        (paid (pay-fee))
        ) 
        (asserts! (<= amount balance) ERR-AMOUNT-TOO-HIGH)
        (asserts! (>= amount (var-get min-spoint-trade-amount)) ERR-AMOUNT-TOO-LOW)
        (asserts! (not (is-paused)) ERR-SYSTEM-PAUSED)
        (try! (contract-call? 'SP1C2K603TGWJGKPT2Z3WWHA0ARM66D352385TTWH.spaghettipunk-club transfer spc-id tx-sender (as-contract tx-sender)))
        (unwrap-panic (create-order tx-sender spc-id amount price))
        (ok true)))

(define-public (cancel-order (spc-id uint)) 
    (let (
        (trade (map-get? orders {spc-id: spc-id}))
        (trade-maker (get maker (unwrap-panic trade)))
        ) 
        (asserts! (is-eq trade-maker tx-sender) ERR-NOT-AUTHORIZED)
        (try! (as-contract (contract-call? 'SP1C2K603TGWJGKPT2Z3WWHA0ARM66D352385TTWH.spaghettipunk-club transfer spc-id (as-contract tx-sender) trade-maker)))
        (unwrap-panic (delete-order spc-id trade-maker))
        (ok true)))

(define-public (modify-order (spc-id uint) (amount uint) (price uint)) 
    (let (
        (balance (unwrap-panic (contract-call? 'SP217FZ8AZYTGPKMERWZ6FYRAK4ZZ6YHMJ7XQXGEV.spoints get-balance spc-id)))
        (trade (map-get? orders {spc-id: spc-id}))
        (trade-maker (get maker (unwrap-panic trade)))
        ) 
        (asserts! (is-eq trade-maker tx-sender) ERR-NOT-AUTHORIZED)
        (asserts! (<= amount balance) ERR-AMOUNT-TOO-HIGH)
        (unwrap-panic (delete-order spc-id trade-maker))
        (unwrap-panic (create-order tx-sender spc-id amount price))
        (ok true)))

(define-public (fulfil-order (spc-id uint) (receiver-id uint) (purchase-amount uint)) 
    (let (
        (balance (unwrap-panic (contract-call? 'SP217FZ8AZYTGPKMERWZ6FYRAK4ZZ6YHMJ7XQXGEV.spoints get-balance receiver-id)))
        (trade (map-get? orders {spc-id: spc-id}))
        (trade-maker (get maker (unwrap-panic trade)))
        (spoint-amount (get amount (unwrap-panic trade)))
        (spoint-price (get price (unwrap-panic trade)))
        (stx-amount (/ (* purchase-amount spoint-price) u1000000))
        (fee (if (is-eq (var-get admin) tx-sender) u0 (/ (* stx-amount (var-get trade-fee-percent)) u10000)))
        ) 
        (asserts! (<= purchase-amount (- MAX-SPOINT-AMOUNT balance)) ERR-AMOUNT-TOO-HIGH)
        (asserts! (<= purchase-amount spoint-amount) ERR-AMOUNT-TOO-HIGH)
        (try! (stx-transfer? stx-amount tx-sender (as-contract tx-sender)))
        (try! (as-contract (stx-transfer? fee (as-contract tx-sender) (var-get admin))))
        (try! (as-contract (stx-transfer? (- stx-amount fee) (as-contract tx-sender) trade-maker)))
        (try! (as-contract (contract-call? 'SP217FZ8AZYTGPKMERWZ6FYRAK4ZZ6YHMJ7XQXGEV.spoints send spc-id receiver-id purchase-amount)))
        (var-set volume-spoint (+ (var-get volume-spoint) purchase-amount))
        (var-set volume-stx (+ (var-get volume-stx) stx-amount))
        (unwrap-panic (delete-order spc-id trade-maker))
        (if (> (- spoint-amount purchase-amount) (var-get min-spoint-trade-amount)) 
            (unwrap-panic (create-order trade-maker spc-id (- spoint-amount purchase-amount) spoint-price))            
            (try! (as-contract (contract-call? 'SP1C2K603TGWJGKPT2Z3WWHA0ARM66D352385TTWH.spaghettipunk-club transfer spc-id (as-contract tx-sender) trade-maker)))
            )
        (print {action: "market-fulfil-order", buyer: tx-sender, buyer-id: receiver-id, stx-amount: stx-amount, purchase-amount: purchase-amount, price: spoint-price, seller: trade-maker, seller-id: spc-id })
        (ok true)))

;;storage
(define-data-var id-to-remove uint u0)
(define-data-var market-capacity uint u500)
(define-data-var orders-count uint u0)
(define-map address-orders-count principal uint)
(define-map orders { spc-id: uint } {maker: principal, spc-id: uint, amount: uint, price: uint})
(define-data-var orders-list (list 1000 {maker: principal, spc-id: uint, amount: uint, price: uint}) (list ))

;;read only functions 
(define-read-only (get-overall-volume-stx) (var-get volume-stx))

(define-read-only (get-overall-volume-spoint) (var-get volume-spoint))

(define-read-only (is-paused) (var-get system-pause))

(define-read-only (get-orders-list) (var-get orders-list))

(define-read-only (get-market-capacity) (var-get market-capacity))

(define-read-only (get-orders-count) (var-get orders-count))

(define-read-only (get-address-orders-count (address principal)) (default-to u0 (map-get? address-orders-count address)))

(define-read-only (get-open-order-by-id (spc-id uint)) (unwrap-panic (map-get? orders { spc-id: spc-id })))

(define-read-only (get-min-spoint-trade-amount) (var-get min-spoint-trade-amount))

(define-private (create-order (address principal) (id uint) (amount uint) (price uint)) 
    (begin 
        (asserts! (< (get-orders-count) (get-market-capacity)) ERR-MAKET-CAPACITY-EXCEEDED)
        (var-set orders-list (unwrap-panic (as-max-len? (append (var-get orders-list) {maker: address, spc-id: id, amount: amount, price: price}) u1000)))
        (map-set orders {spc-id: id} {maker: address, spc-id: id, amount: amount, price: price})
        (var-set orders-count (+ (get-orders-count) u1))
        (map-set address-orders-count address (+ (get-address-orders-count address) u1))
        (ok true)))

(define-private (delete-order (id uint) (address principal))  
    (begin 
        (var-set id-to-remove id)
        (var-set orders-list (unwrap-panic (as-max-len? (filter is-to-remove (var-get orders-list)) u1000)))
        (map-delete orders {spc-id: id})
        (var-set orders-count (- (get-orders-count) u1))
        (map-set address-orders-count address (- (get-address-orders-count address) u1))
        (ok true)))

(define-private (is-to-remove (trade {maker: principal, spc-id: uint, amount: uint, price: uint}))
  (if (is-eq (get spc-id trade) (var-get id-to-remove)) false true))

Functions (24)

FunctionAccessArgs
pay-feeprivate
change-adminpublicnew-admin: principal
change-order-feepublicnew-fee: uint
modify-order-fee-percentpublicnew-fee: uint
change-min-spoint-trade-amountpublicnew-amount: uint
change-market-capacitypublicnew-capacity: uint
toggle-pausepublic
admin-cancel-orderpublicspc-id: uint
place-orderpublicspc-id: uint, amount: uint, price: uint
cancel-orderpublicspc-id: uint
modify-orderpublicspc-id: uint, amount: uint, price: uint
fulfil-orderpublicspc-id: uint, receiver-id: uint, purchase-amount: uint
get-overall-volume-stxread-only
get-overall-volume-spointread-only
is-pausedread-only
get-orders-listread-only
get-market-capacityread-only
get-orders-countread-only
get-address-orders-countread-onlyaddress: principal
get-open-order-by-idread-onlyspc-id: uint
get-min-spoint-trade-amountread-only
create-orderprivateaddress: principal, id: uint, amount: uint, price: uint
delete-orderprivateid: uint, address: principal
is-to-removeprivatetrade: {maker: principal, spc-id: uint, amount: uint, price: uint}