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/rule.py
CHANGED
|
@@ -1,53 +1,148 @@
|
|
|
1
|
+
# dao_treasury/sorting/rule.py
|
|
2
|
+
|
|
3
|
+
"""Module defining transaction sorting rules for the DAO treasury.
|
|
4
|
+
|
|
5
|
+
This module provides the `_SortRule` base class and subclasses for categorizing
|
|
6
|
+
`TreasuryTx` entries based on their attributes or a custom function. When a rule
|
|
7
|
+
is instantiated, it registers itself in the global `SORT_RULES` mapping under its
|
|
8
|
+
class and configures which transaction attributes to match via `_match_all`.
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
# Define a revenue rule for sales (assuming you only transact in DAI for sales)
|
|
12
|
+
>>> from dao_treasury.sorting.rule import RevenueSortRule, SORT_RULES
|
|
13
|
+
>>> RevenueSortRule(
|
|
14
|
+
... txgroup='Sale',
|
|
15
|
+
... token_address='0x6B175474E89094d879c81e570a000000000000',
|
|
16
|
+
... symbol='DAI'
|
|
17
|
+
... )
|
|
18
|
+
# Inspect rules registered for RevenueSortRule
|
|
19
|
+
>>> len(SORT_RULES[RevenueSortRule])
|
|
20
|
+
1
|
|
21
|
+
|
|
22
|
+
# Iterate over all ExpenseSortRule instances
|
|
23
|
+
>>> from dao_treasury.sorting.rule import ExpenseSortRule
|
|
24
|
+
>>> for rule in SORT_RULES[ExpenseSortRule]:
|
|
25
|
+
... print(rule.txgroup)
|
|
26
|
+
|
|
27
|
+
See Also:
|
|
28
|
+
:const:`~dao_treasury.sorting.rule.SORT_RULES`
|
|
29
|
+
:class:`~dao_treasury.sorting.rule._SortRule`
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from collections import defaultdict
|
|
1
33
|
from dataclasses import dataclass
|
|
2
|
-
from
|
|
34
|
+
from logging import getLogger
|
|
35
|
+
from typing import (
|
|
36
|
+
TYPE_CHECKING,
|
|
37
|
+
DefaultDict,
|
|
38
|
+
Dict,
|
|
39
|
+
Final,
|
|
40
|
+
List,
|
|
41
|
+
Optional,
|
|
42
|
+
Type,
|
|
43
|
+
TypeVar,
|
|
44
|
+
)
|
|
3
45
|
|
|
4
46
|
from brownie.convert.datatypes import EthAddress
|
|
5
47
|
from eth_typing import HexStr
|
|
48
|
+
from mypy_extensions import mypyc_attr
|
|
6
49
|
|
|
7
50
|
from dao_treasury._wallet import TreasuryWallet
|
|
8
|
-
from dao_treasury.types import SortFunction, TxGroupDbid, TxGroupName
|
|
51
|
+
from dao_treasury.types import SortFunction, SortRule, TxGroupDbid, TxGroupName
|
|
9
52
|
|
|
10
53
|
if TYPE_CHECKING:
|
|
11
54
|
from dao_treasury.db import TreasuryTx
|
|
12
55
|
|
|
13
56
|
|
|
14
|
-
|
|
57
|
+
logger: Final = getLogger(__name__)
|
|
58
|
+
_log_debug: Final = logger.debug
|
|
15
59
|
|
|
60
|
+
SORT_RULES: DefaultDict[Type[SortRule], List[SortRule]] = defaultdict(list)
|
|
61
|
+
"""Mapping from sort rule classes to lists of instantiated rules, in creation order per class.
|
|
62
|
+
|
|
63
|
+
Each key is a subclass of :class:`~dao_treasury.types.SortRule` and the corresponding
|
|
64
|
+
value is the list of rule instances of that class.
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
>>> from dao_treasury.sorting.rule import RevenueSortRule, SORT_RULES
|
|
68
|
+
>>> RevenueSortRule(txgroup='Interest', symbol='DAI')
|
|
69
|
+
>>> SORT_RULES[RevenueSortRule][0].txgroup
|
|
70
|
+
'Revenue:Interest'
|
|
71
|
+
"""
|
|
16
72
|
|
|
17
73
|
_match_all: Final[Dict[TxGroupName, List[str]]] = {}
|
|
18
|
-
"""An internal cache defining
|
|
74
|
+
"""An internal cache defining which matcher attributes are used for each `txgroup`."""
|
|
19
75
|
|
|
76
|
+
_MATCHING_ATTRS: Final = (
|
|
77
|
+
"hash",
|
|
78
|
+
"from_address",
|
|
79
|
+
"from_nickname",
|
|
80
|
+
"to_address",
|
|
81
|
+
"to_nickname",
|
|
82
|
+
"token_address",
|
|
83
|
+
"symbol",
|
|
84
|
+
"log_index",
|
|
85
|
+
)
|
|
20
86
|
|
|
87
|
+
|
|
88
|
+
@mypyc_attr(native_class=False)
|
|
21
89
|
@dataclass(kw_only=True, frozen=True)
|
|
22
90
|
class _SortRule:
|
|
91
|
+
"""Base class for defining transaction matching rules.
|
|
92
|
+
|
|
93
|
+
When instantiated, a rule validates its inputs, determines which transaction
|
|
94
|
+
attributes to match (or uses a custom function), and registers itself
|
|
95
|
+
in the global `SORT_RULES` mapping under its class.
|
|
96
|
+
|
|
97
|
+
Matched transactions are assigned to the specified `txgroup`.
|
|
98
|
+
|
|
99
|
+
See Also:
|
|
100
|
+
:const:`dao_treasury.sorting.rule.SORT_RULES`
|
|
101
|
+
"""
|
|
102
|
+
|
|
23
103
|
txgroup: TxGroupName
|
|
104
|
+
"""Name of the transaction group to assign upon match."""
|
|
105
|
+
|
|
24
106
|
hash: Optional[HexStr] = None
|
|
107
|
+
"""Exact transaction hash to match."""
|
|
108
|
+
|
|
25
109
|
from_address: Optional[EthAddress] = None
|
|
110
|
+
"""Source wallet address to match."""
|
|
111
|
+
|
|
26
112
|
from_nickname: Optional[str] = None
|
|
113
|
+
"""Sender nickname (alias) to match."""
|
|
114
|
+
|
|
27
115
|
to_address: Optional[EthAddress] = None
|
|
116
|
+
"""Recipient wallet address to match."""
|
|
117
|
+
|
|
28
118
|
to_nickname: Optional[str] = None
|
|
119
|
+
"""Recipient nickname (alias) to match."""
|
|
120
|
+
|
|
29
121
|
token_address: Optional[EthAddress] = None
|
|
122
|
+
"""Token contract address to match."""
|
|
123
|
+
|
|
30
124
|
symbol: Optional[str] = None
|
|
125
|
+
"""Token symbol to match."""
|
|
126
|
+
|
|
31
127
|
log_index: Optional[int] = None
|
|
32
|
-
|
|
128
|
+
"""Log index within the transaction receipt to match."""
|
|
33
129
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"hash",
|
|
37
|
-
"from_address",
|
|
38
|
-
"from_nickname",
|
|
39
|
-
"to_address",
|
|
40
|
-
"to_nickname",
|
|
41
|
-
"token_address",
|
|
42
|
-
"symbol",
|
|
43
|
-
"log_index",
|
|
44
|
-
]
|
|
130
|
+
func: Optional[SortFunction] = None
|
|
131
|
+
"""Custom matching function that takes a `TreasuryTx` and returns a bool or an awaitable that returns a bool."""
|
|
45
132
|
|
|
46
133
|
def __post_init__(self) -> None:
|
|
47
|
-
"""
|
|
48
|
-
|
|
134
|
+
"""Validate inputs, checksum addresses, and register the rule.
|
|
135
|
+
|
|
136
|
+
- Ensures no duplicate rule exists for the same `txgroup`.
|
|
137
|
+
- Converts address fields to checksummed format.
|
|
138
|
+
- Determines which attributes will be used for direct matching.
|
|
139
|
+
- Validates that exactly one of attribute-based or function-based matching is provided.
|
|
140
|
+
- Registers the instance in :attr:`SORT_RULES` and :data:`_match_all`.
|
|
141
|
+
"""
|
|
49
142
|
if self.txgroup in _match_all:
|
|
50
|
-
raise ValueError(
|
|
143
|
+
raise ValueError(
|
|
144
|
+
f"there is already a matcher defined for txgroup {self.txgroup}: {self}"
|
|
145
|
+
)
|
|
51
146
|
|
|
52
147
|
# ensure addresses are checksummed if applicable
|
|
53
148
|
for attr in ["from_address", "to_address", "token_address"]:
|
|
@@ -58,105 +153,192 @@ class _SortRule:
|
|
|
58
153
|
object.__setattr__(self, attr, checksummed)
|
|
59
154
|
|
|
60
155
|
# define matchers used for this instance
|
|
61
|
-
|
|
62
|
-
matchers = [
|
|
63
|
-
attr
|
|
64
|
-
for attr in self.__matching_attrs__
|
|
65
|
-
if getattr(self, attr) is not None
|
|
66
|
-
]
|
|
67
|
-
|
|
156
|
+
matchers = [attr for attr in _MATCHING_ATTRS if getattr(self, attr) is not None]
|
|
68
157
|
_match_all[self.txgroup] = matchers
|
|
69
158
|
|
|
70
159
|
if self.func is not None and matchers:
|
|
71
160
|
raise ValueError(
|
|
72
161
|
"You must specify attributes for matching or pass in a custom matching function, not both."
|
|
73
162
|
)
|
|
74
|
-
|
|
163
|
+
|
|
75
164
|
if self.func is None and not matchers:
|
|
76
165
|
raise ValueError(
|
|
77
166
|
"You must specify attributes for matching or pass in a custom matching function."
|
|
78
167
|
)
|
|
79
|
-
|
|
168
|
+
|
|
80
169
|
if self.func is not None and not callable(self.func):
|
|
81
170
|
raise TypeError(f"func must be callable. You passed {self.func}")
|
|
82
171
|
|
|
83
172
|
# append new instance to instances classvar
|
|
84
|
-
|
|
85
|
-
|
|
173
|
+
# TODO: fix dataclass ClassVar handling in mypyc and reenable
|
|
174
|
+
# self.__instances__.append(self)
|
|
175
|
+
|
|
176
|
+
# append new instance under its class key
|
|
177
|
+
SORT_RULES[type(self)].append(self)
|
|
178
|
+
|
|
86
179
|
@property
|
|
87
180
|
def txgroup_dbid(self) -> TxGroupDbid:
|
|
181
|
+
"""Compute the database ID for this rule's `txgroup`.
|
|
182
|
+
|
|
183
|
+
Splits the `txgroup` string on ':' and resolves or creates the hierarchical
|
|
184
|
+
`TxGroup` entries in the database, returning the final group ID.
|
|
185
|
+
|
|
186
|
+
See Also:
|
|
187
|
+
:class:`~dao_treasury.db.TxGroup`.
|
|
188
|
+
"""
|
|
88
189
|
from dao_treasury.db import TxGroup
|
|
89
190
|
|
|
90
191
|
txgroup = None
|
|
91
|
-
for part in
|
|
192
|
+
for part in self.txgroup.split(":"):
|
|
92
193
|
txgroup = TxGroup.get_dbid(part, txgroup)
|
|
93
194
|
return txgroup
|
|
94
195
|
|
|
95
196
|
async def match(self, tx: "TreasuryTx") -> bool:
|
|
96
|
-
"""
|
|
197
|
+
"""Determine if the given transaction matches this rule.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
tx: A `TreasuryTx` entity to test against this rule.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
True if the transaction matches the rule criteria; otherwise False.
|
|
204
|
+
|
|
205
|
+
Examples:
|
|
206
|
+
# match by symbol and recipient
|
|
207
|
+
>>> rule = _SortRule(txgroup='Foo', symbol='DAI', to_address='0xabc...')
|
|
208
|
+
>>> await rule.match(tx) # where tx.symbol == 'DAI' and tx.to_address == '0xabc...'
|
|
209
|
+
True
|
|
210
|
+
|
|
211
|
+
See Also:
|
|
212
|
+
:attr:`_match_all`
|
|
213
|
+
"""
|
|
97
214
|
if matchers := _match_all[self.txgroup]:
|
|
98
215
|
return all(
|
|
99
|
-
getattr(
|
|
100
|
-
for matcher in matchers
|
|
216
|
+
getattr(tx, matcher) == getattr(self, matcher) for matcher in matchers
|
|
101
217
|
)
|
|
102
218
|
|
|
219
|
+
_log_debug("checking %s for %s", tx, self.func)
|
|
103
220
|
match = self.func(tx) # type: ignore [misc]
|
|
104
221
|
return match if isinstance(match, bool) else await match
|
|
105
222
|
|
|
106
223
|
|
|
224
|
+
@mypyc_attr(native_class=False)
|
|
107
225
|
class _InboundSortRule(_SortRule):
|
|
226
|
+
"""Sort rule that applies only to inbound transactions (to the DAO's wallet).
|
|
227
|
+
|
|
228
|
+
Checks that the transaction's `to_address` belongs to a known `TreasuryWallet`
|
|
229
|
+
before applying the base matching logic.
|
|
230
|
+
"""
|
|
231
|
+
|
|
108
232
|
async def match(self, tx: "TreasuryTx") -> bool:
|
|
109
233
|
return (
|
|
110
234
|
tx.to_address is not None
|
|
111
|
-
and TreasuryWallet.
|
|
112
|
-
and await super().match(tx)
|
|
235
|
+
and TreasuryWallet.check_membership(tx.to_address.address, tx.block)
|
|
236
|
+
and await super(_InboundSortRule, self).match(tx)
|
|
113
237
|
)
|
|
114
238
|
|
|
239
|
+
|
|
240
|
+
@mypyc_attr(native_class=False)
|
|
115
241
|
class _OutboundSortRule(_SortRule):
|
|
242
|
+
"""Sort rule that applies only to outbound transactions (from the DAO's wallet).
|
|
243
|
+
|
|
244
|
+
Checks that the transaction's `from_address` belongs to a known `TreasuryWallet`
|
|
245
|
+
before applying the base matching logic.
|
|
246
|
+
"""
|
|
247
|
+
|
|
116
248
|
async def match(self, tx: "TreasuryTx") -> bool:
|
|
117
|
-
return (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
249
|
+
return TreasuryWallet.check_membership(
|
|
250
|
+
tx.from_address.address, tx.block
|
|
251
|
+
) and await super(_OutboundSortRule, self).match(tx)
|
|
121
252
|
|
|
122
253
|
|
|
254
|
+
@mypyc_attr(native_class=False)
|
|
123
255
|
class RevenueSortRule(_InboundSortRule):
|
|
256
|
+
"""Rule to categorize inbound transactions as revenue.
|
|
257
|
+
|
|
258
|
+
Prepends 'Revenue:' to the `txgroup` name before registration.
|
|
259
|
+
|
|
260
|
+
Examples:
|
|
261
|
+
>>> RevenueSortRule(txgroup='Sale', to_address='0xabc...', symbol='DAI')
|
|
262
|
+
# results in a rule with txgroup 'Revenue:Sale'
|
|
263
|
+
"""
|
|
264
|
+
|
|
124
265
|
def __post_init__(self) -> None:
|
|
125
266
|
"""Prepends `self.txgroup` with 'Revenue:'."""
|
|
126
267
|
object.__setattr__(self, "txgroup", f"Revenue:{self.txgroup}")
|
|
127
|
-
super().__post_init__()
|
|
268
|
+
super(RevenueSortRule, self).__post_init__()
|
|
128
269
|
|
|
129
270
|
|
|
271
|
+
@mypyc_attr(native_class=False)
|
|
130
272
|
class CostOfRevenueSortRule(_OutboundSortRule):
|
|
273
|
+
"""Rule to categorize outbound transactions as cost of revenue.
|
|
274
|
+
|
|
275
|
+
Prepends 'Cost of Revenue:' to the `txgroup` name before registration.
|
|
276
|
+
"""
|
|
277
|
+
|
|
131
278
|
def __post_init__(self) -> None:
|
|
132
279
|
"""Prepends `self.txgroup` with 'Cost of Revenue:'."""
|
|
133
280
|
object.__setattr__(self, "txgroup", f"Cost of Revenue:{self.txgroup}")
|
|
134
|
-
super().__post_init__()
|
|
281
|
+
super(CostOfRevenueSortRule, self).__post_init__()
|
|
135
282
|
|
|
136
283
|
|
|
284
|
+
@mypyc_attr(native_class=False)
|
|
137
285
|
class ExpenseSortRule(_OutboundSortRule):
|
|
286
|
+
"""Rule to categorize outbound transactions as expenses.
|
|
287
|
+
|
|
288
|
+
Prepends 'Expenses:' to the `txgroup` name before registration.
|
|
289
|
+
"""
|
|
290
|
+
|
|
138
291
|
def __post_init__(self) -> None:
|
|
139
292
|
"""Prepends `self.txgroup` with 'Expenses:'."""
|
|
140
293
|
object.__setattr__(self, "txgroup", f"Expenses:{self.txgroup}")
|
|
141
|
-
super().__post_init__()
|
|
294
|
+
super(ExpenseSortRule, self).__post_init__()
|
|
142
295
|
|
|
143
296
|
|
|
297
|
+
@mypyc_attr(native_class=False)
|
|
144
298
|
class OtherIncomeSortRule(_InboundSortRule):
|
|
299
|
+
"""Rule to categorize inbound transactions as other income.
|
|
300
|
+
|
|
301
|
+
Prepends 'Other Income:' to the `txgroup` name before registration.
|
|
302
|
+
"""
|
|
303
|
+
|
|
145
304
|
def __post_init__(self) -> None:
|
|
146
305
|
"""Prepends `self.txgroup` with 'Other Income:'."""
|
|
147
306
|
object.__setattr__(self, "txgroup", f"Other Income:{self.txgroup}")
|
|
148
|
-
super().__post_init__()
|
|
307
|
+
super(OtherIncomeSortRule, self).__post_init__()
|
|
149
308
|
|
|
150
309
|
|
|
310
|
+
@mypyc_attr(native_class=False)
|
|
151
311
|
class OtherExpenseSortRule(_OutboundSortRule):
|
|
312
|
+
"""Rule to categorize outbound transactions as other expenses.
|
|
313
|
+
|
|
314
|
+
Prepends 'Other Expenses:' to the `txgroup` name before registration.
|
|
315
|
+
"""
|
|
316
|
+
|
|
152
317
|
def __post_init__(self) -> None:
|
|
153
318
|
"""Prepends `self.txgroup` with 'Other Expenses:'."""
|
|
154
319
|
object.__setattr__(self, "txgroup", f"Other Expenses:{self.txgroup}")
|
|
155
|
-
super().__post_init__()
|
|
320
|
+
super(OtherExpenseSortRule, self).__post_init__()
|
|
156
321
|
|
|
157
322
|
|
|
323
|
+
@mypyc_attr(native_class=False)
|
|
158
324
|
class IgnoreSortRule(_SortRule):
|
|
325
|
+
"""Rule to ignore certain transactions.
|
|
326
|
+
|
|
327
|
+
Prepends 'Ignore:' to the `txgroup` name before registration.
|
|
328
|
+
"""
|
|
329
|
+
|
|
159
330
|
def __post_init__(self) -> None:
|
|
160
331
|
"""Prepends `self.txgroup` with 'Ignore:'."""
|
|
161
332
|
object.__setattr__(self, "txgroup", f"Ignore:{self.txgroup}")
|
|
162
|
-
super().__post_init__()
|
|
333
|
+
super(IgnoreSortRule, self).__post_init__()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
TRule = TypeVar(
|
|
337
|
+
"TRule",
|
|
338
|
+
RevenueSortRule,
|
|
339
|
+
CostOfRevenueSortRule,
|
|
340
|
+
ExpenseSortRule,
|
|
341
|
+
OtherIncomeSortRule,
|
|
342
|
+
OtherExpenseSortRule,
|
|
343
|
+
IgnoreSortRule,
|
|
344
|
+
)
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from dao_treasury.sorting.rules.ignore import *
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from dao_treasury.sorting.rules.ignore.llamapay import *
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dao_treasury import TreasuryTx
|
|
2
|
+
from dao_treasury.sorting.factory import ignore
|
|
3
|
+
from dao_treasury.streams import llamapay
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@ignore("LlamaPay")
|
|
7
|
+
def is_llamapay_stream_replenishment(tx: TreasuryTx) -> bool:
|
|
8
|
+
if tx.to_address.address in llamapay.factories: # type: ignore [operator]
|
|
9
|
+
# We amortize these streams daily in the `llamapay` module, you'll sort each stream appropriately.
|
|
10
|
+
return True
|
|
11
|
+
|
|
12
|
+
# NOTE: not sure if we want this yet
|
|
13
|
+
# Puling unused funds back from vesting escrow / llamapay
|
|
14
|
+
# elif tx.from_address == "Contract: LlamaPay" and "StreamCancelled" in tx.events:
|
|
15
|
+
# if tx.amount > 0:
|
|
16
|
+
# tx.amount *= -1
|
|
17
|
+
# if tx.value_usd > 0:
|
|
18
|
+
# tx.value_usd *= -1
|
|
19
|
+
# return True
|
|
20
|
+
return False
|
|
Binary file
|
|
File without changes
|
|
Binary file
|