|
39 | 39 | ;; Whitelist for approved receiver contracts |
40 | 40 | (define-map approved-receivers principal bool) |
41 | 41 |
|
42 | | -;; Per-block volume tracking |
| 42 | +;; Per-block volume tracking (uses block-height - M-02 fix) |
43 | 43 | (define-map block-loan-volume uint uint) |
44 | 44 |
|
| 45 | +;; Treasury address for collected fees (H-01 fix) |
| 46 | +(define-data-var treasury principal tx-sender) |
| 47 | + |
45 | 48 | ;; Collateral ratio: 300% = 3x leverage max |
46 | 49 | (define-constant MIN-COLLATERAL-RATIO u300) |
47 | 50 |
|
|
82 | 85 | (locked-stx (get-stx-locked borrower)) |
83 | 86 | (min-required (/ (* amount MIN-COLLATERAL-RATIO) u100)) |
84 | 87 | (receiver-principal (contract-of receiver)) |
85 | | - (fee (/ (* amount (var-get flash-fee-basis-points)) u10000)) |
| 88 | + ;; L-01 fix: enforce minimum fee of 1 sat to prevent free flash loans |
| 89 | + (raw-fee (/ (* amount (var-get flash-fee-basis-points)) u10000)) |
| 90 | + ;; L-01 fix: enforce minimum fee of 1 sat to prevent free flash loans on tiny amounts |
| 91 | + (fee (if (> raw-fee u0) raw-fee u1)) |
86 | 92 | (total-owed (+ amount fee)) |
87 | 93 | ) |
88 | 94 | ;; ===== SECURITY CHECKS ===== |
|
99 | 105 | ;; Circuit breaker: Check single loan limit |
100 | 106 | (asserts! (<= amount (var-get max-single-loan)) ERR-LOAN-TOO-LARGE) |
101 | 107 |
|
102 | | - ;; Circuit breaker: Check block volume limit |
| 108 | + ;; Circuit breaker: Check block volume limit (M-02: use block-height) |
103 | 109 | (let ( |
104 | 110 | (current-block-volume (default-to u0 (map-get? block-loan-volume block-height))) |
105 | 111 | (new-block-volume (+ current-block-volume amount)) |
|
124 | 130 | ;; Execute callback |
125 | 131 | (match (contract-call? receiver execute-flash amount borrower) |
126 | 132 | success (begin |
127 | | - ;; Burn the returned tokens to complete the cycle |
128 | | - (try! (as-contract (contract-call? .sbtc-token burn total-owed tx-sender))) |
129 | | - |
130 | | - ;; Verify supply invariant: total supply must equal pre-mint level, |
131 | | - ;; proving the exact minted amount was burned (not satisfied via pre-existing tokens) |
| 133 | + ;; H-01 fix: Burn only the principal (amount), not the fee. |
| 134 | + ;; The receiver returns total-owed to this contract; we burn |
| 135 | + ;; only amount and transfer the fee to treasury. |
| 136 | + (try! (as-contract (contract-call? .sbtc-token burn amount tx-sender))) |
| 137 | + |
| 138 | + ;; Transfer fee to treasury (H-01: protocol earns the fee) |
| 139 | + (try! (as-contract (contract-call? .sbtc-token transfer |
| 140 | + fee |
| 141 | + tx-sender |
| 142 | + (var-get treasury) |
| 143 | + (some 0x464c415348) |
| 144 | + ))) |
| 145 | + |
| 146 | + ;; Verify supply invariant: supply must have decreased by exactly amount |
| 147 | + ;; (amount burned + fee still exists in treasury, so supply-after = supply-before + fee) |
| 148 | + ;; The invariant we enforce: supply-after == supply-before + fee |
| 149 | + ;; This proves amount was burned and fee was not destroyed. |
132 | 150 | (let ( |
133 | 151 | (supply-after (unwrap! (contract-call? .sbtc-token get-total-supply) ERR-REPAY-FAILED)) |
134 | 152 | ) |
135 | | - (asserts! (is-eq supply-after supply-before) ERR-REPAY-FAILED) |
| 153 | + (asserts! (is-eq supply-after (+ supply-before fee)) ERR-REPAY-FAILED) |
136 | 154 |
|
137 | 155 | (var-set total-flash-mints (+ (var-get total-flash-mints) u1)) |
138 | 156 | (var-set total-volume (+ (var-get total-volume) amount)) |
|
204 | 222 | (define-public (set-fee (new-fee-bp uint)) |
205 | 223 | (begin |
206 | 224 | (asserts! (is-eq contract-caller (var-get admin)) ERR-UNAUTHORIZED) |
207 | | - (asserts! (<= new-fee-bp u100) ERR-UNAUTHORIZED) |
| 225 | + ;; L-02 fix: use ERR-INVALID-AMOUNT for fee validation (not ERR-UNAUTHORIZED) |
| 226 | + (asserts! (<= new-fee-bp u100) ERR-INVALID-AMOUNT) |
208 | 227 | (ok (var-set flash-fee-basis-points new-fee-bp)) |
209 | 228 | ) |
210 | 229 | ) |
211 | 230 |
|
| 231 | +(define-public (set-treasury (new-treasury principal)) |
| 232 | + (begin |
| 233 | + (asserts! (is-eq contract-caller (var-get admin)) ERR-UNAUTHORIZED) |
| 234 | + (ok (var-set treasury new-treasury)) |
| 235 | + ) |
| 236 | +) |
| 237 | + |
| 238 | +(define-read-only (get-treasury) |
| 239 | + (ok (var-get treasury)) |
| 240 | +) |
| 241 | + |
212 | 242 | (define-public (set-admin (new-admin principal)) |
213 | 243 | (begin |
214 | 244 | (asserts! (is-eq contract-caller (var-get admin)) ERR-UNAUTHORIZED) |
|
0 commit comments