eth-portfolio-temp 0.0.35.dev0__cp310-cp310-win32.whl → 0.2.17__cp310-cp310-win32.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.
- eth_portfolio/_argspec.cp310-win32.pyd +0 -0
- eth_portfolio/_cache.py +2 -2
- eth_portfolio/_config.cp310-win32.pyd +0 -0
- eth_portfolio/_db/utils.py +7 -9
- eth_portfolio/_decimal.py +11 -10
- eth_portfolio/_ledgers/address.py +1 -1
- eth_portfolio/_loaders/_nonce.cp310-win32.pyd +0 -0
- eth_portfolio/_loaders/_nonce.py +4 -4
- eth_portfolio/_loaders/balances.cp310-win32.pyd +0 -0
- eth_portfolio/_loaders/token_transfer.py +1 -1
- eth_portfolio/_loaders/transaction.py +1 -1
- eth_portfolio/_loaders/utils.cp310-win32.pyd +0 -0
- eth_portfolio/_loaders/utils.py +1 -1
- eth_portfolio/_shitcoins.cp310-win32.pyd +0 -0
- eth_portfolio/_shitcoins.py +54 -0
- eth_portfolio/_stableish.cp310-win32.pyd +0 -0
- eth_portfolio/_utils.py +12 -10
- eth_portfolio/_ydb/token_transfers.py +32 -23
- eth_portfolio/address.py +2 -1
- eth_portfolio/buckets.py +3 -3
- eth_portfolio/constants.cp310-win32.pyd +0 -0
- eth_portfolio/portfolio.py +1 -1
- eth_portfolio/protocols/lending/liquity.py +1 -1
- eth_portfolio/protocols/lending/maker.py +13 -14
- eth_portfolio/structs/structs.py +2 -2
- eth_portfolio/typing/__init__.py +6 -6
- eth_portfolio__mypyc.cp310-win32.pyd +0 -0
- eth_portfolio_scripts/_portfolio.py +54 -41
- eth_portfolio_scripts/_utils.py +20 -6
- eth_portfolio_scripts/balances.cp310-win32.pyd +0 -0
- eth_portfolio_scripts/balances.py +7 -4
- eth_portfolio_scripts/docker/__init__.cp310-win32.pyd +0 -0
- eth_portfolio_scripts/docker/check.cp310-win32.pyd +0 -0
- eth_portfolio_scripts/docker/check.py +28 -17
- eth_portfolio_scripts/docker/docker-compose.yaml +2 -2
- eth_portfolio_scripts/docker/docker_compose.cp310-win32.pyd +0 -0
- eth_portfolio_scripts/docker/docker_compose.py +38 -18
- eth_portfolio_scripts/main.py +6 -0
- eth_portfolio_scripts/victoria/__init__.py +3 -0
- {eth_portfolio_temp-0.0.35.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/METADATA +8 -7
- eth_portfolio_temp-0.2.17.dist-info/RECORD +83 -0
- {eth_portfolio_temp-0.0.35.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/top_level.txt +1 -1
- 295eace8438df6ec133b__mypyc.cp310-win32.pyd +0 -0
- eth_portfolio_temp-0.0.35.dev0.dist-info/RECORD +0 -83
- {eth_portfolio_temp-0.0.35.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/WHEEL +0 -0
- {eth_portfolio_temp-0.0.35.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/entry_points.txt +0 -0
eth_portfolio/structs/structs.py
CHANGED
|
@@ -80,7 +80,7 @@ class _LedgerEntryBase(DictStruct, kw_only=True, frozen=True, omit_defaults=True
|
|
|
80
80
|
The USD value of the cryptocurrency transferred in the {cls_name}, if price is known.
|
|
81
81
|
"""
|
|
82
82
|
|
|
83
|
-
def __init_subclass__(cls, **kwargs):
|
|
83
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
84
84
|
super().__init_subclass__(**kwargs)
|
|
85
85
|
|
|
86
86
|
# Replace {cls_name} in attribute-level docstrings
|
|
@@ -273,7 +273,7 @@ class _TransactionBase(
|
|
|
273
273
|
return self.transaction.yParity
|
|
274
274
|
|
|
275
275
|
@property
|
|
276
|
-
def __db_primary_key__(self):
|
|
276
|
+
def __db_primary_key__(self) -> Dict[str, tuple[int, Address] | int]:
|
|
277
277
|
return {"from_address": (chain.id, self.from_address), "nonce": self.nonce}
|
|
278
278
|
|
|
279
279
|
|
eth_portfolio/typing/__init__.py
CHANGED
|
@@ -43,10 +43,10 @@ from typing import (
|
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
from checksum_dict import DefaultChecksumDict
|
|
46
|
-
from eth_typing import BlockNumber
|
|
46
|
+
from eth_typing import BlockNumber, HexAddress
|
|
47
47
|
from pandas import DataFrame, concat
|
|
48
48
|
from typing_extensions import ParamSpec, Self
|
|
49
|
-
from y import ERC20
|
|
49
|
+
from y import Contract, ERC20
|
|
50
50
|
from y.datatypes import Address
|
|
51
51
|
|
|
52
52
|
from eth_portfolio._decimal import Decimal
|
|
@@ -159,10 +159,10 @@ class TokenBalances(DefaultChecksumDict[Balance], _SummableNonNumericMixin): #
|
|
|
159
159
|
raise
|
|
160
160
|
self[token.address] += balance
|
|
161
161
|
|
|
162
|
-
def __getitem__(self, key) -> Balance:
|
|
162
|
+
def __getitem__(self, key: HexAddress) -> Balance:
|
|
163
163
|
return super().__getitem__(key) if key in self else Balance(token=key, block=self.block)
|
|
164
164
|
|
|
165
|
-
def __setitem__(self, key, value):
|
|
165
|
+
def __setitem__(self, key: HexAddress, value: Balance) -> None:
|
|
166
166
|
"""
|
|
167
167
|
Sets the balance for a given token address.
|
|
168
168
|
|
|
@@ -393,7 +393,7 @@ class RemoteTokenBalances(DefaultDict[ProtocolLabel, TokenBalances], _SummableNo
|
|
|
393
393
|
)
|
|
394
394
|
self[remote] += token_balances # type: ignore [has-type]
|
|
395
395
|
|
|
396
|
-
def __setitem__(self, protocol: str, value: TokenBalances):
|
|
396
|
+
def __setitem__(self, protocol: str, value: TokenBalances) -> None:
|
|
397
397
|
"""
|
|
398
398
|
Sets the token balances for a given protocol.
|
|
399
399
|
|
|
@@ -911,7 +911,7 @@ class PortfolioBalances(DefaultChecksumDict[WalletBalances], _SummableNonNumeric
|
|
|
911
911
|
)
|
|
912
912
|
self[wallet] += balances
|
|
913
913
|
|
|
914
|
-
def __setitem__(self, key, value):
|
|
914
|
+
def __setitem__(self, key: HexAddress, value: WalletBalances) -> None:
|
|
915
915
|
if not isinstance(value, WalletBalances):
|
|
916
916
|
raise TypeError(
|
|
917
917
|
f"value must be a `WalletBalances` object. You passed {value}"
|
|
Binary file
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from datetime import datetime, timezone
|
|
2
3
|
from logging import getLogger
|
|
3
4
|
from math import floor
|
|
@@ -5,10 +6,11 @@ from typing import Awaitable, Callable, Final, Iterator, List, Optional, Tuple
|
|
|
5
6
|
|
|
6
7
|
import a_sync
|
|
7
8
|
import eth_retry
|
|
9
|
+
import y
|
|
8
10
|
from a_sync.functools import cached_property_unsafe as cached_property
|
|
9
11
|
from eth_typing import BlockNumber, ChecksumAddress
|
|
10
12
|
from msgspec import ValidationError, json
|
|
11
|
-
from y import ERC20, Network, NonStandardERC20
|
|
13
|
+
from y import ERC20, Network, NonStandardERC20
|
|
12
14
|
from y.constants import CHAINID
|
|
13
15
|
from y.time import NoBlockFound
|
|
14
16
|
|
|
@@ -33,6 +35,19 @@ logger: Final = getLogger("eth_portfolio")
|
|
|
33
35
|
log_debug: Final = logger.debug
|
|
34
36
|
log_error: Final = logger.error
|
|
35
37
|
|
|
38
|
+
_block_at_timestamp_semaphore: Final = a_sync.Semaphore(
|
|
39
|
+
50, name="eth-portfolio get_block_at_timestamp"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def get_block_at_timestamp(dt: datetime) -> BlockNumber:
|
|
44
|
+
async with _block_at_timestamp_semaphore:
|
|
45
|
+
while True:
|
|
46
|
+
try:
|
|
47
|
+
return await y.get_block_at_timestamp(dt, sync=False)
|
|
48
|
+
except NoBlockFound:
|
|
49
|
+
await asyncio.sleep(10)
|
|
50
|
+
|
|
36
51
|
|
|
37
52
|
class ExportablePortfolio(Portfolio):
|
|
38
53
|
"""Adds methods to export full portoflio data."""
|
|
@@ -40,8 +55,10 @@ class ExportablePortfolio(Portfolio):
|
|
|
40
55
|
def __init__(
|
|
41
56
|
self,
|
|
42
57
|
addresses: Addresses,
|
|
58
|
+
*,
|
|
43
59
|
start_block: int = 0,
|
|
44
60
|
label: str = _DEFAULT_LABEL,
|
|
61
|
+
concurrency: int = 40,
|
|
45
62
|
load_prices: bool = True,
|
|
46
63
|
get_bucket: Callable[[ChecksumAddress], Awaitable[str]] = get_token_bucket,
|
|
47
64
|
num_workers_transactions: int = 1000,
|
|
@@ -51,6 +68,7 @@ class ExportablePortfolio(Portfolio):
|
|
|
51
68
|
addresses, start_block, label, load_prices, num_workers_transactions, asynchronous
|
|
52
69
|
)
|
|
53
70
|
self.get_bucket = get_bucket
|
|
71
|
+
self._semaphore = a_sync.Semaphore(concurrency)
|
|
54
72
|
|
|
55
73
|
@cached_property
|
|
56
74
|
def _data_queries(self) -> Tuple[str, str]:
|
|
@@ -71,53 +89,48 @@ class ExportablePortfolio(Portfolio):
|
|
|
71
89
|
return True
|
|
72
90
|
return False
|
|
73
91
|
|
|
74
|
-
async def export_snapshot(self, dt: datetime):
|
|
92
|
+
async def export_snapshot(self, dt: datetime) -> None:
|
|
75
93
|
log_debug("checking data at %s for %s", dt, self.label)
|
|
76
94
|
try:
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
else:
|
|
84
|
-
break
|
|
85
|
-
log_debug("block at %s: %s", dt, block)
|
|
86
|
-
data = await self.get_data_for_export(block, dt, sync=False)
|
|
87
|
-
await victoria.post_data(data)
|
|
95
|
+
if await self.data_exists(dt, sync=False):
|
|
96
|
+
return
|
|
97
|
+
block = await get_block_at_timestamp(dt)
|
|
98
|
+
log_debug("block at %s: %s", dt, block)
|
|
99
|
+
data = await self.get_data_for_export(block, dt, sync=False)
|
|
100
|
+
await victoria.post_data(data)
|
|
88
101
|
except Exception as e:
|
|
89
102
|
log_error("Error processing %s:", dt, exc_info=True)
|
|
90
103
|
|
|
91
|
-
@a_sync.Semaphore(60)
|
|
92
104
|
async def get_data_for_export(self, block: BlockNumber, ts: datetime) -> List[victoria.Metric]:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
for
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
await self.__process_token(ts, section, wallet, token, bals)
|
|
105
|
-
)
|
|
106
|
-
elif isinstance(section_data, RemoteTokenBalances):
|
|
107
|
-
if section == "external":
|
|
108
|
-
section = "assets"
|
|
109
|
-
for protocol, token_bals in section_data.items():
|
|
110
|
-
for token, bals in dict.items(token_bals):
|
|
105
|
+
async with self._semaphore:
|
|
106
|
+
print(f"exporting {ts} for {self.label}")
|
|
107
|
+
start = datetime.now(tz=timezone.utc)
|
|
108
|
+
|
|
109
|
+
metrics_to_export = []
|
|
110
|
+
data: PortfolioBalances = await self.describe(block, sync=False)
|
|
111
|
+
|
|
112
|
+
for wallet, wallet_data in dict.items(data):
|
|
113
|
+
for section, section_data in wallet_data.items():
|
|
114
|
+
if isinstance(section_data, TokenBalances):
|
|
115
|
+
for token, bals in dict.items(section_data):
|
|
111
116
|
metrics_to_export.extend(
|
|
112
|
-
await self.__process_token(
|
|
113
|
-
ts, section, wallet, token, bals, protocol=protocol
|
|
114
|
-
)
|
|
117
|
+
await self.__process_token(ts, section, wallet, token, bals)
|
|
115
118
|
)
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
elif isinstance(section_data, RemoteTokenBalances):
|
|
120
|
+
if section == "external":
|
|
121
|
+
section = "assets"
|
|
122
|
+
for protocol, token_bals in section_data.items():
|
|
123
|
+
for token, bals in dict.items(token_bals):
|
|
124
|
+
metrics_to_export.extend(
|
|
125
|
+
await self.__process_token(
|
|
126
|
+
ts, section, wallet, token, bals, protocol=protocol
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
raise NotImplementedError()
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
print(f"got data for {ts} in {datetime.now(tz=timezone.utc) - start}")
|
|
133
|
+
return metrics_to_export
|
|
121
134
|
|
|
122
135
|
def __get_data_exists_coros(self, dt: datetime) -> Iterator[str]:
|
|
123
136
|
for query in self._data_queries:
|
|
@@ -131,7 +144,7 @@ class ExportablePortfolio(Portfolio):
|
|
|
131
144
|
token: ChecksumAddress,
|
|
132
145
|
bal: Balance,
|
|
133
146
|
protocol: Optional[str] = None,
|
|
134
|
-
):
|
|
147
|
+
) -> Tuple[victoria.types.PrometheusItem, victoria.types.PrometheusItem]:
|
|
135
148
|
# TODO wallet nicknames in grafana
|
|
136
149
|
# wallet = KNOWN_ADDRESSES[wallet] if wallet in KNOWN_ADDRESSES else wallet
|
|
137
150
|
if protocol is not None:
|
|
@@ -172,7 +185,7 @@ class ExportablePortfolio(Portfolio):
|
|
|
172
185
|
)
|
|
173
186
|
|
|
174
187
|
|
|
175
|
-
async def _get_symbol(token) -> str:
|
|
188
|
+
async def _get_symbol(token: str) -> str:
|
|
176
189
|
if token == "ETH":
|
|
177
190
|
return "ETH"
|
|
178
191
|
try:
|
eth_portfolio_scripts/_utils.py
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from asyncio import Task, create_task, sleep
|
|
3
3
|
from datetime import datetime, timedelta, timezone
|
|
4
|
-
from typing import Any, AsyncGenerator, Dict, List, Optional
|
|
4
|
+
from typing import Any, AsyncGenerator, Dict, Final, List, Optional
|
|
5
5
|
|
|
6
6
|
from brownie import chain
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
result = regex.findall(value)
|
|
9
|
+
timedelta_pattern: Final = re.compile(r"(\d+)([dhms]?)")
|
|
10
|
+
|
|
12
11
|
|
|
12
|
+
def parse_timedelta(value: str) -> timedelta:
|
|
13
13
|
days, hours, minutes, seconds = 0, 0, 0, 0
|
|
14
14
|
|
|
15
|
-
for val, unit in
|
|
15
|
+
for val, unit in timedelta_pattern.findall(value):
|
|
16
16
|
val = int(val)
|
|
17
17
|
if unit == "d":
|
|
18
18
|
days = val
|
|
@@ -58,10 +58,24 @@ async def aiter_timestamps(
|
|
|
58
58
|
|
|
59
59
|
timestamp = start
|
|
60
60
|
|
|
61
|
+
timestamps = []
|
|
61
62
|
while timestamp <= datetime.now(tz=timezone.utc):
|
|
62
|
-
|
|
63
|
+
timestamps.append(timestamp)
|
|
63
64
|
timestamp = timestamp + interval
|
|
64
65
|
|
|
66
|
+
# cycle between yielding earliest, latest, and middle from `timestamps` until complete
|
|
67
|
+
while timestamps:
|
|
68
|
+
# yield the earliest timestamp
|
|
69
|
+
yield timestamps.pop(0)
|
|
70
|
+
# yield the most recent timestamp if there is one
|
|
71
|
+
if timestamps:
|
|
72
|
+
yield timestamps.pop(-1)
|
|
73
|
+
# yield the most middle timestamp if there is one
|
|
74
|
+
if timestamps:
|
|
75
|
+
yield timestamps.pop(len(timestamps) // 2)
|
|
76
|
+
|
|
77
|
+
del timestamps
|
|
78
|
+
|
|
65
79
|
while run_forever:
|
|
66
80
|
while timestamp > datetime.now(tz=timezone.utc):
|
|
67
81
|
await _get_waiter(timestamp)
|
|
Binary file
|
|
@@ -26,13 +26,16 @@ async def export_balances(args: Namespace) -> None:
|
|
|
26
26
|
|
|
27
27
|
interval = parse_timedelta(args.interval)
|
|
28
28
|
portfolio = ExportablePortfolio(
|
|
29
|
-
args.wallet,
|
|
29
|
+
args.wallet,
|
|
30
|
+
label=args.label,
|
|
31
|
+
start_block=args.first_tx_block,
|
|
32
|
+
concurrency=args.concurrency,
|
|
33
|
+
load_prices=False,
|
|
30
34
|
)
|
|
31
35
|
|
|
32
36
|
if export_start_block := args.export_start_block or args.first_tx_block:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
37
|
+
start_ts = await dank_mids.eth.get_block_timestamp(export_start_block)
|
|
38
|
+
start = datetime.fromtimestamp(start_ts, tz=timezone.utc)
|
|
36
39
|
else:
|
|
37
40
|
start = None
|
|
38
41
|
|
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from functools import lru_cache
|
|
2
2
|
from subprocess import CalledProcessError, check_output
|
|
3
|
+
from typing import List
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
def check_docker() -> None:
|
|
@@ -9,48 +10,58 @@ def check_docker() -> None:
|
|
|
9
10
|
Raises:
|
|
10
11
|
RuntimeError: If docker is not installed.
|
|
11
12
|
"""
|
|
13
|
+
print(" 🔍 checking your computer for docker")
|
|
12
14
|
try:
|
|
13
15
|
check_output(["docker", "--version"])
|
|
14
|
-
print("docker found!")
|
|
15
16
|
except (CalledProcessError, FileNotFoundError):
|
|
16
|
-
print("checking your computer for docker")
|
|
17
17
|
raise RuntimeError(
|
|
18
18
|
"Docker is not installed. You must install Docker before using dao-treasury."
|
|
19
19
|
) from None
|
|
20
|
+
else:
|
|
21
|
+
print(" ✔️ eth-portfolio found docker!")
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
def check_docker_compose() ->
|
|
24
|
+
def check_docker_compose() -> List[str]:
|
|
23
25
|
"""
|
|
24
|
-
Check that docker-compose is installed on the user's system.
|
|
26
|
+
Check that either `docker-compose` or `docker compose` is installed on the user's system.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
A valid compose command.
|
|
25
30
|
|
|
26
31
|
Raises:
|
|
27
32
|
RuntimeError: If docker-compose is not installed.
|
|
28
33
|
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
except (CalledProcessError, FileNotFoundError):
|
|
33
|
-
print("checking your computer for docker-compose")
|
|
34
|
+
for cmd in ["docker-compose", "docker compose"]:
|
|
35
|
+
print(f" 🔍 checking your computer for {cmd}")
|
|
36
|
+
|
|
34
37
|
try:
|
|
35
|
-
check_output(["
|
|
36
|
-
print("docker compose found!")
|
|
38
|
+
check_output([*cmd.split(" "), "--version"])
|
|
37
39
|
except (CalledProcessError, FileNotFoundError):
|
|
38
|
-
print("
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
print(f" ❌ {cmd} not found")
|
|
41
|
+
continue
|
|
42
|
+
else:
|
|
43
|
+
print(f" ✔️ eth-portfolio found {cmd}!")
|
|
44
|
+
return cmd.split(" ")
|
|
45
|
+
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
"Docker Compose is not installed. You must install Docker Compose before using dao-treasury."
|
|
48
|
+
) from None
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
@lru_cache(maxsize=None)
|
|
45
|
-
def check_system() ->
|
|
52
|
+
def check_system() -> List[str]:
|
|
46
53
|
"""
|
|
47
54
|
Check that docker and docker-compose is installed on the user's system.
|
|
48
55
|
|
|
56
|
+
Returns:
|
|
57
|
+
A valid compose command.
|
|
58
|
+
|
|
49
59
|
Raises:
|
|
50
60
|
RuntimeError: If docker-compose is not installed.
|
|
51
61
|
"""
|
|
62
|
+
print("eth-portfolio is checking for the required docker dependencies...")
|
|
52
63
|
check_docker()
|
|
53
|
-
check_docker_compose()
|
|
64
|
+
return check_docker_compose()
|
|
54
65
|
|
|
55
66
|
|
|
56
67
|
__all__ = ["check_docker", "check_docker_compose", "check_system"]
|
|
@@ -3,7 +3,7 @@ networks:
|
|
|
3
3
|
|
|
4
4
|
services:
|
|
5
5
|
grafana:
|
|
6
|
-
image: grafana/grafana:
|
|
6
|
+
image: grafana/grafana:12.2.1
|
|
7
7
|
ports:
|
|
8
8
|
- 127.0.0.1:${GRAFANA_PORT:-3000}:3000
|
|
9
9
|
environment:
|
|
@@ -47,7 +47,7 @@ services:
|
|
|
47
47
|
restart: always
|
|
48
48
|
|
|
49
49
|
victoria-metrics:
|
|
50
|
-
image: victoriametrics/victoria-metrics:v1.
|
|
50
|
+
image: victoriametrics/victoria-metrics:v1.129.1
|
|
51
51
|
volumes:
|
|
52
52
|
- ~/.eth-portfolio/data/victoria/:/victoria-metrics-data
|
|
53
53
|
command:
|
|
Binary file
|
|
@@ -3,7 +3,7 @@ from functools import wraps
|
|
|
3
3
|
from importlib import resources
|
|
4
4
|
from os import path
|
|
5
5
|
from subprocess import CalledProcessError, check_output
|
|
6
|
-
from typing import Callable, Final, Iterable, List, Tuple, TypeVar
|
|
6
|
+
from typing import Callable, Final, Iterable, List, Literal, Tuple, TypeVar
|
|
7
7
|
|
|
8
8
|
from typing_extensions import ParamSpec
|
|
9
9
|
|
|
@@ -12,33 +12,33 @@ from eth_portfolio_scripts.docker.check import check_system
|
|
|
12
12
|
|
|
13
13
|
logger: Final = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
COMPOSE_FILE: Final = str(
|
|
16
16
|
resources.files("eth_portfolio_scripts").joinpath("docker/docker-compose.yaml")
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def up(*services: str) -> None:
|
|
21
|
+
"""Build and start the specified docker-compose services."""
|
|
21
22
|
build(*services)
|
|
22
|
-
|
|
23
|
+
_print_notice("starting", services)
|
|
23
24
|
_exec_command(["up", "-d", *services])
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def down() -> None:
|
|
28
|
+
"""Stop all of eth-portfolio's docker-compose services."""
|
|
27
29
|
_exec_command(["down"])
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def build(*services: str) -> None:
|
|
31
|
-
|
|
33
|
+
"""Build the specified docker-compose services."""
|
|
34
|
+
_print_notice("building", services)
|
|
32
35
|
_exec_command(["build", *services])
|
|
33
36
|
|
|
34
37
|
|
|
35
|
-
def stop(
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
print(f"stopping the {container_name} container...")
|
|
41
|
-
_exec_command(["stop", container_name])
|
|
38
|
+
def stop(*services: str) -> None:
|
|
39
|
+
"""Stop the specified docker-compose services, if running."""
|
|
40
|
+
_print_notice("stopping", services)
|
|
41
|
+
_exec_command(["stop", *services])
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
_P = ParamSpec("_P")
|
|
@@ -67,12 +67,32 @@ def ensure_containers(fn: Callable[_P, _T]) -> Callable[_P, _T]:
|
|
|
67
67
|
return compose_wrap
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
def
|
|
71
|
-
|
|
70
|
+
def _print_notice(
|
|
71
|
+
doing: Literal["building", "starting", "stopping"],
|
|
72
|
+
services: Tuple[str, ...],
|
|
73
|
+
) -> None:
|
|
74
|
+
if len(services) == 0:
|
|
75
|
+
print(f"{doing} the backend containers")
|
|
76
|
+
elif len(services) == 1:
|
|
77
|
+
container = services[0]
|
|
78
|
+
print(f"{doing} the {container} container")
|
|
79
|
+
elif len(services) == 2:
|
|
80
|
+
first, second = services
|
|
81
|
+
print(f"{doing} the {first} and {second} containers")
|
|
82
|
+
else:
|
|
83
|
+
*all_but_last, last = services
|
|
84
|
+
print(f"{doing} the {', '.join(all_but_last)}, and {last} containers")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _exec_command(
|
|
88
|
+
command: List[str],
|
|
89
|
+
*,
|
|
90
|
+
compose_file: str = COMPOSE_FILE,
|
|
91
|
+
compose_options: Tuple[str, ...] = (),
|
|
92
|
+
) -> None:
|
|
93
|
+
compose = check_system()
|
|
94
|
+
full_command = [*compose, *compose_options, "-f", compose_file, *command]
|
|
72
95
|
try:
|
|
73
|
-
check_output(
|
|
96
|
+
check_output(full_command)
|
|
74
97
|
except (CalledProcessError, FileNotFoundError) as e:
|
|
75
|
-
|
|
76
|
-
check_output(["docker-compose", *compose_options, "-f", compose_file, *command])
|
|
77
|
-
except (CalledProcessError, FileNotFoundError) as _e:
|
|
78
|
-
raise RuntimeError(f"Error occurred while running {' '.join(command)}: {_e}") from _e
|
|
98
|
+
raise RuntimeError(f"Error occurred while running `{' '.join(full_command)}`: {e}") from e
|
eth_portfolio_scripts/main.py
CHANGED
|
@@ -54,6 +54,12 @@ export_parser.add_argument(
|
|
|
54
54
|
help="The time interval between datapoints. default: 6h",
|
|
55
55
|
default="6h",
|
|
56
56
|
)
|
|
57
|
+
export_parser.add_argument(
|
|
58
|
+
"--concurrency",
|
|
59
|
+
type=int,
|
|
60
|
+
help="The max number of historical blocks to export concurrently. default: 40",
|
|
61
|
+
default=40,
|
|
62
|
+
)
|
|
57
63
|
export_parser.add_argument(
|
|
58
64
|
"--first-tx-block",
|
|
59
65
|
type=int,
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eth_portfolio_temp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.17
|
|
4
4
|
Summary: eth-portfolio makes it easy to analyze your portfolio.
|
|
5
5
|
Home-page: https://github.com/BobTheBuidler/eth-portfolio
|
|
6
6
|
Author: BobTheBuidler
|
|
7
7
|
Author-email: bobthebuidlerdefi@gmail.com
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.10,<3.14
|
|
9
9
|
Requires-Dist: checksum_dict>=2.1.7
|
|
10
10
|
Requires-Dist: dank_mids>=4.20.154
|
|
11
|
-
Requires-Dist: eth-brownie<1.
|
|
11
|
+
Requires-Dist: eth-brownie<1.23,>=1.22.0.dev0
|
|
12
12
|
Requires-Dist: eth_retry<1,>=0.3.4
|
|
13
13
|
Requires-Dist: evmspec>=0.4.1
|
|
14
|
-
Requires-Dist: ez-a-sync>=0.32.
|
|
14
|
+
Requires-Dist: ez-a-sync>=0.32.27
|
|
15
|
+
Requires-Dist: faster-async-lru==2.0.5
|
|
15
16
|
Requires-Dist: faster-eth-utils
|
|
16
|
-
Requires-Dist: numpy<
|
|
17
|
-
Requires-Dist: pandas<
|
|
17
|
+
Requires-Dist: numpy<3
|
|
18
|
+
Requires-Dist: pandas<3,>=1.4.3
|
|
18
19
|
Requires-Dist: typed-envs>=0.0.9
|
|
19
|
-
Requires-Dist: ypricemagic<5,>=4.
|
|
20
|
+
Requires-Dist: ypricemagic<5,>=4.10.0
|
|
20
21
|
Dynamic: author
|
|
21
22
|
Dynamic: author-email
|
|
22
23
|
Dynamic: home-page
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
eth_portfolio__mypyc.cp310-win32.pyd,sha256=vP2Arva-HjNQSr81H1atzjRWgnVVxEDZCSyiHycgqVE,234496
|
|
2
|
+
eth_portfolio/__init__.py,sha256=5GmHYzhUX2gOC4MScgXnqS8x2MawbpWOuWPmqlvCotA,525
|
|
3
|
+
eth_portfolio/_argspec.cp310-win32.pyd,sha256=DVYWzMUU3yeWYrnkKH2RgGfTuP1IYtvNSRTr6aESYzM,9216
|
|
4
|
+
eth_portfolio/_argspec.py,sha256=au8ycJ56-7UzbQVzYUOoMX9sToH0QNi2tS1O820xB3A,1637
|
|
5
|
+
eth_portfolio/_cache.py,sha256=OntwbiDOrBV5JzHHDzJlxcEVrmKK5GG0uXeLeoDDbpA,4926
|
|
6
|
+
eth_portfolio/_config.cp310-win32.pyd,sha256=dLCBGs3KBzk1g4R1bumN2aUR78N7GAdm1AAVPeKGK58,9216
|
|
7
|
+
eth_portfolio/_config.py,sha256=BSOvFS4D0oqgVTtBbtW2g2kpL8Mk6xiP0PipfXs_91A,102
|
|
8
|
+
eth_portfolio/_decimal.py,sha256=DwLUg8pzeTIREG2sHgRK48hoWmjelWNvpEKj10Ra7to,5002
|
|
9
|
+
eth_portfolio/_decorators.py,sha256=AvB-q69MvaFe0Ykh1os6hSewcUujURQ9onqobMaONOM,2899
|
|
10
|
+
eth_portfolio/_exceptions.py,sha256=xb5VmCtBtXy9WXu_1ofoG4j9BJsMQXMVydCJNUulrNY,2500
|
|
11
|
+
eth_portfolio/_shitcoins.cp310-win32.pyd,sha256=3Jkmma44-1z8R7TXAIYIUpT_Y3O3ubeczHCV5S-GMAk,9216
|
|
12
|
+
eth_portfolio/_shitcoins.py,sha256=9i7tcwZN620oQkDBa_gSE4jqZn0TK52AyqZh0MIP8m4,17351
|
|
13
|
+
eth_portfolio/_stableish.cp310-win32.pyd,sha256=gaC_d2KsAb7TzSxcvPnsLBaxe6cuUnxtzJ3RVHZeQqU,9216
|
|
14
|
+
eth_portfolio/_stableish.py,sha256=8pdlYe2YcZ8rTzdCzg-ihRQLPtSFXBNDjtnvuTgPRcQ,2063
|
|
15
|
+
eth_portfolio/_submodules.py,sha256=zne-2YyVFoKHwkLuHx8cdz5lpcwwSDw1edJ9v8G4c1A,2263
|
|
16
|
+
eth_portfolio/_utils.py,sha256=iGck5SKUwpfnuZvLnKDR1A00obZNKx622Y9GY_TYiZA,7886
|
|
17
|
+
eth_portfolio/address.py,sha256=RrFIWSx6jSITwh16Hqs6W6S9fz1KEaebQEk1vqL3HwI,14536
|
|
18
|
+
eth_portfolio/buckets.py,sha256=c5_RLfMyJbnK_PBx5mmdL3YlIX67dD6LqnBxiAr52N0,6896
|
|
19
|
+
eth_portfolio/constants.cp310-win32.pyd,sha256=Kf84IQgSHghjvWveM0IkKaSkwC-mFYEJMgKt6l5hpGM,9216
|
|
20
|
+
eth_portfolio/constants.py,sha256=rUXWFaCGrrF_qzIdDvVzakQJsLnrGtFNn2EgRbZh8fQ,3741
|
|
21
|
+
eth_portfolio/portfolio.py,sha256=FSJ1_FG120dN5vWSPyhbHfyuFHdmFqTHJ5_Ci-4Rts4,24909
|
|
22
|
+
eth_portfolio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
eth_portfolio/_db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
eth_portfolio/_db/decorators.py,sha256=RS-RiuOAgbrMKQpCwwAtP3lPRs8vKM6aPhh9LP4SwtI,5308
|
|
25
|
+
eth_portfolio/_db/entities.py,sha256=1Z1AsR400SJYzDrDGxUJt1HuSKB3ErvxIytFtju7UHU,10345
|
|
26
|
+
eth_portfolio/_db/utils.py,sha256=3P4EVQ5cHLbDrAnBESAIKhoDpJLXLS8IDJNnsh7T2Sc,21586
|
|
27
|
+
eth_portfolio/_ledgers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
eth_portfolio/_ledgers/address.py,sha256=wght92UXf6UIO9GNRzdMy3H43Em7kUk9ArtBsRF-xjw,33830
|
|
29
|
+
eth_portfolio/_ledgers/portfolio.py,sha256=rAr5S1tksgLPUOK1J1FbIWgvTQNzTqrRp8oukiP2BTo,13427
|
|
30
|
+
eth_portfolio/_loaders/__init__.py,sha256=YrBeRuA48cN2Bg2R2nXiNt1CsUIG_7N6ENIpY6aISPo,1735
|
|
31
|
+
eth_portfolio/_loaders/_nonce.cp310-win32.pyd,sha256=uhgNAicfH2hN6cV2wXdeF3bfFdqmoKLDHodQmeF3NDI,9216
|
|
32
|
+
eth_portfolio/_loaders/_nonce.py,sha256=bA-5fZG8ALuADgQGrnoxt6p6j7QEwI6vIGnZRF1Zx7M,6481
|
|
33
|
+
eth_portfolio/_loaders/balances.cp310-win32.pyd,sha256=AfhH5TxDCPmTZ1iHcgfmrev-lhhjd3WBSdVnvvaDWzs,9216
|
|
34
|
+
eth_portfolio/_loaders/balances.py,sha256=_v-x1M3lzDHPt8svHmCcpxZKw0BaGHfZimhkwgphY8A,3226
|
|
35
|
+
eth_portfolio/_loaders/token_transfer.py,sha256=itaWVDyotG0EYPb4o94UOpxYvTdE6YIuUR2pzpXAnwI,8748
|
|
36
|
+
eth_portfolio/_loaders/transaction.py,sha256=oOG3cFdLAa10k2hpN17GO7Wtq-rpUJ1oo7hLAKDjbDc,9393
|
|
37
|
+
eth_portfolio/_loaders/utils.cp310-win32.pyd,sha256=4jp-6fhYJAYtjAJGdJ2EuBx8CMmdoYo6URuxNb5lZmA,9216
|
|
38
|
+
eth_portfolio/_loaders/utils.py,sha256=gJvIikinjCPj-elQVep55VTya9slSARApx-FJKYwnkE,2333
|
|
39
|
+
eth_portfolio/_ydb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
+
eth_portfolio/_ydb/token_transfers.py,sha256=HUTeWShcQD4IzocTHmBOreml0jbVttLcaed1fNiJ2qg,5297
|
|
41
|
+
eth_portfolio/protocols/__init__.py,sha256=k1e4MI8KPALIg7awaVA9LbBuEjqxWZF7uGgxysQ4y90,2623
|
|
42
|
+
eth_portfolio/protocols/_base.py,sha256=nuMnnltg4HZ0Z5Nl83ofwAsTE3A9jLYhDZFkRhAKIIY,3793
|
|
43
|
+
eth_portfolio/protocols/convex.py,sha256=az8FhxLtXf0WKo25Ke0IDRGF8x1yMlffoCRz9aG2fGE,535
|
|
44
|
+
eth_portfolio/protocols/dsr.py,sha256=jn_4niDINXQxqu9Tfb77vsfbhv4oIRRFKsKvnMgEO4I,1806
|
|
45
|
+
eth_portfolio/protocols/liquity.py,sha256=Ptiem5zd1op6qmZj2d6PmcgSqrKCLhlTw3CmIwII1Dc,656
|
|
46
|
+
eth_portfolio/protocols/lending/README.md,sha256=0C941RnyfQwT1DE9bvxwLP-gBTHlQcQysF-75FXHINo,498
|
|
47
|
+
eth_portfolio/protocols/lending/__init__.py,sha256=4jKDPx3sb7Iyi9kOdJRpM6LJy_GhUpviLIbpV_6noYU,1693
|
|
48
|
+
eth_portfolio/protocols/lending/_base.py,sha256=_1zXroYHlliLP685xiRfw8I_tQOoZyaPlnxA_gzLytU,2311
|
|
49
|
+
eth_portfolio/protocols/lending/compound.py,sha256=1aoksFSvIIsk-cNDs2-Kdwl-RtIdTzh3QgpksXG1kEs,7439
|
|
50
|
+
eth_portfolio/protocols/lending/liquity.py,sha256=bJ5TKD-WTVn3S1tlbSGGVFCoKFvgStHVOzKBM2LbsRY,4294
|
|
51
|
+
eth_portfolio/protocols/lending/maker.py,sha256=zqi1E5yQ_QDFOk5OMR4gB5_Ag74RMIkp6u3TyX-P3fg,4073
|
|
52
|
+
eth_portfolio/protocols/lending/unit.py,sha256=OQGDZWdsTOUL4jOKJ1xef6wXhWLHnsRFJjAzn0XSBmM,2011
|
|
53
|
+
eth_portfolio/structs/__init__.py,sha256=x-9CdKe8XMukp8Kjr_lD49ohrSd0iGEvvsMAHtAPrLI,1486
|
|
54
|
+
eth_portfolio/structs/modified.py,sha256=QIuFh-u5VTHe0oborn3oHAiAGD0wqhUQW7XFJVZu2KI,1853
|
|
55
|
+
eth_portfolio/structs/structs.py,sha256=TDJ4u3E_H8uG7iX-C1Pux8TesY2zXRhQUJxmN7cp4cc,20490
|
|
56
|
+
eth_portfolio/typing/__init__.py,sha256=v_KbOgqTKcR4y-05Bel_mg6uAV-IsGurGR-jDW46SMg,58763
|
|
57
|
+
eth_portfolio/typing/balance/single.py,sha256=NmtWXqCWMIkaoyvfL67Rh0wWurLf55fVaDkyUxZ27oY,6501
|
|
58
|
+
eth_portfolio_scripts/__init__.py,sha256=DIBnQmjmhmNL1lO9Lq4QptrZmC7u6_N3p9zRjcZ9vbY,281
|
|
59
|
+
eth_portfolio_scripts/_args.py,sha256=M33vPkja62XEJeCZAqacNSBCqbzse07wepwyBOtkvVo,682
|
|
60
|
+
eth_portfolio_scripts/_logging.py,sha256=EgW8ozQZLAgt_cUgqe5BYZLMVQwB6X-0gq0T-BvqlTY,369
|
|
61
|
+
eth_portfolio_scripts/_portfolio.py,sha256=XP6fUfZfv2hBdW6PCBdpUyT3TRAU56u2QKWwhmHBTAE,7333
|
|
62
|
+
eth_portfolio_scripts/_utils.py,sha256=lj2B79G8YX21ATNp_v-onsU_kNFZF3unBclCVp3AIn8,3163
|
|
63
|
+
eth_portfolio_scripts/balances.cp310-win32.pyd,sha256=xeyimxldnzXayhj8VCJtg1shpA7CEGa3d2-d9Dg822A,9216
|
|
64
|
+
eth_portfolio_scripts/balances.py,sha256=Fr8NGCt5yhvu7LnLH27oOVKcL2XwowmAVezXUOIEzd0,1640
|
|
65
|
+
eth_portfolio_scripts/main.py,sha256=VfwWEVOu4oxp5M-rQLGo-XlDKlG_Q7Y4cLiXMUSXeZg,3804
|
|
66
|
+
eth_portfolio_scripts/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
67
|
+
eth_portfolio_scripts/docker/__init__.cp310-win32.pyd,sha256=PbG45wiTNsF4eXS_1d9LtzdSDTR4QRvLRUTCs7HNJqo,9216
|
|
68
|
+
eth_portfolio_scripts/docker/__init__.py,sha256=R27uZPLUEK2--fb9IrxrDUk6R5_IVFJSv7s_ia7p5Xk,409
|
|
69
|
+
eth_portfolio_scripts/docker/check.cp310-win32.pyd,sha256=O626VmwWbI4iet7RK870vIkpNMbcbTDZ27zCMLSPpSs,9216
|
|
70
|
+
eth_portfolio_scripts/docker/check.py,sha256=mf853kj0_M2iI3d2KuWuwsXtSieP9qWhvTJdrqX8iU8,1997
|
|
71
|
+
eth_portfolio_scripts/docker/docker-compose.yaml,sha256=lCLNTFUYGty_ogZPrhiQMh-lHiw25hrhWXr7T7jyss4,1870
|
|
72
|
+
eth_portfolio_scripts/docker/docker_compose.cp310-win32.pyd,sha256=wvBdvtWvQcljKJYYKHXV-oxbttL5Tqeq2Un6Az14SgA,9216
|
|
73
|
+
eth_portfolio_scripts/docker/docker_compose.py,sha256=w1Z4XfSCC1vVLnss74XbkHUBVb0P2-zOIkMyidkZh38,2903
|
|
74
|
+
eth_portfolio_scripts/docker/.grafana/dashboards/dashboards.yaml,sha256=wktTI-OAdF_khhbciZFo4Gt2V9bUjbe7GLqwdzTKf0U,212
|
|
75
|
+
eth_portfolio_scripts/docker/.grafana/dashboards/Portfolio/Balances.json,sha256=XGMV8e4tDak53e9bmymwAB4uqZmAIcU5JlRT3OiwTeU,70750
|
|
76
|
+
eth_portfolio_scripts/docker/.grafana/datasources/datasources.yml,sha256=8PPH_QDhfbRRh3IidskW46rifJejloa1a9I1KCw2FTk,199
|
|
77
|
+
eth_portfolio_scripts/victoria/__init__.py,sha256=R0VvKiAC0e57zZNihcCptVkFO5CBHIbp2trFYuyY01M,2038
|
|
78
|
+
eth_portfolio_scripts/victoria/types.py,sha256=KNq8aIiNXeiDnCKL7xycmouo0YeKI-sbQkIcTymcSYk,745
|
|
79
|
+
eth_portfolio_temp-0.2.17.dist-info/METADATA,sha256=vZtFUNdKJF5u14oqr0klpNn_ik07bpj1J3jhovPi2Mw,840
|
|
80
|
+
eth_portfolio_temp-0.2.17.dist-info/WHEEL,sha256=GWZF0cboiU4MhsG0baPl8rrtCaXFSLW25384gp3vddM,97
|
|
81
|
+
eth_portfolio_temp-0.2.17.dist-info/entry_points.txt,sha256=yqoC6X3LU1NA_-oJ6mloEYEPNmS-0hPS9OtEwgIeDGU,66
|
|
82
|
+
eth_portfolio_temp-0.2.17.dist-info/top_level.txt,sha256=4MlbY-Yj8oGBGL8piXiO4SOpk2gZFF9ZXVTObTZOzqM,57
|
|
83
|
+
eth_portfolio_temp-0.2.17.dist-info/RECORD,,
|
|
Binary file
|