wayfinder-paths 0.1.15__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.

Potentially problematic release.


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

Files changed (47) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +19 -20
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +66 -37
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +2 -8
  4. wayfinder_paths/adapters/brap_adapter/README.md +22 -19
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +33 -34
  6. wayfinder_paths/adapters/brap_adapter/test_adapter.py +2 -18
  7. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +40 -56
  8. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +1 -8
  9. wayfinder_paths/adapters/moonwell_adapter/README.md +29 -31
  10. wayfinder_paths/adapters/moonwell_adapter/adapter.py +301 -662
  11. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +275 -179
  12. wayfinder_paths/core/config.py +8 -47
  13. wayfinder_paths/core/constants/base.py +0 -1
  14. wayfinder_paths/core/constants/erc20_abi.py +13 -13
  15. wayfinder_paths/core/strategies/Strategy.py +6 -2
  16. wayfinder_paths/core/utils/erc20_service.py +100 -0
  17. wayfinder_paths/core/utils/evm_helpers.py +1 -1
  18. wayfinder_paths/core/utils/transaction.py +191 -0
  19. wayfinder_paths/core/utils/web3.py +66 -0
  20. wayfinder_paths/run_strategy.py +37 -6
  21. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +200 -224
  22. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +128 -151
  23. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -1
  24. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +52 -78
  25. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -12
  26. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +0 -1
  27. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +39 -64
  28. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -1
  29. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +42 -85
  30. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -8
  31. wayfinder_paths/templates/strategy/README.md +1 -5
  32. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/METADATA +3 -41
  33. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/RECORD +35 -44
  34. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/WHEEL +1 -1
  35. wayfinder_paths/core/clients/sdk_example.py +0 -125
  36. wayfinder_paths/core/engine/__init__.py +0 -5
  37. wayfinder_paths/core/services/__init__.py +0 -0
  38. wayfinder_paths/core/services/base.py +0 -131
  39. wayfinder_paths/core/services/local_evm_txn.py +0 -350
  40. wayfinder_paths/core/services/local_token_txn.py +0 -238
  41. wayfinder_paths/core/services/web3_service.py +0 -43
  42. wayfinder_paths/core/wallets/README.md +0 -88
  43. wayfinder_paths/core/wallets/WalletManager.py +0 -56
  44. wayfinder_paths/core/wallets/__init__.py +0 -7
  45. wayfinder_paths/scripts/run_strategy.py +0 -152
  46. wayfinder_paths/strategies/config.py +0 -85
  47. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/LICENSE +0 -0
@@ -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(
@@ -77,20 +82,6 @@ class HyperlendAdapter(BaseAdapter):
77
82
  buffer_bps=buffer_bps,
78
83
  min_buffer_tokens=min_buffer_tokens,
79
84
  )
80
- # Strategies expect a dict with "markets" and "notes"; normalize if API returns a list
81
- if isinstance(data, list):
82
- markets: dict[str, Any] = {}
83
- for i, item in enumerate(data):
84
- if isinstance(item, dict) and "address" in item:
85
- markets[item["address"]] = item
86
- elif isinstance(item, dict):
87
- markets[str(i)] = item
88
- data = {"markets": markets, "notes": []}
89
- elif isinstance(data, dict) and ("markets" not in data or "notes" not in data):
90
- data = {
91
- "markets": data.get("markets", {}),
92
- "notes": data.get("notes", []),
93
- }
94
85
  return True, data
95
86
  except Exception as exc:
96
87
  return False, str(exc)
@@ -179,7 +170,7 @@ class HyperlendAdapter(BaseAdapter):
179
170
  from_address=strategy,
180
171
  chain_id=chain_id,
181
172
  )
182
- return await self._execute(tx)
173
+ return await self._send_tx(tx)
183
174
 
184
175
  async def unlend(
185
176
  self,
@@ -214,12 +205,19 @@ class HyperlendAdapter(BaseAdapter):
214
205
  from_address=strategy,
215
206
  chain_id=chain_id,
216
207
  )
217
- return await self._execute(tx)
208
+ return await self._send_tx(tx)
218
209
 
219
210
  # ------------------------------------------------------------------ #
220
211
  # Helpers #
221
212
  # ------------------------------------------------------------------ #
222
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
+
223
221
  async def _ensure_allowance(
224
222
  self,
225
223
  *,
@@ -229,32 +227,17 @@ class HyperlendAdapter(BaseAdapter):
229
227
  amount: int,
230
228
  chain_id: int,
231
229
  ) -> tuple[bool, Any]:
232
- chain = {"id": chain_id}
233
- allowance = await self.token_txn_service.read_erc20_allowance(
234
- chain, token_address, owner, spender
235
- )
230
+ allowance = await get_token_allowance(token_address, chain_id, owner, spender)
236
231
  if allowance.get("allowance", 0) >= amount:
237
232
  return True, {}
238
- build_success, approve_tx = self.token_txn_service.build_erc20_approve(
233
+ approve_tx = await build_approve_transaction(
234
+ from_address=owner,
239
235
  chain_id=chain_id,
240
236
  token_address=token_address,
241
- from_address=owner,
242
237
  spender=spender,
243
238
  amount=amount,
244
239
  )
245
- if not build_success:
246
- return False, approve_tx
247
- return await self._broadcast_transaction(approve_tx)
248
-
249
- async def _execute(self, tx: dict[str, Any]) -> tuple[bool, Any]:
250
- return await self.web3.broadcast_transaction(
251
- tx, wait_for_receipt=True, timeout=DEFAULT_TRANSACTION_TIMEOUT
252
- )
253
-
254
- async def _broadcast_transaction(self, tx: dict[str, Any]) -> tuple[bool, Any]:
255
- return await self.web3.evm_transactions.broadcast_transaction(
256
- tx, wait_for_receipt=True, timeout=DEFAULT_TRANSACTION_TIMEOUT
257
- )
240
+ return await self._send_tx(approve_tx)
258
241
 
259
242
  async def _encode_call(
260
243
  self,
@@ -268,23 +251,24 @@ class HyperlendAdapter(BaseAdapter):
268
251
  value: int = 0,
269
252
  ) -> dict[str, Any]:
270
253
  """Encode calldata without touching network."""
271
- web3 = self.web3.get_web3(chain_id)
272
- contract = web3.eth.contract(address=target, abi=abi)
273
- try:
274
- data = await getattr(contract.functions, fn_name)(*args).build_transaction(
275
- {"from": from_address}
276
- )["data"]
277
- except ValueError as exc:
278
- raise ValueError(f"Failed to encode {fn_name}: {exc}") from exc
279
254
 
280
- tx: dict[str, Any] = {
281
- "chainId": int(chain_id),
282
- "from": to_checksum_address(from_address),
283
- "to": to_checksum_address(target),
284
- "data": data,
285
- "value": int(value),
286
- }
287
- 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
288
272
 
289
273
  def _strategy_address(self) -> str:
290
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
@@ -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.