Source Code

;; title: StxBitStack
;; version:
;; summary:
;; description:

;; traits
;;


;; title: bit-stack
;; version:
;; summary:
;; description:
;; Constants
(define-constant ERR-NOT-AUTHORIZED (err u1))
(define-constant ERR-INSUFFICIENT-COLLATERAL (err u2))
(define-constant ERR-LOAN-NOT-FOUND (err u3))
(define-constant ERR-LOAN-ALREADY-ACTIVE (err u4))
(define-constant ERR-INVALID-INPUT (err u5))
(define-constant ERR-INSUFFICIENT-EXCESS-COLLATERAL (err u6))
(define-constant ERR-LOAN-ALREADY-LIQUIDATED (err u7))

;; traits
;;
;; Liquidation-specific constants
(define-constant LIQUIDATION-THRESHOLD u125) ;; 125% collateralization ratio
(define-constant LIQUIDATION-PENALTY u110) ;; 10% penalty on liquidation
(define-constant COLLATERAL-RATIO u150) ;; 150% initial collateralization ratio
(define-constant MAX-LOAN-DURATION u2880) ;; ~20 days (144 blocks/day)
(define-constant MAX-INTEREST-RATE u1000) ;; 10% max interest rate

;; token definitions
;;
;; Data vars
(define-data-var minimum-collateral uint u100000) ;; in sats
(define-data-var protocol-fee uint u100) ;; basis points (1% = 100)
(define-data-var last-loan-id uint u0)

;; constants
;;
;; Data maps
(define-map loans
    { loan-id: uint }
    {
        borrower: principal,
        lender: principal,
        amount: uint,
        collateral: uint,
        interest-rate: uint,
        start-height: uint,
        end-height: uint,
        status: (string-ascii 20)
    }
)

;; data vars
;;
(define-map liquidations
    { loan-id: uint }
    {
        liquidator: principal,
        liquidation-height: uint,
        liquidation-amount: uint
    }
)

;; data maps
;;
(define-map user-loans
    principal
    (list 10 uint)
)




;; Read-only functions
(define-read-only (get-loan (loan-id uint))
    (map-get? loans { loan-id: loan-id })
)

(define-read-only (get-liquidation (loan-id uint))
    (map-get? liquidations { loan-id: loan-id })
)

(define-read-only (get-user-loans (user principal))
    (default-to (list) (map-get? user-loans user))
)





;;;;;;; Private functions;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Input validation functions
(define-private (is-valid-input 
    (amount uint) 
    (collateral uint) 
    (interest-rate uint) 
    (loan-duration uint)
)
    (and
        ;; Amount and collateral must be positive
        (> amount u0)
        (> collateral u0)

        ;; Interest rate within bounds
        (<= interest-rate MAX-INTEREST-RATE)

        ;; Loan duration within reasonable limits
        (and (> loan-duration u0) (<= loan-duration MAX-LOAN-DURATION))

        ;; Validate collateral ratio
        (is-collateral-ratio-valid amount collateral)
    )
)


;; Collateral and liquidation helper functions
(define-private (is-collateral-ratio-valid (loan-amount uint) (collateral-amount uint))
    (let
        (
            (min-collateral (* loan-amount COLLATERAL-RATIO))
        )
        (>= (* collateral-amount u10000) min-collateral)
    )
)

(define-private (calculate-current-collateral-ratio (loan-amount uint) (collateral-amount uint))
    (/ (* collateral-amount u10000) loan-amount)
)


;; Existing helper function
(define-private (calculate-max-withdrawable-collateral 
    (current-collateral uint) 
    (loan-amount uint)
)
    (let
        (
            (min-required (/ (* loan-amount COLLATERAL-RATIO) u10000))
            ;; Add a small buffer to prevent risky withdrawals
            (safe-buffer (/ min-required u10))
        )
        (if (> current-collateral (+ min-required safe-buffer))
            (- current-collateral (+ min-required safe-buffer))
            u0
        )
    )
)

;; Existing helper functions (calculate-interest, get-next-loan-id, etc.)
(define-private (calculate-interest (principal uint) (rate uint) (blocks uint))
    (let
        (
            (interest-per-block (/ (* principal rate) (* u10000 u144))) ;; Assuming 144 blocks per day
        )
        (* interest-per-block blocks)
    )
)

(define-private (get-next-loan-id)
    (let
        (
            (current-id (var-get last-loan-id))
            (next-id (+ current-id u1))
        )
        (var-set last-loan-id next-id)
        next-id
    )
)



;;;;;;; PUBLIC FUNCTIONS ;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Existing functions from previous implementation
(define-public (create-loan 
    (amount uint) 
    (collateral uint) 
    (interest-rate uint) 
    (loan-duration uint)
)
    (let
        (
            (caller tx-sender)
            (loan-id (get-next-loan-id))
        )
        ;; Validate all inputs
        (asserts! 
            (is-valid-input amount collateral interest-rate loan-duration) 
            ERR-INVALID-INPUT
        )

        ;; Create loan entry
        (map-set loans 
            { loan-id: loan-id }
            {
                borrower: caller,
                lender: caller,
                amount: amount,
                collateral: collateral,
                interest-rate: interest-rate,
                start-height: stacks-block-height,
                end-height: (+ stacks-block-height loan-duration),
                status: "PENDING"
            }
        )

        ;; Update user's loan list
        (map-set user-loans
            caller
            (unwrap! 
                (as-max-len? 
                    (append 
                        (default-to (list) (map-get? user-loans caller)) 
                        loan-id
                    ) 
                    u10
                )
                ERR-NOT-AUTHORIZED
            )
        )

        (ok loan-id)
    )
)


(define-public (check-and-liquidate (loan-id uint))
    (let
        (
            (loan (unwrap! (map-get? loans { loan-id: loan-id }) ERR-LOAN-NOT-FOUND))
            (caller tx-sender)
        )
        ;; Additional check for loan-id
        (asserts! (> loan-id u0) ERR-INVALID-INPUT)

        ;; Ensure loan is not already liquidated or repaid
        (asserts! (is-eq (get status loan) "ACTIVE") ERR-LOAN-NOT-FOUND)

        ;; Calculate current collateral ratio
        (let
            (
                (current-ratio 
                    (calculate-current-collateral-ratio 
                        (get amount loan) 
                        (get collateral loan)
                    )
                )
            )
            ;; Check if loan is below liquidation threshold
            (asserts! (< current-ratio LIQUIDATION-THRESHOLD) ERR-INSUFFICIENT-COLLATERAL)

            ;; Calculate liquidation amount with penalty
            (let
                (
                    (penalty-multiplier LIQUIDATION-PENALTY)
                    (liquidation-amount 
                        (/ 
                            (* (get amount loan) penalty-multiplier) 
                            u100
                        )
                    )
                )
                ;; Update loan status to liquidated
                (map-set loans
                    { loan-id: loan-id }
                    (merge loan {
                        status: "LIQUIDATED",
                        collateral: u0
                    })
                )

                ;; Record liquidation details
                (map-set liquidations
                    { loan-id: loan-id }
                    {
                        liquidator: caller,
                        liquidation-height: stacks-block-height,
                        liquidation-amount: liquidation-amount
                    }
                )

                (ok liquidation-amount)
            )
        )
    )
)

;; Partial collateral withdrawal function
(define-public (withdraw-excess-collateral 
    (loan-id uint) 
    (withdrawal-amount uint)
)
    (let
        (
            (loan (unwrap! (map-get? loans { loan-id: loan-id }) ERR-LOAN-NOT-FOUND))
            (caller tx-sender)
        )
        ;; Additional check for loan-id
        (asserts! (> loan-id u0) ERR-INVALID-INPUT)

        ;; Validate inputs
        (asserts! (is-eq (get borrower loan) caller) ERR-NOT-AUTHORIZED)
        (asserts! (is-eq (get status loan) "ACTIVE") ERR-LOAN-NOT-FOUND)

        ;; Calculate maximum withdrawable amount
        (let
            (
                (max-withdrawable 
                    (calculate-max-withdrawable-collateral 
                        (get collateral loan) 
                        (get amount loan)
                    )
                )
            )
            ;; Ensure withdrawal amount is valid
            (asserts! (> withdrawal-amount u0) ERR-INVALID-INPUT)
            (asserts! (<= withdrawal-amount max-withdrawable) ERR-INSUFFICIENT-EXCESS-COLLATERAL)

            ;; Update loan with reduced collateral
            (map-set loans
                { loan-id: loan-id }
                (merge loan {
                    collateral: (- (get collateral loan) withdrawal-amount)
                })
            )

            ;; Transfer collateral back to borrower (placeholder)
            (ok withdrawal-amount)
        )
    )
)


(define-public (fund-loan (loan-id uint))
    (let
        (
            (loan (unwrap! (map-get? loans { loan-id: loan-id }) ERR-LOAN-NOT-FOUND))
            (caller tx-sender)
        )
        ;; Additional validation for loan-id
        (asserts! (> loan-id u0) ERR-INVALID-INPUT)

        (asserts! (is-eq (get status loan) "PENDING") ERR-LOAN-ALREADY-ACTIVE)
        (asserts! (not (is-eq (get borrower loan) caller)) ERR-NOT-AUTHORIZED)

        ;; Update loan status and set lender
        (map-set loans
            { loan-id: loan-id }
            (merge loan {
                lender: caller,
                status: "ACTIVE"
            })
        )

        (ok true)
    )
)

(define-public (repay-loan (loan-id uint))
    (let
        (
            (loan (unwrap! (map-get? loans { loan-id: loan-id }) ERR-LOAN-NOT-FOUND))
            (caller tx-sender)
        )
        ;; Additional validation for loan-id
        (asserts! (> loan-id u0) ERR-INVALID-INPUT)

        (asserts! (is-eq (get borrower loan) caller) ERR-NOT-AUTHORIZED)
        (asserts! (is-eq (get status loan) "ACTIVE") ERR-LOAN-NOT-FOUND)

        ;; Calculate repayment amount with interest
        (let
            (
                (interest-amount (calculate-interest
                    (get amount loan)
                    (get interest-rate loan)
                    (- stacks-block-height (get start-height loan))
                ))
                (total-repayment (+ (get amount loan) interest-amount))
            )

            ;; Update loan status
            (map-set loans
                { loan-id: loan-id }
                (merge loan {
                    status: "REPAID"
                })
            )

            (ok true)
        )
    )
)

Functions (10)

FunctionAccessArgs
get-loanread-onlyloan-id: uint
get-liquidationread-onlyloan-id: uint
get-user-loansread-onlyuser: principal
is-collateral-ratio-validprivateloan-amount: uint, collateral-amount: uint
calculate-current-collateral-ratioprivateloan-amount: uint, collateral-amount: uint
calculate-interestprivateprincipal: uint, rate: uint, blocks: uint
get-next-loan-idprivate
check-and-liquidatepublicloan-id: uint
fund-loanpublicloan-id: uint
repay-loanpublicloan-id: uint