arkadiko-vaults-manager-v1-1

SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR

Source Code

;; Vaults Manager 
;; External operations on vaults (liquidation & redemption)
;;

(use-trait oracle-trait .arkadiko-oracle-trait-v1.oracle-trait)
(use-trait ft-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)
(use-trait vaults-tokens-trait .arkadiko-vaults-tokens-trait-v1-1.vaults-tokens-trait)
(use-trait vaults-data-trait .arkadiko-vaults-data-trait-v1-1.vaults-data-trait)
(use-trait vaults-sorted-trait .arkadiko-vaults-sorted-trait-v1-1.vaults-sorted-trait)
(use-trait vaults-pool-active-trait .arkadiko-vaults-pool-active-trait-v1-1.vaults-pool-active-trait)
(use-trait vaults-pool-liq-trait .arkadiko-vaults-pool-liq-trait-v1-1.vaults-pool-liq-trait)
(use-trait vaults-helpers-trait .arkadiko-vaults-helpers-trait-v1-1.vaults-helpers-trait)

;; ---------------------------------------------------------
;; Constants
;; ---------------------------------------------------------

(define-constant ERR_NOT_AUTHORIZED u920401)
(define-constant ERR_WRONG_TRAIT u920402)
(define-constant ERR_SHUTDOWN u920501)
(define-constant ERR_CAN_NOT_LIQUIDATE u920001)
(define-constant ERR_UNKNOWN_TOKEN u920002)
(define-constant ERR_NOT_FIRST_VAULT u920003)

(define-constant STATUS_ACTIVE u101)
(define-constant STATUS_CLOSED_BY_LIQUIDATION u201)
(define-constant STATUS_CLOSED_BY_REDEMPTION u202)

;; ---------------------------------------------------------
;; Variables
;; ---------------------------------------------------------

(define-data-var shutdown-activated bool false)

;; ---------------------------------------------------------
;; Maps
;; ---------------------------------------------------------

(define-map redemption-block-last
  { 
    token: principal
  }
  {
    block-last: uint
  }
)

;; ---------------------------------------------------------
;; Getter
;; ---------------------------------------------------------

(define-read-only (get-shutdown-activated) 
  (var-get shutdown-activated)
)

(define-read-only (get-redemption-block-last (token principal))
  (default-to
    {
      block-last: u0,
    }
    (map-get? redemption-block-last { token: token })
  )
)

;; ---------------------------------------------------------
;; Liquidations
;; ---------------------------------------------------------

;; Liquidate vault
(define-public (liquidate-vault 
  (vaults-tokens <vaults-tokens-trait>) 
  (vaults-data <vaults-data-trait>) 
  (vaults-sorted <vaults-sorted-trait>)
  (vaults-pool-active <vaults-pool-active-trait>)
  (vaults-pool-liq <vaults-pool-liq-trait>)
  (vaults-helpers <vaults-helpers-trait>)
  (oracle <oracle-trait>) 
  (owner principal) 
  (token <ft-trait>)
)
  (let (
    (vault (unwrap-panic (contract-call? vaults-data get-vault owner (contract-of token))))
    (coll-to-debt (try! (contract-call? vaults-helpers get-collateral-to-debt vaults-tokens vaults-data oracle owner (contract-of token) (get collateral vault) (get debt vault))))
    
    (stability-fee (try! (contract-call? vaults-helpers get-stability-fee vaults-tokens vaults-data owner (contract-of token))))
    (new-debt (+ stability-fee (get debt vault)))
    
    (collateral (try! (get-collateral-for-liquidation vaults-tokens oracle (contract-of token) (get collateral vault) new-debt)))
  )
    (asserts! (is-eq (contract-of vaults-tokens) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-tokens"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-data) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-data"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-sorted) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-sorted"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-pool-active) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-pool-active"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-pool-liq) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-pool-liq"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-helpers) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-helpers"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "oracle"))) (err ERR_WRONG_TRAIT))
    (asserts! (not (var-get shutdown-activated)) (err ERR_SHUTDOWN))
    (asserts! (not (get valid coll-to-debt)) (err ERR_CAN_NOT_LIQUIDATE))

    ;; Update vault data
    (try! (contract-call? vaults-data set-vault owner (contract-of token) STATUS_CLOSED_BY_LIQUIDATION u0 u0))
    (try! (contract-call? vaults-sorted remove owner (contract-of token)))

    ;; Burn debt, mint stability fee
    (try! (as-contract (contract-call? vaults-pool-liq burn-usda new-debt)))
    (try! (as-contract (contract-call? .arkadiko-dao mint-token .usda-token stability-fee .arkadiko-vaults-pool-fees-v1-1)))

    ;; Add rewards to liquidation pool
    (try! (as-contract (contract-call? vaults-pool-active withdraw token tx-sender (get collateral-needed collateral))))
    (try! (as-contract (contract-call? vaults-pool-liq add-rewards vaults-tokens token (get collateral-needed collateral))))

    ;; Send leftover back to owner
    (if (> (get collateral-left collateral) u0)
      (try! (as-contract (contract-call? vaults-pool-active withdraw token owner (get collateral-left collateral))))
      false
    )

    (ok true)
  )
)

;; Get collateral amount info to use in liquidation
(define-public (get-collateral-for-liquidation (vaults-tokens <vaults-tokens-trait>) (oracle <oracle-trait>) (token principal) (collateral uint) (debt uint))
  (let (
    (collateral-info (unwrap! (contract-call? vaults-tokens get-token token) (err ERR_UNKNOWN_TOKEN)))
    (collateral-price (try! (contract-call? oracle fetch-price (get token-name collateral-info))))
    (collateral-value (/ (* collateral (get last-price collateral-price)) (get decimals collateral-price)))
    (collateral-needed (/ (* collateral debt) collateral-value))
    (collateral-penalty (/ (* collateral-needed (get liquidation-penalty collateral-info)) u10000))
    (collateral-total (+ collateral-needed collateral-penalty))
  )
    (if (>= collateral-total collateral)
      (ok { collateral-needed: collateral, collateral-left: u0 })
      (ok { collateral-needed: collateral-total, collateral-left: (- collateral collateral-total) })
    )
  )
)

;; ---------------------------------------------------------
;; Redemption
;; ---------------------------------------------------------

(define-public (redeem-vault 
  (vaults-tokens <vaults-tokens-trait>)
  (vaults-data <vaults-data-trait>)
  (vaults-sorted <vaults-sorted-trait>)
  (vaults-pool-active <vaults-pool-active-trait>)
  (vaults-helpers <vaults-helpers-trait>)
  (oracle <oracle-trait>) 
  (owner principal) 
  (token <ft-trait>) 
  (debt-payoff uint)
  (prev-owner-hint (optional principal)) 
)
  (let (
    (redeemer tx-sender)
    (vault (unwrap-panic (contract-call? vaults-data get-vault owner (contract-of token))))
    (token-list (unwrap-panic (contract-call? vaults-sorted get-token (contract-of token))))

    (collateral-info (unwrap! (contract-call? vaults-tokens get-token (contract-of token)) (err ERR_UNKNOWN_TOKEN)))
    (collateral-price (try! (contract-call? oracle fetch-price (get token-name collateral-info))))
    (collateral-value (/ (* (get collateral vault) (get last-price collateral-price)) (get decimals collateral-price)))

    (stability-fee (try! (contract-call? vaults-helpers get-stability-fee vaults-tokens vaults-data owner (contract-of token))))
    (debt-total (+ (get debt vault) stability-fee))

    (debt-payoff-used (if (>= debt-payoff debt-total)
      debt-total
      debt-payoff
    ))
    (debt-left (if (>= debt-payoff debt-total)
      u0
      (- debt-total debt-payoff)
    ))

    (fee (try! (get-redemption-fee vaults-tokens (contract-of token))))
    (fee-block-last (get block-last (get-redemption-block-last (contract-of token))))
    (fee-block-last-cap (if (< fee-block-last (- burn-block-height (get redemption-fee-block-interval collateral-info))) 
      (- burn-block-height (get redemption-fee-block-interval collateral-info)) 
      fee-block-last
    ))
    (fee-block-change (/ debt-payoff-used (get redemption-fee-block-rate collateral-info)))
    (new-redemption-last-block (+ fee-block-last-cap fee-block-change))

    (collateral-needed (/ (* (get collateral vault) debt-payoff-used) collateral-value))
    (collateral-fee (/ (* collateral-needed (get current-fee fee)) u10000))
    (collateral-received (- collateral-needed collateral-fee))
    
    (collateral-left (- (get collateral vault) collateral-needed))
  )
    (asserts! (is-eq (contract-of vaults-tokens) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-tokens"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-data) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-data"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-sorted) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-sorted"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-pool-active) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-pool-active"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of vaults-helpers) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-helpers"))) (err ERR_WRONG_TRAIT))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "oracle"))) (err ERR_WRONG_TRAIT))
    (asserts! (not (var-get shutdown-activated)) (err ERR_SHUTDOWN))
    (asserts! (is-eq owner (unwrap-panic (get first-owner token-list))) (err ERR_NOT_FIRST_VAULT))

    (if (is-eq debt-left u0)
      ;; Vault closed
      (begin
        (try! (as-contract (contract-call? vaults-pool-active withdraw token owner collateral-left)))
        (try! (contract-call? vaults-data set-vault owner (contract-of token) STATUS_CLOSED_BY_REDEMPTION u0 u0))
        (try! (contract-call? vaults-sorted remove owner (contract-of token)))
      )

      ;; Partial redemption
      (let (
        (nicr (/ (* collateral-left u100000000) debt-left))
      )
        (try! (contract-call? vaults-data set-vault owner (contract-of token) STATUS_ACTIVE collateral-left debt-left))
        (try! (contract-call? vaults-sorted reinsert owner (contract-of token) nicr prev-owner-hint))
      )
    )

    ;; Burn USDA
    (try! (as-contract (contract-call? .arkadiko-dao burn-token .usda-token debt-payoff-used redeemer)))

    ;; Send tokens to redeemer
    (try! (as-contract (contract-call? vaults-pool-active withdraw token redeemer collateral-received)))

    ;; Get fee
    (try! (as-contract (contract-call? vaults-pool-active withdraw token .arkadiko-vaults-pool-fees-v1-1 collateral-fee)))

    ;; Increase redemption fee by decreasing last block
    (map-set redemption-block-last { token: (contract-of token) } 
      { block-last: new-redemption-last-block }
    )
    
    ;; Return 
    (ok { debt-payoff-used: debt-payoff-used, collateral-received: collateral-received, collateral-fee: collateral-fee })
  )
)

;; Get current redemption fee in bps
;; Fee has a min/max and moves between these values
;; Fee goes up on redemption, fee goes down over time
(define-public (get-redemption-fee (vaults-tokens <vaults-tokens-trait>) (token principal)) 
  (let (
    (collateral-info (unwrap! (contract-call? vaults-tokens get-token token) (err ERR_UNKNOWN_TOKEN)))
    (fee-info (get-redemption-block-last token))
    (fee-diff (- (get redemption-fee-max collateral-info) (get redemption-fee-min collateral-info)))
    (block-diff (- burn-block-height (get block-last fee-info)))
    (fee-change (/ (* fee-diff block-diff) (get redemption-fee-block-interval collateral-info)))
    (current-fee (if (>= fee-change fee-diff)
      (get redemption-fee-min collateral-info)
      (- (get redemption-fee-max collateral-info) fee-change)
    ))
  )
    (ok { current-fee: current-fee })
  )
)

;; ---------------------------------------------------------
;; Admin
;; ---------------------------------------------------------

(define-public (set-shutdown-activated (activated bool))
  (begin
    (asserts! (is-eq contract-caller (contract-call? .arkadiko-dao get-dao-owner)) (err ERR_NOT_AUTHORIZED))

    (var-set shutdown-activated activated)

    (ok true)
  )
)

Functions (6)

FunctionAccessArgs
get-shutdown-activatedread-only
get-redemption-block-lastread-onlytoken: principal
get-collateral-for-liquidationpublicvaults-tokens: <vaults-tokens-trait>, oracle: <oracle-trait>, token: principal, collateral: uint, debt: uint
redeem-vaultpublicvaults-tokens: <vaults-tokens-trait>, vaults-data: <vaults-data-trait>, vaults-sorted: <vaults-sorted-trait>, vaults-pool-active: <vaults-pool-active-trait>, vaults-helpers: <vaults-helpers-trait>, oracle: <oracle-trait>, owner: principal, token: <ft-trait>, debt-payoff: uint, prev-owner-hint: (optional principal
get-redemption-feepublicvaults-tokens: <vaults-tokens-trait>, token: principal
set-shutdown-activatedpublicactivated: bool