yearn-treasury 0.1.7__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.
Files changed (128) hide show
  1. yearn_treasury/__init__.py +19 -0
  2. yearn_treasury/_db.cp310-win32.pyd +0 -0
  3. yearn_treasury/_db.py +50 -0
  4. yearn_treasury/_ens.cp310-win32.pyd +0 -0
  5. yearn_treasury/_ens.py +30 -0
  6. yearn_treasury/_logging.cp310-win32.pyd +0 -0
  7. yearn_treasury/_logging.py +41 -0
  8. yearn_treasury/address_labels.yaml +39 -0
  9. yearn_treasury/budget/__init__.cp310-win32.pyd +0 -0
  10. yearn_treasury/budget/__init__.py +6 -0
  11. yearn_treasury/budget/_request.cp310-win32.pyd +0 -0
  12. yearn_treasury/budget/_request.py +43 -0
  13. yearn_treasury/budget/_requests.cp310-win32.pyd +0 -0
  14. yearn_treasury/budget/_requests.py +96 -0
  15. yearn_treasury/constants.py +91 -0
  16. yearn_treasury/main.py +175 -0
  17. yearn_treasury/py.typed +1 -0
  18. yearn_treasury/rules/__init__.py +20 -0
  19. yearn_treasury/rules/constants.cp310-win32.pyd +0 -0
  20. yearn_treasury/rules/constants.py +15 -0
  21. yearn_treasury/rules/cost_of_revenue/__init__.py +1 -0
  22. yearn_treasury/rules/cost_of_revenue/gas.cp310-win32.pyd +0 -0
  23. yearn_treasury/rules/cost_of_revenue/gas.py +64 -0
  24. yearn_treasury/rules/cost_of_revenue/match_on_hash.yaml +12 -0
  25. yearn_treasury/rules/expense/__init__.cp310-win32.pyd +0 -0
  26. yearn_treasury/rules/expense/__init__.py +4 -0
  27. yearn_treasury/rules/expense/general.cp310-win32.pyd +0 -0
  28. yearn_treasury/rules/expense/general.py +13 -0
  29. yearn_treasury/rules/expense/infrastructure.cp310-win32.pyd +0 -0
  30. yearn_treasury/rules/expense/infrastructure.py +46 -0
  31. yearn_treasury/rules/expense/match_on_hash.yaml +48 -0
  32. yearn_treasury/rules/expense/match_on_to_address.yaml +7 -0
  33. yearn_treasury/rules/expense/people.cp310-win32.pyd +0 -0
  34. yearn_treasury/rules/expense/people.py +83 -0
  35. yearn_treasury/rules/expense/security.cp310-win32.pyd +0 -0
  36. yearn_treasury/rules/expense/security.py +146 -0
  37. yearn_treasury/rules/ignore/__init__.py +8 -0
  38. yearn_treasury/rules/ignore/general.cp310-win32.pyd +0 -0
  39. yearn_treasury/rules/ignore/general.py +11 -0
  40. yearn_treasury/rules/ignore/maker.py +91 -0
  41. yearn_treasury/rules/ignore/passthru.py +309 -0
  42. yearn_treasury/rules/ignore/staking.py +101 -0
  43. yearn_treasury/rules/ignore/swaps/__init__.py +24 -0
  44. yearn_treasury/rules/ignore/swaps/_skip_tokens.py +8 -0
  45. yearn_treasury/rules/ignore/swaps/aave.py +68 -0
  46. yearn_treasury/rules/ignore/swaps/auctions.cp310-win32.pyd +0 -0
  47. yearn_treasury/rules/ignore/swaps/auctions.py +30 -0
  48. yearn_treasury/rules/ignore/swaps/compound.py +57 -0
  49. yearn_treasury/rules/ignore/swaps/conversion_factory.cp310-win32.pyd +0 -0
  50. yearn_treasury/rules/ignore/swaps/conversion_factory.py +20 -0
  51. yearn_treasury/rules/ignore/swaps/cowswap.py +86 -0
  52. yearn_treasury/rules/ignore/swaps/curve.py +174 -0
  53. yearn_treasury/rules/ignore/swaps/gearbox.cp310-win32.pyd +0 -0
  54. yearn_treasury/rules/ignore/swaps/gearbox.py +36 -0
  55. yearn_treasury/rules/ignore/swaps/iearn.cp310-win32.pyd +0 -0
  56. yearn_treasury/rules/ignore/swaps/iearn.py +41 -0
  57. yearn_treasury/rules/ignore/swaps/otc.cp310-win32.pyd +0 -0
  58. yearn_treasury/rules/ignore/swaps/otc.py +58 -0
  59. yearn_treasury/rules/ignore/swaps/pooltogether.cp310-win32.pyd +0 -0
  60. yearn_treasury/rules/ignore/swaps/pooltogether.py +23 -0
  61. yearn_treasury/rules/ignore/swaps/synthetix.cp310-win32.pyd +0 -0
  62. yearn_treasury/rules/ignore/swaps/synthetix.py +10 -0
  63. yearn_treasury/rules/ignore/swaps/uniswap.py +293 -0
  64. yearn_treasury/rules/ignore/swaps/unwrapper.cp310-win32.pyd +0 -0
  65. yearn_treasury/rules/ignore/swaps/unwrapper.py +17 -0
  66. yearn_treasury/rules/ignore/swaps/vaults.cp310-win32.pyd +0 -0
  67. yearn_treasury/rules/ignore/swaps/vaults.py +263 -0
  68. yearn_treasury/rules/ignore/swaps/woofy.cp310-win32.pyd +0 -0
  69. yearn_treasury/rules/ignore/swaps/woofy.py +79 -0
  70. yearn_treasury/rules/ignore/swaps/yfi.cp310-win32.pyd +0 -0
  71. yearn_treasury/rules/ignore/swaps/yfi.py +110 -0
  72. yearn_treasury/rules/ignore/swaps/yla.cp310-win32.pyd +0 -0
  73. yearn_treasury/rules/ignore/swaps/yla.py +27 -0
  74. yearn_treasury/rules/ignore/unit.cp310-win32.pyd +0 -0
  75. yearn_treasury/rules/ignore/unit.py +39 -0
  76. yearn_treasury/rules/ignore/weth.cp310-win32.pyd +0 -0
  77. yearn_treasury/rules/ignore/weth.py +47 -0
  78. yearn_treasury/rules/ignore/ygov.cp310-win32.pyd +0 -0
  79. yearn_treasury/rules/ignore/ygov.py +15 -0
  80. yearn_treasury/rules/other_expense/__init__.cp310-win32.pyd +0 -0
  81. yearn_treasury/rules/other_expense/__init__.py +7 -0
  82. yearn_treasury/rules/other_expense/boost.cp310-win32.pyd +0 -0
  83. yearn_treasury/rules/other_expense/boost.py +49 -0
  84. yearn_treasury/rules/other_expense/bugs.cp310-win32.pyd +0 -0
  85. yearn_treasury/rules/other_expense/bugs.py +80 -0
  86. yearn_treasury/rules/other_expense/donations.cp310-win32.pyd +0 -0
  87. yearn_treasury/rules/other_expense/donations.py +42 -0
  88. yearn_treasury/rules/other_expense/dyfi.cp310-win32.pyd +0 -0
  89. yearn_treasury/rules/other_expense/dyfi.py +28 -0
  90. yearn_treasury/rules/other_expense/events.cp310-win32.pyd +0 -0
  91. yearn_treasury/rules/other_expense/events.py +20 -0
  92. yearn_treasury/rules/other_expense/match_on_hash.yaml +43 -0
  93. yearn_treasury/rules/other_expense/match_on_to_address.yaml +8 -0
  94. yearn_treasury/rules/other_expense/misc.cp310-win32.pyd +0 -0
  95. yearn_treasury/rules/other_expense/misc.py +48 -0
  96. yearn_treasury/rules/other_expense/revshare.cp310-win32.pyd +0 -0
  97. yearn_treasury/rules/other_expense/revshare.py +19 -0
  98. yearn_treasury/rules/other_income/__init__.cp310-win32.pyd +0 -0
  99. yearn_treasury/rules/other_income/__init__.py +2 -0
  100. yearn_treasury/rules/other_income/airdrops.cp310-win32.pyd +0 -0
  101. yearn_treasury/rules/other_income/airdrops.py +29 -0
  102. yearn_treasury/rules/other_income/match_on_hash.yaml +21 -0
  103. yearn_treasury/rules/other_income/misc.cp310-win32.pyd +0 -0
  104. yearn_treasury/rules/other_income/misc.py +78 -0
  105. yearn_treasury/rules/revenue/__init__.py +6 -0
  106. yearn_treasury/rules/revenue/bribes.cp310-win32.pyd +0 -0
  107. yearn_treasury/rules/revenue/bribes.py +25 -0
  108. yearn_treasury/rules/revenue/farming.cp310-win32.pyd +0 -0
  109. yearn_treasury/rules/revenue/farming.py +55 -0
  110. yearn_treasury/rules/revenue/keepcoins.cp310-win32.pyd +0 -0
  111. yearn_treasury/rules/revenue/keepcoins.py +61 -0
  112. yearn_treasury/rules/revenue/match_on_hash.yaml +4 -0
  113. yearn_treasury/rules/revenue/seasolver.cp310-win32.pyd +0 -0
  114. yearn_treasury/rules/revenue/seasolver.py +23 -0
  115. yearn_treasury/rules/revenue/vaults.py +171 -0
  116. yearn_treasury/rules/revenue/yteams.cp310-win32.pyd +0 -0
  117. yearn_treasury/rules/revenue/yteams.py +16 -0
  118. yearn_treasury/shitcoins.py +143 -0
  119. yearn_treasury/vaults.cp310-win32.pyd +0 -0
  120. yearn_treasury/vaults.py +49 -0
  121. yearn_treasury/wallets.yaml +54 -0
  122. yearn_treasury/yteams.py +207 -0
  123. yearn_treasury-0.1.7.dist-info/METADATA +85 -0
  124. yearn_treasury-0.1.7.dist-info/RECORD +128 -0
  125. yearn_treasury-0.1.7.dist-info/WHEEL +5 -0
  126. yearn_treasury-0.1.7.dist-info/entry_points.txt +2 -0
  127. yearn_treasury-0.1.7.dist-info/top_level.txt +2 -0
  128. yearn_treasury__mypyc.cp310-win32.pyd +0 -0
@@ -0,0 +1,293 @@
1
+ from typing import Final
2
+
3
+ from brownie import ZERO_ADDRESS, chain
4
+ from dao_treasury import TreasuryTx, TreasuryWallet
5
+ from y import Contract, Network
6
+ from y.constants import CHAINID, WRAPPED_GAS_COIN
7
+
8
+ from yearn_treasury.rules.ignore.swaps import swaps
9
+ from yearn_treasury.rules.ignore.swaps._skip_tokens import SKIP_TOKENS
10
+
11
+ uniswap: Final = swaps("Uniswap")
12
+
13
+
14
+ ROUTERS: Final = ("0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F",)
15
+
16
+
17
+ @uniswap("Add Liquidity")
18
+ async def is_uniswap_deposit(tx: TreasuryTx) -> bool:
19
+ if tx.to_address:
20
+ try:
21
+ events = tx.events
22
+ except KeyError as e:
23
+ if e.args[0] == "components":
24
+ return False
25
+ raise
26
+
27
+ if "Mint" in events and "Transfer" in events:
28
+ transfers = events["Transfer"]
29
+ for mint in events["Mint"]:
30
+ event_args = {"sender", "amount0", "amount1"}
31
+ if any(arg not in mint for arg in event_args):
32
+ continue
33
+
34
+ # LP token
35
+ if tx.from_address == ZERO_ADDRESS and (
36
+ tx.token == mint.address
37
+ or
38
+ # KP3R/WETH Uni v3 LP -- used while depositing to kLP-KP3R/WETH
39
+ mint.address == "0x11B7a6bc0259ed6Cf9DB8F499988F9eCc7167bf5"
40
+ ):
41
+ lp = tx.token.contract
42
+ tokens = [await lp.token0, await lp.token1]
43
+ if all(
44
+ any(
45
+ token == transfer.address
46
+ and tx.to_address == transfer[0]
47
+ and transfer[1] == mint.address
48
+ for transfer in events["Transfer"]
49
+ )
50
+ for token in tokens
51
+ ):
52
+ return True
53
+
54
+ # Maybe native asset was used instead of wrapped.
55
+ if tokens[0] == WRAPPED_GAS_COIN:
56
+ if any(
57
+ tokens[1] == transfer.address
58
+ and tx.to_address
59
+ == transfer.values()[:1] # type: ignore [index]
60
+ == [mint["sender"], mint.address]
61
+ for transfer in transfers
62
+ ):
63
+ for int_tx in chain.get_transaction(tx.hash).internal_transfers:
64
+ if (
65
+ tx.to_address == int_tx["from"] == mint["sender"]
66
+ and int_tx["to"] in ROUTERS
67
+ ):
68
+ for transfer in transfers:
69
+ if (
70
+ transfer[0] == WRAPPED_GAS_COIN == transfer.address
71
+ and tx.token == transfer[1]
72
+ and transfer[2] == int_tx["value"]
73
+ ):
74
+ return True
75
+
76
+ elif tokens[1] == WRAPPED_GAS_COIN:
77
+ if any(
78
+ tokens[0] == transfer.address
79
+ and tx.to_address
80
+ == transfer.values()[:1] # type: ignore [index]
81
+ == [mint["sender"], mint.address]
82
+ for transfer in transfers
83
+ ):
84
+ for int_tx in chain.get_transaction(tx.hash).internal_transfers:
85
+ if (
86
+ tx.to_address == int_tx["from"] == mint["sender"]
87
+ and int_tx["to"] in ROUTERS
88
+ ):
89
+ for transfer in transfers:
90
+ if (
91
+ transfer[0] == WRAPPED_GAS_COIN == transfer.address
92
+ and tx.token == transfer[1]
93
+ and transfer[2] == int_tx["value"]
94
+ ):
95
+ return True
96
+
97
+ else:
98
+ print(f"tokens: {tokens}")
99
+
100
+ # Component tokens
101
+ elif tx.to_address == mint.address:
102
+ return True
103
+
104
+ if CHAINID == Network.Mainnet:
105
+ return (
106
+ tx.hash == "0x3a000d3aa5d0d83a3ff359de261bfcecdc62cd13500b8ab517802742ac918627"
107
+ ) # uni v3
108
+ return False
109
+
110
+
111
+ @uniswap("Remove Liquidity")
112
+ async def is_uniswap_withdrawal(tx: TreasuryTx) -> bool:
113
+ if tx.to_address:
114
+ try:
115
+ events = tx.events
116
+ except KeyError as e:
117
+ if e.args[0] == "components":
118
+ return False
119
+ raise
120
+
121
+ if "Burn" in events and "Transfer" in events:
122
+ transfers = events["Transfer"]
123
+ for burn in events["Burn"]:
124
+ event_args = {"sender", "amount0", "amount1", "to"}
125
+ if any(arg not in burn for arg in event_args):
126
+ continue
127
+
128
+ # LP token
129
+ if (
130
+ TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
131
+ and tx.from_address == burn["to"]
132
+ and tx.token == tx.to_address == burn.address
133
+ ):
134
+ lp = tx.token.contract
135
+ tokens = [await lp.token0, await lp.token1]
136
+ if tx.token == tx.to_address and all(
137
+ any(
138
+ token == transfer.address
139
+ and tx.to_address == transfer[0]
140
+ and tx.from_address == transfer[1] == burn["to"]
141
+ for transfer in transfers
142
+ )
143
+ for token in tokens
144
+ ):
145
+ return True
146
+
147
+ # Maybe native asset was used instead of wrapped.
148
+ if tokens[0] == WRAPPED_GAS_COIN:
149
+ if any(
150
+ tokens[1] == transfer.address
151
+ and tx.token == tx.to_address == transfer[0]
152
+ and tx.from_address == transfer[1] == burn["to"]
153
+ for transfer in transfers
154
+ ):
155
+ for int_tx in chain.get_transaction(tx.hash).internal_transfers:
156
+ if int_tx["from"] in ROUTERS and tx.from_address == int_tx["to"]:
157
+ for transfer in transfers:
158
+ if (
159
+ tx.token == transfer[0]
160
+ and transfer[1] == transfer.address == WRAPPED_GAS_COIN
161
+ and transfer[2] == int_tx["value"]
162
+ ):
163
+ return True
164
+
165
+ elif tokens[1] == WRAPPED_GAS_COIN:
166
+ if any(
167
+ tokens[0] == transfer.address
168
+ and tx.token == tx.to_address == transfer[0]
169
+ and tx.from_address == transfer[1] == burn["to"]
170
+ for transfer in transfers
171
+ ):
172
+ for int_tx in chain.get_transaction(tx.hash).internal_transfers:
173
+ if int_tx["from"] in ROUTERS and tx.from_address == int_tx["to"]:
174
+ for transfer in transfers:
175
+ if (
176
+ transfer[0] == tx.token
177
+ and transfer[1] == transfer.address == WRAPPED_GAS_COIN
178
+ and transfer[2] == int_tx["value"]
179
+ ):
180
+ return True
181
+
182
+ else:
183
+ print(f"tokens: {tokens}")
184
+
185
+ # Component tokens
186
+ elif tx.from_address == burn.address:
187
+ return True
188
+
189
+ return CHAINID == Network.Mainnet and tx.hash in (
190
+ "0xf0723677162cdf8105c0f752a8c03c53803cb9dd9a6649f3b9bc5d26822d531f",
191
+ "0xaf1b7f138fb8bf3f5e13a680cb4a9b7983ec71a75836111c03dee6ae530db176", # v3
192
+ # these use ETH not WETH so they dont match
193
+ "0x5b05dfd3305c471df0ad944237edc2dbb14b268f7415252de566a5ab283002af",
194
+ "0x46ab9b383751f612ea0de8c0c6e9fa86e7324de04b032ecb48161989b7dbdbf7",
195
+ )
196
+
197
+
198
+ @uniswap("Swap")
199
+ async def is_uniswap_swap(tx: TreasuryTx) -> bool:
200
+ # The LP for dumping solidSEX is not verified :( devs blz do something
201
+ # Sell side
202
+ if (
203
+ TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
204
+ and tx.to_nickname == "Non-Verified Contract: 0xa66901D1965F5410dEeB4d0Bb43f7c1B628Cb20b"
205
+ and tx.symbol == "SOLIDsex"
206
+ ):
207
+ return True
208
+ # Buy side
209
+ elif (
210
+ tx.from_nickname == "Non-Verified Contract: 0xa66901D1965F5410dEeB4d0Bb43f7c1B628Cb20b"
211
+ and TreasuryWallet._get_instance(tx.to_address.address) # type: ignore [union-attr, arg-type]
212
+ and tx.symbol == "WFTM"
213
+ ):
214
+ return True
215
+
216
+ elif CHAINID == Network.Mainnet and tx.hash in (
217
+ # uni v3
218
+ "0x490245ef6e3c60127491415afdea23c13f4ca1a8c04de4fb3a498e7f7574b724",
219
+ "0xf2c6ff1863c60ca9924b611dad5548ffc4fecbab2fee34e2601dd16f0aa8e333",
220
+ ):
221
+ return True
222
+
223
+ # All other swaps
224
+ for swap in tx.get_events("Swap"):
225
+ # Sell side
226
+ if (
227
+ TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
228
+ and tx.to_address == swap.address
229
+ ):
230
+ pool = await Contract.coroutine(swap.address)
231
+ if not is_pool(pool): # type: ignore [arg-type]
232
+ continue
233
+
234
+ token0 = await pool.token0 # type: ignore [attr-defined]
235
+ token1 = await pool.token1 # type: ignore [attr-defined]
236
+ if token0 in SKIP_TOKENS or token1 in SKIP_TOKENS:
237
+ # This will be recorded elsewhere
238
+ continue
239
+
240
+ # The below code only works for v2 swaps, let's skip v3 swaps
241
+ if "sqrtPriceX96" in swap:
242
+ continue
243
+
244
+ if tx.token == token0:
245
+ # TODO: get rid of this rounding when we migrate to postgres
246
+ event_amount = round(tx.token.scale_value(swap["amount0In"]), 10)
247
+ if event_amount == round(tx.amount, 10):
248
+ return True
249
+ print(
250
+ f"Uniswap sell token0 amount does not match: {round(tx.amount, 10)} {event_amount}"
251
+ )
252
+ elif tx.token == token1:
253
+ # TODO: get rid of this rounding when we migrate to postgres
254
+ event_amount = round(tx.token.scale_value(swap["amount1In"]), 10)
255
+ if event_amount == round(tx.amount, 10):
256
+ return True
257
+ print(
258
+ f"Uniswap sell token1 amount does not match: {round(tx.amount, 10)} {event_amount}"
259
+ )
260
+
261
+ # Buy side
262
+ elif tx.from_address == swap.address and TreasuryWallet._get_instance(
263
+ tx.to_address.address # type: ignore [union-attr, arg-type]
264
+ ):
265
+ pool = await Contract.coroutine(swap.address)
266
+ if not is_pool(pool): # type: ignore [arg-type]
267
+ continue
268
+ token0 = await pool.token0 # type: ignore [attr-defined]
269
+ token1 = await pool.token1 # type: ignore [attr-defined]
270
+ if token0 in SKIP_TOKENS or token1 in SKIP_TOKENS:
271
+ # This will be recorded elsewhere
272
+ continue
273
+ if "amount0Out" in swap and tx.token == token0:
274
+ # TODO: get rid of this rounding when we migrate to postgres
275
+ event_amount = round(tx.token.scale_value(swap["amount0Out"]), 9)
276
+ if event_amount == round(tx.amount, 9):
277
+ return True
278
+ print(
279
+ f"Uniswap buy token0 amount does not match: {round(tx.amount, 9)} {event_amount}"
280
+ )
281
+ elif "amount1Out" in swap and tx.token == token1:
282
+ # TODO: get rid of this rounding when we migrate to postgres
283
+ event_amount = round(tx.token.scale_value(swap["amount1Out"]), 10)
284
+ if event_amount == round(tx.amount, 10):
285
+ return True
286
+ print(
287
+ f"Uniswap buy token1 amount does not match: {round(tx.amount, 10)} {event_amount}"
288
+ )
289
+ return False
290
+
291
+
292
+ def is_pool(pool: Contract) -> bool:
293
+ return hasattr(pool, "token0") and hasattr(pool, "token1")
@@ -0,0 +1,17 @@
1
+ """
2
+ Ignore rules for the Unwrapper contract.
3
+
4
+ This module defines matching logic for swaps involving the Unwrapper
5
+ contract, so those transactions can be ignored from analytics and
6
+ reporting.
7
+ """
8
+
9
+ from dao_treasury import TreasuryTx
10
+ from y import Network
11
+
12
+ from yearn_treasury.rules.ignore.swaps import swaps
13
+
14
+
15
+ @swaps("Unwrapper", Network.Mainnet)
16
+ def is_unwrapper(tx: TreasuryTx) -> bool:
17
+ return "Contract: Unwrapper" in [tx.from_nickname, tx.to_nickname]
@@ -0,0 +1,263 @@
1
+ from typing import Final, cast
2
+
3
+ from dao_treasury import TreasuryTx, TreasuryWallet
4
+ from dao_treasury.db import Address
5
+ from eth_typing import BlockNumber, ChecksumAddress
6
+ from faster_async_lru import alru_cache
7
+ from y import Contract, Network
8
+ from y.prices.yearn import YearnInspiredVault
9
+
10
+ from yearn_treasury.constants import CHAINID, TREASURY_WALLETS
11
+ from yearn_treasury.rules.constants import ZERO_ADDRESS
12
+ from yearn_treasury.rules.ignore.swaps import swaps
13
+ from yearn_treasury.vaults import v1, v2
14
+
15
+ vaults: Final = swaps("Vaults")
16
+
17
+ TREASURY_AND_ZERO: Final = {*TREASURY_WALLETS, ZERO_ADDRESS}
18
+
19
+ all_vaults: Final = tuple(v1.keys()) + tuple(v2.values())
20
+
21
+
22
+ @vaults("Deposit")
23
+ async def is_vault_deposit(tx: TreasuryTx) -> bool:
24
+ return (
25
+ await is_v1_or_v2_vault_deposit(tx)
26
+ or tx.hash
27
+ in {
28
+ # TODO these go thru zaps and do not get caught by the logic below. figure out how to capture these
29
+ Network.Mainnet: { # type: ignore [call-overload]
30
+ "0x39616fdfc8851e10e17d955f55beea5c3dd4eed7c066a8ecbed8e50b496012ff",
31
+ "0x248e896eb732dfe40a0fa49131717bb7d2c1721743a2945ab9680787abcf9c50",
32
+ "0x2ce0240a08c8cc8d35b018995862711eb660a24d294b1aa674fbc467af4e621b",
33
+ # this is not thru a zap, its a lp-yCRVv2 deposit I probably dont need to write hueristics for
34
+ "0x93cf055d82b7e82b3877ab506629de6359fc5385ffb6b8c2fbfe0d61947fab59",
35
+ }
36
+ }.get(CHAINID, set())
37
+ or is_v3_vault_deposit(tx)
38
+ )
39
+
40
+
41
+ async def is_v1_or_v2_vault_deposit(tx: TreasuryTx) -> bool:
42
+ """This code doesn't validate amounts but so far that's not been a problem."""
43
+ try:
44
+ if "Transfer" not in tx.events:
45
+ return False
46
+ except KeyError as e:
47
+ # This happens sometimes from a busted abi, shouldnt impact us
48
+ if str(e) == "'components'":
49
+ return False
50
+ raise
51
+
52
+ transfer_events = tx.events["Transfer"]
53
+
54
+ tx_token = tx.token_address
55
+
56
+ block = BlockNumber(tx.block)
57
+ sender: ChecksumAddress
58
+ receiver: ChecksumAddress
59
+ underlying_address: ChecksumAddress
60
+
61
+ # vault side
62
+ for vault in all_vaults:
63
+ if tx_token == vault.address:
64
+ for event in transfer_events:
65
+ if tx_token == event.address:
66
+ event_pos = event.pos
67
+ sender, receiver, value = event.values()
68
+ if sender == ZERO_ADDRESS and TreasuryWallet.check_membership(receiver, block):
69
+ tx_to_address = tx.to_address
70
+ underlying_address = await _get_underlying(vault)
71
+ for _event in transfer_events:
72
+ _sender, _receiver, _value = _event.values()
73
+ if (
74
+ _event.address == underlying_address
75
+ and tx_to_address == _sender
76
+ and tx_token == _receiver
77
+ ):
78
+ # v1
79
+ if _event.pos < event_pos:
80
+ return True
81
+ # v2
82
+ if event_pos < _event.pos:
83
+ return True
84
+
85
+ # token side
86
+ for vault in all_vaults:
87
+ if tx_token == await _get_underlying(vault):
88
+ for event in transfer_events:
89
+ if tx_token == event.address:
90
+ vault_address = vault.address
91
+ event_pos = event.pos
92
+ sender, receiver, value = event.values()
93
+ if TreasuryWallet.check_membership(sender, block) and receiver == vault_address:
94
+ for _event in transfer_events:
95
+ _sender, _receiver, _value = _event.values()
96
+ if (
97
+ _event.address == vault_address
98
+ and _sender == ZERO_ADDRESS
99
+ and TreasuryWallet.check_membership(_receiver, block)
100
+ ):
101
+ # v1?
102
+ if event_pos < _event.pos:
103
+ return True
104
+ # v2
105
+ if _event.pos < event_pos:
106
+ return True
107
+ return False
108
+
109
+
110
+ _v3_deposit_keys: Final = "sender", "owner", "assets", "shares"
111
+
112
+
113
+ def is_v3_vault_deposit(tx: TreasuryTx) -> bool:
114
+ try:
115
+ if "Deposit" not in tx.events:
116
+ return False
117
+ except KeyError as e:
118
+ # This happens sometimes due to a busted abi, shouldnt impact us
119
+ if str(e) == "'components'":
120
+ return False
121
+ raise
122
+
123
+ if deposits := [
124
+ event for event in tx.events["Deposit"] if all(key in event for key in _v3_deposit_keys)
125
+ ]:
126
+ token = tx.token
127
+ to_address = tx.to_address
128
+ amount = tx.amount
129
+
130
+ # Vault side
131
+ if tx.from_address == ZERO_ADDRESS:
132
+ token_address = token.address.address
133
+ if deposits := [d for d in deposits if token_address == d.address]:
134
+ for deposit in deposits:
135
+ if to_address != deposit["owner"]:
136
+ print("wrong owner")
137
+ continue
138
+ # TODO: once postgres is in, remove the `round`
139
+ # elif amount == (scaled := token.scale_value(deposit["shares"])):
140
+ # return True
141
+ amount = round(amount, 8)
142
+ scaled = round(token.scale_value(deposit["shares"]), 8)
143
+ if amount == scaled:
144
+ return True
145
+ print(f"wrong amount: tx={amount} event={scaled}")
146
+ print("no matching vault-side deposit found")
147
+
148
+ # Token side
149
+ elif deposits := [d for d in deposits if to_address == d.address]:
150
+ from_address = cast(Address, tx.from_address).address
151
+ for deposit in deposits:
152
+ if from_address != deposit["sender"]:
153
+ print("sender doesnt match")
154
+ continue
155
+ # TODO: once postgres is in, remove the `round`
156
+ amount = round(amount, 8)
157
+ scaled = round(token.scale_value(deposit["assets"]), 8)
158
+ if amount == scaled:
159
+ return True
160
+ print(f"wrong amount: tx={amount} event={scaled}")
161
+ print("no matching token-side deposit found")
162
+ return False
163
+
164
+
165
+ @alru_cache(maxsize=None)
166
+ async def _get_underlying(vault: Contract) -> ChecksumAddress:
167
+ underlying = await YearnInspiredVault(vault, asynchronous=True).underlying
168
+ return underlying.address # type: ignore [return-value]
169
+
170
+
171
+ @vaults("Withdrawal")
172
+ async def is_vault_withdrawal(tx: TreasuryTx) -> bool:
173
+ to_address = cast(Address, tx.to_address).address
174
+ if to_address not in TREASURY_AND_ZERO:
175
+ return False
176
+
177
+ try:
178
+ if "Transfer" not in tx.events:
179
+ return False
180
+ except KeyError as e:
181
+ if str(e) == "'components'":
182
+ return False
183
+ raise
184
+
185
+ transfer_events = tx.events["Transfer"]
186
+
187
+ token = tx.token
188
+ token_address = cast(ChecksumAddress, token.address.address)
189
+ block = BlockNumber(tx.block)
190
+
191
+ underlying: ChecksumAddress
192
+
193
+ # vault side
194
+ if any(token_address == vault.address for vault in all_vaults):
195
+ for event in transfer_events:
196
+ if token_address == event.address:
197
+ sender, receiver, value = event.values()
198
+ if (
199
+ to_address == ZERO_ADDRESS == receiver
200
+ and TreasuryWallet.check_membership(sender, block)
201
+ and tx.from_address == sender
202
+ ):
203
+ underlying = await _get_underlying(token_address)
204
+ for _event in transfer_events:
205
+ _sender, _receiver, _value = _event.values()
206
+ if (
207
+ _event.address == underlying
208
+ and tx.from_address == _receiver
209
+ and event.pos < _event.pos
210
+ and token_address == _sender
211
+ ):
212
+ return True
213
+ # token side
214
+ for vault in all_vaults:
215
+ if token_address == await _get_underlying(vault):
216
+ vault_address = vault.address
217
+ for event in transfer_events:
218
+ if token_address == event.address:
219
+ sender, receiver, value = event.values()
220
+ if tx.from_address == vault_address == sender and to_address == receiver:
221
+ for _event in transfer_events:
222
+ _sender, _receiver, _value = _event.values()
223
+ if (
224
+ _event.address == vault_address
225
+ and _receiver == ZERO_ADDRESS
226
+ and TreasuryWallet.check_membership(_sender, block)
227
+ and to_address == _sender
228
+ and _event.pos < event.pos
229
+ ):
230
+ return True
231
+ return False
232
+
233
+
234
+ @vaults("DOLA Fed Withdrawal")
235
+ def is_dolla_fed_withdrawal(tx: TreasuryTx) -> bool:
236
+ if tx.from_nickname == "Token: Curve DOLA Pool yVault - Unlisted" and TreasuryWallet.check_membership(tx.to_address.address, tx.block) and tx.symbol == "DOLA3POOL3CRV-f": # type: ignore [union-attr, arg-type]
237
+ return True
238
+ elif TreasuryWallet.check_membership(tx.from_address.address, tx.block) and tx.to_address == ZERO_ADDRESS and tx.symbol == "yvCurve-DOLA-U": # type: ignore [union-attr, arg-type]
239
+ return True
240
+ return False
241
+
242
+
243
+ @vaults("DOLA FRAX Vault Withdrawal")
244
+ def is_dola_frax_withdrawal(tx: TreasuryTx) -> bool:
245
+ symbol = tx.symbol
246
+ from_nickname = tx.from_nickname
247
+ to_nickname = tx.to_nickname
248
+ if (
249
+ symbol == "yvCurve-DOLA-FRAXBP-U"
250
+ and from_nickname == "Yearn yChad Multisig"
251
+ and to_nickname == "Zero Address"
252
+ ):
253
+ return True
254
+ elif (
255
+ symbol == "DOLAFRAXBP3CRV-f"
256
+ and from_nickname == "Token: Curve DOLA-FRAXBP Pool yVault - Unlisted"
257
+ and to_nickname == "Yearn yChad Multisig"
258
+ ):
259
+ return True
260
+ return (
261
+ tx.hash == "0x59a3a3b9e724835958eab6d0956a3acf697191182c41403c96d39976047d7240"
262
+ and tx.log_index == 232
263
+ )
@@ -0,0 +1,79 @@
1
+ from decimal import Decimal
2
+ from typing import Final
3
+
4
+ from brownie import ZERO_ADDRESS
5
+ from dao_treasury import TreasuryTx
6
+ from y import Network
7
+
8
+ from yearn_treasury.constants import YFI
9
+ from yearn_treasury.rules.ignore.swaps import swaps
10
+
11
+ WOOFY: Final = "0xD0660cD418a64a1d44E9214ad8e459324D8157f1"
12
+
13
+ YFI_SCALE: Final = Decimal(10**18)
14
+ WOOFY_SCALE: Final = Decimal(10**12)
15
+
16
+
17
+ @swaps("WOOFY", Network.Mainnet)
18
+ def is_woofy(tx: TreasuryTx) -> bool:
19
+ """
20
+ Returns True if the tx involved wrapping or unwrapping WOOFY.
21
+
22
+ https://docs.yearn.fi/resources/deprecated/woofy
23
+ """
24
+
25
+ # Wrapping, YFI side
26
+ if tx.to_address == WOOFY and tx.symbol == "YFI":
27
+ # Check for WOOFY transfer
28
+ for transfer in tx.get_events("Transfer"):
29
+ if transfer.address != WOOFY:
30
+ continue
31
+ sender, receiver, amount = transfer.values()
32
+ if sender == ZERO_ADDRESS and tx.from_address == receiver:
33
+ scaled = Decimal(amount) / YFI_SCALE
34
+ if scaled == tx.amount:
35
+ return True
36
+ print(f"woofy wrapping woofy side amount no match: [{scaled}, {tx.amount}]")
37
+
38
+ # Wrapping, WOOFY side
39
+ elif tx.from_address == ZERO_ADDRESS and tx.symbol == "WOOFY":
40
+ # Check for YFI transfer
41
+ for transfer in tx.get_events("Transfer"):
42
+ if transfer.address != YFI:
43
+ continue
44
+ sender, receiver, amount = transfer.values()
45
+ if receiver == WOOFY and tx.to_address == sender:
46
+ scaled = Decimal(amount) / WOOFY_SCALE
47
+ if scaled == tx.amount:
48
+ return True
49
+ print(f"woofy wrapping yfi side amount no match: [{scaled}, {tx.amount}]")
50
+
51
+ # Unwrapping, YFI side
52
+ elif tx.from_address == WOOFY and tx.symbol == "YFI":
53
+ # Check for WOOFY transfer
54
+ for transfer in tx.get_events("Transfer"):
55
+ if transfer.address != WOOFY:
56
+ continue
57
+ sender, receiver, amount = transfer.values()
58
+ if tx.to_address == sender and receiver == ZERO_ADDRESS:
59
+ scaled = round(Decimal(amount) / YFI_SCALE, 12)
60
+ rounded = round(tx.amount, 12)
61
+ if scaled == rounded:
62
+ return True
63
+ print(f"woofy unwrapping yfi side amount no match: [{scaled}, {rounded}]")
64
+
65
+ # Unwrapping, WOOFY side
66
+ elif tx.to_address == ZERO_ADDRESS and tx.symbol == "WOOFY":
67
+ # Check for YFI transfer
68
+ for transfer in tx.get_events("Transfer"):
69
+ if transfer.address != YFI:
70
+ continue
71
+ sender, receiver, amount = transfer.values()
72
+ if sender == WOOFY and tx.from_address == receiver:
73
+ # TODO remove this rounding once sqlite is replaced with postgres
74
+ scaled = round(Decimal(amount) / WOOFY_SCALE, 7)
75
+ rounded = round(tx.amount, 7)
76
+ if scaled == rounded:
77
+ return True
78
+ print(f"woofy unwrapping woofy side amount no match: [{scaled}, {rounded}]")
79
+ return False