eth-portfolio 0.5.2__cp312-cp312-musllinux_1_2_x86_64.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.
Files changed (83) hide show
  1. eth_portfolio/__init__.py +25 -0
  2. eth_portfolio/_argspec.cpython-312-x86_64-linux-musl.so +0 -0
  3. eth_portfolio/_argspec.py +42 -0
  4. eth_portfolio/_cache.py +121 -0
  5. eth_portfolio/_config.cpython-312-x86_64-linux-musl.so +0 -0
  6. eth_portfolio/_config.py +4 -0
  7. eth_portfolio/_db/__init__.py +0 -0
  8. eth_portfolio/_db/decorators.py +147 -0
  9. eth_portfolio/_db/entities.py +311 -0
  10. eth_portfolio/_db/utils.py +610 -0
  11. eth_portfolio/_decimal.py +156 -0
  12. eth_portfolio/_decorators.py +84 -0
  13. eth_portfolio/_exceptions.py +67 -0
  14. eth_portfolio/_ledgers/__init__.py +0 -0
  15. eth_portfolio/_ledgers/address.py +937 -0
  16. eth_portfolio/_ledgers/portfolio.py +328 -0
  17. eth_portfolio/_loaders/__init__.py +33 -0
  18. eth_portfolio/_loaders/_nonce.cpython-312-x86_64-linux-musl.so +0 -0
  19. eth_portfolio/_loaders/_nonce.py +196 -0
  20. eth_portfolio/_loaders/balances.cpython-312-x86_64-linux-musl.so +0 -0
  21. eth_portfolio/_loaders/balances.py +94 -0
  22. eth_portfolio/_loaders/token_transfer.py +217 -0
  23. eth_portfolio/_loaders/transaction.py +241 -0
  24. eth_portfolio/_loaders/utils.cpython-312-x86_64-linux-musl.so +0 -0
  25. eth_portfolio/_loaders/utils.py +68 -0
  26. eth_portfolio/_shitcoins.cpython-312-x86_64-linux-musl.so +0 -0
  27. eth_portfolio/_shitcoins.py +340 -0
  28. eth_portfolio/_stableish.cpython-312-x86_64-linux-musl.so +0 -0
  29. eth_portfolio/_stableish.py +42 -0
  30. eth_portfolio/_submodules.py +73 -0
  31. eth_portfolio/_utils.py +225 -0
  32. eth_portfolio/_ydb/__init__.py +0 -0
  33. eth_portfolio/_ydb/token_transfers.py +146 -0
  34. eth_portfolio/address.py +397 -0
  35. eth_portfolio/buckets.py +212 -0
  36. eth_portfolio/constants.cpython-312-x86_64-linux-musl.so +0 -0
  37. eth_portfolio/constants.py +87 -0
  38. eth_portfolio/portfolio.py +661 -0
  39. eth_portfolio/protocols/__init__.py +67 -0
  40. eth_portfolio/protocols/_base.py +108 -0
  41. eth_portfolio/protocols/convex.py +17 -0
  42. eth_portfolio/protocols/dsr.py +51 -0
  43. eth_portfolio/protocols/lending/README.md +6 -0
  44. eth_portfolio/protocols/lending/__init__.py +50 -0
  45. eth_portfolio/protocols/lending/_base.py +57 -0
  46. eth_portfolio/protocols/lending/compound.py +187 -0
  47. eth_portfolio/protocols/lending/liquity.py +110 -0
  48. eth_portfolio/protocols/lending/maker.py +111 -0
  49. eth_portfolio/protocols/lending/unit.py +46 -0
  50. eth_portfolio/protocols/liquity.py +16 -0
  51. eth_portfolio/py.typed +0 -0
  52. eth_portfolio/structs/__init__.py +43 -0
  53. eth_portfolio/structs/modified.py +69 -0
  54. eth_portfolio/structs/structs.py +637 -0
  55. eth_portfolio/typing/__init__.py +1445 -0
  56. eth_portfolio/typing/balance/single.py +176 -0
  57. eth_portfolio-0.5.2.dist-info/METADATA +26 -0
  58. eth_portfolio-0.5.2.dist-info/RECORD +83 -0
  59. eth_portfolio-0.5.2.dist-info/WHEEL +5 -0
  60. eth_portfolio-0.5.2.dist-info/entry_points.txt +2 -0
  61. eth_portfolio-0.5.2.dist-info/top_level.txt +3 -0
  62. eth_portfolio__mypyc.cpython-312-x86_64-linux-musl.so +0 -0
  63. eth_portfolio_scripts/__init__.py +20 -0
  64. eth_portfolio_scripts/_args.py +26 -0
  65. eth_portfolio_scripts/_logging.py +15 -0
  66. eth_portfolio_scripts/_portfolio.py +209 -0
  67. eth_portfolio_scripts/_utils.py +106 -0
  68. eth_portfolio_scripts/balances.cpython-312-x86_64-linux-musl.so +0 -0
  69. eth_portfolio_scripts/balances.py +57 -0
  70. eth_portfolio_scripts/docker/.grafana/dashboards/Portfolio/Balances.json +1962 -0
  71. eth_portfolio_scripts/docker/.grafana/dashboards/dashboards.yaml +10 -0
  72. eth_portfolio_scripts/docker/.grafana/datasources/datasources.yml +11 -0
  73. eth_portfolio_scripts/docker/__init__.cpython-312-x86_64-linux-musl.so +0 -0
  74. eth_portfolio_scripts/docker/__init__.py +16 -0
  75. eth_portfolio_scripts/docker/check.cpython-312-x86_64-linux-musl.so +0 -0
  76. eth_portfolio_scripts/docker/check.py +67 -0
  77. eth_portfolio_scripts/docker/docker-compose.yaml +61 -0
  78. eth_portfolio_scripts/docker/docker_compose.cpython-312-x86_64-linux-musl.so +0 -0
  79. eth_portfolio_scripts/docker/docker_compose.py +99 -0
  80. eth_portfolio_scripts/main.py +119 -0
  81. eth_portfolio_scripts/py.typed +1 -0
  82. eth_portfolio_scripts/victoria/__init__.py +73 -0
  83. eth_portfolio_scripts/victoria/types.py +38 -0
@@ -0,0 +1,106 @@
1
+ import re
2
+ from asyncio import Task, create_task, sleep
3
+ from datetime import datetime, timedelta, timezone
4
+ from typing import Any, AsyncGenerator, Dict, Final, List, Optional
5
+
6
+ from brownie import chain
7
+
8
+
9
+ timedelta_pattern: Final = re.compile(r"(\d+)([dhms]?)")
10
+
11
+
12
+ def parse_timedelta(value: str) -> timedelta:
13
+ days, hours, minutes, seconds = 0, 0, 0, 0
14
+
15
+ for val, unit in timedelta_pattern.findall(value):
16
+ val = int(val)
17
+ if unit == "d":
18
+ days = val
19
+ elif unit == "h":
20
+ hours = val
21
+ elif unit == "m":
22
+ minutes = val
23
+ elif unit == "s":
24
+ seconds = val
25
+
26
+ return timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
27
+
28
+
29
+ async def aiter_timestamps(
30
+ *,
31
+ start: Optional[datetime] = None,
32
+ interval: timedelta = timedelta(days=1),
33
+ run_forever: bool = False,
34
+ ) -> AsyncGenerator[datetime, None]:
35
+ """
36
+ Generates the timestamps to be queried based on the specified range and interval.
37
+ """
38
+ if not isinstance(run_forever, bool):
39
+ raise TypeError(f"`run_forever` must be boolean. You passed {run_forever}")
40
+
41
+ if start is None:
42
+ start = datetime.now(tz=timezone.utc)
43
+
44
+ block0_ts = datetime.fromtimestamp(chain[0].timestamp, tz=timezone.utc)
45
+ helper = datetime(
46
+ year=block0_ts.year,
47
+ month=block0_ts.month,
48
+ day=block0_ts.day,
49
+ hour=block0_ts.hour,
50
+ minute=block0_ts.minute,
51
+ tzinfo=timezone.utc,
52
+ )
53
+ while helper + interval < start:
54
+ helper += interval
55
+ start = helper
56
+ if start < block0_ts:
57
+ start += interval
58
+
59
+ timestamp = start
60
+
61
+ timestamps = []
62
+ while timestamp <= datetime.now(tz=timezone.utc):
63
+ timestamps.append(timestamp)
64
+ timestamp = timestamp + interval
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
+
79
+ while run_forever:
80
+ while timestamp > datetime.now(tz=timezone.utc):
81
+ await _get_waiter(timestamp)
82
+ yield timestamp
83
+ timestamp += interval
84
+
85
+
86
+ _waiters: Dict[datetime, "Task[None]"] = {}
87
+
88
+
89
+ def _get_waiter(timestamp: datetime) -> "Task[None]":
90
+ if timestamp not in _waiters:
91
+ waiter = create_task(sleep_until(timestamp))
92
+ waiter.add_done_callback(_sleep_done_callback)
93
+ _waiters[timestamp] = waiter
94
+ return _waiters[timestamp]
95
+
96
+
97
+ async def sleep_until(until: datetime) -> None:
98
+ now = datetime.now(tz=timezone.utc)
99
+ await sleep((until - now).total_seconds())
100
+
101
+
102
+ def _sleep_done_callback(t: "Task[Any]") -> None:
103
+ low_to_hi = sorted(_waiters)
104
+ for k in low_to_hi:
105
+ if _waiters[k] is t:
106
+ _waiters.pop(k)
@@ -0,0 +1,57 @@
1
+ from argparse import Namespace
2
+ from datetime import datetime, timezone
3
+ from typing import Dict, Final, Optional
4
+
5
+ import a_sync
6
+ import a_sync.asyncio
7
+ from eth_typing import HexAddress
8
+
9
+ from eth_portfolio_scripts import docker
10
+ from eth_portfolio_scripts._utils import aiter_timestamps, parse_timedelta
11
+
12
+
13
+ _UTC: Final = timezone.utc
14
+
15
+ create_task: Final = a_sync.create_task
16
+ yield_to_loop: Final = a_sync.asyncio.sleep0
17
+
18
+
19
+ @docker.ensure_containers
20
+ async def export_balances(
21
+ args: Namespace,
22
+ custom_buckets: Optional[Dict[HexAddress, str]] = None,
23
+ ) -> None:
24
+ import dank_mids
25
+
26
+ from eth_portfolio_scripts._portfolio import ExportablePortfolio
27
+
28
+ if args.daemon is True:
29
+ raise NotImplementedError("This feature must be implemented")
30
+
31
+ interval = parse_timedelta(args.interval)
32
+ portfolio = ExportablePortfolio(
33
+ args.wallet,
34
+ label=args.label,
35
+ start_block=args.first_tx_block,
36
+ concurrency=args.concurrency,
37
+ custom_buckets=custom_buckets,
38
+ load_prices=False,
39
+ )
40
+
41
+ if export_start_block := args.export_start_block or args.first_tx_block:
42
+ start_ts = await dank_mids.eth.get_block_timestamp(export_start_block)
43
+ start = datetime.fromtimestamp(start_ts, tz=timezone.utc)
44
+ else:
45
+ start = None
46
+
47
+ print(f"Exporting {portfolio}")
48
+ async for ts in aiter_timestamps(start=start, interval=interval, run_forever=True):
49
+ create_task(
50
+ coro=portfolio.export_snapshot(ts, sync=False),
51
+ name=f"eth-portfolio export snapshot {ts}",
52
+ skip_gc_until_done=True,
53
+ )
54
+ # get some work in before yielding the next task
55
+ await yield_to_loop()
56
+ await yield_to_loop()
57
+ await yield_to_loop()