dao-treasury 0.0.38__cp310-cp310-win_amd64.whl → 0.0.61__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.
Files changed (46) hide show
  1. dao_treasury/.grafana/provisioning/dashboards/breakdowns/Expenses.json +526 -0
  2. dao_treasury/.grafana/provisioning/dashboards/breakdowns/Revenue.json +526 -0
  3. dao_treasury/.grafana/provisioning/dashboards/dashboards.yaml +25 -1
  4. dao_treasury/.grafana/provisioning/dashboards/streams/LlamaPay.json +8 -8
  5. dao_treasury/.grafana/provisioning/dashboards/summary/Monthly.json +10 -9
  6. dao_treasury/.grafana/provisioning/dashboards/transactions/Treasury Transactions.json +8 -35
  7. dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow (Including Unsorted).json +74 -33
  8. dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow.json +53 -23
  9. dao_treasury/.grafana/provisioning/dashboards/treasury/Current Treasury Assets.json +593 -0
  10. dao_treasury/.grafana/provisioning/dashboards/treasury/Historical Treasury Balances.json +2999 -0
  11. dao_treasury/.grafana/provisioning/dashboards/treasury/Operating Cashflow.json +38 -17
  12. dao_treasury/ENVIRONMENT_VARIABLES.py +12 -0
  13. dao_treasury/__init__.py +14 -0
  14. dao_treasury/_docker.cp310-win_amd64.pyd +0 -0
  15. dao_treasury/_docker.py +14 -1
  16. dao_treasury/_nicknames.cp310-win_amd64.pyd +0 -0
  17. dao_treasury/_nicknames.py +15 -0
  18. dao_treasury/_wallet.cp310-win_amd64.pyd +0 -0
  19. dao_treasury/constants.cp310-win_amd64.pyd +0 -0
  20. dao_treasury/constants.py +24 -0
  21. dao_treasury/db.py +68 -3
  22. dao_treasury/docker-compose.yaml +1 -1
  23. dao_treasury/main.py +6 -0
  24. dao_treasury/sorting/__init__.cp310-win_amd64.pyd +0 -0
  25. dao_treasury/sorting/_matchers.cp310-win_amd64.pyd +0 -0
  26. dao_treasury/sorting/_rules.cp310-win_amd64.pyd +0 -0
  27. dao_treasury/sorting/factory.cp310-win_amd64.pyd +0 -0
  28. dao_treasury/sorting/rule.cp310-win_amd64.pyd +0 -0
  29. dao_treasury/sorting/rule.py +8 -10
  30. dao_treasury/sorting/rules/__init__.cp310-win_amd64.pyd +0 -0
  31. dao_treasury/sorting/rules/ignore/__init__.cp310-win_amd64.pyd +0 -0
  32. dao_treasury/sorting/rules/ignore/llamapay.cp310-win_amd64.pyd +0 -0
  33. dao_treasury/streams/__init__.cp310-win_amd64.pyd +0 -0
  34. dao_treasury/streams/llamapay.cp310-win_amd64.pyd +0 -0
  35. dao_treasury/streams/llamapay.py +14 -2
  36. dao_treasury/treasury.py +33 -14
  37. dao_treasury/types.cp310-win_amd64.pyd +0 -0
  38. {dao_treasury-0.0.38.dist-info → dao_treasury-0.0.61.dist-info}/METADATA +3 -2
  39. dao_treasury-0.0.61.dist-info/RECORD +54 -0
  40. dao_treasury-0.0.61.dist-info/top_level.txt +2 -0
  41. dao_treasury__mypyc.cp310-win_amd64.pyd +0 -0
  42. bf2b4fe1f86ad2ea158b__mypyc.cp310-win_amd64.pyd +0 -0
  43. dao_treasury/.grafana/provisioning/dashboards/treasury/Treasury.json +0 -2018
  44. dao_treasury-0.0.38.dist-info/RECORD +0 -51
  45. dao_treasury-0.0.38.dist-info/top_level.txt +0 -2
  46. {dao_treasury-0.0.38.dist-info → dao_treasury-0.0.61.dist-info}/WHEEL +0 -0
@@ -18,12 +18,13 @@
18
18
  "editable": true,
19
19
  "fiscalYearStartMonth": 0,
20
20
  "graphTooltip": 1,
21
+ "id": 6,
21
22
  "links": [],
22
- "liveNow": false,
23
23
  "panels": [
24
24
  {
25
25
  "datasource": "SQLite",
26
26
  "fieldConfig": {
27
+ "unit": "currencyUSD",
27
28
  "defaults": {
28
29
  "color": {
29
30
  "mode": "thresholds"
@@ -34,7 +35,7 @@
34
35
  "steps": [
35
36
  {
36
37
  "color": "green",
37
- "value": null
38
+ "value": 0
38
39
  },
39
40
  {
40
41
  "color": "red",
@@ -57,6 +58,7 @@
57
58
  "graphMode": "area",
58
59
  "justifyMode": "auto",
59
60
  "orientation": "auto",
61
+ "percentChangeColorMode": "standard",
60
62
  "reduceOptions": {
61
63
  "calcs": [
62
64
  "lastNotNull"
@@ -64,9 +66,11 @@
64
66
  "fields": "",
65
67
  "values": false
66
68
  },
67
- "textMode": "auto"
69
+ "showPercentChange": false,
70
+ "textMode": "auto",
71
+ "wideLayout": true
68
72
  },
69
- "pluginVersion": "10.2.0",
73
+ "pluginVersion": "12.1.1",
70
74
  "targets": [
71
75
  {
72
76
  "datasource": "SQLite",
@@ -86,6 +90,7 @@
86
90
  {
87
91
  "datasource": "SQLite",
88
92
  "fieldConfig": {
93
+ "unit": "currencyUSD",
89
94
  "defaults": {
90
95
  "color": {
91
96
  "mode": "thresholds"
@@ -96,7 +101,7 @@
96
101
  "steps": [
97
102
  {
98
103
  "color": "green",
99
- "value": null
104
+ "value": 0
100
105
  },
101
106
  {
102
107
  "color": "red",
@@ -119,6 +124,7 @@
119
124
  "graphMode": "area",
120
125
  "justifyMode": "auto",
121
126
  "orientation": "auto",
127
+ "percentChangeColorMode": "standard",
122
128
  "reduceOptions": {
123
129
  "calcs": [
124
130
  "lastNotNull"
@@ -126,9 +132,11 @@
126
132
  "fields": "",
127
133
  "values": false
128
134
  },
129
- "textMode": "auto"
135
+ "showPercentChange": false,
136
+ "textMode": "auto",
137
+ "wideLayout": true
130
138
  },
131
- "pluginVersion": "10.2.0",
139
+ "pluginVersion": "12.1.1",
132
140
  "targets": [
133
141
  {
134
142
  "datasource": "SQLite",
@@ -148,6 +156,7 @@
148
156
  {
149
157
  "datasource": "SQLite",
150
158
  "fieldConfig": {
159
+ "unit": "currencyUSD",
151
160
  "defaults": {
152
161
  "color": {
153
162
  "mode": "thresholds"
@@ -158,7 +167,7 @@
158
167
  "steps": [
159
168
  {
160
169
  "color": "green",
161
- "value": null
170
+ "value": 0
162
171
  },
163
172
  {
164
173
  "color": "red",
@@ -181,6 +190,7 @@
181
190
  "graphMode": "area",
182
191
  "justifyMode": "auto",
183
192
  "orientation": "auto",
193
+ "percentChangeColorMode": "standard",
184
194
  "reduceOptions": {
185
195
  "calcs": [
186
196
  "lastNotNull"
@@ -188,9 +198,11 @@
188
198
  "fields": "",
189
199
  "values": false
190
200
  },
191
- "textMode": "auto"
201
+ "showPercentChange": false,
202
+ "textMode": "auto",
203
+ "wideLayout": true
192
204
  },
193
- "pluginVersion": "10.2.0",
205
+ "pluginVersion": "12.1.1",
194
206
  "targets": [
195
207
  {
196
208
  "datasource": "SQLite",
@@ -211,6 +223,7 @@
211
223
  "datasource": "SQLite",
212
224
  "description": "",
213
225
  "fieldConfig": {
226
+ "unit": "currencyUSD",
214
227
  "defaults": {
215
228
  "color": {
216
229
  "mode": "thresholds"
@@ -221,7 +234,7 @@
221
234
  "steps": [
222
235
  {
223
236
  "color": "green",
224
- "value": null
237
+ "value": 0
225
238
  },
226
239
  {
227
240
  "color": "red",
@@ -244,6 +257,7 @@
244
257
  "graphMode": "area",
245
258
  "justifyMode": "auto",
246
259
  "orientation": "auto",
260
+ "percentChangeColorMode": "standard",
247
261
  "reduceOptions": {
248
262
  "calcs": [
249
263
  "lastNotNull"
@@ -251,9 +265,11 @@
251
265
  "fields": "",
252
266
  "values": false
253
267
  },
254
- "textMode": "auto"
268
+ "showPercentChange": false,
269
+ "textMode": "auto",
270
+ "wideLayout": true
255
271
  },
256
- "pluginVersion": "10.2.0",
272
+ "pluginVersion": "12.1.1",
257
273
  "targets": [
258
274
  {
259
275
  "datasource": "SQLite",
@@ -274,6 +290,7 @@
274
290
  "datasource": "SQLite",
275
291
  "description": "",
276
292
  "fieldConfig": {
293
+ "unit": "currencyUSD",
277
294
  "defaults": {
278
295
  "color": {
279
296
  "mode": "palette-classic"
@@ -285,6 +302,7 @@
285
302
  "axisLabel": "",
286
303
  "axisPlacement": "auto",
287
304
  "barAlignment": 0,
305
+ "barWidthFactor": 0.6,
288
306
  "drawStyle": "bars",
289
307
  "fillOpacity": 70,
290
308
  "gradientMode": "none",
@@ -316,7 +334,7 @@
316
334
  "steps": [
317
335
  {
318
336
  "color": "green",
319
- "value": null
337
+ "value": 0
320
338
  },
321
339
  {
322
340
  "color": "red",
@@ -366,10 +384,12 @@
366
384
  "showLegend": true
367
385
  },
368
386
  "tooltip": {
387
+ "hideZeros": false,
369
388
  "mode": "single",
370
389
  "sort": "none"
371
390
  }
372
391
  },
392
+ "pluginVersion": "12.1.1",
373
393
  "targets": [
374
394
  {
375
395
  "datasource": "SQLite",
@@ -473,8 +493,9 @@
473
493
  "type": "timeseries"
474
494
  }
475
495
  ],
496
+ "preload": false,
476
497
  "refresh": "",
477
- "schemaVersion": 38,
498
+ "schemaVersion": 41,
478
499
  "tags": [],
479
500
  "templating": {
480
501
  "list": []
@@ -486,7 +507,7 @@
486
507
  "timepicker": {},
487
508
  "timezone": "",
488
509
  "title": "Operating Cashflow Summary",
510
+ "description": "Provides a focused view of core operational cashflows, including only sorted transactions in Revenue, Cost of Revenue, and Expenses categories. Excludes unsorted transactions and non-operational activity, helping monitor specifically the DAO's operational financial flows.",
489
511
  "uid": "fdf9969c-357a-4203-ae7b-12816e87bc7e",
490
- "version": 1,
491
- "weekStart": ""
512
+ "version": 5
492
513
  }
@@ -1,3 +1,15 @@
1
+ """Environment variable configuration for DAO Treasury.
2
+
3
+ Defines and loads environment variables (with types and defaults) used for
4
+ system configuration, such as SQL debugging. Uses :mod:`typed_envs` for convenience and safety.
5
+
6
+ Key Responsibilities:
7
+ - Define and load environment variables for the system.
8
+ - Provide type-safe access to configuration options.
9
+
10
+ This is the single source of truth for environment-based settings.
11
+ """
12
+
1
13
  from typing import Final
2
14
 
3
15
  from typed_envs import EnvVarFactory
dao_treasury/__init__.py CHANGED
@@ -1,3 +1,17 @@
1
+ """DAO Treasury package initializer.
2
+
3
+ Exposes the main public API for the library, including the Treasury class,
4
+ wallet management, sorting rules, and database models. Sets up address
5
+ nicknames and enables SQL debugging if configured.
6
+
7
+ Key Responsibilities:
8
+ - Import and expose core classes and functions.
9
+ - Initialize address nicknames in the database.
10
+ - Configure SQL debugging for development.
11
+
12
+ This is the main import point for users and integrations.
13
+ """
14
+
1
15
  from dao_treasury import ENVIRONMENT_VARIABLES as ENVS
2
16
  from dao_treasury._nicknames import setup_address_nicknames_in_db
3
17
  from dao_treasury._wallet import TreasuryWallet
Binary file
dao_treasury/_docker.py CHANGED
@@ -1,4 +1,17 @@
1
- """This module contains utilities for managing dao-treasury's docker containers"""
1
+ """Docker orchestration utilities for DAO Treasury.
2
+
3
+ Provides functions to build, start, and stop Docker Compose services
4
+ required for analytics dashboards (Grafana, renderer). Integrates with
5
+ eth-portfolio's Docker setup and ensures all containers are managed
6
+ consistently for local analytics.
7
+
8
+ Key Responsibilities:
9
+ - Build and manage Grafana and renderer containers.
10
+ - Integrate with eth-portfolio Docker services.
11
+ - Provide decorators/utilities for container lifecycle management.
12
+
13
+ This is the main entry for all Docker-based orchestration.
14
+ """
2
15
 
3
16
  import logging
4
17
  from importlib import resources
Binary file
@@ -1,3 +1,18 @@
1
+ """Address nickname setup utilities.
2
+
3
+ This module provides functions to assign human-readable nicknames to
4
+ important on-chain addresses (e.g., Zero Address, Disperse.app, tokens).
5
+ It is used at package initialization to ensure all analytics and dashboards
6
+ display professional, consistent labels.
7
+
8
+ Key Responsibilities:
9
+ - Set nicknames for core addresses in the database.
10
+ - Integrate with constants and token metadata.
11
+ - Support professional, readable analytics outputs.
12
+
13
+ This is called automatically on package import.
14
+ """
15
+
1
16
  from typing import Final
2
17
 
3
18
  from pony.orm import db_session
Binary file
Binary file
dao_treasury/constants.py CHANGED
@@ -1,11 +1,30 @@
1
+ """Core constants for DAO Treasury.
2
+
3
+ All constants are marked with `Final`, ensuring immutability and allowing
4
+ mypyc to compile them as extremely fast C-level constants for maximum
5
+ performance. Defines chain IDs, zero address, and key contract addresses
6
+ (e.g., Disperse.app) used throughout the system for transaction processing,
7
+ nickname assignment, and analytics.
8
+
9
+ Key Responsibilities:
10
+ - Provide canonical addresses and chain IDs.
11
+ - Support nickname setup and transaction categorization.
12
+ - Guarantee fast, immutable constants at runtime.
13
+
14
+ This is the single source of truth for system-wide constants.
15
+ """
16
+
1
17
  from typing import Final
2
18
 
19
+ import eth_portfolio._utils
3
20
  import y.constants
4
21
 
5
22
 
6
23
  CHAINID: Final = y.constants.CHAINID
24
+ # TODO: add docstring
7
25
 
8
26
  ZERO_ADDRESS: Final = "0x0000000000000000000000000000000000000000"
27
+ # TODO: add docstring
9
28
 
10
29
  # TODO: move disperse.app stuff from yearn-treasury to dao-treasury and then write a docs file
11
30
  DISPERSE_APP: Final = (
@@ -13,3 +32,8 @@ DISPERSE_APP: Final = (
13
32
  "0xd15fE25eD0Dba12fE05e7029C88b10C25e8880E3",
14
33
  )
15
34
  """If your treasury sends funds to disperse.app, we create additional txs in the db so each individual send can be accounted for."""
35
+ # TODO: all crosslink to disperse.py once ready
36
+
37
+
38
+ SUPPRESS_ERROR_LOGS: Final = eth_portfolio._utils.SUPPRESS_ERROR_LOGS
39
+ """Append tokens here when you don't expect them to price successfully and do not want to see the associated error logs."""
dao_treasury/db.py CHANGED
@@ -3,6 +3,7 @@
3
3
  Database models and utilities for DAO treasury reporting.
4
4
 
5
5
  This module defines Pony ORM entities for:
6
+
6
7
  - Blockchain networks (:class:`Chain`)
7
8
  - On-chain addresses (:class:`Address`)
8
9
  - ERC-20 tokens and native coin placeholder (:class:`Token`)
@@ -17,27 +18,41 @@ and creating SQL views for reporting.
17
18
 
18
19
  import typing
19
20
  from asyncio import Semaphore
21
+ from collections import OrderedDict
20
22
  from decimal import Decimal, InvalidOperation
21
23
  from functools import lru_cache
22
24
  from logging import getLogger
23
25
  from os import path
24
26
  from pathlib import Path
25
- from typing import TYPE_CHECKING, Dict, Final, Tuple, Union, final
27
+ from typing import (
28
+ TYPE_CHECKING,
29
+ Any,
30
+ Coroutine,
31
+ Dict,
32
+ Final,
33
+ Literal,
34
+ Tuple,
35
+ Union,
36
+ final,
37
+ overload,
38
+ )
26
39
  from datetime import date, datetime, time, timezone
27
40
 
41
+ import eth_portfolio
28
42
  from a_sync import AsyncThreadPoolExecutor
29
43
  from brownie import chain
30
44
  from brownie.convert.datatypes import HexString
31
45
  from brownie.exceptions import EventLookupError
32
46
  from brownie.network.event import EventDict, _EventItem
33
47
  from brownie.network.transaction import TransactionReceipt
34
- from eth_typing import ChecksumAddress, HexAddress, HexStr
35
48
  from eth_portfolio.structs import (
36
49
  InternalTransfer,
37
50
  LedgerEntry,
38
51
  TokenTransfer,
39
52
  Transaction,
40
53
  )
54
+ from eth_retry import auto_retry
55
+ from eth_typing import ChecksumAddress, HexAddress, HexStr
41
56
  from pony.orm import (
42
57
  Database,
43
58
  InterfaceError,
@@ -61,6 +76,9 @@ from dao_treasury.constants import CHAINID
61
76
  from dao_treasury.types import TxGroupDbid, TxGroupName
62
77
 
63
78
 
79
+ EventItem = _EventItem[_EventItem[OrderedDict[str, Any]]]
80
+
81
+
64
82
  SQLITE_DIR = Path(path.expanduser("~")) / ".dao-treasury"
65
83
  """Path to the directory in the user's home where the DAO treasury SQLite database is stored."""
66
84
 
@@ -69,6 +87,7 @@ SQLITE_DIR.mkdir(parents=True, exist_ok=True)
69
87
 
70
88
  _INSERT_THREAD = AsyncThreadPoolExecutor(1)
71
89
  _SORT_THREAD = AsyncThreadPoolExecutor(1)
90
+ _EVENTS_THREADS = AsyncThreadPoolExecutor(16)
72
91
  _SORT_SEMAPHORE = Semaphore(50)
73
92
 
74
93
  _UTC = timezone.utc
@@ -235,6 +254,10 @@ class Address(DbEntity):
235
254
  def contract(self) -> Contract:
236
255
  return Contract(self.address)
237
256
 
257
+ @property
258
+ def contract_coro(self) -> Coroutine[Any, Any, Contract]:
259
+ return Contract.coroutine(self.address)
260
+
238
261
  @staticmethod
239
262
  @lru_cache(maxsize=None)
240
263
  def get_dbid(address: HexAddress) -> int:
@@ -405,6 +428,10 @@ class Token(DbEntity):
405
428
  def contract(self) -> Contract:
406
429
  return Contract(self.address.address)
407
430
 
431
+ @property
432
+ def contract_coro(self) -> Coroutine[Any, Any, Contract]:
433
+ return Contract.coroutine(self.address.address)
434
+
408
435
  @property
409
436
  def scale(self) -> int:
410
437
  """Base for division according to `decimals`, e.g., `10**decimals`.
@@ -729,7 +756,23 @@ class TreasuryTx(DbEntity):
729
756
  """Decoded event logs for this transaction."""
730
757
  return self._transaction.events
731
758
 
732
- def get_events(self, event_name: str) -> _EventItem:
759
+ async def events_async(self) -> EventDict:
760
+ """Asynchronously fetch decoded event logs for this transaction."""
761
+ tx = self._transaction
762
+ events = tx._events
763
+ if events is None:
764
+ events = await _EVENTS_THREADS.run(getattr, tx, "events")
765
+ return events
766
+
767
+ @overload
768
+ def get_events(
769
+ self, event_name: str, sync: Literal[False]
770
+ ) -> Coroutine[Any, Any, EventItem]: ...
771
+ @overload
772
+ def get_events(self, event_name: str, sync: bool = True) -> EventItem: ...
773
+ def get_events(self, event_name: str, sync: bool = True) -> EventItem:
774
+ if not sync:
775
+ return _EVENTS_THREADS.run(self.get_events, event_name)
733
776
  try:
734
777
  return self.events[event_name]
735
778
  except EventLookupError:
@@ -746,6 +789,7 @@ class TreasuryTx(DbEntity):
746
789
  return get_transaction(self.hash)
747
790
 
748
791
  @staticmethod
792
+ @auto_retry
749
793
  async def insert(entry: LedgerEntry) -> None:
750
794
  """Asynchronously insert and sort a ledger entry.
751
795
 
@@ -1383,3 +1427,24 @@ def _validate_integrity_error(
1383
1427
  )
1384
1428
  else None
1385
1429
  )
1430
+
1431
+
1432
+ def _drop_shitcoin_txs() -> None:
1433
+ """
1434
+ Purge any shitcoin txs from the db.
1435
+
1436
+ These should not be frequent, and only occur if a user populated the db before a shitcoin was added to the SHITCOINS mapping.
1437
+ """
1438
+ shitcoins = eth_portfolio.SHITCOINS[CHAINID]
1439
+ with db_session:
1440
+ shitcoin_txs = select(
1441
+ tx for tx in TreasuryTx if tx.token.address.address in shitcoins
1442
+ )
1443
+ if count := shitcoin_txs.count():
1444
+ logger.info(f"Purging {count} shitcoin txs from the database...")
1445
+ for tx in shitcoin_txs:
1446
+ tx.delete()
1447
+ logger.info("Shitcoin tx purge complete.")
1448
+
1449
+
1450
+ _drop_shitcoin_txs()
@@ -5,7 +5,7 @@ networks:
5
5
 
6
6
  services:
7
7
  grafana:
8
- image: grafana/grafana:10.2.0
8
+ image: grafana/grafana:12.2.1
9
9
  ports:
10
10
  - 127.0.0.1:${DAO_TREASURY_GRAFANA_PORT:-3004}:3000
11
11
  environment:
dao_treasury/main.py CHANGED
@@ -97,6 +97,12 @@ parser.add_argument(
97
97
  help="The time interval between datapoints. default: 1d",
98
98
  default="1d",
99
99
  )
100
+ parser.add_argument(
101
+ "--concurrency",
102
+ type=int,
103
+ help="The max number of historical blocks to export concurrently. default: 30",
104
+ default=30,
105
+ )
100
106
  parser.add_argument(
101
107
  "--daemon",
102
108
  action="store_true",
@@ -130,8 +130,6 @@ class _SortRule:
130
130
  func: Optional[SortFunction] = None
131
131
  """Custom matching function that takes a `TreasuryTx` and returns a bool or an awaitable that returns a bool."""
132
132
 
133
- # __instances__: ClassVar[List[Self]] = []
134
-
135
133
  def __post_init__(self) -> None:
136
134
  """Validate inputs, checksum addresses, and register the rule.
137
135
 
@@ -235,7 +233,7 @@ class _InboundSortRule(_SortRule):
235
233
  return (
236
234
  tx.to_address is not None
237
235
  and TreasuryWallet.check_membership(tx.to_address.address, tx.block)
238
- and await super().match(tx)
236
+ and await super(_InboundSortRule, self).match(tx)
239
237
  )
240
238
 
241
239
 
@@ -250,7 +248,7 @@ class _OutboundSortRule(_SortRule):
250
248
  async def match(self, tx: "TreasuryTx") -> bool:
251
249
  return TreasuryWallet.check_membership(
252
250
  tx.from_address.address, tx.block
253
- ) and await super().match(tx)
251
+ ) and await super(_OutboundSortRule, self).match(tx)
254
252
 
255
253
 
256
254
  @mypyc_attr(native_class=False)
@@ -267,7 +265,7 @@ class RevenueSortRule(_InboundSortRule):
267
265
  def __post_init__(self) -> None:
268
266
  """Prepends `self.txgroup` with 'Revenue:'."""
269
267
  object.__setattr__(self, "txgroup", f"Revenue:{self.txgroup}")
270
- super().__post_init__()
268
+ super(RevenueSortRule, self).__post_init__()
271
269
 
272
270
 
273
271
  @mypyc_attr(native_class=False)
@@ -280,7 +278,7 @@ class CostOfRevenueSortRule(_OutboundSortRule):
280
278
  def __post_init__(self) -> None:
281
279
  """Prepends `self.txgroup` with 'Cost of Revenue:'."""
282
280
  object.__setattr__(self, "txgroup", f"Cost of Revenue:{self.txgroup}")
283
- super().__post_init__()
281
+ super(CostOfRevenueSortRule, self).__post_init__()
284
282
 
285
283
 
286
284
  @mypyc_attr(native_class=False)
@@ -293,7 +291,7 @@ class ExpenseSortRule(_OutboundSortRule):
293
291
  def __post_init__(self) -> None:
294
292
  """Prepends `self.txgroup` with 'Expenses:'."""
295
293
  object.__setattr__(self, "txgroup", f"Expenses:{self.txgroup}")
296
- super().__post_init__()
294
+ super(ExpenseSortRule, self).__post_init__()
297
295
 
298
296
 
299
297
  @mypyc_attr(native_class=False)
@@ -306,7 +304,7 @@ class OtherIncomeSortRule(_InboundSortRule):
306
304
  def __post_init__(self) -> None:
307
305
  """Prepends `self.txgroup` with 'Other Income:'."""
308
306
  object.__setattr__(self, "txgroup", f"Other Income:{self.txgroup}")
309
- super().__post_init__()
307
+ super(OtherIncomeSortRule, self).__post_init__()
310
308
 
311
309
 
312
310
  @mypyc_attr(native_class=False)
@@ -319,7 +317,7 @@ class OtherExpenseSortRule(_OutboundSortRule):
319
317
  def __post_init__(self) -> None:
320
318
  """Prepends `self.txgroup` with 'Other Expenses:'."""
321
319
  object.__setattr__(self, "txgroup", f"Other Expenses:{self.txgroup}")
322
- super().__post_init__()
320
+ super(OtherExpenseSortRule, self).__post_init__()
323
321
 
324
322
 
325
323
  @mypyc_attr(native_class=False)
@@ -332,7 +330,7 @@ class IgnoreSortRule(_SortRule):
332
330
  def __post_init__(self) -> None:
333
331
  """Prepends `self.txgroup` with 'Ignore:'."""
334
332
  object.__setattr__(self, "txgroup", f"Ignore:{self.txgroup}")
335
- super().__post_init__()
333
+ super(IgnoreSortRule, self).__post_init__()
336
334
 
337
335
 
338
336
  TRule = TypeVar(
@@ -22,7 +22,7 @@ from eth_typing import BlockNumber, ChecksumAddress, HexAddress, HexStr
22
22
  from tqdm.asyncio import tqdm_asyncio
23
23
 
24
24
  import y
25
- from y.time import UnixTimestamp
25
+ from y.time import NoBlockFound, UnixTimestamp
26
26
  from y.utils.events import decode_logs, get_logs_asap
27
27
 
28
28
  from dao_treasury import constants
@@ -338,7 +338,19 @@ class LlamaPayProcessor:
338
338
  check_at = date_obj + timedelta(days=1) - timedelta(seconds=1)
339
339
  if check_at > now(tz=_UTC):
340
340
  await sleep((check_at - now(tz=_UTC)).total_seconds())
341
- block = await get_block_at_timestamp(check_at, sync=False)
341
+
342
+ while True:
343
+ try:
344
+ block = await get_block_at_timestamp(check_at, sync=False)
345
+ except NoBlockFound:
346
+ sleep_time = (check_at - now(tz=_UTC)).total_seconds()
347
+ logger.debug(
348
+ "no block found for %s, sleeping %ss", check_at, sleep_time
349
+ )
350
+ await sleep(sleep_time)
351
+ else:
352
+ break
353
+
342
354
  price_fut = create_task(get_price(stream_token, block, sync=False))
343
355
  start_timestamp = await _get_start_timestamp(stream_id, block)
344
356
  if start_timestamp == 0: