Source Code

;; Minimal sBTC Pool - Ultra-simplified AMM
;; No as-contract complexity, basic constant product AMM

(use-trait sip-010-trait .sip-010-trait.sip-010-trait)

;; Constants
(define-constant CONTRACT_OWNER tx-sender)
(define-constant SBTC_CONTRACT .test-token)
(define-constant ERR_NOT_AUTHORIZED (err u401))
(define-constant ERR_ALREADY_INITIALIZED (err u402))
(define-constant ERR_NOT_INITIALIZED (err u403))
(define-constant ERR_ZERO_AMOUNT (err u404))
(define-constant ERR_SLIPPAGE (err u405))
(define-constant ERR_INSUFFICIENT_BALANCE (err u406))

;; Data variables
(define-data-var initialized bool false)
(define-data-var reserve-stx uint u0)
(define-data-var reserve-sbtc uint u0)
(define-data-var total-lp-supply uint u0)

;; Data maps
(define-map lp-balances principal uint)

;; Initialize pool
(define-public (initialize)
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_AUTHORIZED)
    (asserts! (not (var-get initialized)) ERR_ALREADY_INITIALIZED)
    (var-set initialized true)
    (ok true)
  )
)

;; Add liquidity
(define-public (add-liquidity (stx-amount uint) (sbtc-amount uint) (min-lp uint))
  (begin
    (asserts! (var-get initialized) ERR_NOT_INITIALIZED)
    (asserts! (> stx-amount u0) ERR_ZERO_AMOUNT)
    (asserts! (> sbtc-amount u0) ERR_ZERO_AMOUNT)

    (let (
      (current-stx (var-get reserve-stx))
      (current-sbtc (var-get reserve-sbtc))
      (current-supply (var-get total-lp-supply))
      (lp-tokens (if (is-eq current-supply u0)
                   ;; First liquidity: simple sum
                   (+ stx-amount sbtc-amount)
                   ;; Subsequent: proportional to smaller ratio
                   (let (
                     (lp-from-stx (/ (* stx-amount current-supply) current-stx))
                     (lp-from-sbtc (/ (* sbtc-amount current-supply) current-sbtc))
                   )
                     (if (<= lp-from-stx lp-from-sbtc) lp-from-stx lp-from-sbtc)
                   )))
    )
      (asserts! (>= lp-tokens min-lp) ERR_SLIPPAGE)

      ;; Transfer tokens to pool (user sends to this contract)
      (try! (stx-transfer? stx-amount tx-sender (as-contract tx-sender)))
      (try! (contract-call? SBTC_CONTRACT transfer sbtc-amount tx-sender (as-contract tx-sender) none))

      ;; Update state
      (var-set reserve-stx (+ current-stx stx-amount))
      (var-set reserve-sbtc (+ current-sbtc sbtc-amount))
      (var-set total-lp-supply (+ current-supply lp-tokens))
      (map-set lp-balances tx-sender (+ (default-to u0 (map-get? lp-balances tx-sender)) lp-tokens))

      (ok lp-tokens)
    )
  )
)

;; Remove liquidity
(define-public (remove-liquidity (lp-amount uint) (min-stx uint) (min-sbtc uint))
  (begin
    (asserts! (var-get initialized) ERR_NOT_INITIALIZED)
    (asserts! (> lp-amount u0) ERR_ZERO_AMOUNT)

    (let (
      (user-balance (default-to u0 (map-get? lp-balances tx-sender)))
      (current-stx (var-get reserve-stx))
      (current-sbtc (var-get reserve-sbtc))
      (current-supply (var-get total-lp-supply))
      (stx-out (/ (* lp-amount current-stx) current-supply))
      (sbtc-out (/ (* lp-amount current-sbtc) current-supply))
    )
      (asserts! (>= user-balance lp-amount) ERR_INSUFFICIENT_BALANCE)
      (asserts! (>= stx-out min-stx) ERR_SLIPPAGE)
      (asserts! (>= sbtc-out min-sbtc) ERR_SLIPPAGE)

      ;; Update state first
      (var-set reserve-stx (- current-stx stx-out))
      (var-set reserve-sbtc (- current-sbtc sbtc-out))
      (var-set total-lp-supply (- current-supply lp-amount))
      (map-set lp-balances tx-sender (- user-balance lp-amount))

      ;; Transfer tokens back to user
      (try! (as-contract (stx-transfer? stx-out tx-sender tx-sender)))
      (try! (as-contract (contract-call? SBTC_CONTRACT transfer sbtc-out tx-sender tx-sender none)))

      (ok {stx: stx-out, sbtc: sbtc-out})
    )
  )
)

;; Swap STX for sBTC
(define-public (swap-stx-for-sbtc (stx-in uint) (min-sbtc-out uint))
  (begin
    (asserts! (var-get initialized) ERR_NOT_INITIALIZED)
    (asserts! (> stx-in u0) ERR_ZERO_AMOUNT)

    (let (
      (current-stx (var-get reserve-stx))
      (current-sbtc (var-get reserve-sbtc))
      ;; 0.3% fee: multiply by 997, divide by 1000
      (stx-after-fee (/ (* stx-in u997) u1000))
      (sbtc-out (/ (* stx-after-fee current-sbtc) (+ current-stx stx-after-fee)))
    )
      (asserts! (>= sbtc-out min-sbtc-out) ERR_SLIPPAGE)

      ;; Transfer STX in
      (try! (stx-transfer? stx-in tx-sender (as-contract tx-sender)))

      ;; Update reserves
      (var-set reserve-stx (+ current-stx stx-in))
      (var-set reserve-sbtc (- current-sbtc sbtc-out))

      ;; Transfer sBTC out
      (try! (as-contract (contract-call? SBTC_CONTRACT transfer sbtc-out tx-sender tx-sender none)))

      (ok sbtc-out)
    )
  )
)

;; Swap sBTC for STX
(define-public (swap-sbtc-for-stx (sbtc-in uint) (min-stx-out uint))
  (begin
    (asserts! (var-get initialized) ERR_NOT_INITIALIZED)
    (asserts! (> sbtc-in u0) ERR_ZERO_AMOUNT)

    (let (
      (current-stx (var-get reserve-stx))
      (current-sbtc (var-get reserve-sbtc))
      ;; 0.3% fee
      (sbtc-after-fee (/ (* sbtc-in u997) u1000))
      (stx-out (/ (* sbtc-after-fee current-stx) (+ current-sbtc sbtc-after-fee)))
    )
      (asserts! (>= stx-out min-stx-out) ERR_SLIPPAGE)

      ;; Transfer sBTC in
      (try! (contract-call? SBTC_CONTRACT transfer sbtc-in tx-sender (as-contract tx-sender) none))

      ;; Update reserves
      (var-set reserve-sbtc (+ current-sbtc sbtc-in))
      (var-set reserve-stx (- current-stx stx-out))

      ;; Transfer STX out
      (try! (as-contract (stx-transfer? stx-out tx-sender tx-sender)))

      (ok stx-out)
    )
  )
)

;; Read-only functions
(define-read-only (get-reserves)
  {
    stx: (var-get reserve-stx),
    sbtc: (var-get reserve-sbtc),
    lp-supply: (var-get total-lp-supply)
  }
)

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

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

Functions (8)

FunctionAccessArgs
initializepublic
add-liquiditypublicstx-amount: uint, sbtc-amount: uint, min-lp: uint
remove-liquiditypubliclp-amount: uint, min-stx: uint, min-sbtc: uint
swap-stx-for-sbtcpublicstx-in: uint, min-sbtc-out: uint
swap-sbtc-for-stxpublicsbtc-in: uint, min-stx-out: uint
get-reservesread-only
get-lp-balanceread-onlyaccount: principal
is-initializedread-only