eth-portfolio-temp 0.1.1.dev0__cp310-cp310-win_amd64.whl → 0.2.17__cp310-cp310-win_amd64.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.
- eth_portfolio/_argspec.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_cache.py +2 -2
- eth_portfolio/_config.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_db/utils.py +6 -8
- eth_portfolio/_decimal.py +11 -10
- eth_portfolio/_ledgers/address.py +1 -1
- eth_portfolio/_loaders/_nonce.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_loaders/balances.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_loaders/token_transfer.py +1 -1
- eth_portfolio/_loaders/transaction.py +1 -1
- eth_portfolio/_loaders/utils.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_loaders/utils.py +1 -1
- eth_portfolio/_shitcoins.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_shitcoins.py +53 -0
- eth_portfolio/_stableish.cp310-win_amd64.pyd +0 -0
- eth_portfolio/_utils.py +12 -10
- eth_portfolio/_ydb/token_transfers.py +6 -11
- eth_portfolio/buckets.py +1 -1
- eth_portfolio/constants.cp310-win_amd64.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 +4 -4
- eth_portfolio__mypyc.cp310-win_amd64.pyd +0 -0
- eth_portfolio_scripts/_portfolio.py +51 -38
- eth_portfolio_scripts/_utils.py +20 -6
- eth_portfolio_scripts/balances.cp310-win_amd64.pyd +0 -0
- eth_portfolio_scripts/balances.py +7 -4
- eth_portfolio_scripts/docker/__init__.cp310-win_amd64.pyd +0 -0
- eth_portfolio_scripts/docker/check.cp310-win_amd64.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-win_amd64.pyd +0 -0
- eth_portfolio_scripts/docker/docker_compose.py +38 -18
- eth_portfolio_scripts/main.py +6 -0
- {eth_portfolio_temp-0.1.1.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/METADATA +7 -6
- {eth_portfolio_temp-0.1.1.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/RECORD +41 -41
- {eth_portfolio_temp-0.1.1.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/top_level.txt +1 -1
- 295eace8438df6ec133b__mypyc.cp310-win_amd64.pyd +0 -0
- {eth_portfolio_temp-0.1.1.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/WHEEL +0 -0
- {eth_portfolio_temp-0.1.1.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/entry_points.txt +0 -0
|
Binary file
|
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
|
eth_portfolio/_db/utils.py
CHANGED
|
@@ -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
|
|
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
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
Binary file
|
eth_portfolio/_loaders/utils.py
CHANGED
|
Binary file
|
eth_portfolio/_shitcoins.py
CHANGED
|
@@ -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,11 @@ shitcoins: Final = {
|
|
|
67
68
|
"0x635701CC5fE41FE8AbD02aa74Beb02e3540E9BB2",
|
|
68
69
|
"0xF709642622729feafC8bf0934679706df49d8A30",
|
|
69
70
|
# Tagged as phishing on Etherscan
|
|
71
|
+
"0x4AeDC0B9Acdf5BE1d312Ace7c2df425C2f56F9C3",
|
|
72
|
+
"0xd16A07Ac860c95F3E225b3A847F81C267b3f929d",
|
|
73
|
+
"0x211c1eB92D74cbdA58bA82116502fD02dd8F319E",
|
|
74
|
+
"0x6ef7D6682086Ad4936D2bC141cD94b2c43476FeD",
|
|
75
|
+
"0x0C98216a20f8e19C1483cf8B307A5E9ce758428D",
|
|
70
76
|
"0x6426b6C2A9108Fa815bcccA3a3232301E1895742",
|
|
71
77
|
"0x0bF377fb3b5F1dD601e693B8fAF6b0bD249f37D3",
|
|
72
78
|
"0xBf5fB1563ef58ba41325454ca61Cc3D62bd40744",
|
|
@@ -91,6 +97,22 @@ shitcoins: Final = {
|
|
|
91
97
|
"0x0e1CD6d2715432e4DBedFE969b0Eb2867FF61d5b",
|
|
92
98
|
"0x9aE357521153FB07bE6F5792CE7a49752638fbb7",
|
|
93
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",
|
|
94
116
|
"0xDFC01a7956C0d151ae197274B974fA7527EbAFB9",
|
|
95
117
|
"0xE256CF1C7caEff4383DabafEe6Dd53910F97213D",
|
|
96
118
|
"0x7CD6143B8781dC7e0667e50DB02Eb6539799722F",
|
|
@@ -164,7 +186,32 @@ shitcoins: Final = {
|
|
|
164
186
|
"0xc6a76f7ad66d0e6Ccd1AaAd6e7568c9bd55Dce62",
|
|
165
187
|
"0xB4d4334eB5a251cbE5cC7Afb0Ba26119aCE62281",
|
|
166
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",
|
|
167
213
|
# Tagged as well known address scam on Etherscan
|
|
214
|
+
"0x59bed83a385571ffac0FC15A43f0f6f72e66Dccb",
|
|
168
215
|
"0x2Ec109a0ceFEC70661a242a8B54cae8f45630397",
|
|
169
216
|
"0x11d666B9C8fa057774324a9bFe6B53eB8d23079f",
|
|
170
217
|
"0x0598BEf845934d83463cEaDd8C86F0801e2Ee058",
|
|
@@ -212,6 +259,7 @@ shitcoins: Final = {
|
|
|
212
259
|
"0xAba86342C3f57E002F47F6eb38099A577312F2e8",
|
|
213
260
|
"0x94C4bfDD351A2EeA9d57e602C2a24d6Fe823Fd1a",
|
|
214
261
|
"0xAfF006725DeeAF5BF6750A6C7c98D9e70cB189a4",
|
|
262
|
+
"0x54c4D37aD550f9776CE2021df245d98B4Bb5Cb5E",
|
|
215
263
|
# fake USDC
|
|
216
264
|
"0x98a7800EB17e1A0e2b2A4b69e83c58F4535F2180",
|
|
217
265
|
"0xdC5BCA9992191c900D7A7F5b8e1b3e4161d1aa51",
|
|
@@ -219,6 +267,8 @@ shitcoins: Final = {
|
|
|
219
267
|
"0xbADbe6619c0563c60E62C7409681440a80391a27",
|
|
220
268
|
"0x89D3Ac7C32Aa14bEE6Fa90E041241dc4eEbbDFB3",
|
|
221
269
|
"0x640c6fdAfbDaC9bfCe79943890fdc98f8d39bE3e",
|
|
270
|
+
"0xA04a3a553548090a7b81D2de75B8a08Ee45860e1",
|
|
271
|
+
"0x1b8F28C0d410456FE04b8f421d00d6654F66AbB6",
|
|
222
272
|
# fake DAI
|
|
223
273
|
"0xf84f908E78f9295b53883AD9ACa38F86d513f03F",
|
|
224
274
|
"0x20Dd160716b0387220Eea7B9951bDb51B3728DC4",
|
|
@@ -228,6 +278,9 @@ shitcoins: Final = {
|
|
|
228
278
|
"0x9257Cf49802E00048A30a6429e9c4d86210F4253",
|
|
229
279
|
"0x6b1b80b4b3228060EA21C292a3b9c576691Ca6cC",
|
|
230
280
|
"0xfCDFf724385Dcb47f2E62f7F2E383a7822Ed9718",
|
|
281
|
+
"0x94a2a4d842F522Dac0E793f2f9Ad08058997D06d",
|
|
282
|
+
"0xb44D28295E3D7C898C087A1cB209A444DeD5E4c1",
|
|
283
|
+
"0xFf3A7EE33203F5d3C69C309B5425b0E3fEfB736B",
|
|
231
284
|
# fake ETH
|
|
232
285
|
"0x225795bb6D20773a4A34c682Ae8457D00C80F955",
|
|
233
286
|
"0x25741E94782d3EaA7fb0A02D78256bf065AEC3B9",
|
|
Binary file
|
eth_portfolio/_utils.py
CHANGED
|
@@ -18,10 +18,10 @@ 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
|
|
24
|
-
from
|
|
23
|
+
from faster_async_lru import alru_cache
|
|
24
|
+
from faster_eth_abi.exceptions import InsufficientDataBytes
|
|
25
25
|
from eth_typing import ChecksumAddress
|
|
26
26
|
from pandas import DataFrame # type: ignore
|
|
27
27
|
from y import ERC20, Contract, Network
|
|
@@ -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
|
-
|
|
136
|
-
|
|
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)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from asyncio import Task, create_task, sleep
|
|
3
3
|
from logging import DEBUG, getLogger
|
|
4
|
-
from typing import AsyncIterator, Final, List
|
|
4
|
+
from typing import Any, AsyncIterator, Final, List
|
|
5
5
|
|
|
6
6
|
import dank_mids
|
|
7
7
|
import evmspec
|
|
@@ -9,6 +9,7 @@ import y._db.log
|
|
|
9
9
|
from a_sync import ASyncIterable, ASyncIterator, as_yielded
|
|
10
10
|
from brownie import chain
|
|
11
11
|
from eth_typing import BlockNumber, ChecksumAddress
|
|
12
|
+
from faster_eth_abi import encode
|
|
12
13
|
from faster_eth_utils import encode_hex
|
|
13
14
|
from y.utils.events import ProcessedEvents
|
|
14
15
|
|
|
@@ -18,21 +19,15 @@ from eth_portfolio.constants import TRANSFER_SIGS
|
|
|
18
19
|
from eth_portfolio.structs import TokenTransfer
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
try:
|
|
22
|
-
# this is only available in 4.0.0+
|
|
23
|
-
from eth_abi import encode
|
|
24
|
-
|
|
25
|
-
encode_address = lambda address: encode_hex(encode(["address"], [str(address)]))
|
|
26
|
-
except ImportError:
|
|
27
|
-
from eth_abi import encode_single
|
|
28
|
-
|
|
29
|
-
encode_address = lambda address: encode_hex(encode_single("address", str(address)))
|
|
30
|
-
|
|
31
22
|
logger: Final = getLogger(__name__)
|
|
32
23
|
_logger_is_enabled_for: Final = logger.isEnabledFor
|
|
33
24
|
_logger_log: Final = logger._log
|
|
34
25
|
|
|
35
26
|
|
|
27
|
+
def encode_address(address: Any) -> bytes:
|
|
28
|
+
return encode_hex(encode(["address"], [str(address)]))
|
|
29
|
+
|
|
30
|
+
|
|
36
31
|
class _TokenTransfers(ProcessedEvents["Task[TokenTransfer]"]):
|
|
37
32
|
"""A helper mixin that contains all logic for fetching token transfers for a particular wallet address"""
|
|
38
33
|
|
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
|
|
Binary file
|
eth_portfolio/portfolio.py
CHANGED
|
@@ -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 [
|
|
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,10 +1,12 @@
|
|
|
1
1
|
from asyncio import gather
|
|
2
|
-
from typing import List, Optional
|
|
2
|
+
from typing import Final, List, Optional
|
|
3
3
|
|
|
4
4
|
from a_sync import igather
|
|
5
|
-
from
|
|
5
|
+
from brownie import ZERO_ADDRESS
|
|
6
6
|
from dank_mids.exceptions import Revert
|
|
7
7
|
from eth_typing import HexStr
|
|
8
|
+
from faster_async_lru import alru_cache
|
|
9
|
+
from faster_eth_abi import encode
|
|
8
10
|
from y import Contract, Network, contract_creation_block_async, get_price
|
|
9
11
|
from y._decorators import stuck_coro_debugger
|
|
10
12
|
from y.constants import dai
|
|
@@ -14,18 +16,15 @@ from eth_portfolio._utils import Decimal
|
|
|
14
16
|
from eth_portfolio.protocols.lending._base import LendingProtocolWithLockedCollateral
|
|
15
17
|
from eth_portfolio.typing import Balance, TokenBalances
|
|
16
18
|
|
|
17
|
-
try:
|
|
18
|
-
# this is only available in 4.0.0+
|
|
19
|
-
from eth_abi import encode
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
yfi: Final = "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e"
|
|
21
|
+
dai: Contract
|
|
22
|
+
_1e18: Final = Decimal(10**18)
|
|
23
|
+
_1e45: Final = Decimal(10**45)
|
|
24
24
|
|
|
25
|
-
encode_bytes = lambda bytestring: encode_single("bytes32", bytestring)
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
def encode_bytes(bytestring: str) -> bytes:
|
|
27
|
+
return encode(["bytes32"], [bytestring])
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
class Maker(LendingProtocolWithLockedCollateral):
|
|
@@ -50,8 +49,8 @@ class Maker(LendingProtocolWithLockedCollateral):
|
|
|
50
49
|
|
|
51
50
|
balances: TokenBalances = TokenBalances(block=block)
|
|
52
51
|
for token, data in zip(gems, ink_data):
|
|
53
|
-
if ink := data.dict()["ink"]:
|
|
54
|
-
balance = ink /
|
|
52
|
+
if token != ZERO_ADDRESS and (ink := data.dict()["ink"]):
|
|
53
|
+
balance = ink / _1e18
|
|
55
54
|
value = round(balance * Decimal(await get_price(token, block, sync=False)), 18)
|
|
56
55
|
balances[token] = Balance(balance, value, token=token, block=block)
|
|
57
56
|
return balances
|
|
@@ -75,7 +74,7 @@ class Maker(LendingProtocolWithLockedCollateral):
|
|
|
75
74
|
for urns, ilk_info in data:
|
|
76
75
|
art = urns.dict()["art"]
|
|
77
76
|
rate = ilk_info.dict()["rate"]
|
|
78
|
-
debt = art * rate /
|
|
77
|
+
debt = art * rate / _1e45
|
|
79
78
|
balances[dai.address] += Balance(debt, debt, token=dai, block=block)
|
|
80
79
|
return balances
|
|
81
80
|
|
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,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}"
|
|
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]:
|
|
@@ -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
|
|
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:
|
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.10,<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.23,>=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: 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.
|
|
20
|
+
Requires-Dist: ypricemagic<5,>=4.10.0
|
|
20
21
|
Dynamic: author
|
|
21
22
|
Dynamic: author-email
|
|
22
23
|
Dynamic: home-page
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
eth_portfolio__mypyc.cp310-win_amd64.pyd,sha256=bk_x4lnn5UHCqcLFNwC8UwxCCk_SU8RRN0OkM0hP_MY,279040
|
|
2
2
|
eth_portfolio/__init__.py,sha256=5GmHYzhUX2gOC4MScgXnqS8x2MawbpWOuWPmqlvCotA,525
|
|
3
|
-
eth_portfolio/_argspec.cp310-win_amd64.pyd,sha256=
|
|
3
|
+
eth_portfolio/_argspec.cp310-win_amd64.pyd,sha256=ShPLpfF-_rz7BUuoUxuHOoONzRYasqOII-eef-vhbhQ,10752
|
|
4
4
|
eth_portfolio/_argspec.py,sha256=au8ycJ56-7UzbQVzYUOoMX9sToH0QNi2tS1O820xB3A,1637
|
|
5
|
-
eth_portfolio/_cache.py,sha256=
|
|
6
|
-
eth_portfolio/_config.cp310-win_amd64.pyd,sha256=
|
|
5
|
+
eth_portfolio/_cache.py,sha256=OntwbiDOrBV5JzHHDzJlxcEVrmKK5GG0uXeLeoDDbpA,4926
|
|
6
|
+
eth_portfolio/_config.cp310-win_amd64.pyd,sha256=YOBKVY0vx7B8JluRHTvsj16JIYgvRaDGXtCcioz8Qug,10752
|
|
7
7
|
eth_portfolio/_config.py,sha256=BSOvFS4D0oqgVTtBbtW2g2kpL8Mk6xiP0PipfXs_91A,102
|
|
8
|
-
eth_portfolio/_decimal.py,sha256=
|
|
8
|
+
eth_portfolio/_decimal.py,sha256=DwLUg8pzeTIREG2sHgRK48hoWmjelWNvpEKj10Ra7to,5002
|
|
9
9
|
eth_portfolio/_decorators.py,sha256=AvB-q69MvaFe0Ykh1os6hSewcUujURQ9onqobMaONOM,2899
|
|
10
10
|
eth_portfolio/_exceptions.py,sha256=xb5VmCtBtXy9WXu_1ofoG4j9BJsMQXMVydCJNUulrNY,2500
|
|
11
|
-
eth_portfolio/_shitcoins.cp310-win_amd64.pyd,sha256=
|
|
12
|
-
eth_portfolio/_shitcoins.py,sha256=
|
|
13
|
-
eth_portfolio/_stableish.cp310-win_amd64.pyd,sha256=
|
|
11
|
+
eth_portfolio/_shitcoins.cp310-win_amd64.pyd,sha256=eysBgfwrkiL4jtdyRJaqP3XeT5lKDf8q93dVCjVtoQU,10752
|
|
12
|
+
eth_portfolio/_shitcoins.py,sha256=9i7tcwZN620oQkDBa_gSE4jqZn0TK52AyqZh0MIP8m4,17351
|
|
13
|
+
eth_portfolio/_stableish.cp310-win_amd64.pyd,sha256=RBhItaf5yN4KDzwO5QQTjyw4KC3dK522kjOCY8aLE8I,10752
|
|
14
14
|
eth_portfolio/_stableish.py,sha256=8pdlYe2YcZ8rTzdCzg-ihRQLPtSFXBNDjtnvuTgPRcQ,2063
|
|
15
15
|
eth_portfolio/_submodules.py,sha256=zne-2YyVFoKHwkLuHx8cdz5lpcwwSDw1edJ9v8G4c1A,2263
|
|
16
|
-
eth_portfolio/_utils.py,sha256=
|
|
16
|
+
eth_portfolio/_utils.py,sha256=iGck5SKUwpfnuZvLnKDR1A00obZNKx622Y9GY_TYiZA,7886
|
|
17
17
|
eth_portfolio/address.py,sha256=RrFIWSx6jSITwh16Hqs6W6S9fz1KEaebQEk1vqL3HwI,14536
|
|
18
|
-
eth_portfolio/buckets.py,sha256=
|
|
19
|
-
eth_portfolio/constants.cp310-win_amd64.pyd,sha256=
|
|
18
|
+
eth_portfolio/buckets.py,sha256=c5_RLfMyJbnK_PBx5mmdL3YlIX67dD6LqnBxiAr52N0,6896
|
|
19
|
+
eth_portfolio/constants.cp310-win_amd64.pyd,sha256=Z-_tRO2wGgKZ0T2Pz0EcKvA652FbAyI9bhfy3rsLwf0,10752
|
|
20
20
|
eth_portfolio/constants.py,sha256=rUXWFaCGrrF_qzIdDvVzakQJsLnrGtFNn2EgRbZh8fQ,3741
|
|
21
|
-
eth_portfolio/portfolio.py,sha256=
|
|
21
|
+
eth_portfolio/portfolio.py,sha256=FSJ1_FG120dN5vWSPyhbHfyuFHdmFqTHJ5_Ci-4Rts4,24909
|
|
22
22
|
eth_portfolio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
eth_portfolio/_db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
eth_portfolio/_db/decorators.py,sha256=RS-RiuOAgbrMKQpCwwAtP3lPRs8vKM6aPhh9LP4SwtI,5308
|
|
25
25
|
eth_portfolio/_db/entities.py,sha256=1Z1AsR400SJYzDrDGxUJt1HuSKB3ErvxIytFtju7UHU,10345
|
|
26
|
-
eth_portfolio/_db/utils.py,sha256=
|
|
26
|
+
eth_portfolio/_db/utils.py,sha256=3P4EVQ5cHLbDrAnBESAIKhoDpJLXLS8IDJNnsh7T2Sc,21586
|
|
27
27
|
eth_portfolio/_ledgers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
eth_portfolio/_ledgers/address.py,sha256=
|
|
28
|
+
eth_portfolio/_ledgers/address.py,sha256=wght92UXf6UIO9GNRzdMy3H43Em7kUk9ArtBsRF-xjw,33830
|
|
29
29
|
eth_portfolio/_ledgers/portfolio.py,sha256=rAr5S1tksgLPUOK1J1FbIWgvTQNzTqrRp8oukiP2BTo,13427
|
|
30
30
|
eth_portfolio/_loaders/__init__.py,sha256=YrBeRuA48cN2Bg2R2nXiNt1CsUIG_7N6ENIpY6aISPo,1735
|
|
31
|
-
eth_portfolio/_loaders/_nonce.cp310-win_amd64.pyd,sha256=
|
|
31
|
+
eth_portfolio/_loaders/_nonce.cp310-win_amd64.pyd,sha256=U7IexsT47g_413dtjuggNiuI3Qv9faSpFgQvRJb-4o4,10752
|
|
32
32
|
eth_portfolio/_loaders/_nonce.py,sha256=bA-5fZG8ALuADgQGrnoxt6p6j7QEwI6vIGnZRF1Zx7M,6481
|
|
33
|
-
eth_portfolio/_loaders/balances.cp310-win_amd64.pyd,sha256=
|
|
33
|
+
eth_portfolio/_loaders/balances.cp310-win_amd64.pyd,sha256=_2aooQHv2pwBITEfUl3RtjWxd39IRT6lEvcW-xDs1CY,10752
|
|
34
34
|
eth_portfolio/_loaders/balances.py,sha256=_v-x1M3lzDHPt8svHmCcpxZKw0BaGHfZimhkwgphY8A,3226
|
|
35
|
-
eth_portfolio/_loaders/token_transfer.py,sha256=
|
|
36
|
-
eth_portfolio/_loaders/transaction.py,sha256=
|
|
37
|
-
eth_portfolio/_loaders/utils.cp310-win_amd64.pyd,sha256=
|
|
38
|
-
eth_portfolio/_loaders/utils.py,sha256=
|
|
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-win_amd64.pyd,sha256=TbmE8jR1hw0cVZHIUaNQEOP-GFHqt4DfibouZnQKuJE,10752
|
|
38
|
+
eth_portfolio/_loaders/utils.py,sha256=gJvIikinjCPj-elQVep55VTya9slSARApx-FJKYwnkE,2333
|
|
39
39
|
eth_portfolio/_ydb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
-
eth_portfolio/_ydb/token_transfers.py,sha256=
|
|
40
|
+
eth_portfolio/_ydb/token_transfers.py,sha256=HUTeWShcQD4IzocTHmBOreml0jbVttLcaed1fNiJ2qg,5297
|
|
41
41
|
eth_portfolio/protocols/__init__.py,sha256=k1e4MI8KPALIg7awaVA9LbBuEjqxWZF7uGgxysQ4y90,2623
|
|
42
42
|
eth_portfolio/protocols/_base.py,sha256=nuMnnltg4HZ0Z5Nl83ofwAsTE3A9jLYhDZFkRhAKIIY,3793
|
|
43
43
|
eth_portfolio/protocols/convex.py,sha256=az8FhxLtXf0WKo25Ke0IDRGF8x1yMlffoCRz9aG2fGE,535
|
|
@@ -47,37 +47,37 @@ eth_portfolio/protocols/lending/README.md,sha256=0C941RnyfQwT1DE9bvxwLP-gBTHlQcQ
|
|
|
47
47
|
eth_portfolio/protocols/lending/__init__.py,sha256=4jKDPx3sb7Iyi9kOdJRpM6LJy_GhUpviLIbpV_6noYU,1693
|
|
48
48
|
eth_portfolio/protocols/lending/_base.py,sha256=_1zXroYHlliLP685xiRfw8I_tQOoZyaPlnxA_gzLytU,2311
|
|
49
49
|
eth_portfolio/protocols/lending/compound.py,sha256=1aoksFSvIIsk-cNDs2-Kdwl-RtIdTzh3QgpksXG1kEs,7439
|
|
50
|
-
eth_portfolio/protocols/lending/liquity.py,sha256=
|
|
51
|
-
eth_portfolio/protocols/lending/maker.py,sha256=
|
|
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
52
|
eth_portfolio/protocols/lending/unit.py,sha256=OQGDZWdsTOUL4jOKJ1xef6wXhWLHnsRFJjAzn0XSBmM,2011
|
|
53
53
|
eth_portfolio/structs/__init__.py,sha256=x-9CdKe8XMukp8Kjr_lD49ohrSd0iGEvvsMAHtAPrLI,1486
|
|
54
54
|
eth_portfolio/structs/modified.py,sha256=QIuFh-u5VTHe0oborn3oHAiAGD0wqhUQW7XFJVZu2KI,1853
|
|
55
|
-
eth_portfolio/structs/structs.py,sha256=
|
|
56
|
-
eth_portfolio/typing/__init__.py,sha256=
|
|
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
57
|
eth_portfolio/typing/balance/single.py,sha256=NmtWXqCWMIkaoyvfL67Rh0wWurLf55fVaDkyUxZ27oY,6501
|
|
58
58
|
eth_portfolio_scripts/__init__.py,sha256=DIBnQmjmhmNL1lO9Lq4QptrZmC7u6_N3p9zRjcZ9vbY,281
|
|
59
59
|
eth_portfolio_scripts/_args.py,sha256=M33vPkja62XEJeCZAqacNSBCqbzse07wepwyBOtkvVo,682
|
|
60
60
|
eth_portfolio_scripts/_logging.py,sha256=EgW8ozQZLAgt_cUgqe5BYZLMVQwB6X-0gq0T-BvqlTY,369
|
|
61
|
-
eth_portfolio_scripts/_portfolio.py,sha256=
|
|
62
|
-
eth_portfolio_scripts/_utils.py,sha256=
|
|
63
|
-
eth_portfolio_scripts/balances.cp310-win_amd64.pyd,sha256=
|
|
64
|
-
eth_portfolio_scripts/balances.py,sha256=
|
|
65
|
-
eth_portfolio_scripts/main.py,sha256=
|
|
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-win_amd64.pyd,sha256=3hF_1hFg8Ra8bkAijZimV77V5d9yzkJ8RNf17q9Bfl4,10752
|
|
64
|
+
eth_portfolio_scripts/balances.py,sha256=Fr8NGCt5yhvu7LnLH27oOVKcL2XwowmAVezXUOIEzd0,1640
|
|
65
|
+
eth_portfolio_scripts/main.py,sha256=VfwWEVOu4oxp5M-rQLGo-XlDKlG_Q7Y4cLiXMUSXeZg,3804
|
|
66
66
|
eth_portfolio_scripts/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
67
|
-
eth_portfolio_scripts/docker/__init__.cp310-win_amd64.pyd,sha256=
|
|
67
|
+
eth_portfolio_scripts/docker/__init__.cp310-win_amd64.pyd,sha256=vVEIDZQmFDmX7NKyOW31bhJBqes7ow0AEw8uhfltJ8U,10752
|
|
68
68
|
eth_portfolio_scripts/docker/__init__.py,sha256=R27uZPLUEK2--fb9IrxrDUk6R5_IVFJSv7s_ia7p5Xk,409
|
|
69
|
-
eth_portfolio_scripts/docker/check.cp310-win_amd64.pyd,sha256=
|
|
70
|
-
eth_portfolio_scripts/docker/check.py,sha256=
|
|
71
|
-
eth_portfolio_scripts/docker/docker-compose.yaml,sha256=
|
|
72
|
-
eth_portfolio_scripts/docker/docker_compose.cp310-win_amd64.pyd,sha256=
|
|
73
|
-
eth_portfolio_scripts/docker/docker_compose.py,sha256=
|
|
69
|
+
eth_portfolio_scripts/docker/check.cp310-win_amd64.pyd,sha256=3lYELU58DRlyg8oWmW8ZDD4lbRjTHEZBnmHz8c4oZu4,10752
|
|
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-win_amd64.pyd,sha256=st2IZdwvbVOR_mUkGh9igeBlxiCgn1p_-r5r0OYvLdg,10752
|
|
73
|
+
eth_portfolio_scripts/docker/docker_compose.py,sha256=w1Z4XfSCC1vVLnss74XbkHUBVb0P2-zOIkMyidkZh38,2903
|
|
74
74
|
eth_portfolio_scripts/docker/.grafana/dashboards/dashboards.yaml,sha256=wktTI-OAdF_khhbciZFo4Gt2V9bUjbe7GLqwdzTKf0U,212
|
|
75
75
|
eth_portfolio_scripts/docker/.grafana/dashboards/Portfolio/Balances.json,sha256=XGMV8e4tDak53e9bmymwAB4uqZmAIcU5JlRT3OiwTeU,70750
|
|
76
76
|
eth_portfolio_scripts/docker/.grafana/datasources/datasources.yml,sha256=8PPH_QDhfbRRh3IidskW46rifJejloa1a9I1KCw2FTk,199
|
|
77
77
|
eth_portfolio_scripts/victoria/__init__.py,sha256=R0VvKiAC0e57zZNihcCptVkFO5CBHIbp2trFYuyY01M,2038
|
|
78
78
|
eth_portfolio_scripts/victoria/types.py,sha256=KNq8aIiNXeiDnCKL7xycmouo0YeKI-sbQkIcTymcSYk,745
|
|
79
|
-
eth_portfolio_temp-0.
|
|
80
|
-
eth_portfolio_temp-0.
|
|
81
|
-
eth_portfolio_temp-0.
|
|
82
|
-
eth_portfolio_temp-0.
|
|
83
|
-
eth_portfolio_temp-0.
|
|
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=KUuBC6lxAbHCKilKua8R9W_TM71_-9Sg5uEP3uDWcoU,101
|
|
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
|
|
File without changes
|
{eth_portfolio_temp-0.1.1.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/entry_points.txt
RENAMED
|
File without changes
|