dao-treasury 0.0.41__cp310-cp310-win32.whl → 0.0.70__cp310-cp310-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.
- dao_treasury/.grafana/provisioning/dashboards/breakdowns/Expenses.json +551 -0
- dao_treasury/.grafana/provisioning/dashboards/breakdowns/Revenue.json +551 -0
- dao_treasury/.grafana/provisioning/dashboards/dashboards.yaml +7 -57
- dao_treasury/.grafana/provisioning/dashboards/streams/LlamaPay.json +11 -16
- dao_treasury/.grafana/provisioning/dashboards/summary/Monthly.json +151 -22
- dao_treasury/.grafana/provisioning/dashboards/transactions/Treasury Transactions.json +38 -61
- dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow (Including Unsorted).json +122 -149
- dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow.json +87 -100
- dao_treasury/.grafana/provisioning/dashboards/treasury/Current Treasury Assets.json +981 -0
- dao_treasury/.grafana/provisioning/dashboards/treasury/Historical Treasury Balances.json +2989 -0
- dao_treasury/.grafana/provisioning/dashboards/treasury/Operating Cashflow.json +64 -78
- dao_treasury/_docker.cp310-win32.pyd +0 -0
- dao_treasury/_docker.py +24 -20
- dao_treasury/_nicknames.cp310-win32.pyd +0 -0
- dao_treasury/_wallet.cp310-win32.pyd +0 -0
- dao_treasury/constants.cp310-win32.pyd +0 -0
- dao_treasury/constants.py +5 -0
- dao_treasury/db.py +49 -3
- dao_treasury/docker-compose.yaml +1 -1
- dao_treasury/main.py +39 -1
- dao_treasury/sorting/__init__.cp310-win32.pyd +0 -0
- dao_treasury/sorting/_matchers.cp310-win32.pyd +0 -0
- dao_treasury/sorting/_rules.cp310-win32.pyd +0 -0
- dao_treasury/sorting/factory.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rule.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rule.py +8 -10
- dao_treasury/sorting/rules/__init__.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rules/ignore/__init__.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rules/ignore/llamapay.cp310-win32.pyd +0 -0
- dao_treasury/streams/__init__.cp310-win32.pyd +0 -0
- dao_treasury/streams/llamapay.cp310-win32.pyd +0 -0
- dao_treasury/treasury.py +4 -2
- dao_treasury/types.cp310-win32.pyd +0 -0
- {dao_treasury-0.0.41.dist-info → dao_treasury-0.0.70.dist-info}/METADATA +18 -3
- dao_treasury-0.0.70.dist-info/RECORD +54 -0
- dao_treasury-0.0.70.dist-info/top_level.txt +2 -0
- dao_treasury__mypyc.cp310-win32.pyd +0 -0
- bf2b4fe1f86ad2ea158b__mypyc.cp310-win32.pyd +0 -0
- dao_treasury/.grafana/provisioning/dashboards/treasury/Treasury.json +0 -2018
- dao_treasury-0.0.41.dist-info/RECORD +0 -51
- dao_treasury-0.0.41.dist-info/top_level.txt +0 -2
- {dao_treasury-0.0.41.dist-info → dao_treasury-0.0.70.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":
|
|
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
|
-
"
|
|
67
|
+
"showPercentChange": false,
|
|
68
|
+
"textMode": "auto",
|
|
69
|
+
"wideLayout": true
|
|
68
70
|
},
|
|
69
|
-
"pluginVersion": "
|
|
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":
|
|
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
|
-
"
|
|
129
|
+
"showPercentChange": false,
|
|
130
|
+
"textMode": "auto",
|
|
131
|
+
"wideLayout": true
|
|
130
132
|
},
|
|
131
|
-
"pluginVersion": "
|
|
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":
|
|
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
|
-
"
|
|
191
|
+
"showPercentChange": false,
|
|
192
|
+
"textMode": "auto",
|
|
193
|
+
"wideLayout": true
|
|
192
194
|
},
|
|
193
|
-
"pluginVersion": "
|
|
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":
|
|
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
|
-
"
|
|
254
|
+
"showPercentChange": false,
|
|
255
|
+
"textMode": "auto",
|
|
256
|
+
"wideLayout": true
|
|
255
257
|
},
|
|
256
|
-
"pluginVersion": "
|
|
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":
|
|
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":
|
|
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":
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
Binary file
|
|
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
|
|
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
|
|
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
|
|
dao_treasury/docker-compose.yaml
CHANGED
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",
|
|
@@ -119,6 +125,18 @@ parser.add_argument(
|
|
|
119
125
|
help="Port for the Grafana rendering service. Default: 8091",
|
|
120
126
|
default=8091,
|
|
121
127
|
)
|
|
128
|
+
parser.add_argument(
|
|
129
|
+
"--custom-bucket",
|
|
130
|
+
type=str,
|
|
131
|
+
action="append",
|
|
132
|
+
help=(
|
|
133
|
+
"Custom bucket mapping for a wallet address. "
|
|
134
|
+
"Specify as 'address:bucket_name'. "
|
|
135
|
+
"Can be used multiple times. Example: "
|
|
136
|
+
"--custom-bucket '0x123:My Bucket' --custom-bucket '0x456:Other Bucket'"
|
|
137
|
+
),
|
|
138
|
+
default=None,
|
|
139
|
+
)
|
|
122
140
|
|
|
123
141
|
args = parser.parse_args()
|
|
124
142
|
|
|
@@ -203,7 +221,27 @@ async def export(args) -> None:
|
|
|
203
221
|
for address in addresses:
|
|
204
222
|
db.Address.set_nickname(address, nickname)
|
|
205
223
|
|
|
206
|
-
|
|
224
|
+
# Parse custom_buckets from --custom-bucket arguments
|
|
225
|
+
custom_buckets = None
|
|
226
|
+
if args.custom_bucket:
|
|
227
|
+
custom_buckets = {}
|
|
228
|
+
for item in args.custom_bucket:
|
|
229
|
+
if ":" not in item:
|
|
230
|
+
parser.error(
|
|
231
|
+
f"Invalid format for --custom-bucket: '{item}'. Must be 'address:bucket_name'."
|
|
232
|
+
)
|
|
233
|
+
address, bucket = item.split(":", 1)
|
|
234
|
+
address = address.strip()
|
|
235
|
+
bucket = bucket.strip()
|
|
236
|
+
if not address or not bucket:
|
|
237
|
+
parser.error(
|
|
238
|
+
f"Invalid format for --custom-bucket: '{item}'. Both address and bucket_name are required."
|
|
239
|
+
)
|
|
240
|
+
custom_buckets[address] = bucket
|
|
241
|
+
|
|
242
|
+
treasury = Treasury(
|
|
243
|
+
wallets, args.sort_rules, custom_buckets=custom_buckets, asynchronous=True
|
|
244
|
+
)
|
|
207
245
|
|
|
208
246
|
# Start only the requested containers
|
|
209
247
|
if args.start_renderer is True:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|