;; aries-chain - Automated Market Maker (AMM) DEX
;; Constant-product AMM swap protocol
(define-constant CONTRACT-OWNER tx-sender)
(define-constant FEE-RATE u30)
(define-constant FEE-DENOMINATOR u10000)
(define-constant MIN-LIQUIDITY u1000)
(define-constant ERR-OWNER-ONLY (err u100))
(define-constant ERR-INSUFFICIENT-LIQUIDITY (err u101))
(define-constant ERR-ZERO-AMOUNT (err u102))
(define-constant ERR-SLIPPAGE (err u103))
(define-constant ERR-POOL-EXISTS (err u104))
(define-constant ERR-NO-POOL (err u105))
(define-map liquidity-pools
{ token-a: principal, token-b: principal }
{ reserve-a: uint, reserve-b: uint, total-lp: uint, fee-earned: uint })
(define-map lp-balances
{ pool: { token-a: principal, token-b: principal }, provider: principal }
uint)
(define-data-var total-volume uint u0)
(define-data-var protocol-fees-collected uint u0)
(define-private (get-amount-out (amount-in uint) (reserve-in uint) (reserve-out uint))
(let ((amount-with-fee (* amount-in (- FEE-DENOMINATOR FEE-RATE)))
(numerator (* amount-with-fee reserve-out))
(denominator (+ (* reserve-in FEE-DENOMINATOR) amount-with-fee)))
(/ numerator denominator)))
(define-public (add-liquidity
(token-a principal) (token-b principal)
(amount-a uint) (amount-b uint) (min-lp uint))
(let ((pool-key { token-a: token-a, token-b: token-b }))
(asserts! (> amount-a u0) ERR-ZERO-AMOUNT)
(asserts! (> amount-b u0) ERR-ZERO-AMOUNT)
(match (map-get? liquidity-pools pool-key)
pool
(let ((lp-minted (/ (* amount-a (get total-lp pool)) (get reserve-a pool))))
(asserts! (>= lp-minted min-lp) ERR-SLIPPAGE)
(map-set liquidity-pools pool-key
(merge pool { reserve-a: (+ (get reserve-a pool) amount-a),
reserve-b: (+ (get reserve-b pool) amount-b),
total-lp: (+ (get total-lp pool) lp-minted) }))
(ok lp-minted))
(begin
(asserts! (>= amount-a MIN-LIQUIDITY) ERR-INSUFFICIENT-LIQUIDITY)
(map-set liquidity-pools pool-key
{ reserve-a: amount-a, reserve-b: amount-b, total-lp: amount-a, fee-earned: u0 })
(ok amount-a)))))
(define-public (swap-exact-in
(token-a principal) (token-b principal)
(amount-in uint) (min-out uint))
(let ((pool-key { token-a: token-a, token-b: token-b })
(pool (unwrap! (map-get? liquidity-pools { token-a: token-a, token-b: token-b }) ERR-NO-POOL)))
(asserts! (> amount-in u0) ERR-ZERO-AMOUNT)
(let ((amount-out (get-amount-out amount-in (get reserve-a pool) (get reserve-b pool))))
(asserts! (>= amount-out min-out) ERR-SLIPPAGE)
(map-set liquidity-pools pool-key
(merge pool { reserve-a: (+ (get reserve-a pool) amount-in),
reserve-b: (- (get reserve-b pool) amount-out) }))
(var-set total-volume (+ (var-get total-volume) amount-in))
(ok amount-out))))
(define-read-only (get-pool (token-a principal) (token-b principal))
(ok (map-get? liquidity-pools { token-a: token-a, token-b: token-b })))
(define-read-only (quote-swap (amount-in uint) (reserve-in uint) (reserve-out uint))
(ok (get-amount-out amount-in reserve-in reserve-out)))
(define-read-only (get-total-volume) (ok (var-get total-volume)))