Source Code

;; DeFi Lending Pool Contract
;; Main contract for handling deposits, borrows, repayments, and withdrawals

;; Constants
(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_UNAUTHORIZED (err u100))
(define-constant ERR_INSUFFICIENT_COLLATERAL (err u101))
(define-constant ERR_INSUFFICIENT_LIQUIDITY (err u102))
(define-constant ERR_INVALID_AMOUNT (err u103))
(define-constant ERR_POSITION_NOT_FOUND (err u104))
(define-constant ERR_HEALTH_FACTOR_TOO_LOW (err u105))
(define-constant ERR_NO_COLLATERAL (err u106))
(define-constant ERR_OUTSTANDING_DEBT (err u107))

;; Collateral ratio: 75% (represented as 75 out of 100)
(define-constant COLLATERAL_RATIO u75)
(define-constant COLLATERAL_RATIO_DENOMINATOR u100)

;; Minimum health factor: 1.2 (represented as 120 out of 100)
(define-constant MIN_HEALTH_FACTOR u120)
(define-constant HEALTH_FACTOR_DENOMINATOR u100)

;; Interest precision (1e6 for 6 decimal places)
(define-constant INTEREST_PRECISION u1000000)

;; Data Variables
(define-data-var total-liquidity uint u0)
(define-data-var total-borrowed uint u0)
(define-data-var global-interest-index uint INTEREST_PRECISION)
(define-data-var last-update-block uint u0)
(define-data-var protocol-paused bool false)

;; Data Maps
(define-map user-positions
  principal
  {
    collateral: uint,
    borrowed: uint,
    interest-index: uint,
    last-interaction-block: uint
  }
)

(define-map pool-stats
  { stat-type: (string-ascii 20) }
  { value: uint }
)

;; Read-only functions

(define-read-only (get-user-position (user principal))
  (default-to
    { collateral: u0, borrowed: u0, interest-index: INTEREST_PRECISION, last-interaction-block: u0 }
    (map-get? user-positions user)
  )
)

(define-read-only (get-pool-liquidity)
  (var-get total-liquidity)
)

(define-read-only (get-total-borrowed)
  (var-get total-borrowed)
)

(define-read-only (get-utilization-rate)
  (let
    (
      (liquidity (var-get total-liquidity))
      (borrowed (var-get total-borrowed))
    )
    (if (is-eq liquidity u0)
      u0
      ;; Calculate utilization as percentage (borrowed / liquidity * 100)
      (/ (* borrowed u100) liquidity)
    )
  )
)

(define-read-only (get-global-interest-index)
  (var-get global-interest-index)
)

(define-read-only (calculate-max-borrow (user principal))
  (let
    (
      (position (get-user-position user))
      (collateral-value (get collateral position))
    )
    ;; Max borrow = collateral * COLLATERAL_RATIO / 100
    (/ (* collateral-value COLLATERAL_RATIO) COLLATERAL_RATIO_DENOMINATOR)
  )
)

(define-read-only (calculate-current-debt (user principal))
  (let
    (
      (position (get-user-position user))
      (borrowed (get borrowed position))
      (user-index (get interest-index position))
      (current-index (var-get global-interest-index))
    )
    (if (is-eq borrowed u0)
      u0
      ;; Debt = borrowed * (current-index / user-index)
      (/ (* borrowed current-index) user-index)
    )
  )
)

(define-read-only (get-health-factor (user principal))
  (let
    (
      (position (get-user-position user))
      (collateral (get collateral position))
      (current-debt (calculate-current-debt user))
    )
    (if (is-eq current-debt u0)
      u999999 ;; Essentially infinite health factor
      ;; Health factor = (collateral * COLLATERAL_RATIO / 100) / debt * 100
      (/ (* (/ (* collateral COLLATERAL_RATIO) COLLATERAL_RATIO_DENOMINATOR) HEALTH_FACTOR_DENOMINATOR) current-debt)
    )
  )
)

(define-read-only (is-protocol-paused)
  (var-get protocol-paused)
)

;; Private functions

(define-private (update-interest-index)
  (let
    (
      (blocks-elapsed (- block-height (var-get last-update-block)))
      (current-rate (contract-call? .interest-calculator get-current-rate))
    )
    (if (> blocks-elapsed u0)
      (let
        (
          ;; Calculate new interest based on blocks elapsed
          ;; Interest per block = rate / blocks-per-year (approximately 52560 blocks per year)
          (interest-per-block (/ current-rate u52560))
          (interest-multiplier (+ INTEREST_PRECISION (/ (* interest-per-block blocks-elapsed) u100)))
          (new-index (/ (* (var-get global-interest-index) interest-multiplier) INTEREST_PRECISION))
        )
        (var-set global-interest-index new-index)
        (var-set last-update-block block-height)
        true
      )
      true
    )
  )
)

;; Public functions

(define-public (deposit-collateral (amount uint))
  (begin
    (asserts! (not (var-get protocol-paused)) ERR_UNAUTHORIZED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    
    ;; Transfer STX from user to contract
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    
    ;; Update interest index
    (update-interest-index)
    
    ;; Update user position
    (let
      (
        (current-position (get-user-position tx-sender))
        (new-collateral (+ (get collateral current-position) amount))
      )
      (map-set user-positions tx-sender
        (merge current-position {
          collateral: new-collateral,
          last-interaction-block: block-height
        })
      )
      
      ;; Update total liquidity
      (var-set total-liquidity (+ (var-get total-liquidity) amount))
      
      (ok true)
    )
  )
)

(define-public (borrow-funds (amount uint))
  (begin
    (asserts! (not (var-get protocol-paused)) ERR_UNAUTHORIZED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    
    ;; Update interest index
    (update-interest-index)
    
    (let
      (
        (current-position (get-user-position tx-sender))
        (current-debt (calculate-current-debt tx-sender))
        (max-borrow (calculate-max-borrow tx-sender))
        (new-debt (+ current-debt amount))
      )
      ;; Check if user has enough collateral
      (asserts! (<= new-debt max-borrow) ERR_INSUFFICIENT_COLLATERAL)
      
      ;; Check if pool has enough liquidity
      (asserts! (<= amount (var-get total-liquidity)) ERR_INSUFFICIENT_LIQUIDITY)
      
      ;; Transfer borrowed amount to user
      (try! (as-contract (stx-transfer? amount tx-sender tx-sender)))
      
      ;; Update user position
      (map-set user-positions tx-sender
        (merge current-position {
          borrowed: new-debt,
          interest-index: (var-get global-interest-index),
          last-interaction-block: block-height
        })
      )
      
      ;; Update pool stats
      (var-set total-borrowed (+ (var-get total-borrowed) amount))
      (var-set total-liquidity (- (var-get total-liquidity) amount))
      
      ;; Update interest rates based on new utilization
      (try! (contract-call? .interest-calculator update-rates (get-utilization-rate)))
      
      (ok true)
    )
  )
)

(define-public (repay-loan (amount uint))
  (begin
    (asserts! (not (var-get protocol-paused)) ERR_UNAUTHORIZED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    
    ;; Update interest index
    (update-interest-index)
    
    (let
      (
        (current-position (get-user-position tx-sender))
        (current-debt (calculate-current-debt tx-sender))
        (repay-amount (if (<= amount current-debt) amount current-debt))
      )
      (asserts! (> current-debt u0) ERR_POSITION_NOT_FOUND)
      
      ;; Transfer repayment from user to contract
      (try! (stx-transfer? repay-amount tx-sender (as-contract tx-sender)))
      
      ;; Update user position
      (let
        (
          (new-debt (- current-debt repay-amount))
        )
        (map-set user-positions tx-sender
          (merge current-position {
            borrowed: new-debt,
            interest-index: (var-get global-interest-index),
            last-interaction-block: block-height
          })
        )
        
        ;; Update pool stats
        (var-set total-borrowed (- (var-get total-borrowed) repay-amount))
        (var-set total-liquidity (+ (var-get total-liquidity) repay-amount))
        
        ;; Update interest rates
        (try! (contract-call? .interest-calculator update-rates (get-utilization-rate)))
        
        (ok repay-amount)
      )
    )
  )
)

(define-public (withdraw-collateral (amount uint))
  (begin
    (asserts! (not (var-get protocol-paused)) ERR_UNAUTHORIZED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    
    ;; Update interest index
    (update-interest-index)
    
    (let
      (
        (current-position (get-user-position tx-sender))
        (collateral (get collateral current-position))
        (current-debt (calculate-current-debt tx-sender))
      )
      (asserts! (>= collateral amount) ERR_NO_COLLATERAL)
      
      ;; If user has debt, check health factor after withdrawal
      (if (> current-debt u0)
        (let
          (
            (new-collateral (- collateral amount))
            (max-borrow-after (/ (* new-collateral COLLATERAL_RATIO) COLLATERAL_RATIO_DENOMINATOR))
            (new-health-factor (/ (* max-borrow-after HEALTH_FACTOR_DENOMINATOR) current-debt))
          )
          (asserts! (>= new-health-factor MIN_HEALTH_FACTOR) ERR_HEALTH_FACTOR_TOO_LOW)
          true
        )
        true
      )
      
      ;; Transfer collateral back to user
      (try! (as-contract (stx-transfer? amount tx-sender tx-sender)))
      
      ;; Update user position
      (map-set user-positions tx-sender
        (merge current-position {
          collateral: (- collateral amount),
          last-interaction-block: block-height
        })
      )
      
      ;; Update total liquidity
      (var-set total-liquidity (- (var-get total-liquidity) amount))
      
      (ok true)
    )
  )
)

;; Admin functions

(define-public (toggle-protocol-pause)
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
    (var-set protocol-paused (not (var-get protocol-paused)))
    (ok (var-get protocol-paused))
  )
)

Functions (15)

FunctionAccessArgs
get-user-positionread-onlyuser: principal
get-pool-liquidityread-only
get-total-borrowedread-only
get-utilization-rateread-only
get-global-interest-indexread-only
calculate-max-borrowread-onlyuser: principal
calculate-current-debtread-onlyuser: principal
get-health-factorread-onlyuser: principal
is-protocol-pausedread-only
update-interest-indexprivate
deposit-collateralpublicamount: uint
borrow-fundspublicamount: uint
repay-loanpublicamount: uint
withdraw-collateralpublicamount: uint
toggle-protocol-pausepublic