wayfinder-paths 0.1.22__py3-none-any.whl → 0.1.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wayfinder-paths might be problematic. Click here for more details.

Files changed (156) hide show
  1. wayfinder_paths/__init__.py +0 -4
  2. wayfinder_paths/adapters/balance_adapter/README.md +0 -1
  3. wayfinder_paths/adapters/balance_adapter/adapter.py +313 -167
  4. wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
  5. wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -124
  6. wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
  7. wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
  8. wayfinder_paths/adapters/boros_adapter/client.py +476 -0
  9. wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
  10. wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
  11. wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
  12. wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
  13. wayfinder_paths/adapters/boros_adapter/types.py +70 -0
  14. wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
  15. wayfinder_paths/adapters/brap_adapter/README.md +22 -75
  16. wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
  17. wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
  18. wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
  19. wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
  20. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +180 -92
  21. wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
  22. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +82 -14
  23. wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
  24. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +586 -61
  25. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
  26. wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
  27. wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
  28. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
  29. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
  30. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
  31. wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
  32. wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
  33. wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
  34. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
  35. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
  36. wayfinder_paths/adapters/moonwell_adapter/adapter.py +649 -547
  37. wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
  38. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +160 -239
  39. wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
  40. wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
  41. wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
  42. wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
  43. wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
  44. wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
  45. wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
  46. wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
  47. wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
  48. wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
  49. wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
  50. wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
  51. wayfinder_paths/adapters/token_adapter/examples.json +0 -4
  52. wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
  53. wayfinder_paths/conftest.py +24 -17
  54. wayfinder_paths/core/__init__.py +0 -3
  55. wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
  56. wayfinder_paths/core/adapters/models.py +17 -7
  57. wayfinder_paths/core/clients/BRAPClient.py +4 -1
  58. wayfinder_paths/core/clients/ClientManager.py +0 -7
  59. wayfinder_paths/core/clients/LedgerClient.py +196 -172
  60. wayfinder_paths/core/clients/TokenClient.py +47 -1
  61. wayfinder_paths/core/clients/WayfinderClient.py +1 -3
  62. wayfinder_paths/core/clients/__init__.py +0 -5
  63. wayfinder_paths/core/clients/protocols.py +21 -35
  64. wayfinder_paths/core/clients/test_ledger_client.py +448 -0
  65. wayfinder_paths/core/config.py +10 -162
  66. wayfinder_paths/core/constants/__init__.py +73 -2
  67. wayfinder_paths/core/constants/base.py +8 -17
  68. wayfinder_paths/core/constants/chains.py +36 -0
  69. wayfinder_paths/core/constants/contracts.py +52 -0
  70. wayfinder_paths/core/constants/erc20_abi.py +0 -1
  71. wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
  72. wayfinder_paths/core/constants/hyperliquid.py +16 -0
  73. wayfinder_paths/core/constants/moonwell_abi.py +0 -15
  74. wayfinder_paths/core/constants/tokens.py +9 -0
  75. wayfinder_paths/core/engine/manifest.py +66 -0
  76. wayfinder_paths/core/strategies/Strategy.py +0 -71
  77. wayfinder_paths/core/strategies/__init__.py +10 -1
  78. wayfinder_paths/core/strategies/opa_loop.py +167 -0
  79. wayfinder_paths/core/utils/evm_helpers.py +5 -15
  80. wayfinder_paths/core/utils/test_transaction.py +289 -0
  81. wayfinder_paths/core/utils/tokens.py +28 -0
  82. wayfinder_paths/core/utils/transaction.py +57 -8
  83. wayfinder_paths/core/utils/web3.py +8 -3
  84. wayfinder_paths/mcp/__init__.py +5 -0
  85. wayfinder_paths/mcp/preview.py +185 -0
  86. wayfinder_paths/mcp/scripting.py +84 -0
  87. wayfinder_paths/mcp/server.py +52 -0
  88. wayfinder_paths/mcp/state/profile_store.py +195 -0
  89. wayfinder_paths/mcp/state/store.py +89 -0
  90. wayfinder_paths/mcp/test_scripting.py +267 -0
  91. wayfinder_paths/mcp/tools/__init__.py +0 -0
  92. wayfinder_paths/mcp/tools/balances.py +290 -0
  93. wayfinder_paths/mcp/tools/discovery.py +158 -0
  94. wayfinder_paths/mcp/tools/execute.py +770 -0
  95. wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
  96. wayfinder_paths/mcp/tools/quotes.py +288 -0
  97. wayfinder_paths/mcp/tools/run_script.py +286 -0
  98. wayfinder_paths/mcp/tools/strategies.py +188 -0
  99. wayfinder_paths/mcp/tools/tokens.py +46 -0
  100. wayfinder_paths/mcp/tools/wallets.py +354 -0
  101. wayfinder_paths/mcp/utils.py +129 -0
  102. wayfinder_paths/policies/enso.py +1 -2
  103. wayfinder_paths/policies/hyper_evm.py +6 -3
  104. wayfinder_paths/policies/hyperlend.py +1 -2
  105. wayfinder_paths/policies/hyperliquid.py +1 -1
  106. wayfinder_paths/policies/lifi.py +18 -0
  107. wayfinder_paths/policies/moonwell.py +12 -7
  108. wayfinder_paths/policies/prjx.py +1 -3
  109. wayfinder_paths/policies/util.py +8 -2
  110. wayfinder_paths/run_strategy.py +97 -300
  111. wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
  112. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +47 -133
  113. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
  114. wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
  115. wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
  116. wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
  117. wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
  118. wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
  119. wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
  120. wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
  121. wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
  122. wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
  123. wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
  124. wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
  125. wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
  126. wayfinder_paths/{templates/strategy → strategies/boros_hype_strategy}/test_strategy.py +99 -63
  127. wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
  128. wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
  129. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +15 -23
  130. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +27 -62
  131. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +84 -58
  132. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
  133. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -164
  134. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +43 -76
  135. wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
  136. wayfinder_paths/tests/test_test_coverage.py +1 -4
  137. wayfinder_paths-0.1.24.dist-info/METADATA +378 -0
  138. wayfinder_paths-0.1.24.dist-info/RECORD +185 -0
  139. {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
  140. wayfinder_paths/core/clients/WalletClient.py +0 -41
  141. wayfinder_paths/core/engine/StrategyJob.py +0 -110
  142. wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
  143. wayfinder_paths/scripts/create_strategy.py +0 -139
  144. wayfinder_paths/scripts/make_wallets.py +0 -142
  145. wayfinder_paths/templates/adapter/README.md +0 -150
  146. wayfinder_paths/templates/adapter/adapter.py +0 -16
  147. wayfinder_paths/templates/adapter/examples.json +0 -8
  148. wayfinder_paths/templates/adapter/test_adapter.py +0 -30
  149. wayfinder_paths/templates/strategy/README.md +0 -186
  150. wayfinder_paths/templates/strategy/examples.json +0 -11
  151. wayfinder_paths/templates/strategy/strategy.py +0 -35
  152. wayfinder_paths/tests/test_smoke_manifest.py +0 -63
  153. wayfinder_paths-0.1.22.dist-info/METADATA +0 -355
  154. wayfinder_paths-0.1.22.dist-info/RECORD +0 -129
  155. /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
  156. {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
@@ -0,0 +1,365 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from enum import Enum, auto
6
+ from typing import Any
7
+
8
+ from .constants import (
9
+ ALLOCATION_DEVIATION_THRESHOLD,
10
+ BOROS_ENABLE_MIN_TOTAL_USD,
11
+ FULL_REBALANCE_THRESHOLD,
12
+ PARTIAL_TRIM_THRESHOLD,
13
+ )
14
+
15
+ # ─────────────────────────────────────────────────────────────────────────────
16
+ # CONFIGURATION
17
+ # ─────────────────────────────────────────────────────────────────────────────
18
+
19
+
20
+ @dataclass
21
+ class HedgeConfig:
22
+ spot_pct: float # Total allocation to spot (kHYPE + looped HYPE)
23
+ khype_fraction: float # Fraction of spot allocation for kHYPE
24
+ looped_hype_fraction: float # Fraction of spot allocation for looped HYPE
25
+ hyperliquid_pct: float # Allocation to Hyperliquid perp margin
26
+ boros_pct: float # Allocation to Boros rate lock
27
+
28
+ @classmethod
29
+ def default(cls) -> HedgeConfig:
30
+ return cls(
31
+ # New routing:
32
+ # - Send 100% of Arbitrum USDC to Hyperliquid first.
33
+ # - Target ~50% of portfolio value staying on Hyperliquid (margin + cash buffer).
34
+ # - Target ~50% HYPE exposure split between HyperEVM spot (≈47.5%) and Boros HYPE collateral (≈2.5%).
35
+ spot_pct=0.475,
36
+ khype_fraction=0.5,
37
+ looped_hype_fraction=0.5,
38
+ hyperliquid_pct=0.50,
39
+ boros_pct=0.025,
40
+ )
41
+
42
+
43
+ # ─────────────────────────────────────────────────────────────────────────────
44
+ # INVENTORY (World Model)
45
+ # ─────────────────────────────────────────────────────────────────────────────
46
+
47
+
48
+ @dataclass
49
+ class Inventory:
50
+ # HyperEVM wallet balances
51
+ hype_hyperevm_balance: float
52
+ hype_hyperevm_value_usd: float
53
+ whype_balance: float # Wrapped HYPE balance on HyperEVM
54
+ whype_value_usd: float # Value in USD (1:1 with HYPE)
55
+ khype_balance: float
56
+ khype_value_usd: float
57
+ looped_hype_balance: float
58
+ looped_hype_value_usd: float
59
+
60
+ # Arbitrum wallet balances
61
+ usdc_arb_idle: float
62
+ usdt_arb_idle: float
63
+ eth_arb_balance: float # For gas
64
+
65
+ # Arbitrum OFT HYPE (bridged from HyperEVM)
66
+ hype_oft_arb_balance: float
67
+ hype_oft_arb_value_usd: float
68
+
69
+ # Hyperliquid venue
70
+ hl_perp_margin: float
71
+ hl_spot_usdc: float
72
+ hl_spot_hype: float
73
+ hl_spot_hype_value_usd: float
74
+ hl_short_size_hype: float
75
+ hl_short_value_usd: float
76
+ hl_unrealized_pnl: float
77
+ hl_withdrawable_usd: float
78
+
79
+ # Boros venue
80
+ boros_idle_collateral_isolated: float # HYPE units
81
+ boros_idle_collateral_cross: float # HYPE units
82
+ boros_collateral_hype: float # Total HYPE collateral in Boros
83
+ boros_collateral_usd: float # USD value (HYPE * hype_price_usd)
84
+ boros_pending_withdrawal_hype: float # HYPE units
85
+ boros_pending_withdrawal_usd: float # USD value
86
+ boros_committed_collateral_usd: float # boros_collateral_usd + hype_oft_arb_value_usd (+ any in-flight OFT bridge)
87
+ boros_position_size: float # YU notional
88
+ boros_position_value: float # Unrealized PnL
89
+
90
+ # Exchange rates
91
+ khype_to_hype_ratio: float
92
+ looped_hype_to_hype_ratio: float
93
+ hype_price_usd: float
94
+
95
+ # Aggregates
96
+ spot_value_usd: float
97
+ total_hype_exposure: float
98
+ total_value: float
99
+
100
+ # Optional
101
+ boros_position_market_ids: list[int] | None = None
102
+
103
+ # Liquidation detection
104
+ hl_liquidation_detected: bool = False
105
+ hl_liquidation_fills: list[dict] = field(default_factory=list)
106
+
107
+
108
+ @dataclass
109
+ class AllocationStatus:
110
+ # Actual values
111
+ spot_value: float
112
+ hl_value: float
113
+ boros_value: float
114
+ idle_value: float
115
+ total_value: float
116
+
117
+ # Actual percentages
118
+ spot_pct_actual: float
119
+ hl_pct_actual: float
120
+ boros_pct_actual: float
121
+
122
+ # Deviations from target (negative = underallocated)
123
+ spot_deviation: float
124
+ hl_deviation: float
125
+ boros_deviation: float
126
+
127
+ # Dollar amounts needed to reach target
128
+ spot_needed_usd: float
129
+ hl_needed_usd: float
130
+ boros_needed_usd: float
131
+
132
+
133
+ @dataclass
134
+ class YieldInfo:
135
+ khype_apy: float | None = None
136
+ lhype_apy: float | None = None
137
+ boros_apr: float | None = None
138
+ hl_funding_rate: float | None = None
139
+
140
+ khype_expected_yield_usd: float = 0.0
141
+ lhype_expected_yield_usd: float = 0.0
142
+ boros_expected_yield_usd: float = 0.0
143
+ hl_expected_yield_usd: float = 0.0
144
+
145
+ total_expected_yield_usd: float = 0.0
146
+ blended_apy: float | None = None
147
+
148
+
149
+ # ─────────────────────────────────────────────────────────────────────────────
150
+ # PLANNING ENUMS
151
+ # ─────────────────────────────────────────────────────────────────────────────
152
+
153
+
154
+ class Mode(Enum):
155
+ NORMAL = auto() # Regular operations
156
+ TRIM = auto() # Risk at 75%+: reduce exposure
157
+ REDEPLOY = auto() # Risk at 90%+: emergency redeploy
158
+
159
+
160
+ class PlanOp(Enum):
161
+ # Priority 0: Safety/Risk mitigation
162
+ CLOSE_AND_REDEPLOY = "close_and_redeploy"
163
+ PARTIAL_TRIM_SPOT = "partial_trim_spot"
164
+ COMPLETE_PENDING_WITHDRAWAL = "complete_pending_withdrawal"
165
+
166
+ # Priority 5: Gas routing (must happen first!)
167
+ ENSURE_GAS_ON_HYPEREVM = "ensure_gas_on_hyperevm"
168
+ ENSURE_GAS_ON_ARBITRUM = "ensure_gas_on_arbitrum"
169
+
170
+ # Priority 10: Capital routing
171
+ FUND_BOROS = "fund_boros"
172
+ SEND_USDC_TO_HL = "send_usdc_to_hl"
173
+ BRIDGE_TO_HYPEREVM = "bridge_to_hyperevm"
174
+ TRANSFER_HL_SPOT_TO_HYPEREVM = "transfer_hl_spot_to_hyperevm"
175
+ DEPLOY_EXCESS_HL_MARGIN = "deploy_excess_hl_margin"
176
+
177
+ # Priority 20: Position management
178
+ SWAP_HYPE_TO_LST = "swap_hype_to_lst"
179
+ ENSURE_HL_SHORT = "ensure_hl_short"
180
+
181
+ # Priority 30: Rate positions
182
+ ENSURE_BOROS_POSITION = "ensure_boros_position"
183
+
184
+
185
+ # ─────────────────────────────────────────────────────────────────────────────
186
+ # PLANNING DATACLASSES
187
+ # ─────────────────────────────────────────────────────────────────────────────
188
+
189
+
190
+ @dataclass
191
+ class PlanStep:
192
+ op: PlanOp
193
+ priority: int # Lower = execute first
194
+ key: str # Unique identifier for deduplication
195
+ params: dict[str, Any] = field(default_factory=dict)
196
+ reason: str = ""
197
+
198
+ def __lt__(self, other: PlanStep) -> bool:
199
+ return self.priority < other.priority
200
+
201
+
202
+ @dataclass
203
+ class PlannerConfig:
204
+ # Risk thresholds
205
+ partial_trim_threshold: float = PARTIAL_TRIM_THRESHOLD
206
+ full_rebalance_threshold: float = FULL_REBALANCE_THRESHOLD
207
+
208
+ # Allocation thresholds
209
+ allocation_deviation_threshold: float = ALLOCATION_DEVIATION_THRESHOLD
210
+ position_size_tolerance: float = 0.05
211
+
212
+ # Minimum amounts to act on
213
+ min_usdc_action: float = 5.0
214
+ min_usdt_action: float = 1.0
215
+ min_hype_swap: float = 0.1
216
+
217
+ # Boros guard
218
+ min_total_for_boros: float = BOROS_ENABLE_MIN_TOTAL_USD
219
+
220
+ # Hyperliquid guardrails
221
+ hl_withdrawable_buffer_usd: float = 5.0
222
+ hl_withdraw_for_boros_cooldown_minutes: int = 30
223
+ hl_max_withdraw_for_boros_usd: float = 25.0
224
+
225
+ # HL margin management
226
+ hl_target_margin_ratio: float = 0.50
227
+ hl_margin_buffer_ratio: float = 0.15
228
+
229
+ # Boros hysteresis
230
+ boros_market_switch_cooldown_hours: int = 24
231
+ boros_apr_improvement_threshold: float = 0.02
232
+
233
+ # Boros coverage - target 100%, resize threshold is the hysteresis band
234
+ boros_coverage_target: float = 1.0
235
+ boros_resize_min_excess_usd: float = (
236
+ 10.0 # Min YU diff to trigger resize (hysteresis)
237
+ )
238
+
239
+ # Delta neutral tolerances
240
+ delta_neutral_rel_tol: float = 0.02
241
+ delta_neutral_abs_tol_hype: float = 0.11
242
+
243
+ # Execution limits
244
+ max_steps_per_iteration: int = 5
245
+ max_iterations_per_tick: int = 4
246
+ max_total_steps_per_tick: int = 15
247
+
248
+ @classmethod
249
+ def default(cls) -> PlannerConfig:
250
+ return cls()
251
+
252
+
253
+ @dataclass
254
+ class PlannerRuntime:
255
+ # Boros market selection (persistent state for hysteresis)
256
+ current_boros_market_id: int | None = None
257
+ current_boros_token_id: int | None = None
258
+ current_boros_collateral_address: str | None = None
259
+ boros_market_selected_at: datetime | None = None
260
+
261
+ # Execution tracking
262
+ last_update_at: datetime | None = None
263
+ steps_executed_this_session: int = 0
264
+
265
+ # Virtual ledger for same-tick tracking
266
+ committed_usdc_arb: float = 0.0
267
+ committed_usdt_arb: float = 0.0
268
+ funded_boros_this_tick: bool = False
269
+
270
+ last_hl_withdraw_for_boros_at: datetime | None = None
271
+
272
+ # OFT bridge tracking (HyperEVM native HYPE -> Arbitrum OFT HYPE) so we don't
273
+ # repeatedly fund Boros while the bridge is still settling.
274
+ in_flight_boros_oft_hype: float = 0.0
275
+ in_flight_boros_oft_hype_balance_before: float = 0.0
276
+ in_flight_boros_oft_hype_started_at: datetime | None = None
277
+
278
+ # HL state
279
+ leverage_set_for_hype: bool = False
280
+ builder_fee_approved: bool = False
281
+
282
+ def reset_virtual_ledger(self) -> None:
283
+ self.committed_usdc_arb = 0.0
284
+ self.committed_usdt_arb = 0.0
285
+
286
+ def reset_tick_flags(self) -> None:
287
+ self.funded_boros_this_tick = False
288
+
289
+ def available_usdc_arb(self, inv_usdc: float) -> float:
290
+ return max(0.0, inv_usdc - self.committed_usdc_arb)
291
+
292
+ def available_usdt_arb(self, inv_usdt: float) -> float:
293
+ return max(0.0, inv_usdt - self.committed_usdt_arb)
294
+
295
+ def commit_usdc(self, amount: float) -> None:
296
+ self.committed_usdc_arb += amount
297
+
298
+ def commit_usdt(self, amount: float) -> None:
299
+ self.committed_usdt_arb += amount
300
+
301
+
302
+ @dataclass
303
+ class DesiredState:
304
+ mode: Mode
305
+
306
+ # Target allocations (USD)
307
+ target_spot_usd: float
308
+ target_hl_margin_usd: float
309
+ target_boros_collateral_usd: float
310
+
311
+ # Target positions
312
+ target_hype_short_size: float
313
+ target_boros_position_usd: float
314
+
315
+ # Selected Boros market
316
+ boros_market_id: int | None = None
317
+ boros_market_symbol: str | None = None
318
+ boros_tenor_days: float | None = None
319
+
320
+
321
+ @dataclass
322
+ class Plan:
323
+ desired_state: DesiredState
324
+ steps: list[PlanStep] = field(default_factory=list)
325
+ messages: list[str] = field(default_factory=list)
326
+
327
+ def add_step(
328
+ self,
329
+ op: PlanOp,
330
+ priority: int,
331
+ key: str,
332
+ params: dict[str, Any] | None = None,
333
+ reason: str = "",
334
+ ) -> None:
335
+ if not any(s.key == key for s in self.steps):
336
+ self.steps.append(
337
+ PlanStep(
338
+ op=op,
339
+ priority=priority,
340
+ key=key,
341
+ params=params or {},
342
+ reason=reason,
343
+ )
344
+ )
345
+
346
+ def sort_steps(self) -> None:
347
+ self.steps.sort()
348
+
349
+
350
+ # Operations that change inventory and require re-observing
351
+ # Note: ENSURE_GAS_ON_HYPEREVM is excluded because it may return early
352
+ # without actually changing anything (e.g., "will be provisioned during routing")
353
+ INVENTORY_CHANGING_OPS: set[PlanOp] = {
354
+ PlanOp.CLOSE_AND_REDEPLOY,
355
+ PlanOp.PARTIAL_TRIM_SPOT,
356
+ PlanOp.COMPLETE_PENDING_WITHDRAWAL,
357
+ PlanOp.FUND_BOROS,
358
+ PlanOp.SEND_USDC_TO_HL,
359
+ PlanOp.BRIDGE_TO_HYPEREVM,
360
+ PlanOp.TRANSFER_HL_SPOT_TO_HYPEREVM,
361
+ PlanOp.DEPLOY_EXCESS_HL_MARGIN,
362
+ PlanOp.SWAP_HYPE_TO_LST,
363
+ PlanOp.ENSURE_HL_SHORT,
364
+ PlanOp.ENSURE_BOROS_POSITION,
365
+ }