LCOV - code coverage report
Current view: top level - contracts/extensions - bme032-0-scalar-strategy-hedge.clar (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.6 % 106 96
Test Date: 2025-11-10 13:03:30 Functions: 100.0 % 14 14
Branches: 55.8 % 43 24

             Branch data     Line data    Source code
       1                 :             : ;; Title: BME032 Strategy for scalar market hedging
       2                 :             : ;; Synopsis:
       3                 :             : ;; Provides a hook for scalar markets to run hedging function during cool down.
       4                 :             : ;; Description:
       5                 :             : ;; The idea is to create an algorithmic hedge fund - we assume the community 
       6                 :             : ;; surfaces the winning outcome and swap treasury tokens in response. The contractcalls the 
       7                 :             : ;; the treasury with the form/to token contracts and works out the direction bearish/bullish
       8                 :             : ;; of the signal together with the signal strength strong/medium/weak. 
       9                 :             : 
      10                 :             : (impl-trait .hedge-trait.hedge-trait)
      11                 :             : (use-trait ft-velar-token 'SP2AKWJYC7BNY18W1XXKPGP0YVEK63QJG4793Z2D4.sip-010-trait-ft-standard.sip-010-trait)
      12                 :             : (impl-trait 'SP3JP0N1ZXGASRJ0F7QAHWFPGTVK9T2XNXDB908Z.extension-trait.extension-trait)
      13                 :             : 
      14                 :             : (define-constant err-unauthorised (err u32000))
      15                 :             : (define-constant err-already-hedged (err u32001))
      16                 :             : (define-constant err-already-executed (err u32002))
      17                 :             : (define-constant err-hedge-not-found (err u32003))
      18                 :             : (define-constant err-token-in-incorrect (err u32004))
      19                 :             : (define-constant err-token-out-incorrect (err u32004))
      20                 :             : (define-constant err-pair-not-found (err u32005))
      21                 :             : (define-constant err-cooldown (err u32006))
      22                 :             : (define-constant err-amount-zero (err u32007))
      23                 :             : (define-constant err-slippage (err u32008))
      24                 :             : (define-constant err-invalid-amount (err u32009))
      25                 :             : (define-constant err-wrong-market-contract (err u32010))
      26                 :             : (define-constant err-token-in-out-equal (err u32011))
      27                 :             : (define-constant err-token-in-equal (err u32012))
      28                 :             : (define-constant err-token-out-equal (err u32012))
      29                 :             : (define-constant err-token-incorrect (err u32013))
      30                 :             : 
      31                 :             : (define-constant SCALE u1000000)
      32                 :             : 
      33                 :             : (define-data-var hedge-market-contract principal .bme024-0-market-predicting)
      34                 :             : (define-data-var hedge-scalar-contract principal .bme024-0-market-scalar-pyth)
      35                 :             : (define-data-var hedge-multipliers (list 6 uint) (list u750 u500 u250 u250 u500 u750))
      36                 :             : (define-data-var max-hedge-bips uint u1000)      ;; hard cap per trade = 10% of balance
      37                 :             : (define-data-var max-hedge-abs  uint u0)         ;; optional absolute cap (0 = disabled)
      38                 :             : (define-data-var min-trade      uint u0)         ;; optional minimum trade size (0 = disabled)
      39                 :             : (define-data-var per-market-cooldown uint u144)  ;; ~1 day (tune as needed)
      40                 :             : (define-data-var per-trade-slippage uint u300)   ;; 3% default for hedges (stricter than treasury 5%)
      41                 :             : 
      42                 :             : (define-map last-hedge-height {market-id:uint} uint)  ;; anti-replay
      43                 :             : 
      44                 :             : (define-map swap-token-pairs (buff 32) {token-in: principal, token-out: principal, token0: principal, token1: principal})
      45                 :             : (define-map hedges
      46                 :             :   uint
      47                 :             :   {executed: bool, feed-id: (buff 32)}
      48                 :             : )
      49                 :             : 
      50                 :          17 : (define-public (is-dao-or-extension)
      51    [ + ][ +  + ]:         174 :         (ok (asserts! (or (is-eq tx-sender .bigmarket-dao) (contract-call? .bigmarket-dao is-extension contract-caller)) err-unauthorised)))
      52                 :             : 
      53                 :          16 : (define-public (set-hedge-multipliers (multipliers (list 6 uint)))
      54                 :          18 :   (begin
      55                 :          18 :     (try! (is-dao-or-extension))
      56                 :          16 :     (var-set hedge-multipliers multipliers)
      57                 :          16 :     (print {event: "hedge-multipliers", multipliers: multipliers})
      58                 :          16 :     (ok true)
      59                 :             :   )
      60                 :             : )
      61                 :             : 
      62                 :          16 : (define-public (set-hedge-market-contract (market-contract principal))
      63                 :          20 :   (begin
      64                 :          20 :     (try! (is-dao-or-extension))
      65                 :          18 :     (var-set hedge-market-contract market-contract)
      66                 :          18 :     (print {event: "hedge-market-contract", market-contract: market-contract})
      67                 :          18 :     (ok true)
      68                 :             :   )
      69                 :             : )
      70                 :             : 
      71                 :          16 : (define-public (set-hedge-scalar-contract (market-contract principal))
      72                 :          20 :   (begin
      73                 :          20 :     (try! (is-dao-or-extension))
      74                 :          18 :     (var-set hedge-scalar-contract market-contract)
      75                 :          18 :     (print {event: "hedge-scalar-contract", market-contract: market-contract})
      76                 :          18 :     (ok true)
      77                 :             :   )
      78                 :             : )
      79                 :             : 
      80                 :          16 : (define-public (set-hedge-caps (bips uint) (abs uint))
      81                 :          18 :   (begin (try! (is-dao-or-extension))
      82            [ - ]:          16 :          (asserts! (<= bips u5000) err-invalid-amount) ;; <=50%
      83                 :          16 :          (var-set max-hedge-bips bips)
      84                 :          16 :          (var-set max-hedge-abs  abs)
      85                 :          16 :          (ok true)))
      86                 :             : 
      87                 :          16 : (define-public (set-hedge-min-trade (min uint))
      88                 :          18 :   (begin (try! (is-dao-or-extension)) (var-set min-trade min) (ok true)))
      89                 :             : 
      90                 :          16 : (define-public (set-hedge-cooldown (blocks uint))
      91                 :          18 :   (begin (try! (is-dao-or-extension)) (var-set per-market-cooldown blocks) (ok true)))
      92                 :             : 
      93                 :          16 : (define-public (set-hedge-slippage (bips uint))
      94                 :          16 :   (begin (try! (is-dao-or-extension))
      95    [ - ][ +  + ]:          16 :          (asserts! (and (>= bips u1) (<= bips u3000)) err-invalid-amount)
      96                 :          16 :          (var-set per-trade-slippage bips) (ok true)))
      97                 :             : 
      98                 :          17 : (define-public (set-swap-token-pair
      99                 :             :   (feed-id (buff 32))
     100                 :             :   (token-a principal)
     101                 :             :   (token-b principal)
     102                 :             :   (token-in principal)
     103                 :             :   (token-out principal))
     104                 :          39 :   (begin
     105                 :          39 :     (try! (is-dao-or-extension))
     106                 :             :     ;; Check token-in is one of token-a or token-b
     107    [ + ][ +  + ]:          37 :     (asserts! (or (is-eq token-in token-a) (is-eq token-in token-b)) err-token-in-equal)
     108                 :             :     ;; Check token-out is the other one
     109    [ + ][ +  + ]:          36 :     (asserts! (or (is-eq token-out token-a) (is-eq token-out token-b)) err-token-out-equal)
     110            [ + ]:          35 :     (asserts! (not (is-eq token-in token-out)) err-token-in-out-equal)
     111                 :             : 
     112                 :             :     ;; Lexicographic ordering
     113                 :          34 :     (let (
     114                 :          34 :       (buff-a (unwrap! (to-consensus-buff? token-a) err-token-incorrect))
     115                 :          34 :       (buff-b (unwrap! (to-consensus-buff? token-b) err-token-incorrect))
     116         [ +  - ]:          34 :       (token0 (if (< buff-a buff-b) token-a token-b))
     117         [ +  - ]:          34 :       (token1 (if (< buff-a buff-b) token-b token-a))
     118                 :             : 
     119                 :             :     )
     120                 :          34 :       (map-set swap-token-pairs
     121                 :          34 :         feed-id
     122                 :             :         {
     123                 :          34 :           token-in: token-in,
     124                 :          34 :           token-out: token-out,
     125                 :          34 :           token0: token0,
     126                 :          34 :           token1: token1
     127                 :             :         })
     128                 :          34 :       (print {event: "swap-token-pair", feed-id: feed-id, token-in: token-in, token-out: token-out, token0: token0, token1: token1})
     129                 :          34 :       (ok true)
     130                 :             :     )
     131                 :             :   )
     132                 :             : )
     133                 :             : 
     134                 :           1 : (define-read-only (get-swap-token-pair (feed-id (buff 32)))
     135                 :           1 :         (map-get? swap-token-pairs feed-id)
     136                 :             : )
     137                 :             : 
     138                 :           6 : (define-public (perform-swap-hedge
     139                 :             :   (market-id uint) (predicted-index uint) (feed-id (buff 32))
     140                 :             :   (token0 <ft-velar-token>) (token1 <ft-velar-token>)
     141                 :             :   (token-in <ft-velar-token>) (token-out <ft-velar-token>)
     142                 :             : )
     143                 :           8 :   (let (
     144                 :           8 :     (pair (unwrap! (map-get? swap-token-pairs feed-id) err-pair-not-found))
     145                 :           4 :     (is-bearish (< predicted-index u3))
     146         [ +  - ]:           4 :     (expected-in  (if is-bearish (get token-in pair)  (get token-out pair)))
     147         [ +  - ]:           4 :     (expected-out (if is-bearish (get token-out pair) (get token-in pair)))
     148                 :           4 :     (lh (default-to u0 (map-get? last-hedge-height {market-id: market-id})))
     149                 :           4 :     (cool (var-get per-market-cooldown))
     150                 :             :   )
     151                 :             :     ;; auth + source contract check
     152                 :           4 :     (try! (is-dao-or-extension))
     153            [ + ]:           2 :     (asserts! (is-eq contract-caller (var-get hedge-scalar-contract)) err-wrong-market-contract)
     154                 :             : 
     155                 :             :     ;; one-shot safety: refuse if this market was already hedged here
     156            [ - ]:           1 :     (asserts! (is-none (map-get? hedges market-id)) err-already-executed)
     157                 :             : 
     158                 :             :     ;; cooldown even if another component tries to hedge repeatedly
     159                 :           1 :     (if (> lh u0)
     160       [ - ][ - ]:           0 :       (asserts! (> stacks-block-height (+ lh cool)) err-cooldown)
     161            [ + ]:           1 :       true
     162                 :             :     )
     163                 :             : 
     164                 :             :     ;; pair validation: enforce tokens match configured direction
     165            [ - ]:           1 :     (asserts! (is-eq (contract-of token-in)  expected-in)  err-token-in-incorrect)
     166            [ - ]:           1 :     (asserts! (is-eq (contract-of token-out) expected-out) err-token-out-incorrect)
     167                 :             : 
     168                 :             :     ;; compute bounded amount
     169                 :           1 :     (let (
     170                 :           1 :       (balance (unwrap! (contract-call? token-in get-balance .bme006-0-treasury) err-unauthorised))
     171                 :           1 :       (bips-mult (unwrap! (element-at? (var-get hedge-multipliers) predicted-index) err-token-incorrect)) ;; e.g., 750 = 7.5%
     172                 :           1 :       (cap-bips (var-get max-hedge-bips))
     173         [ -  + ]:           1 :       (bips (if (> bips-mult cap-bips) cap-bips bips-mult))
     174                 :           1 :       (raw-scaled (/ (* (* balance bips) SCALE) u10000))
     175                 :           1 :       (raw (/ raw-scaled SCALE))
     176                 :             : 
     177                 :           1 :       (abs-cap (var-get max-hedge-abs))
     178 [ -  + ][ +  + ]:           1 :       (amt (if (and (> abs-cap u0) (> raw abs-cap)) abs-cap raw))
     179                 :           1 :       (min-size (var-get min-trade))
     180                 :             :     )
     181            [ + ]:           1 :       (asserts! (> amt u0) err-amount-zero)
     182    [ -  - ][ - ]:           0 :       (if (> min-size u0) (asserts! (>= amt min-size) err-amount-zero) true)
     183                 :             : 
     184                 :             :       ;; do the swap with stricter slippage than treasury default
     185                 :           0 :       (let ((slip (var-get per-trade-slippage)))
     186    [ - ][ -  - ]:           0 :         (asserts! (and (>= slip u1) (<= slip u3000)) err-slippage)
     187                 :           0 :         (try! (contract-call? .bme006-0-treasury swap-tokens-with-slippage
     188                 :           0 :               token0 token1 token-in token-out amt slip))
     189                 :             :       )
     190                 :             : 
     191                 :             :       ;; record hedge + height
     192                 :           0 :       (map-set hedges market-id {executed: true, feed-id: feed-id})
     193                 :           0 :       (map-set last-hedge-height {market-id: market-id} stacks-block-height)
     194                 :           0 :       (print {event:"perform-swap-hedge", market-id: market-id, predicted: predicted-index, feed-id: feed-id, amount: amt})
     195                 :           0 :       (ok true)
     196                 :             :     )
     197                 :             :   )
     198                 :             : )
     199                 :             : 
     200                 :             : ;; can be implemented in later contract
     201                 :           3 : (define-public (perform-custom-hedge (market-id uint) (predicted-index uint))
     202                 :           3 :   (begin 
     203                 :             :     ;; caller must be both an ACTIVE extension and sepecifically the scalar prediction market
     204                 :           3 :     (try! (is-dao-or-extension))
     205            [ + ]:           2 :     (asserts! (is-eq contract-caller (var-get hedge-market-contract)) err-wrong-market-contract)
     206                 :           1 :     (print {event: "perform-custom-hedge", market-id: market-id, predicted: predicted-index})
     207                 :           1 :     (ok true)
     208                 :             :   )
     209                 :             : )
     210                 :             : 
     211                 :           1 : (define-private (compute-swap-amount (token <ft-velar-token>) (index uint))
     212                 :           1 :   (let (
     213                 :           1 :       (balance (unwrap! (contract-call? token get-balance .bme006-0-treasury) err-unauthorised))
     214                 :           1 :       (bips (unwrap! (element-at? (var-get hedge-multipliers) index) err-token-incorrect))
     215                 :           1 :       (amt-scaled (/ (* (* balance bips) SCALE) u10000))
     216                 :           1 :       (amt (/ amt-scaled SCALE))
     217                 :             :     )
     218                 :           1 :     (ok amt)
     219                 :             :   )
     220                 :             : )
     221                 :             : 
     222                 :             : ;; --- Extension callback
     223                 :             : 
     224                 :           1 : (define-public (callback (sender principal) (memo (buff 34)))
     225                 :           1 :         (ok true)
     226                 :             : )
        

Generated by: LCOV version 2.3.2-1