dao-treasury 0.0.42__cp311-cp311-macosx_11_0_arm64.whl → 0.0.69__cp311-cp311-macosx_11_0_arm64.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 (41) hide show
  1. dao_treasury/.grafana/provisioning/dashboards/breakdowns/Expenses.json +551 -0
  2. dao_treasury/.grafana/provisioning/dashboards/breakdowns/Revenue.json +551 -0
  3. dao_treasury/.grafana/provisioning/dashboards/dashboards.yaml +7 -57
  4. dao_treasury/.grafana/provisioning/dashboards/streams/LlamaPay.json +11 -16
  5. dao_treasury/.grafana/provisioning/dashboards/summary/Monthly.json +15 -15
  6. dao_treasury/.grafana/provisioning/dashboards/transactions/Treasury Transactions.json +38 -61
  7. dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow (Including Unsorted).json +122 -149
  8. dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow.json +87 -100
  9. dao_treasury/.grafana/provisioning/dashboards/treasury/Current Treasury Assets.json +1009 -0
  10. dao_treasury/.grafana/provisioning/dashboards/treasury/Historical Treasury Balances.json +2989 -0
  11. dao_treasury/.grafana/provisioning/dashboards/treasury/Operating Cashflow.json +64 -78
  12. dao_treasury/_docker.cpython-311-darwin.so +0 -0
  13. dao_treasury/_docker.py +24 -20
  14. dao_treasury/_nicknames.cpython-311-darwin.so +0 -0
  15. dao_treasury/_wallet.cpython-311-darwin.so +0 -0
  16. dao_treasury/constants.cpython-311-darwin.so +0 -0
  17. dao_treasury/constants.py +5 -0
  18. dao_treasury/db.py +49 -3
  19. dao_treasury/docker-compose.yaml +1 -1
  20. dao_treasury/main.py +6 -0
  21. dao_treasury/sorting/__init__.cpython-311-darwin.so +0 -0
  22. dao_treasury/sorting/_matchers.cpython-311-darwin.so +0 -0
  23. dao_treasury/sorting/_rules.cpython-311-darwin.so +0 -0
  24. dao_treasury/sorting/factory.cpython-311-darwin.so +0 -0
  25. dao_treasury/sorting/rule.cpython-311-darwin.so +0 -0
  26. dao_treasury/sorting/rule.py +8 -10
  27. dao_treasury/sorting/rules/__init__.cpython-311-darwin.so +0 -0
  28. dao_treasury/sorting/rules/ignore/__init__.cpython-311-darwin.so +0 -0
  29. dao_treasury/sorting/rules/ignore/llamapay.cpython-311-darwin.so +0 -0
  30. dao_treasury/streams/__init__.cpython-311-darwin.so +0 -0
  31. dao_treasury/streams/llamapay.cpython-311-darwin.so +0 -0
  32. dao_treasury/types.cpython-311-darwin.so +0 -0
  33. {dao_treasury-0.0.42.dist-info → dao_treasury-0.0.69.dist-info}/METADATA +4 -3
  34. dao_treasury-0.0.69.dist-info/RECORD +54 -0
  35. dao_treasury-0.0.69.dist-info/top_level.txt +2 -0
  36. dao_treasury__mypyc.cpython-311-darwin.so +0 -0
  37. bf2b4fe1f86ad2ea158b__mypyc.cpython-311-darwin.so +0 -0
  38. dao_treasury/.grafana/provisioning/dashboards/treasury/Treasury.json +0 -2018
  39. dao_treasury-0.0.42.dist-info/RECORD +0 -51
  40. dao_treasury-0.0.42.dist-info/top_level.txt +0 -2
  41. {dao_treasury-0.0.42.dist-info → dao_treasury-0.0.69.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,16 +58,17 @@
57
58
  "graphMode": "area",
58
59
  "justifyMode": "auto",
59
60
  "orientation": "auto",
61
+ "percentChangeColorMode": "standard",
60
62
  "reduceOptions": {
61
- "calcs": [
62
- "lastNotNull"
63
- ],
63
+ "calcs": ["lastNotNull"],
64
64
  "fields": "",
65
65
  "values": false
66
66
  },
67
- "textMode": "auto"
67
+ "showPercentChange": false,
68
+ "textMode": "auto",
69
+ "wideLayout": true
68
70
  },
69
- "pluginVersion": "10.2.0",
71
+ "pluginVersion": "12.1.1",
70
72
  "targets": [
71
73
  {
72
74
  "datasource": "SQLite",
@@ -74,18 +76,17 @@
74
76
  "queryType": "table",
75
77
  "rawQueryText": "SELECT SUM(t.value_usd) AS total_revenue FROM general_ledger t JOIN txgroup_hierarchy gh USING (txgroup_id) WHERE gh.top_category = 'Revenue' AND t.timestamp >= $__from/1000 AND t.timestamp <= $__to/1000",
76
78
  "refId": "A",
77
- "timeColumns": [
78
- "time",
79
- "ts"
80
- ]
79
+ "timeColumns": ["time", "ts"]
81
80
  }
82
81
  ],
83
82
  "title": "Revenue",
84
- "type": "stat"
83
+ "type": "stat",
84
+ "description": "Displays the total value of all transactions sorted as Revenue for the selected period."
85
85
  },
86
86
  {
87
87
  "datasource": "SQLite",
88
88
  "fieldConfig": {
89
+ "unit": "currencyUSD",
89
90
  "defaults": {
90
91
  "color": {
91
92
  "mode": "thresholds"
@@ -96,7 +97,7 @@
96
97
  "steps": [
97
98
  {
98
99
  "color": "green",
99
- "value": null
100
+ "value": 0
100
101
  },
101
102
  {
102
103
  "color": "red",
@@ -119,16 +120,17 @@
119
120
  "graphMode": "area",
120
121
  "justifyMode": "auto",
121
122
  "orientation": "auto",
123
+ "percentChangeColorMode": "standard",
122
124
  "reduceOptions": {
123
- "calcs": [
124
- "lastNotNull"
125
- ],
125
+ "calcs": ["lastNotNull"],
126
126
  "fields": "",
127
127
  "values": false
128
128
  },
129
- "textMode": "auto"
129
+ "showPercentChange": false,
130
+ "textMode": "auto",
131
+ "wideLayout": true
130
132
  },
131
- "pluginVersion": "10.2.0",
133
+ "pluginVersion": "12.1.1",
132
134
  "targets": [
133
135
  {
134
136
  "datasource": "SQLite",
@@ -136,18 +138,17 @@
136
138
  "queryType": "table",
137
139
  "rawQueryText": "SELECT SUM(t.value_usd) AS total_revenue FROM general_ledger t JOIN txgroup_hierarchy gh USING (txgroup_id) WHERE gh.top_category = 'Cost of Revenue' AND t.timestamp >= $__from/1000 AND t.timestamp <= $__to/1000",
138
140
  "refId": "A",
139
- "timeColumns": [
140
- "time",
141
- "ts"
142
- ]
141
+ "timeColumns": ["time", "ts"]
143
142
  }
144
143
  ],
145
144
  "title": "Cost of Revenue",
146
- "type": "stat"
145
+ "type": "stat",
146
+ "description": "Displays the total value of all transactions sorted as Cost of Revenue for the selected period."
147
147
  },
148
148
  {
149
149
  "datasource": "SQLite",
150
150
  "fieldConfig": {
151
+ "unit": "currencyUSD",
151
152
  "defaults": {
152
153
  "color": {
153
154
  "mode": "thresholds"
@@ -158,7 +159,7 @@
158
159
  "steps": [
159
160
  {
160
161
  "color": "green",
161
- "value": null
162
+ "value": 0
162
163
  },
163
164
  {
164
165
  "color": "red",
@@ -181,16 +182,17 @@
181
182
  "graphMode": "area",
182
183
  "justifyMode": "auto",
183
184
  "orientation": "auto",
185
+ "percentChangeColorMode": "standard",
184
186
  "reduceOptions": {
185
- "calcs": [
186
- "lastNotNull"
187
- ],
187
+ "calcs": ["lastNotNull"],
188
188
  "fields": "",
189
189
  "values": false
190
190
  },
191
- "textMode": "auto"
191
+ "showPercentChange": false,
192
+ "textMode": "auto",
193
+ "wideLayout": true
192
194
  },
193
- "pluginVersion": "10.2.0",
195
+ "pluginVersion": "12.1.1",
194
196
  "targets": [
195
197
  {
196
198
  "datasource": "SQLite",
@@ -198,19 +200,18 @@
198
200
  "queryType": "table",
199
201
  "rawQueryText": "SELECT SUM(t.value_usd) AS total_revenue FROM general_ledger t JOIN txgroup_hierarchy gh USING (txgroup_id) WHERE gh.top_category = 'Expenses' AND t.timestamp >= $__from/1000 AND t.timestamp <= $__to/1000",
200
202
  "refId": "A",
201
- "timeColumns": [
202
- "time",
203
- "ts"
204
- ]
203
+ "timeColumns": ["time", "ts"]
205
204
  }
206
205
  ],
207
206
  "title": "Expenses",
208
- "type": "stat"
207
+ "type": "stat",
208
+ "description": "Displays the total value of all transactions sorted as Expenses for the selected period."
209
209
  },
210
210
  {
211
211
  "datasource": "SQLite",
212
- "description": "",
212
+ "description": "Shows the net operating cashflow (Revenue minus Cost of Revenue and Expenses) for the selected period.",
213
213
  "fieldConfig": {
214
+ "unit": "currencyUSD",
214
215
  "defaults": {
215
216
  "color": {
216
217
  "mode": "thresholds"
@@ -221,7 +222,7 @@
221
222
  "steps": [
222
223
  {
223
224
  "color": "green",
224
- "value": null
225
+ "value": 0
225
226
  },
226
227
  {
227
228
  "color": "red",
@@ -244,16 +245,17 @@
244
245
  "graphMode": "area",
245
246
  "justifyMode": "auto",
246
247
  "orientation": "auto",
248
+ "percentChangeColorMode": "standard",
247
249
  "reduceOptions": {
248
- "calcs": [
249
- "lastNotNull"
250
- ],
250
+ "calcs": ["lastNotNull"],
251
251
  "fields": "",
252
252
  "values": false
253
253
  },
254
- "textMode": "auto"
254
+ "showPercentChange": false,
255
+ "textMode": "auto",
256
+ "wideLayout": true
255
257
  },
256
- "pluginVersion": "10.2.0",
258
+ "pluginVersion": "12.1.1",
257
259
  "targets": [
258
260
  {
259
261
  "datasource": "SQLite",
@@ -261,10 +263,7 @@
261
263
  "queryType": "table",
262
264
  "rawQueryText": "SELECT (\n SUM(CASE WHEN gh.top_category = 'Revenue' THEN t.value_usd ELSE 0 END)\n - SUM(CASE WHEN gh.top_category = 'Cost of Revenue' THEN t.value_usd ELSE 0 END)\n - SUM(CASE WHEN gh.top_category = 'Expenses' THEN t.value_usd ELSE 0 END)\n) AS total_net\nFROM general_ledger t\nJOIN txgroup_hierarchy gh USING (txgroup_id)\nWHERE t.timestamp >= $__from/1000 AND t.timestamp <= $__to/1000",
263
265
  "refId": "A",
264
- "timeColumns": [
265
- "time",
266
- "ts"
267
- ]
266
+ "timeColumns": ["time", "ts"]
268
267
  }
269
268
  ],
270
269
  "title": "Operating Total",
@@ -272,8 +271,9 @@
272
271
  },
273
272
  {
274
273
  "datasource": "SQLite",
275
- "description": "",
274
+ "description": "Time series chart of all sorted operational cashflows, grouped by category, for the selected period. Shows Total Revenue, Total Expenses, and Net for each week, helping to analyze trends in the DAO's operational financial flows over time.",
276
275
  "fieldConfig": {
276
+ "unit": "currencyUSD",
277
277
  "defaults": {
278
278
  "color": {
279
279
  "mode": "palette-classic"
@@ -285,6 +285,7 @@
285
285
  "axisLabel": "",
286
286
  "axisPlacement": "auto",
287
287
  "barAlignment": 0,
288
+ "barWidthFactor": 0.6,
288
289
  "drawStyle": "bars",
289
290
  "fillOpacity": 70,
290
291
  "gradientMode": "none",
@@ -316,7 +317,7 @@
316
317
  "steps": [
317
318
  {
318
319
  "color": "green",
319
- "value": null
320
+ "value": 0
320
321
  },
321
322
  {
322
323
  "color": "red",
@@ -366,10 +367,12 @@
366
367
  "showLegend": true
367
368
  },
368
369
  "tooltip": {
370
+ "hideZeros": false,
369
371
  "mode": "single",
370
372
  "sort": "none"
371
373
  }
372
374
  },
375
+ "pluginVersion": "12.1.1",
373
376
  "targets": [
374
377
  {
375
378
  "datasource": "SQLite",
@@ -377,9 +380,7 @@
377
380
  "queryType": "table",
378
381
  "rawQueryText": "SELECT t.timestamp,\r\n SUM(CASE WHEN gh.top_category = 'Revenue' THEN t.value_usd ELSE 0 END) AS \"Total Revenue\",\r\n SUM(CASE WHEN gh.top_category in ('Cost of Revenue','Expenses') THEN t.value_usd ELSE 0 END) * -1 AS \"Total Expenses\",\r\n SUM(CASE WHEN gh.top_category = 'Revenue' THEN t.value_usd ELSE 0 END) - SUM(CASE WHEN gh.top_category in ('Cost of Revenue','Expenses') THEN t.value_usd ELSE 0 END) AS \"Net\"\r\nFROM general_ledger AS t\r\nJOIN txgroup_hierarchy gh ON t.txgroup_id = gh.txgroup_id\r\nWHERE t.timestamp >= $__from/1000 AND t.timestamp <= $__to/1000\r\nGROUP BY timestamp\r\nORDER BY timestamp;",
379
382
  "refId": "A",
380
- "timeColumns": [
381
- "timestamp"
382
- ]
383
+ "timeColumns": ["timestamp"]
383
384
  }
384
385
  ],
385
386
  "title": "Weekly Cashflow",
@@ -411,39 +412,27 @@
411
412
  "options": {
412
413
  "fields": {
413
414
  "Cost of Revenue": {
414
- "aggregations": [
415
- "sum"
416
- ],
415
+ "aggregations": ["sum"],
417
416
  "operation": "aggregate"
418
417
  },
419
418
  "Expenses": {
420
- "aggregations": [
421
- "sum"
422
- ],
419
+ "aggregations": ["sum"],
423
420
  "operation": "aggregate"
424
421
  },
425
422
  "Net": {
426
- "aggregations": [
427
- "sum"
428
- ],
423
+ "aggregations": ["sum"],
429
424
  "operation": "aggregate"
430
425
  },
431
426
  "Other Expenses": {
432
- "aggregations": [
433
- "sum"
434
- ],
427
+ "aggregations": ["sum"],
435
428
  "operation": "aggregate"
436
429
  },
437
430
  "Other Income": {
438
- "aggregations": [
439
- "sum"
440
- ],
431
+ "aggregations": ["sum"],
441
432
  "operation": "aggregate"
442
433
  },
443
434
  "Revenue": {
444
- "aggregations": [
445
- "sum"
446
- ],
435
+ "aggregations": ["sum"],
447
436
  "operation": "aggregate"
448
437
  },
449
438
  "Timestamp": {
@@ -451,15 +440,11 @@
451
440
  "operation": "groupby"
452
441
  },
453
442
  "Total Expenses": {
454
- "aggregations": [
455
- "sum"
456
- ],
443
+ "aggregations": ["sum"],
457
444
  "operation": "aggregate"
458
445
  },
459
446
  "Total Revenue": {
460
- "aggregations": [
461
- "sum"
462
- ],
447
+ "aggregations": ["sum"],
463
448
  "operation": "aggregate"
464
449
  },
465
450
  "timestamp": {
@@ -473,9 +458,10 @@
473
458
  "type": "timeseries"
474
459
  }
475
460
  ],
461
+ "preload": false,
476
462
  "refresh": "",
477
- "schemaVersion": 38,
478
- "tags": [],
463
+ "schemaVersion": 41,
464
+ "tags": ["cashflow", "operating"],
479
465
  "templating": {
480
466
  "list": []
481
467
  },
@@ -486,7 +472,7 @@
486
472
  "timepicker": {},
487
473
  "timezone": "",
488
474
  "title": "Operating Cashflow Summary",
475
+ "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
476
  "uid": "fdf9969c-357a-4203-ae7b-12816e87bc7e",
490
- "version": 1,
491
- "weekStart": ""
477
+ "version": 7
492
478
  }
Binary file
dao_treasury/_docker.py CHANGED
@@ -14,17 +14,17 @@ This is the main entry for all Docker-based orchestration.
14
14
  """
15
15
 
16
16
  import logging
17
- from importlib import resources
18
- import subprocess
19
17
  from functools import wraps
20
- from typing import Any, Callable, Coroutine, Final, Iterable, Tuple, TypeVar, List
18
+ from importlib import resources
19
+ from typing import Any, Callable, Coroutine, Final, Literal, Tuple, TypeVar, List
21
20
 
22
21
  import eth_portfolio_scripts.docker
22
+ from eth_portfolio_scripts.docker import docker_compose
23
23
  from typing_extensions import ParamSpec
24
24
 
25
25
  logger: Final = logging.getLogger(__name__)
26
26
 
27
- compose_file: Final = str(
27
+ COMPOSE_FILE: Final = str(
28
28
  resources.files("dao_treasury").joinpath("docker-compose.yaml")
29
29
  )
30
30
  """The path of dao-treasury's docker-compose.yaml file on your machine"""
@@ -55,7 +55,7 @@ def up(*services: str) -> None:
55
55
  # eth-portfolio containers must be started first so dao-treasury can attach to the eth-portfolio docker network
56
56
  eth_portfolio_scripts.docker.up("victoria-metrics")
57
57
  build(*services)
58
- print(f"starting the {', '.join(services) if services else 'grafana'} container(s)")
58
+ _print_notice("starting", services)
59
59
  _exec_command(["up", "-d", *services])
60
60
 
61
61
 
@@ -72,6 +72,7 @@ def down() -> None:
72
72
  See Also:
73
73
  :func:`up`
74
74
  """
75
+ print("stopping all dao-treasury containers")
75
76
  _exec_command(["down"])
76
77
 
77
78
 
@@ -90,10 +91,24 @@ def build(*services: str) -> None:
90
91
  :func:`up`
91
92
  :func:`_exec_command`
92
93
  """
93
- print("building the grafana containers")
94
+ _print_notice("building", services)
94
95
  _exec_command(["build", *services])
95
96
 
96
97
 
98
+ def _print_notice(
99
+ doing: Literal["building", "starting"], services: Tuple[str, ...]
100
+ ) -> None:
101
+ if len(services) == 1:
102
+ container = services[0]
103
+ print(f"{doing} the {container} container")
104
+ elif len(services) == 2:
105
+ first, second = services
106
+ print(f"{doing} the {first} and {second} containers")
107
+ else:
108
+ *all_but_last, last = services
109
+ print(f"{doing} the {', '.join(all_but_last)}, and {last} containers")
110
+
111
+
97
112
  _P = ParamSpec("_P")
98
113
  _T = TypeVar("_T")
99
114
 
@@ -174,17 +189,6 @@ def _exec_command(command: List[str], *, compose_options: Tuple[str, ...] = ())
174
189
  See Also:
175
190
  :func:`check_system`
176
191
  """
177
- eth_portfolio_scripts.docker.check_system()
178
- try:
179
- subprocess.check_output(
180
- ["docker", "compose", *compose_options, "-f", compose_file, *command]
181
- )
182
- except (subprocess.CalledProcessError, FileNotFoundError) as e:
183
- try:
184
- subprocess.check_output(
185
- ["docker-compose", *compose_options, "-f", compose_file, *command]
186
- )
187
- except (subprocess.CalledProcessError, FileNotFoundError) as _e:
188
- raise RuntimeError(
189
- f"Error occurred while running {' '.join(command)}: {_e}"
190
- ) from _e
192
+ docker_compose._exec_command(
193
+ command, compose_file=COMPOSE_FILE, compose_options=compose_options
194
+ )
Binary file
dao_treasury/constants.py CHANGED
@@ -16,6 +16,7 @@ This is the single source of truth for system-wide constants.
16
16
 
17
17
  from typing import Final
18
18
 
19
+ import eth_portfolio._utils
19
20
  import y.constants
20
21
 
21
22
 
@@ -32,3 +33,7 @@ DISPERSE_APP: Final = (
32
33
  )
33
34
  """If your treasury sends funds to disperse.app, we create additional txs in the db so each individual send can be accounted for."""
34
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
@@ -18,12 +18,24 @@ and creating SQL views for reporting.
18
18
 
19
19
  import typing
20
20
  from asyncio import Semaphore
21
+ from collections import OrderedDict
21
22
  from decimal import Decimal, InvalidOperation
22
23
  from functools import lru_cache
23
24
  from logging import getLogger
24
25
  from os import path
25
26
  from pathlib import Path
26
- 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
+ )
27
39
  from datetime import date, datetime, time, timezone
28
40
 
29
41
  import eth_portfolio
@@ -33,13 +45,14 @@ from brownie.convert.datatypes import HexString
33
45
  from brownie.exceptions import EventLookupError
34
46
  from brownie.network.event import EventDict, _EventItem
35
47
  from brownie.network.transaction import TransactionReceipt
36
- from eth_typing import ChecksumAddress, HexAddress, HexStr
37
48
  from eth_portfolio.structs import (
38
49
  InternalTransfer,
39
50
  LedgerEntry,
40
51
  TokenTransfer,
41
52
  Transaction,
42
53
  )
54
+ from eth_retry import auto_retry
55
+ from eth_typing import ChecksumAddress, HexAddress, HexStr
43
56
  from pony.orm import (
44
57
  Database,
45
58
  InterfaceError,
@@ -63,6 +76,9 @@ from dao_treasury.constants import CHAINID
63
76
  from dao_treasury.types import TxGroupDbid, TxGroupName
64
77
 
65
78
 
79
+ EventItem = _EventItem[_EventItem[OrderedDict[str, Any]]]
80
+
81
+
66
82
  SQLITE_DIR = Path(path.expanduser("~")) / ".dao-treasury"
67
83
  """Path to the directory in the user's home where the DAO treasury SQLite database is stored."""
68
84
 
@@ -71,6 +87,7 @@ SQLITE_DIR.mkdir(parents=True, exist_ok=True)
71
87
 
72
88
  _INSERT_THREAD = AsyncThreadPoolExecutor(1)
73
89
  _SORT_THREAD = AsyncThreadPoolExecutor(1)
90
+ _EVENTS_THREADS = AsyncThreadPoolExecutor(16)
74
91
  _SORT_SEMAPHORE = Semaphore(50)
75
92
 
76
93
  _UTC = timezone.utc
@@ -237,6 +254,10 @@ class Address(DbEntity):
237
254
  def contract(self) -> Contract:
238
255
  return Contract(self.address)
239
256
 
257
+ @property
258
+ def contract_coro(self) -> Coroutine[Any, Any, Contract]:
259
+ return Contract.coroutine(self.address)
260
+
240
261
  @staticmethod
241
262
  @lru_cache(maxsize=None)
242
263
  def get_dbid(address: HexAddress) -> int:
@@ -407,6 +428,10 @@ class Token(DbEntity):
407
428
  def contract(self) -> Contract:
408
429
  return Contract(self.address.address)
409
430
 
431
+ @property
432
+ def contract_coro(self) -> Coroutine[Any, Any, Contract]:
433
+ return Contract.coroutine(self.address.address)
434
+
410
435
  @property
411
436
  def scale(self) -> int:
412
437
  """Base for division according to `decimals`, e.g., `10**decimals`.
@@ -721,6 +746,10 @@ class TreasuryTx(DbEntity):
721
746
  """Human-readable label for the sender address."""
722
747
  return self.from_address.nickname or self.from_address.address # type: ignore [union-attr]
723
748
 
749
+ @property
750
+ def token_address(self) -> ChecksumAddress:
751
+ return self.token.address.address
752
+
724
753
  @property
725
754
  def symbol(self) -> str:
726
755
  """Ticker symbol for the transferred token."""
@@ -731,7 +760,23 @@ class TreasuryTx(DbEntity):
731
760
  """Decoded event logs for this transaction."""
732
761
  return self._transaction.events
733
762
 
734
- def get_events(self, event_name: str) -> _EventItem:
763
+ async def events_async(self) -> EventDict:
764
+ """Asynchronously fetch decoded event logs for this transaction."""
765
+ tx = self._transaction
766
+ events = tx._events
767
+ if events is None:
768
+ events = await _EVENTS_THREADS.run(getattr, tx, "events")
769
+ return events
770
+
771
+ @overload
772
+ def get_events(
773
+ self, event_name: str, sync: Literal[False]
774
+ ) -> Coroutine[Any, Any, EventItem]: ...
775
+ @overload
776
+ def get_events(self, event_name: str, sync: bool = True) -> EventItem: ...
777
+ def get_events(self, event_name: str, sync: bool = True) -> EventItem:
778
+ if not sync:
779
+ return _EVENTS_THREADS.run(self.get_events, event_name)
735
780
  try:
736
781
  return self.events[event_name]
737
782
  except EventLookupError:
@@ -748,6 +793,7 @@ class TreasuryTx(DbEntity):
748
793
  return get_transaction(self.hash)
749
794
 
750
795
  @staticmethod
796
+ @auto_retry
751
797
  async def insert(entry: LedgerEntry) -> None:
752
798
  """Asynchronously insert and sort a ledger entry.
753
799
 
@@ -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",