Source Code

;; 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)
    )
  )
)

Functions (17)

FunctionAccessArgs
min-uintprivatea: uint, b: uint
get-amount-outprivateamount-in: uint, reserve-in: uint, reserve-out: uint
sqrt-newtonprivaten: uint
update-liquidity-historyprivateprovider: principal, amount: uint, is-add: bool
add-liquiditypublicamount-b2s: uint, amount-stx: uint, min-lp-tokens: uint
remove-liquiditypubliclp-tokens: uint, min-b2s: uint, min-stx: uint
swap-b2s-for-stxpublicamount-b2s-in: uint, min-stx-out: uint
swap-stx-for-b2spublicamount-stx-in: uint, min-b2s-out: uint
get-reservesread-only
get-lp-balanceread-onlyprovider: principal
get-total-lp-tokensread-only
quote-swap-b2s-for-stxread-onlyamount-b2s: uint
quote-swap-stx-for-b2sread-onlyamount-stx: uint
get-priceread-only
get-total-volumeread-only
get-liquidity-historyread-onlyprovider: principal
get-pool-shareread-onlyprovider: principal