Source Code

;; SPDX-License-Identifier: BUSL-1.1
;; TITLE: kinked-interest-rate-utility
;; VERSION: 1.0

;; CONSTANTS
(define-constant one-8 u100000000)
(define-constant one-12 u1000000000000)
(define-constant fact_2 u2000000000000)
(define-constant fact_3 u6000000000000)
(define-constant fact_4 u24000000000000)
(define-constant fact_5 u120000000000000)
(define-constant fact_6 u720000000000000)
(define-constant seconds-in-year u31536000)


;; READ-ONLY FUNCTIONS 

;; Accrues interest based on the the last accrued block
;; returns updated lp interest, staked interest, protocol interest, total assets, and last accrued block
(define-read-only (accrue-interest 
    (last-accrued-block-time uint)
    (lp-open-interest uint)
    (staked-open-interest uint)
    (staking-reward-percentage uint)
    (protocol-open-interest uint)
    (protocol-reserve-percentage uint)
    (total-assets uint)
    (time-now uint)
    (ir-slope-1 uint) (ir-slope-2 uint) (utilization-kink uint) (base-ir uint)
  )
  (let (
    (elapsed-block-time (- time-now last-accrued-block-time))
    (premature-return (asserts!
      (not (is-eq u0 elapsed-block-time))
      (ok {
        last-accrued-block-time: last-accrued-block-time,
        lp-open-interest: lp-open-interest,
        protocol-open-interest: protocol-open-interest,
        staked-open-interest: staked-open-interest,
        total-assets: total-assets
      })))
    (open-interest (+ (+ lp-open-interest protocol-open-interest) staked-open-interest))
    (interest-rate-per-block (get-ir total-assets open-interest ir-slope-1 ir-slope-2 utilization-kink base-ir))
    (compounded-interest-rate (compounded-interest interest-rate-per-block elapsed-block-time))
    (total-interest (calc-total-interest compounded-interest-rate open-interest))
    (protocol-interest (/ (* total-interest protocol-reserve-percentage) one-8))
    (lp-interest (- total-interest protocol-interest))
    (staked-interest (/ (* lp-interest staking-reward-percentage) one-8))
  )
    (print {
        interest-rate-per-block: interest-rate-per-block,
        compounded-interest-rate: compounded-interest-rate,
        total-interest: total-interest,
        lp-open-interest: lp-interest,
        protocol-open-interest: protocol-interest,
      })
    (ok {
      last-accrued-block-time: time-now,
      lp-open-interest: (+ lp-open-interest (- lp-interest staked-interest)),
      staked-open-interest: (+ staked-open-interest staked-interest),
      protocol-open-interest: (+ protocol-open-interest protocol-interest),
      total-assets: (+ total-assets lp-interest),
    })
  )
)

;; PRIVATE HELPER FUNCTIONS 
;; total-assets and open-interest are fixed to u8 precision
(define-private (utilization-calc (total-assets uint) (open-interest uint))
  (if (> (+ total-assets open-interest) u0) (/ (* open-interest one-12) total-assets) u0)
)

(define-private (get-ir (total-assets uint) (open-interest uint) (ir-slope-1 uint) (ir-slope-2 uint) (utilization-kink uint) (base-ir uint))
  (ir-calc (utilization-calc total-assets open-interest) ir-slope-1 ir-slope-2 utilization-kink base-ir))

(define-private (compounded-interest (current-interest-rate uint) (elapsed-block-time uint))
  (taylor-6 (get-rt-by-block current-interest-rate elapsed-block-time)))


(define-private (calc-total-interest (ir uint) (open-ir uint))
  (let ((interest-factor (- ir one-12))
    (total-interest (divide-round-up (* open-ir interest-factor) one-12)))
    total-interest
))

(define-private (divide-round-up (numerator uint) (denominator uint))
  (if (> (mod numerator denominator) u0)
    (+ u1 (/ numerator denominator))
    (/ numerator denominator)
))

(define-private (interest-util-less-than-kink (util-with-res uint) (ir-slope-1 uint) (base-ir uint)) 
  (+ (/ (* ir-slope-1 util-with-res) one-12) base-ir)
)

(define-private (interest-util-geq-kink (util-with-res uint) (ir-slope-1 uint) (ir-slope-2 uint) (utilization-kink uint) (base-ir uint)) 
  (+ 
    (/
      (+ 
        (* ir-slope-2 (- util-with-res utilization-kink)) 
        (* ir-slope-1 utilization-kink)
      )
      one-12
    ) 
    base-ir
))

(define-private (ir-calc (util-with-res uint) (ir-slope-1 uint) (ir-slope-2 uint) (utilization-kink uint) (base-ir uint))
  (if (is-eq util-with-res u0)
    u0
    (if
      (>= util-with-res utilization-kink)
        (interest-util-geq-kink util-with-res ir-slope-1 ir-slope-2 utilization-kink base-ir)
        (interest-util-less-than-kink util-with-res ir-slope-1 base-ir)
    )
))

(define-private (mul (x uint) (y uint))
	(/ (+ (* x y) (/ one-12 u2)) one-12)
)

(define-private (div (x uint) (y uint))
	(/ (+ (* x one-12) (/ y u2)) y)
)

;; rate in 12-fixed
;; n-blocks
(define-private (get-rt-by-block (rate uint) (elapsed-block-time uint))
  (/ (* rate (/ (* elapsed-block-time one-12) seconds-in-year)) one-12)
)

;; taylor series expansion to the 6th degree to estimate e^x
(define-private (taylor-6 (x uint))
  (let (
      (x_2 (mul x x))
      (x_3 (mul x x_2))
      (x_4 (mul x x_3))
      (x_5 (mul x x_4))
      (x_6 (mul x x_5))
    )
    (+
      one-12 
      x
      (div x_2 fact_2)
      (div x_3 fact_3)
      (div x_4 fact_4)
      (div x_5 fact_5)
      (div x_6 fact_6)
    )
))

Functions (12)

FunctionAccessArgs
utilization-calcprivatetotal-assets: uint, open-interest: uint
get-irprivatetotal-assets: uint, open-interest: uint, ir-slope-1: uint, ir-slope-2: uint, utilization-kink: uint, base-ir: uint
compounded-interestprivatecurrent-interest-rate: uint, elapsed-block-time: uint
calc-total-interestprivateir: uint, open-ir: uint
divide-round-upprivatenumerator: uint, denominator: uint
interest-util-less-than-kinkprivateutil-with-res: uint, ir-slope-1: uint, base-ir: uint
interest-util-geq-kinkprivateutil-with-res: uint, ir-slope-1: uint, ir-slope-2: uint, utilization-kink: uint, base-ir: uint
ir-calcprivateutil-with-res: uint, ir-slope-1: uint, ir-slope-2: uint, utilization-kink: uint, base-ir: uint
mulprivatex: uint, y: uint
divprivatex: uint, y: uint
get-rt-by-blockprivaterate: uint, elapsed-block-time: uint
taylor-6privatex: uint