wayfinder-paths 0.1.14__py3-none-any.whl → 0.1.16__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.
Files changed (58) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +19 -20
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +91 -22
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +5 -11
  4. wayfinder_paths/adapters/brap_adapter/README.md +22 -19
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +95 -45
  6. wayfinder_paths/adapters/brap_adapter/test_adapter.py +8 -24
  7. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +40 -42
  8. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +8 -15
  9. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +6 -6
  10. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +12 -12
  11. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +6 -6
  12. wayfinder_paths/adapters/moonwell_adapter/README.md +29 -31
  13. wayfinder_paths/adapters/moonwell_adapter/adapter.py +326 -364
  14. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +285 -189
  15. wayfinder_paths/adapters/pool_adapter/test_adapter.py +2 -2
  16. wayfinder_paths/adapters/token_adapter/test_adapter.py +4 -4
  17. wayfinder_paths/core/config.py +8 -47
  18. wayfinder_paths/core/constants/base.py +0 -1
  19. wayfinder_paths/core/constants/erc20_abi.py +13 -24
  20. wayfinder_paths/core/engine/StrategyJob.py +3 -1
  21. wayfinder_paths/core/services/test_local_evm_txn.py +145 -0
  22. wayfinder_paths/core/strategies/Strategy.py +22 -4
  23. wayfinder_paths/core/utils/erc20_service.py +100 -0
  24. wayfinder_paths/core/utils/evm_helpers.py +1 -8
  25. wayfinder_paths/core/utils/transaction.py +191 -0
  26. wayfinder_paths/core/utils/web3.py +66 -0
  27. wayfinder_paths/policies/erc20.py +1 -1
  28. wayfinder_paths/run_strategy.py +42 -6
  29. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +263 -220
  30. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +132 -155
  31. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -1
  32. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +123 -80
  33. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -12
  34. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +6 -6
  35. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2270 -1328
  36. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +282 -121
  37. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -1
  38. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +107 -85
  39. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -8
  40. wayfinder_paths/templates/adapter/README.md +1 -1
  41. wayfinder_paths/templates/strategy/README.md +1 -5
  42. {wayfinder_paths-0.1.14.dist-info → wayfinder_paths-0.1.16.dist-info}/METADATA +3 -41
  43. {wayfinder_paths-0.1.14.dist-info → wayfinder_paths-0.1.16.dist-info}/RECORD +45 -54
  44. {wayfinder_paths-0.1.14.dist-info → wayfinder_paths-0.1.16.dist-info}/WHEEL +1 -1
  45. wayfinder_paths/abis/generic/erc20.json +0 -383
  46. wayfinder_paths/core/clients/sdk_example.py +0 -125
  47. wayfinder_paths/core/engine/__init__.py +0 -5
  48. wayfinder_paths/core/services/__init__.py +0 -0
  49. wayfinder_paths/core/services/base.py +0 -130
  50. wayfinder_paths/core/services/local_evm_txn.py +0 -334
  51. wayfinder_paths/core/services/local_token_txn.py +0 -242
  52. wayfinder_paths/core/services/web3_service.py +0 -43
  53. wayfinder_paths/core/wallets/README.md +0 -88
  54. wayfinder_paths/core/wallets/WalletManager.py +0 -56
  55. wayfinder_paths/core/wallets/__init__.py +0 -7
  56. wayfinder_paths/scripts/run_strategy.py +0 -152
  57. wayfinder_paths/strategies/config.py +0 -85
  58. {wayfinder_paths-0.1.14.dist-info → wayfinder_paths-0.1.16.dist-info}/LICENSE +0 -0
@@ -1,4 +1,3 @@
1
- from types import SimpleNamespace
2
1
  from unittest.mock import AsyncMock
3
2
 
4
3
  import pytest
@@ -16,24 +15,9 @@ class TestBRAPAdapter:
16
15
  return mock_client
17
16
 
18
17
  @pytest.fixture
19
- def mock_web3_service(self):
20
- """Minimal Web3Service stub for adapter construction."""
21
- wallet_provider = SimpleNamespace(
22
- broadcast_transaction=AsyncMock(return_value=(True, {}))
23
- )
24
- token_txn = SimpleNamespace(
25
- build_send=AsyncMock(return_value=(True, {})),
26
- build_erc20_approve=AsyncMock(return_value=(True, {})),
27
- read_erc20_allowance=AsyncMock(return_value={"allowance": 0}),
28
- )
29
- return SimpleNamespace(
30
- evm_transactions=wallet_provider, token_transactions=token_txn
31
- )
32
-
33
- @pytest.fixture
34
- def adapter(self, mock_brap_client, mock_web3_service):
18
+ def adapter(self, mock_brap_client):
35
19
  """Create a BRAPAdapter instance with mocked client for testing"""
36
- adapter = BRAPAdapter(web3_service=mock_web3_service)
20
+ adapter = BRAPAdapter()
37
21
  adapter.brap_client = mock_brap_client
38
22
  return adapter
39
23
 
@@ -83,7 +67,7 @@ class TestBRAPAdapter:
83
67
  slippage=0.01,
84
68
  )
85
69
 
86
- assert success is True
70
+ assert success
87
71
  assert data == mock_response
88
72
  mock_brap_client.get_quote.assert_called_once_with(
89
73
  from_token="0x" + "a" * 40,
@@ -125,7 +109,7 @@ class TestBRAPAdapter:
125
109
  amount="1000000000000000000",
126
110
  )
127
111
 
128
- assert success is True
112
+ assert success
129
113
  assert data["input_amount"] == 1000000000000000000
130
114
  assert data["output_amount"] == 995000000000000000
131
115
 
@@ -183,7 +167,7 @@ class TestBRAPAdapter:
183
167
  slippage=0.01,
184
168
  )
185
169
 
186
- assert success is True
170
+ assert success
187
171
  assert data["input_amount"] == 1000000000000000000
188
172
  assert data["output_amount"] == 995000000000000000
189
173
  assert data["gas_fee"] == 5000000000000000
@@ -242,7 +226,7 @@ class TestBRAPAdapter:
242
226
  amount="1000000000000000000",
243
227
  )
244
228
 
245
- assert success is True
229
+ assert success
246
230
  assert data["total_routes"] == 2
247
231
  assert len(data["all_routes"]) == 2
248
232
  assert data["best_route"]["output_amount"] == 995000000000000000
@@ -254,7 +238,7 @@ class TestBRAPAdapter:
254
238
  from_chain_id=8453, to_chain_id=1, operation_type="swap"
255
239
  )
256
240
 
257
- assert success is True
241
+ assert success
258
242
  assert data["from_chain"] == "base"
259
243
  assert data["to_chain"] == "ethereum"
260
244
  assert data["from_gas_estimate"] == 100000
@@ -288,7 +272,7 @@ class TestBRAPAdapter:
288
272
  amount="1000000000000000000",
289
273
  )
290
274
 
291
- assert success is True
275
+ assert success
292
276
  assert data["valid"] is True
293
277
  assert data["quote_available"] is True
294
278
  assert data["estimated_output"] == "995000000000000000"
@@ -12,12 +12,16 @@ from wayfinder_paths.core.clients.HyperlendClient import (
12
12
  MarketEntry,
13
13
  StableMarketsHeadroomResponse,
14
14
  )
15
- from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
16
15
  from wayfinder_paths.core.constants.hyperlend_abi import (
17
16
  POOL_ABI,
18
17
  WRAPPED_TOKEN_GATEWAY_ABI,
19
18
  )
20
- from wayfinder_paths.core.services.base import Web3Service
19
+ from wayfinder_paths.core.utils.erc20_service import (
20
+ build_approve_transaction,
21
+ get_token_allowance,
22
+ )
23
+ from wayfinder_paths.core.utils.transaction import send_transaction
24
+ from wayfinder_paths.core.utils.web3 import web3_from_chain_id
21
25
 
22
26
  HYPERLEND_DEFAULTS = {
23
27
  "pool": "0x00A89d7a5A02160f20150EbEA7a2b5E4879A1A8b",
@@ -34,15 +38,16 @@ class HyperlendAdapter(BaseAdapter):
34
38
  def __init__(
35
39
  self,
36
40
  config: dict[str, Any],
37
- web3_service: Web3Service,
41
+ simulation: bool = False,
42
+ strategy_wallet_signing_callback=None,
38
43
  ) -> None:
39
44
  super().__init__("hyperlend_adapter", config)
40
45
  cfg = config or {}
41
46
  adapter_cfg = cfg.get("hyperlend_adapter") or {}
42
47
 
48
+ self.simulation = simulation
49
+ self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
43
50
  self.hyperlend_client = HyperlendClient()
44
- self.web3 = web3_service
45
- self.token_txn_service = web3_service.token_transactions
46
51
 
47
52
  self.strategy_wallet = cfg.get("strategy_wallet") or {}
48
53
  self.pool_address = self._checksum(
@@ -165,7 +170,7 @@ class HyperlendAdapter(BaseAdapter):
165
170
  from_address=strategy,
166
171
  chain_id=chain_id,
167
172
  )
168
- return await self._execute(tx)
173
+ return await self._send_tx(tx)
169
174
 
170
175
  async def unlend(
171
176
  self,
@@ -200,12 +205,19 @@ class HyperlendAdapter(BaseAdapter):
200
205
  from_address=strategy,
201
206
  chain_id=chain_id,
202
207
  )
203
- return await self._execute(tx)
208
+ return await self._send_tx(tx)
204
209
 
205
210
  # ------------------------------------------------------------------ #
206
211
  # Helpers #
207
212
  # ------------------------------------------------------------------ #
208
213
 
214
+ async def _send_tx(self, tx: dict[str, Any]) -> tuple[bool, Any]:
215
+ """Send transaction with simulation check."""
216
+ if self.simulation:
217
+ return True, {"simulation": tx}
218
+ txn_hash = await send_transaction(tx, self.strategy_wallet_signing_callback)
219
+ return True, txn_hash
220
+
209
221
  async def _ensure_allowance(
210
222
  self,
211
223
  *,
@@ -215,32 +227,17 @@ class HyperlendAdapter(BaseAdapter):
215
227
  amount: int,
216
228
  chain_id: int,
217
229
  ) -> tuple[bool, Any]:
218
- chain = {"id": chain_id}
219
- allowance = await self.token_txn_service.read_erc20_allowance(
220
- chain, token_address, owner, spender
221
- )
230
+ allowance = await get_token_allowance(token_address, chain_id, owner, spender)
222
231
  if allowance.get("allowance", 0) >= amount:
223
232
  return True, {}
224
- build_success, approve_tx = self.token_txn_service.build_erc20_approve(
233
+ approve_tx = await build_approve_transaction(
234
+ from_address=owner,
225
235
  chain_id=chain_id,
226
236
  token_address=token_address,
227
- from_address=owner,
228
237
  spender=spender,
229
238
  amount=amount,
230
239
  )
231
- if not build_success:
232
- return False, approve_tx
233
- return await self._broadcast_transaction(approve_tx)
234
-
235
- async def _execute(self, tx: dict[str, Any]) -> tuple[bool, Any]:
236
- return await self.web3.broadcast_transaction(
237
- tx, wait_for_receipt=True, timeout=DEFAULT_TRANSACTION_TIMEOUT
238
- )
239
-
240
- async def _broadcast_transaction(self, tx: dict[str, Any]) -> tuple[bool, Any]:
241
- return await self.web3.evm_transactions.broadcast_transaction(
242
- tx, wait_for_receipt=True, timeout=DEFAULT_TRANSACTION_TIMEOUT
243
- )
240
+ return await self._send_tx(approve_tx)
244
241
 
245
242
  async def _encode_call(
246
243
  self,
@@ -254,23 +251,24 @@ class HyperlendAdapter(BaseAdapter):
254
251
  value: int = 0,
255
252
  ) -> dict[str, Any]:
256
253
  """Encode calldata without touching network."""
257
- web3 = self.web3.get_web3(chain_id)
258
- contract = web3.eth.contract(address=target, abi=abi)
259
- try:
260
- data = await getattr(contract.functions, fn_name)(*args).build_transaction(
261
- {"from": from_address}
262
- )["data"]
263
- except ValueError as exc:
264
- raise ValueError(f"Failed to encode {fn_name}: {exc}") from exc
265
254
 
266
- tx: dict[str, Any] = {
267
- "chainId": int(chain_id),
268
- "from": to_checksum_address(from_address),
269
- "to": to_checksum_address(target),
270
- "data": data,
271
- "value": int(value),
272
- }
273
- return tx
255
+ async with web3_from_chain_id(chain_id) as web3:
256
+ contract = web3.eth.contract(address=target, abi=abi)
257
+ try:
258
+ data = await getattr(contract.functions, fn_name)(
259
+ *args
260
+ ).build_transaction({"from": from_address})["data"]
261
+ except ValueError as exc:
262
+ raise ValueError(f"Failed to encode {fn_name}: {exc}") from exc
263
+
264
+ tx: dict[str, Any] = {
265
+ "chainId": int(chain_id),
266
+ "from": to_checksum_address(from_address),
267
+ "to": to_checksum_address(target),
268
+ "data": data,
269
+ "value": int(value),
270
+ }
271
+ return tx
274
272
 
275
273
  def _strategy_address(self) -> str:
276
274
  addr = None
@@ -1,4 +1,3 @@
1
- from types import SimpleNamespace
2
1
  from unittest.mock import AsyncMock
3
2
 
4
3
  import pytest
@@ -16,16 +15,10 @@ class TestHyperlendAdapter:
16
15
  return mock_client
17
16
 
18
17
  @pytest.fixture
19
- def mock_web3_service(self):
20
- """Minimal Web3Service stub for adapter construction."""
21
- return SimpleNamespace(token_transactions=SimpleNamespace())
22
-
23
- @pytest.fixture
24
- def adapter(self, mock_hyperlend_client, mock_web3_service):
18
+ def adapter(self, mock_hyperlend_client):
25
19
  """Create a HyperlendAdapter instance with mocked client for testing"""
26
20
  adapter = HyperlendAdapter(
27
21
  config={},
28
- web3_service=mock_web3_service,
29
22
  )
30
23
  adapter.hyperlend_client = mock_hyperlend_client
31
24
  return adapter
@@ -64,7 +57,7 @@ class TestHyperlendAdapter:
64
57
  min_buffer_tokens=100.0,
65
58
  )
66
59
 
67
- assert success is True
60
+ assert success
68
61
  assert data == mock_response
69
62
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
70
63
  required_underlying_tokens=1000.0,
@@ -95,7 +88,7 @@ class TestHyperlendAdapter:
95
88
 
96
89
  success, data = await adapter.get_stable_markets()
97
90
 
98
- assert success is True
91
+ assert success
99
92
  assert data == mock_response
100
93
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
101
94
  required_underlying_tokens=None,
@@ -115,7 +108,7 @@ class TestHyperlendAdapter:
115
108
  required_underlying_tokens=500.0
116
109
  )
117
110
 
118
- assert success is True
111
+ assert success
119
112
  assert data == mock_response
120
113
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
121
114
  required_underlying_tokens=500.0,
@@ -157,7 +150,7 @@ class TestHyperlendAdapter:
157
150
 
158
151
  success, data = await adapter.get_stable_markets()
159
152
 
160
- assert success is True
153
+ assert success
161
154
  assert data == mock_response
162
155
  assert len(data.get("markets", {})) == 0
163
156
 
@@ -203,7 +196,7 @@ class TestHyperlendAdapter:
203
196
 
204
197
  success, data = await adapter.get_stable_markets()
205
198
 
206
- assert success is True
199
+ assert success
207
200
  assert data == mock_response
208
201
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
209
202
  required_underlying_tokens=None,
@@ -270,7 +263,7 @@ class TestHyperlendAdapter:
270
263
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
271
264
  )
272
265
 
273
- assert success is True
266
+ assert success
274
267
  assert data == mock_response
275
268
  mock_hyperlend_client.get_assets_view.assert_called_once_with(
276
269
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
@@ -321,7 +314,7 @@ class TestHyperlendAdapter:
321
314
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
322
315
  )
323
316
 
324
- assert success is True
317
+ assert success
325
318
  assert data == mock_response
326
319
  assert len(data.get("assets", [])) == 0
327
320
  # New API uses account_data; total_value may not be present
@@ -72,41 +72,41 @@ class TestHyperliquidAdapter:
72
72
  async def test_get_meta_and_asset_ctxs(self, adapter):
73
73
  """Test fetching market metadata."""
74
74
  success, data = await adapter.get_meta_and_asset_ctxs()
75
- assert success is True
75
+ assert success
76
76
  assert "universe" in data[0]
77
77
 
78
78
  @pytest.mark.asyncio
79
79
  async def test_get_spot_meta(self, adapter):
80
80
  """Test fetching spot metadata."""
81
81
  success, data = await adapter.get_spot_meta()
82
- assert success is True
82
+ assert success
83
83
 
84
84
  @pytest.mark.asyncio
85
85
  async def test_get_funding_history(self, adapter):
86
86
  """Test fetching funding history."""
87
87
  success, data = await adapter.get_funding_history("ETH", 1700000000000)
88
- assert success is True
88
+ assert success
89
89
  assert isinstance(data, list)
90
90
 
91
91
  @pytest.mark.asyncio
92
92
  async def test_get_candles(self, adapter):
93
93
  """Test fetching candle data."""
94
94
  success, data = await adapter.get_candles("ETH", "1h", 1700000000000)
95
- assert success is True
95
+ assert success
96
96
  assert isinstance(data, list)
97
97
 
98
98
  @pytest.mark.asyncio
99
99
  async def test_get_l2_book(self, adapter):
100
100
  """Test fetching order book."""
101
101
  success, data = await adapter.get_l2_book("ETH")
102
- assert success is True
102
+ assert success
103
103
  assert "levels" in data
104
104
 
105
105
  @pytest.mark.asyncio
106
106
  async def test_get_user_state(self, adapter):
107
107
  """Test fetching user state."""
108
108
  success, data = await adapter.get_user_state("0x1234")
109
- assert success is True
109
+ assert success
110
110
  assert "assetPositions" in data
111
111
 
112
112
  @pytest.mark.asyncio
@@ -28,7 +28,7 @@ class TestSpotAssetIDs:
28
28
  """Verify get_spot_assets returns a populated dict."""
29
29
  success, spot_assets = await live_adapter.get_spot_assets()
30
30
 
31
- assert success is True
31
+ assert success
32
32
  assert isinstance(spot_assets, dict)
33
33
  assert len(spot_assets) > 0
34
34
 
@@ -37,7 +37,7 @@ class TestSpotAssetIDs:
37
37
  """PURR/USDC should be the first spot pair (index 0 + 10000 = 10000)."""
38
38
  success, spot_assets = await live_adapter.get_spot_assets()
39
39
 
40
- assert success is True
40
+ assert success
41
41
  assert "PURR/USDC" in spot_assets
42
42
  assert spot_assets["PURR/USDC"] == 10000
43
43
 
@@ -46,7 +46,7 @@ class TestSpotAssetIDs:
46
46
  """HYPE/USDC should have asset ID 10107."""
47
47
  success, spot_assets = await live_adapter.get_spot_assets()
48
48
 
49
- assert success is True
49
+ assert success
50
50
  assert "HYPE/USDC" in spot_assets
51
51
  # HYPE is index 107, so asset_id = 10107
52
52
  assert spot_assets["HYPE/USDC"] == 10107
@@ -56,7 +56,7 @@ class TestSpotAssetIDs:
56
56
  """ETH/USDC spot pair should exist."""
57
57
  success, spot_assets = await live_adapter.get_spot_assets()
58
58
 
59
- assert success is True
59
+ assert success
60
60
  # ETH spot may have different naming, check common variants
61
61
  eth_pairs = [k for k in spot_assets if "ETH" in k and "USDC" in k]
62
62
  assert len(eth_pairs) > 0, (
@@ -68,7 +68,7 @@ class TestSpotAssetIDs:
68
68
  """BTC/USDC spot pair should exist."""
69
69
  success, spot_assets = await live_adapter.get_spot_assets()
70
70
 
71
- assert success is True
71
+ assert success
72
72
  # BTC spot may have different naming
73
73
  btc_pairs = [k for k in spot_assets if "BTC" in k and "USDC" in k]
74
74
  assert len(btc_pairs) > 0, (
@@ -80,7 +80,7 @@ class TestSpotAssetIDs:
80
80
  """All spot asset IDs should be >= 10000."""
81
81
  success, spot_assets = await live_adapter.get_spot_assets()
82
82
 
83
- assert success is True
83
+ assert success
84
84
  for name, asset_id in spot_assets.items():
85
85
  assert asset_id >= 10000, f"{name} has invalid asset_id {asset_id}"
86
86
 
@@ -89,7 +89,7 @@ class TestSpotAssetIDs:
89
89
  """Test synchronous helper after cache is populated."""
90
90
  # First populate cache
91
91
  success, _ = await live_adapter.get_spot_assets()
92
- assert success is True
92
+ assert success
93
93
 
94
94
  # Now use sync helper
95
95
  purr_id = live_adapter.get_spot_asset_id("PURR", "USDC")
@@ -147,7 +147,7 @@ class TestSpotMetaStructure:
147
147
  """Spot meta should have tokens array."""
148
148
  success, spot_meta = await live_adapter.get_spot_meta()
149
149
 
150
- assert success is True
150
+ assert success
151
151
  assert "tokens" in spot_meta
152
152
  assert isinstance(spot_meta["tokens"], list)
153
153
  assert len(spot_meta["tokens"]) > 0
@@ -157,7 +157,7 @@ class TestSpotMetaStructure:
157
157
  """Spot meta should have universe array with pairs."""
158
158
  success, spot_meta = await live_adapter.get_spot_meta()
159
159
 
160
- assert success is True
160
+ assert success
161
161
  assert "universe" in spot_meta
162
162
  assert isinstance(spot_meta["universe"], list)
163
163
  assert len(spot_meta["universe"]) > 0
@@ -167,7 +167,7 @@ class TestSpotMetaStructure:
167
167
  """Each spot universe entry should have tokens and index."""
168
168
  success, spot_meta = await live_adapter.get_spot_meta()
169
169
 
170
- assert success is True
170
+ assert success
171
171
  for pair in spot_meta["universe"][:5]: # Check first 5
172
172
  assert "tokens" in pair, f"Missing tokens in {pair}"
173
173
  assert "index" in pair, f"Missing index in {pair}"
@@ -182,7 +182,7 @@ class TestL2BookResolution:
182
182
  """PURR/USDC (10000) should return valid L2 book."""
183
183
  success, book = await live_adapter.get_spot_l2_book(10000)
184
184
 
185
- assert success is True
185
+ assert success
186
186
  assert "levels" in book
187
187
 
188
188
  @pytest.mark.asyncio
@@ -190,7 +190,7 @@ class TestL2BookResolution:
190
190
  """HYPE/USDC (10107) should return valid L2 book."""
191
191
  success, book = await live_adapter.get_spot_l2_book(10107)
192
192
 
193
- assert success is True
193
+ assert success
194
194
  assert "levels" in book
195
195
 
196
196
 
@@ -45,7 +45,7 @@ class TestLedgerAdapter:
45
45
  offset=0,
46
46
  )
47
47
 
48
- assert success is True
48
+ assert success
49
49
  assert data == mock_response
50
50
  mock_ledger_client.get_strategy_transactions.assert_called_once_with(
51
51
  wallet_address="0x1234567890123456789012345678901234567890",
@@ -84,7 +84,7 @@ class TestLedgerAdapter:
84
84
  wallet_address="0x1234567890123456789012345678901234567890"
85
85
  )
86
86
 
87
- assert success is True
87
+ assert success
88
88
  assert data == mock_response
89
89
  mock_ledger_client.get_strategy_net_deposit.assert_called_once_with(
90
90
  wallet_address="0x1234567890123456789012345678901234567890"
@@ -110,7 +110,7 @@ class TestLedgerAdapter:
110
110
  strategy_name="TestStrategy",
111
111
  )
112
112
 
113
- assert success is True
113
+ assert success
114
114
  assert data == mock_response
115
115
  mock_ledger_client.add_strategy_deposit.assert_called_once_with(
116
116
  wallet_address="0x1234567890123456789012345678901234567890",
@@ -142,7 +142,7 @@ class TestLedgerAdapter:
142
142
  strategy_name="TestStrategy",
143
143
  )
144
144
 
145
- assert success is True
145
+ assert success
146
146
  assert data == mock_response
147
147
 
148
148
  @pytest.mark.asyncio
@@ -177,7 +177,7 @@ class TestLedgerAdapter:
177
177
  strategy_name="TestStrategy",
178
178
  )
179
179
 
180
- assert success is True
180
+ assert success
181
181
  assert data == mock_response
182
182
 
183
183
  @pytest.mark.asyncio
@@ -197,7 +197,7 @@ class TestLedgerAdapter:
197
197
  wallet_address="0x1234567890123456789012345678901234567890", limit=10
198
198
  )
199
199
 
200
- assert success is True
200
+ assert success
201
201
  assert data["total_transactions"] == 3
202
202
  assert data["operations"]["deposits"] == 1
203
203
  assert data["operations"]["withdrawals"] == 1
@@ -8,6 +8,7 @@ Adapter for interacting with the [Moonwell](https://moonwell.fi/) lending protoc
8
8
  ## Capabilities
9
9
 
10
10
  The adapter provides the following capabilities:
11
+
11
12
  - Lending: Supply and withdraw tokens
12
13
  - Borrowing: Borrow and repay tokens
13
14
  - Collateral management: Enable/disable markets as collateral
@@ -17,6 +18,7 @@ The adapter provides the following capabilities:
17
18
  ## Overview
18
19
 
19
20
  The MoonwellAdapter provides functionality for:
21
+
20
22
  - **Lending**: Supply tokens to earn yield
21
23
  - **Borrowing**: Borrow against collateral
22
24
  - **Collateral Management**: Enable/disable markets as collateral
@@ -25,10 +27,10 @@ The MoonwellAdapter provides functionality for:
25
27
 
26
28
  ## Supported Markets (Base Chain)
27
29
 
28
- | Token | mToken Address | Underlying Address |
29
- |-------|----------------|-------------------|
30
- | USDC | `0xEdc817A28E8B93B03976FBd4a3dDBc9f7D176c22` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
31
- | WETH | `0x628ff693426583D9a7FB391E54366292F509D457` | `0x4200000000000000000000000000000000000006` |
30
+ | Token | mToken Address | Underlying Address |
31
+ | ------ | -------------------------------------------- | -------------------------------------------- |
32
+ | USDC | `0xEdc817A28E8B93B03976FBd4a3dDBc9f7D176c22` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
33
+ | WETH | `0x628ff693426583D9a7FB391E54366292F509D457` | `0x4200000000000000000000000000000000000006` |
32
34
  | wstETH | `0x627Fe393Bc6EdDA28e99AE648fD6fF362514304b` | `0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452` |
33
35
 
34
36
  ## Protocol Addresses (Base Chain)
@@ -40,7 +42,6 @@ The MoonwellAdapter provides functionality for:
40
42
  ## Construction
41
43
 
42
44
  ```python
43
- from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
44
45
  from wayfinder_paths.adapters.moonwell_adapter import MoonwellAdapter
45
46
 
46
47
  config = {
@@ -49,12 +50,9 @@ config = {
49
50
  "chain_id": 8453, # Base chain (default)
50
51
  }
51
52
  }
52
- web3_service = DefaultWeb3Service(config)
53
- adapter = MoonwellAdapter(config=config, web3_service=web3_service)
53
+ adapter = MoonwellAdapter(config=config)
54
54
  ```
55
55
 
56
- `web3_service` is required so the adapter can share the same wallet provider as the rest of the strategy.
57
-
58
56
  ## Usage
59
57
 
60
58
  ```python
@@ -102,45 +100,45 @@ await adapter.claim_rewards()
102
100
 
103
101
  ### Lending Operations
104
102
 
105
- | Method | Description |
106
- |--------|-------------|
107
- | `lend(mtoken, underlying_token, amount)` | Supply tokens to earn yield |
108
- | `unlend(mtoken, amount)` | Withdraw by redeeming mTokens |
103
+ | Method | Description |
104
+ | ---------------------------------------- | ----------------------------- |
105
+ | `lend(mtoken, underlying_token, amount)` | Supply tokens to earn yield |
106
+ | `unlend(mtoken, amount)` | Withdraw by redeeming mTokens |
109
107
 
110
108
  ### Borrowing Operations
111
109
 
112
- | Method | Description |
113
- |--------|-------------|
114
- | `borrow(mtoken, amount)` | Borrow underlying tokens |
115
- | `repay(mtoken, underlying_token, amount)` | Repay borrowed tokens |
110
+ | Method | Description |
111
+ | ----------------------------------------- | ------------------------ |
112
+ | `borrow(mtoken, amount)` | Borrow underlying tokens |
113
+ | `repay(mtoken, underlying_token, amount)` | Repay borrowed tokens |
116
114
 
117
115
  ### Collateral Management
118
116
 
119
- | Method | Description |
120
- |--------|-------------|
121
- | `set_collateral(mtoken)` | Enable market as collateral (enterMarkets) |
122
- | `remove_collateral(mtoken)` | Disable market as collateral (exitMarket) |
117
+ | Method | Description |
118
+ | --------------------------- | ------------------------------------------ |
119
+ | `set_collateral(mtoken)` | Enable market as collateral (enterMarkets) |
120
+ | `remove_collateral(mtoken)` | Disable market as collateral (exitMarket) |
123
121
 
124
122
  ### Position & Market Data
125
123
 
126
- | Method | Description |
127
- |--------|-------------|
128
- | `get_pos(mtoken, account)` | Get position data (balances, debt, rewards) |
129
- | `get_collateral_factor(mtoken)` | Get collateral factor (LTV) |
130
- | `get_apy(mtoken, apy_type)` | Get supply or borrow APY |
131
- | `get_borrowable_amount(account)` | Get max borrowable in USD |
124
+ | Method | Description |
125
+ | ------------------------------------------ | -------------------------------------------------- |
126
+ | `get_pos(mtoken, account)` | Get position data (balances, debt, rewards) |
127
+ | `get_collateral_factor(mtoken)` | Get collateral factor (LTV) |
128
+ | `get_apy(mtoken, apy_type)` | Get supply or borrow APY |
129
+ | `get_borrowable_amount(account)` | Get max borrowable in USD |
132
130
  | `max_withdrawable_mtoken(mtoken, account)` | Calculate safe withdrawal amount via binary search |
133
131
 
134
132
  ### Rewards
135
133
 
136
- | Method | Description |
137
- |--------|-------------|
134
+ | Method | Description |
135
+ | -------------------------------- | --------------------------------------------- |
138
136
  | `claim_rewards(min_rewards_usd)` | Claim WELL rewards (skips if below threshold) |
139
137
 
140
138
  ### Utilities
141
139
 
142
- | Method | Description |
143
- |--------|-------------|
140
+ | Method | Description |
141
+ | ------------------ | ---------------- |
144
142
  | `wrap_eth(amount)` | Wrap ETH to WETH |
145
143
 
146
144
  All methods return `(success: bool, payload: Any)` tuples. On failure the payload is an error string.