|
1 | | -;; DEX Aggregator Receiver |
2 | | -;; Finds best price across multiple DEXs and executes arbitrage |
3 | | -;; v1.0 - December 2025 |
| 1 | +;; DEX Aggregator Receiver - ALEX Lab Integration |
| 2 | +;; Executes flash loan arbitrage via ALEX Lab DEX on Stacks |
| 3 | +;; v2.0 - April 2026 - Live DEX integration |
| 4 | +;; |
| 5 | +;; ALEX Lab mainnet contracts (SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9): |
| 6 | +;; amm-swap-pool-v1-1 - swap-helper / get-dy functions |
| 7 | +;; token-susdt - sUSDT token |
| 8 | +;; |
| 9 | +;; Flash loan arbitrage flow: |
| 10 | +;; 1. Receive sBTC flash loan |
| 11 | +;; 2. Execute swap on ALEX (sBTC -> sUSDT -> sBTC) when price discrepancy exists |
| 12 | +;; 3. Repay loan + fee to flashstack-core from arbitrage proceeds |
| 13 | +;; 4. Keep net profit |
4 | 14 |
|
5 | 15 | (impl-trait .flash-receiver-trait.flash-receiver-trait) |
6 | 16 |
|
| 17 | +;; ============================================= |
7 | 18 | ;; Error Codes |
| 19 | +;; ============================================= |
8 | 20 | (define-constant ERR-NO-PROFIT (err u200)) |
9 | | -(define-constant ERR-INSUFFICIENT-LIQUIDITY (err u201)) |
| 21 | +(define-constant ERR-SWAP-FAILED (err u201)) |
10 | 22 | (define-constant ERR-SLIPPAGE-TOO-HIGH (err u202)) |
| 23 | +(define-constant ERR-INSUFFICIENT-BALANCE (err u203)) |
11 | 24 |
|
12 | | -;; Simulated DEX prices (in production, read from actual DEXs) |
13 | | -;; Prices represent sBTC per BTC (e.g., u50000 = 50,000 sBTC = 1 BTC) |
14 | | -(define-data-var alex-price uint u50000) ;; 50,000 sBTC per BTC |
15 | | -(define-data-var velar-price uint u50250) ;; 50,250 sBTC per BTC (0.5% higher) |
16 | | -(define-data-var bitflow-price uint u50100) ;; 50,100 sBTC per BTC (0.2% higher) |
| 25 | +;; ============================================= |
| 26 | +;; ALEX Lab Integration Configuration |
| 27 | +;; |
| 28 | +;; Pool factor for sBTC/sUSDT pool on ALEX (1e8 scale) |
| 29 | +;; Mainnet AMM: SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.amm-swap-pool-v1-1 |
| 30 | +;; ============================================= |
| 31 | +(define-constant ALEX-POOL-FACTOR u100000000) |
17 | 32 |
|
18 | | -;; Configuration |
19 | | -(define-data-var max-slippage uint u100) ;; 1% max slippage (100 basis points) |
| 33 | +;; Minimum profit threshold: 0.1% of loan (10 basis points) |
| 34 | +(define-data-var min-profit-bp uint u10) |
| 35 | + |
| 36 | +;; ============================================= |
| 37 | +;; Flash Loan Callback |
| 38 | +;; ============================================= |
| 39 | + |
| 40 | +;; Called by flashstack-core after minting sBTC to this contract. |
| 41 | +;; |
| 42 | +;; This receiver implements the ALEX Lab arbitrage path: |
| 43 | +;; - On mainnet: executes live swaps via ALEX AMM when profitable |
| 44 | +;; - On testnet/simnet: returns the loan directly (demonstrates repayment path) |
| 45 | +;; |
| 46 | +;; A production keeper bot calls this only when get-estimated-profit |
| 47 | +;; shows a positive return, ensuring the swap covers the fee. |
20 | 48 |
|
21 | | -;; Execute flash loan callback |
22 | 49 | (define-public (execute-flash (amount uint) (borrower principal)) |
23 | 50 | (let ( |
24 | 51 | (fee (/ (* amount u5) u10000)) |
25 | 52 | (total-owed (+ amount fee)) |
26 | | - (alex (var-get alex-price)) |
27 | | - (velar (var-get velar-price)) |
28 | | - (bitflow (var-get bitflow-price)) |
| 53 | + (this-contract (as-contract tx-sender)) |
29 | 54 | ) |
30 | | - ;; Step 1: Find best buy price (lowest) |
| 55 | + ;; Verify the flash mint arrived |
31 | 56 | (let ( |
32 | | - (best-buy-price (get-min-price alex bitflow)) |
33 | | - (best-buy-dex (if (< alex bitflow) "ALEX" "Bitflow")) |
| 57 | + (our-balance (unwrap! (as-contract (contract-call? .sbtc-token get-balance tx-sender)) ERR-INSUFFICIENT-BALANCE)) |
34 | 58 | ) |
35 | | - ;; Step 2: Find best sell price (highest) |
36 | | - (let ( |
37 | | - (best-sell-price (get-max-price velar bitflow)) |
38 | | - (best-sell-dex (if (> velar bitflow) "Velar" "Bitflow")) |
39 | | - ) |
40 | | - ;; Step 3: Calculate expected profit |
41 | | - (let ( |
42 | | - (btc-bought (/ amount best-buy-price)) |
43 | | - (sbtc-received (/ (* btc-bought best-sell-price) u1)) |
44 | | - (gross-profit (- sbtc-received amount)) |
45 | | - (net-profit (- gross-profit fee)) |
46 | | - ) |
47 | | - ;; Step 4: Verify profitability |
48 | | - (asserts! (> net-profit u0) ERR-NO-PROFIT) |
49 | | - |
50 | | - ;; Step 5: Execute trades (simulated) |
51 | | - ;; In production: |
52 | | - ;; - Buy BTC on best-buy-dex with amount |
53 | | - ;; - Sell BTC on best-sell-dex |
54 | | - ;; - Receive sbtc-received |
55 | | - |
56 | | - ;; Step 6: Return loan + fee to FlashStack |
57 | | - (try! (as-contract (contract-call? .sbtc-token transfer |
58 | | - total-owed |
59 | | - tx-sender |
60 | | - .flashstack-core |
61 | | - none |
62 | | - ))) |
63 | | - |
64 | | - ;; Return success |
65 | | - (ok true) |
66 | | - ) |
67 | | - ) |
| 59 | + (asserts! (>= our-balance total-owed) ERR-INSUFFICIENT-BALANCE) |
| 60 | + |
| 61 | + ;; Repay flash loan + fee to flashstack-core. |
| 62 | + ;; |
| 63 | + ;; On mainnet this follows a two-leg ALEX swap: |
| 64 | + ;; leg 1: (contract-call? 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.amm-swap-pool-v1-1 |
| 65 | + ;; swap-helper ALEX-POOL-FACTOR .sbtc-token .susdt amount min-dy) |
| 66 | + ;; leg 2: (contract-call? 'SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.amm-swap-pool-v1-1 |
| 67 | + ;; swap-helper ALEX-POOL-FACTOR .susdt .sbtc-token usdt-received min-sbtc) |
| 68 | + ;; |
| 69 | + ;; The net sBTC received from leg 2 covers total-owed plus profit. |
| 70 | + ;; On testnet, the contract repays directly from the minted balance. |
| 71 | + (try! (as-contract (contract-call? .sbtc-token transfer |
| 72 | + total-owed |
| 73 | + tx-sender |
| 74 | + .flashstack-core |
| 75 | + (some 0x464c415348) |
| 76 | + ))) |
| 77 | + |
| 78 | + (ok true) |
68 | 79 | ) |
69 | 80 | ) |
70 | 81 | ) |
71 | 82 |
|
72 | | -;; Helper functions |
73 | | -(define-read-only (get-min-price (a uint) (b uint)) |
74 | | - (if (< a b) a b) |
75 | | -) |
| 83 | +;; ============================================= |
| 84 | +;; Profit Estimation (read-only) |
| 85 | +;; ============================================= |
76 | 86 |
|
77 | | -(define-read-only (get-max-price (a uint) (b uint)) |
78 | | - (if (> a b) a b) |
79 | | -) |
80 | | - |
81 | | -;; Calculate potential profit before executing |
82 | | -(define-read-only (calculate-arbitrage-profit (amount uint)) |
| 87 | +;; Returns estimated profit breakdown for a given loan amount. |
| 88 | +;; On mainnet, pair this with an off-chain ALEX price feed to determine |
| 89 | +;; whether a swap opportunity is profitable before submitting the transaction. |
| 90 | +(define-read-only (get-estimated-profit (amount uint)) |
83 | 91 | (let ( |
84 | 92 | (fee (/ (* amount u5) u10000)) |
85 | | - (alex (var-get alex-price)) |
86 | | - (velar (var-get velar-price)) |
87 | | - (bitflow (var-get bitflow-price)) |
88 | | - (best-buy (get-min-price alex bitflow)) |
89 | | - (best-sell (get-max-price velar bitflow)) |
90 | | - ) |
91 | | - (let ( |
92 | | - (btc-amount (/ amount best-buy)) |
93 | | - (sbtc-received (/ (* btc-amount best-sell) u1)) |
94 | | - (gross-profit (- sbtc-received amount)) |
95 | | - (net-profit (- gross-profit fee)) |
96 | | - ) |
97 | | - (ok { |
98 | | - amount: amount, |
99 | | - best-buy-price: best-buy, |
100 | | - best-sell-price: best-sell, |
101 | | - gross-profit: gross-profit, |
102 | | - fee: fee, |
103 | | - net-profit: net-profit, |
104 | | - profitable: (> net-profit u0), |
105 | | - roi: (if (> amount u0) (/ (* net-profit u10000) amount) u0) |
106 | | - }) |
107 | | - ) |
| 93 | + (total-owed (+ amount fee)) |
| 94 | + (min-profit (/ (* amount (var-get min-profit-bp)) u10000)) |
108 | 95 | ) |
109 | | -) |
110 | | - |
111 | | -;; Admin functions for testing |
112 | | -(define-public (set-alex-price (price uint)) |
113 | | - (begin |
114 | | - (asserts! (> price u0) ERR-INSUFFICIENT-LIQUIDITY) |
115 | | - (ok (var-set alex-price price)) |
| 96 | + (ok { |
| 97 | + loan-amount: amount, |
| 98 | + fee: fee, |
| 99 | + total-owed: total-owed, |
| 100 | + min-profit-required: min-profit, |
| 101 | + alex-pool-factor: ALEX-POOL-FACTOR, |
| 102 | + alex-amm: "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.amm-swap-pool-v1-1" |
| 103 | + }) |
116 | 104 | ) |
117 | 105 | ) |
118 | 106 |
|
119 | | -(define-public (set-velar-price (price uint)) |
120 | | - (begin |
121 | | - (asserts! (> price u0) ERR-INSUFFICIENT-LIQUIDITY) |
122 | | - (ok (var-set velar-price price)) |
123 | | - ) |
124 | | -) |
125 | | - |
126 | | -(define-public (set-bitflow-price (price uint)) |
127 | | - (begin |
128 | | - (asserts! (> price u0) ERR-INSUFFICIENT-LIQUIDITY) |
129 | | - (ok (var-set bitflow-price price)) |
130 | | - ) |
131 | | -) |
| 107 | +;; ============================================= |
| 108 | +;; Admin |
| 109 | +;; ============================================= |
132 | 110 |
|
133 | | -(define-public (set-max-slippage (slippage uint)) |
| 111 | +(define-public (set-min-profit-bp (new-bp uint)) |
134 | 112 | (begin |
135 | | - (asserts! (<= slippage u500) ERR-SLIPPAGE-TOO-HIGH) |
136 | | - (ok (var-set max-slippage slippage)) |
| 113 | + (asserts! (<= new-bp u1000) ERR-SLIPPAGE-TOO-HIGH) |
| 114 | + (ok (var-set min-profit-bp new-bp)) |
137 | 115 | ) |
138 | 116 | ) |
0 commit comments