eth-portfolio-temp 0.3.0__cp313-cp313-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.

Files changed (83) hide show
  1. eth_portfolio/__init__.py +25 -0
  2. eth_portfolio/_argspec.cp313-win_amd64.pyd +0 -0
  3. eth_portfolio/_argspec.py +42 -0
  4. eth_portfolio/_cache.py +121 -0
  5. eth_portfolio/_config.cp313-win_amd64.pyd +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 +604 -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 +938 -0
  16. eth_portfolio/_ledgers/portfolio.py +327 -0
  17. eth_portfolio/_loaders/__init__.py +33 -0
  18. eth_portfolio/_loaders/_nonce.cp313-win_amd64.pyd +0 -0
  19. eth_portfolio/_loaders/_nonce.py +196 -0
  20. eth_portfolio/_loaders/balances.cp313-win_amd64.pyd +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 +240 -0
  24. eth_portfolio/_loaders/utils.cp313-win_amd64.pyd +0 -0
  25. eth_portfolio/_loaders/utils.py +68 -0
  26. eth_portfolio/_shitcoins.cp313-win_amd64.pyd +0 -0
  27. eth_portfolio/_shitcoins.py +330 -0
  28. eth_portfolio/_stableish.cp313-win_amd64.pyd +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 +145 -0
  34. eth_portfolio/address.py +397 -0
  35. eth_portfolio/buckets.py +212 -0
  36. eth_portfolio/constants.cp313-win_amd64.pyd +0 -0
  37. eth_portfolio/constants.py +82 -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 +104 -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 +1447 -0
  56. eth_portfolio/typing/balance/single.py +176 -0
  57. eth_portfolio__mypyc.cp313-win_amd64.pyd +0 -0
  58. eth_portfolio_scripts/__init__.py +20 -0
  59. eth_portfolio_scripts/_args.py +26 -0
  60. eth_portfolio_scripts/_logging.py +15 -0
  61. eth_portfolio_scripts/_portfolio.py +209 -0
  62. eth_portfolio_scripts/_utils.py +106 -0
  63. eth_portfolio_scripts/balances.cp313-win_amd64.pyd +0 -0
  64. eth_portfolio_scripts/balances.py +52 -0
  65. eth_portfolio_scripts/docker/.grafana/dashboards/Portfolio/Balances.json +1962 -0
  66. eth_portfolio_scripts/docker/.grafana/dashboards/dashboards.yaml +10 -0
  67. eth_portfolio_scripts/docker/.grafana/datasources/datasources.yml +11 -0
  68. eth_portfolio_scripts/docker/__init__.cp313-win_amd64.pyd +0 -0
  69. eth_portfolio_scripts/docker/__init__.py +16 -0
  70. eth_portfolio_scripts/docker/check.cp313-win_amd64.pyd +0 -0
  71. eth_portfolio_scripts/docker/check.py +67 -0
  72. eth_portfolio_scripts/docker/docker-compose.yaml +61 -0
  73. eth_portfolio_scripts/docker/docker_compose.cp313-win_amd64.pyd +0 -0
  74. eth_portfolio_scripts/docker/docker_compose.py +98 -0
  75. eth_portfolio_scripts/main.py +119 -0
  76. eth_portfolio_scripts/py.typed +1 -0
  77. eth_portfolio_scripts/victoria/__init__.py +73 -0
  78. eth_portfolio_scripts/victoria/types.py +38 -0
  79. eth_portfolio_temp-0.3.0.dist-info/METADATA +26 -0
  80. eth_portfolio_temp-0.3.0.dist-info/RECORD +83 -0
  81. eth_portfolio_temp-0.3.0.dist-info/WHEEL +5 -0
  82. eth_portfolio_temp-0.3.0.dist-info/entry_points.txt +2 -0
  83. eth_portfolio_temp-0.3.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,212 @@
1
+ import logging
2
+ from typing import Any, Final, Optional, Set, Dict
3
+
4
+ from a_sync import igather
5
+ from eth_typing import ChecksumAddress
6
+ from faster_async_lru import alru_cache
7
+ from y.constants import CHAINID, STABLECOINS, WRAPPED_GAS_COIN
8
+ from y.convert import to_address
9
+ from y.datatypes import Address, AnyAddressType
10
+ from y.exceptions import ContractNotVerified
11
+ from y.prices.lending.aave import aave
12
+ from y.prices.lending.compound import CToken, compound
13
+ from y.prices.stable_swap.curve import curve
14
+ from y.prices.yearn import YearnInspiredVault, is_yearn_vault
15
+
16
+ from eth_portfolio.constants import BTC_LIKE, ETH_LIKE, INTL_STABLECOINS
17
+ from eth_portfolio._stableish import STABLEISH_COINS
18
+
19
+ logger: Final = logging.getLogger(__name__)
20
+ log_debug: Final = logger.debug
21
+
22
+ SORT_AS_STABLES: Final = STABLECOINS.keys() | STABLEISH_COINS[CHAINID]
23
+ OTHER_LONG_TERM_ASSETS: Final[Set[ChecksumAddress]] = {}.get(CHAINID, set()) # type: ignore [call-overload]
24
+
25
+
26
+ async def get_token_bucket(
27
+ token: AnyAddressType, custom_buckets: Optional[Dict[str, str]] = None
28
+ ) -> str:
29
+ """
30
+ Categorize a token into a specific bucket based on its type.
31
+
32
+ This function attempts to categorize a given token into predefined buckets
33
+ such as "Cash & cash equivalents", "ETH", "BTC", "Other long term assets",
34
+ or "Other short term assets". The categorization is based on the token's
35
+ characteristics and its presence in specific sets like :data:`ETH_LIKE`, :data:`BTC_LIKE`,
36
+ and :data:`OTHER_LONG_TERM_ASSETS`.
37
+
38
+ Args:
39
+ token: The address of the token to categorize.
40
+ custom_buckets: Optional mapping of token_address (lowercase) to bucket name.
41
+ If provided, after unwrapping the token, the function will check if the
42
+ unwrapped token address (lowercased) is present in this mapping and, if so,
43
+ return the mapped bucket name instead of using the default categorization logic.
44
+
45
+ Returns:
46
+ A string representing the bucket category of the token.
47
+
48
+ Raises:
49
+ ValueError: If the token's source has not been verified and the error message
50
+ does not match the expected pattern.
51
+
52
+ Example:
53
+ Categorize a stablecoin:
54
+
55
+ >>> await get_token_bucket("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
56
+ 'Cash & cash equivalents'
57
+
58
+ Categorize an ETH-like token:
59
+
60
+ >>> await get_token_bucket("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE")
61
+ 'ETH'
62
+
63
+ Use a custom mapping:
64
+
65
+ >>> custom_buckets = {"0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "My Stablecoin Bucket"}
66
+ >>> await get_token_bucket("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", custom_buckets=custom_buckets)
67
+ 'My Stablecoin Bucket'
68
+
69
+ See Also:
70
+ - :func:`_unwrap_token`
71
+ - :func:`_is_stable`
72
+ """
73
+ token_address = to_address(token)
74
+ try:
75
+ token_address = await _unwrap_token(token_address)
76
+ except ContractNotVerified as e:
77
+ return "Other short term assets"
78
+
79
+ # Check custom mapping AFTER unwrapping
80
+ if custom_buckets:
81
+ custom_bucket = custom_buckets.get(str(token_address).lower())
82
+ if custom_bucket is not None:
83
+ return custom_bucket
84
+
85
+ if _is_stable(token_address):
86
+ return "Cash & cash equivalents"
87
+ if token_address in ETH_LIKE:
88
+ return "ETH"
89
+ if token_address in BTC_LIKE:
90
+ return "BTC"
91
+ if token_address in OTHER_LONG_TERM_ASSETS:
92
+ return "Other long term assets"
93
+ return "Other short term assets"
94
+
95
+
96
+ @alru_cache(maxsize=None)
97
+ async def _unwrap_token(token: Any) -> ChecksumAddress:
98
+ """
99
+ Recursively unwrap a token to its underlying asset.
100
+
101
+ This function attempts to unwrap a given token to its underlying asset by
102
+ checking if the token is a Yearn vault, a Curve pool, an Aave aToken, or a
103
+ Compound market. It recursively retrieves the underlying asset until it
104
+ reaches the base token.
105
+
106
+ Args:
107
+ token: The address of the token to unwrap.
108
+
109
+ Returns:
110
+ The address of the underlying asset.
111
+
112
+ Example:
113
+ Unwrap a Yearn vault token:
114
+
115
+ >>> await _unwrap_token("0x5f18C75AbDAe578b483E5F43f12a39cF75b973a9")
116
+ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
117
+
118
+ See Also:
119
+ - :func:`y.prices.yearn.is_yearn_vault`
120
+ - :class:`y.prices.yearn.YearnInspiredVault`
121
+ - :class:`y.prices.stable_swap.curve`
122
+ - :class:`y.prices.lending.aave`
123
+ - :class:`y.prices.lending.compound.CToken`
124
+ """
125
+ log_debug("unwrapping %s", token)
126
+ if str(token) in {"ETH", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"}:
127
+ log_debug("returning eee address")
128
+ return token
129
+
130
+ if await is_yearn_vault(token, sync=False):
131
+ underlying = await YearnInspiredVault(token, asynchronous=True).underlying
132
+ log_debug("underlying: %s", underlying)
133
+ return await _unwrap_token(underlying)
134
+ if curve and (pool := await curve.get_pool(token)):
135
+ pool_tokens = set(await igather(map(_unwrap_token, await pool.coins)))
136
+ log_debug("pool_tokens: %s", pool_tokens)
137
+ if pool_bucket := _pool_bucket(pool_tokens):
138
+ log_debug("returning pool bucket: %s", pool_bucket)
139
+ return pool_bucket # type: ignore
140
+ if aave and await aave.is_atoken(token):
141
+ log_debug("atoken")
142
+ return str(await aave.underlying(token))
143
+ if compound and await compound.is_compound_market(token):
144
+ log_debug("unwrapping ctoken %s", token)
145
+ try:
146
+ return str(await CToken(token, asynchronous=True).underlying)
147
+ except AttributeError:
148
+ return WRAPPED_GAS_COIN
149
+ log_debug("returning: %s", token)
150
+ return token
151
+
152
+
153
+ def _pool_bucket(pool_tokens: set) -> Optional[str]:
154
+ """
155
+ Determine the bucket for a set of pool tokens.
156
+
157
+ This function checks if a set of pool tokens belongs to specific categories
158
+ such as BTC-like, ETH-like, or stablecoins, and returns the corresponding
159
+ bucket.
160
+
161
+ Args:
162
+ pool_tokens: A set of token addresses representing the pool tokens.
163
+
164
+ Returns:
165
+ A string representing the bucket category of the pool tokens, or None if
166
+ no specific category is found.
167
+
168
+ Example:
169
+ Determine the bucket for a BTC-like pool:
170
+
171
+ >>> _pool_bucket({"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"})
172
+ '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'
173
+
174
+ See Also:
175
+ - :data:`BTC_LIKE`
176
+ - :data:`ETH_LIKE`
177
+ - :data:`STABLECOINS`
178
+ - :data:`INTL_STABLECOINS`
179
+ """
180
+ log_debug("Pool tokens: %s", pool_tokens)
181
+ if pool_tokens < BTC_LIKE:
182
+ return list(BTC_LIKE)[0]
183
+ if pool_tokens < ETH_LIKE:
184
+ return list(ETH_LIKE)[0]
185
+ if pool_tokens < SORT_AS_STABLES:
186
+ return list(SORT_AS_STABLES)[0]
187
+ return list(INTL_STABLECOINS)[0] if pool_tokens < INTL_STABLECOINS else None
188
+
189
+
190
+ def _is_stable(token: ChecksumAddress) -> bool:
191
+ """
192
+ Check if a token is a stablecoin or stable-ish coin.
193
+
194
+ This function checks if a given token is present in the :data:`STABLECOINS`,
195
+ :data:`INTL_STABLECOINS`, or :data:`STABLEISH_COINS` sets, indicating that it is
196
+ a stablecoin or considered stable by the wider market.
197
+
198
+ Args:
199
+ token: The address of the token to check.
200
+
201
+ Example:
202
+ Check if a token is a stablecoin or stable-ish coin:
203
+
204
+ >>> _is_stable("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")
205
+ True
206
+
207
+ See Also:
208
+ - :data:`STABLECOINS`
209
+ - :data:`INTL_STABLECOINS`
210
+ - :data:`STABLEISH_COINS`
211
+ """
212
+ return token in SORT_AS_STABLES or token in INTL_STABLECOINS
@@ -0,0 +1,82 @@
1
+ import os
2
+ from typing import Final
3
+
4
+ from brownie import chain
5
+ from y import Network, convert, weth
6
+
7
+ ERC20_TRANSFER_EVENT_HASH: Final = (
8
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
9
+ )
10
+ ERC677_TRANSFER_EVENT_HASH: Final = (
11
+ "0xe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16"
12
+ )
13
+
14
+ NUM_WALLETS: Final = int(os.environ.get("NUM_WALLETS", "10"))
15
+ ADDRESSES: Final = [
16
+ convert.to_address(address)
17
+ for address in {os.environ.get(f"PORTFOLIO_ADDRESS_{i}", None) for i in range(NUM_WALLETS)}
18
+ if address is not None
19
+ ]
20
+
21
+ TRANSFER_SIGS: Final = [ERC20_TRANSFER_EVENT_HASH, ERC677_TRANSFER_EVENT_HASH]
22
+
23
+ ETH_LIKE: Final = {
24
+ Network.Mainnet: {
25
+ "ETH",
26
+ weth.address,
27
+ "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
28
+ "0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb", # seth
29
+ "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", # eth
30
+ "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", # steth
31
+ "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", # wstETH
32
+ "0x9559Aaa82d9649C7A7b220E7c461d2E74c9a3593", # reth
33
+ "0xE95A203B1a91a908F9B9CE46459d101078c2c3cb", # ankreth
34
+ "0x04C154b66CB340F3Ae24111CC767e0184Ed00Cc6", # pxETH
35
+ "0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3", # oETH
36
+ "0x0100546F2cD4C9D97f798fFC9755E47865FF7Ee6", # alETH
37
+ "0x1BED97CBC3c24A4fb5C069C6E311a967386131f7", # yETH
38
+ "0x24Ae2dA0f361AA4BE46b48EB19C91e02c5e4f27E", # mevETH
39
+ "0x5E8422345238F34275888049021821E8E08CAa1f", # frxETH
40
+ "0x821A278dFff762c76410264303F25bF42e195C0C", # pETH
41
+ "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", # cbETH
42
+ "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee", # weETH
43
+ "0x7C07F7aBe10CE8e33DC6C5aD68FE033085256A84", # icETH
44
+ },
45
+ }.get(chain.id, set())
46
+
47
+ BTC_LIKE: Final = {
48
+ Network.Mainnet: {
49
+ "0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D", # renbtc
50
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", # wbtc
51
+ "0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6", # sbtc
52
+ "0x8064d9Ae6cDf087b1bcd5BDf3531bD5d8C537a68", # obtc
53
+ "0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541", # bbtc
54
+ "0x0316EB71485b0Ab14103307bf65a021042c6d380", # hbtc
55
+ "0x5228a22e72ccC52d415EcFd199F99D0665E7733b", # pbtc
56
+ "0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa", # tbtc
57
+ "0x66eFF5221ca926636224650Fd3B9c497FF828F7D", # multiBTC
58
+ "0x657e8C867D8B37dCC18fA4Caead9C45EB088C642", # eBTC
59
+ },
60
+ }.get(chain.id, set())
61
+
62
+ INTL_STABLECOINS: Final = {
63
+ Network.Mainnet: {
64
+ "0xD71eCFF9342A5Ced620049e616c5035F1dB98620", # sEUR
65
+ "0xC581b735A1688071A1746c968e0798D642EDE491", # EURT
66
+ "0xdB25f211AB05b1c97D595516F45794528a807ad8", # EURS
67
+ "0x96E61422b6A9bA0e068B6c5ADd4fFaBC6a4aae27", # ibEUR
68
+ "0x9fcf418B971134625CdF38448B949C8640971671", # EURN
69
+ "0x39b8B6385416f4cA36a20319F70D28621895279D", # EURe
70
+ "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c", # EURC
71
+ "0x3F1B0278A9ee595635B61817630cC19DE792f506", # sAUD
72
+ "0xFAFdF0C4c1CB09d430Bf88c75D88BB46DAe09967", # ibAUD
73
+ "0x97fe22E7341a0Cd8Db6F6C021A24Dc8f4DAD855F", # sGBP
74
+ "0x69681f8fde45345C3870BCD5eaf4A05a60E7D227", # ibGBP
75
+ "0xF6b1C627e95BFc3c1b4c9B825a032Ff0fBf3e07d", # sJPY
76
+ "0x5555f75e3d5278082200Fb451D1b6bA946D8e13b", # ibJPY
77
+ "0x0F83287FF768D1c1e17a42F44d644D7F22e8ee1d", # sCHF
78
+ "0x1CC481cE2BD2EC7Bf67d1Be64d4878b16078F309", # ibCHF
79
+ "0x269895a3dF4D73b077Fc823dD6dA1B95f72Aaf9B", # sKRW
80
+ "0x95dFDC8161832e4fF7816aC4B6367CE201538253", # ibKRW
81
+ },
82
+ }.get(chain.id, set())