Source Code

;; Stacks AMM DEX v2 - Constant Product Market Maker
;; Simplified version without STX transfers in deploy

;; Constants
(define-constant contract-owner tx-sender)
(define-constant treasury 'SP2PEBKJ2W1ZDDF2QQ6Y4FXKZEDPT9J9R2NKD9WJB)
(define-constant err-owner-only (err u100))
(define-constant err-insufficient-balance (err u101))
(define-constant err-insufficient-liquidity (err u102))
(define-constant err-slippage-exceeded (err u103))
(define-constant err-zero-amount (err u104))
(define-constant err-pool-not-found (err u106))

;; Fee: 0.3% (30 basis points)
(define-constant swap-fee u30)
(define-constant fee-denominator u10000)

;; Pool Data
(define-data-var pool-count uint u0)
(define-data-var total-volume uint u0)
(define-data-var total-fees-collected uint u0)

;; Pool storage
(define-map pools uint
  {
    token-x-reserve: uint,
    token-y-reserve: uint,
    lp-token-supply: uint,
    k-last: uint,
    created-block: uint
  }
)

;; LP token balances per pool
(define-map lp-balances { pool-id: uint, owner: principal } uint)

;; User stats
(define-map user-stats principal
  {
    total-swaps: uint,
    total-volume: uint,
    total-liquidity-provided: uint
  }
)

;; Read-only functions
(define-read-only (get-pool (pool-id uint))
  (map-get? pools pool-id)
)

(define-read-only (get-lp-balance (pool-id uint) (owner principal))
  (default-to u0 (map-get? lp-balances { pool-id: pool-id, owner: owner }))
)

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

(define-read-only (get-total-volume)
  (var-get total-volume)
)

(define-read-only (get-user-stats (user principal))
  (default-to 
    { total-swaps: u0, total-volume: u0, total-liquidity-provided: u0 }
    (map-get? user-stats user)
  )
)

;; Calculate output amount using constant product formula
(define-read-only (get-amount-out (amount-in uint) (reserve-in uint) (reserve-out uint))
  (let (
    (amount-in-with-fee (/ (* amount-in (- fee-denominator swap-fee)) fee-denominator))
    (numerator (* amount-in-with-fee reserve-out))
    (denominator (+ reserve-in amount-in-with-fee))
  )
    (/ numerator denominator)
  )
)

;; Get price of token X in terms of token Y
(define-read-only (get-price (pool-id uint))
  (match (map-get? pools pool-id)
    pool
    (if (> (get token-x-reserve pool) u0)
      (ok (/ (* (get token-y-reserve pool) u1000000) (get token-x-reserve pool)))
      (err u0)
    )
    (err u0)
  )
)

;; Create a new liquidity pool (simplified - no transfers)
(define-public (create-pool (initial-x uint) (initial-y uint))
  (let (
    (pool-id (var-get pool-count))
    (initial-lp (* initial-x initial-y))
  )
    (asserts! (> initial-x u0) err-zero-amount)
    (asserts! (> initial-y u0) err-zero-amount)
    
    ;; Create pool (accounting only)
    (map-set pools pool-id {
      token-x-reserve: initial-x,
      token-y-reserve: initial-y,
      lp-token-supply: initial-lp,
      k-last: (* initial-x initial-y),
      created-block: stacks-block-height
    })
    
    ;; Mint LP tokens to creator
    (map-set lp-balances { pool-id: pool-id, owner: tx-sender } initial-lp)
    
    ;; Update stats
    (var-set pool-count (+ pool-id u1))
    (map-set user-stats tx-sender 
      (merge (get-user-stats tx-sender)
        { total-liquidity-provided: (+ (get total-liquidity-provided (get-user-stats tx-sender)) initial-x) }
      )
    )
    
    (ok { pool-id: pool-id, lp-tokens: initial-lp })
  )
)

;; Add liquidity to existing pool
(define-public (add-liquidity (pool-id uint) (amount-x uint) (amount-y uint) (min-lp uint))
  (match (map-get? pools pool-id)
    pool
    (let (
      (reserve-x (get token-x-reserve pool))
      (reserve-y (get token-y-reserve pool))
      (lp-supply (get lp-token-supply pool))
      (lp-x (/ (* amount-x lp-supply) reserve-x))
      (lp-y (/ (* amount-y lp-supply) reserve-y))
      (lp-minted (if (< lp-x lp-y) lp-x lp-y))
    )
      (asserts! (>= lp-minted min-lp) err-slippage-exceeded)
      
      ;; Update pool
      (map-set pools pool-id 
        (merge pool {
          token-x-reserve: (+ reserve-x amount-x),
          token-y-reserve: (+ reserve-y amount-y),
          lp-token-supply: (+ lp-supply lp-minted),
          k-last: (* (+ reserve-x amount-x) (+ reserve-y amount-y))
        })
      )
      
      ;; Mint LP tokens
      (map-set lp-balances { pool-id: pool-id, owner: tx-sender }
        (+ (get-lp-balance pool-id tx-sender) lp-minted)
      )
      
      (ok { lp-minted: lp-minted })
    )
    err-pool-not-found
  )
)

;; Remove liquidity from pool
(define-public (remove-liquidity (pool-id uint) (lp-amount uint) (min-x uint) (min-y uint))
  (match (map-get? pools pool-id)
    pool
    (let (
      (user-lp (get-lp-balance pool-id tx-sender))
      (lp-supply (get lp-token-supply pool))
      (reserve-x (get token-x-reserve pool))
      (reserve-y (get token-y-reserve pool))
      (amount-x (/ (* lp-amount reserve-x) lp-supply))
      (amount-y (/ (* lp-amount reserve-y) lp-supply))
    )
      (asserts! (>= user-lp lp-amount) err-insufficient-balance)
      (asserts! (>= amount-x min-x) err-slippage-exceeded)
      (asserts! (>= amount-y min-y) err-slippage-exceeded)
      
      ;; Update pool
      (map-set pools pool-id 
        (merge pool {
          token-x-reserve: (- reserve-x amount-x),
          token-y-reserve: (- reserve-y amount-y),
          lp-token-supply: (- lp-supply lp-amount),
          k-last: (* (- reserve-x amount-x) (- reserve-y amount-y))
        })
      )
      
      ;; Burn LP tokens
      (map-set lp-balances { pool-id: pool-id, owner: tx-sender } (- user-lp lp-amount))
      
      (ok { amount-x: amount-x, amount-y: amount-y })
    )
    err-pool-not-found
  )
)

;; Swap token X for token Y
(define-public (swap-x-for-y (pool-id uint) (amount-in uint) (min-out uint))
  (match (map-get? pools pool-id)
    pool
    (let (
      (reserve-x (get token-x-reserve pool))
      (reserve-y (get token-y-reserve pool))
      (amount-out (get-amount-out amount-in reserve-x reserve-y))
      (fee-amount (/ (* amount-in swap-fee) fee-denominator))
    )
      (asserts! (> amount-in u0) err-zero-amount)
      (asserts! (>= amount-out min-out) err-slippage-exceeded)
      (asserts! (< amount-out reserve-y) err-insufficient-liquidity)
      
      ;; Update pool reserves
      (map-set pools pool-id 
        (merge pool {
          token-x-reserve: (+ reserve-x amount-in),
          token-y-reserve: (- reserve-y amount-out),
          k-last: (* (+ reserve-x amount-in) (- reserve-y amount-out))
        })
      )
      
      ;; Update stats
      (var-set total-volume (+ (var-get total-volume) amount-in))
      (var-set total-fees-collected (+ (var-get total-fees-collected) fee-amount))
      (map-set user-stats tx-sender 
        (merge (get-user-stats tx-sender)
          { 
            total-swaps: (+ (get total-swaps (get-user-stats tx-sender)) u1),
            total-volume: (+ (get total-volume (get-user-stats tx-sender)) amount-in)
          }
        )
      )
      
      (ok { amount-in: amount-in, amount-out: amount-out, fee: fee-amount })
    )
    err-pool-not-found
  )
)

;; Swap token Y for token X
(define-public (swap-y-for-x (pool-id uint) (amount-in uint) (min-out uint))
  (match (map-get? pools pool-id)
    pool
    (let (
      (reserve-x (get token-x-reserve pool))
      (reserve-y (get token-y-reserve pool))
      (amount-out (get-amount-out amount-in reserve-y reserve-x))
      (fee-amount (/ (* amount-in swap-fee) fee-denominator))
    )
      (asserts! (> amount-in u0) err-zero-amount)
      (asserts! (>= amount-out min-out) err-slippage-exceeded)
      (asserts! (< amount-out reserve-x) err-insufficient-liquidity)
      
      ;; Update pool reserves
      (map-set pools pool-id 
        (merge pool {
          token-x-reserve: (- reserve-x amount-out),
          token-y-reserve: (+ reserve-y amount-in),
          k-last: (* (- reserve-x amount-out) (+ reserve-y amount-in))
        })
      )
      
      ;; Update stats
      (var-set total-volume (+ (var-get total-volume) amount-in))
      (var-set total-fees-collected (+ (var-get total-fees-collected) fee-amount))
      
      (ok { amount-in: amount-in, amount-out: amount-out, fee: fee-amount })
    )
    err-pool-not-found
  )
)

Functions (12)

FunctionAccessArgs
get-poolread-onlypool-id: uint
get-lp-balanceread-onlypool-id: uint, owner: principal
get-pool-countread-only
get-total-volumeread-only
get-user-statsread-onlyuser: principal
get-amount-outread-onlyamount-in: uint, reserve-in: uint, reserve-out: uint
get-priceread-onlypool-id: uint
create-poolpublicinitial-x: uint, initial-y: uint
add-liquiditypublicpool-id: uint, amount-x: uint, amount-y: uint, min-lp: uint
remove-liquiditypublicpool-id: uint, lp-amount: uint, min-x: uint, min-y: uint
swap-x-for-ypublicpool-id: uint, amount-in: uint, min-out: uint
swap-y-for-xpublicpool-id: uint, amount-in: uint, min-out: uint