;; B2S Liquidity Pool - AMM (Automated Market Maker)
;; Swap B2S <-> STX with constant product formula (x * y = k)
(define-constant contract-owner tx-sender)
;; Error codes
(define-constant err-not-authorized (err u401))
(define-constant err-insufficient-balance (err u402))
(define-constant err-slippage-too-high (err u403))
(define-constant err-insufficient-liquidity (err u404))
(define-constant err-invalid-amount (err u405))
(define-constant err-zero-amount (err u406))
;; Constants
(define-constant fee-numerator u25)
(define-constant fee-denominator u10000)
(define-constant minimum-liquidity u1000)
;; Data vars
(define-data-var reserve-b2s uint u0)
(define-data-var reserve-stx uint u0)
(define-data-var total-lp-tokens uint u0)
(define-data-var total-volume-b2s uint u0)
(define-data-var total-volume-stx uint u0)
;; LP token balances
(define-map lp-balances principal uint)
;; User liquidity history
(define-map liquidity-history principal {
added: uint,
removed: uint,
rewards: uint
})
;; Private helpers
(define-private (min-uint (a uint) (b uint))
(if (<= a b) a b)
)
(define-private (get-amount-out (amount-in uint) (reserve-in uint) (reserve-out uint))
(/ (* amount-in reserve-out) (+ reserve-in amount-in))
)
(define-private (sqrt-newton (n uint))
(if (<= n u1)
n
(let (
(x1 (/ n u2))
(x2 (/ (+ x1 (/ n x1)) u2))
(x3 (/ (+ x2 (/ n x2)) u2))
(x4 (/ (+ x3 (/ n x3)) u2))
(x5 (/ (+ x4 (/ n x4)) u2))
(x6 (/ (+ x5 (/ n x5)) u2))
(x7 (/ (+ x6 (/ n x6)) u2))
(x8 (/ (+ x7 (/ n x7)) u2))
)
(if (<= x8 x7) x8 x7)
)
)
)
(define-private (update-liquidity-history (provider principal) (amount uint) (is-add bool))
(let (
(current-history (default-to
{added: u0, removed: u0, rewards: u0}
(map-get? liquidity-history provider)))
)
(map-set liquidity-history provider {
added: (if is-add (+ (get added current-history) amount) (get added current-history)),
removed: (if is-add (get removed current-history) (+ (get removed current-history) amount)),
rewards: (get rewards current-history)
})
)
)
;; Public Functions
(define-public (add-liquidity (amount-b2s uint) (amount-stx uint) (min-lp-tokens uint))
(let (
(provider tx-sender)
(current-reserve-b2s (var-get reserve-b2s))
(current-reserve-stx (var-get reserve-stx))
(current-total-lp (var-get total-lp-tokens))
)
(asserts! (> amount-b2s u0) err-zero-amount)
(asserts! (> amount-stx u0) err-zero-amount)
(let (
(lp-to-mint (if (is-eq current-total-lp u0)
(sqrt-newton (* amount-b2s amount-stx))
(min-uint
(/ (* amount-b2s current-total-lp) current-reserve-b2s)
(/ (* amount-stx current-total-lp) current-reserve-stx)
)
))
)
(asserts! (>= lp-to-mint min-lp-tokens) err-slippage-too-high)
(var-set reserve-b2s (+ current-reserve-b2s amount-b2s))
(var-set reserve-stx (+ current-reserve-stx amount-stx))
(var-set total-lp-tokens (+ current-total-lp lp-to-mint))
(map-set lp-balances provider
(+ (default-to u0 (map-get? lp-balances provider)) lp-to-mint))
(update-liquidity-history provider lp-to-mint true)
(ok lp-to-mint)
)
)
)
(define-public (remove-liquidity (lp-tokens uint) (min-b2s uint) (min-stx uint))
(let (
(provider tx-sender)
(provider-balance (default-to u0 (map-get? lp-balances provider)))
(current-reserve-b2s (var-get reserve-b2s))
(current-reserve-stx (var-get reserve-stx))
(current-total-lp (var-get total-lp-tokens))
)
(asserts! (> lp-tokens u0) err-zero-amount)
(asserts! (>= provider-balance lp-tokens) err-insufficient-balance)
(let (
(b2s-to-return (/ (* lp-tokens current-reserve-b2s) current-total-lp))
(stx-to-return (/ (* lp-tokens current-reserve-stx) current-total-lp))
)
(asserts! (>= b2s-to-return min-b2s) err-slippage-too-high)
(asserts! (>= stx-to-return min-stx) err-slippage-too-high)
(var-set reserve-b2s (- current-reserve-b2s b2s-to-return))
(var-set reserve-stx (- current-reserve-stx stx-to-return))
(var-set total-lp-tokens (- current-total-lp lp-tokens))
(map-set lp-balances provider (- provider-balance lp-tokens))
(update-liquidity-history provider lp-tokens false)
(ok {b2s: b2s-to-return, stx: stx-to-return})
)
)
)
(define-public (swap-b2s-for-stx (amount-b2s-in uint) (min-stx-out uint))
(let (
(reserve-b2s-before (var-get reserve-b2s))
(reserve-stx-before (var-get reserve-stx))
)
(asserts! (> amount-b2s-in u0) err-zero-amount)
(asserts! (> reserve-stx-before u0) err-insufficient-liquidity)
(let (
(amount-with-fee (- amount-b2s-in (/ (* amount-b2s-in fee-numerator) fee-denominator)))
(stx-out (get-amount-out amount-with-fee reserve-b2s-before reserve-stx-before))
)
(asserts! (>= stx-out min-stx-out) err-slippage-too-high)
(var-set reserve-b2s (+ reserve-b2s-before amount-b2s-in))
(var-set reserve-stx (- reserve-stx-before stx-out))
(var-set total-volume-b2s (+ (var-get total-volume-b2s) amount-b2s-in))
(ok stx-out)
)
)
)
(define-public (swap-stx-for-b2s (amount-stx-in uint) (min-b2s-out uint))
(let (
(reserve-b2s-before (var-get reserve-b2s))
(reserve-stx-before (var-get reserve-stx))
)
(asserts! (> amount-stx-in u0) err-zero-amount)
(asserts! (> reserve-b2s-before u0) err-insufficient-liquidity)
(let (
(amount-with-fee (- amount-stx-in (/ (* amount-stx-in fee-numerator) fee-denominator)))
(b2s-out (get-amount-out amount-with-fee reserve-stx-before reserve-b2s-before))
)
(asserts! (>= b2s-out min-b2s-out) err-slippage-too-high)
(var-set reserve-stx (+ reserve-stx-before amount-stx-in))
(var-set reserve-b2s (- reserve-b2s-before b2s-out))
(var-set total-volume-stx (+ (var-get total-volume-stx) amount-stx-in))
(ok b2s-out)
)
)
)
;; Read-only Functions
(define-read-only (get-reserves)
(ok {b2s: (var-get reserve-b2s), stx: (var-get reserve-stx)})
)
(define-read-only (get-lp-balance (provider principal))
(ok (default-to u0 (map-get? lp-balances provider)))
)
(define-read-only (get-total-lp-tokens)
(ok (var-get total-lp-tokens))
)
(define-read-only (quote-swap-b2s-for-stx (amount-b2s uint))
(let (
(rb2s (var-get reserve-b2s))
(rstx (var-get reserve-stx))
(amount-with-fee (- amount-b2s (/ (* amount-b2s fee-numerator) fee-denominator)))
)
(ok (get-amount-out amount-with-fee rb2s rstx))
)
)
(define-read-only (quote-swap-stx-for-b2s (amount-stx uint))
(let (
(rb2s (var-get reserve-b2s))
(rstx (var-get reserve-stx))
(amount-with-fee (- amount-stx (/ (* amount-stx fee-numerator) fee-denominator)))
)
(ok (get-amount-out amount-with-fee rstx rb2s))
)
)
(define-read-only (get-price)
(let (
(rb2s (var-get reserve-b2s))
(rstx (var-get reserve-stx))
)
(if (and (> rb2s u0) (> rstx u0))
(ok (/ rb2s rstx))
(ok u0)
)
)
)
(define-read-only (get-total-volume)
(ok {b2s: (var-get total-volume-b2s), stx: (var-get total-volume-stx)})
)
(define-read-only (get-liquidity-history (provider principal))
(ok (map-get? liquidity-history provider))
)
(define-read-only (get-pool-share (provider principal))
(let (
(provider-lp (default-to u0 (map-get? lp-balances provider)))
(total-lp (var-get total-lp-tokens))
)
(if (> total-lp u0)
(ok (/ (* provider-lp u10000) total-lp))
(ok u0)
)
)
)