wayfinder-paths 0.1.19__py3-none-any.whl → 0.1.21__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/__init__.py +0 -2
- wayfinder_paths/adapters/balance_adapter/README.md +59 -45
- wayfinder_paths/adapters/balance_adapter/adapter.py +1 -22
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -14
- wayfinder_paths/adapters/brap_adapter/README.md +61 -184
- wayfinder_paths/adapters/brap_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/brap_adapter/adapter.py +1 -148
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +0 -15
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +1 -10
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +0 -17
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +3 -312
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +1 -71
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -57
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -17
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +2 -42
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +1 -9
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +15 -47
- wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -7
- wayfinder_paths/adapters/ledger_adapter/README.md +54 -74
- wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -106
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -12
- wayfinder_paths/adapters/moonwell_adapter/README.md +67 -106
- wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +10 -122
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +84 -83
- wayfinder_paths/adapters/pool_adapter/README.md +30 -51
- wayfinder_paths/adapters/pool_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/pool_adapter/adapter.py +0 -19
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -8
- wayfinder_paths/adapters/token_adapter/README.md +41 -49
- wayfinder_paths/adapters/token_adapter/adapter.py +0 -32
- wayfinder_paths/adapters/token_adapter/test_adapter.py +1 -12
- wayfinder_paths/conftest.py +0 -8
- wayfinder_paths/core/__init__.py +0 -2
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -22
- wayfinder_paths/core/adapters/__init__.py +0 -5
- wayfinder_paths/core/adapters/models.py +0 -5
- wayfinder_paths/core/analytics/__init__.py +0 -2
- wayfinder_paths/core/analytics/bootstrap.py +0 -16
- wayfinder_paths/core/analytics/stats.py +0 -7
- wayfinder_paths/core/analytics/test_analytics.py +5 -34
- wayfinder_paths/core/clients/BRAPClient.py +0 -35
- wayfinder_paths/core/clients/ClientManager.py +0 -51
- wayfinder_paths/core/clients/HyperlendClient.py +0 -77
- wayfinder_paths/core/clients/LedgerClient.py +2 -122
- wayfinder_paths/core/clients/PoolClient.py +0 -2
- wayfinder_paths/core/clients/TokenClient.py +0 -39
- wayfinder_paths/core/clients/WalletClient.py +0 -15
- wayfinder_paths/core/clients/WayfinderClient.py +0 -24
- wayfinder_paths/core/clients/__init__.py +0 -4
- wayfinder_paths/core/clients/protocols.py +25 -98
- wayfinder_paths/core/config.py +0 -24
- wayfinder_paths/core/constants/__init__.py +0 -7
- wayfinder_paths/core/constants/base.py +2 -9
- wayfinder_paths/core/constants/erc20_abi.py +0 -5
- wayfinder_paths/core/constants/hyperlend_abi.py +0 -7
- wayfinder_paths/core/constants/moonwell_abi.py +0 -35
- wayfinder_paths/core/engine/StrategyJob.py +0 -32
- wayfinder_paths/core/strategies/Strategy.py +0 -99
- wayfinder_paths/core/strategies/__init__.py +0 -2
- wayfinder_paths/core/utils/__init__.py +0 -1
- wayfinder_paths/core/utils/evm_helpers.py +0 -50
- wayfinder_paths/core/utils/{erc20_service.py → tokens.py} +25 -21
- wayfinder_paths/core/utils/transaction.py +0 -1
- wayfinder_paths/run_strategy.py +0 -46
- wayfinder_paths/scripts/create_strategy.py +0 -17
- wayfinder_paths/scripts/make_wallets.py +1 -4
- wayfinder_paths/strategies/basis_trading_strategy/README.md +71 -163
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -24
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +36 -400
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +15 -64
- wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -4
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +65 -56
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +4 -27
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -10
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +71 -72
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +23 -227
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +120 -113
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +64 -59
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +4 -44
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +2 -35
- wayfinder_paths/templates/adapter/README.md +107 -46
- wayfinder_paths/templates/adapter/adapter.py +0 -9
- wayfinder_paths/templates/adapter/test_adapter.py +0 -19
- wayfinder_paths/templates/strategy/README.md +113 -59
- wayfinder_paths/templates/strategy/strategy.py +0 -22
- wayfinder_paths/templates/strategy/test_strategy.py +0 -28
- wayfinder_paths/tests/test_test_coverage.py +2 -12
- wayfinder_paths/tests/test_utils.py +1 -31
- wayfinder_paths-0.1.21.dist-info/METADATA +355 -0
- wayfinder_paths-0.1.21.dist-info/RECORD +129 -0
- {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.21.dist-info}/WHEEL +1 -1
- wayfinder_paths/core/adapters/base.py +0 -5
- wayfinder_paths-0.1.19.dist-info/METADATA +0 -592
- wayfinder_paths-0.1.19.dist-info/RECORD +0 -130
- {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.21.dist-info}/LICENSE +0 -0
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Client Manager
|
|
3
|
-
Consolidated client management for all API interactions
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
1
|
from typing import Any
|
|
7
2
|
|
|
8
3
|
from wayfinder_paths.core.clients.BRAPClient import BRAPClient
|
|
@@ -22,28 +17,11 @@ from wayfinder_paths.core.clients.WalletClient import WalletClient
|
|
|
22
17
|
|
|
23
18
|
|
|
24
19
|
class ClientManager:
|
|
25
|
-
"""
|
|
26
|
-
Manages all API client instances for a strategy job.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
clients: Optional dict of pre-instantiated clients to inject directly.
|
|
30
|
-
Keys: 'token', 'hyperlend', 'ledger', 'wallet', 'transaction', 'pool', 'brap'.
|
|
31
|
-
If not provided, defaults to HTTP-based clients.
|
|
32
|
-
skip_auth: If True, skips authentication (for SDK usage).
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
20
|
def __init__(
|
|
36
21
|
self,
|
|
37
22
|
clients: dict[str, Any] | None = None,
|
|
38
23
|
skip_auth: bool = False,
|
|
39
24
|
):
|
|
40
|
-
"""
|
|
41
|
-
Initialize ClientManager.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
clients: Optional dict of pre-instantiated clients to inject directly.
|
|
45
|
-
skip_auth: If True, skips authentication (for SDK usage).
|
|
46
|
-
"""
|
|
47
25
|
self._injected_clients = clients or {}
|
|
48
26
|
self._skip_auth = skip_auth
|
|
49
27
|
|
|
@@ -60,17 +38,6 @@ class ClientManager:
|
|
|
60
38
|
injected_key: str,
|
|
61
39
|
client_class: type[Any],
|
|
62
40
|
) -> Any:
|
|
63
|
-
"""
|
|
64
|
-
Helper method to get or create a client instance.
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
client_attr: Name of the private attribute storing the client (e.g., "_token_client").
|
|
68
|
-
injected_key: Key to look up in _injected_clients dict.
|
|
69
|
-
client_class: Client class to instantiate if not injected.
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
Client instance.
|
|
73
|
-
"""
|
|
74
41
|
client = getattr(self, client_attr)
|
|
75
42
|
if not client:
|
|
76
43
|
client = self._injected_clients.get(injected_key) or client_class()
|
|
@@ -79,44 +46,26 @@ class ClientManager:
|
|
|
79
46
|
|
|
80
47
|
@property
|
|
81
48
|
def token(self) -> TokenClientProtocol:
|
|
82
|
-
"""Get or create token client"""
|
|
83
49
|
return self._get_or_create_client("_token_client", "token", TokenClient)
|
|
84
50
|
|
|
85
51
|
@property
|
|
86
52
|
def ledger(self) -> LedgerClientProtocol:
|
|
87
|
-
"""Get or create ledger client"""
|
|
88
53
|
return self._get_or_create_client("_ledger_client", "ledger", LedgerClient)
|
|
89
54
|
|
|
90
55
|
@property
|
|
91
56
|
def pool(self) -> PoolClientProtocol:
|
|
92
|
-
"""Get or create pool client"""
|
|
93
57
|
return self._get_or_create_client("_pool_client", "pool", PoolClient)
|
|
94
58
|
|
|
95
59
|
@property
|
|
96
60
|
def hyperlend(self) -> HyperlendClientProtocol:
|
|
97
|
-
"""Get or create hyperlend client"""
|
|
98
61
|
return self._get_or_create_client(
|
|
99
62
|
"_hyperlend_client", "hyperlend", HyperlendClient
|
|
100
63
|
)
|
|
101
64
|
|
|
102
65
|
@property
|
|
103
66
|
def wallet(self) -> WalletClientProtocol:
|
|
104
|
-
"""Get or create wallet client"""
|
|
105
67
|
return self._get_or_create_client("_wallet_client", "wallet", WalletClient)
|
|
106
68
|
|
|
107
69
|
@property
|
|
108
70
|
def brap(self) -> BRAPClientProtocol:
|
|
109
|
-
"""Get or create BRAP client"""
|
|
110
71
|
return self._get_or_create_client("_brap_client", "brap", BRAPClient)
|
|
111
|
-
|
|
112
|
-
def get_all_clients(self) -> dict[str, Any]:
|
|
113
|
-
"""Get all initialized clients for direct access"""
|
|
114
|
-
return {
|
|
115
|
-
"token": self._token_client,
|
|
116
|
-
"transaction": self._transaction_client,
|
|
117
|
-
"ledger": self._ledger_client,
|
|
118
|
-
"pool": self._pool_client,
|
|
119
|
-
"wallet": self._wallet_client,
|
|
120
|
-
"hyperlend": self._hyperlend_client,
|
|
121
|
-
"brap": self._brap_client,
|
|
122
|
-
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Hyperlend Client
|
|
3
|
-
Provides access to Hyperlend stable markets data via blockchain endpoints.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
1
|
from __future__ import annotations
|
|
7
2
|
|
|
8
3
|
from typing import Any, Required, TypedDict
|
|
@@ -12,8 +7,6 @@ from wayfinder_paths.core.config import get_api_base_url
|
|
|
12
7
|
|
|
13
8
|
|
|
14
9
|
class AssetsView(TypedDict):
|
|
15
|
-
"""Assets view response structure"""
|
|
16
|
-
|
|
17
10
|
block_number: Required[int]
|
|
18
11
|
user: Required[str]
|
|
19
12
|
native_balance_wei: Required[int]
|
|
@@ -24,8 +17,6 @@ class AssetsView(TypedDict):
|
|
|
24
17
|
|
|
25
18
|
|
|
26
19
|
class MarketEntry(TypedDict):
|
|
27
|
-
"""Market entry data structure"""
|
|
28
|
-
|
|
29
20
|
symbol: Required[str]
|
|
30
21
|
symbol_canonical: Required[str]
|
|
31
22
|
display_symbol: Required[str]
|
|
@@ -33,14 +24,10 @@ class MarketEntry(TypedDict):
|
|
|
33
24
|
|
|
34
25
|
|
|
35
26
|
class LendRateHistory(TypedDict):
|
|
36
|
-
"""Lend rate history response structure"""
|
|
37
|
-
|
|
38
27
|
history: Required[list[RateHistoryEntry]]
|
|
39
28
|
|
|
40
29
|
|
|
41
30
|
class MarketHeadroom(TypedDict):
|
|
42
|
-
"""Market headroom data structure"""
|
|
43
|
-
|
|
44
31
|
symbol: Required[str]
|
|
45
32
|
symbol_canonical: Required[str]
|
|
46
33
|
display_symbol: Required[str]
|
|
@@ -51,15 +38,11 @@ class MarketHeadroom(TypedDict):
|
|
|
51
38
|
|
|
52
39
|
|
|
53
40
|
class StableMarketsHeadroomResponse(TypedDict):
|
|
54
|
-
"""Stable markets headroom response structure"""
|
|
55
|
-
|
|
56
41
|
markets: Required[dict[str, MarketHeadroom]]
|
|
57
42
|
notes: Required[list[str]]
|
|
58
43
|
|
|
59
44
|
|
|
60
45
|
class RateHistoryEntry(TypedDict):
|
|
61
|
-
"""Rate history entry data structure"""
|
|
62
|
-
|
|
63
46
|
timestamp_ms: Required[int]
|
|
64
47
|
timestamp: Required[float]
|
|
65
48
|
supply_apr: Required[float]
|
|
@@ -72,8 +55,6 @@ class RateHistoryEntry(TypedDict):
|
|
|
72
55
|
|
|
73
56
|
|
|
74
57
|
class AssetInfo(TypedDict):
|
|
75
|
-
"""Asset information structure"""
|
|
76
|
-
|
|
77
58
|
underlying: Required[str]
|
|
78
59
|
symbol: Required[str]
|
|
79
60
|
symbol_canonical: Required[str]
|
|
@@ -102,8 +83,6 @@ class AssetInfo(TypedDict):
|
|
|
102
83
|
|
|
103
84
|
|
|
104
85
|
class AccountData(TypedDict):
|
|
105
|
-
"""Account data structure"""
|
|
106
|
-
|
|
107
86
|
total_collateral_base: Required[int | float]
|
|
108
87
|
total_debt_base: Required[int | float]
|
|
109
88
|
available_borrows_base: Required[int | float]
|
|
@@ -114,8 +93,6 @@ class AccountData(TypedDict):
|
|
|
114
93
|
|
|
115
94
|
|
|
116
95
|
class BaseCurrencyInfo(TypedDict):
|
|
117
|
-
"""Base currency information structure"""
|
|
118
|
-
|
|
119
96
|
marketReferenceCurrencyUnit: Required[int]
|
|
120
97
|
marketReferenceCurrencyPriceInUsd: Required[int]
|
|
121
98
|
networkBaseTokenPriceInUsd: Required[int]
|
|
@@ -123,8 +100,6 @@ class BaseCurrencyInfo(TypedDict):
|
|
|
123
100
|
|
|
124
101
|
|
|
125
102
|
class HyperlendClient(WayfinderClient):
|
|
126
|
-
"""Client for Hyperlend-related operations"""
|
|
127
|
-
|
|
128
103
|
def __init__(self):
|
|
129
104
|
super().__init__()
|
|
130
105
|
self.api_base_url = get_api_base_url()
|
|
@@ -136,20 +111,6 @@ class HyperlendClient(WayfinderClient):
|
|
|
136
111
|
buffer_bps: int | None = None,
|
|
137
112
|
min_buffer_tokens: float | None = None,
|
|
138
113
|
) -> StableMarketsHeadroomResponse:
|
|
139
|
-
"""
|
|
140
|
-
Fetch stable markets headroom from Hyperlend.
|
|
141
|
-
|
|
142
|
-
Args:
|
|
143
|
-
required_underlying_tokens: Required underlying tokens amount
|
|
144
|
-
buffer_bps: Buffer in basis points
|
|
145
|
-
min_buffer_tokens: Minimum buffer in tokens
|
|
146
|
-
|
|
147
|
-
Example:
|
|
148
|
-
GET /api/v1/blockchain/hyperlend/stable_markets_headroom/?required_underlying_tokens=1000&buffer_bps=50&min_buffer_tokens=100
|
|
149
|
-
|
|
150
|
-
Returns:
|
|
151
|
-
Dictionary containing stable markets headroom data with markets and notes
|
|
152
|
-
"""
|
|
153
114
|
url = f"{self.api_base_url}/v1/blockchain/hyperlend/stable_markets_headroom/"
|
|
154
115
|
params: dict[str, Any] = {}
|
|
155
116
|
if required_underlying_tokens is not None:
|
|
@@ -169,18 +130,6 @@ class HyperlendClient(WayfinderClient):
|
|
|
169
130
|
*,
|
|
170
131
|
user_address: str,
|
|
171
132
|
) -> AssetsView:
|
|
172
|
-
"""
|
|
173
|
-
Fetch assets view for a user address from Hyperlend.
|
|
174
|
-
|
|
175
|
-
Args:
|
|
176
|
-
user_address: User wallet address to query assets for
|
|
177
|
-
|
|
178
|
-
Example:
|
|
179
|
-
GET /api/v1/blockchain/hyperlend/assets/?user_address=0x0c737cB5934afCb5B01965141F865F795B324080
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
Dictionary containing assets view data with account information and base currency info
|
|
183
|
-
"""
|
|
184
133
|
url = f"{self.api_base_url}/v1/blockchain/hyperlend/assets/"
|
|
185
134
|
params: dict[str, Any] = {
|
|
186
135
|
"user_address": user_address,
|
|
@@ -196,18 +145,6 @@ class HyperlendClient(WayfinderClient):
|
|
|
196
145
|
*,
|
|
197
146
|
token: str,
|
|
198
147
|
) -> MarketEntry:
|
|
199
|
-
"""
|
|
200
|
-
Fetch market entry from Hyperlend.
|
|
201
|
-
|
|
202
|
-
Args:
|
|
203
|
-
token: Token address to query market for
|
|
204
|
-
|
|
205
|
-
Example:
|
|
206
|
-
GET /api/v1/blockchain/hyperlend/market_entry/?token=0x5555555555555555555555555555555555555555
|
|
207
|
-
|
|
208
|
-
Returns:
|
|
209
|
-
Dictionary containing market entry data with symbol and reserve information
|
|
210
|
-
"""
|
|
211
148
|
url = f"{self.api_base_url}/v1/blockchain/hyperlend/market_entry/"
|
|
212
149
|
params: dict[str, Any] = {
|
|
213
150
|
"token": token,
|
|
@@ -225,20 +162,6 @@ class HyperlendClient(WayfinderClient):
|
|
|
225
162
|
lookback_hours: int,
|
|
226
163
|
force_refresh: bool | None = None,
|
|
227
164
|
) -> LendRateHistory:
|
|
228
|
-
"""
|
|
229
|
-
Fetch lend rate history from Hyperlend.
|
|
230
|
-
|
|
231
|
-
Args:
|
|
232
|
-
token: Token address to query rate history for
|
|
233
|
-
lookback_hours: Number of hours to look back for rate history
|
|
234
|
-
force_refresh: Whether to force refresh the data (optional)
|
|
235
|
-
|
|
236
|
-
Example:
|
|
237
|
-
GET /api/v1/blockchain/hyperlend/lend_rate_history/?token=0x5555555555555555555555555555555555555555&lookback_hours=24&force_refresh=false
|
|
238
|
-
|
|
239
|
-
Returns:
|
|
240
|
-
Dictionary containing lend rate history data with history array
|
|
241
|
-
"""
|
|
242
165
|
url = f"{self.api_base_url}/v1/blockchain/hyperlend/lend_rate_history/"
|
|
243
166
|
params: dict[str, Any] = {
|
|
244
167
|
"token": token,
|
|
@@ -11,8 +11,6 @@ from wayfinder_paths.core.adapters.models import Operation
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class StrategyTransaction(TypedDict):
|
|
14
|
-
"""Individual strategy transaction structure"""
|
|
15
|
-
|
|
16
14
|
id: Required[str]
|
|
17
15
|
operation: Required[str]
|
|
18
16
|
timestamp: Required[str]
|
|
@@ -24,8 +22,6 @@ class StrategyTransaction(TypedDict):
|
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
class StrategyTransactionList(TypedDict):
|
|
27
|
-
"""Vault transaction list response structure"""
|
|
28
|
-
|
|
29
25
|
transactions: Required[list[StrategyTransaction]]
|
|
30
26
|
total: Required[int]
|
|
31
27
|
limit: Required[int]
|
|
@@ -33,8 +29,6 @@ class StrategyTransactionList(TypedDict):
|
|
|
33
29
|
|
|
34
30
|
|
|
35
31
|
class NetDeposit(TypedDict):
|
|
36
|
-
"""Net deposit response structure"""
|
|
37
|
-
|
|
38
32
|
net_deposit: Required[str]
|
|
39
33
|
total_deposits: Required[str]
|
|
40
34
|
total_withdrawals: Required[str]
|
|
@@ -42,34 +36,13 @@ class NetDeposit(TypedDict):
|
|
|
42
36
|
|
|
43
37
|
|
|
44
38
|
class TransactionRecord(TypedDict):
|
|
45
|
-
"""Transaction record response structure"""
|
|
46
|
-
|
|
47
39
|
transaction_id: Required[str]
|
|
48
40
|
status: Required[str]
|
|
49
41
|
timestamp: Required[str]
|
|
50
42
|
|
|
51
43
|
|
|
52
44
|
class LedgerClient:
|
|
53
|
-
"""
|
|
54
|
-
Client for strategy transaction history and bookkeeping operations using local JSON files.
|
|
55
|
-
|
|
56
|
-
Supports:
|
|
57
|
-
- GET strategy transactions
|
|
58
|
-
- GET strategy net deposit
|
|
59
|
-
- GET strategy latest transactions
|
|
60
|
-
- POST add deposit
|
|
61
|
-
- POST add withdraw
|
|
62
|
-
- POST add operation
|
|
63
|
-
- POST record snapshot
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
45
|
def __init__(self, ledger_dir: Path | str | None = None) -> None:
|
|
67
|
-
"""
|
|
68
|
-
Initialize the ledger client.
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
ledger_dir: Directory to store ledger JSON files. Defaults to .ledger in project root
|
|
72
|
-
"""
|
|
73
46
|
if ledger_dir is None:
|
|
74
47
|
# Default to .ledger directory in project root
|
|
75
48
|
project_root = Path(__file__).parent.parent.parent.parent
|
|
@@ -89,7 +62,6 @@ class LedgerClient:
|
|
|
89
62
|
self._initialize_files()
|
|
90
63
|
|
|
91
64
|
def _initialize_files(self) -> None:
|
|
92
|
-
"""Create initial JSON files if they don't exist."""
|
|
93
65
|
if not self.transactions_file.exists():
|
|
94
66
|
self.transactions_file.write_text(
|
|
95
67
|
json.dumps({"transactions": []}, indent=2)
|
|
@@ -99,7 +71,6 @@ class LedgerClient:
|
|
|
99
71
|
self.snapshots_file.write_text(json.dumps({"snapshots": []}, indent=2))
|
|
100
72
|
|
|
101
73
|
async def _read_transactions(self) -> dict[str, Any]:
|
|
102
|
-
"""Read transactions from file with lock."""
|
|
103
74
|
async with self._transactions_lock:
|
|
104
75
|
if not self.transactions_file.exists():
|
|
105
76
|
return {"transactions": []}
|
|
@@ -110,12 +81,10 @@ class LedgerClient:
|
|
|
110
81
|
return {"transactions": []}
|
|
111
82
|
|
|
112
83
|
async def _write_transactions(self, data: dict[str, Any]) -> None:
|
|
113
|
-
"""Write transactions to file with lock."""
|
|
114
84
|
async with self._transactions_lock:
|
|
115
85
|
self.transactions_file.write_text(json.dumps(data, indent=2))
|
|
116
86
|
|
|
117
87
|
async def _read_snapshots(self) -> dict[str, Any]:
|
|
118
|
-
"""Read snapshots from file with lock."""
|
|
119
88
|
async with self._snapshots_lock:
|
|
120
89
|
if not self.snapshots_file.exists():
|
|
121
90
|
return {"snapshots": []}
|
|
@@ -126,7 +95,6 @@ class LedgerClient:
|
|
|
126
95
|
return {"snapshots": []}
|
|
127
96
|
|
|
128
97
|
async def _write_snapshots(self, data: dict[str, Any]) -> None:
|
|
129
|
-
"""Write snapshots to file with lock."""
|
|
130
98
|
async with self._snapshots_lock:
|
|
131
99
|
self.snapshots_file.write_text(json.dumps(data, indent=2))
|
|
132
100
|
|
|
@@ -139,17 +107,6 @@ class LedgerClient:
|
|
|
139
107
|
limit: int = 100,
|
|
140
108
|
offset: int = 0,
|
|
141
109
|
) -> StrategyTransactionList:
|
|
142
|
-
"""
|
|
143
|
-
Fetch a paginated list of transactions for a given strategy wallet address.
|
|
144
|
-
|
|
145
|
-
Args:
|
|
146
|
-
wallet_address: The strategy wallet address to filter by
|
|
147
|
-
limit: Maximum number of transactions to return
|
|
148
|
-
offset: Number of transactions to skip
|
|
149
|
-
|
|
150
|
-
Returns:
|
|
151
|
-
StrategyTransactionList with transactions, total, limit, and offset
|
|
152
|
-
"""
|
|
153
110
|
data = await self._read_transactions()
|
|
154
111
|
all_transactions = data.get("transactions", [])
|
|
155
112
|
|
|
@@ -174,15 +131,6 @@ class LedgerClient:
|
|
|
174
131
|
}
|
|
175
132
|
|
|
176
133
|
async def get_strategy_net_deposit(self, *, wallet_address: str) -> float:
|
|
177
|
-
"""
|
|
178
|
-
Calculate the net deposit (deposits - withdrawals) for a strategy wallet.
|
|
179
|
-
|
|
180
|
-
Args:
|
|
181
|
-
wallet_address: The strategy wallet address
|
|
182
|
-
|
|
183
|
-
Returns:
|
|
184
|
-
NetDeposit with net_deposit, total_deposits, total_withdrawals
|
|
185
|
-
"""
|
|
186
134
|
data = await self._read_transactions()
|
|
187
135
|
all_transactions = data.get("transactions", [])
|
|
188
136
|
|
|
@@ -212,16 +160,6 @@ class LedgerClient:
|
|
|
212
160
|
async def get_strategy_latest_transactions(
|
|
213
161
|
self, *, wallet_address: str, limit: int = 10
|
|
214
162
|
) -> StrategyTransactionList:
|
|
215
|
-
"""
|
|
216
|
-
Fetch the most recent transactions for a strategy wallet.
|
|
217
|
-
|
|
218
|
-
Args:
|
|
219
|
-
wallet_address: The strategy wallet address
|
|
220
|
-
limit: Maximum number of transactions to return (default 10)
|
|
221
|
-
|
|
222
|
-
Returns:
|
|
223
|
-
StrategyTransactionList with the latest transactions
|
|
224
|
-
"""
|
|
225
163
|
return await self.get_strategy_transactions(
|
|
226
164
|
wallet_address=wallet_address,
|
|
227
165
|
limit=limit,
|
|
@@ -241,21 +179,6 @@ class LedgerClient:
|
|
|
241
179
|
data: dict[str, Any] | None = None,
|
|
242
180
|
strategy_name: str | None = None,
|
|
243
181
|
) -> TransactionRecord:
|
|
244
|
-
"""
|
|
245
|
-
Record a deposit for a strategy.
|
|
246
|
-
|
|
247
|
-
Args:
|
|
248
|
-
wallet_address: The strategy wallet address
|
|
249
|
-
chain_id: The blockchain chain ID
|
|
250
|
-
token_address: The token contract address
|
|
251
|
-
token_amount: Amount of tokens deposited
|
|
252
|
-
usd_value: USD value of the deposit
|
|
253
|
-
data: Additional metadata
|
|
254
|
-
strategy_name: Name of the strategy
|
|
255
|
-
|
|
256
|
-
Returns:
|
|
257
|
-
TransactionRecord with transaction_id, status, and timestamp
|
|
258
|
-
"""
|
|
259
182
|
transaction_id = str(uuid.uuid4())
|
|
260
183
|
timestamp = datetime.now(UTC).isoformat()
|
|
261
184
|
|
|
@@ -267,7 +190,7 @@ class LedgerClient:
|
|
|
267
190
|
"chain_id": chain_id,
|
|
268
191
|
"token_address": token_address,
|
|
269
192
|
"token_amount": str(token_amount),
|
|
270
|
-
"amount": str(token_amount),
|
|
193
|
+
"amount": str(token_amount),
|
|
271
194
|
"usd_value": str(usd_value),
|
|
272
195
|
"data": data or {},
|
|
273
196
|
}
|
|
@@ -275,7 +198,6 @@ class LedgerClient:
|
|
|
275
198
|
if strategy_name is not None:
|
|
276
199
|
transaction["strategy_name"] = strategy_name
|
|
277
200
|
|
|
278
|
-
# Add to transactions
|
|
279
201
|
file_data = await self._read_transactions()
|
|
280
202
|
file_data["transactions"].append(transaction)
|
|
281
203
|
await self._write_transactions(file_data)
|
|
@@ -297,21 +219,6 @@ class LedgerClient:
|
|
|
297
219
|
data: dict[str, Any] | None = None,
|
|
298
220
|
strategy_name: str | None = None,
|
|
299
221
|
) -> TransactionRecord:
|
|
300
|
-
"""
|
|
301
|
-
Record a withdrawal for a strategy.
|
|
302
|
-
|
|
303
|
-
Args:
|
|
304
|
-
wallet_address: The strategy wallet address
|
|
305
|
-
chain_id: The blockchain chain ID
|
|
306
|
-
token_address: The token contract address
|
|
307
|
-
token_amount: Amount of tokens withdrawn
|
|
308
|
-
usd_value: USD value of the withdrawal
|
|
309
|
-
data: Additional metadata
|
|
310
|
-
strategy_name: Name of the strategy
|
|
311
|
-
|
|
312
|
-
Returns:
|
|
313
|
-
TransactionRecord with transaction_id, status, and timestamp
|
|
314
|
-
"""
|
|
315
222
|
transaction_id = str(uuid.uuid4())
|
|
316
223
|
timestamp = datetime.now(UTC).isoformat()
|
|
317
224
|
|
|
@@ -323,7 +230,7 @@ class LedgerClient:
|
|
|
323
230
|
"chain_id": chain_id,
|
|
324
231
|
"token_address": token_address,
|
|
325
232
|
"token_amount": str(token_amount),
|
|
326
|
-
"amount": str(token_amount),
|
|
233
|
+
"amount": str(token_amount),
|
|
327
234
|
"usd_value": str(usd_value),
|
|
328
235
|
"data": data or {},
|
|
329
236
|
}
|
|
@@ -331,7 +238,6 @@ class LedgerClient:
|
|
|
331
238
|
if strategy_name is not None:
|
|
332
239
|
transaction["strategy_name"] = strategy_name
|
|
333
240
|
|
|
334
|
-
# Add to transactions
|
|
335
241
|
file_data = await self._read_transactions()
|
|
336
242
|
file_data["transactions"].append(transaction)
|
|
337
243
|
await self._write_transactions(file_data)
|
|
@@ -350,18 +256,6 @@ class LedgerClient:
|
|
|
350
256
|
usd_value: str | float,
|
|
351
257
|
strategy_name: str | None = None,
|
|
352
258
|
) -> TransactionRecord:
|
|
353
|
-
"""
|
|
354
|
-
Record a strategy operation (e.g., swaps, rebalances) for bookkeeping.
|
|
355
|
-
|
|
356
|
-
Args:
|
|
357
|
-
wallet_address: Strategy wallet address
|
|
358
|
-
operation_data: Operation model (SWAP, LEND, UNLEND, etc.)
|
|
359
|
-
usd_value: USD value of the operation
|
|
360
|
-
strategy_name: Optional strategy name
|
|
361
|
-
|
|
362
|
-
Returns:
|
|
363
|
-
TransactionRecord with transaction_id, status, and timestamp
|
|
364
|
-
"""
|
|
365
259
|
transaction_id = str(uuid.uuid4())
|
|
366
260
|
timestamp = datetime.now(UTC).isoformat()
|
|
367
261
|
|
|
@@ -378,7 +272,6 @@ class LedgerClient:
|
|
|
378
272
|
"data": {},
|
|
379
273
|
}
|
|
380
274
|
|
|
381
|
-
# Extract relevant fields from operation data for easier querying
|
|
382
275
|
if operation_type == "SWAP":
|
|
383
276
|
transaction["token_address"] = op_dict.get("to_token_id", "")
|
|
384
277
|
transaction["amount"] = op_dict.get("to_amount", "0")
|
|
@@ -389,7 +282,6 @@ class LedgerClient:
|
|
|
389
282
|
if strategy_name is not None:
|
|
390
283
|
transaction["strategy_name"] = strategy_name
|
|
391
284
|
|
|
392
|
-
# Add to transactions
|
|
393
285
|
file_data = await self._read_transactions()
|
|
394
286
|
file_data["transactions"].append(transaction)
|
|
395
287
|
await self._write_transactions(file_data)
|
|
@@ -409,17 +301,6 @@ class LedgerClient:
|
|
|
409
301
|
gas_available: float,
|
|
410
302
|
gassed_up: bool,
|
|
411
303
|
) -> None:
|
|
412
|
-
"""
|
|
413
|
-
Record a periodic snapshot of strategy state.
|
|
414
|
-
|
|
415
|
-
Args:
|
|
416
|
-
wallet_address: Strategy wallet address
|
|
417
|
-
strat_portfolio_value: Total portfolio value in USD
|
|
418
|
-
net_deposit: Net deposit amount in USD
|
|
419
|
-
strategy_status: Arbitrary strategy status data
|
|
420
|
-
gas_available: Available gas tokens
|
|
421
|
-
gassed_up: Whether strategy has sufficient gas
|
|
422
|
-
"""
|
|
423
304
|
snapshot_id = str(uuid.uuid4())
|
|
424
305
|
timestamp = datetime.now(UTC).isoformat()
|
|
425
306
|
|
|
@@ -434,7 +315,6 @@ class LedgerClient:
|
|
|
434
315
|
"strategy_status": strategy_status,
|
|
435
316
|
}
|
|
436
317
|
|
|
437
|
-
# Add to snapshots
|
|
438
318
|
file_data = await self._read_snapshots()
|
|
439
319
|
file_data["snapshots"].append(snapshot)
|
|
440
320
|
await self._write_snapshots(file_data)
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Token Adapter
|
|
3
|
-
Handles token information, prices, and parsing
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
1
|
from __future__ import annotations
|
|
7
2
|
|
|
8
3
|
from typing import NotRequired, Required, TypedDict
|
|
@@ -12,8 +7,6 @@ from wayfinder_paths.core.config import get_api_base_url
|
|
|
12
7
|
|
|
13
8
|
|
|
14
9
|
class TokenLinks(TypedDict):
|
|
15
|
-
"""Token links structure"""
|
|
16
|
-
|
|
17
10
|
github: NotRequired[list[str]]
|
|
18
11
|
reddit: NotRequired[str]
|
|
19
12
|
discord: NotRequired[str]
|
|
@@ -23,8 +16,6 @@ class TokenLinks(TypedDict):
|
|
|
23
16
|
|
|
24
17
|
|
|
25
18
|
class ChainAddress(TypedDict):
|
|
26
|
-
"""Chain address structure"""
|
|
27
|
-
|
|
28
19
|
address: Required[str]
|
|
29
20
|
token_id: Required[str]
|
|
30
21
|
is_contract: NotRequired[bool]
|
|
@@ -32,16 +23,12 @@ class ChainAddress(TypedDict):
|
|
|
32
23
|
|
|
33
24
|
|
|
34
25
|
class ChainInfo(TypedDict):
|
|
35
|
-
"""Chain information structure"""
|
|
36
|
-
|
|
37
26
|
id: Required[int]
|
|
38
27
|
name: Required[str]
|
|
39
28
|
code: Required[str]
|
|
40
29
|
|
|
41
30
|
|
|
42
31
|
class TokenMetadata(TypedDict):
|
|
43
|
-
"""Token metadata structure"""
|
|
44
|
-
|
|
45
32
|
query_processed: NotRequired[str]
|
|
46
33
|
query_type: NotRequired[str]
|
|
47
34
|
has_addresses: NotRequired[bool]
|
|
@@ -50,8 +37,6 @@ class TokenMetadata(TypedDict):
|
|
|
50
37
|
|
|
51
38
|
|
|
52
39
|
class TokenDetails(TypedDict):
|
|
53
|
-
"""Token details response structure"""
|
|
54
|
-
|
|
55
40
|
asset_id: NotRequired[str]
|
|
56
41
|
token_ids: NotRequired[list[str]]
|
|
57
42
|
name: Required[str]
|
|
@@ -81,8 +66,6 @@ class TokenDetails(TypedDict):
|
|
|
81
66
|
|
|
82
67
|
|
|
83
68
|
class GasToken(TypedDict):
|
|
84
|
-
"""Gas token response structure"""
|
|
85
|
-
|
|
86
69
|
id: Required[str]
|
|
87
70
|
coingecko_id: NotRequired[str]
|
|
88
71
|
token_id: Required[str]
|
|
@@ -94,8 +77,6 @@ class GasToken(TypedDict):
|
|
|
94
77
|
|
|
95
78
|
|
|
96
79
|
class TokenClient(WayfinderClient):
|
|
97
|
-
"""Adapter for token-related operations"""
|
|
98
|
-
|
|
99
80
|
def __init__(self):
|
|
100
81
|
super().__init__()
|
|
101
82
|
self.api_base_url = f"{get_api_base_url()}/v1/blockchain/tokens"
|
|
@@ -103,17 +84,6 @@ class TokenClient(WayfinderClient):
|
|
|
103
84
|
async def get_token_details(
|
|
104
85
|
self, query: str, market_data: bool = False, chain_id: int | None = None
|
|
105
86
|
) -> TokenDetails:
|
|
106
|
-
"""
|
|
107
|
-
Get token data including price from the token-details endpoint.
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
query: Token identifier, address, or symbol to query
|
|
111
|
-
market_data: Whether to include market data (default: True)
|
|
112
|
-
chain_id: Optional chain ID
|
|
113
|
-
|
|
114
|
-
Returns:
|
|
115
|
-
Full token data including price information
|
|
116
|
-
"""
|
|
117
87
|
url = f"{self.api_base_url}/detail/"
|
|
118
88
|
params = {
|
|
119
89
|
"query": query,
|
|
@@ -127,15 +97,6 @@ class TokenClient(WayfinderClient):
|
|
|
127
97
|
return data.get("data", data)
|
|
128
98
|
|
|
129
99
|
async def get_gas_token(self, query: str) -> GasToken:
|
|
130
|
-
"""
|
|
131
|
-
Fetch the native gas token for a given chain code or query.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
query: Chain code or query string
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
Gas token information including chain details
|
|
138
|
-
"""
|
|
139
100
|
url = f"{self.api_base_url}/gas/"
|
|
140
101
|
params = {"query": query}
|
|
141
102
|
response = await self._authed_request("GET", url, params=params)
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Wallet Client
|
|
3
|
-
Fetches wallet-related data such as aggregated balances for the authenticated user.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
1
|
from __future__ import annotations
|
|
7
2
|
|
|
8
3
|
from typing import NotRequired, Required, TypedDict
|
|
@@ -12,8 +7,6 @@ from wayfinder_paths.core.config import get_api_base_url
|
|
|
12
7
|
|
|
13
8
|
|
|
14
9
|
class AddressBalance(TypedDict):
|
|
15
|
-
"""Balance response structure for address/query lookups."""
|
|
16
|
-
|
|
17
10
|
balance: Required[int]
|
|
18
11
|
balance_human: NotRequired[float | None]
|
|
19
12
|
usd_value: NotRequired[float | None]
|
|
@@ -35,14 +28,6 @@ class WalletClient(WayfinderClient):
|
|
|
35
28
|
query: str,
|
|
36
29
|
chain_id: int | None = None,
|
|
37
30
|
) -> AddressBalance:
|
|
38
|
-
"""
|
|
39
|
-
Fetch a balance for a wallet address + chain + query.
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
wallet_address: Wallet address
|
|
43
|
-
query: Token address, pool address, or equivalent identifier
|
|
44
|
-
chain_id: Chain ID (required)
|
|
45
|
-
"""
|
|
46
31
|
if chain_id is None:
|
|
47
32
|
raise ValueError("chain_id is required")
|
|
48
33
|
|