|
| 1 | +;; Vaults Manager |
| 2 | +;; External operations on vaults (liquidation & redemption) |
| 3 | +;; |
| 4 | + |
| 5 | +(use-trait oracle-trait .arkadiko-oracle-trait-v1.oracle-trait) |
| 6 | +(use-trait ft-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait) |
| 7 | +(use-trait vaults-tokens-trait .arkadiko-vaults-tokens-trait-v1-1.vaults-tokens-trait) |
| 8 | +(use-trait vaults-data-trait .arkadiko-vaults-data-trait-v1-1.vaults-data-trait) |
| 9 | +(use-trait vaults-sorted-trait .arkadiko-vaults-sorted-trait-v1-1.vaults-sorted-trait) |
| 10 | +(use-trait vaults-pool-active-trait .arkadiko-vaults-pool-active-trait-v1-1.vaults-pool-active-trait) |
| 11 | +(use-trait vaults-pool-liq-trait .arkadiko-vaults-pool-liq-trait-v1-1.vaults-pool-liq-trait) |
| 12 | +(use-trait vaults-helpers-trait .arkadiko-vaults-helpers-trait-v1-1.vaults-helpers-trait) |
| 13 | + |
| 14 | +;; --------------------------------------------------------- |
| 15 | +;; Constants |
| 16 | +;; --------------------------------------------------------- |
| 17 | + |
| 18 | +(define-constant ERR_NOT_AUTHORIZED u920401) |
| 19 | +(define-constant ERR_WRONG_TRAIT u920402) |
| 20 | +(define-constant ERR_SHUTDOWN u920501) |
| 21 | +(define-constant ERR_CAN_NOT_LIQUIDATE u920001) |
| 22 | +(define-constant ERR_UNKNOWN_TOKEN u920002) |
| 23 | +(define-constant ERR_NOT_FIRST_VAULT u920003) |
| 24 | + |
| 25 | +(define-constant STATUS_ACTIVE u101) |
| 26 | +(define-constant STATUS_CLOSED_BY_LIQUIDATION u201) |
| 27 | +(define-constant STATUS_CLOSED_BY_REDEMPTION u202) |
| 28 | + |
| 29 | +;; --------------------------------------------------------- |
| 30 | +;; Variables |
| 31 | +;; --------------------------------------------------------- |
| 32 | + |
| 33 | +(define-data-var shutdown-activated bool false) |
| 34 | + |
| 35 | +;; --------------------------------------------------------- |
| 36 | +;; Maps |
| 37 | +;; --------------------------------------------------------- |
| 38 | + |
| 39 | +(define-map redemption-block-last |
| 40 | + { |
| 41 | + token: principal |
| 42 | + } |
| 43 | + { |
| 44 | + block-last: uint |
| 45 | + } |
| 46 | +) |
| 47 | + |
| 48 | +;; --------------------------------------------------------- |
| 49 | +;; Getter |
| 50 | +;; --------------------------------------------------------- |
| 51 | + |
| 52 | +(define-read-only (get-shutdown-activated) |
| 53 | + (var-get shutdown-activated) |
| 54 | +) |
| 55 | + |
| 56 | +(define-read-only (get-redemption-block-last (token principal)) |
| 57 | + (default-to |
| 58 | + { |
| 59 | + block-last: u0, |
| 60 | + } |
| 61 | + (map-get? redemption-block-last { token: token }) |
| 62 | + ) |
| 63 | +) |
| 64 | + |
| 65 | +;; --------------------------------------------------------- |
| 66 | +;; Liquidations |
| 67 | +;; --------------------------------------------------------- |
| 68 | + |
| 69 | +;; Liquidate vault |
| 70 | +(define-public (liquidate-vault |
| 71 | + (vaults-tokens <vaults-tokens-trait>) |
| 72 | + (vaults-data <vaults-data-trait>) |
| 73 | + (vaults-sorted <vaults-sorted-trait>) |
| 74 | + (vaults-pool-active <vaults-pool-active-trait>) |
| 75 | + (vaults-pool-liq <vaults-pool-liq-trait>) |
| 76 | + (vaults-helpers <vaults-helpers-trait>) |
| 77 | + (oracle <oracle-trait>) |
| 78 | + (owner principal) |
| 79 | + (token <ft-trait>) |
| 80 | +) |
| 81 | + (let ( |
| 82 | + (vault (unwrap-panic (contract-call? vaults-data get-vault owner (contract-of token)))) |
| 83 | + (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)))) |
| 84 | + |
| 85 | + (stability-fee (try! (contract-call? vaults-helpers get-stability-fee vaults-tokens vaults-data owner (contract-of token)))) |
| 86 | + (new-debt (+ stability-fee (get debt vault))) |
| 87 | + |
| 88 | + (collateral (try! (get-collateral-for-liquidation vaults-tokens oracle (contract-of token) (get collateral vault) new-debt))) |
| 89 | + ) |
| 90 | + (asserts! (is-eq (contract-of vaults-tokens) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-tokens"))) (err ERR_WRONG_TRAIT)) |
| 91 | + (asserts! (is-eq (contract-of vaults-data) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-data"))) (err ERR_WRONG_TRAIT)) |
| 92 | + (asserts! (is-eq (contract-of vaults-sorted) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-sorted"))) (err ERR_WRONG_TRAIT)) |
| 93 | + (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)) |
| 94 | + (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)) |
| 95 | + (asserts! (is-eq (contract-of vaults-helpers) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-helpers"))) (err ERR_WRONG_TRAIT)) |
| 96 | + (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "oracle"))) (err ERR_WRONG_TRAIT)) |
| 97 | + (asserts! (not (var-get shutdown-activated)) (err ERR_SHUTDOWN)) |
| 98 | + (asserts! (not (get valid coll-to-debt)) (err ERR_CAN_NOT_LIQUIDATE)) |
| 99 | + |
| 100 | + ;; Update vault data |
| 101 | + (try! (contract-call? vaults-data set-vault owner (contract-of token) STATUS_CLOSED_BY_LIQUIDATION u0 u0)) |
| 102 | + (try! (contract-call? vaults-sorted remove owner (contract-of token))) |
| 103 | + |
| 104 | + ;; Burn debt, mint stability fee |
| 105 | + (try! (as-contract (contract-call? vaults-pool-liq burn-usda new-debt))) |
| 106 | + (try! (as-contract (contract-call? .arkadiko-dao mint-token .usda-token stability-fee .arkadiko-vaults-pool-fees-v1-1))) |
| 107 | + |
| 108 | + ;; Add rewards to liquidation pool |
| 109 | + (try! (as-contract (contract-call? vaults-pool-active withdraw token tx-sender (get collateral-needed collateral)))) |
| 110 | + (try! (as-contract (contract-call? vaults-pool-liq add-rewards vaults-tokens token (get collateral-needed collateral)))) |
| 111 | + |
| 112 | + ;; Send leftover back to owner |
| 113 | + (if (> (get collateral-left collateral) u0) |
| 114 | + (try! (as-contract (contract-call? vaults-pool-active withdraw token owner (get collateral-left collateral)))) |
| 115 | + false |
| 116 | + ) |
| 117 | + |
| 118 | + (ok true) |
| 119 | + ) |
| 120 | +) |
| 121 | + |
| 122 | +;; Get collateral amount info to use in liquidation |
| 123 | +(define-public (get-collateral-for-liquidation (vaults-tokens <vaults-tokens-trait>) (oracle <oracle-trait>) (token principal) (collateral uint) (debt uint)) |
| 124 | + (let ( |
| 125 | + (collateral-info (unwrap! (contract-call? vaults-tokens get-token token) (err ERR_UNKNOWN_TOKEN))) |
| 126 | + (collateral-price (try! (contract-call? oracle fetch-price (get token-name collateral-info)))) |
| 127 | + (collateral-value (/ (* collateral (get last-price collateral-price)) (get decimals collateral-price))) |
| 128 | + (collateral-needed (/ (* collateral debt) collateral-value)) |
| 129 | + (collateral-penalty (/ (* collateral-needed (get liquidation-penalty collateral-info)) u10000)) |
| 130 | + (collateral-total (+ collateral-needed collateral-penalty)) |
| 131 | + ) |
| 132 | + (if (>= collateral-total collateral) |
| 133 | + (ok { collateral-needed: collateral, collateral-left: u0 }) |
| 134 | + (ok { collateral-needed: collateral-total, collateral-left: (- collateral collateral-total) }) |
| 135 | + ) |
| 136 | + ) |
| 137 | +) |
| 138 | + |
| 139 | +;; --------------------------------------------------------- |
| 140 | +;; Redemption |
| 141 | +;; --------------------------------------------------------- |
| 142 | + |
| 143 | +(define-public (redeem-vault |
| 144 | + (vaults-tokens <vaults-tokens-trait>) |
| 145 | + (vaults-data <vaults-data-trait>) |
| 146 | + (vaults-sorted <vaults-sorted-trait>) |
| 147 | + (vaults-pool-active <vaults-pool-active-trait>) |
| 148 | + (vaults-helpers <vaults-helpers-trait>) |
| 149 | + (oracle <oracle-trait>) |
| 150 | + (owner principal) |
| 151 | + (token <ft-trait>) |
| 152 | + (debt-payoff uint) |
| 153 | + (prev-owner-hint (optional principal)) |
| 154 | +) |
| 155 | + (let ( |
| 156 | + (redeemer tx-sender) |
| 157 | + (vault (unwrap-panic (contract-call? vaults-data get-vault owner (contract-of token)))) |
| 158 | + (token-list (unwrap-panic (contract-call? vaults-sorted get-token (contract-of token)))) |
| 159 | + |
| 160 | + (collateral-info (unwrap! (contract-call? vaults-tokens get-token (contract-of token)) (err ERR_UNKNOWN_TOKEN))) |
| 161 | + (collateral-price (try! (contract-call? oracle fetch-price (get token-name collateral-info)))) |
| 162 | + (collateral-value (/ (* (get collateral vault) (get last-price collateral-price)) (get decimals collateral-price))) |
| 163 | + |
| 164 | + (stability-fee (try! (contract-call? vaults-helpers get-stability-fee vaults-tokens vaults-data owner (contract-of token)))) |
| 165 | + (debt-total (+ (get debt vault) stability-fee)) |
| 166 | + |
| 167 | + (debt-payoff-used (if (>= debt-payoff debt-total) |
| 168 | + debt-total |
| 169 | + debt-payoff |
| 170 | + )) |
| 171 | + (debt-left (if (>= debt-payoff debt-total) |
| 172 | + u0 |
| 173 | + (- debt-total debt-payoff) |
| 174 | + )) |
| 175 | + |
| 176 | + (fee (try! (get-redemption-fee vaults-tokens (contract-of token)))) |
| 177 | + (fee-block-last (get block-last (get-redemption-block-last (contract-of token)))) |
| 178 | + (fee-block-last-cap (if (< fee-block-last (- burn-block-height (get redemption-fee-block-interval collateral-info))) |
| 179 | + (- burn-block-height (get redemption-fee-block-interval collateral-info)) |
| 180 | + fee-block-last |
| 181 | + )) |
| 182 | + (fee-block-change (/ debt-payoff-used (get redemption-fee-block-rate collateral-info))) |
| 183 | + |
| 184 | + (new-redemption-last-block (if (< (+ fee-block-last-cap fee-block-change) burn-block-height) |
| 185 | + (+ fee-block-last-cap fee-block-change) |
| 186 | + burn-block-height |
| 187 | + )) |
| 188 | + |
| 189 | + (collateral-needed (/ (* (get collateral vault) debt-payoff-used) collateral-value)) |
| 190 | + (collateral-fee (/ (* collateral-needed (get current-fee fee)) u10000)) |
| 191 | + (collateral-received (- collateral-needed collateral-fee)) |
| 192 | + |
| 193 | + (collateral-left (- (get collateral vault) collateral-needed)) |
| 194 | + ) |
| 195 | + (asserts! (is-eq (contract-of vaults-tokens) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-tokens"))) (err ERR_WRONG_TRAIT)) |
| 196 | + (asserts! (is-eq (contract-of vaults-data) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-data"))) (err ERR_WRONG_TRAIT)) |
| 197 | + (asserts! (is-eq (contract-of vaults-sorted) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-sorted"))) (err ERR_WRONG_TRAIT)) |
| 198 | + (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)) |
| 199 | + (asserts! (is-eq (contract-of vaults-helpers) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "vaults-helpers"))) (err ERR_WRONG_TRAIT)) |
| 200 | + (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .arkadiko-dao get-qualified-name-by-name "oracle"))) (err ERR_WRONG_TRAIT)) |
| 201 | + (asserts! (not (var-get shutdown-activated)) (err ERR_SHUTDOWN)) |
| 202 | + (asserts! (is-eq owner (unwrap-panic (get first-owner token-list))) (err ERR_NOT_FIRST_VAULT)) |
| 203 | + |
| 204 | + (if (is-eq debt-left u0) |
| 205 | + ;; Vault closed |
| 206 | + (begin |
| 207 | + (try! (as-contract (contract-call? vaults-pool-active withdraw token owner collateral-left))) |
| 208 | + (try! (contract-call? vaults-data set-vault owner (contract-of token) STATUS_CLOSED_BY_REDEMPTION u0 u0)) |
| 209 | + (try! (contract-call? vaults-sorted remove owner (contract-of token))) |
| 210 | + ) |
| 211 | + |
| 212 | + ;; Partial redemption |
| 213 | + (let ( |
| 214 | + (nicr (/ (* collateral-left u100000000) debt-left)) |
| 215 | + ) |
| 216 | + (try! (contract-call? vaults-data set-vault owner (contract-of token) STATUS_ACTIVE collateral-left debt-left)) |
| 217 | + (try! (contract-call? vaults-sorted reinsert owner (contract-of token) nicr prev-owner-hint)) |
| 218 | + ) |
| 219 | + ) |
| 220 | + |
| 221 | + ;; Burn USDA |
| 222 | + (try! (as-contract (contract-call? .arkadiko-dao burn-token .usda-token debt-payoff-used redeemer))) |
| 223 | + |
| 224 | + ;; Send tokens to redeemer |
| 225 | + (try! (as-contract (contract-call? vaults-pool-active withdraw token redeemer collateral-received))) |
| 226 | + |
| 227 | + ;; Get fee |
| 228 | + (try! (as-contract (contract-call? vaults-pool-active withdraw token .arkadiko-vaults-pool-fees-v1-1 collateral-fee))) |
| 229 | + |
| 230 | + ;; Increase redemption fee by decreasing last block |
| 231 | + (map-set redemption-block-last { token: (contract-of token) } |
| 232 | + { block-last: new-redemption-last-block } |
| 233 | + ) |
| 234 | + |
| 235 | + ;; Return |
| 236 | + (ok { debt-payoff-used: debt-payoff-used, collateral-received: collateral-received, collateral-fee: collateral-fee }) |
| 237 | + ) |
| 238 | +) |
| 239 | + |
| 240 | +;; Get current redemption fee in bps |
| 241 | +;; Fee has a min/max and moves between these values |
| 242 | +;; Fee goes up on redemption, fee goes down over time |
| 243 | +(define-public (get-redemption-fee (vaults-tokens <vaults-tokens-trait>) (token principal)) |
| 244 | + (let ( |
| 245 | + (collateral-info (unwrap! (contract-call? vaults-tokens get-token token) (err ERR_UNKNOWN_TOKEN))) |
| 246 | + (fee-info (get-redemption-block-last token)) |
| 247 | + (fee-diff (- (get redemption-fee-max collateral-info) (get redemption-fee-min collateral-info))) |
| 248 | + (block-diff (- burn-block-height (get block-last fee-info))) |
| 249 | + (fee-change (/ (* fee-diff block-diff) (get redemption-fee-block-interval collateral-info))) |
| 250 | + (current-fee (if (>= fee-change fee-diff) |
| 251 | + (get redemption-fee-min collateral-info) |
| 252 | + (- (get redemption-fee-max collateral-info) fee-change) |
| 253 | + )) |
| 254 | + ) |
| 255 | + (ok { current-fee: current-fee }) |
| 256 | + ) |
| 257 | +) |
| 258 | + |
| 259 | +;; --------------------------------------------------------- |
| 260 | +;; Admin |
| 261 | +;; --------------------------------------------------------- |
| 262 | + |
| 263 | +(define-public (set-shutdown-activated (activated bool)) |
| 264 | + (begin |
| 265 | + (asserts! (is-eq contract-caller (contract-call? .arkadiko-dao get-dao-owner)) (err ERR_NOT_AUTHORIZED)) |
| 266 | + |
| 267 | + (var-set shutdown-activated activated) |
| 268 | + |
| 269 | + (ok true) |
| 270 | + ) |
| 271 | +) |
0 commit comments