|
| 1 | +;; FlashStack - Zest Flash Liquidation Receiver (STX + sBTC) |
| 2 | +;; |
| 3 | +;; Executes zero-capital liquidations on Zest Protocol using FlashStack flash loans. |
| 4 | +;; Supports both STX and canonical sBTC flash loans depending on the debt asset. |
| 5 | +;; |
| 6 | +;; Two entry points: |
| 7 | +;; execute-stx-flash - called by flashstack-stx-core (STX debt positions) |
| 8 | +;; execute-sbtc-flash - called by flashstack-sbtc-core (sBTC debt positions) |
| 9 | +;; |
| 10 | +;; Flow (same for both): |
| 11 | +;; 1. FlashStack core transfers asset to this contract |
| 12 | +;; 2. This contract calls Zest's liquidation-call with borrowed funds |
| 13 | +;; 3. Zest sends collateral bonus to this contract |
| 14 | +;; 4. Repay FlashStack core: principal + 0.05% fee |
| 15 | +;; 5. Surplus profit stays here - sweep via sweep-stx / sweep-sbtc |
| 16 | +;; |
| 17 | +;; Liquidation economics: |
| 18 | +;; Profit = liquidation_bonus - flash_loan_fee |
| 19 | +;; e.g. 5% bonus on 1000 STX loan = 50 STX bonus - 0.5 STX fee = 49.5 STX profit |
| 20 | +;; |
| 21 | +;; Integration note: |
| 22 | +;; Zest function signatures marked as TODO pending confirmation from Zest team. |
| 23 | +;; |
| 24 | +;; Must be whitelisted in both flashstack-stx-core and flashstack-sbtc-core before use. |
| 25 | + |
| 26 | +(impl-trait 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.flashstack-stx-core.stx-flash-receiver-trait) |
| 27 | +(impl-trait 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.sbtc-flash-receiver-trait.sbtc-flash-receiver-trait) |
| 28 | + |
| 29 | +;; ============================================= |
| 30 | +;; Constants |
| 31 | +;; ============================================= |
| 32 | + |
| 33 | +;; TODO: Confirm Zest pool address with Zest team |
| 34 | +(define-constant ZEST-POOL 'SP2ZNGJ85ENDY6QRHQ5P2D4FXKGZWCKTB2T0Z55KS.pool-borrow-v2-0) |
| 35 | +(define-constant SBTC 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token) |
| 36 | +(define-constant STX-CORE 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.flashstack-stx-core) |
| 37 | +(define-constant SBTC-CORE 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.flashstack-sbtc-core) |
| 38 | + |
| 39 | +(define-constant ERR-NOT-OWNER (err u800)) |
| 40 | +(define-constant ERR-REPAY-FAILED (err u801)) |
| 41 | +(define-constant ERR-LIQUIDATION (err u802)) |
| 42 | +(define-constant ERR-INSUFFICIENT (err u803)) |
| 43 | +(define-constant ERR-ZERO-AMOUNT (err u804)) |
| 44 | + |
| 45 | +;; ============================================= |
| 46 | +;; State |
| 47 | +;; ============================================= |
| 48 | + |
| 49 | +(define-data-var owner principal tx-sender) |
| 50 | + |
| 51 | +;; ============================================= |
| 52 | +;; STX Flash Loan Callback |
| 53 | +;; Use when: borrower has STX-denominated debt on Zest |
| 54 | +;; Flash borrows from: flashstack-stx-core |
| 55 | +;; ============================================= |
| 56 | + |
| 57 | +(define-public (execute-stx-flash (amount uint) (core principal)) |
| 58 | + (let ( |
| 59 | + (fee-bp (unwrap! (contract-call? 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.flashstack-stx-core |
| 60 | + get-fee-basis-points) ERR-REPAY-FAILED)) |
| 61 | + (raw-fee (/ (* amount fee-bp) u10000)) |
| 62 | + (fee (if (> raw-fee u0) raw-fee u1)) |
| 63 | + (owed (+ amount fee)) |
| 64 | + ) |
| 65 | + ;; ── Step 1: Liquidate the Zest position using borrowed STX ────────────── |
| 66 | + ;; TODO: Replace with confirmed Zest liquidation-call signature. |
| 67 | + ;; Typical pattern (AAVE-style): |
| 68 | + ;; (as-contract (contract-call? ZEST-POOL liquidation-call |
| 69 | + ;; collateral-asset ;; e.g. 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
| 70 | + ;; debt-asset ;; STX (native) |
| 71 | + ;; borrower ;; principal being liquidated |
| 72 | + ;; amount ;; debt amount to repay |
| 73 | + ;; true ;; receive underlying collateral (not aToken) |
| 74 | + ;; )) |
| 75 | + |
| 76 | + ;; ── Step 2: Verify we received enough to repay ─────────────────────────── |
| 77 | + (let ((stx-balance (stx-get-balance (as-contract tx-sender)))) |
| 78 | + (asserts! (>= stx-balance owed) ERR-INSUFFICIENT) |
| 79 | + |
| 80 | + ;; ── Step 3: Repay STX flash loan (principal + fee) ─────────────────── |
| 81 | + (unwrap! |
| 82 | + (as-contract (stx-transfer? owed tx-sender core)) |
| 83 | + ERR-REPAY-FAILED) |
| 84 | + |
| 85 | + (ok true) |
| 86 | + ) |
| 87 | + ) |
| 88 | +) |
| 89 | + |
| 90 | +;; ============================================= |
| 91 | +;; sBTC Flash Loan Callback |
| 92 | +;; Use when: borrower has sBTC-denominated debt on Zest |
| 93 | +;; Flash borrows from: flashstack-sbtc-core |
| 94 | +;; ============================================= |
| 95 | + |
| 96 | +(define-public (execute-sbtc-flash (amount uint) (core principal)) |
| 97 | + (let ( |
| 98 | + (fee-bp (unwrap! (contract-call? 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.flashstack-sbtc-core |
| 99 | + get-fee-basis-points) ERR-REPAY-FAILED)) |
| 100 | + (raw-fee (/ (* amount fee-bp) u10000)) |
| 101 | + (fee (if (> raw-fee u0) raw-fee u1)) |
| 102 | + (owed (+ amount fee)) |
| 103 | + ) |
| 104 | + ;; ── Step 1: Liquidate the Zest position using borrowed sBTC ───────────── |
| 105 | + ;; TODO: Replace with confirmed Zest liquidation-call signature. |
| 106 | + ;; Typical pattern: |
| 107 | + ;; (as-contract (contract-call? ZEST-POOL liquidation-call |
| 108 | + ;; collateral-asset ;; e.g. STX or xBTC |
| 109 | + ;; debt-asset ;; 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
| 110 | + ;; borrower ;; principal being liquidated |
| 111 | + ;; amount ;; sBTC debt amount to repay (sats) |
| 112 | + ;; true ;; receive underlying collateral |
| 113 | + ;; )) |
| 114 | + |
| 115 | + ;; ── Step 2: Verify sufficient sBTC to repay ────────────────────────────── |
| 116 | + (let ((sbtc-balance (unwrap! |
| 117 | + (as-contract (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
| 118 | + get-balance tx-sender)) |
| 119 | + ERR-REPAY-FAILED))) |
| 120 | + (asserts! (>= sbtc-balance owed) ERR-INSUFFICIENT) |
| 121 | + |
| 122 | + ;; ── Step 3: Repay sBTC flash loan (principal + fee) ────────────────── |
| 123 | + (unwrap! |
| 124 | + (as-contract (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
| 125 | + transfer owed tx-sender core none)) |
| 126 | + ERR-REPAY-FAILED) |
| 127 | + |
| 128 | + (ok true) |
| 129 | + ) |
| 130 | + ) |
| 131 | +) |
| 132 | + |
| 133 | +;; ============================================= |
| 134 | +;; Owner Functions |
| 135 | +;; ============================================= |
| 136 | + |
| 137 | +(define-public (sweep-stx (amount uint)) |
| 138 | + (begin |
| 139 | + (asserts! (is-eq tx-sender (var-get owner)) ERR-NOT-OWNER) |
| 140 | + (asserts! (> amount u0) ERR-ZERO-AMOUNT) |
| 141 | + (unwrap! |
| 142 | + (as-contract (stx-transfer? amount tx-sender (var-get owner))) |
| 143 | + ERR-REPAY-FAILED) |
| 144 | + (ok true) |
| 145 | + ) |
| 146 | +) |
| 147 | + |
| 148 | +(define-public (sweep-sbtc (amount uint)) |
| 149 | + (begin |
| 150 | + (asserts! (is-eq tx-sender (var-get owner)) ERR-NOT-OWNER) |
| 151 | + (asserts! (> amount u0) ERR-ZERO-AMOUNT) |
| 152 | + (unwrap! |
| 153 | + (as-contract (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
| 154 | + transfer amount tx-sender (var-get owner) none)) |
| 155 | + ERR-REPAY-FAILED) |
| 156 | + (ok true) |
| 157 | + ) |
| 158 | +) |
| 159 | + |
| 160 | +(define-public (set-owner (new-owner principal)) |
| 161 | + (begin |
| 162 | + (asserts! (is-eq tx-sender (var-get owner)) ERR-NOT-OWNER) |
| 163 | + (ok (var-set owner new-owner)) |
| 164 | + ) |
| 165 | +) |
| 166 | + |
| 167 | +;; ============================================= |
| 168 | +;; Read-only |
| 169 | +;; ============================================= |
| 170 | + |
| 171 | +;; Pre-flight profitability check before executing a liquidation. |
| 172 | +;; debt-amount : amount to repay (microSTX or sats) |
| 173 | +;; bonus-bp : Zest liquidation bonus in basis points (e.g. u500 = 5%) |
| 174 | +(define-read-only (simulate (debt-amount uint) (bonus-bp uint)) |
| 175 | + (let ( |
| 176 | + (raw-fee (/ (* debt-amount u5) u10000)) |
| 177 | + (fee (if (> raw-fee u0) raw-fee u1)) |
| 178 | + (bonus (/ (* debt-amount bonus-bp) u10000)) |
| 179 | + (profit (if (> bonus fee) (- bonus fee) u0)) |
| 180 | + ) |
| 181 | + { |
| 182 | + debt-amount: debt-amount, |
| 183 | + bonus-bp: bonus-bp, |
| 184 | + bonus: bonus, |
| 185 | + flash-fee: fee, |
| 186 | + net-profit: profit, |
| 187 | + profitable: (> bonus fee), |
| 188 | + owed-to-core: (+ debt-amount fee), |
| 189 | + } |
| 190 | + ) |
| 191 | +) |
| 192 | + |
| 193 | +(define-read-only (get-stx-balance) |
| 194 | + (stx-get-balance (as-contract tx-sender)) |
| 195 | +) |
| 196 | + |
| 197 | +(define-read-only (get-sbtc-balance) |
| 198 | + (as-contract (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
| 199 | + get-balance tx-sender)) |
| 200 | +) |
| 201 | + |
| 202 | +(define-read-only (get-owner) |
| 203 | + (ok (var-get owner)) |
| 204 | +) |
0 commit comments