|
| 1 | +;; Bitflow Arbitrage Receiver |
| 2 | +;; Borrows STX from flashstack-stx-core, executes STX/stSTX round-trip |
| 3 | +;; on Bitflow stableswap, repays with profit. |
| 4 | +;; |
| 5 | +;; Live contracts used: |
| 6 | +;; Bitflow pool: SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M.stableswap-stx-ststx-v-1-2 |
| 7 | +;; stSTX token: SP4SZE494VC2YC5JYG7AYFQ44F5Q4PYV7DVMDPBG.ststx-token |
| 8 | +;; |
| 9 | +;; Why this works: |
| 10 | +;; stSTX accumulates staking yield. When yield accrues, stSTX briefly |
| 11 | +;; trades above 1 STX on Bitflow before arbitrageurs equalise it. |
| 12 | +;; This receiver captures that spread atomically in one flash loan tx. |
| 13 | +;; |
| 14 | +;; Bitflow swap-x-for-y: STX (x) -> stSTX (y) |
| 15 | +;; Bitflow swap-y-for-x: stSTX (y) -> STX (x) |
| 16 | + |
| 17 | +(impl-trait 'SP3TGRVG7DKGFVRTTVGGS60S59R916FWB4DAB9STZ.stx-flash-receiver-trait.stx-flash-receiver-trait) |
| 18 | + |
| 19 | +;; Minimal SIP-010 trait for calling stSTX token |
| 20 | +(define-trait sip-010-trait |
| 21 | + ( |
| 22 | + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) |
| 23 | + (get-name () (response (string-ascii 32) uint)) |
| 24 | + (get-symbol () (response (string-ascii 32) uint)) |
| 25 | + (get-decimals () (response uint uint)) |
| 26 | + (get-balance (principal) (response uint uint)) |
| 27 | + (get-total-supply () (response uint uint)) |
| 28 | + (get-token-uri () (response (optional (string-utf8 256)) uint)) |
| 29 | + ) |
| 30 | +) |
| 31 | + |
| 32 | +;; ============================================= |
| 33 | +;; Constants |
| 34 | +;; ============================================= |
| 35 | + |
| 36 | +(define-constant CONTRACT-OWNER tx-sender) |
| 37 | + |
| 38 | +(define-constant ERR-NOT-OWNER (err u400)) |
| 39 | +(define-constant ERR-SWAP-FAILED (err u401)) |
| 40 | +(define-constant ERR-NO-PROFIT (err u402)) |
| 41 | +(define-constant ERR-REPAY-FAILED (err u403)) |
| 42 | + |
| 43 | +;; Bitflow STX/stSTX stableswap pool |
| 44 | +(define-constant BITFLOW-POOL 'SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M.stableswap-stx-ststx-v-1-2) |
| 45 | + |
| 46 | +;; stSTX SIP-010 token (y-token in the pool) |
| 47 | +(define-constant STSTX 'SP4SZE494VC2YC5JYG7AYFQ44F5Q4PYV7DVMDPBG.ststx-token) |
| 48 | + |
| 49 | +;; Bitflow STX/stSTX LP token (lp-token parameter) |
| 50 | +(define-constant BITFLOW-LP 'SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M.stx-ststx-lp-token-v-1-2) |
| 51 | + |
| 52 | +;; Slippage tolerance in basis points (default 100 = 1%) |
| 53 | +(define-data-var slippage-bp uint u100) |
| 54 | + |
| 55 | +;; ============================================= |
| 56 | +;; Flash Loan Callback |
| 57 | +;; ============================================= |
| 58 | + |
| 59 | +(define-public (execute-stx-flash (amount uint) (core principal)) |
| 60 | + (let ( |
| 61 | + ;; Calculate repayment |
| 62 | + (fee-bp (unwrap! (contract-call? .flashstack-stx-core get-fee-basis-points) ERR-SWAP-FAILED)) |
| 63 | + (raw-fee (/ (* amount fee-bp) u10000)) |
| 64 | + (fee (if (> raw-fee u0) raw-fee u1)) |
| 65 | + (total-owed (+ amount fee)) |
| 66 | + |
| 67 | + ;; Min stSTX out from leg 1 (amount minus slippage) |
| 68 | + (slip (var-get slippage-bp)) |
| 69 | + (min-ststx (- amount (/ (* amount slip) u10000))) |
| 70 | + |
| 71 | + ;; Min STX back from leg 2 (must cover total-owed) |
| 72 | + (min-stx total-owed) |
| 73 | + ) |
| 74 | + ;; Leg 1: STX -> stSTX on Bitflow |
| 75 | + (unwrap! (contract-call? BITFLOW-POOL swap-x-for-y |
| 76 | + STSTX ;; y-token (stSTX SIP-010) |
| 77 | + BITFLOW-LP ;; lp-token (LP token for this pool) |
| 78 | + amount ;; STX amount in microstacks |
| 79 | + min-ststx ;; minimum stSTX to receive |
| 80 | + ) ERR-SWAP-FAILED) |
| 81 | + |
| 82 | + ;; Get how much stSTX we received |
| 83 | + (let ( |
| 84 | + (ststx-balance (unwrap! |
| 85 | + (contract-call? STSTX get-balance (as-contract tx-sender)) |
| 86 | + ERR-SWAP-FAILED)) |
| 87 | + ) |
| 88 | + (asserts! (> ststx-balance u0) ERR-SWAP-FAILED) |
| 89 | + |
| 90 | + ;; Leg 2: stSTX -> STX on Bitflow |
| 91 | + (unwrap! (contract-call? BITFLOW-POOL swap-y-for-x |
| 92 | + STSTX ;; y-token (stSTX SIP-010) |
| 93 | + BITFLOW-LP ;; lp-token (LP token for this pool) |
| 94 | + ststx-balance ;; all stSTX we hold |
| 95 | + min-stx ;; minimum STX back (must cover repayment) |
| 96 | + ) ERR-SWAP-FAILED) |
| 97 | + |
| 98 | + ;; Repay STX + fee back to flashstack-stx-core |
| 99 | + (let ((stx-now (stx-get-balance (as-contract tx-sender)))) |
| 100 | + (asserts! (>= stx-now total-owed) ERR-REPAY-FAILED) |
| 101 | + (unwrap! (as-contract (stx-transfer? total-owed tx-sender core)) ERR-REPAY-FAILED) |
| 102 | + (ok true) |
| 103 | + ) |
| 104 | + ) |
| 105 | + ) |
| 106 | +) |
| 107 | + |
| 108 | +;; ============================================= |
| 109 | +;; Admin |
| 110 | +;; ============================================= |
| 111 | + |
| 112 | +(define-public (set-slippage-bp (new-bp uint)) |
| 113 | + (begin |
| 114 | + (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-OWNER) |
| 115 | + (asserts! (<= new-bp u500) ERR-SWAP-FAILED) ;; max 5% slippage |
| 116 | + (ok (var-set slippage-bp new-bp)) |
| 117 | + ) |
| 118 | +) |
| 119 | + |
| 120 | +;; Rescue stuck STX (admin only) |
| 121 | +(define-public (rescue-stx (amount uint) (to principal)) |
| 122 | + (begin |
| 123 | + (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-OWNER) |
| 124 | + (unwrap! (as-contract (stx-transfer? amount tx-sender to)) ERR-NOT-OWNER) |
| 125 | + (ok true) |
| 126 | + ) |
| 127 | +) |
| 128 | + |
| 129 | +;; ============================================= |
| 130 | +;; Read-only |
| 131 | +;; ============================================= |
| 132 | + |
| 133 | +(define-read-only (get-slippage-bp) |
| 134 | + (ok (var-get slippage-bp)) |
| 135 | +) |
| 136 | + |
| 137 | +(define-read-only (estimate-profit (amount uint)) |
| 138 | + (let ( |
| 139 | + (fee-bp (unwrap-panic (contract-call? .flashstack-stx-core get-fee-basis-points))) |
| 140 | + (raw-fee (/ (* amount fee-bp) u10000)) |
| 141 | + (fee (if (> raw-fee u0) raw-fee u1)) |
| 142 | + (slip (var-get slippage-bp)) |
| 143 | + (min-ststx (- amount (/ (* amount slip) u10000))) |
| 144 | + ) |
| 145 | + (ok { |
| 146 | + loan-amount: amount, |
| 147 | + fee-to-pay: fee, |
| 148 | + total-owed: (+ amount fee), |
| 149 | + min-ststx-leg1: min-ststx, |
| 150 | + note: "Profit = STX received from leg2 - total-owed. Positive when stSTX trades above peg." |
| 151 | + }) |
| 152 | + ) |
| 153 | +) |
0 commit comments