eth-portfolio 0.5.7__cp312-cp312-win32.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 might be problematic. Click here for more details.

Files changed (83) hide show
  1. eth_portfolio/__init__.py +24 -0
  2. eth_portfolio/_argspec.cp312-win32.pyd +0 -0
  3. eth_portfolio/_argspec.py +43 -0
  4. eth_portfolio/_cache.py +119 -0
  5. eth_portfolio/_config.cp312-win32.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 +616 -0
  11. eth_portfolio/_decimal.py +154 -0
  12. eth_portfolio/_decorators.py +84 -0
  13. eth_portfolio/_exceptions.py +65 -0
  14. eth_portfolio/_ledgers/__init__.py +0 -0
  15. eth_portfolio/_ledgers/address.py +924 -0
  16. eth_portfolio/_ledgers/portfolio.py +328 -0
  17. eth_portfolio/_loaders/__init__.py +33 -0
  18. eth_portfolio/_loaders/_nonce.cp312-win32.pyd +0 -0
  19. eth_portfolio/_loaders/_nonce.py +193 -0
  20. eth_portfolio/_loaders/balances.cp312-win32.pyd +0 -0
  21. eth_portfolio/_loaders/balances.py +95 -0
  22. eth_portfolio/_loaders/token_transfer.py +215 -0
  23. eth_portfolio/_loaders/transaction.py +240 -0
  24. eth_portfolio/_loaders/utils.cp312-win32.pyd +0 -0
  25. eth_portfolio/_loaders/utils.py +67 -0
  26. eth_portfolio/_shitcoins.cp312-win32.pyd +0 -0
  27. eth_portfolio/_shitcoins.py +342 -0
  28. eth_portfolio/_stableish.cp312-win32.pyd +0 -0
  29. eth_portfolio/_stableish.py +42 -0
  30. eth_portfolio/_submodules.py +72 -0
  31. eth_portfolio/_utils.py +215 -0
  32. eth_portfolio/_ydb/__init__.py +0 -0
  33. eth_portfolio/_ydb/token_transfers.py +145 -0
  34. eth_portfolio/address.py +396 -0
  35. eth_portfolio/buckets.py +212 -0
  36. eth_portfolio/constants.cp312-win32.pyd +0 -0
  37. eth_portfolio/constants.py +87 -0
  38. eth_portfolio/portfolio.py +662 -0
  39. eth_portfolio/protocols/__init__.py +64 -0
  40. eth_portfolio/protocols/_base.py +107 -0
  41. eth_portfolio/protocols/convex.py +17 -0
  42. eth_portfolio/protocols/dsr.py +50 -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 +56 -0
  46. eth_portfolio/protocols/lending/compound.py +186 -0
  47. eth_portfolio/protocols/lending/liquity.py +108 -0
  48. eth_portfolio/protocols/lending/maker.py +110 -0
  49. eth_portfolio/protocols/lending/unit.py +44 -0
  50. eth_portfolio/protocols/liquity.py +17 -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 +626 -0
  55. eth_portfolio/typing/__init__.py +1418 -0
  56. eth_portfolio/typing/balance/single.py +176 -0
  57. eth_portfolio-0.5.7.dist-info/METADATA +26 -0
  58. eth_portfolio-0.5.7.dist-info/RECORD +83 -0
  59. eth_portfolio-0.5.7.dist-info/WHEEL +5 -0
  60. eth_portfolio-0.5.7.dist-info/entry_points.txt +2 -0
  61. eth_portfolio-0.5.7.dist-info/top_level.txt +3 -0
  62. eth_portfolio__mypyc.cp312-win32.pyd +0 -0
  63. eth_portfolio_scripts/__init__.py +17 -0
  64. eth_portfolio_scripts/_args.py +26 -0
  65. eth_portfolio_scripts/_logging.py +14 -0
  66. eth_portfolio_scripts/_portfolio.py +209 -0
  67. eth_portfolio_scripts/_utils.py +106 -0
  68. eth_portfolio_scripts/balances.cp312-win32.pyd +0 -0
  69. eth_portfolio_scripts/balances.py +56 -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__.cp312-win32.pyd +0 -0
  74. eth_portfolio_scripts/docker/__init__.py +16 -0
  75. eth_portfolio_scripts/docker/check.cp312-win32.pyd +0 -0
  76. eth_portfolio_scripts/docker/check.py +66 -0
  77. eth_portfolio_scripts/docker/docker-compose.yaml +61 -0
  78. eth_portfolio_scripts/docker/docker_compose.cp312-win32.pyd +0 -0
  79. eth_portfolio_scripts/docker/docker_compose.py +97 -0
  80. eth_portfolio_scripts/main.py +118 -0
  81. eth_portfolio_scripts/py.typed +1 -0
  82. eth_portfolio_scripts/victoria/__init__.py +72 -0
  83. eth_portfolio_scripts/victoria/types.py +38 -0
@@ -0,0 +1,145 @@
1
+ from abc import abstractmethod
2
+ from asyncio import Task, create_task, sleep
3
+ from collections.abc import AsyncIterator
4
+ from logging import DEBUG, getLogger
5
+ from typing import Any, Final
6
+
7
+ import dank_mids
8
+ import evmspec
9
+ import y._db.log
10
+ from a_sync import ASyncIterable, ASyncIterator, as_yielded
11
+ from brownie import chain
12
+ from eth_typing import BlockNumber, ChecksumAddress
13
+ from faster_eth_abi import encode
14
+ from faster_eth_utils import encode_hex
15
+ from y.utils.events import ProcessedEvents
16
+
17
+ from eth_portfolio._loaders import load_token_transfer
18
+ from eth_portfolio._shitcoins import SHITCOINS
19
+ from eth_portfolio.constants import TRANSFER_SIGS
20
+ from eth_portfolio.structs import TokenTransfer
21
+
22
+ logger: Final = getLogger(__name__)
23
+ _logger_is_enabled_for: Final = logger.isEnabledFor
24
+ _logger_log: Final = logger._log
25
+
26
+
27
+ def encode_address(address: Any) -> bytes:
28
+ return encode_hex(encode(["address"], [str(address)]))
29
+
30
+
31
+ class _TokenTransfers(ProcessedEvents["Task[TokenTransfer]"]):
32
+ """A helper mixin that contains all logic for fetching token transfers for a particular wallet address"""
33
+
34
+ __slots__ = "address", "_load_prices"
35
+
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
+ super().__init__(topics=self._topics, from_block=from_block)
45
+
46
+ def __repr__(self) -> str:
47
+ return f"{self.__class__.__name__}(address={self.address})"
48
+
49
+ @property
50
+ @abstractmethod
51
+ def _topics(self) -> list: ...
52
+
53
+ @ASyncIterator.wrap # type: ignore [call-overload]
54
+ async def yield_thru_block(self, block: BlockNumber) -> AsyncIterator["Task[TokenTransfer]"]:
55
+ if not _logger_is_enabled_for(DEBUG):
56
+ async for task in self._objects_thru(block=block):
57
+ yield task
58
+ return
59
+
60
+ _logger_log(DEBUG, "%s yielding all objects thru block %s", (self, block))
61
+ async for task in self._objects_thru(block=block):
62
+ _logger_log(
63
+ DEBUG,
64
+ "yielding %s at block %s [thru: %s, lock: %s]",
65
+ (task, task.block, block, self._lock.value),
66
+ )
67
+ yield task
68
+ _logger_log(DEBUG, "%s yield thru %s complete", (self, block))
69
+
70
+ async def _extend(self, objs: list[evmspec.Log]) -> None:
71
+ shitcoins = SHITCOINS.get(chain.id, set())
72
+ append_loader_task = self._objects.append
73
+ done = 0
74
+ for log in objs:
75
+ if log.address in shitcoins:
76
+ continue
77
+ # save i/o
78
+ array_encodable_log = y._db.log.Log(**log)
79
+ task = create_task(load_token_transfer(array_encodable_log, self._load_prices))
80
+ task.block = log.block # type: ignore [attr-defined]
81
+ append_loader_task(task)
82
+ done += 1
83
+ # Make sure the event loop doesn't get blocked
84
+ if done % 100 == 0:
85
+ await sleep(0)
86
+
87
+ def _get_block_for_obj(self, task: "Task[TokenTransfer]") -> int:
88
+ return task.block # type: ignore [attr-defined]
89
+
90
+ def _process_event(self, task: "Task[TokenTransfer]") -> "Task[TokenTransfer]":
91
+ return task
92
+
93
+ def _done_callback(self, task: Task) -> None:
94
+ if e := task.exception():
95
+ self._exc = e
96
+ logger.exception(e)
97
+ raise e
98
+
99
+
100
+ class InboundTokenTransfers(_TokenTransfers):
101
+ """A container that fetches and iterates over all inbound token transfers for a particular wallet address"""
102
+
103
+ @property
104
+ def _topics(self) -> list:
105
+ return [TRANSFER_SIGS, None, encode_address(self.address)]
106
+
107
+
108
+ class OutboundTokenTransfers(_TokenTransfers):
109
+ """A container that fetches and iterates over all outbound token transfers for a particular wallet address"""
110
+
111
+ @property
112
+ def _topics(self) -> list:
113
+ return [TRANSFER_SIGS, encode_address(self.address)]
114
+
115
+
116
+ class TokenTransfers(ASyncIterable[TokenTransfer]):
117
+ """
118
+ A container that fetches and iterates over all token transfers for a particular wallet address.
119
+ NOTE: These do not come back in chronologcal order.
120
+ """
121
+
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
+ )
134
+
135
+ async def __aiter__(self) -> AsyncIterator["Task[TokenTransfer]"]:
136
+ async for transfer in self.__yield_thru_block(await dank_mids.eth.block_number):
137
+ yield transfer
138
+
139
+ def yield_thru_block(self, block: BlockNumber) -> ASyncIterator["Task[TokenTransfer]"]:
140
+ return ASyncIterator(self.__yield_thru_block(block))
141
+
142
+ def __yield_thru_block(self, block: BlockNumber) -> AsyncIterator["Task[TokenTransfer]"]:
143
+ return as_yielded(
144
+ self.transfers_in.yield_thru_block(block), self.transfers_out.yield_thru_block(block)
145
+ )
@@ -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 Final, 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 eth_typing import BlockNumber
27
+ from y import convert
28
+ from y._decorators import stuck_coro_debugger
29
+ from y.datatypes import Address, Block
30
+
31
+ from eth_portfolio import protocols
32
+ from eth_portfolio._ledgers.address import (
33
+ AddressInternalTransfersLedger,
34
+ AddressLedgerBase,
35
+ AddressTokenTransfersLedger,
36
+ AddressTransactionsLedger,
37
+ PandableLedgerEntryList,
38
+ )
39
+ from eth_portfolio._loaders import balances
40
+ from eth_portfolio._utils import _get_price, _LedgeredBase
41
+ from eth_portfolio.typing import Balance, RemoteTokenBalances, TokenBalances, WalletBalances
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: Block | None = 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: Block | None = 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: Block | None = 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: Block | None) -> 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: Block | None) -> 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: BlockNumber) -> 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: Block | None = 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: Block | None = 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
+ )