(impl-trait .stackswap-manager-trait-v1b.vault-manager-trait)
(use-trait vault-trait .stackswap-vault-trait-v1b.vault-trait)
(use-trait ft-trait .sip-010-v1a.sip-010-trait)
(use-trait vault-manager-trait .stackswap-manager-trait-v1b.vault-manager-trait)
(use-trait collateral-types-trait .stackswap-collateral-types-trait-v1b.collateral-types-trait)
(use-trait oracle-trait .stackwap-oracle-trait-v1b.oracle-trait)

(define-constant ERR-NOT-AUTHORIZED u4401)
(define-constant ERR-TRANSFER-FAILED u42)
(define-constant ERR-MINTER-FAILED u43)
(define-constant ERR-BURN-FAILED u44)
(define-constant ERR-DEPOSIT-FAILED u45)
(define-constant ERR-WITHDRAW-FAILED u46)
(define-constant ERR-MINT-FAILED u47)
(define-constant ERR-LIQUIDATION-FAILED u48)
(define-constant ERR-INSUFFICIENT-COLLATERAL u49)
(define-constant ERR-INSUFFICIENT-LBTC u50)
(define-constant ERR-NO-LIQUIDATION-REQUIRED u52)
(define-constant ERR-MAXIMUM-DEBT-REACHED u410)
(define-constant ERR-BURN-HEIGHT-NOT-REACHED u412)
(define-constant ERR-VAULT-LIQUIDATED u413)
(define-constant ERR-STACKING-IN-PROGRESS u414)
(define-constant ERR-WRONG-COLLATERAL-TOKEN u415)
(define-constant ERR-VAULT-NOT-LIQUIDATED u416)
(define-constant ERR-WRONG-DEBT u417)
(define-constant ERR-LIQUIDATION-NOT-ENDED u418)
(define-constant ERR-WRONG-COLLATERAL-TYPE u419)
(define-constant ERR-VAULT-ALREADY-LIQUIDATED u420)
(define-constant ERR-VAULT-LIQUIDATION-ENDED u421)
(define-constant ERR-VAULT-LIQUIDATION-NOT-ENDED u422)
(define-constant ERR-VAULT-UPDATE u423)
(define-constant ERR-FEE-CALC u424)

(define-constant BLOCKS-PER-DAY u144)

(define-map stacking-unlock-burn-height
  { stacker-name: (string-ascii 256) }
  {
    height: uint
  }
)

(define-read-only (get-stacking-unlock-burn-height (name (string-ascii 256)))
  (ok (get height (unwrap-panic (map-get? stacking-unlock-burn-height { stacker-name: name }))))
)

(define-private (get-stacking-unlock-burn-height-calc (name (string-ascii 256) ))
  (match (map-get? stacking-unlock-burn-height { stacker-name: name }) height-tuple
    (get height height-tuple)
    u0
  )
)

(define-public (set-stacking-unlock-burn-height (name (string-ascii 256)) (burn-height uint))
  (begin
    (asserts! 
      (or
        (is-eq contract-caller (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stacker")))
        (is-eq contract-caller (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stacker-2")))
        (is-eq contract-caller (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stacker-3")))
        (is-eq contract-caller (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stacker-4")))
        (is-eq contract-caller (contract-call? .stackswap-dao-v5k get-dao-owner))
      )
      (err ERR-NOT-AUTHORIZED)
    )

    (ok (map-set stacking-unlock-burn-height { stacker-name: name } { height: burn-height }))
  )
)

(define-read-only (get-vault-by-id (vault-id uint))
  (contract-call? .stackswap-vault-data-v1b get-vault-by-id vault-id)
)

(define-read-only (get-vault-entries (user principal))
  (contract-call? .stackswap-vault-data-v1b get-vault-entries user)
)

(define-read-only (get-collateral-type-for-vault (vault-id uint))
  (let ((vault (get-vault-by-id vault-id)))
    (ok (get collateral-type vault))
  )
)

(define-read-only (get-collateral-token-for-vault (vault-id uint))
  (let ((vault (get-vault-by-id vault-id)))
    (ok (get collateral-token vault))
  )
)

(define-public (calculate-current-collateral-to-debt-ratio
  (vault-id uint)
  (coll-type <collateral-types-trait>)
  (oracle <oracle-trait>)
  (include-stability-fees bool)
)
  (let ((vault (get-vault-by-id vault-id)))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))
      (begin
        (let (
          (price (unwrap-panic (contract-call? oracle fetch-price (get collateral-token vault))))
          (btc-price (unwrap-panic (contract-call? oracle fetch-price "xBTC")))
          )
          (ok
            (/
              (/
                (* (* (get collateral vault) (get last-price price)) u100000000)
                (*
                  (+
                    (get debt vault)
                    (if include-stability-fees
                      (unwrap-panic (stability-fee-helper (get stability-fee-last-accrued vault) (get debt vault) (get collateral-type vault) coll-type))
                      u0
                    )
                  )
                  (get last-price btc-price)
                )
              )
              (/ (get decimals price) u100)
            )
          )
        )
      
    )
  )
)


(define-private (resolve-stacking-amount (collateral-amount uint) (collateral-token (string-ascii 12)) (stack-pox bool))
  (if (and (is-eq collateral-token "STX") stack-pox)
    collateral-amount
    u0
  )
)

(define-public (toggle-stacking (vault-id uint))
  (let (
    (vault (get-vault-by-id vault-id))
    (stacking-height (get-stacking-unlock-burn-height-calc (get stacker-name vault))))

    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq "STX" (get collateral-token vault)) (err ERR-WRONG-COLLATERAL-TOKEN))
    (asserts! (is-eq false (get is-liquidated vault)) (err ERR-VAULT-LIQUIDATED))
    (try! (contract-call? .stackswap-stx-reserve-v1b toggle-stacking (get stacker-name vault) (not (get revoked-stacking vault)) (get collateral vault)))

    (try!
      (contract-call? .stackswap-vault-data-v1b update-vault vault-id (merge vault {
        revoked-stacking: (not (get revoked-stacking vault)),
        updated-at-block-height: block-height,
        toggled-at-block-height: stacking-height
      }))
    )
    (ok true)
  )
)

;; can be called by the vault owner on a non-liquidated STX vault
;; called when collateral was unstacked & want to stack again
(define-public (stack-collateral (vault-id uint))
  (let (
    (vault (get-vault-by-id vault-id))
    (stacking-height (get-stacking-unlock-burn-height-calc (get stacker-name vault))))

    (asserts! (is-eq contract-caller (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq "STX" (get collateral-token vault)) (err ERR-WRONG-COLLATERAL-TOKEN))
    (asserts! (is-eq false (get is-liquidated vault)) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq u0 (get stacked-tokens vault)) (err ERR-STACKING-IN-PROGRESS))

    (try! (contract-call? .stackswap-stx-reserve-v1b add-tokens-to-stack (get stacker-name vault) (get collateral vault)))
    (try!
      (contract-call? .stackswap-vault-data-v1b update-vault vault-id (merge vault {
        stacked-tokens: (get collateral vault),
        revoked-stacking: false,
        updated-at-block-height: block-height,
        toggled-at-block-height: stacking-height
      }))
    )
    (ok true)
  )
)

(define-private (min-of (i1 uint) (i2 uint))
  (if (< i1 i2)
      i1
      i2))

(define-public (collateralize-and-mint
    (collateral-amount uint)
    (debt uint)
    (stack-pox bool)
    (collateral-type (string-ascii 12))
    (reserve <vault-trait>)
    (ft <ft-trait>)
    (coll-type <collateral-types-trait>)
    (oracle <oracle-trait>)
  )
  (let (
    (sender tx-sender)
    (collateral-type-object (unwrap-panic (contract-call? coll-type get-collateral-type-by-name collateral-type)))
    (collateral-token (get token collateral-type-object))
    (check-wrong-col-type (asserts! (not (is-eq "" (get name collateral-type-object))) (err ERR-WRONG-COLLATERAL-TYPE)))
    (stacker-name (unwrap-panic (contract-call? reserve get-next-stacker-name)))
  )

    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))
    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts!
      (or
        (is-eq collateral-token "STX")
        (is-eq (get token-address collateral-type-object) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )
    (asserts!
      (<=
        (+ debt (get total-debt collateral-type-object))
        (get maximum-debt collateral-type-object)
      )
      (err ERR-MAXIMUM-DEBT-REACHED)
    )

    (try! (contract-call? reserve collateralize-and-mint ft collateral-token collateral-amount debt sender stacker-name stack-pox))
    (try! (as-contract (contract-call? .lbtc-token-v1b mint-for-dao debt sender)))
    (let (
      (vault-id (+ (contract-call? .stackswap-vault-data-v1b get-last-vault-id) u1))
      (vault {
        id: vault-id,
        owner: sender,
        collateral: collateral-amount,
        collateral-type: collateral-type,
        collateral-token: collateral-token,
        stacked-tokens: (resolve-stacking-amount collateral-amount collateral-token stack-pox),
        stacker-name: stacker-name,
        revoked-stacking: (not stack-pox),
        debt: debt,
        created-at-block-height: block-height,
        updated-at-block-height: block-height,
        toggled-at-block-height: burn-block-height,
        stability-fee-accrued: u0,
        stability-fee-last-accrued: block-height,
        is-liquidated: false,
        liquidation-finished: false,
      })
    )
      (try! (contract-call? .stackswap-vault-data-v1b update-vault-entries sender vault-id))
      (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id vault))
      (asserts! (>= 
        (unwrap!
          (calculate-current-collateral-to-debt-ratio 
            vault-id
            coll-type
            oracle
            false
          )
          (err ERR-WRONG-DEBT)
        )
        (get collateral-to-debt-ratio collateral-type-object)) (err ERR-INSUFFICIENT-COLLATERAL))
      (try! (contract-call? .stackswap-vault-data-v1b set-last-vault-id vault-id))
      (try! (contract-call? coll-type add-debt-to-collateral-type collateral-type debt))
      (print { type: "vault", action: "created", data: vault })
      (ok debt)
    )
  )
)

(define-public (deposit
  (vault-id uint)
  (uamount uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (collateral-token (unwrap-panic (get-collateral-token-for-vault vault-id)))
    (new-collateral (+ uamount (get collateral vault)))
    (updated-vault (merge vault {
      collateral: new-collateral,
      updated-at-block-height: block-height
    }))
  )

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) false) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts!
      (or
        (is-eq collateral-token "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )

    (unwrap! (contract-call? reserve deposit ft collateral-token uamount (get stacker-name vault)) (err ERR-DEPOSIT-FAILED))
    (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
    (print { type: "vault", action: "deposit", data: updated-vault })
    (ok true)
  )
)

(define-public (withdraw
  (vault-id uint)
  (uamount uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
  (oracle <oracle-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (collateral-token (unwrap-panic (get-collateral-token-for-vault vault-id)))
  )

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) false) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts! (> uamount u0) (err ERR-INSUFFICIENT-COLLATERAL))
    (asserts! (<= uamount (get collateral vault)) (err ERR-INSUFFICIENT-COLLATERAL))
    (asserts! (is-eq u0 (get stacked-tokens vault)) (err ERR-STACKING-IN-PROGRESS))
    (asserts!
      (or
        (is-eq collateral-token "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )
    (try! (accrue-stability-fee vault-id coll-type))
    (let (
      (new-collateral (- (get collateral vault) uamount))
      (updated-vault (merge vault {
        collateral: new-collateral,
        updated-at-block-height: block-height
      }))
      (update-result (unwrap! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault) (err ERR-VAULT-UPDATE)))
      (ratio (unwrap-panic 
        (calculate-current-collateral-to-debt-ratio 
          vault-id
          coll-type
          oracle
          true
        )
      ))
      )
          
      (asserts! (>= ratio (unwrap-panic (contract-call? coll-type get-collateral-to-debt-ratio (get collateral-type vault)))) (err ERR-INSUFFICIENT-COLLATERAL))
      (unwrap! (contract-call? reserve withdraw ft collateral-token (get owner vault) uamount) (err ERR-WITHDRAW-FAILED))
      (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
      (print { type: "vault", action: "withdraw", data: updated-vault })
      (ok true)
    )
  )
)


(define-public (mint
  (vault-id uint)
  (extra-debt uint)
  (reserve <vault-trait>)
  (coll-type <collateral-types-trait>)
  (oracle <oracle-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (new-total-debt (+ extra-debt (get debt vault)))
    (updated-vault (merge vault {
      debt: new-total-debt,
      updated-at-block-height: block-height
    }))
    (collateral-type (unwrap-panic (contract-call? coll-type get-collateral-type-by-name (get collateral-type vault))))
  )
    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) false) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts!
      (<=
        (+ extra-debt (get total-debt collateral-type))
        (get maximum-debt collateral-type)
      )
      (err ERR-MAXIMUM-DEBT-REACHED)
    )

    (try! (accrue-stability-fee vault-id coll-type))
    (try! (contract-call? reserve mint
        (get collateral-token vault)
        (get owner vault)
        (get collateral vault)
        (get debt vault)
        extra-debt
        (get collateral-to-debt-ratio collateral-type)
        oracle
      )
    )
    (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
    (asserts! (>= 
      (unwrap!
        (calculate-current-collateral-to-debt-ratio 
          vault-id
          coll-type
          oracle
          true
        )
        (err ERR-WRONG-DEBT)
      )
      (get collateral-to-debt-ratio collateral-type)) (err ERR-INSUFFICIENT-COLLATERAL))
    (try! (contract-call? coll-type add-debt-to-collateral-type (get collateral-type vault) extra-debt))
    (print { type: "vault", action: "mint", data: updated-vault })
    (ok true)
  )
)

(define-public (burn
  (vault-id uint)
  (debt uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
)
  (let ((vault (get-vault-by-id vault-id)))

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) false) (err ERR-VAULT-LIQUIDATED))
    (asserts!
      (or
        (is-eq (get collateral-token vault) "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )
    (try! (pay-stability-fee vault-id coll-type))
    (print { type: "vault", action: "burn", data: vault })
    (burn-partial-debt vault-id (min-of debt (get debt vault)) coll-type)
  )
)

(define-public (close-vault
  (vault-id uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
)
  (let ((vault (get-vault-by-id vault-id))
       (updated-vault (merge vault {
          collateral: u0,
          debt: u0,
          updated-at-block-height: block-height
        })))
    (asserts! (is-eq u0 (get stacked-tokens vault)) (err ERR-STACKING-IN-PROGRESS))
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) false) (err ERR-VAULT-LIQUIDATED))
    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts!
      (or
        (is-eq (get collateral-token vault) "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )

    (if (is-eq (get debt vault) u0)
      true
      (try! (contract-call? .lbtc-token-v1b burn-for-dao (get debt vault) (get owner vault)))
    )
    (try! (pay-stability-fee vault-id coll-type))
    (try! (contract-call? reserve burn ft (get owner vault) (get collateral vault)))
    (try! (contract-call? coll-type subtract-debt-from-collateral-type (get collateral-type vault) (get debt vault)))
    (print { type: "vault", action: "close", data: updated-vault })
    (try! (contract-call? .stackswap-vault-data-v1b close-vault vault-id))
    (ok true)
  )
)

(define-private (burn-partial-debt
  (vault-id uint)
  (debt uint)
  (coll-type <collateral-types-trait>)
)
  (let ((vault (get-vault-by-id vault-id)))
    (try! (contract-call? .lbtc-token-v1b burn-for-dao debt tx-sender))
    (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id (merge vault {
        debt: (- (get debt vault) debt),
        updated-at-block-height: block-height
      }))
    )
    (try! (contract-call? coll-type subtract-debt-from-collateral-type (get collateral-type vault) debt))
    (ok true)
  )
)


(define-public (get-stability-fee-for-vault
  (vault-id uint)
  (coll-type <collateral-types-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
  )
    (stability-fee-helper (get stability-fee-last-accrued vault) (get debt vault) (get collateral-type vault) coll-type)
  )
)

(define-private (stability-fee-helper
  (stability-fee-last-accrued uint)
  (debt uint)
  (collateral-type-string (string-ascii 12))
  (coll-type <collateral-types-trait>)
)
  (let (
    (number-of-blocks (- block-height stability-fee-last-accrued))
    (collateral-type (unwrap-panic (contract-call? coll-type get-collateral-type-by-name collateral-type-string)))
    (fee (get stability-fee collateral-type))
    (decimals (get stability-fee-decimals collateral-type))
    (interest (/ (* number-of-blocks (* debt fee)) (pow u10 decimals)))
  )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (ok interest)
  )
)

(define-public (accrue-stability-fee
  (vault-id uint)
  (coll-type <collateral-types-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
  )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id (merge vault {
        updated-at-block-height: block-height,
        stability-fee-accrued: (unwrap-panic (get-stability-fee-for-vault vault-id coll-type)),
        stability-fee-last-accrued: block-height
      }))
    )
    (ok true)
  )
)

(define-public (pay-stability-fee
  (vault-id uint)
  (coll-type <collateral-types-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (fee (+ (get stability-fee-accrued vault) (unwrap-panic (get-stability-fee-for-vault vault-id coll-type))))
  )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) false) (err ERR-VAULT-LIQUIDATED))
    (if (> fee u0)
      (begin
        (try! (contract-call? .lbtc-token-v1b transfer fee tx-sender (as-contract tx-sender) none))
        (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id (merge vault {
            updated-at-block-height: block-height,
            stability-fee-accrued: u0,
            stability-fee-last-accrued: block-height
          }))
        )
        (ok fee)
      )
      (ok fee)
    )
  )
)

(define-public (notify-risky-vault
  (vault-id uint)
  (coll-type <collateral-types-trait>)
  (oracle <oracle-trait>)
)
  (let (
    (collateral-type (unwrap-panic (get-collateral-type-for-vault vault-id)))
    (liquidation-ratio (unwrap-panic (contract-call? coll-type get-liquidation-ratio collateral-type)))
  )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))

    (asserts! (>= 
      liquidation-ratio
      (unwrap!
        (calculate-current-collateral-to-debt-ratio 
          vault-id
          coll-type
          oracle
          true
        )
        (err ERR-WRONG-DEBT)
      )
      ) (err ERR-NO-LIQUIDATION-REQUIRED))

    (print "Vault is in danger. Time to liquidate.")
    (liquidate vault-id coll-type)
  )
)


(define-private (liquidate
  (vault-id uint)
  (coll-type <collateral-types-trait>)
)
  (let ((vault (get-vault-by-id vault-id)))

    (let (
      (collateral (get collateral vault))
      (fee (unwrap-panic (get-stability-fee-for-vault vault-id coll-type)))
    )
      (print { type: "vault", action: "liquidated", data: vault })
      (begin
        (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id (merge vault {
            updated-at-block-height: block-height,
            is-liquidated: true,
            liquidation-finished: false,
          }))
        )
        (ok (tuple (ustx-amount collateral) (vault-debt (+ fee (get debt vault))) ))
      )
    )
  )
)

(define-public (burn-to-unliquidate
  (vault-id uint)
  (debt uint)
  (reserve <vault-trait>)
  (coll-type <collateral-types-trait>)
  (oracle <oracle-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (new-total-debt (- (get debt vault) debt))
    (updated-vault (merge vault {
      is-liquidated: false,
      updated-at-block-height: block-height
    }))
    (collateral-type (unwrap-panic (contract-call? coll-type get-collateral-type-by-name (get collateral-type vault))))
  )

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))

    (asserts! (is-eq (get is-liquidated vault) true) (err ERR-VAULT-LIQUIDATED))
    (asserts! (< block-height (+ (get updated-at-block-height vault) u144)) (err ERR-VAULT-LIQUIDATION-ENDED))
    (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
    (try! (accrue-stability-fee vault-id coll-type))
    (try! (burn-partial-debt vault-id (min-of debt (get debt vault)) coll-type))
    (asserts! (>= 
      (unwrap!
        (calculate-current-collateral-to-debt-ratio 
          vault-id
          coll-type
          oracle
          true
        )
        (err ERR-WRONG-DEBT)
      )
      (get collateral-to-debt-ratio collateral-type)) (err ERR-INSUFFICIENT-LBTC))
    (print { type: "vault", action: "burn-to-unliquidate", data: updated-vault })
    (ok true)
  )
)


(define-public (deposit-to-unliquidate
  (vault-id uint)
  (uamount uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
  (oracle <oracle-trait>)
)
  (let (
      (vault (get-vault-by-id vault-id))
      (collateral-token (unwrap-panic (get-collateral-token-for-vault vault-id)))
      (new-collateral (+ uamount (get collateral vault)))
      (updated-vault (merge vault {
        collateral: new-collateral,
        is-liquidated: false,
        updated-at-block-height: block-height
      }))
      (collateral-type (unwrap-panic (contract-call? coll-type get-collateral-type-by-name (get collateral-type vault))))
    )

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )

    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (contract-of oracle) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-oracle"))) (err ERR-NOT-AUTHORIZED))

    (asserts! (is-eq (get is-liquidated vault) true) (err ERR-VAULT-LIQUIDATED))
    (asserts! (< block-height (+ (get updated-at-block-height vault) u144)) (err ERR-VAULT-LIQUIDATION-ENDED))

    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts!
      (or
        (is-eq collateral-token "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )

    (unwrap! (contract-call? reserve deposit ft collateral-token uamount (get stacker-name vault)) (err ERR-DEPOSIT-FAILED))
    (try! (accrue-stability-fee vault-id coll-type))
    (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
    (asserts! (>= 
      (unwrap!
        (calculate-current-collateral-to-debt-ratio 
          vault-id
          coll-type
          oracle
          true
        )
        (err ERR-WRONG-DEBT)
      )
      (get collateral-to-debt-ratio collateral-type)) (err ERR-INSUFFICIENT-COLLATERAL))
    (print { type: "vault", action: "deposit-to-unliquidate", data: updated-vault })
    (ok true)
  )
)

(define-public (finalize-liquidation
  (vault-id uint)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (stacking-height (get-stacking-unlock-burn-height-calc (get stacker-name vault)))
    (payout-address (contract-call? .stackswap-dao-v5k get-payout-address))
    )

    (asserts! (is-eq (get is-liquidated vault) true) (err ERR-VAULT-NOT-LIQUIDATED))
    (asserts! (is-eq (get liquidation-finished vault) false) (err ERR-VAULT-ALREADY-LIQUIDATED))
    (asserts! (> block-height (+ (get updated-at-block-height vault) u144)) (err ERR-VAULT-LIQUIDATION-NOT-ENDED))
  
    (if (and (not (get revoked-stacking vault)) (is-eq "STX" (get collateral-token vault)))
      (try! (contract-call? .stackswap-stx-reserve-v1b toggle-stacking (get stacker-name vault) true (get collateral vault)))
      u0
    )
    (try! (contract-call? .stackswap-vault-data-v1b finalize-liquidate-vault vault-id (merge vault {
        owner: payout-address,
        updated-at-block-height: block-height,
        liquidation-finished: true,
        revoked-stacking: true,
        toggled-at-block-height: stacking-height
      }))
    )
    (ok true)
  )
)


(define-read-only (get-lbtc-balance)
  (contract-call? .lbtc-token-v1b get-balance (as-contract tx-sender))
)

(define-read-only (get-stsw-balance)
  (contract-call? .stsw-token-v4a get-balance (as-contract tx-sender))
)


(define-public (redeem-tokens (lbtc-amount uint) (stsw-amount uint))
  (begin

    (if (and (> lbtc-amount u0) (> stsw-amount u0))
      (begin
        (try! (as-contract (contract-call? .stsw-token-v4a transfer stsw-amount tx-sender (contract-call? .stackswap-dao-v5k get-payout-address) none)))
        (as-contract (contract-call? .lbtc-token-v1b transfer lbtc-amount tx-sender (contract-call? .stackswap-dao-v5k get-payout-address) none))
      )
      (if (> lbtc-amount u0)
        (as-contract (contract-call? .lbtc-token-v1b transfer lbtc-amount tx-sender (contract-call? .stackswap-dao-v5k get-payout-address) none))
        (as-contract (contract-call? .stsw-token-v4a transfer stsw-amount tx-sender (contract-call? .stackswap-dao-v5k get-payout-address) none))
      )
    )
  )
)


(define-public (withdraw-liquidated
  (vault-id uint)
  (uamount uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
)
  (let (
    (vault (get-vault-by-id vault-id))
    (collateral-token (unwrap-panic (get-collateral-token-for-vault vault-id)))
  )

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) true) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq (get liquidation-finished vault) true) (err ERR-LIQUIDATION-NOT-ENDED))

    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts! (> uamount u0) (err ERR-INSUFFICIENT-COLLATERAL))
    (asserts! (<= uamount (get collateral vault)) (err ERR-INSUFFICIENT-COLLATERAL))
    (asserts! (is-eq u0 (get stacked-tokens vault)) (err ERR-STACKING-IN-PROGRESS))
    (asserts!
      (or
        (is-eq collateral-token "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )

    (let (
      (new-collateral (- (get collateral vault) uamount))
      (updated-vault (merge vault {
        collateral: new-collateral,
        updated-at-block-height: block-height
      })))
      (unwrap! (contract-call? reserve withdraw ft collateral-token (get owner vault) uamount) (err ERR-WITHDRAW-FAILED))
      (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
      (print { type: "vault", action: "withdraw", data: updated-vault })
      (ok true)
    )
  )
)

(define-public (burn-liquidated
  (vault-id uint)
  (debt uint)
  (reserve <vault-trait>)
  (coll-type <collateral-types-trait>)
)
  (let ((vault (get-vault-by-id vault-id)))

    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))

    (asserts! (is-eq (get is-liquidated vault) true) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq (get liquidation-finished vault) true) (err ERR-LIQUIDATION-NOT-ENDED))


    (print { type: "vault", action: "burn", data: vault })
    (burn-partial-debt vault-id (min-of debt (get debt vault)) coll-type)
  )
)


(define-public (close-vault-liquidated
  (vault-id uint)
  (reserve <vault-trait>)
  (ft <ft-trait>)
  (coll-type <collateral-types-trait>)
)
  (let ((vault (get-vault-by-id vault-id))
       (updated-vault (merge vault {
          collateral: u0,
          debt: u0,
          updated-at-block-height: block-height
        })))
    (asserts! (is-eq tx-sender (get owner vault)) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq u0 (get stacked-tokens vault)) (err ERR-STACKING-IN-PROGRESS))
    (asserts! (is-eq (contract-of coll-type) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-collateral-types"))) (err ERR-NOT-AUTHORIZED))
    (asserts! (is-eq (get is-liquidated vault) true) (err ERR-VAULT-LIQUIDATED))
    (asserts! (is-eq (get liquidation-finished vault) true) (err ERR-LIQUIDATION-NOT-ENDED))
    (asserts!
      (or
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-stx-reserve")))
        (is-eq (contract-of reserve) (unwrap-panic (contract-call? .stackswap-dao-v5k get-qualified-name-by-name "l-sip10-reserve")))
      )
      (err ERR-NOT-AUTHORIZED)
    )
    (asserts!
      (or
        (is-eq (get collateral-token vault) "STX")
        (is-eq (unwrap-panic (contract-call? coll-type get-token-address (get collateral-type vault))) (contract-of ft))
      )
      (err ERR-WRONG-COLLATERAL-TOKEN)
    )

    (if (is-eq (get debt vault) u0)
      true
      (try! (contract-call? .lbtc-token-v1b burn-for-dao (get debt vault) (get owner vault)))
    )
    (if (is-eq (get collateral vault) u0)
      true
      (try! (contract-call? reserve burn ft (get owner vault) (get collateral vault)))
    )
    (try! (contract-call? coll-type subtract-debt-from-collateral-type (get collateral-type vault) (get debt vault)))
    ;; (try! (contract-call? .stackswap-vault-data-v1b update-vault vault-id updated-vault))
    (print { type: "vault", action: "close-liquidated", data: updated-vault })
    (try! (contract-call? .stackswap-vault-data-v1b close-vault-liquidated vault-id))
    (ok true)
  )
)


;; initialization
(map-set stacking-unlock-burn-height { stacker-name: "l-stacker" } { height: u0 })
(map-set stacking-unlock-burn-height { stacker-name: "l-stacker-2" } { height: u0 })
(map-set stacking-unlock-burn-height { stacker-name: "l-stacker-3" } { height: u0 })
(map-set stacking-unlock-burn-height { stacker-name: "l-stacker-4" } { height: u0 })
