Source Code

;; flux-lend - Collateralized Lending Protocol
;; Borrow against collateral with dynamic interest rates

(define-constant CONTRACT-OWNER tx-sender)
(define-constant INTEREST-RATE u500)
(define-constant COLLATERAL-RATIO u150)
(define-constant LIQUIDATION-THRESHOLD u120)
(define-constant BLOCKS-PER-YEAR u52560)
(define-constant ERR-OWNER-ONLY (err u100))
(define-constant ERR-INSUFFICIENT-COLLATERAL (err u101))
(define-constant ERR-NO-POSITION (err u102))
(define-constant ERR-HEALTHY (err u103))
(define-constant ERR-ZERO-AMOUNT (err u104))
(define-constant ERR-OVER-LIMIT (err u105))

(define-map positions principal
  { collateral: uint, debt: uint, last-update: uint, health: uint })

(define-map interest-checkpoints principal uint)

(define-data-var total-collateral uint u0)
(define-data-var total-debt uint u0)
(define-data-var utilization-rate uint u0)
(define-data-var protocol-reserves uint u0)

(define-private (calculate-interest (debt uint) (blocks uint))
  (/ (* debt (* INTEREST-RATE blocks)) (* u10000 BLOCKS-PER-YEAR)))

(define-private (get-health-factor (collateral uint) (debt uint))
  (if (is-eq debt u0) u999
    (/ (* collateral u100) debt)))

(define-public (deposit-collateral (amount uint))
  (begin
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (match (map-get? positions tx-sender)
      p (map-set positions tx-sender
          (merge p { collateral: (+ (get collateral p) amount),
                     health: (get-health-factor (+ (get collateral p) amount) (get debt p)) }))
      (map-set positions tx-sender
          { collateral: amount, debt: u0, last-update: block-height, health: u999 }))
    (var-set total-collateral (+ (var-get total-collateral) amount))
    (ok true)))

(define-public (borrow (amount uint))
  (let ((position (unwrap! (map-get? positions tx-sender) ERR-NO-POSITION)))
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (let ((new-debt (+ (get debt position) amount))
          (required-collateral (/ (* new-debt COLLATERAL-RATIO) u100)))
      (asserts! (>= (get collateral position) required-collateral) ERR-INSUFFICIENT-COLLATERAL)
      (map-set positions tx-sender
        (merge position { debt: new-debt, last-update: block-height,
                          health: (get-health-factor (get collateral position) new-debt) }))
      (var-set total-debt (+ (var-get total-debt) amount))
      (as-contract (stx-transfer? amount tx-sender contract-caller)))))

(define-public (repay (amount uint))
  (let ((position (unwrap! (map-get? positions tx-sender) ERR-NO-POSITION)))
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (let ((interest (calculate-interest (get debt position) (- block-height (get last-update position))))
          (total-owed (+ (get debt position) interest))
          (repay-amount (if (> amount total-owed) total-owed amount)))
      (try! (stx-transfer? repay-amount tx-sender (as-contract tx-sender)))
      (map-set positions tx-sender
        (merge position { debt: (- total-owed repay-amount), last-update: block-height,
                          health: (get-health-factor (get collateral position) (- total-owed repay-amount)) }))
      (ok true))))

(define-public (liquidate (borrower principal))
  (let ((position (unwrap! (map-get? positions borrower) ERR-NO-POSITION)))
    (asserts! (< (get health position) LIQUIDATION-THRESHOLD) ERR-HEALTHY)
    (let ((debt (get debt position))
          (collateral (get collateral position)))
      (try! (stx-transfer? debt tx-sender (as-contract tx-sender)))
      (map-delete positions borrower)
      (var-set total-collateral (- (var-get total-collateral) collateral))
      (var-set total-debt (- (var-get total-debt) debt))
      (as-contract (stx-transfer? collateral tx-sender tx-sender)))))

(define-read-only (get-position (borrower principal)) (ok (map-get? positions borrower)))
(define-read-only (get-health-factor-of (borrower principal))
  (match (map-get? positions borrower)
    p (ok (get-health-factor (get collateral p) (get debt p)))
    (ok u0)))
(define-read-only (get-protocol-stats)
  (ok { total-collateral: (var-get total-collateral), total-debt: (var-get total-debt) }))

Functions (9)

FunctionAccessArgs
calculate-interestprivatedebt: uint, blocks: uint
get-health-factorprivatecollateral: uint, debt: uint
deposit-collateralpublicamount: uint
borrowpublicamount: uint
repaypublicamount: uint
liquidatepublicborrower: principal
get-positionread-onlyborrower: principal
get-health-factor-ofread-onlyborrower: principal
get-protocol-statsread-only