eth-portfolio-temp 0.1.6.dev0__cp312-cp312-macosx_11_0_arm64.whl → 0.2.16__cp312-cp312-macosx_11_0_arm64.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 eth-portfolio-temp might be problematic. Click here for more details.

Files changed (41) hide show
  1. eth_portfolio/_argspec.cpython-312-darwin.so +0 -0
  2. eth_portfolio/_cache.py +2 -2
  3. eth_portfolio/_config.cpython-312-darwin.so +0 -0
  4. eth_portfolio/_db/utils.py +6 -8
  5. eth_portfolio/_decimal.py +11 -10
  6. eth_portfolio/_ledgers/address.py +1 -1
  7. eth_portfolio/_loaders/_nonce.cpython-312-darwin.so +0 -0
  8. eth_portfolio/_loaders/balances.cpython-312-darwin.so +0 -0
  9. eth_portfolio/_loaders/token_transfer.py +1 -1
  10. eth_portfolio/_loaders/transaction.py +1 -1
  11. eth_portfolio/_loaders/utils.cpython-312-darwin.so +0 -0
  12. eth_portfolio/_loaders/utils.py +1 -1
  13. eth_portfolio/_shitcoins.cpython-312-darwin.so +0 -0
  14. eth_portfolio/_shitcoins.py +52 -0
  15. eth_portfolio/_stableish.cpython-312-darwin.so +0 -0
  16. eth_portfolio/_utils.py +11 -9
  17. eth_portfolio/buckets.py +1 -1
  18. eth_portfolio/constants.cpython-312-darwin.so +0 -0
  19. eth_portfolio/portfolio.py +1 -1
  20. eth_portfolio/protocols/lending/liquity.py +1 -1
  21. eth_portfolio/protocols/lending/maker.py +1 -1
  22. eth_portfolio/structs/structs.py +2 -2
  23. eth_portfolio/typing/__init__.py +4 -4
  24. eth_portfolio__mypyc.cpython-312-darwin.so +0 -0
  25. eth_portfolio_scripts/_portfolio.py +51 -38
  26. eth_portfolio_scripts/_utils.py +20 -6
  27. eth_portfolio_scripts/balances.cpython-312-darwin.so +0 -0
  28. eth_portfolio_scripts/balances.py +7 -4
  29. eth_portfolio_scripts/docker/__init__.cpython-312-darwin.so +0 -0
  30. eth_portfolio_scripts/docker/check.cpython-312-darwin.so +0 -0
  31. eth_portfolio_scripts/docker/check.py +28 -17
  32. eth_portfolio_scripts/docker/docker-compose.yaml +2 -2
  33. eth_portfolio_scripts/docker/docker_compose.cpython-312-darwin.so +0 -0
  34. eth_portfolio_scripts/docker/docker_compose.py +36 -18
  35. eth_portfolio_scripts/main.py +6 -0
  36. {eth_portfolio_temp-0.1.6.dev0.dist-info → eth_portfolio_temp-0.2.16.dist-info}/METADATA +6 -5
  37. {eth_portfolio_temp-0.1.6.dev0.dist-info → eth_portfolio_temp-0.2.16.dist-info}/RECORD +40 -40
  38. {eth_portfolio_temp-0.1.6.dev0.dist-info → eth_portfolio_temp-0.2.16.dist-info}/top_level.txt +1 -1
  39. 295eace8438df6ec133b__mypyc.cpython-312-darwin.so +0 -0
  40. {eth_portfolio_temp-0.1.6.dev0.dist-info → eth_portfolio_temp-0.2.16.dist-info}/WHEEL +0 -0
  41. {eth_portfolio_temp-0.1.6.dev0.dist-info → eth_portfolio_temp-0.2.16.dist-info}/entry_points.txt +0 -0
eth_portfolio/_cache.py CHANGED
@@ -30,7 +30,7 @@ def cache_to_disk(fn: Callable[P, T]) -> Callable[P, T]:
30
30
  cache_path_for_fn = f"{BASE_PATH}{fn.__module__.replace('.', '/')}/{name}"
31
31
  logger = getLogger(f"eth_portfolio.cache_to_disk.{name}")
32
32
 
33
- def get_cache_file_path(args, kwargs):
33
+ def get_cache_file_path(args: tuple[Any, ...], kwargs: dict[str, Any]) -> str:
34
34
  # Create a unique filename based on the function arguments
35
35
  key = md5(dumps((args, sorted(kwargs.items())))).hexdigest()
36
36
  return join(cache_path_for_fn, f"{key}.json")
@@ -46,7 +46,7 @@ def cache_to_disk(fn: Callable[P, T]) -> Callable[P, T]:
46
46
 
47
47
  queue: PriorityQueue = PriorityQueue()
48
48
 
49
- async def cache_deco_worker_coro(func) -> NoReturn:
49
+ async def cache_deco_worker_coro(func: Callable[..., Any]) -> NoReturn:
50
50
  try:
51
51
  while True:
52
52
  _, fut, cache_path, args, kwargs = await queue.get()
Binary file
@@ -7,8 +7,7 @@ import evmspec
7
7
  import y._db.common
8
8
  import y._db.config as config
9
9
  from a_sync import PruningThreadPoolExecutor, a_sync
10
- from brownie import chain
11
- from eth_typing import ChecksumAddress
10
+ from eth_typing import ChecksumAddress, HexAddress
12
11
  from evmspec.data import _decode_hook
13
12
  from logging import getLogger
14
13
  from msgspec import ValidationError, json
@@ -32,7 +31,6 @@ from eth_portfolio._db.decorators import break_locks, requery_objs_on_diff_tx_er
32
31
  from eth_portfolio._db.entities import (
33
32
  AddressExtended,
34
33
  BlockExtended,
35
- ContractExtended,
36
34
  TokenExtended,
37
35
  )
38
36
  from eth_portfolio._decimal import Decimal
@@ -42,7 +40,7 @@ from eth_portfolio.typing import _P, _T, Fn
42
40
  logger = getLogger(__name__)
43
41
 
44
42
 
45
- def __bind():
43
+ def __bind() -> None:
46
44
  try:
47
45
  db.bind(**config.connection_settings)
48
46
  except BindingError as e:
@@ -176,7 +174,7 @@ def ensure_block(block: int) -> None:
176
174
  # )
177
175
 
178
176
 
179
- def is_token(address) -> bool:
177
+ def is_token(address: ChecksumAddress) -> bool:
180
178
  if address == EEE_ADDRESS:
181
179
  return False
182
180
  # with suppress(NonStandardERC20):
@@ -188,7 +186,7 @@ def is_token(address) -> bool:
188
186
  return get_event_loop().run_until_complete(_is_token(address))
189
187
 
190
188
 
191
- async def _is_token(address) -> bool:
189
+ async def _is_token(address: HexAddress) -> bool:
192
190
  # just breaking a weird lock, dont mind me
193
191
  if retval := await get_event_loop().run_in_executor(process, __is_token, address): # type: ignore [name-defined]
194
192
  logger.debug("%s is token")
@@ -197,7 +195,7 @@ async def _is_token(address) -> bool:
197
195
  return retval
198
196
 
199
197
 
200
- def __is_token(address) -> bool:
198
+ def __is_token(address: HexAddress) -> bool:
201
199
  with suppress(NonStandardERC20):
202
200
  erc = ERC20(address, asynchronous=True)
203
201
  if all(
@@ -322,7 +320,7 @@ async def get_transaction(sender: ChecksumAddress, nonce: int) -> Optional[Trans
322
320
  _decoded = 0
323
321
 
324
322
 
325
- async def _yield_to_loop():
323
+ async def _yield_to_loop() -> None:
326
324
  """dont let the event loop get congested, let your rpc begin work asap"""
327
325
  global _decoded
328
326
  _decoded += 1
eth_portfolio/_decimal.py CHANGED
@@ -3,6 +3,7 @@ import logging
3
3
  from typing import Union
4
4
 
5
5
  from evmspec.data import Wei
6
+ from typing_extensions import Self
6
7
 
7
8
 
8
9
  logger = logging.getLogger(__name__)
@@ -69,61 +70,61 @@ class Decimal(decimal.Decimal):
69
70
 
70
71
  return string
71
72
 
72
- def __add__(self, other):
73
+ def __add__(self, other) -> Self:
73
74
  try:
74
75
  return type(self)(super().__add__(other))
75
76
  except TypeError as e:
76
77
  raise TypeError(str(e), other) from e.__cause__
77
78
 
78
- def __radd__(self, other):
79
+ def __radd__(self, other) -> Self:
79
80
  try:
80
81
  return type(self)(super().__radd__(other))
81
82
  except TypeError as e:
82
83
  raise TypeError(str(e), other) from e.__cause__
83
84
 
84
- def __sub__(self, other):
85
+ def __sub__(self, other) -> Self:
85
86
  try:
86
87
  return type(self)(super().__sub__(other))
87
88
  except TypeError as e:
88
89
  raise TypeError(str(e), other) from e.__cause__
89
90
 
90
- def __rsub__(self, other):
91
+ def __rsub__(self, other) -> Self:
91
92
  try:
92
93
  return type(self)(super().__rsub__(other))
93
94
  except TypeError as e:
94
95
  raise TypeError(str(e), other) from e.__cause__
95
96
 
96
- def __mul__(self, other):
97
+ def __mul__(self, other) -> Self:
97
98
  try:
98
99
  return type(self)(super().__mul__(other))
99
100
  except TypeError as e:
100
101
  raise TypeError(str(e), other) from e.__cause__
101
102
 
102
- def __rmul__(self, other):
103
+ def __rmul__(self, other) -> Self:
103
104
  try:
104
105
  return type(self)(super().__rmul__(other))
105
106
  except TypeError as e:
106
107
  raise TypeError(str(e), other) from e.__cause__
107
108
 
108
- def __truediv__(self, other):
109
+ def __truediv__(self, other) -> Self:
109
110
  try:
110
111
  return type(self)(super().__truediv__(other))
111
112
  except TypeError as e:
112
113
  raise TypeError(str(e), other) from e.__cause__
113
114
 
114
- def __rtruediv__(self, other):
115
+ def __rtruediv__(self, other) -> Self:
115
116
  try:
116
117
  return type(self)(super().__rtruediv__(other))
117
118
  except TypeError as e:
118
119
  raise TypeError(str(e), other) from e.__cause__
119
120
 
120
- def __floordiv__(self, other):
121
+ def __floordiv__(self, other) -> Self:
121
122
  try:
122
123
  return type(self)(super().__floordiv__(other))
123
124
  except TypeError as e:
124
125
  raise TypeError(str(e), other) from e.__cause__
125
126
 
126
- def __rfloordiv__(self, other):
127
+ def __rfloordiv__(self, other) -> Self:
127
128
  try:
128
129
  return type(self)(super().__rfloordiv__(other))
129
130
  except TypeError as e:
@@ -37,13 +37,13 @@ import dank_mids
37
37
  import eth_retry
38
38
  from a_sync.asyncio import sleep0 as yield_to_loop
39
39
  from aiohttp import ClientResponseError
40
- from async_lru import alru_cache
41
40
  from brownie import chain
42
41
  from dank_mids.eth import TraceFilterParams
43
42
  from eth_typing import BlockNumber, ChecksumAddress
44
43
  from evmspec import FilterTrace
45
44
  from evmspec.structs.receipt import Status
46
45
  from evmspec.structs.trace import call, reward
46
+ from faster_async_lru import alru_cache
47
47
  from typing_extensions import Unpack
48
48
  from pandas import DataFrame # type: ignore
49
49
  from tqdm import tqdm
@@ -7,10 +7,10 @@ from logging import getLogger
7
7
  from typing import Final, Optional, Set
8
8
 
9
9
  from a_sync import create_task, gather
10
- from async_lru import alru_cache
11
10
  from dank_mids import BlockSemaphore
12
11
  from eth_typing import ChecksumAddress
13
12
  from evmspec.data import TransactionIndex
13
+ from faster_async_lru import alru_cache
14
14
  from msgspec import Struct, ValidationError
15
15
  from msgspec.json import decode
16
16
  from pony.orm import TransactionIntegrityError, UnexpectedError
@@ -15,9 +15,9 @@ import eth_retry
15
15
  import evmspec
16
16
  import msgspec
17
17
  from a_sync import SmartProcessingQueue
18
- from async_lru import alru_cache
19
18
  from eth_typing import ChecksumAddress
20
19
  from evmspec import data
20
+ from faster_async_lru import alru_cache
21
21
  from pony.orm import TransactionIntegrityError
22
22
  from y import get_price
23
23
  from y._decorators import stuck_coro_debugger
@@ -4,8 +4,8 @@ import dank_mids
4
4
  import eth_retry
5
5
  import msgspec
6
6
  from a_sync import SmartProcessingQueue
7
- from async_lru import alru_cache
8
7
  from eth_typing import HexStr
8
+ from faster_async_lru import alru_cache
9
9
  from y._decorators import stuck_coro_debugger
10
10
 
11
11
 
@@ -20,6 +20,7 @@ shitcoins: Final = {
20
20
  "0xBFA9180729f1c549334080005Ca37093593fB7Aa",
21
21
  "0x643695D282f6BA237afe27FFE0Acd89a86b50d3e",
22
22
  # Tagged as spam on Etherscan
23
+ "0x3B809ABd5a7597F8d1cf7dEB2747A1e1580C1488",
23
24
  "0xCfB0Ef3e179427316FBD7F0D0296F173762BeEE0",
24
25
  "0xfAFe8a7c0A9b3807CC1df0DEB0BA0B5a5fb7A872",
25
26
  "0xdf66B9727553fB9Bfa417699CB8F60425d62d1E3",
@@ -67,6 +68,10 @@ shitcoins: Final = {
67
68
  "0x635701CC5fE41FE8AbD02aa74Beb02e3540E9BB2",
68
69
  "0xF709642622729feafC8bf0934679706df49d8A30",
69
70
  # Tagged as phishing on Etherscan
71
+ "0x4AeDC0B9Acdf5BE1d312Ace7c2df425C2f56F9C3",
72
+ "0xd16A07Ac860c95F3E225b3A847F81C267b3f929d",
73
+ "0x211c1eB92D74cbdA58bA82116502fD02dd8F319E",
74
+ "0x6ef7D6682086Ad4936D2bC141cD94b2c43476FeD",
70
75
  "0x0C98216a20f8e19C1483cf8B307A5E9ce758428D",
71
76
  "0x6426b6C2A9108Fa815bcccA3a3232301E1895742",
72
77
  "0x0bF377fb3b5F1dD601e693B8fAF6b0bD249f37D3",
@@ -92,6 +97,22 @@ shitcoins: Final = {
92
97
  "0x0e1CD6d2715432e4DBedFE969b0Eb2867FF61d5b",
93
98
  "0x9aE357521153FB07bE6F5792CE7a49752638fbb7",
94
99
  # Generally looks like shit
100
+ "0x5e43e50A3cB43Fb71eD2500bC847E8d25b8335f9",
101
+ "0xE8ED1fca5af1c7dd46A3D5bbDFf7e99749D9e0aa",
102
+ "0x00d0F0250364C431376cC64AADd3aa13c6A8998D",
103
+ "0x256099A072ea5fd35eC134758440413095967109",
104
+ "0xe975ACEb3b61a88A355B066D9cCC910CAb8b4853",
105
+ "0x59fF00f75b49089385377c7f7D905E193c9eb5e2",
106
+ "0x99304A346F8FD562336Fd3485301c02cEbE4F0ae",
107
+ "0x74b1C36268B5dC659aEEA588cb1683E96DaaB71a",
108
+ "0x92BF340B2c67be6911095675f744F9a3aD22d8F0",
109
+ "0xa635305B4a6A1E4A55Eb6A6B54a8D2a55e914F6f",
110
+ "0xC76f77Bff99Ec269050Ca929F4d37B00f012c65F",
111
+ "0x56BcBef8f5Ca4Fc224fE58187c66EA34d3A1ef4a",
112
+ "0xdc6050Ec75Bc4692333A1C301eB799e348d0b77e",
113
+ "0x0542e502cec25bD51b4f3461f4F704807FA1A6D8",
114
+ "0xc01e807Cc73469e9983B614A96847BC01332447d",
115
+ "0x87D38d662A4aae29BB60057e71AFf923C7523DC3",
95
116
  "0xDFC01a7956C0d151ae197274B974fA7527EbAFB9",
96
117
  "0xE256CF1C7caEff4383DabafEe6Dd53910F97213D",
97
118
  "0x7CD6143B8781dC7e0667e50DB02Eb6539799722F",
@@ -165,7 +186,32 @@ shitcoins: Final = {
165
186
  "0xc6a76f7ad66d0e6Ccd1AaAd6e7568c9bd55Dce62",
166
187
  "0xB4d4334eB5a251cbE5cC7Afb0Ba26119aCE62281",
167
188
  "0xE5c5AE39B98eFd9D3c9E0F2a5457D98fFA4b0b46",
189
+ # Generally looks like shit (symbol ERC20)
190
+ "0x2adA6e459089292264133d8D7c85f6907423E444",
191
+ "0x356F680eE21c8CeFfA460c38D20A137F3D07D9af",
192
+ "0x683eAca6CD17383FA93e95f17e7DE443666160eB",
193
+ "0x7452cc6Bd1FA968dd1672FF447f3D7ff1ce210A8",
194
+ "0x773F67800a6d79C0198E8Ac798658f80cb346083",
195
+ "0xaE977D03F5A3999c762eb42a203c436dFF3Df03C",
196
+ "0xbD67D76edFD88719efD7669583258209015B0aB5",
197
+ "0xd814cd86ed2e85156d9Cf3A9319261259458f50c",
198
+ "0xd0E6420b900dE49113B3404b437ee153Bf220B36",
199
+ "0xf1D1C6ea9166bcF97A099A1388E1663933bB2E48",
200
+ "0xF8B49aEC3E356f911f86F58Ff9204242d4b0e6A5",
201
+ "0xe37be01D1337E77aCB0b4293DDb4D410D80010a7",
202
+ "0x7EeAcC32C81e4D78E9705fBf0b977f5A858Bf0F3",
203
+ "0x90133FF815ade1977EA2f1454dBC1d309EAA33f6",
204
+ "0x82ff6D346E424025B1F07e4C8D6527806e50fCA5",
205
+ "0x7C2bc9F685e1f78e42d236Ff819D89DDA864359d",
206
+ "0x3ea463dD031A384DB68d6fD2f37574248E7ffCfE",
207
+ "0x241b047e810c70AB2bB5C567391E0888c4791cbe",
208
+ "0x47D1b57e5B542808eeEd0C509583Fa12d023f05e",
209
+ "0x8a33B3e6f5C0049Cff6dEca0564F5fbDa0eBe9f9",
210
+ "0x84b2e82Ef534e8bE05276DBf4F9e5474fBeD5E59",
211
+ "0x022b5fE59fc45a29d58a7e45a2567b69b649d623",
212
+ "0x178d20220c22008cc1886d2E689cAeE4A5c3239b",
168
213
  # Tagged as well known address scam on Etherscan
214
+ "0x59bed83a385571ffac0FC15A43f0f6f72e66Dccb",
169
215
  "0x2Ec109a0ceFEC70661a242a8B54cae8f45630397",
170
216
  "0x11d666B9C8fa057774324a9bFe6B53eB8d23079f",
171
217
  "0x0598BEf845934d83463cEaDd8C86F0801e2Ee058",
@@ -213,6 +259,7 @@ shitcoins: Final = {
213
259
  "0xAba86342C3f57E002F47F6eb38099A577312F2e8",
214
260
  "0x94C4bfDD351A2EeA9d57e602C2a24d6Fe823Fd1a",
215
261
  "0xAfF006725DeeAF5BF6750A6C7c98D9e70cB189a4",
262
+ "0x54c4D37aD550f9776CE2021df245d98B4Bb5Cb5E",
216
263
  # fake USDC
217
264
  "0x98a7800EB17e1A0e2b2A4b69e83c58F4535F2180",
218
265
  "0xdC5BCA9992191c900D7A7F5b8e1b3e4161d1aa51",
@@ -220,6 +267,8 @@ shitcoins: Final = {
220
267
  "0xbADbe6619c0563c60E62C7409681440a80391a27",
221
268
  "0x89D3Ac7C32Aa14bEE6Fa90E041241dc4eEbbDFB3",
222
269
  "0x640c6fdAfbDaC9bfCe79943890fdc98f8d39bE3e",
270
+ "0xA04a3a553548090a7b81D2de75B8a08Ee45860e1",
271
+ "0x1b8F28C0d410456FE04b8f421d00d6654F66AbB6",
223
272
  # fake DAI
224
273
  "0xf84f908E78f9295b53883AD9ACa38F86d513f03F",
225
274
  "0x20Dd160716b0387220Eea7B9951bDb51B3728DC4",
@@ -229,6 +278,9 @@ shitcoins: Final = {
229
278
  "0x9257Cf49802E00048A30a6429e9c4d86210F4253",
230
279
  "0x6b1b80b4b3228060EA21C292a3b9c576691Ca6cC",
231
280
  "0xfCDFf724385Dcb47f2E62f7F2E383a7822Ed9718",
281
+ "0x94a2a4d842F522Dac0E793f2f9Ad08058997D06d",
282
+ "0xb44D28295E3D7C898C087A1cB209A444DeD5E4c1",
283
+ "0xFf3A7EE33203F5d3C69C309B5425b0E3fEfB736B",
232
284
  # fake ETH
233
285
  "0x225795bb6D20773a4A34c682Ae8457D00C80F955",
234
286
  "0x25741E94782d3EaA7fb0A02D78256bf065AEC3B9",
eth_portfolio/_utils.py CHANGED
@@ -18,9 +18,9 @@ from typing import (
18
18
 
19
19
  import dank_mids
20
20
  from a_sync import ASyncGenericBase, ASyncIterable, ASyncIterator, as_yielded
21
- from async_lru import alru_cache
22
21
  from brownie import chain
23
22
  from brownie.exceptions import ContractNotFound
23
+ from faster_async_lru import alru_cache
24
24
  from faster_eth_abi.exceptions import InsufficientDataBytes
25
25
  from eth_typing import ChecksumAddress
26
26
  from pandas import DataFrame # type: ignore
@@ -47,10 +47,15 @@ if TYPE_CHECKING:
47
47
 
48
48
  logger: Final = logging.getLogger(__name__)
49
49
 
50
- NON_STANDARD_ERC721: Final = {
50
+ NON_STANDARD_ERC721: Final[List[ChecksumAddress]] = {
51
51
  Network.Mainnet: ["0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB"], # CryptoPunks
52
52
  }.get(CHAINID, [])
53
53
 
54
+ SUPPRESS_ERROR_LOGS: Final[List[ChecksumAddress]] = {
55
+ # put tokens here when you don't expect them to price successfully and do not want to see the associated error logs
56
+ }.get(CHAINID, [])
57
+ """Put tokens here when you don't expect them to price successfully and do not want to see the associated error logs."""
58
+
54
59
 
55
60
  async def get_buffered_chain_height() -> int:
56
61
  """Returns an int equal to the current height of the chain minus `_config.REORG_BUFFER`."""
@@ -74,10 +79,6 @@ class Decimal(_decimal.Decimal):
74
79
 
75
80
  # TODO i forget why I had this lol, should I delete?
76
81
 
77
- def __init__(self, v) -> None:
78
- # assert not isinstance(v, _decimal.Decimal)
79
- super().__init__()
80
-
81
82
 
82
83
  async def _describe_err(token: AddressOrContract, block: Optional[Block]) -> str:
83
84
  """
@@ -132,9 +133,10 @@ async def _get_price(token: AddressOrContract, block: Optional[int] = None) -> _
132
133
  # Can't get symbol for handling like other excs
133
134
  logger.warning(f"NonStandardERC20 while fetching price for {token}")
134
135
  elif isinstance(e.exception, PriceError):
135
- logger.warning(
136
- f"PriceError while fetching price for {await _describe_err(token, block)}"
137
- )
136
+ if token not in SUPPRESS_ERROR_LOGS:
137
+ logger.warning(
138
+ f"PriceError while fetching price for {await _describe_err(token, block)}"
139
+ )
138
140
  else:
139
141
  logger.warning(f"{e} while fetching price for {await _describe_err(token, block)}")
140
142
  logger.warning(e, exc_info=True)
eth_portfolio/buckets.py CHANGED
@@ -2,8 +2,8 @@ import logging
2
2
  from typing import Any, Final, Optional, Set
3
3
 
4
4
  from a_sync import igather
5
- from async_lru import alru_cache
6
5
  from eth_typing import ChecksumAddress
6
+ from faster_async_lru import alru_cache
7
7
  from y.constants import CHAINID, STABLECOINS, WRAPPED_GAS_COIN
8
8
  from y.convert import to_address
9
9
  from y.datatypes import Address, AnyAddressType
@@ -37,7 +37,7 @@ from eth_portfolio.typing import Addresses, PortfolioBalances
37
37
  logger = logging.getLogger(__name__)
38
38
 
39
39
 
40
- class PortfolioWallets(Iterable[PortfolioAddress], Dict[Address, PortfolioAddress]): # type: ignore [misc]
40
+ class PortfolioWallets(Iterable[PortfolioAddress], Dict[Address, PortfolioAddress]): # type: ignore [metaclass]
41
41
  """
42
42
  A container that holds all :class:`~eth_portfolio.address.PortfolioAddress` objects for a specific :class:`~eth_portfolio.Portfolio`.
43
43
 
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from async_lru import alru_cache
3
+ from faster_async_lru import alru_cache
4
4
  from y import Contract, Network, get_price
5
5
  from y._decorators import stuck_coro_debugger
6
6
  from y.constants import EEE_ADDRESS
@@ -2,10 +2,10 @@ from asyncio import gather
2
2
  from typing import Final, List, Optional
3
3
 
4
4
  from a_sync import igather
5
- from async_lru import alru_cache
6
5
  from brownie import ZERO_ADDRESS
7
6
  from dank_mids.exceptions import Revert
8
7
  from eth_typing import HexStr
8
+ from faster_async_lru import alru_cache
9
9
  from faster_eth_abi import encode
10
10
  from y import Contract, Network, contract_creation_block_async, get_price
11
11
  from y._decorators import stuck_coro_debugger
@@ -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
 
@@ -43,7 +43,7 @@ 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
49
  from y import Contract, ERC20
@@ -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
 
@@ -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}"
@@ -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, get_block_at_timestamp
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]:
@@ -74,50 +92,45 @@ class ExportablePortfolio(Portfolio):
74
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 not await self.data_exists(dt, sync=False):
78
- while True:
79
- try:
80
- block = await get_block_at_timestamp(dt, sync=False)
81
- except NoBlockFound:
82
- pass
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
- print(f"exporting {ts} for {self.label}")
94
- start = datetime.now(tz=timezone.utc)
95
-
96
- metrics_to_export = []
97
- data: PortfolioBalances = await self.describe(block, sync=False)
98
-
99
- for wallet, wallet_data in dict.items(data):
100
- for section, section_data in wallet_data.items():
101
- if isinstance(section_data, TokenBalances):
102
- for token, bals in dict.items(section_data):
103
- metrics_to_export.extend(
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
- else:
117
- raise NotImplementedError()
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
- print(f"got data for {ts} in {datetime.now(tz=timezone.utc) - start}")
120
- return metrics_to_export
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:
@@ -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
- def parse_timedelta(value: str) -> timedelta:
10
- regex = re.compile(r"(\d+)([dhms]?)")
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 result:
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
- yield timestamp
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)
@@ -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, label=args.label, start_block=args.first_tx_block, load_prices=False
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
- start = datetime.fromtimestamp(
34
- await dank_mids.eth.get_block_timestamp(args.export_start_block), tz=timezone.utc
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
 
@@ -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() -> None:
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
- try:
30
- check_output(["docker-compose", "--version"])
31
- print("docker-compose found!")
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(["docker", "compose", "--version"])
36
- print("docker compose found!")
38
+ check_output([*cmd.split(" "), "--version"])
37
39
  except (CalledProcessError, FileNotFoundError):
38
- print("docker-compose not found, checking your computer for docker compose")
39
- raise RuntimeError(
40
- "Docker Compose is not installed. You must install Docker Compose before using dao-treasury."
41
- ) from None
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() -> None:
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:12.1.1
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.124.0
50
+ image: victoriametrics/victoria-metrics:v1.129.1
51
51
  volumes:
52
52
  - ~/.eth-portfolio/data/victoria/:/victoria-metrics-data
53
53
  command:
@@ -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
- compose_file: Final = str(
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
- print("starting the infra containers...")
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
- print("building the grafana containers")
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(container_name: str) -> None:
36
- """
37
- Stop the specified container if it is running.
38
- Defaults to stopping the 'renderer' container.
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,30 @@ def ensure_containers(fn: Callable[_P, _T]) -> Callable[_P, _T]:
67
67
  return compose_wrap
68
68
 
69
69
 
70
- def _exec_command(command: List[str], *, compose_options: Tuple[str, ...] = ()) -> None:
71
- check_system()
70
+ def _print_notice(
71
+ doing: Literal["building", "starting", "stopping"],
72
+ services: Tuple[str, ...],
73
+ ) -> None:
74
+ if len(services) == 1:
75
+ container = services[0]
76
+ print(f"{doing} the {container} container")
77
+ elif len(services) == 2:
78
+ first, second = services
79
+ print(f"{doing} the {first} and {second} containers")
80
+ else:
81
+ *all_but_last, last = services
82
+ print(f"{doing} the {', '.join(all_but_last)}, and {last} containers")
83
+
84
+
85
+ def _exec_command(
86
+ command: List[str],
87
+ *,
88
+ compose_file: str = COMPOSE_FILE,
89
+ compose_options: Tuple[str, ...] = (),
90
+ ) -> None:
91
+ compose = check_system()
92
+ full_command = [*compose, *compose_options, "-f", compose_file, *command]
72
93
  try:
73
- check_output(["docker", "compose", *compose_options, "-f", compose_file, *command])
94
+ check_output(full_command)
74
95
  except (CalledProcessError, FileNotFoundError) as e:
75
- try:
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
96
+ raise RuntimeError(f"Error occurred while running `{' '.join(full_command)}`: {e}") from e
@@ -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.1.6.dev0
3
+ Version: 0.2.16
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.10,<3.13
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.23,>=1.21.0
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.7
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
17
  Requires-Dist: numpy<3
17
18
  Requires-Dist: pandas<3,>=1.4.3
18
19
  Requires-Dist: typed-envs>=0.0.9
19
- Requires-Dist: ypricemagic<5,>=4.6.27.dev0
20
+ Requires-Dist: ypricemagic<5,>=4.10.0
20
21
  Dynamic: author
21
22
  Dynamic: author-email
22
23
  Dynamic: home-page
@@ -1,83 +1,83 @@
1
- 295eace8438df6ec133b__mypyc.cpython-312-darwin.so,sha256=x3QFQFCAjk8LYecHqV6_Tgzr4mJs81DnzcvRqLyODJE,461456
1
+ eth_portfolio__mypyc.cpython-312-darwin.so,sha256=gz-V6T7MpseErF1saM0261HVe_okaqUoIJS3Y7fg4Yg,711752
2
+ eth_portfolio_temp-0.2.16.dist-info/RECORD,,
3
+ eth_portfolio_temp-0.2.16.dist-info/WHEEL,sha256=V1loQ6TpxABu1APUg0MoTRBOzSKT5xVc3skizX-ovCU,136
4
+ eth_portfolio_temp-0.2.16.dist-info/entry_points.txt,sha256=yqoC6X3LU1NA_-oJ6mloEYEPNmS-0hPS9OtEwgIeDGU,66
5
+ eth_portfolio_temp-0.2.16.dist-info/top_level.txt,sha256=4MlbY-Yj8oGBGL8piXiO4SOpk2gZFF9ZXVTObTZOzqM,57
6
+ eth_portfolio_temp-0.2.16.dist-info/METADATA,sha256=X95Jf9P3LsjGll4sD_H9ibkF4HtBzr7OzBD0_-zoF1Q,814
2
7
  eth_portfolio/address.py,sha256=LvBh4Vp2DBC3gQ0WD-TZ6jfe9s6FZbET1krYG9_KMAA,14139
3
- eth_portfolio/_cache.py,sha256=PYkR-yExjuwd_t7swv4euxL2daJ-VFwh3EMb3MciRJE,4745
4
- eth_portfolio/_shitcoins.cpython-312-darwin.so,sha256=BAM7JLA0BqntPuTSatvkgZIz3dKAqJtLu5beg4w827w,50656
8
+ eth_portfolio/_cache.py,sha256=IOeMXvMgOlEEk01yHP0RARRZXyonuKXUNZbNLFM_KBU,4805
9
+ eth_portfolio/_shitcoins.cpython-312-darwin.so,sha256=HRuQf34wxbdIXcQchajq9K9JhfGfAkqucRVIle-ur0k,50656
5
10
  eth_portfolio/_argspec.py,sha256=VzUZkbDkmOSgNUZBGbGblqtxBfDcmBAB89dY2OX0j-U,1595
6
- eth_portfolio/constants.cpython-312-darwin.so,sha256=p1Y0UxYNQsNMwUXiui8M3LePpHtjhPMON2cWBY90Uu0,50656
11
+ eth_portfolio/constants.cpython-312-darwin.so,sha256=eJI5d8erHVMJL5R0fxjDgO1IOwARmpyU-mCeqZCvZpQ,50656
7
12
  eth_portfolio/_submodules.py,sha256=J8ht9bAAvblUXqwOGN7UIOXUPxrTiq7m1RWiB0-ObaE,2190
8
13
  eth_portfolio/constants.py,sha256=LS9H2P_Qfcreb6z6NknErxvq6OAtIHbHykXy0spol9E,3659
9
14
  eth_portfolio/__init__.py,sha256=0sO4cSJaLYwJfnfVOJRFw7p5_Lzhsr95YuJ1aNQM83A,500
10
- eth_portfolio/_decimal.py,sha256=nuUl6DZHCtrbBN7snwvs4LLaoJ5k7mn3dt4qmlFCkTU,4731
11
- eth_portfolio/buckets.py,sha256=QwdqWzW9iWBmRcJ2aktRtL-obvtBnND179krY5ISbhY,6695
15
+ eth_portfolio/_decimal.py,sha256=tYS0miNoQYZguy0yd1bb3bf49l9F3YICyN8ov6r7VBs,4846
16
+ eth_portfolio/buckets.py,sha256=30Tv5Tq-IL6NsACKbxp7hge84Q-upWsiWGdDTKkXeqE,6702
12
17
  eth_portfolio/_decorators.py,sha256=_ZSurUFEIwZRiMFMhLcIXkD-Ey1CqfBqGaE24hLzOuA,2815
13
- eth_portfolio/_stableish.cpython-312-darwin.so,sha256=ikMbGKrEXFCq3MiUjYJgxVnN-TE5zMNLC5jJocBiFPU,50656
18
+ eth_portfolio/_stableish.cpython-312-darwin.so,sha256=vGf5FBk_fEh2THMJac7o9N10vt0km12fpKIyENXxTEg,50656
14
19
  eth_portfolio/_stableish.py,sha256=VTv69Q91AHxbNbbY0LB4PFwKEseHdkE4_6fLPKH1uW0,2021
15
20
  eth_portfolio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- eth_portfolio/_shitcoins.py,sha256=ShmFtEYaG9k81q2q7s0VLeINtYlzATUQv782kEn2eas,14216
21
+ eth_portfolio/_shitcoins.py,sha256=5OsOC6dYXG6MA2V1hajdiHRfXAX4eG94Wzmh9Dh_5IM,17021
17
22
  eth_portfolio/_config.py,sha256=mw-OA3M8rUA2hqy5wNt30AZRHkWFgR8K2sJg0o5Baxg,98
18
23
  eth_portfolio/_exceptions.py,sha256=bw3IdXhqrWxeFqYLm7pJZqRHKZas_cCg8A5Ih1WQEqQ,2433
19
- eth_portfolio/portfolio.py,sha256=KljK3FZQMtRQLs1D-HHgCN-DVoZ6ZzEdmclmF3__oz0,24243
20
- eth_portfolio/_utils.py,sha256=PUGQKa84BzuwprcFC_S5gjcx1h09-pC9CyZIvaYD6fU,7367
21
- eth_portfolio/_config.cpython-312-darwin.so,sha256=YV7Wh07Zy08XTVRRN3M3HDb7DMZqLGQ9qctq8Da_Xa8,50640
22
- eth_portfolio/_argspec.cpython-312-darwin.so,sha256=jg-osulxfmYo8VzC02AyKa5ex9p5XfQv-6bXvhEqXTU,50640
23
- eth_portfolio/_loaders/balances.cpython-312-darwin.so,sha256=3Eebx1l0Yt8U1OxPVN3OXMNXj4Ye3T1PrwoQZvcdpt0,50656
24
- eth_portfolio/_loaders/transaction.py,sha256=FEYdoNDCgD6-mqZT9MuyYIEkdhG3iUwyNeMmqjpMGJw,9146
25
- eth_portfolio/_loaders/utils.cpython-312-darwin.so,sha256=X_-GKG5AKaphQZ_KVG-oNMxIG68aOz8LlfvbdBbKr4s,50648
24
+ eth_portfolio/portfolio.py,sha256=MP_0Y6fXw8TNoyoFCPiu-MUA44jnMm0L_33gec6MjKU,24248
25
+ eth_portfolio/_utils.py,sha256=8403ioVA6VX4XaF56CDI9IA4ilTZUf_TrdlQ78chXbQ,7661
26
+ eth_portfolio/_config.cpython-312-darwin.so,sha256=smbE84KjbpOmB-ng_AV4DNl7aaFcNZXyqRKljuUMans,50640
27
+ eth_portfolio/_argspec.cpython-312-darwin.so,sha256=EOQxzkssspOcmy504-CMOcY31bf_JZB3x3NrGTqjrAM,50640
28
+ eth_portfolio/_loaders/balances.cpython-312-darwin.so,sha256=P1dadqKGwJ_5CpV_dxK_4UUXdu0OkR8AbFhDgJWEmqc,50656
29
+ eth_portfolio/_loaders/transaction.py,sha256=XLIziLy3YnBPP0E639tQcZfT2Xy4I5VCz6jnfn_C4mE,9153
30
+ eth_portfolio/_loaders/utils.cpython-312-darwin.so,sha256=432IEq_fhMWAg1RtYoofgsHen2Xcl4NSRBmouCNDmvA,50648
26
31
  eth_portfolio/_loaders/__init__.py,sha256=lb45_0ak32Z7N3-Nx1CAoRKiZ1_w-_YGbmSCNuunro8,1702
27
32
  eth_portfolio/_loaders/_nonce.py,sha256=cx0EjTAhVU2EudMKSO-7-t1jl3y4dYn6as4XiY6KyS8,6285
28
- eth_portfolio/_loaders/utils.py,sha256=my3Wg2Ip5gSWuzAWcoLQQEmVmAPhIHbBzQQc0iP1iCA,2258
29
- eth_portfolio/_loaders/_nonce.cpython-312-darwin.so,sha256=-o_nR2SCnCVakJEo1PgIh49Czvf-1-Ye6YRSdvm91WM,50656
33
+ eth_portfolio/_loaders/utils.py,sha256=aoGgWl9ra9F-qb0wA-sX3qelUNGI-fGvM90297L7xWM,2265
34
+ eth_portfolio/_loaders/_nonce.cpython-312-darwin.so,sha256=jLSSsBv5A3N760XSkV8DgHPCqofmHopJVgPNNbsXUYI,50656
30
35
  eth_portfolio/_loaders/balances.py,sha256=BTWfkJIoSraUMe94Wuj8NPyg5EO0OByIjbXu7j6PZEo,3132
31
- eth_portfolio/_loaders/token_transfer.py,sha256=MEw95D2iKBYa_9vIatCHJGe7zEJuYUEC8P1mTjjRXlo,8524
36
+ eth_portfolio/_loaders/token_transfer.py,sha256=JupdQRdTPPsnqFhyRv7vt7IhoC2oEtypjxhEfpzTy9E,8531
32
37
  eth_portfolio/_ydb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
38
  eth_portfolio/_ydb/token_transfers.py,sha256=aKaYSDnJehEcDb_c6YTmcKZxSfZ1jwHMXMOAmE8XH30,5152
34
39
  eth_portfolio/_db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- eth_portfolio/_db/utils.py,sha256=DxwFiuqL6yihPgu1HOPeaFVFydbQT7YmXrv93QAGdQY,20961
40
+ eth_portfolio/_db/utils.py,sha256=Qc4oTv4Qte0ytrpskboJRkKbPNi1Ys-GYiy39EqLXiw,20982
36
41
  eth_portfolio/_db/entities.py,sha256=wZcOa079gznsm7wl7CvRgPdiCBKBsfCeJgc-dH3PYJA,10034
37
42
  eth_portfolio/_db/decorators.py,sha256=GXL6kqnARl0W1roITfrSZkPKoIi9jxJHEvaj9WH86RQ,5161
38
- eth_portfolio/typing/__init__.py,sha256=w3cAQWmSeN5uvUaxJAqxrSFQNUDUhKdSD4wEHfsv3_I,57227
43
+ eth_portfolio/typing/__init__.py,sha256=QtNAK0UHbZZ2ysANFBkb492F339O77m8u-fdIeE6KeA,57316
39
44
  eth_portfolio/typing/balance/single.py,sha256=_HhJL21jOs4IPjRsYbjZVficsNWCS0fcpBryr3JFt7s,6325
40
- eth_portfolio/_ledgers/address.py,sha256=1GsIiI6kv0xQwzmceP3B8ZTQ2rN8gFdQg0KCGCbfv8w,32885
45
+ eth_portfolio/_ledgers/address.py,sha256=8FxDXApRenyjpedtU7nT8RLfqVbB2O5dBjDjB2dWkBM,32892
41
46
  eth_portfolio/_ledgers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
47
  eth_portfolio/_ledgers/portfolio.py,sha256=4qk0tJsD9n6uDwSgF9JJnDuA-osjxeE5b7pTo7ovv6k,13100
43
48
  eth_portfolio/structs/modified.py,sha256=z75XDNXOfm3hDr7pj0PbmUvfPeQSFH9BJ9m0L2V_LFw,1784
44
49
  eth_portfolio/structs/__init__.py,sha256=3EmfKoJGszobO7fCkERLXSN50V6dX3nfhvbiKIvJUZI,1443
45
- eth_portfolio/structs/structs.py,sha256=UYBVPcar3uIid77p0sFzhlCHi7_Ftv-L8_CGBbhCnF0,19800
50
+ eth_portfolio/structs/structs.py,sha256=Baf0rxYadp6Y0yGC4pYwBmLNkJ_LkNrQRuRy1LCN5PM,19853
46
51
  eth_portfolio/protocols/_base.py,sha256=XS4wJZur6Fwl_8E_vFMXx7JSaCQmPn7QbN863TYFSZE,3685
47
52
  eth_portfolio/protocols/liquity.py,sha256=sTUoVtFYj-OWUamwCnJyqna-eAa5Ww7-qGA0St1OTMQ,640
48
53
  eth_portfolio/protocols/__init__.py,sha256=2qpUnGdTbC4tLUbylFvy1AE32XbsCFuFNbvqz91s0W8,2556
49
54
  eth_portfolio/protocols/convex.py,sha256=uNbXxEmhcpXulNkbxoqWl9eAJTZjPJlzXFtrBGvRXLo,518
50
55
  eth_portfolio/protocols/dsr.py,sha256=eGL0DK4IpZkv10CdNr-3OmlAYjqXJAtQHWL34HUBgpY,1755
51
56
  eth_portfolio/protocols/lending/_base.py,sha256=UBdJ5eV2baewEvppjES72eOJzKTK2XHGYnySQNBcack,2254
52
- eth_portfolio/protocols/lending/liquity.py,sha256=Cpa7TcxZjOqcNIIqfKrx1bLRtKtVgNv13zeUR8ndDI0,4177
53
- eth_portfolio/protocols/lending/maker.py,sha256=ajqkBHAVnY5Tp8k-bew37CqyMi4WfOYgC-N8MVTT-AM,3962
57
+ eth_portfolio/protocols/lending/liquity.py,sha256=N45LTa2VTcj3UDygn76iwaG_l0AYXOWh-9n5YBqkQNM,4184
58
+ eth_portfolio/protocols/lending/maker.py,sha256=jebMGZ2mWsvk-2mjFADgHtcOKr08LSqoag2a2dQVTvs,3969
54
59
  eth_portfolio/protocols/lending/__init__.py,sha256=BZtCOglz6R12wqETlECbCEITdtIT9J6bYzy61iDencI,1643
55
60
  eth_portfolio/protocols/lending/README.md,sha256=OhZfsW8e-aD-q02g0maG9QGgW0IietkDRZc7PBakBvc,493
56
61
  eth_portfolio/protocols/lending/compound.py,sha256=bqnIevm7NWhk6nMfkMZUKlQuVWr-tXnuntX-4De1YZM,7252
57
62
  eth_portfolio/protocols/lending/unit.py,sha256=7oRvIkoKUm8IOnDv59pILSSJWFNNjwr4CR1gg2psW9M,1965
58
63
  eth_portfolio_scripts/_logging.py,sha256=B_rQMYt_1PhpwCOLBRpkKK6M1ljcF0wAIgqfPIsFUGU,354
59
- eth_portfolio_scripts/balances.cpython-312-darwin.so,sha256=j4nOtDzIG14hAitx2MTq9zfFnLO3EmBQAD4V9FMyoGo,50656
64
+ eth_portfolio_scripts/balances.cpython-312-darwin.so,sha256=68vCUGdX3YcjJyll2Hla7cTjZZv2p24oqVjw4JgZv94,50656
60
65
  eth_portfolio_scripts/_args.py,sha256=k6J6XkRe1VuN1DiyGuXLCR7NBSvzH5jnVChfzodKuB8,656
61
66
  eth_portfolio_scripts/__init__.py,sha256=TC5c4WZdzSHhTIBYuwzrAyzFuGzBmHiDX_g6ghO09jQ,261
62
- eth_portfolio_scripts/_portfolio.py,sha256=cv4Qpb3EdwhO2sZEOnHYFWOHaoFo6wwt3nddyfDBYv0,6724
67
+ eth_portfolio_scripts/_portfolio.py,sha256=umA-70yv2PGdHgoQVycknkSKk6QurB86FOXlIqBVY38,7139
63
68
  eth_portfolio_scripts/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
64
- eth_portfolio_scripts/balances.py,sha256=o94uA4FVgPE-chbaoQgQazfXQ0O8k6W_yk9JY2bgRpQ,1524
65
- eth_portfolio_scripts/main.py,sha256=A7N78d2hxoU-Xu-dITCRqZ7yRL2l5r26WWzXzMdbeYo,3520
66
- eth_portfolio_scripts/_utils.py,sha256=qyct1Mwf2-1DYj7BUWt2bocUfGKMap1l9sxUfHAjER0,2538
67
- eth_portfolio_scripts/docker/__init__.cpython-312-darwin.so,sha256=yuZDmrLfxS3Uv20DFfQ2vcqlDZecPKYlFWNOdM3Ft7M,50656
68
- eth_portfolio_scripts/docker/docker-compose.yaml,sha256=n3ifrPTq5UUN3rXfwR3WqLmFSwp2uT0v9RmXUtzy1HM,1809
69
- eth_portfolio_scripts/docker/docker_compose.py,sha256=K7_Y_8NeN5s0pb9WUamgfi9OppjpaU0Mlz2nDzewcRA,2226
70
- eth_portfolio_scripts/docker/check.py,sha256=E6_Ar4dr6HfVAgpwoDPcObIolikHKQr8VFkH01sOuIM,1745
69
+ eth_portfolio_scripts/balances.py,sha256=QvpOdXnH1V-tOuEmAOPxH3MrincDExlIP1eEJ2IG2Lk,1588
70
+ eth_portfolio_scripts/main.py,sha256=crhObJ5P0B45zTBE2QOIOPYCxtJ8zhsciarcSZN1HHE,3685
71
+ eth_portfolio_scripts/_utils.py,sha256=-JiZLpSa8P2_ZFXw1AfpVmTO6X65yPeVVdnrDpNFzNQ,3057
72
+ eth_portfolio_scripts/docker/__init__.cpython-312-darwin.so,sha256=PMXEZ9icSuOq1ojJUL6gwp9Y64Af_yrXj8GyLZHnPc4,50656
73
+ eth_portfolio_scripts/docker/docker-compose.yaml,sha256=ZZo_aOYw-Wm2XDpqJpXjUKbtyqbOE5BWa1gR3h-8fUw,1809
74
+ eth_portfolio_scripts/docker/docker_compose.py,sha256=kVsx2FlRBAsYm8xyax3jvrk-IHKgiZvDqPdpQR8yHKI,2727
75
+ eth_portfolio_scripts/docker/check.py,sha256=80fRi6WvxSQM1NxIhtM6vg6fLOjGuqq5F2mGCf4VSvc,1930
71
76
  eth_portfolio_scripts/docker/__init__.py,sha256=ZXSIYcjp94c0O9o39BDGWju3YrCMLl4gtov7YChc9Ig,393
72
- eth_portfolio_scripts/docker/docker_compose.cpython-312-darwin.so,sha256=oPmBX8LOs42DMR633PZQoYJ0cbcbmoISrLqu7pDbiAY,50712
73
- eth_portfolio_scripts/docker/check.cpython-312-darwin.so,sha256=FsUyMiz7UText8inCXSvT8bAkwLxqAmkT3tirrE30gE,50664
77
+ eth_portfolio_scripts/docker/docker_compose.cpython-312-darwin.so,sha256=__OB1GgE_FFNawJD1p_M8NKsumRbZGtO0K0sat_7R8s,50712
78
+ eth_portfolio_scripts/docker/check.cpython-312-darwin.so,sha256=XxgCm3Srd3vbn1w_RCkZbWARPmAqf_C16DS1Umch4-M,50664
74
79
  eth_portfolio_scripts/docker/.grafana/datasources/datasources.yml,sha256=pBE_0Nh_J7d9Fiy3Xu6vuac_HWCBcFsJJSV-ryjQR1Y,188
75
80
  eth_portfolio_scripts/docker/.grafana/dashboards/dashboards.yaml,sha256=MynNDOk69IihoYdd2bL7j8CnRb2Co4gdqW7T4m6AaEU,202
76
81
  eth_portfolio_scripts/docker/.grafana/dashboards/Portfolio/Balances.json,sha256=dBmjogLJRuixCHWSs4ROE0_FXb-PUbqWBHEdLoP-1MU,68788
77
82
  eth_portfolio_scripts/victoria/__init__.py,sha256=hHVexpiVFJdBrdztihYBKC5hCdYDopo6aSaErI0mmOo,1965
78
83
  eth_portfolio_scripts/victoria/types.py,sha256=FJossvAvwNGkufmEk3JD_I10EEaCsOb-m6aeX_vGsUg,707
79
- eth_portfolio_temp-0.1.6.dev0.dist-info/RECORD,,
80
- eth_portfolio_temp-0.1.6.dev0.dist-info/WHEEL,sha256=V1loQ6TpxABu1APUg0MoTRBOzSKT5xVc3skizX-ovCU,136
81
- eth_portfolio_temp-0.1.6.dev0.dist-info/entry_points.txt,sha256=yqoC6X3LU1NA_-oJ6mloEYEPNmS-0hPS9OtEwgIeDGU,66
82
- eth_portfolio_temp-0.1.6.dev0.dist-info/top_level.txt,sha256=CmTLi8Lvs3RulV5ZNdziaDVvF_WRsgRpM17GSMECxrg,64
83
- eth_portfolio_temp-0.1.6.dev0.dist-info/METADATA,sha256=2C3wr1pfAIRlRBjHAOOu0eoir63ePfIlYIsWVU6iEMo,778
@@ -1,3 +1,3 @@
1
- 295eace8438df6ec133b__mypyc
2
1
  eth_portfolio
2
+ eth_portfolio__mypyc
3
3
  eth_portfolio_scripts