eth-portfolio-temp 0.0.28.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 +7 -9
- 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/_nonce.py +4 -4
- 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 +60 -0
- eth_portfolio/{typing/balance/single.cp310-win_amd64.pyd → _stableish.cp310-win_amd64.pyd} +0 -0
- eth_portfolio/_stableish.py +42 -0
- eth_portfolio/_utils.py +12 -10
- eth_portfolio/_ydb/token_transfers.py +32 -23
- eth_portfolio/address.py +2 -1
- eth_portfolio/buckets.py +39 -26
- eth_portfolio/constants.cp310-win_amd64.pyd +0 -0
- eth_portfolio/constants.py +21 -1
- eth_portfolio/portfolio.py +1 -1
- eth_portfolio/protocols/lending/liquity.py +1 -1
- eth_portfolio/protocols/lending/maker.py +13 -14
- eth_portfolio/structs/structs.py +2 -2
- eth_portfolio/typing/__init__.py +6 -6
- eth_portfolio__mypyc.cp310-win_amd64.pyd +0 -0
- eth_portfolio_scripts/_portfolio.py +54 -41
- 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/.grafana/dashboards/{portfolio → Portfolio}/Balances.json +23 -23
- eth_portfolio_scripts/docker/.grafana/dashboards/dashboards.yaml +2 -2
- 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 -6
- 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_scripts/victoria/__init__.py +3 -0
- {eth_portfolio_temp-0.0.28.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/METADATA +8 -7
- eth_portfolio_temp-0.2.17.dist-info/RECORD +83 -0
- {eth_portfolio_temp-0.0.28.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/top_level.txt +1 -1
- 93dc731d39cdfeb0971f__mypyc.cp310-win_amd64.pyd +0 -0
- eth_portfolio_temp-0.0.28.dev0.dist-info/RECORD +0 -82
- {eth_portfolio_temp-0.0.28.dev0.dist-info → eth_portfolio_temp-0.2.17.dist-info}/WHEEL +0 -0
- {eth_portfolio_temp-0.0.28.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,16 +186,16 @@ 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
|
-
if retval := await get_event_loop().run_in_executor(process, __is_token, address):
|
|
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")
|
|
195
193
|
else:
|
|
196
194
|
logger.debug("%s is not token")
|
|
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
|
eth_portfolio/_loaders/_nonce.py
CHANGED
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from time import time
|
|
5
|
-
from typing import ClassVar, DefaultDict, Dict, Final, Tuple, final
|
|
5
|
+
from typing import ClassVar, DefaultDict, Dict, Final, Optional, Tuple, final
|
|
6
6
|
|
|
7
7
|
import a_sync
|
|
8
8
|
import dank_mids
|
|
@@ -145,7 +145,7 @@ async def _get_area(
|
|
|
145
145
|
return lo, hi
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
def _update_nonces(address: ChecksumAddress, nonce: Nonce, block: BlockNumber):
|
|
148
|
+
def _update_nonces(address: ChecksumAddress, nonce: Nonce, block: BlockNumber) -> None:
|
|
149
149
|
# if you are searching for `nonce` and you verified it occurs AT or ABOVE `block` call this fn.
|
|
150
150
|
if block > nonces[address][nonce]:
|
|
151
151
|
nonces[address][nonce] = block
|
|
@@ -178,13 +178,13 @@ def _get_num_chunks(range_size: int) -> int:
|
|
|
178
178
|
|
|
179
179
|
@final
|
|
180
180
|
class BlockCache:
|
|
181
|
-
block: ClassVar = 0
|
|
181
|
+
block: ClassVar[BlockNumber] = 0
|
|
182
182
|
updated_at: ClassVar = 0.0
|
|
183
183
|
lock: Final = asyncio.Lock()
|
|
184
184
|
ttl: Final = 5.0
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
async def get_block_number():
|
|
187
|
+
async def get_block_number() -> BlockNumber:
|
|
188
188
|
if now() - BlockCache.updated_at < BlockCache.ttl:
|
|
189
189
|
return BlockCache.block
|
|
190
190
|
async with BlockCache.lock:
|
|
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,12 @@ 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",
|
|
76
|
+
"0x6426b6C2A9108Fa815bcccA3a3232301E1895742",
|
|
70
77
|
"0x0bF377fb3b5F1dD601e693B8fAF6b0bD249f37D3",
|
|
71
78
|
"0xBf5fB1563ef58ba41325454ca61Cc3D62bd40744",
|
|
72
79
|
"0x54fd62228C6e1234fd5Fded28555CA963Dcf6d26",
|
|
@@ -88,7 +95,26 @@ shitcoins: Final = {
|
|
|
88
95
|
"0xECF0dE4C8498Cfd686E4702D955426b22d812d6B",
|
|
89
96
|
"0xF01f7A348681776c1FC9A066c6973882B693cdC6",
|
|
90
97
|
"0x0e1CD6d2715432e4DBedFE969b0Eb2867FF61d5b",
|
|
98
|
+
"0x9aE357521153FB07bE6F5792CE7a49752638fbb7",
|
|
91
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",
|
|
116
|
+
"0xDFC01a7956C0d151ae197274B974fA7527EbAFB9",
|
|
117
|
+
"0xE256CF1C7caEff4383DabafEe6Dd53910F97213D",
|
|
92
118
|
"0x7CD6143B8781dC7e0667e50DB02Eb6539799722F",
|
|
93
119
|
"0x174Cd3359C6a4E6B64D2995Da4E2E4631379526E",
|
|
94
120
|
"0x85E199607eb8aEef775Ae6424b42d20616267852",
|
|
@@ -160,7 +186,32 @@ shitcoins: Final = {
|
|
|
160
186
|
"0xc6a76f7ad66d0e6Ccd1AaAd6e7568c9bd55Dce62",
|
|
161
187
|
"0xB4d4334eB5a251cbE5cC7Afb0Ba26119aCE62281",
|
|
162
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",
|
|
163
213
|
# Tagged as well known address scam on Etherscan
|
|
214
|
+
"0x59bed83a385571ffac0FC15A43f0f6f72e66Dccb",
|
|
164
215
|
"0x2Ec109a0ceFEC70661a242a8B54cae8f45630397",
|
|
165
216
|
"0x11d666B9C8fa057774324a9bFe6B53eB8d23079f",
|
|
166
217
|
"0x0598BEf845934d83463cEaDd8C86F0801e2Ee058",
|
|
@@ -208,12 +259,16 @@ shitcoins: Final = {
|
|
|
208
259
|
"0xAba86342C3f57E002F47F6eb38099A577312F2e8",
|
|
209
260
|
"0x94C4bfDD351A2EeA9d57e602C2a24d6Fe823Fd1a",
|
|
210
261
|
"0xAfF006725DeeAF5BF6750A6C7c98D9e70cB189a4",
|
|
262
|
+
"0x54c4D37aD550f9776CE2021df245d98B4Bb5Cb5E",
|
|
211
263
|
# fake USDC
|
|
212
264
|
"0x98a7800EB17e1A0e2b2A4b69e83c58F4535F2180",
|
|
213
265
|
"0xdC5BCA9992191c900D7A7F5b8e1b3e4161d1aa51",
|
|
214
266
|
"0xd27255AA153ae470419B0198fa61e892a612aFE8",
|
|
215
267
|
"0xbADbe6619c0563c60E62C7409681440a80391a27",
|
|
216
268
|
"0x89D3Ac7C32Aa14bEE6Fa90E041241dc4eEbbDFB3",
|
|
269
|
+
"0x640c6fdAfbDaC9bfCe79943890fdc98f8d39bE3e",
|
|
270
|
+
"0xA04a3a553548090a7b81D2de75B8a08Ee45860e1",
|
|
271
|
+
"0x1b8F28C0d410456FE04b8f421d00d6654F66AbB6",
|
|
217
272
|
# fake DAI
|
|
218
273
|
"0xf84f908E78f9295b53883AD9ACa38F86d513f03F",
|
|
219
274
|
"0x20Dd160716b0387220Eea7B9951bDb51B3728DC4",
|
|
@@ -222,12 +277,17 @@ shitcoins: Final = {
|
|
|
222
277
|
# fake USDT
|
|
223
278
|
"0x9257Cf49802E00048A30a6429e9c4d86210F4253",
|
|
224
279
|
"0x6b1b80b4b3228060EA21C292a3b9c576691Ca6cC",
|
|
280
|
+
"0xfCDFf724385Dcb47f2E62f7F2E383a7822Ed9718",
|
|
281
|
+
"0x94a2a4d842F522Dac0E793f2f9Ad08058997D06d",
|
|
282
|
+
"0xb44D28295E3D7C898C087A1cB209A444DeD5E4c1",
|
|
283
|
+
"0xFf3A7EE33203F5d3C69C309B5425b0E3fEfB736B",
|
|
225
284
|
# fake ETH
|
|
226
285
|
"0x225795bb6D20773a4A34c682Ae8457D00C80F955",
|
|
227
286
|
"0x25741E94782d3EaA7fb0A02D78256bf065AEC3B9",
|
|
228
287
|
# fake AERO
|
|
229
288
|
"0x3C78E3391C47fB9310BDB9085955934E1622442f",
|
|
230
289
|
"0x1f186De364a56e741Fcb188d37a393d409D08AeA",
|
|
290
|
+
"0xDd62542e9Be4390147f8c1f5b417f50ec4F834B7",
|
|
231
291
|
],
|
|
232
292
|
Network.Fantom: [
|
|
233
293
|
# generally looks like shit
|
|
Binary file
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from typing import DefaultDict, Final, Set
|
|
3
|
+
|
|
4
|
+
from eth_typing import ChecksumAddress
|
|
5
|
+
from y import Network, convert
|
|
6
|
+
|
|
7
|
+
stableish: Final = {
|
|
8
|
+
Network.Mainnet: [
|
|
9
|
+
"0x674C6Ad92Fd080e4004b2312b45f796a192D27a0", # USDN
|
|
10
|
+
"0x1456688345527bE1f37E9e627DA0837D6f08C925", # USDP
|
|
11
|
+
"0x865377367054516e17014CcdED1e7d814EDC9ce4", # DOLA
|
|
12
|
+
"0x5f98805A4E8be255a32880FDeC7F6728C6568bA0", # LUSD
|
|
13
|
+
"0xB9D7DdDca9a4AC480991865EfEf82E01273F79C3", # bLUSD
|
|
14
|
+
"0x4Fabb145d64652a948d72533023f6E7A623C7C53", # BUSD
|
|
15
|
+
"0x853d955aCEf822Db058eb8505911ED77F175b99e", # FRAX
|
|
16
|
+
"0x5BC25f649fc4e26069dDF4cF4010F9f706c23831", # DUSD
|
|
17
|
+
"0x99D8a9C45b2ecA8864373A26D1459e3Dff1e17F3", # MIM
|
|
18
|
+
"0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd", # GUSD
|
|
19
|
+
"0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919", # RAI
|
|
20
|
+
"0x2A8e1E676Ec238d8A992307B495b45B3fEAa5e86", # OUSD
|
|
21
|
+
"0x57Ab1ec28D129707052df4dF418D58a2D46d5f51", # sUSD
|
|
22
|
+
"0xe2f2a5C287993345a840Db3B0845fbC70f5935a5", # mUSD
|
|
23
|
+
"0x5B3b5DF2BF2B6543f78e053bD91C4Bdd820929f1", # USDM
|
|
24
|
+
"0xdF574c24545E5FfEcb9a659c229253D4111d87e1", # HUSD
|
|
25
|
+
"0xa47c8bf37f92aBed4A126BDA807A7b7498661acD", # UST
|
|
26
|
+
"0x4591DBfF62656E7859Afe5e45f6f47D3669fBB28", # mkUSD
|
|
27
|
+
"0x6c3ea9036406852006290770BEdFcAbA0e23A0e8", # PYUSD
|
|
28
|
+
"0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", # crvUSD
|
|
29
|
+
"0xBC6DA0FE9aD5f3b0d58160288917AA56653660E9", # alUSD
|
|
30
|
+
"0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f", # GHO
|
|
31
|
+
],
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
STABLEISH_COINS: DefaultDict[int, Set[ChecksumAddress]] = defaultdict(set)
|
|
35
|
+
"""
|
|
36
|
+
A dictionary that contains, for each chain, a set of 'stable-ish' coins that are considered stable by the wider market but not by ypriceamgic.
|
|
37
|
+
|
|
38
|
+
These coins, and wrapper tokens that contain them, will be considered stablecoins on all dashboards.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
for chain, tokens in stableish.items():
|
|
42
|
+
STABLEISH_COINS[chain].update(convert.to_address(token) for token in tokens)
|
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,15 +1,16 @@
|
|
|
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, List
|
|
4
|
+
from typing import Any, AsyncIterator, Final, List
|
|
5
5
|
|
|
6
6
|
import dank_mids
|
|
7
7
|
import evmspec
|
|
8
8
|
import y._db.log
|
|
9
9
|
from a_sync import ASyncIterable, ASyncIterator, as_yielded
|
|
10
10
|
from brownie import chain
|
|
11
|
+
from eth_typing import BlockNumber, ChecksumAddress
|
|
12
|
+
from faster_eth_abi import encode
|
|
11
13
|
from faster_eth_utils import encode_hex
|
|
12
|
-
from y.datatypes import Address
|
|
13
14
|
from y.utils.events import ProcessedEvents
|
|
14
15
|
|
|
15
16
|
from eth_portfolio._loaders import load_token_transfer
|
|
@@ -18,19 +19,13 @@ from eth_portfolio.constants import TRANSFER_SIGS
|
|
|
18
19
|
from eth_portfolio.structs import TokenTransfer
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
logger: Final = getLogger(__name__)
|
|
23
|
+
_logger_is_enabled_for: Final = logger.isEnabledFor
|
|
24
|
+
_logger_log: Final = logger._log
|
|
24
25
|
|
|
25
|
-
encode_address = lambda address: encode_hex(encode(["address"], [str(address)]))
|
|
26
|
-
except ImportError:
|
|
27
|
-
from eth_abi import encode_single
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
logger = getLogger(__name__)
|
|
32
|
-
_logger_is_enabled_for = logger.isEnabledFor
|
|
33
|
-
_logger_log = logger._log
|
|
27
|
+
def encode_address(address: Any) -> bytes:
|
|
28
|
+
return encode_hex(encode(["address"], [str(address)]))
|
|
34
29
|
|
|
35
30
|
|
|
36
31
|
class _TokenTransfers(ProcessedEvents["Task[TokenTransfer]"]):
|
|
@@ -38,9 +33,14 @@ class _TokenTransfers(ProcessedEvents["Task[TokenTransfer]"]):
|
|
|
38
33
|
|
|
39
34
|
__slots__ = "address", "_load_prices"
|
|
40
35
|
|
|
41
|
-
def __init__(
|
|
42
|
-
self
|
|
43
|
-
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
address: ChecksumAddress,
|
|
39
|
+
from_block: BlockNumber,
|
|
40
|
+
load_prices: bool = False,
|
|
41
|
+
) -> None:
|
|
42
|
+
self.address: Final = address
|
|
43
|
+
self._load_prices: Final = load_prices
|
|
44
44
|
super().__init__(topics=self._topics, from_block=from_block)
|
|
45
45
|
|
|
46
46
|
def __repr__(self) -> str:
|
|
@@ -51,7 +51,7 @@ class _TokenTransfers(ProcessedEvents["Task[TokenTransfer]"]):
|
|
|
51
51
|
def _topics(self) -> List: ...
|
|
52
52
|
|
|
53
53
|
@ASyncIterator.wrap # type: ignore [call-overload]
|
|
54
|
-
async def yield_thru_block(self, block) -> AsyncIterator["Task[TokenTransfer]"]:
|
|
54
|
+
async def yield_thru_block(self, block: BlockNumber) -> AsyncIterator["Task[TokenTransfer]"]:
|
|
55
55
|
if not _logger_is_enabled_for(DEBUG):
|
|
56
56
|
async for task in self._objects_thru(block=block):
|
|
57
57
|
yield task
|
|
@@ -119,18 +119,27 @@ class TokenTransfers(ASyncIterable[TokenTransfer]):
|
|
|
119
119
|
NOTE: These do not come back in chronologcal order.
|
|
120
120
|
"""
|
|
121
121
|
|
|
122
|
-
def __init__(
|
|
123
|
-
self
|
|
124
|
-
|
|
122
|
+
def __init__(
|
|
123
|
+
self,
|
|
124
|
+
address: ChecksumAddress,
|
|
125
|
+
from_block: BlockNumber,
|
|
126
|
+
load_prices: bool = False,
|
|
127
|
+
) -> None:
|
|
128
|
+
self.transfers_in: Final = InboundTokenTransfers(
|
|
129
|
+
address, from_block, load_prices=load_prices
|
|
130
|
+
)
|
|
131
|
+
self.transfers_out: Final = OutboundTokenTransfers(
|
|
132
|
+
address, from_block, load_prices=load_prices
|
|
133
|
+
)
|
|
125
134
|
|
|
126
|
-
async def __aiter__(self):
|
|
135
|
+
async def __aiter__(self) -> AsyncIterator["Task[TokenTransfer]"]:
|
|
127
136
|
async for transfer in self.__yield_thru_block(await dank_mids.eth.block_number):
|
|
128
137
|
yield transfer
|
|
129
138
|
|
|
130
|
-
def yield_thru_block(self, block:
|
|
139
|
+
def yield_thru_block(self, block: BlockNumber) -> ASyncIterator["Task[TokenTransfer]"]:
|
|
131
140
|
return ASyncIterator(self.__yield_thru_block(block))
|
|
132
141
|
|
|
133
|
-
def __yield_thru_block(self, block:
|
|
142
|
+
def __yield_thru_block(self, block: BlockNumber) -> AsyncIterator["Task[TokenTransfer]"]:
|
|
134
143
|
return as_yielded(
|
|
135
144
|
self.transfers_in.yield_thru_block(block), self.transfers_out.yield_thru_block(block)
|
|
136
145
|
)
|
eth_portfolio/address.py
CHANGED
|
@@ -23,6 +23,7 @@ import dank_mids
|
|
|
23
23
|
import eth_retry
|
|
24
24
|
import y
|
|
25
25
|
from a_sync.exceptions import MappingIsEmptyError
|
|
26
|
+
from eth_typing import BlockNumber
|
|
26
27
|
from y import convert
|
|
27
28
|
from y._decorators import stuck_coro_debugger
|
|
28
29
|
from y.datatypes import Address, Block
|
|
@@ -312,7 +313,7 @@ class PortfolioAddress(_LedgeredBase[AddressLedgerBase]):
|
|
|
312
313
|
return Balance(token=y.EEE_ADDRESS, block=block)
|
|
313
314
|
|
|
314
315
|
@stuck_coro_debugger
|
|
315
|
-
async def token_balances(self, block) -> TokenBalances:
|
|
316
|
+
async def token_balances(self, block: BlockNumber) -> TokenBalances:
|
|
316
317
|
"""
|
|
317
318
|
Retrieves the balances for all tokens in the wallet at a given block.
|
|
318
319
|
|