dao-treasury 0.0.10__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 -7
- dao_treasury/.grafana/provisioning/dashboards/streams/LlamaPay.json +220 -0
- dao_treasury/.grafana/provisioning/dashboards/summary/Monthly.json +153 -29
- dao_treasury/.grafana/provisioning/dashboards/transactions/Treasury Transactions.json +181 -29
- dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow (Including Unsorted).json +808 -0
- dao_treasury/.grafana/provisioning/dashboards/treasury/Cashflow.json +602 -0
- 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 +478 -0
- dao_treasury/.grafana/provisioning/datasources/datasources.yaml +17 -0
- dao_treasury/ENVIRONMENT_VARIABLES.py +20 -0
- dao_treasury/__init__.py +36 -10
- dao_treasury/_docker.cp310-win32.pyd +0 -0
- dao_treasury/_docker.py +169 -37
- dao_treasury/_nicknames.cp310-win32.pyd +0 -0
- dao_treasury/_nicknames.py +32 -0
- dao_treasury/_wallet.cp310-win32.pyd +0 -0
- dao_treasury/_wallet.py +164 -12
- dao_treasury/constants.cp310-win32.pyd +0 -0
- dao_treasury/constants.py +39 -0
- dao_treasury/db.py +925 -150
- dao_treasury/docker-compose.yaml +6 -5
- dao_treasury/main.py +238 -28
- dao_treasury/sorting/__init__.cp310-win32.pyd +0 -0
- dao_treasury/sorting/__init__.py +219 -115
- dao_treasury/sorting/_matchers.cp310-win32.pyd +0 -0
- dao_treasury/sorting/_matchers.py +261 -17
- dao_treasury/sorting/_rules.cp310-win32.pyd +0 -0
- dao_treasury/sorting/_rules.py +166 -21
- dao_treasury/sorting/factory.cp310-win32.pyd +0 -0
- dao_treasury/sorting/factory.py +245 -37
- dao_treasury/sorting/rule.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rule.py +228 -46
- dao_treasury/sorting/rules/__init__.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rules/__init__.py +1 -0
- dao_treasury/sorting/rules/ignore/__init__.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rules/ignore/__init__.py +1 -0
- dao_treasury/sorting/rules/ignore/llamapay.cp310-win32.pyd +0 -0
- dao_treasury/sorting/rules/ignore/llamapay.py +20 -0
- dao_treasury/streams/__init__.cp310-win32.pyd +0 -0
- dao_treasury/streams/__init__.py +0 -0
- dao_treasury/streams/llamapay.cp310-win32.pyd +0 -0
- dao_treasury/streams/llamapay.py +388 -0
- dao_treasury/treasury.py +118 -25
- dao_treasury/types.cp310-win32.pyd +0 -0
- dao_treasury/types.py +104 -7
- dao_treasury-0.0.70.dist-info/METADATA +134 -0
- 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
- a743a720bbc4482d330e__mypyc.cp310-win32.pyd +0 -0
- dao_treasury/.grafana/provisioning/datasources/sqlite.yaml +0 -10
- dao_treasury-0.0.10.dist-info/METADATA +0 -36
- dao_treasury-0.0.10.dist-info/RECORD +0 -28
- dao_treasury-0.0.10.dist-info/top_level.txt +0 -2
- {dao_treasury-0.0.10.dist-info → dao_treasury-0.0.70.dist-info}/WHEEL +0 -0
dao_treasury/sorting/factory.py
CHANGED
|
@@ -1,74 +1,239 @@
|
|
|
1
|
-
from typing import Any, Final, Generic, Optional,
|
|
2
|
-
|
|
3
|
-
from y import constants
|
|
1
|
+
from typing import Any, Final, Generic, Optional, Union, final, overload
|
|
4
2
|
|
|
3
|
+
from dao_treasury.constants import CHAINID
|
|
5
4
|
from dao_treasury.sorting.rule import (
|
|
6
|
-
CostOfRevenueSortRule,
|
|
7
|
-
ExpenseSortRule,
|
|
8
|
-
IgnoreSortRule,
|
|
9
|
-
OtherExpenseSortRule,
|
|
10
|
-
OtherIncomeSortRule,
|
|
11
|
-
RevenueSortRule,
|
|
12
|
-
)
|
|
13
|
-
from dao_treasury.types import Networks, SortFunction, TxGroupName
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
TRule = TypeVar(
|
|
17
|
-
"TRule",
|
|
18
|
-
RevenueSortRule,
|
|
19
5
|
CostOfRevenueSortRule,
|
|
20
6
|
ExpenseSortRule,
|
|
21
|
-
OtherIncomeSortRule,
|
|
22
|
-
OtherExpenseSortRule,
|
|
23
7
|
IgnoreSortRule,
|
|
8
|
+
OtherExpenseSortRule,
|
|
9
|
+
OtherIncomeSortRule,
|
|
10
|
+
RevenueSortRule,
|
|
11
|
+
TRule,
|
|
24
12
|
)
|
|
13
|
+
from dao_treasury.types import Networks, SortFunction, TxGroupName
|
|
14
|
+
|
|
25
15
|
|
|
16
|
+
def revenue(
|
|
17
|
+
txgroup: TxGroupName, networks: Networks = CHAINID
|
|
18
|
+
) -> "SortRuleFactory[RevenueSortRule]":
|
|
19
|
+
"""Create a factory to register revenue sort rules for a given transaction group.
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
Args:
|
|
22
|
+
txgroup: Base name of the transaction group to categorize as revenue.
|
|
23
|
+
networks: Network ID or iterable of network IDs on which this rule applies.
|
|
28
24
|
|
|
25
|
+
See Also:
|
|
26
|
+
:func:`cost_of_revenue`
|
|
27
|
+
:class:`SortRuleFactory`
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
Examples:
|
|
30
|
+
>>> from dao_treasury.sorting.factory import revenue
|
|
31
|
+
>>> @revenue("Token Sales")
|
|
32
|
+
... def match_sales(tx):
|
|
33
|
+
... return tx.amount > 0 and tx.to_address is not None
|
|
34
|
+
"""
|
|
31
35
|
return SortRuleFactory(txgroup, networks, RevenueSortRule)
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
|
|
38
|
+
def cost_of_revenue(
|
|
39
|
+
txgroup: TxGroupName, networks: Networks = CHAINID
|
|
40
|
+
) -> "SortRuleFactory[CostOfRevenueSortRule]":
|
|
41
|
+
"""Create a factory to register cost‐of‐revenue sort rules for a given transaction group.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
txgroup: Base name of the transaction group to categorize as cost of revenue.
|
|
45
|
+
networks: Network ID or iterable of network IDs on which this rule applies.
|
|
46
|
+
|
|
47
|
+
See Also:
|
|
48
|
+
:func:`revenue`
|
|
49
|
+
:class:`SortRuleFactory`
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
>>> from dao_treasury.sorting.factory import cost_of_revenue
|
|
53
|
+
>>> @cost_of_revenue("Manufacturing")
|
|
54
|
+
... def match_manufacturing(tx):
|
|
55
|
+
... return tx.from_address is not None and tx.amount_usd > 1000
|
|
56
|
+
"""
|
|
34
57
|
return SortRuleFactory(txgroup, networks, CostOfRevenueSortRule)
|
|
35
58
|
|
|
36
|
-
|
|
59
|
+
|
|
60
|
+
def expense(
|
|
61
|
+
txgroup: TxGroupName, networks: Networks = CHAINID
|
|
62
|
+
) -> "SortRuleFactory[ExpenseSortRule]":
|
|
63
|
+
"""Create a factory to register expense sort rules for a given transaction group.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
txgroup: Base name of the transaction group to categorize as expense.
|
|
67
|
+
networks: Network ID or iterable of network IDs on which this rule applies.
|
|
68
|
+
|
|
69
|
+
See Also:
|
|
70
|
+
:func:`other_expense`
|
|
71
|
+
:class:`SortRuleFactory`
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
>>> from dao_treasury.sorting.factory import expense
|
|
75
|
+
>>> @expense("Office Supplies")
|
|
76
|
+
... def match_supplies(tx):
|
|
77
|
+
... return tx.symbol == "USD" and tx.amount < 500
|
|
78
|
+
"""
|
|
37
79
|
return SortRuleFactory(txgroup, networks, ExpenseSortRule)
|
|
38
80
|
|
|
39
|
-
|
|
81
|
+
|
|
82
|
+
def other_income(
|
|
83
|
+
txgroup: TxGroupName, networks: Networks = CHAINID
|
|
84
|
+
) -> "SortRuleFactory[OtherIncomeSortRule]":
|
|
85
|
+
"""Create a factory to register other‐income sort rules for a given transaction group.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
txgroup: Base name of the transaction group to categorize as other income.
|
|
89
|
+
networks: Network ID or iterable of network IDs on which this rule applies.
|
|
90
|
+
|
|
91
|
+
See Also:
|
|
92
|
+
:func:`revenue`
|
|
93
|
+
:class:`SortRuleFactory`
|
|
94
|
+
|
|
95
|
+
Examples:
|
|
96
|
+
>>> from dao_treasury.sorting.factory import other_income
|
|
97
|
+
>>> @other_income("Interest")
|
|
98
|
+
... def match_interest(tx):
|
|
99
|
+
... return tx.token_address == SOME_TOKEN and tx.amount > 0
|
|
100
|
+
"""
|
|
40
101
|
return SortRuleFactory(txgroup, networks, OtherIncomeSortRule)
|
|
41
102
|
|
|
42
|
-
|
|
103
|
+
|
|
104
|
+
def other_expense(
|
|
105
|
+
txgroup: TxGroupName, networks: Networks = CHAINID
|
|
106
|
+
) -> "SortRuleFactory[OtherExpenseSortRule]":
|
|
107
|
+
"""Create a factory to register other‐expense sort rules for a given transaction group.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
txgroup: Base name of the transaction group to categorize as other expense.
|
|
111
|
+
networks: Network ID or iterable of network IDs on which this rule applies.
|
|
112
|
+
|
|
113
|
+
See Also:
|
|
114
|
+
:func:`expense`
|
|
115
|
+
:class:`SortRuleFactory`
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
>>> from dao_treasury.sorting.factory import other_expense
|
|
119
|
+
>>> @other_expense("Misc Fees")
|
|
120
|
+
... def match_misc(tx):
|
|
121
|
+
... return tx.amount_usd < 0 and tx.symbol == "ETH"
|
|
122
|
+
"""
|
|
43
123
|
return SortRuleFactory(txgroup, networks, OtherExpenseSortRule)
|
|
44
124
|
|
|
45
|
-
|
|
125
|
+
|
|
126
|
+
def ignore(
|
|
127
|
+
txgroup: TxGroupName, networks: Networks = CHAINID
|
|
128
|
+
) -> "SortRuleFactory[IgnoreSortRule]":
|
|
129
|
+
"""Create a factory to register ignore sort rules for a given transaction group.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
txgroup: Base name of the transaction group to categorize as ignored.
|
|
133
|
+
networks: Network ID or iterable of network IDs on which this rule applies.
|
|
134
|
+
|
|
135
|
+
See Also:
|
|
136
|
+
:class:`SortRuleFactory`
|
|
137
|
+
|
|
138
|
+
Examples:
|
|
139
|
+
>>> from dao_treasury.sorting.factory import ignore
|
|
140
|
+
>>> @ignore("Dust")
|
|
141
|
+
... def match_dust(tx):
|
|
142
|
+
... return abs(tx.value_usd) < 0.01
|
|
143
|
+
"""
|
|
46
144
|
return SortRuleFactory(txgroup, networks, IgnoreSortRule)
|
|
47
145
|
|
|
48
146
|
|
|
49
147
|
@final
|
|
50
148
|
class SortRuleFactory(Generic[TRule]):
|
|
149
|
+
"""Builder for creating sort rule instances for a specific transaction group and network(s).
|
|
150
|
+
|
|
151
|
+
This factory supports two patterns:
|
|
152
|
+
|
|
153
|
+
1. Decorating a function to register a dynamic matching rule.
|
|
154
|
+
2. Calling :meth:`match` to supply static match attributes.
|
|
155
|
+
|
|
156
|
+
Use the convenience functions like :func:`revenue`, :func:`expense`, etc.,
|
|
157
|
+
to obtain an instance of this factory preconfigured with the appropriate rule type.
|
|
158
|
+
|
|
159
|
+
Examples:
|
|
160
|
+
>>> from dao_treasury.sorting.factory import revenue
|
|
161
|
+
>>> @revenue("Sales", networks=[1, 3])
|
|
162
|
+
... def match_large_sales(tx):
|
|
163
|
+
... return tx.value_usd > 1000
|
|
164
|
+
"""
|
|
165
|
+
|
|
51
166
|
def __init__(
|
|
52
167
|
self,
|
|
53
|
-
txgroup: TxGroupName,
|
|
168
|
+
txgroup: TxGroupName,
|
|
54
169
|
networks: Networks,
|
|
55
|
-
rule_type: TRule,
|
|
170
|
+
rule_type: TRule,
|
|
56
171
|
) -> None:
|
|
172
|
+
"""Initialize the sort rule factory.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
txgroup: Base name of the transaction group.
|
|
176
|
+
networks: Single network ID or iterable of network IDs where the rule applies.
|
|
177
|
+
rule_type: Sort rule class (e.g., RevenueSortRule) to instantiate.
|
|
178
|
+
"""
|
|
57
179
|
self.txgroup: Final = txgroup
|
|
58
|
-
self.networks: Final =
|
|
180
|
+
self.networks: Final = (
|
|
181
|
+
[networks] if isinstance(networks, int) else list(networks)
|
|
182
|
+
)
|
|
59
183
|
self.rule_type: Final = rule_type
|
|
60
184
|
self._rule: Optional[TRule] = None
|
|
185
|
+
|
|
61
186
|
@overload
|
|
62
|
-
def __call__(
|
|
187
|
+
def __call__(
|
|
188
|
+
self, txgroup_name: TxGroupName, networks: Optional[Networks] = None
|
|
189
|
+
) -> "SortRuleFactory":
|
|
190
|
+
"""Configure a nested sub‐group.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
txgroup_name: Sub‐group name.
|
|
194
|
+
networks: Optional network specification.
|
|
195
|
+
"""
|
|
196
|
+
|
|
63
197
|
@overload
|
|
64
|
-
def __call__(self, func: SortFunction) -> SortFunction
|
|
198
|
+
def __call__(self, func: SortFunction) -> SortFunction:
|
|
199
|
+
"""Register a matching function.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
func: The custom matching function.
|
|
203
|
+
"""
|
|
204
|
+
|
|
65
205
|
def __call__( # type: ignore [misc]
|
|
66
|
-
self,
|
|
206
|
+
self,
|
|
67
207
|
func: Union[TxGroupName, SortFunction],
|
|
68
208
|
networks: Optional[Networks] = None,
|
|
69
209
|
) -> Union["SortRuleFactory", SortFunction]:
|
|
210
|
+
"""Configure a nested sub‐group or register a matching function.
|
|
211
|
+
|
|
212
|
+
Overloads:
|
|
213
|
+
* If `func` is a string, returns a new factory for `txgroup:func`.
|
|
214
|
+
* If `func` is callable, registers it as the match logic.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
func: Sub‐group suffix (str) or a custom matching function.
|
|
218
|
+
networks: Optional networks override (only valid when `func` is str).
|
|
219
|
+
|
|
220
|
+
Raises:
|
|
221
|
+
RuntimeError: If `networks` is passed when `func` is callable.
|
|
222
|
+
ValueError: If `func` is neither str nor callable.
|
|
223
|
+
|
|
224
|
+
See Also:
|
|
225
|
+
:meth:`match`
|
|
226
|
+
|
|
227
|
+
Examples:
|
|
228
|
+
>>> fees = expense("Fees")
|
|
229
|
+
>>> @fees("Gas")
|
|
230
|
+
... def match_gas(tx):
|
|
231
|
+
... return tx.symbol == "ETH"
|
|
232
|
+
"""
|
|
70
233
|
if isinstance(func, str):
|
|
71
|
-
return SortRuleFactory(
|
|
234
|
+
return SortRuleFactory(
|
|
235
|
+
f"{self.txgroup}:{func}", networks or self.networks, self.rule_type
|
|
236
|
+
)
|
|
72
237
|
elif callable(func):
|
|
73
238
|
if networks:
|
|
74
239
|
raise RuntimeError("you can only pass networks if `func` is a string")
|
|
@@ -77,15 +242,58 @@ class SortRuleFactory(Generic[TRule]):
|
|
|
77
242
|
self._rule = self.rule_type(txgroup=self.txgroup, func=func)
|
|
78
243
|
return func
|
|
79
244
|
raise ValueError(func)
|
|
245
|
+
|
|
80
246
|
@property
|
|
81
247
|
def rule(self) -> Optional[TRule]:
|
|
248
|
+
"""Return the created sort rule instance, if any.
|
|
249
|
+
|
|
250
|
+
After decoration or a call to :meth:`match`, this property holds the
|
|
251
|
+
concrete :class:`~dao_treasury.types.SortRule` instance.
|
|
252
|
+
|
|
253
|
+
Examples:
|
|
254
|
+
>>> @other_income("Interest")
|
|
255
|
+
... def match_i(tx):
|
|
256
|
+
... return tx.value_usd > 100
|
|
257
|
+
"""
|
|
82
258
|
return self._rule
|
|
83
|
-
|
|
259
|
+
|
|
260
|
+
def match(
|
|
261
|
+
self, func: None = None, **match_values: Any
|
|
262
|
+
) -> None: # TODO: give this proper kwargs
|
|
263
|
+
"""Define static matching attributes for the sort rule.
|
|
264
|
+
|
|
265
|
+
Call this method with keyword matchers corresponding to rule attributes
|
|
266
|
+
(e.g., hash, from_address, symbol) to create a rule matching based on these values.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
func: Must be None; a function match must use the decorator form.
|
|
270
|
+
**match_values: Attribute values for matching (e.g., hash="0x123", symbol="DAI").
|
|
271
|
+
|
|
272
|
+
Raises:
|
|
273
|
+
ValueError: If `func` is not None.
|
|
274
|
+
RuntimeError: If a matcher has already been set.
|
|
275
|
+
|
|
276
|
+
See Also:
|
|
277
|
+
:meth:`__call__`
|
|
278
|
+
|
|
279
|
+
Examples:
|
|
280
|
+
>>> ignore("Dust").match(symbol="WETH", from_address="0xAAA")
|
|
281
|
+
"""
|
|
84
282
|
if func is not None:
|
|
85
|
-
raise ValueError(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
283
|
+
raise ValueError(
|
|
284
|
+
f"You cannot pass a func here, call {self} with the function as the sole arg instead"
|
|
285
|
+
)
|
|
286
|
+
# Only instantiate when we're on an allowed network
|
|
287
|
+
if CHAINID in self.networks:
|
|
288
|
+
self.__check_locked()
|
|
289
|
+
self._rule = self.rule_type(txgroup=self.txgroup, **match_values)
|
|
290
|
+
self.locked = True
|
|
291
|
+
|
|
89
292
|
def __check_locked(self) -> None:
|
|
293
|
+
"""Ensure that no matcher has already been registered.
|
|
294
|
+
|
|
295
|
+
Raises:
|
|
296
|
+
RuntimeError: If this factory already has a matcher assigned.
|
|
297
|
+
"""
|
|
90
298
|
if self._rule is not None:
|
|
91
299
|
raise RuntimeError(f"{self} already has a matcher")
|
|
Binary file
|