Source Code

;; USDCx Escrow Contract for ApexTrader-Stacks
;; Built using patterns from clarity-lang/book and friedger/clarity-smart-contracts
;; Handles deposit, withdrawal, and trade approval for automated trading

;; ============================================
;; Constants
;; ============================================

(define-constant CONTRACT_OWNER tx-sender)

;; Error codes (following common pattern from Clarity Book)
(define-constant ERR_UNAUTHORIZED (err u100))
(define-constant ERR_INSUFFICIENT_BALANCE (err u101))
(define-constant ERR_NOT_FOUND (err u102))
(define-constant ERR_INVALID_AMOUNT (err u103))
(define-constant ERR_EXPIRED (err u104))
(define-constant ERR_ALREADY_EXECUTED (err u105))
(define-constant ERR_PAUSED (err u106))

;; ============================================
;; Data Variables
;; ============================================

(define-data-var next-trade-id uint u1)
(define-data-var paused bool false)

;; ============================================
;; Data Maps
;; ============================================

;; User balances in escrow (in microSTX)
(define-map balances principal uint)

;; Authorized bot operators
(define-map authorized-bots principal bool)

;; Approved trades awaiting execution
(define-map trades
  { id: uint }
  {
    user: principal,
    amount: uint,
    expiry: uint,
    executed: bool
  }
)

;; ============================================
;; Read-Only Functions
;; ============================================

(define-read-only (get-balance (user principal))
  (default-to u0 (map-get? balances user))
)

(define-read-only (get-trade (id uint))
  (map-get? trades { id: id })
)

(define-read-only (is-bot-authorized (bot principal))
  (default-to false (map-get? authorized-bots bot))
)

(define-read-only (get-next-trade-id)
  (var-get next-trade-id)
)

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

;; ============================================
;; Public Functions - User Operations
;; ============================================

;; Deposit STX into escrow
(define-public (deposit (amount uint))
  (begin
    (asserts! (not (var-get paused)) ERR_PAUSED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (map-set balances tx-sender (+ (get-balance tx-sender) amount))
    (ok amount)
  )
)

;; Withdraw STX from escrow
(define-public (withdraw (amount uint))
  (let
    (
      (caller tx-sender)
      (current-balance (get-balance tx-sender))
    )
    (asserts! (not (var-get paused)) ERR_PAUSED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    (asserts! (>= current-balance amount) ERR_INSUFFICIENT_BALANCE)
    (map-set balances caller (- current-balance amount))
    (as-contract (stx-transfer? amount tx-sender caller))
  )
)

;; ============================================
;; Public Functions - Bot Operations
;; ============================================

;; Bot requests trade approval
(define-public (request-trade (user principal) (amount uint) (expiry-blocks uint))
  (let ((trade-id (var-get next-trade-id)))
    (asserts! (not (var-get paused)) ERR_PAUSED)
    (asserts! (is-bot-authorized tx-sender) ERR_UNAUTHORIZED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    (asserts! (>= (get-balance user) amount) ERR_INSUFFICIENT_BALANCE)
    (map-insert trades
      { id: trade-id }
      {
        user: user,
        amount: amount,
        expiry: (+ stacks-block-height expiry-blocks),
        executed: false
      }
    )
    (var-set next-trade-id (+ trade-id u1))
    (ok trade-id)
  )
)

;; Bot executes trade and updates balance
(define-public (execute-trade (id uint) (profit int))
  (let ((trade-data (unwrap! (get-trade id) ERR_NOT_FOUND)))
    (asserts! (not (var-get paused)) ERR_PAUSED)
    (asserts! (is-bot-authorized tx-sender) ERR_UNAUTHORIZED)
    (asserts! (not (get executed trade-data)) ERR_ALREADY_EXECUTED)
    (asserts! (<= stacks-block-height (get expiry trade-data)) ERR_EXPIRED)
    (let
      (
        (user (get user trade-data))
        (current-balance (get-balance user))
        (new-balance (if (< profit 0)
                       (if (>= current-balance (to-uint (* profit -1)))
                         (- current-balance (to-uint (* profit -1)))
                         u0)
                       (+ current-balance (to-uint profit))))
      )
      (map-set balances user new-balance)
      (map-set trades { id: id }
        (merge trade-data { executed: true }))
      (ok true)
    )
  )
)

;; ============================================
;; Public Functions - Admin Operations
;; ============================================

(define-public (authorize-bot (bot principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
    (map-set authorized-bots bot true)
    (ok true)
  )
)

(define-public (revoke-bot (bot principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
    (map-delete authorized-bots bot)
    (ok true)
  )
)

(define-public (set-paused (new-paused-state bool))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
    (var-set paused new-paused-state)
    (ok true)
  )
)

Functions (12)

FunctionAccessArgs
get-next-trade-idread-only
is-pausedread-only
get-balanceread-onlyuser: principal
get-traderead-onlyid: uint
is-bot-authorizedread-onlybot: principal
depositpublicamount: uint
withdrawpublicamount: uint
request-tradepublicuser: principal, amount: uint, expiry-blocks: uint
execute-tradepublicid: uint, profit: int
authorize-botpublicbot: principal
revoke-botpublicbot: principal
set-pausedpublicnew-paused-state: bool