wayfinder-paths 0.1.3__py3-none-any.whl → 0.1.5__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.
- wayfinder_paths/CONFIG_GUIDE.md +37 -32
- wayfinder_paths/__init__.py +3 -3
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/README.md +12 -12
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/adapter.py +12 -11
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/examples.json +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/test_adapter.py +12 -6
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/README.md +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/adapter.py +30 -23
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/test_adapter.py +2 -2
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +7 -0
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/adapter.py +33 -26
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/test_adapter.py +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/README.md +27 -40
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/adapter.py +78 -75
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/examples.json +10 -4
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +11 -0
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/test_adapter.py +33 -28
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/README.md +2 -14
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/adapter.py +12 -19
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/test_adapter.py +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/README.md +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/adapter.py +8 -4
- wayfinder_paths/adapters/token_adapter/examples.json +26 -0
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/test_adapter.py +1 -1
- wayfinder_paths/config.example.json +3 -1
- wayfinder_paths/core/__init__.py +3 -3
- wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
- wayfinder_paths/core/adapters/models.py +41 -0
- wayfinder_paths/core/clients/BRAPClient.py +21 -2
- wayfinder_paths/core/clients/ClientManager.py +42 -63
- wayfinder_paths/core/clients/HyperlendClient.py +46 -5
- wayfinder_paths/core/clients/LedgerClient.py +350 -124
- wayfinder_paths/core/clients/PoolClient.py +51 -19
- wayfinder_paths/core/clients/SimulationClient.py +16 -4
- wayfinder_paths/core/clients/TokenClient.py +34 -18
- wayfinder_paths/core/clients/TransactionClient.py +18 -2
- wayfinder_paths/core/clients/WalletClient.py +35 -4
- wayfinder_paths/core/clients/WayfinderClient.py +16 -5
- wayfinder_paths/core/clients/protocols.py +69 -62
- wayfinder_paths/core/clients/sdk_example.py +0 -5
- wayfinder_paths/core/config.py +192 -103
- wayfinder_paths/core/constants/base.py +17 -0
- wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
- wayfinder_paths/core/engine/__init__.py +2 -2
- wayfinder_paths/core/engine/manifest.py +1 -1
- wayfinder_paths/core/services/base.py +6 -4
- wayfinder_paths/core/services/local_evm_txn.py +3 -2
- wayfinder_paths/core/settings.py +2 -2
- wayfinder_paths/core/strategies/Strategy.py +123 -37
- wayfinder_paths/core/utils/evm_helpers.py +12 -10
- wayfinder_paths/core/wallets/README.md +3 -3
- wayfinder_paths/core/wallets/WalletManager.py +3 -3
- wayfinder_paths/{vaults/policies → policies}/enso.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/hyper_evm.py +2 -2
- wayfinder_paths/{vaults/policies → policies}/hyperlend.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/moonwell.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/prjx.py +1 -1
- wayfinder_paths/run_strategy.py +29 -27
- wayfinder_paths/scripts/create_strategy.py +3 -3
- wayfinder_paths/scripts/make_wallets.py +6 -6
- wayfinder_paths/scripts/validate_manifests.py +2 -2
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/README.md +10 -9
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/manifest.yaml +1 -1
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/strategy.py +47 -167
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/test_strategy.py +10 -8
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/README.md +15 -14
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/manifest.yaml +2 -2
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/strategy.py +97 -97
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/test_strategy.py +8 -8
- wayfinder_paths/{vaults/templates → templates}/adapter/README.md +5 -5
- wayfinder_paths/{vaults/templates → templates}/adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/templates → templates}/adapter/test_adapter.py +1 -1
- wayfinder_paths/{vaults/templates → templates}/strategy/README.md +10 -9
- wayfinder_paths/{vaults/templates → templates}/strategy/manifest.yaml +1 -1
- wayfinder_paths/{vaults/templates → templates}/strategy/test_strategy.py +8 -8
- wayfinder_paths/tests/test_test_coverage.py +5 -5
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/METADATA +146 -69
- wayfinder_paths-0.1.5.dist-info/RECORD +126 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +0 -7
- wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +0 -11
- wayfinder_paths/vaults/adapters/token_adapter/examples.json +0 -26
- wayfinder_paths/vaults/strategies/__init__.py +0 -0
- wayfinder_paths-0.1.3.dist-info/RECORD +0 -126
- /wayfinder_paths/{vaults → adapters}/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/token_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/erc20.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/evm.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/hyperliquid.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/util.py +0 -0
- /wayfinder_paths/{vaults/adapters → strategies}/__init__.py +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/config.py +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/adapter/adapter.py +0 -0
- /wayfinder_paths/{vaults/templates → templates}/adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/strategy/strategy.py +0 -0
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/WHEEL +0 -0
|
@@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, patch
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from wayfinder_paths.
|
|
5
|
+
from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TestLedgerAdapter:
|
|
@@ -18,14 +18,14 @@ class TestLedgerAdapter:
|
|
|
18
18
|
def adapter(self, mock_ledger_client):
|
|
19
19
|
"""Create a LedgerAdapter instance with mocked client for testing"""
|
|
20
20
|
with patch(
|
|
21
|
-
"
|
|
21
|
+
"adapters.ledger_adapter.adapter.LedgerClient",
|
|
22
22
|
return_value=mock_ledger_client,
|
|
23
23
|
):
|
|
24
24
|
return LedgerAdapter()
|
|
25
25
|
|
|
26
26
|
@pytest.mark.asyncio
|
|
27
|
-
async def
|
|
28
|
-
"""Test successful
|
|
27
|
+
async def test_get_strategy_transactions_success(self, adapter, mock_ledger_client):
|
|
28
|
+
"""Test successful strategy transaction retrieval"""
|
|
29
29
|
mock_response = {
|
|
30
30
|
"transactions": [
|
|
31
31
|
{
|
|
@@ -37,11 +37,11 @@ class TestLedgerAdapter:
|
|
|
37
37
|
],
|
|
38
38
|
"total": 1,
|
|
39
39
|
}
|
|
40
|
-
mock_ledger_client.
|
|
40
|
+
mock_ledger_client.get_strategy_transactions = AsyncMock(
|
|
41
41
|
return_value=mock_response
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
success, data = await adapter.
|
|
44
|
+
success, data = await adapter.get_strategy_transactions(
|
|
45
45
|
wallet_address="0x1234567890123456789012345678901234567890",
|
|
46
46
|
limit=10,
|
|
47
47
|
offset=0,
|
|
@@ -49,20 +49,20 @@ class TestLedgerAdapter:
|
|
|
49
49
|
|
|
50
50
|
assert success is True
|
|
51
51
|
assert data == mock_response
|
|
52
|
-
mock_ledger_client.
|
|
52
|
+
mock_ledger_client.get_strategy_transactions.assert_called_once_with(
|
|
53
53
|
wallet_address="0x1234567890123456789012345678901234567890",
|
|
54
54
|
limit=10,
|
|
55
55
|
offset=0,
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
@pytest.mark.asyncio
|
|
59
|
-
async def
|
|
60
|
-
"""Test
|
|
61
|
-
mock_ledger_client.
|
|
59
|
+
async def test_get_strategy_transactions_failure(self, adapter, mock_ledger_client):
|
|
60
|
+
"""Test strategy transaction retrieval failure"""
|
|
61
|
+
mock_ledger_client.get_strategy_transactions = AsyncMock(
|
|
62
62
|
side_effect=Exception("API Error")
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
success, data = await adapter.
|
|
65
|
+
success, data = await adapter.get_strategy_transactions(
|
|
66
66
|
wallet_address="0x1234567890123456789012345678901234567890"
|
|
67
67
|
)
|
|
68
68
|
|
|
@@ -70,23 +70,25 @@ class TestLedgerAdapter:
|
|
|
70
70
|
assert "API Error" in data
|
|
71
71
|
|
|
72
72
|
@pytest.mark.asyncio
|
|
73
|
-
async def
|
|
74
|
-
"""Test successful
|
|
73
|
+
async def test_get_strategy_net_deposit_success(self, adapter, mock_ledger_client):
|
|
74
|
+
"""Test successful strategy net deposit retrieval"""
|
|
75
75
|
mock_response = {
|
|
76
76
|
"net_deposit": "1000.00",
|
|
77
77
|
"total_deposits": "1500.00",
|
|
78
78
|
"total_withdrawals": "500.00",
|
|
79
79
|
}
|
|
80
|
-
mock_ledger_client.
|
|
80
|
+
mock_ledger_client.get_strategy_net_deposit = AsyncMock(
|
|
81
|
+
return_value=mock_response
|
|
82
|
+
)
|
|
81
83
|
|
|
82
84
|
# Test
|
|
83
|
-
success, data = await adapter.
|
|
85
|
+
success, data = await adapter.get_strategy_net_deposit(
|
|
84
86
|
wallet_address="0x1234567890123456789012345678901234567890"
|
|
85
87
|
)
|
|
86
88
|
|
|
87
89
|
assert success is True
|
|
88
90
|
assert data == mock_response
|
|
89
|
-
mock_ledger_client.
|
|
91
|
+
mock_ledger_client.get_strategy_net_deposit.assert_called_once_with(
|
|
90
92
|
wallet_address="0x1234567890123456789012345678901234567890"
|
|
91
93
|
)
|
|
92
94
|
|
|
@@ -98,7 +100,7 @@ class TestLedgerAdapter:
|
|
|
98
100
|
"status": "recorded",
|
|
99
101
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
100
102
|
}
|
|
101
|
-
mock_ledger_client.
|
|
103
|
+
mock_ledger_client.add_strategy_deposit.return_value = mock_response
|
|
102
104
|
|
|
103
105
|
# Test
|
|
104
106
|
success, data = await adapter.record_deposit(
|
|
@@ -112,7 +114,7 @@ class TestLedgerAdapter:
|
|
|
112
114
|
|
|
113
115
|
assert success is True
|
|
114
116
|
assert data == mock_response
|
|
115
|
-
mock_ledger_client.
|
|
117
|
+
mock_ledger_client.add_strategy_deposit.assert_called_once_with(
|
|
116
118
|
wallet_address="0x1234567890123456789012345678901234567890",
|
|
117
119
|
chain_id=8453,
|
|
118
120
|
token_address="0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
|
|
@@ -130,7 +132,7 @@ class TestLedgerAdapter:
|
|
|
130
132
|
"status": "recorded",
|
|
131
133
|
"timestamp": "2024-01-15T11:00:00Z",
|
|
132
134
|
}
|
|
133
|
-
mock_ledger_client.
|
|
135
|
+
mock_ledger_client.add_strategy_withdraw.return_value = mock_response
|
|
134
136
|
|
|
135
137
|
# Test
|
|
136
138
|
success, data = await adapter.record_withdrawal(
|
|
@@ -148,21 +150,24 @@ class TestLedgerAdapter:
|
|
|
148
150
|
@pytest.mark.asyncio
|
|
149
151
|
async def test_record_operation_success(self, adapter, mock_ledger_client):
|
|
150
152
|
"""Test successful operation recording"""
|
|
153
|
+
from wayfinder_paths.core.adapters.models import SWAP
|
|
154
|
+
|
|
151
155
|
mock_response = {
|
|
152
156
|
"operation_id": "op_123",
|
|
153
157
|
"status": "recorded",
|
|
154
158
|
"timestamp": "2024-01-15T10:45:00Z",
|
|
155
159
|
}
|
|
156
|
-
mock_ledger_client.
|
|
160
|
+
mock_ledger_client.add_strategy_operation.return_value = mock_response
|
|
157
161
|
|
|
158
162
|
# Test
|
|
159
|
-
operation_data =
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
operation_data = SWAP(
|
|
164
|
+
from_token_id="0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
|
|
165
|
+
to_token_id="0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
|
|
166
|
+
from_amount="1000000000000000000",
|
|
167
|
+
to_amount="995000000000000000",
|
|
168
|
+
from_amount_usd=1000.0,
|
|
169
|
+
to_amount_usd=995.0,
|
|
170
|
+
)
|
|
166
171
|
|
|
167
172
|
success, data = await adapter.record_operation(
|
|
168
173
|
wallet_address="0x1234567890123456789012345678901234567890",
|
|
@@ -184,7 +189,7 @@ class TestLedgerAdapter:
|
|
|
184
189
|
{"operation": "SWAP", "amount": "200000000000000000"},
|
|
185
190
|
]
|
|
186
191
|
}
|
|
187
|
-
mock_ledger_client.
|
|
192
|
+
mock_ledger_client.get_strategy_transactions.return_value = mock_transactions
|
|
188
193
|
|
|
189
194
|
# Test
|
|
190
195
|
success, data = await adapter.get_transaction_summary(
|
|
@@ -24,7 +24,7 @@ The PoolClient will automatically:
|
|
|
24
24
|
### Initialize the Adapter
|
|
25
25
|
|
|
26
26
|
```python
|
|
27
|
-
from wayfinder_paths.
|
|
27
|
+
from wayfinder_paths.adapters.pool_adapter.adapter import PoolAdapter
|
|
28
28
|
|
|
29
29
|
# No configuration needed - uses PoolClient with automatic settings
|
|
30
30
|
adapter = PoolAdapter()
|
|
@@ -55,17 +55,6 @@ else:
|
|
|
55
55
|
print(f"Error: {data}")
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
### Get Combined Pool Reports
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
success, data = await adapter.get_combined_pool_reports()
|
|
62
|
-
if success:
|
|
63
|
-
reports = data.get("reports", [])
|
|
64
|
-
print(f"Received {len(reports)} combined reports")
|
|
65
|
-
else:
|
|
66
|
-
print(f"Error: {data}")
|
|
67
|
-
```
|
|
68
|
-
|
|
69
58
|
### Find High Yield Pools
|
|
70
59
|
|
|
71
60
|
```python
|
|
@@ -194,7 +183,6 @@ The adapter uses the following Wayfinder API endpoints:
|
|
|
194
183
|
|
|
195
184
|
- `GET /api/v1/public/pools/?pool_ids=X` - Get pools by IDs
|
|
196
185
|
- `GET /api/v1/public/pools/` - Get all pools
|
|
197
|
-
- `GET /api/v1/public/pools/combined/` - Get combined pool reports
|
|
198
186
|
- `GET /api/v1/public/pools/llama/matches/` - Get Llama matches
|
|
199
187
|
- `GET /api/v1/public/pools/llama/reports/` - Get Llama reports
|
|
200
188
|
|
|
@@ -209,7 +197,7 @@ All methods return a tuple of `(success: bool, data: Any)` where:
|
|
|
209
197
|
Run the adapter tests:
|
|
210
198
|
|
|
211
199
|
```bash
|
|
212
|
-
pytest wayfinder_paths/
|
|
200
|
+
pytest wayfinder_paths/adapters/pool_adapter/test_adapter.py -v
|
|
213
201
|
```
|
|
214
202
|
|
|
215
203
|
## Dependencies
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
4
|
-
from wayfinder_paths.core.clients.PoolClient import
|
|
4
|
+
from wayfinder_paths.core.clients.PoolClient import (
|
|
5
|
+
LlamaMatch,
|
|
6
|
+
LlamaReport,
|
|
7
|
+
PoolClient,
|
|
8
|
+
PoolList,
|
|
9
|
+
)
|
|
5
10
|
|
|
6
11
|
|
|
7
12
|
class PoolAdapter(BaseAdapter):
|
|
@@ -27,7 +32,7 @@ class PoolAdapter(BaseAdapter):
|
|
|
27
32
|
|
|
28
33
|
async def get_pools_by_ids(
|
|
29
34
|
self, pool_ids: list[str], merge_external: bool | None = None
|
|
30
|
-
) -> tuple[bool,
|
|
35
|
+
) -> tuple[bool, PoolList | str]:
|
|
31
36
|
"""
|
|
32
37
|
Get pool information by pool IDs.
|
|
33
38
|
|
|
@@ -50,7 +55,7 @@ class PoolAdapter(BaseAdapter):
|
|
|
50
55
|
|
|
51
56
|
async def get_all_pools(
|
|
52
57
|
self, merge_external: bool | None = None
|
|
53
|
-
) -> tuple[bool,
|
|
58
|
+
) -> tuple[bool, PoolList | str]:
|
|
54
59
|
"""
|
|
55
60
|
Get all available pools.
|
|
56
61
|
|
|
@@ -67,21 +72,7 @@ class PoolAdapter(BaseAdapter):
|
|
|
67
72
|
self.logger.error(f"Error fetching all pools: {e}")
|
|
68
73
|
return (False, str(e))
|
|
69
74
|
|
|
70
|
-
async def
|
|
71
|
-
"""
|
|
72
|
-
Get combined pool reports with analytics.
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
Tuple of (success, data) where data is combined reports or error message
|
|
76
|
-
"""
|
|
77
|
-
try:
|
|
78
|
-
data = await self.pool_client.get_combined_pool_reports()
|
|
79
|
-
return (True, data)
|
|
80
|
-
except Exception as e:
|
|
81
|
-
self.logger.error(f"Error fetching combined pool reports: {e}")
|
|
82
|
-
return (False, str(e))
|
|
83
|
-
|
|
84
|
-
async def get_llama_matches(self) -> tuple[bool, Any]:
|
|
75
|
+
async def get_llama_matches(self) -> tuple[bool, dict[str, LlamaMatch] | str]:
|
|
85
76
|
"""
|
|
86
77
|
Get Llama protocol matches for pools.
|
|
87
78
|
|
|
@@ -95,7 +86,9 @@ class PoolAdapter(BaseAdapter):
|
|
|
95
86
|
self.logger.error(f"Error fetching Llama matches: {e}")
|
|
96
87
|
return (False, str(e))
|
|
97
88
|
|
|
98
|
-
async def get_llama_reports(
|
|
89
|
+
async def get_llama_reports(
|
|
90
|
+
self, identifiers: list[str]
|
|
91
|
+
) -> tuple[bool, dict[str, LlamaReport] | str]:
|
|
99
92
|
"""
|
|
100
93
|
Get Llama reports for specific identifiers.
|
|
101
94
|
|
|
@@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, patch
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from wayfinder_paths.
|
|
5
|
+
from wayfinder_paths.adapters.pool_adapter.adapter import PoolAdapter
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TestPoolAdapter:
|
|
@@ -18,7 +18,7 @@ class TestPoolAdapter:
|
|
|
18
18
|
def adapter(self, mock_pool_client):
|
|
19
19
|
"""Create a PoolAdapter instance with mocked client for testing"""
|
|
20
20
|
with patch(
|
|
21
|
-
"
|
|
21
|
+
"adapters.pool_adapter.adapter.PoolClient",
|
|
22
22
|
return_value=mock_pool_client,
|
|
23
23
|
):
|
|
24
24
|
return PoolAdapter()
|
|
@@ -20,7 +20,7 @@ The TokenClient will automatically:
|
|
|
20
20
|
### Initialize the Adapter
|
|
21
21
|
|
|
22
22
|
```python
|
|
23
|
-
from wayfinder_paths.
|
|
23
|
+
from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
|
|
24
24
|
|
|
25
25
|
# No configuration needed - uses TokenClient with automatic settings
|
|
26
26
|
adapter = TokenAdapter()
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
4
|
-
from wayfinder_paths.core.clients.TokenClient import
|
|
4
|
+
from wayfinder_paths.core.clients.TokenClient import (
|
|
5
|
+
GasToken,
|
|
6
|
+
TokenClient,
|
|
7
|
+
TokenDetails,
|
|
8
|
+
)
|
|
5
9
|
|
|
6
10
|
|
|
7
11
|
class TokenAdapter(BaseAdapter):
|
|
@@ -20,7 +24,7 @@ class TokenAdapter(BaseAdapter):
|
|
|
20
24
|
super().__init__("token_adapter", config)
|
|
21
25
|
self.token_client = token_client or TokenClient()
|
|
22
26
|
|
|
23
|
-
async def get_token(self, query: str) -> tuple[bool,
|
|
27
|
+
async def get_token(self, query: str) -> tuple[bool, TokenDetails | str]:
|
|
24
28
|
"""
|
|
25
29
|
Get token data by address using the token-details endpoint.
|
|
26
30
|
|
|
@@ -39,7 +43,7 @@ class TokenAdapter(BaseAdapter):
|
|
|
39
43
|
self.logger.error(f"Error getting token by query {query}: {e}")
|
|
40
44
|
return (False, str(e))
|
|
41
45
|
|
|
42
|
-
async def get_token_price(self, token_id: str) -> tuple[bool, Any]:
|
|
46
|
+
async def get_token_price(self, token_id: str) -> tuple[bool, dict[str, Any] | str]:
|
|
43
47
|
"""
|
|
44
48
|
Get token price by token ID or address using the token-details endpoint.
|
|
45
49
|
|
|
@@ -72,7 +76,7 @@ class TokenAdapter(BaseAdapter):
|
|
|
72
76
|
self.logger.error(f"Error getting token price for {token_id}: {e}")
|
|
73
77
|
return (False, str(e))
|
|
74
78
|
|
|
75
|
-
async def get_gas_token(self, chain_code: str) -> tuple[bool,
|
|
79
|
+
async def get_gas_token(self, chain_code: str) -> tuple[bool, GasToken | str]:
|
|
76
80
|
"""
|
|
77
81
|
Get gas token for a given chain code.
|
|
78
82
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"basic_usage": {
|
|
3
|
+
"description": "Basic usage of TokenAdapter to get token information",
|
|
4
|
+
"code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Get token by address\nsuccess, data = await adapter.get_token_by_address(\"0x1234567890abcdef1234567890abcdef12345678\")\nif success:\n print(f\"Token symbol: {data.get('symbol')}\")\n print(f\"Token name: {data.get('name')}\")\n print(f\"Decimals: {data.get('decimals')}\")\nelse:\n print(f\"Error: {data}\")"
|
|
5
|
+
},
|
|
6
|
+
"get_by_token_id": {
|
|
7
|
+
"description": "Get token information using token ID",
|
|
8
|
+
"code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Get token by ID\nsuccess, data = await adapter.get_token_by_id(\"token-12345\")\nif success:\n print(f\"Token data: {data}\")\nelse:\n print(f\"Error: {data}\")"
|
|
9
|
+
},
|
|
10
|
+
"flexible_lookup": {
|
|
11
|
+
"description": "Use flexible get_token method that tries both address and token_id",
|
|
12
|
+
"code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Try both address and token_id\nsuccess, data = await adapter.get_token(\n address=\"0x1234567890abcdef1234567890abcdef12345678\",\n token_id=\"token-12345\"\n)\nif success:\n print(f\"Found token: {data}\")\nelse:\n print(f\"Token not found: {data}\")"
|
|
13
|
+
},
|
|
14
|
+
"error_handling": {
|
|
15
|
+
"description": "Proper error handling for various scenarios",
|
|
16
|
+
"code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Handle missing parameters\ntry:\n success, data = await adapter.get_token()\n if not success:\n print(f\"Error: {data}\")\nexcept Exception as e:\n print(f\"Unexpected error: {e}\")\n\n# Handle API errors\nsuccess, data = await adapter.get_token_by_address(\"invalid-address\")\nif not success:\n print(f\"API error: {data}\")\nelse:\n print(f\"Token found: {data}\")"
|
|
17
|
+
},
|
|
18
|
+
"health_check": {
|
|
19
|
+
"description": "Check adapter health and connectivity",
|
|
20
|
+
"code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Check health\nhealth = await adapter.health_check()\nprint(f\"Adapter status: {health['status']}\")\nprint(f\"Connected: {health['connected']}\")\nprint(f\"Adapter type: {health['adapter']}\")\n\nif health['status'] == 'healthy':\n print(\"Adapter is ready to use\")\nelse:\n print(f\"Adapter has issues: {health.get('error', 'Unknown error')}\")"
|
|
21
|
+
},
|
|
22
|
+
"batch_operations": {
|
|
23
|
+
"description": "Perform multiple token lookups efficiently",
|
|
24
|
+
"code": "from adapters.token_adapter.adapter import TokenAdapter\nimport asyncio\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# List of token addresses to lookup\ntoken_addresses = [\n \"0x1234567890abcdef1234567890abcdef12345678\",\n \"0xabcdef1234567890abcdef1234567890abcdef12\",\n \"0x9876543210fedcba9876543210fedcba98765432\"\n]\n\n# Batch lookup\ntoken_data = {}\nfor address in token_addresses:\n success, data = await adapter.get_token_by_address(address)\n if success:\n token_data[address] = data\n else:\n print(f\"Failed to get token for {address}: {data}\")\n\nprint(f\"Successfully retrieved {len(token_data)} tokens\")\nfor address, data in token_data.items():\n print(f\"{address}: {data.get('symbol', 'Unknown')} - {data.get('name', 'Unknown')}\")"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
"user": {
|
|
3
3
|
"username": "your_username",
|
|
4
4
|
"password": "your_password",
|
|
5
|
-
"refresh_token": null
|
|
5
|
+
"refresh_token": null,
|
|
6
|
+
"api_key": "sk_live_abc123..."
|
|
6
7
|
},
|
|
7
8
|
"system": {
|
|
8
9
|
"api_base_url": "https://wayfinder.ai/api/v1",
|
|
10
|
+
"api_key": "sk_live_abc123...",
|
|
9
11
|
"wallets_path": "wallets.json"
|
|
10
12
|
},
|
|
11
13
|
"strategy": {
|
wayfinder_paths/core/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""Wayfinder
|
|
1
|
+
"""Wayfinder Paths Core Engine"""
|
|
2
2
|
|
|
3
3
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
4
|
-
from wayfinder_paths.core.engine.
|
|
4
|
+
from wayfinder_paths.core.engine.StrategyJob import StrategyJob
|
|
5
5
|
from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
@@ -9,5 +9,5 @@ __all__ = [
|
|
|
9
9
|
"StatusDict",
|
|
10
10
|
"StatusTuple",
|
|
11
11
|
"BaseAdapter",
|
|
12
|
-
"
|
|
12
|
+
"StrategyJob",
|
|
13
13
|
]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import ABC
|
|
2
4
|
from typing import Any
|
|
3
5
|
|
|
@@ -7,7 +9,7 @@ from loguru import logger
|
|
|
7
9
|
class BaseAdapter(ABC):
|
|
8
10
|
"""Base adapter class for exchange/protocol integrations"""
|
|
9
11
|
|
|
10
|
-
adapter_type: str = None
|
|
12
|
+
adapter_type: str | None = None
|
|
11
13
|
|
|
12
14
|
def __init__(self, name: str, config: dict[str, Any] | None = None):
|
|
13
15
|
self.name = name
|
|
@@ -19,7 +21,22 @@ class BaseAdapter(ABC):
|
|
|
19
21
|
return True
|
|
20
22
|
|
|
21
23
|
async def get_balance(self, asset: str) -> dict[str, Any]:
|
|
22
|
-
"""
|
|
24
|
+
"""
|
|
25
|
+
Get balance for an asset.
|
|
26
|
+
Optional method that can be overridden by subclasses.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
asset: Asset identifier (token address, token ID, etc.).
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Dictionary containing balance information.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ValueError: If asset is empty or invalid.
|
|
36
|
+
NotImplementedError: If this adapter does not support balance queries.
|
|
37
|
+
"""
|
|
38
|
+
if not asset or not isinstance(asset, str) or not asset.strip():
|
|
39
|
+
raise ValueError("asset must be a non-empty string")
|
|
23
40
|
raise NotImplementedError(
|
|
24
41
|
f"get_balance not supported by {self.__class__.__name__}"
|
|
25
42
|
)
|
|
@@ -43,6 +60,6 @@ class BaseAdapter(ABC):
|
|
|
43
60
|
"adapter": self.adapter_type or self.__class__.__name__,
|
|
44
61
|
}
|
|
45
62
|
|
|
46
|
-
async def close(self):
|
|
63
|
+
async def close(self) -> None:
|
|
47
64
|
"""Clean up resources"""
|
|
48
65
|
pass
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Pydantic models for ledger operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SWAP(BaseModel):
|
|
9
|
+
"""Swap operation."""
|
|
10
|
+
|
|
11
|
+
type: Literal["SWAP"] = "SWAP"
|
|
12
|
+
from_token_id: str
|
|
13
|
+
to_token_id: str
|
|
14
|
+
from_amount: str
|
|
15
|
+
to_amount: str
|
|
16
|
+
from_amount_usd: float
|
|
17
|
+
to_amount_usd: float
|
|
18
|
+
transaction_hash: str | None = None
|
|
19
|
+
transaction_status: str | None = None
|
|
20
|
+
transaction_receipt: dict[str, Any] | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LEND(BaseModel):
|
|
24
|
+
type: Literal["LEND"] = "LEND"
|
|
25
|
+
contract: str
|
|
26
|
+
amount: int
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class UNLEND(BaseModel):
|
|
30
|
+
type: Literal["UNLEND"] = "UNLEND"
|
|
31
|
+
contract: str
|
|
32
|
+
amount: int
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Type alias for operation types (currently only SWAP is used)
|
|
36
|
+
# Add more operation types here as needed
|
|
37
|
+
Operation = SWAP | LEND | UNLEND
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class STRAT_OP(BaseModel):
|
|
41
|
+
op_data: Annotated[Operation, Field(discriminator="type")]
|
|
@@ -3,8 +3,10 @@ BRAP (Bridge/Router/Adapter Protocol) Client
|
|
|
3
3
|
Provides access to quote operations via the public quote endpoint.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import time
|
|
7
|
-
from typing import Any
|
|
9
|
+
from typing import Any, NotRequired, Required, TypedDict
|
|
8
10
|
|
|
9
11
|
from loguru import logger
|
|
10
12
|
|
|
@@ -13,6 +15,23 @@ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
|
13
15
|
from wayfinder_paths.core.settings import settings
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
class BRAPQuote(TypedDict):
|
|
19
|
+
"""BRAP quote response structure"""
|
|
20
|
+
|
|
21
|
+
from_token_address: Required[str]
|
|
22
|
+
to_token_address: Required[str]
|
|
23
|
+
from_chain_id: Required[int]
|
|
24
|
+
to_chain_id: Required[int]
|
|
25
|
+
from_address: Required[str]
|
|
26
|
+
to_address: Required[str]
|
|
27
|
+
amount1: Required[str]
|
|
28
|
+
amount2: NotRequired[str]
|
|
29
|
+
routes: NotRequired[list[dict[str, Any]]]
|
|
30
|
+
fees: NotRequired[dict[str, Any] | None]
|
|
31
|
+
slippage: NotRequired[float | None]
|
|
32
|
+
wayfinder_fee: NotRequired[float | None]
|
|
33
|
+
|
|
34
|
+
|
|
16
35
|
class BRAPClient(WayfinderClient):
|
|
17
36
|
"""Client for BRAP quote operations"""
|
|
18
37
|
|
|
@@ -33,7 +52,7 @@ class BRAPClient(WayfinderClient):
|
|
|
33
52
|
amount1: str,
|
|
34
53
|
slippage: float | None = None,
|
|
35
54
|
wayfinder_fee: float | None = None,
|
|
36
|
-
) ->
|
|
55
|
+
) -> BRAPQuote:
|
|
37
56
|
"""
|
|
38
57
|
Get a quote for a bridge/swap operation.
|
|
39
58
|
|