Source Code

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; errors
(define-constant err-init-preconditions     (err u331))
(define-constant err-init-postconditions    (err u333))
(define-constant err-update-preconditions   (err u334))
(define-constant err-update-postconditions  (err u335))

(define-constant err-permissions            (err u400))
;; (define-constant err-invariants             (err u499))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; permissions
(define-private
  (INTERNAL)
  (begin
   (asserts!
    (or
     (is-eq contract-caller .gl-fees)
     (is-eq contract-caller .gl-core)
     (is-eq contract-caller .gl-positions)
     )
    err-permissions)
   (ok true)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; storage
(define-map fees-store
  uint
  {
  id                   : uint,
  t0                   : uint,
  t1                   : uint,
  borrowing-long       : uint,
  borrowing-short      : uint,
  funding-long         : uint,
  funding-short        : uint,
  long-collateral      : uint, ;;quote
  short-collateral     : uint, ;;base

  borrowing-long-sum   : uint,
  borrowing-short-sum  : uint,
  funding-long-sum     : uint,
  funding-short-sum    : uint,

  received-long-sum    : uint,
  received-short-sum   : uint,
  })

(define-read-only (lookup (id uint)) (unwrap-panic (map-get? fees-store id)))

(define-private
 (insert
  (new
   {
   id                   : uint,
   t0                   : uint,
   t1                   : uint,
   borrowing-long       : uint,
   borrowing-short      : uint,
   funding-long         : uint,
   funding-short        : uint,
   long-collateral      : uint, ;;quote
   short-collateral     : uint, ;;base

   borrowing-long-sum   : uint,
   borrowing-short-sum  : uint,
   funding-long-sum     : uint,
   funding-short-sum    : uint,

   received-long-sum    : uint,
   received-short-sum   : uint,
   }))
 (begin
  (map-set fees-store (get id new) new)
  new))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; init
(define-public
  (init (id uint))
  (begin
   (try! (INTERNAL))
   (ok (insert
    {
    id                   : id,
    t0                   : stacks-block-height,
    t1                   : stacks-block-height,
    borrowing-long       : u0,
    borrowing-short      : u0,
    funding-long         : u0,
    funding-short        : u0,
    long-collateral      : u0,
    short-collateral     : u0,

    borrowing-long-sum   : u0,
    borrowing-short-sum  : u0,
    funding-long-sum     : u0,
    funding-short-sum    : u0,

    received-long-sum    : u0,
    received-short-sum   : u0,
    }))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; rolling sums (~ partially applied averages)
(define-read-only
  (extend
   (X   uint)
   (x_m uint)
   (m   uint))
  (+ X (* m x_m)))

(define-read-only
  (slice
   (y_i uint)
   (y_j uint))
  (- y_j y_i))

;; max total supply    = 10^10
;; min decimals        = 10^6
;; max decimals        = 10^8
;; --> max             = 10^18
;; --> min             = 10^6
;; max representable   = 10^38
;;
;; we assume collateral 10^6 <= c <= 10^16
;; (TODO: add checks / make this configurable)
;; 16-6 = 10 + 2 decimals
(define-constant ZEROS u1000000000000) ;;10^12

;; store paid * 1/C = paid * 1/sum(c_i)
(define-read-only
  (divide
   (paid       uint)
   (collateral uint))
  (if (is-eq collateral u0)
      u0
      (/ (* paid ZEROS)
         collateral))) ;;worst case: 10^16 * 10^12  / 10^6 = 10^22

(define-read-only
  (multiply
   (ci    uint)           ;;single position collateral
   (terms uint))          ;;paid_0 * 1/C_0 + ... paid_1 * 1/C_n
  (/ (* ci terms) ZEROS)) ;;worst case: 10^16 * 10^22 = 10^38

;; spiritually amount*fee (< 1)
(define-read-only
  (apply
   (amount uint)
   (fee    uint))
  (get fee (contract-call? .gl-math apply amount fee)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; update

(define-public (update (id uint))
  (begin
    (try! (INTERNAL))
    (ok (insert (current-fees id)))
  ))

(define-read-only (current-fees (id uint))
  (let ((fees     (lookup id))                                   ;;prev/last updated state
        (pool     (contract-call? .gl-pools lookup id))          ;;current state
        (new-fees (contract-call? .gl-params dynamic-fees pool)) ;;current state

        ;; this records:
        ;;   - the sum up to and not including this block
        ;;   - the total (sample) for this block

        (new-terms (- stacks-block-height  (get t1 fees)))

        ;; f_0 + ... + f_i
        (borrowing-long-sum  (extend (get borrowing-long-sum  fees) (get borrowing-long  fees) new-terms))
        (borrowing-short-sum (extend (get borrowing-short-sum fees) (get borrowing-short fees) new-terms))
        (funding-long-sum    (extend (get funding-long-sum    fees) (get funding-long    fees) new-terms))
        (funding-short-sum   (extend (get funding-short-sum   fees) (get funding-short   fees) new-terms))

        ;; payments made from longs to shorts (and vice versa) PER UNIT COLLATERAL:
        ;; paid    : f_0 * C_l_0           + ... + f_i * C_l_i
        ;; received: f_0 * C_l_0 * 1/C_s_0 + ... + f_i * C_l_i * 1/C_s_i
        (paid-long-term      (apply (get long-collateral fees) (* (get funding-long fees) new-terms)))
        (received-short-term (divide paid-long-term (get short-collateral fees)))

        (paid-short-term     (apply (get short-collateral fees) (* (get funding-short fees) new-terms)))
        (received-long-term  (divide paid-short-term (get long-collateral fees)))

        (received-long-sum   (extend (get received-long-sum  fees) received-long-term  u1))
        (received-short-sum  (extend (get received-short-sum fees) received-short-term u1))
        )

    (if (is-eq new-terms u0)
        (merge
            fees
              {
              borrowing-long      : (get borrowing-long   new-fees),
              borrowing-short     : (get borrowing-short  new-fees),
              funding-long        : (get funding-long     new-fees),
              funding-short       : (get funding-short    new-fees),
              long-collateral     : (get quote-collateral pool),
              short-collateral    : (get base-collateral  pool),
             })
            {
             id                   : (get id fees),
             t0                   : (get t0 fees),
             t1                   : stacks-block-height,
             ;; samples
             borrowing-long       : (get borrowing-long   new-fees),
             borrowing-short      : (get borrowing-short  new-fees),
             funding-long         : (get funding-long     new-fees),
             funding-short        : (get funding-short    new-fees),
             long-collateral      : (get quote-collateral pool),
             short-collateral     : (get base-collateral  pool),
             ;; sums
             borrowing-long-sum   : borrowing-long-sum,
             borrowing-short-sum  : borrowing-short-sum,
             funding-long-sum     : funding-long-sum,
             funding-short-sum    : funding-short-sum,
             received-long-sum    : received-long-sum,
             received-short-sum   : received-short-sum,
             })
        )
    )

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; query

;; TODO: query without update?

;; (define-private
;;   (query
;;    (id   uint)
;;    (from uint))
;;   (begin
;;    (unwrap-panic (update id))
;;    (ok (do-query id from))))

(define-read-only
  (query
   (id        uint)
   (opened-at uint))
  (let ((fees-i (fees-at-block opened-at id))
        (fees-j (current-fees id)))
    {
    borrowing-long  : (slice (get borrowing-long-sum  fees-i) (get borrowing-long-sum  fees-j)),
    borrowing-short : (slice (get borrowing-short-sum fees-i) (get borrowing-short-sum fees-j)),
    funding-long    : (slice (get funding-long-sum    fees-i) (get funding-long-sum    fees-j)),
    funding-short   : (slice (get funding-short-sum   fees-i) (get funding-short-sum   fees-j)),
    received-long   : (slice (get received-long-sum   fees-i) (get received-long-sum   fees-j)),
    received-short  : (slice (get received-short-sum  fees-i) (get received-short-sum  fees-j)),
    }))

(define-read-only
 (fees-at-block
  (height uint)
  (id     uint))
 (let ((header (unwrap-panic (get-stacks-block-info? id-header-hash height))))
   (at-block header (lookup id)) ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; calc
(define-read-only
 (calc
  (id         uint)
  (long       bool)
  (collateral uint)
  (from       uint))
 (let ((period (query id from))
       (P_b    (if long
                   (apply    collateral (get borrowing-long  period))
                   (apply    collateral (get borrowing-short period))))
       (P_f    (if long
                   (apply    collateral (get funding-long    period))
                   (apply    collateral (get funding-short   period))))
       (R_f    (if long
                   (multiply collateral (get received-long   period))
                   (multiply collateral (get received-short  period))))
       )

   {
   funding-paid    : P_f,
   funding-received: R_f,
   borrowing-paid  : P_b,
   }))

;;; eof

Functions (13)

FunctionAccessArgs
INTERNALprivate
lookupread-onlyid: uint
insertprivatenew: { id : uint, t0 : uint, t1 : uint, borrowing-long : uint, borrowing-short : uint, funding-long : uint, funding-short : uint, long-collateral : uint, ;;quote short-collateral : uint, ;;base borrowing-long-sum : uint, borrowing-short-sum : uint, funding-long-sum : uint, funding-short-sum : uint, received-long-sum : uint, received-short-sum : uint, }
initpublicid: uint
extendread-onlyX: uint, x_m: uint, m: uint
sliceread-onlyy_i: uint, y_j: uint
divideread-onlypaid: uint, collateral: uint
applyread-onlyamount: uint, fee: uint
updatepublicid: uint
current-feesread-onlyid: uint
queryread-onlyid: uint, opened-at: uint
fees-at-blockread-onlyheight: uint, id: uint
calcread-onlyid: uint, long: bool, collateral: uint, from: uint