eth-portfolio-temp 0.0.39.dev0__cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.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. 295eace8438df6ec133b__mypyc.cpython-39-i386-linux-gnu.so +0 -0
  2. eth_portfolio/__init__.py +25 -0
  3. eth_portfolio/_argspec.cpython-39-i386-linux-gnu.so +0 -0
  4. eth_portfolio/_argspec.py +42 -0
  5. eth_portfolio/_cache.py +121 -0
  6. eth_portfolio/_config.cpython-39-i386-linux-gnu.so +0 -0
  7. eth_portfolio/_config.py +4 -0
  8. eth_portfolio/_db/__init__.py +0 -0
  9. eth_portfolio/_db/decorators.py +147 -0
  10. eth_portfolio/_db/entities.py +311 -0
  11. eth_portfolio/_db/utils.py +606 -0
  12. eth_portfolio/_decimal.py +155 -0
  13. eth_portfolio/_decorators.py +84 -0
  14. eth_portfolio/_exceptions.py +67 -0
  15. eth_portfolio/_ledgers/__init__.py +0 -0
  16. eth_portfolio/_ledgers/address.py +938 -0
  17. eth_portfolio/_ledgers/portfolio.py +327 -0
  18. eth_portfolio/_loaders/__init__.py +33 -0
  19. eth_portfolio/_loaders/_nonce.cpython-39-i386-linux-gnu.so +0 -0
  20. eth_portfolio/_loaders/_nonce.py +196 -0
  21. eth_portfolio/_loaders/balances.cpython-39-i386-linux-gnu.so +0 -0
  22. eth_portfolio/_loaders/balances.py +94 -0
  23. eth_portfolio/_loaders/token_transfer.py +217 -0
  24. eth_portfolio/_loaders/transaction.py +240 -0
  25. eth_portfolio/_loaders/utils.cpython-39-i386-linux-gnu.so +0 -0
  26. eth_portfolio/_loaders/utils.py +68 -0
  27. eth_portfolio/_shitcoins.cpython-39-i386-linux-gnu.so +0 -0
  28. eth_portfolio/_shitcoins.py +277 -0
  29. eth_portfolio/_stableish.cpython-39-i386-linux-gnu.so +0 -0
  30. eth_portfolio/_stableish.py +42 -0
  31. eth_portfolio/_submodules.py +73 -0
  32. eth_portfolio/_utils.py +223 -0
  33. eth_portfolio/_ydb/__init__.py +0 -0
  34. eth_portfolio/_ydb/token_transfers.py +150 -0
  35. eth_portfolio/address.py +396 -0
  36. eth_portfolio/buckets.py +194 -0
  37. eth_portfolio/constants.cpython-39-i386-linux-gnu.so +0 -0
  38. eth_portfolio/constants.py +82 -0
  39. eth_portfolio/portfolio.py +661 -0
  40. eth_portfolio/protocols/__init__.py +67 -0
  41. eth_portfolio/protocols/_base.py +108 -0
  42. eth_portfolio/protocols/convex.py +17 -0
  43. eth_portfolio/protocols/dsr.py +51 -0
  44. eth_portfolio/protocols/lending/README.md +6 -0
  45. eth_portfolio/protocols/lending/__init__.py +50 -0
  46. eth_portfolio/protocols/lending/_base.py +57 -0
  47. eth_portfolio/protocols/lending/compound.py +187 -0
  48. eth_portfolio/protocols/lending/liquity.py +110 -0
  49. eth_portfolio/protocols/lending/maker.py +105 -0
  50. eth_portfolio/protocols/lending/unit.py +46 -0
  51. eth_portfolio/protocols/liquity.py +16 -0
  52. eth_portfolio/py.typed +0 -0
  53. eth_portfolio/structs/__init__.py +43 -0
  54. eth_portfolio/structs/modified.py +69 -0
  55. eth_portfolio/structs/structs.py +637 -0
  56. eth_portfolio/typing/__init__.py +1447 -0
  57. eth_portfolio/typing/balance/single.py +176 -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 +181 -0
  62. eth_portfolio_scripts/_utils.py +92 -0
  63. eth_portfolio_scripts/balances.cpython-39-i386-linux-gnu.so +0 -0
  64. eth_portfolio_scripts/balances.py +49 -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__.cpython-39-i386-linux-gnu.so +0 -0
  69. eth_portfolio_scripts/docker/__init__.py +16 -0
  70. eth_portfolio_scripts/docker/check.cpython-39-i386-linux-gnu.so +0 -0
  71. eth_portfolio_scripts/docker/check.py +56 -0
  72. eth_portfolio_scripts/docker/docker-compose.yaml +61 -0
  73. eth_portfolio_scripts/docker/docker_compose.cpython-39-i386-linux-gnu.so +0 -0
  74. eth_portfolio_scripts/docker/docker_compose.py +78 -0
  75. eth_portfolio_scripts/main.py +113 -0
  76. eth_portfolio_scripts/py.typed +1 -0
  77. eth_portfolio_scripts/victoria/__init__.py +70 -0
  78. eth_portfolio_scripts/victoria/types.py +38 -0
  79. eth_portfolio_temp-0.0.39.dev0.dist-info/METADATA +25 -0
  80. eth_portfolio_temp-0.0.39.dev0.dist-info/RECORD +83 -0
  81. eth_portfolio_temp-0.0.39.dev0.dist-info/WHEEL +7 -0
  82. eth_portfolio_temp-0.0.39.dev0.dist-info/entry_points.txt +2 -0
  83. eth_portfolio_temp-0.0.39.dev0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,150 @@
1
+ from abc import abstractmethod
2
+ from asyncio import Task, create_task, sleep
3
+ from logging import DEBUG, getLogger
4
+ from typing import AsyncIterator, Final, List
5
+
6
+ import dank_mids
7
+ import evmspec
8
+ import y._db.log
9
+ from a_sync import ASyncIterable, ASyncIterator, as_yielded
10
+ from brownie import chain
11
+ from eth_typing import BlockNumber, ChecksumAddress
12
+ from faster_eth_utils import encode_hex
13
+ from y.utils.events import ProcessedEvents
14
+
15
+ from eth_portfolio._loaders import load_token_transfer
16
+ from eth_portfolio._shitcoins import SHITCOINS
17
+ from eth_portfolio.constants import TRANSFER_SIGS
18
+ from eth_portfolio.structs import TokenTransfer
19
+
20
+
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
+ logger: Final = getLogger(__name__)
32
+ _logger_is_enabled_for: Final = logger.isEnabledFor
33
+ _logger_log: Final = logger._log
34
+
35
+
36
+ class _TokenTransfers(ProcessedEvents["Task[TokenTransfer]"]):
37
+ """A helper mixin that contains all logic for fetching token transfers for a particular wallet address"""
38
+
39
+ __slots__ = "address", "_load_prices"
40
+
41
+ def __init__(
42
+ self,
43
+ address: ChecksumAddress,
44
+ from_block: BlockNumber,
45
+ load_prices: bool = False,
46
+ ) -> None:
47
+ self.address: Final = address
48
+ self._load_prices: Final = load_prices
49
+ super().__init__(topics=self._topics, from_block=from_block)
50
+
51
+ def __repr__(self) -> str:
52
+ return f"{self.__class__.__name__}(address={self.address})"
53
+
54
+ @property
55
+ @abstractmethod
56
+ def _topics(self) -> List: ...
57
+
58
+ @ASyncIterator.wrap # type: ignore [call-overload]
59
+ async def yield_thru_block(self, block) -> AsyncIterator["Task[TokenTransfer]"]:
60
+ if not _logger_is_enabled_for(DEBUG):
61
+ async for task in self._objects_thru(block=block):
62
+ yield task
63
+ return
64
+
65
+ _logger_log(DEBUG, "%s yielding all objects thru block %s", (self, block))
66
+ async for task in self._objects_thru(block=block):
67
+ _logger_log(
68
+ DEBUG,
69
+ "yielding %s at block %s [thru: %s, lock: %s]",
70
+ (task, task.block, block, self._lock.value),
71
+ )
72
+ yield task
73
+ _logger_log(DEBUG, "%s yield thru %s complete", (self, block))
74
+
75
+ async def _extend(self, objs: List[evmspec.Log]) -> None:
76
+ shitcoins = SHITCOINS.get(chain.id, set())
77
+ append_loader_task = self._objects.append
78
+ done = 0
79
+ for log in objs:
80
+ if log.address in shitcoins:
81
+ continue
82
+ # save i/o
83
+ array_encodable_log = y._db.log.Log(**log)
84
+ task = create_task(load_token_transfer(array_encodable_log, self._load_prices))
85
+ task.block = log.block # type: ignore [attr-defined]
86
+ append_loader_task(task)
87
+ done += 1
88
+ # Make sure the event loop doesn't get blocked
89
+ if done % 100 == 0:
90
+ await sleep(0)
91
+
92
+ def _get_block_for_obj(self, task: "Task[TokenTransfer]") -> int:
93
+ return task.block # type: ignore [attr-defined]
94
+
95
+ def _process_event(self, task: "Task[TokenTransfer]") -> "Task[TokenTransfer]":
96
+ return task
97
+
98
+ def _done_callback(self, task: Task) -> None:
99
+ if e := task.exception():
100
+ self._exc = e
101
+ logger.exception(e)
102
+ raise e
103
+
104
+
105
+ class InboundTokenTransfers(_TokenTransfers):
106
+ """A container that fetches and iterates over all inbound token transfers for a particular wallet address"""
107
+
108
+ @property
109
+ def _topics(self) -> List:
110
+ return [TRANSFER_SIGS, None, encode_address(self.address)]
111
+
112
+
113
+ class OutboundTokenTransfers(_TokenTransfers):
114
+ """A container that fetches and iterates over all outbound token transfers for a particular wallet address"""
115
+
116
+ @property
117
+ def _topics(self) -> List:
118
+ return [TRANSFER_SIGS, encode_address(self.address)]
119
+
120
+
121
+ class TokenTransfers(ASyncIterable[TokenTransfer]):
122
+ """
123
+ A container that fetches and iterates over all token transfers for a particular wallet address.
124
+ NOTE: These do not come back in chronologcal order.
125
+ """
126
+
127
+ def __init__(
128
+ self,
129
+ address: ChecksumAddress,
130
+ from_block: BlockNumber,
131
+ load_prices: bool = False,
132
+ ) -> None:
133
+ self.transfers_in: Final = InboundTokenTransfers(
134
+ address, from_block, load_prices=load_prices
135
+ )
136
+ self.transfers_out: Final = OutboundTokenTransfers(
137
+ address, from_block, load_prices=load_prices
138
+ )
139
+
140
+ async def __aiter__(self):
141
+ async for transfer in self.__yield_thru_block(await dank_mids.eth.block_number):
142
+ yield transfer
143
+
144
+ def yield_thru_block(self, block: int) -> ASyncIterator["Task[TokenTransfer]"]:
145
+ return ASyncIterator(self.__yield_thru_block(block))
146
+
147
+ def __yield_thru_block(self, block: int) -> AsyncIterator["Task[TokenTransfer]"]:
148
+ return as_yielded(
149
+ self.transfers_in.yield_thru_block(block), self.transfers_out.yield_thru_block(block)
150
+ )
@@ -0,0 +1,396 @@
1
+ """
2
+ This module defines the :class:`~PortfolioAddress` class, which represents an address managed by the `eth-portfolio` system.
3
+ The :class:`~PortfolioAddress` class is designed to manage different aspects of an Ethereum address within the portfolio,
4
+ such as transactions, transfers, balances, and interactions with both external and lending protocols.
5
+
6
+ Key components and functionalities provided by the :class:`~eth_portfolio.address.PortfolioAddress` class include:
7
+ - Handling Ethereum and token balances
8
+ - Managing debt and collateral from lending protocols
9
+ - Tracking transactions and transfers (both internal and token transfers)
10
+ - Providing comprehensive balance descriptions at specific block heights
11
+
12
+ The class leverages asynchronous operations using the `a_sync` library to efficiently gather and process data.
13
+ It also integrates with various submodules from `eth-portfolio` to load balances, manage ledgers, and interact
14
+ with external protocols.
15
+ """
16
+
17
+ import logging
18
+ from asyncio import Task, create_task, gather
19
+ from typing import Dict, Final, Optional, final
20
+
21
+ import a_sync
22
+ import dank_mids
23
+ import eth_retry
24
+ import y
25
+ from a_sync.exceptions import MappingIsEmptyError
26
+ from y import convert
27
+ from y._decorators import stuck_coro_debugger
28
+ from y.datatypes import Address, Block
29
+
30
+ from eth_portfolio import protocols
31
+ from eth_portfolio._ledgers.address import (
32
+ AddressInternalTransfersLedger,
33
+ AddressLedgerBase,
34
+ AddressTokenTransfersLedger,
35
+ AddressTransactionsLedger,
36
+ PandableLedgerEntryList,
37
+ )
38
+ from eth_portfolio._loaders import balances
39
+ from eth_portfolio._utils import _LedgeredBase, _get_price
40
+ from eth_portfolio.typing import Balance, RemoteTokenBalances, TokenBalances, WalletBalances
41
+
42
+
43
+ logger: Final = logging.getLogger(__name__)
44
+
45
+ checksum: Final = convert.to_address
46
+
47
+
48
+ @final
49
+ class PortfolioAddress(_LedgeredBase[AddressLedgerBase]):
50
+ """
51
+ Represents a portfolio address within the eth-portfolio system.
52
+
53
+ This class is designed to manage different aspects of an Ethereum address within the portfolio,
54
+ such as transactions, transfers, balances, and interactions with both external and lending protocols.
55
+
56
+ Key components and functionalities provided by the :class:`~eth_portfolio.address.PortfolioAddress` class include:
57
+ - Handling Ethereum and token balances
58
+ - Managing debt and collateral from lending protocols
59
+ - Tracking transactions and transfers (both internal and token transfers)
60
+ - Providing comprehensive balance descriptions at specific block heights
61
+
62
+ The class leverages asynchronous operations using the `a_sync` library to efficiently gather and process data.
63
+ It also integrates with various submodules from `eth-portfolio` to load balances, manage ledgers, and interact
64
+ with external protocols.
65
+ """
66
+
67
+ def __init__(
68
+ self,
69
+ address: Address,
70
+ start_block: Block,
71
+ load_prices: bool,
72
+ num_workers_transactions: int = 1000,
73
+ asynchronous: bool = False,
74
+ ) -> None: # type: ignore
75
+ """
76
+ Initializes the :class:`~PortfolioAddress` instance.
77
+
78
+ Args:
79
+ address: The Ethereum address to manage.
80
+ start_block: The block number from which to start tracking.
81
+ load_prices: Flag indicating if price loading is enabled.
82
+ num_workers_transactions (optional): Number of workers for transaction processing. Defaults to 1000.
83
+ asynchronous (optional): Flag for asynchronous operation. Defaults to False.
84
+
85
+ Raises:
86
+ TypeError: If `asynchronous` is not a boolean.
87
+
88
+ Examples:
89
+ >>> address = PortfolioAddress('0x1234...', 0, True)
90
+ >>> print(address)
91
+
92
+ >>> address = PortfolioAddress('0x1234...', 0, False, num_workers_transactions=500, asynchronous=True)
93
+ >>> print(address)
94
+
95
+ See Also:
96
+ - :class:`~eth_portfolio.portfolio.Portfolio`
97
+ - :class:`~eth_portfolio._ledgers.address.AddressTransactionsLedger`
98
+ - :class:`~eth_portfolio._ledgers.address.AddressInternalTransfersLedger`
99
+ - :class:`~eth_portfolio._ledgers.address.AddressTokenTransfersLedger`
100
+ """
101
+ self.address: Final = convert.to_address(address)
102
+ """
103
+ The address being managed.
104
+ """
105
+ if not isinstance(asynchronous, bool):
106
+ raise TypeError(f"`asynchronous` must be a boolean, you passed {type(asynchronous)}")
107
+
108
+ self.asynchronous: Final = asynchronous
109
+ """
110
+ Flag indicating if the operations are asynchronous.
111
+ """
112
+
113
+ self.load_prices: Final = load_prices
114
+ """
115
+ Indicates if price loading is enabled.
116
+ """
117
+
118
+ super().__init__(start_block)
119
+
120
+ self.transactions: Final = AddressTransactionsLedger(self, num_workers_transactions) # type: ignore [misc]
121
+ """
122
+ Ledger for tracking transactions.
123
+ """
124
+
125
+ self.internal_transfers: Final = AddressInternalTransfersLedger(self) # type: ignore [misc]
126
+ """
127
+ Ledger for tracking internal transfers.
128
+ """
129
+
130
+ self.token_transfers: Final = AddressTokenTransfersLedger(self) # type: ignore [misc]
131
+ """
132
+ Ledger for tracking token transfers.
133
+ """
134
+
135
+ def __str__(self) -> str:
136
+ """
137
+ Returns the string representation of the address.
138
+
139
+ Returns:
140
+ The address as a string.
141
+ """
142
+ return self.address
143
+
144
+ def __repr__(self) -> str:
145
+ """
146
+ Returns the string representation of the PortfolioAddress instance.
147
+
148
+ Returns:
149
+ The string representation.
150
+ """
151
+ return f"<{self.__class__.__name__} address={self.address} at {hex(id(self))}>"
152
+
153
+ def __eq__(self, other: object) -> bool:
154
+ """
155
+ Checks equality with another object.
156
+
157
+ Args:
158
+ other: The object to compare with.
159
+
160
+ Returns:
161
+ True if equal, False otherwise.
162
+ """
163
+ if isinstance(other, PortfolioAddress):
164
+ return self.address == other.address
165
+ elif isinstance(other, str):
166
+ return self.address == checksum(other)
167
+ return False
168
+
169
+ def __hash__(self) -> int:
170
+ """
171
+ Returns the hash of the address.
172
+
173
+ Returns:
174
+ The hash value.
175
+ """
176
+ return hash(self.address)
177
+
178
+ # Primary functions
179
+
180
+ @stuck_coro_debugger
181
+ async def describe(self, block: int) -> WalletBalances:
182
+ """
183
+ Describes all of the wallet's balances at a given block.
184
+
185
+ Args:
186
+ block: The block number.
187
+
188
+ Returns:
189
+ :class:`~eth_portfolio.typing.WalletBalances`: The wallet balances.
190
+
191
+ Raises:
192
+ TypeError: If block is not an integer.
193
+
194
+ Examples:
195
+ >>> wallet_balances = await address.describe(12345678)
196
+ """
197
+ if not isinstance(block, int):
198
+ raise TypeError(f"Block must be an integer. You passed {type(block)} {block}")
199
+ coros = {
200
+ "assets": self.assets(block, sync=False),
201
+ "debt": self.debt(block, sync=False),
202
+ "external": self.external_balances(block, sync=False),
203
+ }
204
+ return WalletBalances(await a_sync.gather(coros), block=block) # type: ignore [arg-type]
205
+
206
+ @stuck_coro_debugger
207
+ async def assets(self, block: Optional[Block] = None) -> TokenBalances:
208
+ """
209
+ Retrieves the balances for every asset in the wallet at a given block.
210
+
211
+ Args:
212
+ block (optional): The block number to query. Defaults to None, which uses the latest block.
213
+
214
+ Returns:
215
+ :class:`~eth_portfolio.typing.TokenBalances`: The asset balances at `block`.
216
+
217
+ Examples:
218
+ >>> assets = await address.assets(12345678)
219
+ """
220
+ return await self.balances(block=block, sync=False) # type: ignore [return-value]
221
+
222
+ @stuck_coro_debugger
223
+ async def debt(self, block: Optional[Block] = None) -> RemoteTokenBalances:
224
+ """
225
+ Retrieves all debt balances for the wallet at a given block.
226
+
227
+ Args:
228
+ block (optional): The block number. Defaults to None, which uses the latest block.
229
+
230
+ Returns:
231
+ :class:`~eth_portfolio.typing.RemoteTokenBalances`: The debt balances at `block`.
232
+
233
+ Examples:
234
+ >>> debt = await address.debt(12345678)
235
+ """
236
+ return await protocols.lending.debt(self.address, block=block)
237
+
238
+ @stuck_coro_debugger
239
+ async def external_balances(self, block: Optional[Block] = None) -> RemoteTokenBalances:
240
+ """
241
+ Retrieves the balances owned by the wallet, but not held *in* the wallet, at a given block.
242
+
243
+ Args:
244
+ block (optional): The block number. Defaults to None, which uses the latest block.
245
+
246
+ Returns:
247
+ :class:`~eth_portfolio.typing.RemoteTokenBalances`: The external balances.
248
+
249
+ Examples:
250
+ >>> external_balances = await address.external_balances(12345678)
251
+ """
252
+ staking: "Task[RemoteTokenBalances]"
253
+ collateral: RemoteTokenBalances
254
+
255
+ staking = create_task(self.staking(block, sync=False)) # type: ignore [arg-type]
256
+ try:
257
+ collateral = await self.collateral(block, sync=False) # type: ignore [assignment]
258
+ except:
259
+ staking.cancel()
260
+ raise
261
+ else:
262
+ return collateral + await staking
263
+
264
+ # Assets
265
+
266
+ @stuck_coro_debugger
267
+ async def balances(self, block: Optional[Block]) -> TokenBalances:
268
+ """
269
+ Retrieves balances for all assets in the wallet at a given block.
270
+
271
+ Args:
272
+ block: The block number.
273
+
274
+ Returns:
275
+ :class:`~eth_portfolio.typing.TokenBalances`: The balances.
276
+
277
+ Examples:
278
+ >>> balances = await address.balances(12345678)
279
+ """
280
+ eth_balance, token_balances = await gather(
281
+ self.eth_balance(block, sync=False),
282
+ self.token_balances(block, sync=False),
283
+ )
284
+ token_balances[y.EEE_ADDRESS] = eth_balance # type: ignore [call-overload]
285
+ return token_balances # type: ignore [return-value]
286
+
287
+ @eth_retry.auto_retry
288
+ @stuck_coro_debugger
289
+ async def eth_balance(self, block: Optional[Block]) -> Balance:
290
+ """
291
+ Retrieves the ETH balance for the wallet at a given block.
292
+
293
+ Args:
294
+ block: The block number.
295
+
296
+ Returns:
297
+ :class:`~eth_portfolio.typing.Balance`: The ETH balance at `block`.
298
+
299
+ Examples:
300
+ >>> eth_balance = await address.eth_balance(12345678)
301
+ """
302
+ if balance := await dank_mids.eth.get_balance(
303
+ self.address, block_identifier=block # type: ignore [arg-type]
304
+ ): # TODO: move hex into dank
305
+ price = await _get_price(y.WRAPPED_GAS_COIN, block)
306
+ return Balance(
307
+ balance.scaled,
308
+ round(balance.scaled * price, 18),
309
+ token=y.EEE_ADDRESS,
310
+ block=block,
311
+ )
312
+ return Balance(token=y.EEE_ADDRESS, block=block)
313
+
314
+ @stuck_coro_debugger
315
+ async def token_balances(self, block) -> TokenBalances:
316
+ """
317
+ Retrieves the balances for all tokens in the wallet at a given block.
318
+
319
+ Args:
320
+ block: The block number.
321
+
322
+ Returns:
323
+ :class:`~eth_portfolio.typing.TokenBalances`: The token balances at `block`.
324
+
325
+ Examples:
326
+ >>> token_balances = await address.token_balances(12345678)
327
+ """
328
+ try:
329
+ data = a_sync.map(
330
+ balances.load_token_balance,
331
+ self.token_transfers._yield_tokens_at_block(block=block),
332
+ address=self.address,
333
+ block=block,
334
+ )
335
+ return TokenBalances(await data, block=block)
336
+ except MappingIsEmptyError:
337
+ return TokenBalances(block=block)
338
+
339
+ @stuck_coro_debugger
340
+ async def collateral(self, block: Optional[Block] = None) -> RemoteTokenBalances:
341
+ """
342
+ Retrieves all balances held by lending protocols on behalf of the wallet at a given block.
343
+
344
+ Args:
345
+ block (optional): The block number. Defaults to None, which uses the latest block.
346
+
347
+ Returns:
348
+ :class:`~eth_portfolio.typing.RemoteTokenBalances`: The collateral balances.
349
+
350
+ Examples:
351
+ >>> collateral = await address.collateral(12345678)
352
+ """
353
+ return await protocols.lending.collateral(self.address, block=block)
354
+
355
+ @stuck_coro_debugger
356
+ async def staking(self, block: Optional[Block] = None) -> RemoteTokenBalances:
357
+ """
358
+ Retrieves all balances staked in protocols supported by eth_portfolio on behalf of the wallet at a given block.
359
+
360
+ Args:
361
+ block (optional): The block number. Defaults to None, which uses the latest block.
362
+
363
+ Returns:
364
+ :class:`~eth_portfolio.typing.RemoteTokenBalances`: The staked balances.
365
+
366
+ Examples:
367
+ >>> staking_balances = await address.staking(12345678)
368
+ """
369
+ return await protocols.balances(self.address, block=block)
370
+
371
+ # Ledger Entries
372
+
373
+ @stuck_coro_debugger
374
+ async def all(self, start_block: Block, end_block: Block) -> Dict[str, PandableLedgerEntryList]:
375
+ """
376
+ Retrieves all ledger entries between two blocks.
377
+
378
+ Args:
379
+ start_block: The starting block number.
380
+ end_block: The ending block number.
381
+
382
+ Returns:
383
+ Dict[str, :class:`~eth_portfolio._ledgers.address.PandableLedgerEntryList`]: The ledger entries.
384
+
385
+ Examples:
386
+ >>> all_entries = await address.all(12000000, 12345678)
387
+ """
388
+ return await a_sync.gather(
389
+ {
390
+ "transactions": self.transactions.get(start_block, end_block, sync=False),
391
+ "internal_transactions": self.internal_transfers.get(
392
+ start_block, end_block, sync=False
393
+ ),
394
+ "token_transfers": self.token_transfers.get(start_block, end_block, sync=False),
395
+ }
396
+ )