wayfinder-paths 0.1.22__py3-none-any.whl → 0.1.23__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 -4
- wayfinder_paths/adapters/balance_adapter/README.md +0 -1
- wayfinder_paths/adapters/balance_adapter/adapter.py +65 -169
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -113
- wayfinder_paths/adapters/brap_adapter/README.md +22 -75
- wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
- wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +39 -86
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +5 -1
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +6 -5
- wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
- wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +108 -198
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +37 -23
- wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
- wayfinder_paths/core/__init__.py +0 -3
- wayfinder_paths/core/clients/BRAPClient.py +3 -0
- wayfinder_paths/core/clients/ClientManager.py +0 -7
- wayfinder_paths/core/clients/LedgerClient.py +196 -172
- wayfinder_paths/core/clients/WayfinderClient.py +0 -1
- wayfinder_paths/core/clients/__init__.py +0 -5
- wayfinder_paths/core/clients/protocols.py +0 -13
- wayfinder_paths/core/config.py +0 -164
- wayfinder_paths/core/constants/__init__.py +58 -2
- wayfinder_paths/core/constants/base.py +8 -22
- wayfinder_paths/core/constants/chains.py +36 -0
- wayfinder_paths/core/constants/contracts.py +39 -0
- wayfinder_paths/core/constants/tokens.py +9 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -10
- wayfinder_paths/core/utils/evm_helpers.py +5 -15
- wayfinder_paths/core/utils/tokens.py +28 -0
- wayfinder_paths/core/utils/transaction.py +13 -7
- wayfinder_paths/core/utils/web3.py +5 -3
- wayfinder_paths/policies/enso.py +1 -2
- wayfinder_paths/policies/hyper_evm.py +6 -3
- wayfinder_paths/policies/hyperlend.py +1 -2
- wayfinder_paths/policies/moonwell.py +12 -7
- wayfinder_paths/policies/prjx.py +1 -3
- wayfinder_paths/run_strategy.py +97 -300
- wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +19 -14
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +12 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +20 -33
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +21 -18
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -130
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +32 -42
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/METADATA +2 -3
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/RECORD +51 -60
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/WHEEL +1 -1
- wayfinder_paths/core/clients/WalletClient.py +0 -41
- wayfinder_paths/core/engine/StrategyJob.py +0 -110
- wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
- wayfinder_paths/templates/adapter/README.md +0 -150
- wayfinder_paths/templates/adapter/adapter.py +0 -16
- wayfinder_paths/templates/adapter/examples.json +0 -8
- wayfinder_paths/templates/adapter/test_adapter.py +0 -30
- wayfinder_paths/templates/strategy/README.md +0 -186
- wayfinder_paths/templates/strategy/examples.json +0 -11
- wayfinder_paths/templates/strategy/strategy.py +0 -35
- wayfinder_paths/templates/strategy/test_strategy.py +0 -166
- wayfinder_paths/tests/test_smoke_manifest.py +0 -63
- {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.23.dist-info}/LICENSE +0 -0
|
@@ -5,9 +5,7 @@ import json
|
|
|
5
5
|
import uuid
|
|
6
6
|
from datetime import UTC, datetime
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, NotRequired, Required, TypedDict
|
|
9
|
-
|
|
10
|
-
from wayfinder_paths.core.adapters.models import Operation
|
|
8
|
+
from typing import Any, Literal, NotRequired, Required, TypedDict
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
class StrategyTransaction(TypedDict):
|
|
@@ -19,6 +17,8 @@ class StrategyTransaction(TypedDict):
|
|
|
19
17
|
usd_value: Required[str]
|
|
20
18
|
strategy_name: NotRequired[str | None]
|
|
21
19
|
chain_id: NotRequired[int | None]
|
|
20
|
+
created: NotRequired[str]
|
|
21
|
+
op_data: NotRequired[dict[str, Any]]
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class StrategyTransactionList(TypedDict):
|
|
@@ -28,148 +28,197 @@ class StrategyTransactionList(TypedDict):
|
|
|
28
28
|
offset: Required[int]
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
class NetDeposit(TypedDict):
|
|
32
|
-
net_deposit: Required[str]
|
|
33
|
-
total_deposits: Required[str]
|
|
34
|
-
total_withdrawals: Required[str]
|
|
35
|
-
wallet_address: NotRequired[str | None]
|
|
36
|
-
|
|
37
|
-
|
|
38
31
|
class TransactionRecord(TypedDict):
|
|
39
32
|
transaction_id: Required[str]
|
|
40
33
|
status: Required[str]
|
|
41
34
|
timestamp: Required[str]
|
|
42
35
|
|
|
43
36
|
|
|
37
|
+
class OperationData(TypedDict, total=False):
|
|
38
|
+
type: str
|
|
39
|
+
transaction_hash: str
|
|
40
|
+
transaction_chain_id: int
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class StrategyOperationTransactionData(TypedDict):
|
|
44
|
+
op_data: OperationData
|
|
45
|
+
|
|
46
|
+
|
|
44
47
|
class LedgerClient:
|
|
45
48
|
def __init__(self, ledger_dir: Path | str | None = None) -> None:
|
|
46
49
|
if ledger_dir is None:
|
|
47
|
-
|
|
48
|
-
project_root = Path(__file__).parent.parent.parent.parent
|
|
49
|
-
ledger_dir = project_root / ".ledger"
|
|
50
|
-
|
|
50
|
+
ledger_dir = Path(__file__).resolve().parents[3] / ".ledger"
|
|
51
51
|
self.ledger_dir = Path(ledger_dir)
|
|
52
52
|
self.ledger_dir.mkdir(parents=True, exist_ok=True)
|
|
53
|
-
|
|
54
53
|
self.transactions_file = self.ledger_dir / "transactions.json"
|
|
55
54
|
self.snapshots_file = self.ledger_dir / "snapshots.json"
|
|
56
|
-
|
|
57
|
-
# File locks for thread-safe operations
|
|
58
55
|
self._transactions_lock = asyncio.Lock()
|
|
59
56
|
self._snapshots_lock = asyncio.Lock()
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
for path, default in [
|
|
58
|
+
(self.transactions_file, {"transactions": []}),
|
|
59
|
+
(self.snapshots_file, {"snapshots": []}),
|
|
60
|
+
]:
|
|
61
|
+
if not path.exists():
|
|
62
|
+
path.write_text(json.dumps(default, indent=2))
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def _load_json_file(path: Path, default: dict[str, Any]) -> dict[str, Any]:
|
|
66
|
+
if not path.exists():
|
|
67
|
+
return default
|
|
68
|
+
try:
|
|
69
|
+
return json.loads(path.read_text())
|
|
70
|
+
except json.JSONDecodeError:
|
|
71
|
+
return default
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _save_json_file(path: Path, data: dict[str, Any]) -> None:
|
|
75
|
+
path.write_text(json.dumps(data, indent=2))
|
|
76
|
+
|
|
77
|
+
async def _read_json(
|
|
78
|
+
self, path: Path, lock: asyncio.Lock, default: dict[str, Any]
|
|
79
|
+
) -> dict[str, Any]:
|
|
80
|
+
async with lock:
|
|
81
|
+
return self._load_json_file(path, default)
|
|
82
|
+
|
|
83
|
+
async def _write_json(
|
|
84
|
+
self, path: Path, lock: asyncio.Lock, data: dict[str, Any]
|
|
85
|
+
) -> None:
|
|
86
|
+
async with lock:
|
|
87
|
+
self._save_json_file(path, data)
|
|
72
88
|
|
|
73
89
|
async def _read_transactions(self) -> dict[str, Any]:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
try:
|
|
78
|
-
content = self.transactions_file.read_text()
|
|
79
|
-
return json.loads(content)
|
|
80
|
-
except json.JSONDecodeError:
|
|
81
|
-
return {"transactions": []}
|
|
90
|
+
return await self._read_json(
|
|
91
|
+
self.transactions_file, self._transactions_lock, {"transactions": []}
|
|
92
|
+
)
|
|
82
93
|
|
|
83
94
|
async def _write_transactions(self, data: dict[str, Any]) -> None:
|
|
84
|
-
|
|
85
|
-
self.transactions_file.write_text(json.dumps(data, indent=2))
|
|
95
|
+
await self._write_json(self.transactions_file, self._transactions_lock, data)
|
|
86
96
|
|
|
87
97
|
async def _read_snapshots(self) -> dict[str, Any]:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
try:
|
|
92
|
-
content = self.snapshots_file.read_text()
|
|
93
|
-
return json.loads(content)
|
|
94
|
-
except json.JSONDecodeError:
|
|
95
|
-
return {"snapshots": []}
|
|
98
|
+
return await self._read_json(
|
|
99
|
+
self.snapshots_file, self._snapshots_lock, {"snapshots": []}
|
|
100
|
+
)
|
|
96
101
|
|
|
97
102
|
async def _write_snapshots(self, data: dict[str, Any]) -> None:
|
|
98
|
-
|
|
99
|
-
self.snapshots_file.write_text(json.dumps(data, indent=2))
|
|
100
|
-
|
|
101
|
-
# ===================== Read Endpoints =====================
|
|
103
|
+
await self._write_json(self.snapshots_file, self._snapshots_lock, data)
|
|
102
104
|
|
|
103
|
-
async def
|
|
105
|
+
async def _transactions_for_wallet(
|
|
104
106
|
self,
|
|
105
|
-
*,
|
|
106
107
|
wallet_address: str,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
) ->
|
|
108
|
+
*,
|
|
109
|
+
operation: str | None = None,
|
|
110
|
+
) -> list[dict[str, Any]]:
|
|
110
111
|
data = await self._read_transactions()
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# Filter by wallet_address
|
|
114
|
-
filtered = [
|
|
112
|
+
txs = [
|
|
115
113
|
tx
|
|
116
|
-
for tx in
|
|
114
|
+
for tx in data.get("transactions", [])
|
|
117
115
|
if tx.get("wallet_address", "").lower() == wallet_address.lower()
|
|
118
116
|
]
|
|
117
|
+
if operation is not None:
|
|
118
|
+
txs = [tx for tx in txs if tx.get("operation") == operation]
|
|
119
|
+
txs.sort(key=lambda x: x.get("timestamp", ""), reverse=True)
|
|
120
|
+
return txs
|
|
121
|
+
|
|
122
|
+
def _to_strategy_transaction(self, tx: dict[str, Any]) -> StrategyTransaction:
|
|
123
|
+
operation = tx["operation"]
|
|
124
|
+
op_data: dict[str, Any] = {}
|
|
125
|
+
if operation == "STRAT_OP":
|
|
126
|
+
op_data = (tx.get("data") or {}).get("op_data") or {}
|
|
127
|
+
if op_data:
|
|
128
|
+
operation = op_data.get("type") or operation
|
|
129
|
+
|
|
130
|
+
amount = tx["amount"]
|
|
131
|
+
token_address = tx["token_address"]
|
|
132
|
+
if op_data:
|
|
133
|
+
op_type = op_data.get("type", "")
|
|
134
|
+
if op_type == "SWAP":
|
|
135
|
+
token_address = op_data.get("to_token_id", "")
|
|
136
|
+
amount = op_data.get("to_amount", "0")
|
|
137
|
+
elif op_type in ("LEND", "UNLEND"):
|
|
138
|
+
token_address = op_data.get("contract", "")
|
|
139
|
+
amount = str(op_data.get("amount", 0))
|
|
140
|
+
else:
|
|
141
|
+
amount = amount or "0"
|
|
142
|
+
token_address = token_address or ""
|
|
143
|
+
|
|
144
|
+
out: StrategyTransaction = {
|
|
145
|
+
"id": tx["id"],
|
|
146
|
+
"operation": str(operation),
|
|
147
|
+
"timestamp": tx["timestamp"],
|
|
148
|
+
"created": tx["timestamp"],
|
|
149
|
+
"amount": amount,
|
|
150
|
+
"token_address": token_address,
|
|
151
|
+
"usd_value": tx["usd_value"],
|
|
152
|
+
}
|
|
153
|
+
if "chain_id" in tx:
|
|
154
|
+
out["chain_id"] = tx["chain_id"]
|
|
155
|
+
if "strategy_name" in tx:
|
|
156
|
+
out["strategy_name"] = tx["strategy_name"]
|
|
157
|
+
if op_data:
|
|
158
|
+
out["op_data"] = op_data
|
|
159
|
+
return out
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def _record(transaction_id: str, timestamp: str) -> TransactionRecord:
|
|
163
|
+
return {
|
|
164
|
+
"transaction_id": transaction_id,
|
|
165
|
+
"status": "success",
|
|
166
|
+
"timestamp": timestamp,
|
|
167
|
+
}
|
|
119
168
|
|
|
120
|
-
|
|
121
|
-
|
|
169
|
+
async def _append_transaction(self, transaction: dict[str, Any]) -> None:
|
|
170
|
+
async with self._transactions_lock:
|
|
171
|
+
data = self._load_json_file(self.transactions_file, {"transactions": []})
|
|
172
|
+
data.setdefault("transactions", []).append(transaction)
|
|
173
|
+
self._save_json_file(self.transactions_file, data)
|
|
122
174
|
|
|
175
|
+
async def get_strategy_transactions(
|
|
176
|
+
self,
|
|
177
|
+
*,
|
|
178
|
+
wallet_address: str,
|
|
179
|
+
limit: int = 50,
|
|
180
|
+
offset: int = 0,
|
|
181
|
+
operation: str | None = None,
|
|
182
|
+
) -> StrategyTransactionList:
|
|
183
|
+
filtered = await self._transactions_for_wallet(
|
|
184
|
+
wallet_address, operation=operation
|
|
185
|
+
)
|
|
123
186
|
total = len(filtered)
|
|
124
187
|
paginated = filtered[offset : offset + limit]
|
|
125
|
-
|
|
188
|
+
transactions = [self._to_strategy_transaction(tx) for tx in paginated]
|
|
126
189
|
return {
|
|
127
|
-
"transactions":
|
|
190
|
+
"transactions": transactions,
|
|
128
191
|
"total": total,
|
|
129
192
|
"limit": limit,
|
|
130
193
|
"offset": offset,
|
|
131
194
|
}
|
|
132
195
|
|
|
133
196
|
async def get_strategy_net_deposit(self, *, wallet_address: str) -> float:
|
|
134
|
-
|
|
135
|
-
all_transactions = data.get("transactions", [])
|
|
136
|
-
|
|
137
|
-
# Filter by wallet_address
|
|
138
|
-
filtered = [
|
|
139
|
-
tx
|
|
140
|
-
for tx in all_transactions
|
|
141
|
-
if tx.get("wallet_address", "").lower() == wallet_address.lower()
|
|
142
|
-
]
|
|
143
|
-
|
|
197
|
+
filtered = await self._transactions_for_wallet(wallet_address)
|
|
144
198
|
total_deposits = 0.0
|
|
145
199
|
total_withdrawals = 0.0
|
|
146
|
-
|
|
147
200
|
for tx in filtered:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
net_deposit = total_deposits - total_withdrawals
|
|
157
|
-
|
|
158
|
-
return float(net_deposit)
|
|
201
|
+
op = tx.get("operation", "").upper()
|
|
202
|
+
usd = float(tx.get("usd_value", 0))
|
|
203
|
+
if op == "DEPOSIT":
|
|
204
|
+
total_deposits += usd
|
|
205
|
+
elif op == "WITHDRAW":
|
|
206
|
+
total_withdrawals += usd
|
|
207
|
+
return total_deposits - total_withdrawals
|
|
159
208
|
|
|
160
209
|
async def get_strategy_latest_transactions(
|
|
161
|
-
self, *, wallet_address: str
|
|
210
|
+
self, *, wallet_address: str
|
|
162
211
|
) -> StrategyTransactionList:
|
|
163
212
|
return await self.get_strategy_transactions(
|
|
164
213
|
wallet_address=wallet_address,
|
|
165
|
-
limit=
|
|
214
|
+
limit=80,
|
|
166
215
|
offset=0,
|
|
216
|
+
operation="STRAT_OP",
|
|
167
217
|
)
|
|
168
218
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
async def add_strategy_deposit(
|
|
219
|
+
async def _add_deposit_or_withdraw(
|
|
172
220
|
self,
|
|
221
|
+
operation: Literal["DEPOSIT", "WITHDRAW"],
|
|
173
222
|
*,
|
|
174
223
|
wallet_address: str,
|
|
175
224
|
chain_id: int,
|
|
@@ -181,32 +230,44 @@ class LedgerClient:
|
|
|
181
230
|
) -> TransactionRecord:
|
|
182
231
|
transaction_id = str(uuid.uuid4())
|
|
183
232
|
timestamp = datetime.now(UTC).isoformat()
|
|
184
|
-
|
|
185
|
-
transaction = {
|
|
233
|
+
transaction: dict[str, Any] = {
|
|
186
234
|
"id": transaction_id,
|
|
187
235
|
"wallet_address": wallet_address,
|
|
188
|
-
"operation":
|
|
236
|
+
"operation": operation,
|
|
189
237
|
"timestamp": timestamp,
|
|
190
|
-
"chain_id": chain_id,
|
|
191
|
-
"token_address": token_address,
|
|
192
|
-
"token_amount": str(token_amount),
|
|
193
238
|
"amount": str(token_amount),
|
|
239
|
+
"token_address": token_address,
|
|
194
240
|
"usd_value": str(usd_value),
|
|
195
|
-
"
|
|
241
|
+
"chain_id": chain_id,
|
|
196
242
|
}
|
|
197
|
-
|
|
243
|
+
if data is not None:
|
|
244
|
+
transaction["data"] = data
|
|
198
245
|
if strategy_name is not None:
|
|
199
246
|
transaction["strategy_name"] = strategy_name
|
|
247
|
+
await self._append_transaction(transaction)
|
|
248
|
+
return self._record(transaction_id, timestamp)
|
|
200
249
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
250
|
+
async def add_strategy_deposit(
|
|
251
|
+
self,
|
|
252
|
+
*,
|
|
253
|
+
wallet_address: str,
|
|
254
|
+
chain_id: int,
|
|
255
|
+
token_address: str,
|
|
256
|
+
token_amount: str | float,
|
|
257
|
+
usd_value: str | float,
|
|
258
|
+
data: dict[str, Any] | None = None,
|
|
259
|
+
strategy_name: str | None = None,
|
|
260
|
+
) -> TransactionRecord:
|
|
261
|
+
return await self._add_deposit_or_withdraw(
|
|
262
|
+
"DEPOSIT",
|
|
263
|
+
wallet_address=wallet_address,
|
|
264
|
+
chain_id=chain_id,
|
|
265
|
+
token_address=token_address,
|
|
266
|
+
token_amount=token_amount,
|
|
267
|
+
usd_value=usd_value,
|
|
268
|
+
data=data,
|
|
269
|
+
strategy_name=strategy_name,
|
|
270
|
+
)
|
|
210
271
|
|
|
211
272
|
async def add_strategy_withdraw(
|
|
212
273
|
self,
|
|
@@ -219,78 +280,47 @@ class LedgerClient:
|
|
|
219
280
|
data: dict[str, Any] | None = None,
|
|
220
281
|
strategy_name: str | None = None,
|
|
221
282
|
) -> TransactionRecord:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
"token_amount": str(token_amount),
|
|
233
|
-
"amount": str(token_amount),
|
|
234
|
-
"usd_value": str(usd_value),
|
|
235
|
-
"data": data or {},
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if strategy_name is not None:
|
|
239
|
-
transaction["strategy_name"] = strategy_name
|
|
240
|
-
|
|
241
|
-
file_data = await self._read_transactions()
|
|
242
|
-
file_data["transactions"].append(transaction)
|
|
243
|
-
await self._write_transactions(file_data)
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
"transaction_id": transaction_id,
|
|
247
|
-
"status": "success",
|
|
248
|
-
"timestamp": timestamp,
|
|
249
|
-
}
|
|
283
|
+
return await self._add_deposit_or_withdraw(
|
|
284
|
+
"WITHDRAW",
|
|
285
|
+
wallet_address=wallet_address,
|
|
286
|
+
chain_id=chain_id,
|
|
287
|
+
token_address=token_address,
|
|
288
|
+
token_amount=token_amount,
|
|
289
|
+
usd_value=usd_value,
|
|
290
|
+
data=data,
|
|
291
|
+
strategy_name=strategy_name,
|
|
292
|
+
)
|
|
250
293
|
|
|
251
294
|
async def add_strategy_operation(
|
|
252
295
|
self,
|
|
253
296
|
*,
|
|
254
297
|
wallet_address: str,
|
|
255
|
-
operation_data:
|
|
298
|
+
operation_data: dict[str, Any],
|
|
256
299
|
usd_value: str | float,
|
|
257
300
|
strategy_name: str | None = None,
|
|
258
301
|
) -> TransactionRecord:
|
|
259
302
|
transaction_id = str(uuid.uuid4())
|
|
260
303
|
timestamp = datetime.now(UTC).isoformat()
|
|
261
|
-
|
|
262
|
-
op_dict = operation_data.model_dump(mode="json")
|
|
263
|
-
operation_type = op_dict.get("type", "OPERATION")
|
|
264
|
-
|
|
265
|
-
transaction = {
|
|
304
|
+
transaction: dict[str, Any] = {
|
|
266
305
|
"id": transaction_id,
|
|
267
306
|
"wallet_address": wallet_address,
|
|
268
|
-
"operation":
|
|
307
|
+
"operation": "STRAT_OP",
|
|
269
308
|
"timestamp": timestamp,
|
|
309
|
+
"amount": "0",
|
|
310
|
+
"token_address": "",
|
|
270
311
|
"usd_value": str(usd_value),
|
|
271
|
-
"op_data":
|
|
272
|
-
"data": {},
|
|
312
|
+
"data": {"op_data": operation_data},
|
|
273
313
|
}
|
|
274
|
-
|
|
275
|
-
if operation_type == "SWAP":
|
|
276
|
-
transaction["token_address"] = op_dict.get("to_token_id", "")
|
|
277
|
-
transaction["amount"] = op_dict.get("to_amount", "0")
|
|
278
|
-
elif operation_type in ("LEND", "UNLEND"):
|
|
279
|
-
transaction["token_address"] = op_dict.get("contract", "")
|
|
280
|
-
transaction["amount"] = str(op_dict.get("amount", 0))
|
|
281
|
-
|
|
282
314
|
if strategy_name is not None:
|
|
283
315
|
transaction["strategy_name"] = strategy_name
|
|
316
|
+
await self._append_transaction(transaction)
|
|
317
|
+
return self._record(transaction_id, timestamp)
|
|
284
318
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
"transaction_id": transaction_id,
|
|
291
|
-
"status": "success",
|
|
292
|
-
"timestamp": timestamp,
|
|
293
|
-
}
|
|
319
|
+
async def _append_snapshot(self, snapshot: dict[str, Any]) -> None:
|
|
320
|
+
async with self._snapshots_lock:
|
|
321
|
+
data = self._load_json_file(self.snapshots_file, {"snapshots": []})
|
|
322
|
+
data.setdefault("snapshots", []).append(snapshot)
|
|
323
|
+
self._save_json_file(self.snapshots_file, data)
|
|
294
324
|
|
|
295
325
|
async def strategy_snapshot(
|
|
296
326
|
self,
|
|
@@ -301,20 +331,14 @@ class LedgerClient:
|
|
|
301
331
|
gas_available: float,
|
|
302
332
|
gassed_up: bool,
|
|
303
333
|
) -> None:
|
|
304
|
-
snapshot_id = str(uuid.uuid4())
|
|
305
|
-
timestamp = datetime.now(UTC).isoformat()
|
|
306
|
-
|
|
307
334
|
snapshot = {
|
|
308
|
-
"id":
|
|
335
|
+
"id": str(uuid.uuid4()),
|
|
309
336
|
"wallet_address": wallet_address,
|
|
310
|
-
"timestamp":
|
|
337
|
+
"timestamp": datetime.now(UTC).isoformat(),
|
|
311
338
|
"portfolio_value": strat_portfolio_value,
|
|
312
339
|
"net_deposit": net_deposit,
|
|
313
340
|
"gas_available": gas_available,
|
|
314
341
|
"gassed_up": gassed_up,
|
|
315
342
|
"strategy_status": strategy_status,
|
|
316
343
|
}
|
|
317
|
-
|
|
318
|
-
file_data = await self._read_snapshots()
|
|
319
|
-
file_data["snapshots"].append(snapshot)
|
|
320
|
-
await self._write_snapshots(file_data)
|
|
344
|
+
await self._append_snapshot(snapshot)
|
|
@@ -9,26 +9,21 @@ from wayfinder_paths.core.clients.protocols import (
|
|
|
9
9
|
LedgerClientProtocol,
|
|
10
10
|
PoolClientProtocol,
|
|
11
11
|
TokenClientProtocol,
|
|
12
|
-
WalletClientProtocol,
|
|
13
12
|
)
|
|
14
13
|
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
15
|
-
from wayfinder_paths.core.clients.WalletClient import WalletClient
|
|
16
14
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
17
15
|
|
|
18
16
|
__all__ = [
|
|
19
17
|
"WayfinderClient",
|
|
20
18
|
"ClientManager",
|
|
21
19
|
"TokenClient",
|
|
22
|
-
"WalletClient",
|
|
23
20
|
"LedgerClient",
|
|
24
21
|
"PoolClient",
|
|
25
22
|
"BRAPClient",
|
|
26
23
|
"HyperlendClient",
|
|
27
|
-
# Protocols for SDK usage
|
|
28
24
|
"TokenClientProtocol",
|
|
29
25
|
"HyperlendClientProtocol",
|
|
30
26
|
"LedgerClientProtocol",
|
|
31
|
-
"WalletClientProtocol",
|
|
32
27
|
"PoolClientProtocol",
|
|
33
28
|
"BRAPClientProtocol",
|
|
34
29
|
]
|
|
@@ -22,9 +22,6 @@ if TYPE_CHECKING:
|
|
|
22
22
|
GasToken,
|
|
23
23
|
TokenDetails,
|
|
24
24
|
)
|
|
25
|
-
from wayfinder_paths.core.clients.WalletClient import (
|
|
26
|
-
AddressBalance,
|
|
27
|
-
)
|
|
28
25
|
|
|
29
26
|
|
|
30
27
|
class TokenClientProtocol(Protocol):
|
|
@@ -117,16 +114,6 @@ class LedgerClientProtocol(Protocol):
|
|
|
117
114
|
) -> TransactionRecord: ...
|
|
118
115
|
|
|
119
116
|
|
|
120
|
-
class WalletClientProtocol(Protocol):
|
|
121
|
-
async def get_token_balance_for_address(
|
|
122
|
-
self,
|
|
123
|
-
*,
|
|
124
|
-
wallet_address: str,
|
|
125
|
-
query: str,
|
|
126
|
-
chain_id: int | None = None,
|
|
127
|
-
) -> AddressBalance: ...
|
|
128
|
-
|
|
129
|
-
|
|
130
117
|
class PoolClientProtocol(Protocol):
|
|
131
118
|
async def get_pools_by_ids(
|
|
132
119
|
self,
|