yearn-treasury 0.0.18__cp310-cp310-win32.whl → 0.0.46__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.
Potentially problematic release.
This version of yearn-treasury might be problematic. Click here for more details.
- yearn_treasury/__init__.py +2 -0
- yearn_treasury/_db.cp310-win32.pyd +0 -0
- yearn_treasury/_db.py +21 -0
- yearn_treasury/_ens.cp310-win32.pyd +0 -0
- yearn_treasury/_ens.py +14 -0
- yearn_treasury/_logging.cp310-win32.pyd +0 -0
- yearn_treasury/_logging.py +43 -0
- yearn_treasury/address_labels.yaml +10 -0
- yearn_treasury/budget/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/budget/__init__.py +2 -2
- yearn_treasury/budget/_request.cp310-win32.pyd +0 -0
- yearn_treasury/budget/_request.py +8 -0
- yearn_treasury/budget/_requests.cp310-win32.pyd +0 -0
- yearn_treasury/budget/_requests.py +44 -17
- yearn_treasury/constants.py +20 -2
- yearn_treasury/main.py +54 -20
- yearn_treasury/rules/__init__.py +14 -0
- yearn_treasury/rules/constants.cp310-win32.pyd +0 -0
- yearn_treasury/rules/cost_of_revenue/gas.cp310-win32.pyd +0 -0
- yearn_treasury/rules/cost_of_revenue/gas.py +9 -1
- yearn_treasury/rules/expense/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/general.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/infrastructure.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/infrastructure.py +7 -0
- yearn_treasury/rules/expense/people.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/people.py +43 -0
- yearn_treasury/rules/expense/security.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/security.py +17 -0
- yearn_treasury/rules/ignore/__init__.py +1 -0
- yearn_treasury/rules/ignore/general.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/maker.py +40 -28
- yearn_treasury/rules/ignore/passthru.py +172 -12
- yearn_treasury/rules/ignore/swaps/__init__.py +14 -0
- yearn_treasury/rules/ignore/swaps/aave.py +39 -18
- yearn_treasury/rules/ignore/swaps/auctions.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
- yearn_treasury/rules/ignore/swaps/compound.py +32 -22
- yearn_treasury/rules/ignore/swaps/conversion_factory.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/conversion_factory.py +21 -0
- yearn_treasury/rules/ignore/swaps/cowswap.py +87 -0
- yearn_treasury/rules/ignore/swaps/curve.py +170 -0
- yearn_treasury/rules/ignore/swaps/gearbox.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/gearbox.py +37 -0
- yearn_treasury/rules/ignore/swaps/iearn.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/iearn.py +43 -0
- yearn_treasury/rules/ignore/swaps/otc.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/otc.py +58 -0
- yearn_treasury/rules/ignore/swaps/pooltogether.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/pooltogether.py +23 -0
- yearn_treasury/rules/ignore/swaps/synthetix.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/synthetix.py +10 -0
- yearn_treasury/rules/ignore/swaps/uniswap.py +29 -6
- yearn_treasury/rules/ignore/swaps/unwrapper.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/unwrapper.py +17 -0
- yearn_treasury/rules/ignore/swaps/vaults.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/vaults.py +264 -0
- yearn_treasury/rules/ignore/swaps/woofy.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/woofy.py +80 -0
- yearn_treasury/rules/ignore/swaps/yfi.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/yfi.py +111 -0
- yearn_treasury/rules/ignore/swaps/yla.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/yla.py +28 -0
- yearn_treasury/rules/ignore/unit.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/unit.py +40 -0
- yearn_treasury/rules/ignore/weth.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/weth.py +12 -4
- yearn_treasury/rules/ignore/ygov.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/boost.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/bugs.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/donations.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/donations.py +8 -0
- yearn_treasury/rules/other_expense/dyfi.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/events.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/misc.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/misc.py +22 -0
- yearn_treasury/rules/other_expense/revshare.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_income/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_income/__init__.py +2 -100
- yearn_treasury/rules/other_income/airdrops.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_income/airdrops.py +30 -0
- yearn_treasury/rules/other_income/match_on_hash.yaml +2 -0
- yearn_treasury/rules/other_income/misc.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_income/misc.py +80 -0
- yearn_treasury/rules/revenue/bribes.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/farming.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/keepcoins.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/seasolver.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/vaults.py +14 -10
- yearn_treasury/rules/revenue/yteams.cp310-win32.pyd +0 -0
- yearn_treasury/shitcoins.py +92 -2
- yearn_treasury/vaults.cp310-win32.pyd +0 -0
- yearn_treasury/vaults.py +17 -4
- yearn_treasury/wallets.yaml +14 -1
- yearn_treasury/yteams.py +208 -0
- {yearn_treasury-0.0.18.dist-info → yearn_treasury-0.0.46.dist-info}/METADATA +3 -3
- yearn_treasury-0.0.46.dist-info/RECORD +128 -0
- yearn_treasury-0.0.46.dist-info/top_level.txt +2 -0
- yearn_treasury__mypyc.cp310-win32.pyd +0 -0
- 5f18f6d379e6cea9f0c7__mypyc.cp310-win32.pyd +0 -0
- yearn_treasury-0.0.18.dist-info/RECORD +0 -93
- yearn_treasury-0.0.18.dist-info/top_level.txt +0 -2
- {yearn_treasury-0.0.18.dist-info → yearn_treasury-0.0.46.dist-info}/WHEEL +0 -0
- {yearn_treasury-0.0.18.dist-info → yearn_treasury-0.0.46.dist-info}/entry_points.txt +0 -0
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ignore rules for Maker protocol.
|
|
3
|
+
|
|
4
|
+
This module defines matching logic for Maker protocol transactions,
|
|
5
|
+
including DAI minting/burning, DSR deposit/withdrawal, and CDP
|
|
6
|
+
deposit/withdrawal for YFI and USDC.
|
|
7
|
+
"""
|
|
8
|
+
|
|
1
9
|
from decimal import Decimal
|
|
2
10
|
from typing import Final
|
|
3
11
|
|
|
@@ -26,55 +34,59 @@ dsr("Withdrawal").match(from_nickname="Contract: DsrManager")
|
|
|
26
34
|
|
|
27
35
|
@cdp("YFI Deposit")
|
|
28
36
|
def is_yfi_cdp_deposit(tx: TreasuryTx) -> bool:
|
|
29
|
-
if tx.symbol == "YFI" and TreasuryWallet.
|
|
30
|
-
tx.from_address.address # type: ignore [union-attr, arg-type]
|
|
37
|
+
if tx.symbol == "YFI" and TreasuryWallet.check_membership(
|
|
38
|
+
tx.from_address.address, tx.block # type: ignore [union-attr, arg-type]
|
|
31
39
|
):
|
|
32
40
|
for event in tx.get_events("slip"):
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
if all(arg in event for arg in DEPOSIT_EVENT_ARGS):
|
|
42
|
+
# TODO: remove this rounding once we move to postgres
|
|
43
|
+
scaled = round(Decimal(event["wad"]) / 10**18, 12)
|
|
44
|
+
rounded = round(tx.amount, 12)
|
|
45
|
+
if scaled == rounded:
|
|
46
|
+
return True
|
|
47
|
+
print(f"yfi cdp deposit amount no match [{scaled}, {rounded}]")
|
|
38
48
|
return False
|
|
39
49
|
|
|
40
50
|
|
|
41
51
|
@cdp("YFI Withdrawal")
|
|
42
52
|
def is_yfi_cdp_withdrawal(tx: TreasuryTx) -> bool:
|
|
43
|
-
if tx.symbol == "YFI" and TreasuryWallet.
|
|
44
|
-
tx.to_address.address # type: ignore [union-attr, arg-type]
|
|
53
|
+
if tx.symbol == "YFI" and TreasuryWallet.check_membership(
|
|
54
|
+
tx.to_address.address, tx.block # type: ignore [union-attr, arg-type]
|
|
45
55
|
):
|
|
46
56
|
for event in tx.get_events("flux"):
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
if all(arg in event for arg in WITHDRAWAL_EVENT_ARGS):
|
|
58
|
+
# TODO: remove this rounding once we move to postgres
|
|
59
|
+
scaled = round(Decimal(event["wad"]) / 10**18, 12)
|
|
60
|
+
rounded = round(tx.amount, 12)
|
|
61
|
+
if scaled == rounded:
|
|
62
|
+
return True
|
|
63
|
+
print(f"yfi cdp withdrawal amount no match [{scaled}, {rounded}]")
|
|
52
64
|
return False
|
|
53
65
|
|
|
54
66
|
|
|
55
67
|
@cdp("USDC Deposit")
|
|
56
68
|
def is_usdc_cdp_deposit(tx: TreasuryTx) -> bool:
|
|
57
|
-
if tx.symbol == "USDC" and TreasuryWallet.
|
|
58
|
-
tx.from_address.address # type: ignore [union-attr, arg-type]
|
|
69
|
+
if tx.symbol == "USDC" and TreasuryWallet.check_membership(
|
|
70
|
+
tx.from_address.address, tx.block # type: ignore [union-attr, arg-type]
|
|
59
71
|
):
|
|
60
72
|
for event in tx.get_events("slip"):
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
if all(arg in event for arg in DEPOSIT_EVENT_ARGS):
|
|
74
|
+
scaled = Decimal(event["wad"]) / 10**18
|
|
75
|
+
if scaled == tx.amount:
|
|
76
|
+
return True
|
|
77
|
+
print(f"usdc cdp deposit amount no match [{scaled}, {tx.amount}]")
|
|
66
78
|
return False
|
|
67
79
|
|
|
68
80
|
|
|
69
81
|
@cdp("USDC Withdrawal")
|
|
70
82
|
def is_usdc_cdp_withdrawal(tx: TreasuryTx) -> bool:
|
|
71
|
-
if tx.symbol == "USDC" and TreasuryWallet.
|
|
72
|
-
tx.to_address.address # type: ignore [union-attr, arg-type]
|
|
83
|
+
if tx.symbol == "USDC" and TreasuryWallet.check_membership(
|
|
84
|
+
tx.to_address.address, tx.block # type: ignore [union-attr, arg-type]
|
|
73
85
|
):
|
|
74
86
|
for event in tx.get_events("flux"):
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
if all(arg in event for arg in WITHDRAWAL_EVENT_ARGS):
|
|
88
|
+
scaled = Decimal(event["wad"]) / 10**18
|
|
89
|
+
if scaled == tx.amount:
|
|
90
|
+
return True
|
|
91
|
+
print(f"usdc cdp withdrawal amount no match [{scaled}, {tx.amount}]")
|
|
80
92
|
return False
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
|
-
from typing import Final
|
|
2
|
+
from typing import Final, Tuple
|
|
3
3
|
|
|
4
|
-
from dao_treasury import TreasuryTx, ignore
|
|
4
|
+
from dao_treasury import TreasuryTx, TreasuryWallet, ignore
|
|
5
|
+
from eth_typing import BlockNumber, ChecksumAddress
|
|
5
6
|
from y import Network
|
|
6
7
|
|
|
8
|
+
from yearn_treasury.constants import CHAINID, YSWAP_MULTISIG, ZERO_ADDRESS
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
passthru: Final = ignore("Pass-Thru")
|
|
9
12
|
|
|
@@ -20,10 +23,12 @@ def is_sent_to_dinoswap(tx: TreasuryTx) -> bool:
|
|
|
20
23
|
@passthru("Bribes for yCRV", Network.Mainnet)
|
|
21
24
|
def is_ycrv(tx: TreasuryTx) -> bool:
|
|
22
25
|
"""These are routed thru cowswap with dai as the purchase token."""
|
|
23
|
-
yswaps = "0x7d2aB9CA511EBD6F03971Fb417d3492aA82513f0"
|
|
24
26
|
ymechs = "0x2C01B4AD51a67E2d8F02208F54dF9aC4c0B778B6"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
|
|
28
|
+
from_address = tx.from_address
|
|
29
|
+
symbol = tx.symbol
|
|
30
|
+
if (from_address == YSWAP_MULTISIG and symbol == "DAI") or (
|
|
31
|
+
from_address == ymechs and symbol == "3Crv"
|
|
27
32
|
):
|
|
28
33
|
if tx.to_address == cowswap_router:
|
|
29
34
|
for trade in tx.get_events("Trade"):
|
|
@@ -36,13 +41,12 @@ def is_ycrv(tx: TreasuryTx) -> bool:
|
|
|
36
41
|
fee_amount,
|
|
37
42
|
order_uid,
|
|
38
43
|
) = trade.values()
|
|
39
|
-
if
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return True
|
|
44
|
+
if tx.from_address == owner and tx.token == sell_token and buy_token == ycrv:
|
|
45
|
+
scaled = Decimal(sell_amount) / 10**18
|
|
46
|
+
# TODO: remove this rounding when we implement postgres
|
|
47
|
+
if round(scaled, 11) == round(tx.amount, 11):
|
|
48
|
+
return True
|
|
49
|
+
print(f"bribes for ycrv amount no match: [{scaled}, {tx.amount}]")
|
|
46
50
|
|
|
47
51
|
elif tx.hash in {
|
|
48
52
|
# one off exception case to correct accounting mix-up
|
|
@@ -148,3 +152,159 @@ def is_idle(tx: TreasuryTx) -> bool:
|
|
|
148
152
|
"0x55d89a5890cfe80da06f6831fdfa3a366c0ed9cf9b7f1b4d53f5007bb9698fa0",
|
|
149
153
|
"0x6c6fc43dca1841af82b517bc5fc53ea8607e3f95512e4dd3009c0dbb425669f7",
|
|
150
154
|
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
_factory_strat_to_yield_tokens = {
|
|
158
|
+
"Contract: StrategyCurveBoostedFactoryClonable": ("CRV", "LDO"),
|
|
159
|
+
"Contract: StrategyConvexFactoryClonable": ("CRV", "CVX"),
|
|
160
|
+
"Contract: StrategyConvexFraxFactoryClonable": ("CRV", "CVX", "FXS"),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@passthru("Factory Vault Yield", Network.Mainnet)
|
|
165
|
+
def is_factory_vault_yield(tx: TreasuryTx) -> bool:
|
|
166
|
+
return tx.to_nickname == "yMechs Multisig" and tx.symbol in _factory_strat_to_yield_tokens.get(
|
|
167
|
+
tx.from_nickname, ()
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@passthru("CowSwap Migration", Network.Mainnet)
|
|
172
|
+
def is_cowswap_migration(tx: TreasuryTx) -> bool:
|
|
173
|
+
"""A one-time tx that transferred tokens from an old contract to its replacement."""
|
|
174
|
+
return tx.hash == "0xb50341d3db2ff4a39b9bfa21753893035554ae44abb7d104ab650753db1c4855"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def is_curve_bribe(tx: TreasuryTx) -> bool:
|
|
178
|
+
"""All present and future curve bribes are committed to yveCRV holders."""
|
|
179
|
+
from_nickname = tx.from_nickname
|
|
180
|
+
if from_nickname == "Contract: CurveYCRVVoter" and tx.hash not in [
|
|
181
|
+
# took place before bribes were committed to yveCRV
|
|
182
|
+
"0x6824345c8a0c1f0b801d8050bb6f587032c4a9fa153faa113d723a2068d844f4",
|
|
183
|
+
# was a whitehat hack of the v1 bribe contract, necessary to safeguard user funds
|
|
184
|
+
"0xfcef3911809770fe351b2b526e4ee0274589a3f7d6ef9408a8f5643fa006b771",
|
|
185
|
+
]:
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
# Bribe V3
|
|
189
|
+
elif from_nickname == "Contract: yBribe" and tx.to_nickname in [
|
|
190
|
+
"Yearn Treasury",
|
|
191
|
+
"ySwap Multisig",
|
|
192
|
+
]:
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
# NOTE: I added this one-off to capture tokens sent to BribeSplitter 0x527e80008D212E2891C737Ba8a2768a7337D7Fd2
|
|
196
|
+
return tx.hash == "0xce45da7e3a7616ed0c0d356d6dfa8a784606c9a8034bae9faa40abf7b52be114"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
_pass_thru_hashes: Tuple[str, ...] = {
|
|
200
|
+
Network.Mainnet: ("0xf662c68817c56a64b801181a3175c8a7e7a5add45f8242990c695d418651e50d",),
|
|
201
|
+
Network.Fantom: (
|
|
202
|
+
"0x411d0aff42c3862d06a0b04b5ffd91f4593a9a8b2685d554fe1fbe5dc7e4fc04",
|
|
203
|
+
"0xa347da365286cc912e4590fc71e97a5bcba9e258c98a301f85918826538aa021",
|
|
204
|
+
),
|
|
205
|
+
}.get(
|
|
206
|
+
CHAINID, ()
|
|
207
|
+
) # type: ignore [call-overload]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@passthru("Misc.", Network.Mainnet)
|
|
211
|
+
def is_misc_passthru_mainnet(tx: TreasuryTx) -> bool:
|
|
212
|
+
# not sure if we still need this, TODO figure it out
|
|
213
|
+
# NOTE skipped the hashmatcher to do strange things ... there is probably a better way to do this
|
|
214
|
+
if tx.hash in _pass_thru_hashes and str(tx.log_index).lower() != "nan":
|
|
215
|
+
return True
|
|
216
|
+
|
|
217
|
+
txhash = tx.hash
|
|
218
|
+
if txhash in {
|
|
219
|
+
"0xae6797ad466de75731117df46ccea5c263265dd6258d596b9d6d8cf3a7b1e3c2",
|
|
220
|
+
"0x2a6191ba8426d3ae77e2a6c91de10a6e76d1abdb2d0f831c6c5aad52be3d6246",
|
|
221
|
+
# https://github.com/yearn/chief-multisig-officer/pull/924
|
|
222
|
+
"0x25b54e113e58a3a4bbffc011cdfcb8c07a0424f33b0dbda921803d82b88f1429",
|
|
223
|
+
"0xcb000dd2b623f9924fe0234831800950a3269b2d412ce9eeabb0ec65cd737059",
|
|
224
|
+
}:
|
|
225
|
+
return True
|
|
226
|
+
# do these need hueristics? build real sort logic if these keep reoccurring
|
|
227
|
+
elif txhash == "0x14faeac8ee0734875611e68ce0614eaf39db94a5ffb5bc6f9739da6daf58282a" and (
|
|
228
|
+
tx.symbol in ("CRV", "CVX", "yPRISMA") or tx.log_index == 254
|
|
229
|
+
):
|
|
230
|
+
return True
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@passthru("Misc.", Network.Fantom)
|
|
235
|
+
def is_misc_passthru_fantom(tx: TreasuryTx) -> bool:
|
|
236
|
+
# not sure if we still need this, TODO figure it out
|
|
237
|
+
# NOTE skipped the hashmatcher to do strange things ... there is probably a better way to do this
|
|
238
|
+
if tx.hash in _pass_thru_hashes and str(tx.log_index).lower() != "nan":
|
|
239
|
+
return True
|
|
240
|
+
|
|
241
|
+
if tx.hash == "0x14faeac8ee0734875611e68ce0614eaf39db94a5ffb5bc6f9739da6daf58282a":
|
|
242
|
+
return True
|
|
243
|
+
# Passing thru to yvWFTM
|
|
244
|
+
if (
|
|
245
|
+
tx.symbol == "WFTM"
|
|
246
|
+
and TreasuryWallet.check_membership(tx.from_address.address, tx.block) # type: ignore [arg-type, union-attr]
|
|
247
|
+
and tx.to_address == "0x0DEC85e74A92c52b7F708c4B10207D9560CEFaf0"
|
|
248
|
+
):
|
|
249
|
+
# dont want to accidenally sort a vault deposit here
|
|
250
|
+
is_deposit = False
|
|
251
|
+
for event in tx.get_events("Transfer"):
|
|
252
|
+
sender, receiver, _ = event.values()
|
|
253
|
+
if (
|
|
254
|
+
tx.to_address == event.address
|
|
255
|
+
and sender == ZERO_ADDRESS
|
|
256
|
+
and tx.from_address == receiver
|
|
257
|
+
):
|
|
258
|
+
is_deposit = True
|
|
259
|
+
if not is_deposit:
|
|
260
|
+
return True
|
|
261
|
+
return False
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@passthru("yvBoost INCOMPLETE", Network.Mainnet)
|
|
265
|
+
def is_buying_yvboost(tx: TreasuryTx) -> bool:
|
|
266
|
+
"""Bought back yvBoost is unwrapped and sent back to vault holders."""
|
|
267
|
+
symbol = tx.symbol
|
|
268
|
+
block: BlockNumber = tx.block # type: ignore [assignment]
|
|
269
|
+
from_address: ChecksumAddress = tx.from_address.address # type: ignore [union-attr, assignment]
|
|
270
|
+
to_address: ChecksumAddress = tx.to_address.address # type: ignore [union-attr, assignment]
|
|
271
|
+
if (
|
|
272
|
+
symbol == "SPELL"
|
|
273
|
+
and TreasuryWallet.check_membership(from_address, block)
|
|
274
|
+
and to_address == cowswap_router
|
|
275
|
+
):
|
|
276
|
+
return True
|
|
277
|
+
|
|
278
|
+
elif (
|
|
279
|
+
symbol == "yveCRV-DAO"
|
|
280
|
+
and TreasuryWallet.check_membership(from_address, block)
|
|
281
|
+
and to_address
|
|
282
|
+
in (
|
|
283
|
+
"0xd7240B32d24B814fE52946cD44d94a2e3532E63d",
|
|
284
|
+
"0x7fe508eE30316e3261079e2C81f4451E0445103b",
|
|
285
|
+
)
|
|
286
|
+
):
|
|
287
|
+
return True
|
|
288
|
+
|
|
289
|
+
elif (
|
|
290
|
+
symbol == "3Crv"
|
|
291
|
+
and from_address == "0xd7240B32d24B814fE52946cD44d94a2e3532E63d"
|
|
292
|
+
and TreasuryWallet.check_membership(to_address, block)
|
|
293
|
+
):
|
|
294
|
+
return True
|
|
295
|
+
|
|
296
|
+
# SPELL bribe handling
|
|
297
|
+
elif symbol == "SPELL":
|
|
298
|
+
if tx.to_nickname in ("Abracadabra Treasury", "Contract: BribeSplitter"):
|
|
299
|
+
return True
|
|
300
|
+
|
|
301
|
+
return tx in (
|
|
302
|
+
"0x9eabdf110efbfb44aab7a50eb4fe187f68deae7c8f28d78753c355029f2658d3",
|
|
303
|
+
"0x5a80f5ff90fc6f4f4597290b2432adbb62ab4154ead68b515accdf19b01c1086",
|
|
304
|
+
"0x848b4d629e137ad8d8eefe5db40eab895c9959b9c210d0ae0fef16a04bfaaee1",
|
|
305
|
+
"0x896663aa9e2633b5d152028bdf84d7f4b1137dd27a8e61daca3863db16bebc4f",
|
|
306
|
+
"0xd8aa1e5d093a89515530b7267a9fd216b97fddb6478b3027b2f5c1d53070cd5f",
|
|
307
|
+
"0x169aab84b408fce76e0b776ebf412c796240300c5610f0263d5c09d0d3f1b062",
|
|
308
|
+
"0xe6fefbf061f4489cd967cdff6aa8aca616f0c709e08c3696f12b0027e9e166c9",
|
|
309
|
+
"0x10be8a3345660f3c51b695e8716f758b1a91628bd612093784f0516a604f79c1",
|
|
310
|
+
)
|
|
@@ -7,5 +7,19 @@ swaps: Final[SortRuleFactory[IgnoreSortRule]] = ignore("Swaps")
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
from yearn_treasury.rules.ignore.swaps.aave import *
|
|
10
|
+
from yearn_treasury.rules.ignore.swaps.auctions import *
|
|
10
11
|
from yearn_treasury.rules.ignore.swaps.compound import *
|
|
12
|
+
from yearn_treasury.rules.ignore.swaps.conversion_factory import *
|
|
13
|
+
from yearn_treasury.rules.ignore.swaps.cowswap import *
|
|
14
|
+
from yearn_treasury.rules.ignore.swaps.curve import *
|
|
15
|
+
from yearn_treasury.rules.ignore.swaps.gearbox import *
|
|
16
|
+
from yearn_treasury.rules.ignore.swaps.iearn import *
|
|
17
|
+
from yearn_treasury.rules.ignore.swaps.otc import *
|
|
18
|
+
from yearn_treasury.rules.ignore.swaps.pooltogether import *
|
|
19
|
+
from yearn_treasury.rules.ignore.swaps.synthetix import *
|
|
11
20
|
from yearn_treasury.rules.ignore.swaps.uniswap import *
|
|
21
|
+
from yearn_treasury.rules.ignore.swaps.unwrapper import *
|
|
22
|
+
from yearn_treasury.rules.ignore.swaps.vaults import *
|
|
23
|
+
from yearn_treasury.rules.ignore.swaps.woofy import *
|
|
24
|
+
from yearn_treasury.rules.ignore.swaps.yfi import *
|
|
25
|
+
from yearn_treasury.rules.ignore.swaps.yla import *
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ignore rules for Aave-related transactions.
|
|
3
|
+
|
|
4
|
+
This module defines rules for identifying and ignoring Aave-related
|
|
5
|
+
transactions in the Yearn Treasury system. It provides matching logic
|
|
6
|
+
for deposit and withdrawal events, so these transactions can be
|
|
7
|
+
ignored in analytics and reporting.
|
|
8
|
+
"""
|
|
9
|
+
|
|
1
10
|
from typing import Final
|
|
2
11
|
|
|
3
12
|
from dao_treasury import TreasuryTx, TreasuryWallet
|
|
13
|
+
from eth_typing import ChecksumAddress
|
|
4
14
|
|
|
5
15
|
from yearn_treasury.rules.constants import ZERO_ADDRESS
|
|
6
16
|
from yearn_treasury.rules.ignore.swaps import swaps
|
|
@@ -20,29 +30,40 @@ def is_aave_deposit(tx: TreasuryTx) -> bool:
|
|
|
20
30
|
|
|
21
31
|
@aave("Withdrawal")
|
|
22
32
|
async def is_aave_withdrawal(tx: TreasuryTx) -> bool:
|
|
33
|
+
from_address: ChecksumAddress = tx.from_address.address # type: ignore [union-attr, assignment]
|
|
34
|
+
to_address: ChecksumAddress = tx.to_address.address # type: ignore [union-attr, assignment]
|
|
23
35
|
# Atoken side
|
|
24
36
|
if (
|
|
25
|
-
TreasuryWallet.
|
|
26
|
-
and
|
|
27
|
-
and hasattr(tx.token.contract, "underlyingAssetAddress")
|
|
37
|
+
TreasuryWallet.check_membership(from_address, tx.block) # type: ignore [union-attr, arg-type]
|
|
38
|
+
and to_address == ZERO_ADDRESS
|
|
28
39
|
):
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
token = tx.token
|
|
41
|
+
if hasattr(token.contract, "underlyingAssetAddress"):
|
|
42
|
+
for event in await tx.get_events("RedeemUnderlying", sync=False):
|
|
43
|
+
if (
|
|
44
|
+
from_address == event["_user"]
|
|
45
|
+
and await token.contract.underlyingAssetAddress == event["_reserve"]
|
|
46
|
+
):
|
|
47
|
+
# TODO get rid of this rounding when we migrate the db to postgres
|
|
48
|
+
event_amount = round(token.scale_value(event["_amount"]), 11)
|
|
49
|
+
if event_amount == round(tx.amount, 11):
|
|
50
|
+
return True
|
|
51
|
+
print(
|
|
52
|
+
f"Aave Withdrawal atoken side does not match: {round(tx.amount, 14)} {event_amount}"
|
|
53
|
+
)
|
|
36
54
|
|
|
37
55
|
# Underlying side
|
|
38
|
-
if TreasuryWallet.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
if TreasuryWallet.check_membership(tx.to_address.address, tx.block): # type: ignore [union-attr, arg-type]
|
|
57
|
+
token = tx.token
|
|
58
|
+
for event in await tx.get_events("RedeemUnderlying", sync=False):
|
|
59
|
+
if token == event["_reserve"] and to_address == event["_user"]:
|
|
60
|
+
# TODO get rid of this rounding when we migrate the db to postgres
|
|
61
|
+
event_amount = round(token.scale_value(event["_amount"]), 11)
|
|
62
|
+
if event_amount == round(tx.amount, 11):
|
|
63
|
+
return True
|
|
64
|
+
print(
|
|
65
|
+
f"Aave Withdrawal underlying side does not match: {round(tx.amount, 14)} {event_amount}"
|
|
66
|
+
)
|
|
46
67
|
|
|
47
68
|
# TODO: If these end up becoming more frequent, figure out sorting hueristics.
|
|
48
69
|
return tx.hash == "0x36ee5631859a15f57b44e41b8590023cf6f0c7b12d28ea760e9d8f8003f4fc50"
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
|
|
3
|
+
from dao_treasury import TreasuryTx
|
|
4
|
+
from y import Network
|
|
5
|
+
|
|
6
|
+
from yearn_treasury.rules.ignore.swaps import swaps
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
auctions: Final = swaps("Auctions")
|
|
10
|
+
|
|
11
|
+
YEARNFI_DUTCH_AUCTIONS: Final = "0x861fE45742f70054917B65bE18904662bD0dBd30"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@auctions("Auction Proceeds", Network.Mainnet)
|
|
15
|
+
async def is_auction_proceeds(tx: TreasuryTx) -> bool:
|
|
16
|
+
# NOTE: the other side of these swaps is currently recorded under
|
|
17
|
+
# 'Ignore:Internal Transfer' when it goes to the Generic bucket contract
|
|
18
|
+
if tx.from_nickname != "Contract: GPv2Settlement":
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
for trade in await tx.get_events("Trade", sync=False):
|
|
22
|
+
if trade["owner"] != YEARNFI_DUTCH_AUCTIONS or tx.token != trade["buyToken"]:
|
|
23
|
+
continue
|
|
24
|
+
buy_amount = tx.token.scale_value(trade["buyAmount"])
|
|
25
|
+
if round(buy_amount, 14) == round(tx.amount, 14):
|
|
26
|
+
return True
|
|
27
|
+
print(
|
|
28
|
+
f"auction proceeds amount does not match: {round(buy_amount, 14)} {round(tx.amount, 14)}"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
return False
|
|
@@ -9,40 +9,50 @@ compound: Final = swaps("Compound")
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@compound("Deposit")
|
|
12
|
-
def is_compound_deposit(tx: TreasuryTx) -> bool:
|
|
13
|
-
for event in tx.get_events("Mint"):
|
|
12
|
+
async def is_compound_deposit(tx: TreasuryTx) -> bool:
|
|
13
|
+
for event in await tx.get_events("Mint", sync=False):
|
|
14
14
|
if all(arg in event for arg in ("minter", "mintTokens", "mintAmount")):
|
|
15
15
|
minter = event["minter"]
|
|
16
16
|
minted = tx.token.scale_value(event["mintTokens"])
|
|
17
17
|
# cToken side
|
|
18
|
-
if
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
if tx.token == tx.from_address == event.address and tx.to_address == minter:
|
|
19
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
20
|
+
if round(minted, 14) == round(tx.amount, 14):
|
|
21
|
+
return True
|
|
22
|
+
print(
|
|
23
|
+
f"Compound deposit ctoken side does not match: {round(minted, 14)} {round(tx.amount, 14)}"
|
|
24
|
+
)
|
|
24
25
|
# underlying side
|
|
25
|
-
elif
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
elif tx.to_address == event.address and tx.from_address == minter:
|
|
27
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
28
|
+
if round(minted, 14) == round(tx.amount, 14):
|
|
29
|
+
return True
|
|
30
|
+
print(
|
|
31
|
+
f"Compound deposit underlying side does not match: {round(minted, 14)} {round(tx.amount, 14)}"
|
|
32
|
+
)
|
|
29
33
|
return False
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
@compound("Withdrawal")
|
|
33
|
-
def is_compound_withdrawal(tx: TreasuryTx) -> bool:
|
|
34
|
-
for event in tx.get_events("Redeem"):
|
|
37
|
+
async def is_compound_withdrawal(tx: TreasuryTx) -> bool:
|
|
38
|
+
for event in await tx.get_events("Redeem", sync=False):
|
|
35
39
|
if all(arg in event for arg in ("redeemer", "redeemTokens", "redeemAmount")):
|
|
36
40
|
redeemer = event["redeemer"]
|
|
37
41
|
redeemed = tx.token.scale_value(event["redeemTokens"])
|
|
38
42
|
# cToken side
|
|
39
|
-
if tx.token == event.address and tx.from_address == redeemer
|
|
40
|
-
|
|
43
|
+
if tx.token == event.address and tx.from_address == redeemer:
|
|
44
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
45
|
+
if round(redeemed, 7) == round(tx.amount, 7):
|
|
46
|
+
return True
|
|
47
|
+
print(
|
|
48
|
+
f"Compound withdrawal ctoken side does not match: {round(redeemed, 7)} {round(tx.amount, 7)}"
|
|
49
|
+
)
|
|
41
50
|
# underlying side
|
|
42
|
-
elif
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
elif tx.to_address == redeemer and tx.from_address == event.address:
|
|
52
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
53
|
+
if round(redeemed, 14) == round(tx.amount, 14):
|
|
54
|
+
return True
|
|
55
|
+
print(
|
|
56
|
+
f"Compound withdrawal underlying side does not match: {round(redeemed, 14)} {round(tx.amount, 14)}"
|
|
57
|
+
)
|
|
48
58
|
return False
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
|
|
3
|
+
from dao_treasury import TreasuryTx
|
|
4
|
+
|
|
5
|
+
from yearn_treasury.rules.ignore.swaps import swaps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
CONVERSION_FACTORY: Final = "0x8E6A115bd8e24d2D86A1AacCC56221e5Bd4577ba"
|
|
9
|
+
ROBOTREASURY: Final = "0xEf77cc176c748d291EfB6CdC982c5744fC7211c8"
|
|
10
|
+
GENERIC_BUCKET: Final = "0x278374fFb10B7D16E7633444c13e6E565EA57c28"
|
|
11
|
+
SOME_RELATED_NON_VERIFIED_CONTRACT: Final = "0x5CECc042b2A320937c04980148Fc2a4b66Da0fbF"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@swaps("Conversion Factory")
|
|
15
|
+
def is_conversion_factory(tx: TreasuryTx) -> bool:
|
|
16
|
+
# TODO: track the balances that are held by the conversion factory but not yet dumped
|
|
17
|
+
from_address = tx.from_address.address # type: ignore [union-attr]
|
|
18
|
+
to_address = tx.to_address.address # type: ignore [union-attr]
|
|
19
|
+
return (from_address == GENERIC_BUCKET and to_address == CONVERSION_FACTORY) or (
|
|
20
|
+
from_address == SOME_RELATED_NON_VERIFIED_CONTRACT and to_address == ROBOTREASURY
|
|
21
|
+
)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
|
|
3
|
+
from dao_treasury import TreasuryTx, TreasuryWallet
|
|
4
|
+
from eth_typing import BlockNumber
|
|
5
|
+
from pony.orm import select
|
|
6
|
+
from y import Network
|
|
7
|
+
|
|
8
|
+
from yearn_treasury.constants import TREASURY_WALLETS
|
|
9
|
+
from yearn_treasury.rules.ignore.swaps import swaps
|
|
10
|
+
from yearn_treasury.rules.ignore.swaps._skip_tokens import SKIP_TOKENS
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
COWSWAP: Final = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@swaps("Cowswap", Network.Mainnet)
|
|
17
|
+
def is_cowswap_swap(tx: TreasuryTx) -> bool:
|
|
18
|
+
# One sided, other side goes elsewhere. typically used for output tokens passed-thru to vaults.
|
|
19
|
+
if tx.from_nickname == "yMechs Multisig" and tx.to_nickname == "Contract: GPv2Settlement":
|
|
20
|
+
return True
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
if "Trade" not in tx.events:
|
|
24
|
+
return False
|
|
25
|
+
except KeyError as e:
|
|
26
|
+
if "components" in str(e):
|
|
27
|
+
return False
|
|
28
|
+
raise
|
|
29
|
+
|
|
30
|
+
token = tx.token
|
|
31
|
+
block: BlockNumber = tx.block # type: ignore [assignment]
|
|
32
|
+
amount = tx.amount
|
|
33
|
+
token_address = token.address.address
|
|
34
|
+
|
|
35
|
+
for trade in tx.events["Trade"]:
|
|
36
|
+
if (
|
|
37
|
+
trade.address == COWSWAP
|
|
38
|
+
and TreasuryWallet.check_membership(trade["owner"], block)
|
|
39
|
+
and trade["buyToken"] not in SKIP_TOKENS
|
|
40
|
+
):
|
|
41
|
+
# buy side
|
|
42
|
+
if token_address == trade["buyToken"] and TreasuryWallet.check_membership(
|
|
43
|
+
tx.to_address.address, block # type: ignore [union-attr, arg-type]
|
|
44
|
+
):
|
|
45
|
+
# TODO get rid of this rounding when we move to postgres
|
|
46
|
+
buy_amount = round(token.scale_value(trade["buyAmount"]), 8)
|
|
47
|
+
if round(amount, 8) == buy_amount:
|
|
48
|
+
return True
|
|
49
|
+
print(f"Cowswap buy amount does not match: {round(amount, 8)} {buy_amount}")
|
|
50
|
+
# sell side
|
|
51
|
+
elif token_address == trade["sellToken"] and tx.from_address == trade["owner"]:
|
|
52
|
+
# TODO get rid of this rounding when we move to postgres
|
|
53
|
+
sell_amount = round(token.scale_value(trade["sellAmount"]), 8)
|
|
54
|
+
if round(amount, 8) != sell_amount:
|
|
55
|
+
print(f"Cowswap sell amount does not match: {round(amount, 8)} {sell_amount}")
|
|
56
|
+
continue
|
|
57
|
+
# Did Yearn actually receive the other side of the trade?
|
|
58
|
+
for address in TREASURY_WALLETS:
|
|
59
|
+
if TreasuryWallet.check_membership(address, block):
|
|
60
|
+
other_side_query = select(
|
|
61
|
+
t
|
|
62
|
+
for t in TreasuryTx # type: ignore [attr-defined]
|
|
63
|
+
if t.hash == tx.hash
|
|
64
|
+
and t.token.address.address == trade["buyToken"]
|
|
65
|
+
and t.from_address.address == COWSWAP
|
|
66
|
+
and t.to_address.address == address
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if len(other_side_query) > 0:
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
# made with some help from other contracts
|
|
73
|
+
return tx.hash in {
|
|
74
|
+
"0xd41e40a0e9b49c4f06e1956066006de901a4ed8c856a43c31ac1cbd344ff0ccf",
|
|
75
|
+
"0x94e1a85fa25e433976962449588e522ce0f2a81ae3d4b67ae199e458dfce4e39",
|
|
76
|
+
"0x579056f777c1f05a18e566dd329ea85f5c747fd6e7246411b5e016c8bebe8742",
|
|
77
|
+
"0xd007d04560fc42df93da0fd25ac3942f89f7f5458eb492872b3d87be91d7a571",
|
|
78
|
+
"0x2eb9a897ea48c9802f0129674d0203835a236e2f41c6db8edb017a4c315b84f4",
|
|
79
|
+
"0xce2338b61c8c5875ce4e19f9d5993895a4d5d9eb81b78e1b33279436d2c2c047",
|
|
80
|
+
"0x6dd14144594e7b19fdc4529682eb1cf554132ae318d8ba5b238cdeb3e694d52a",
|
|
81
|
+
"0xb0d430e1ec4d6fa7bd4e56f86e6fefd7f71946549710ca0b9f39de14c04d02ed",
|
|
82
|
+
"0x0b1b00f0a29f787b44421461a3f9444081fe94ca29958c98cd2c23e271f3f69a",
|
|
83
|
+
"0x69eeeb5d7cb0f23ee98ef23f38d02ff0a695d75a1da1fa010abaa7a5aab947ff",
|
|
84
|
+
"0xdfbe76c0af4ec68021c1603d4f9a6b5f20734e70af86d32fbd57fa87461064f2",
|
|
85
|
+
"0x60cfe7c2c19e1fc3010e441ed369fc414bd10dbb7ba88fb4f490d96e15eb2f26",
|
|
86
|
+
"0x1ddddfff1f02c66ef07817144e9e30d6f734432c2736f7859236d3199d4b23fb",
|
|
87
|
+
}
|