yearn-treasury 0.0.44__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 +20 -0
- yearn_treasury/_db.cp310-win32.pyd +0 -0
- yearn_treasury/_db.py +41 -0
- yearn_treasury/_ens.cp310-win32.pyd +0 -0
- yearn_treasury/_ens.py +31 -0
- yearn_treasury/_logging.cp310-win32.pyd +0 -0
- yearn_treasury/_logging.py +43 -0
- yearn_treasury/address_labels.yaml +39 -0
- yearn_treasury/budget/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/budget/__init__.py +6 -0
- yearn_treasury/budget/_request.cp310-win32.pyd +0 -0
- yearn_treasury/budget/_request.py +44 -0
- yearn_treasury/budget/_requests.cp310-win32.pyd +0 -0
- yearn_treasury/budget/_requests.py +96 -0
- yearn_treasury/constants.py +89 -0
- yearn_treasury/main.py +176 -0
- yearn_treasury/py.typed +1 -0
- yearn_treasury/rules/__init__.py +20 -0
- yearn_treasury/rules/constants.cp310-win32.pyd +0 -0
- yearn_treasury/rules/constants.py +17 -0
- yearn_treasury/rules/cost_of_revenue/__init__.py +1 -0
- yearn_treasury/rules/cost_of_revenue/gas.cp310-win32.pyd +0 -0
- yearn_treasury/rules/cost_of_revenue/gas.py +66 -0
- yearn_treasury/rules/cost_of_revenue/match_on_hash.yaml +12 -0
- yearn_treasury/rules/expense/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/__init__.py +4 -0
- yearn_treasury/rules/expense/general.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/general.py +13 -0
- yearn_treasury/rules/expense/infrastructure.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/infrastructure.py +47 -0
- yearn_treasury/rules/expense/match_on_hash.yaml +48 -0
- yearn_treasury/rules/expense/match_on_to_address.yaml +7 -0
- yearn_treasury/rules/expense/people.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/people.py +84 -0
- yearn_treasury/rules/expense/security.cp310-win32.pyd +0 -0
- yearn_treasury/rules/expense/security.py +147 -0
- yearn_treasury/rules/ignore/__init__.py +8 -0
- yearn_treasury/rules/ignore/general.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/general.py +11 -0
- yearn_treasury/rules/ignore/maker.py +92 -0
- yearn_treasury/rules/ignore/passthru.py +310 -0
- yearn_treasury/rules/ignore/staking.py +102 -0
- yearn_treasury/rules/ignore/swaps/__init__.py +25 -0
- yearn_treasury/rules/ignore/swaps/_skip_tokens.py +9 -0
- yearn_treasury/rules/ignore/swaps/aave.py +69 -0
- 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 +58 -0
- 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 +294 -0
- 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 +263 -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 +48 -0
- yearn_treasury/rules/ignore/ygov.cp310-win32.pyd +0 -0
- yearn_treasury/rules/ignore/ygov.py +16 -0
- yearn_treasury/rules/other_expense/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/__init__.py +7 -0
- yearn_treasury/rules/other_expense/boost.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/boost.py +49 -0
- yearn_treasury/rules/other_expense/bugs.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/bugs.py +81 -0
- yearn_treasury/rules/other_expense/donations.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/donations.py +43 -0
- yearn_treasury/rules/other_expense/dyfi.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/dyfi.py +29 -0
- yearn_treasury/rules/other_expense/events.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/events.py +21 -0
- yearn_treasury/rules/other_expense/match_on_hash.yaml +43 -0
- yearn_treasury/rules/other_expense/match_on_to_address.yaml +8 -0
- yearn_treasury/rules/other_expense/misc.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/misc.py +49 -0
- yearn_treasury/rules/other_expense/revshare.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/revshare.py +20 -0
- yearn_treasury/rules/other_income/__init__.cp310-win32.pyd +0 -0
- yearn_treasury/rules/other_income/__init__.py +2 -0
- 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 +21 -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/__init__.py +6 -0
- yearn_treasury/rules/revenue/bribes.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/bribes.py +26 -0
- yearn_treasury/rules/revenue/farming.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/farming.py +56 -0
- yearn_treasury/rules/revenue/keepcoins.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/keepcoins.py +63 -0
- yearn_treasury/rules/revenue/match_on_hash.yaml +4 -0
- yearn_treasury/rules/revenue/seasolver.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/seasolver.py +23 -0
- yearn_treasury/rules/revenue/vaults.py +168 -0
- yearn_treasury/rules/revenue/yteams.cp310-win32.pyd +0 -0
- yearn_treasury/rules/revenue/yteams.py +17 -0
- yearn_treasury/shitcoins.py +141 -0
- yearn_treasury/vaults.cp310-win32.pyd +0 -0
- yearn_treasury/vaults.py +50 -0
- yearn_treasury/wallets.yaml +54 -0
- yearn_treasury/yteams.py +208 -0
- yearn_treasury-0.0.44.dist-info/METADATA +86 -0
- yearn_treasury-0.0.44.dist-info/RECORD +128 -0
- yearn_treasury-0.0.44.dist-info/WHEEL +5 -0
- yearn_treasury-0.0.44.dist-info/entry_points.txt +2 -0
- yearn_treasury-0.0.44.dist-info/top_level.txt +2 -0
- yearn_treasury__mypyc.cp310-win32.pyd +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
|
|
3
|
+
from brownie import ZERO_ADDRESS, chain
|
|
4
|
+
from dao_treasury import TreasuryTx, TreasuryWallet
|
|
5
|
+
from y import Network, Contract
|
|
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
|
+
|
|
12
|
+
uniswap: Final = swaps("Uniswap")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
ROUTERS: Final = ("0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F",)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@uniswap("Add Liquidity")
|
|
19
|
+
async def is_uniswap_deposit(tx: TreasuryTx) -> bool:
|
|
20
|
+
if tx.to_address:
|
|
21
|
+
try:
|
|
22
|
+
events = tx.events
|
|
23
|
+
except KeyError as e:
|
|
24
|
+
if e.args[0] == "components":
|
|
25
|
+
return False
|
|
26
|
+
raise
|
|
27
|
+
|
|
28
|
+
if "Mint" in events and "Transfer" in events:
|
|
29
|
+
transfers = events["Transfer"]
|
|
30
|
+
for mint in events["Mint"]:
|
|
31
|
+
event_args = {"sender", "amount0", "amount1"}
|
|
32
|
+
if any(arg not in mint for arg in event_args):
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
# LP token
|
|
36
|
+
if tx.from_address == ZERO_ADDRESS and (
|
|
37
|
+
tx.token == mint.address
|
|
38
|
+
or
|
|
39
|
+
# KP3R/WETH Uni v3 LP -- used while depositing to kLP-KP3R/WETH
|
|
40
|
+
mint.address == "0x11B7a6bc0259ed6Cf9DB8F499988F9eCc7167bf5"
|
|
41
|
+
):
|
|
42
|
+
lp = tx.token.contract
|
|
43
|
+
tokens = [await lp.token0, await lp.token1]
|
|
44
|
+
if all(
|
|
45
|
+
any(
|
|
46
|
+
token == transfer.address
|
|
47
|
+
and tx.to_address == transfer[0]
|
|
48
|
+
and transfer[1] == mint.address
|
|
49
|
+
for transfer in events["Transfer"]
|
|
50
|
+
)
|
|
51
|
+
for token in tokens
|
|
52
|
+
):
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
# Maybe native asset was used instead of wrapped.
|
|
56
|
+
if tokens[0] == WRAPPED_GAS_COIN:
|
|
57
|
+
if any(
|
|
58
|
+
tokens[1] == transfer.address
|
|
59
|
+
and tx.to_address
|
|
60
|
+
== transfer.values()[:1] # type: ignore [index]
|
|
61
|
+
== [mint["sender"], mint.address]
|
|
62
|
+
for transfer in transfers
|
|
63
|
+
):
|
|
64
|
+
for int_tx in chain.get_transaction(tx.hash).internal_transfers:
|
|
65
|
+
if (
|
|
66
|
+
tx.to_address == int_tx["from"] == mint["sender"]
|
|
67
|
+
and int_tx["to"] in ROUTERS
|
|
68
|
+
):
|
|
69
|
+
for transfer in transfers:
|
|
70
|
+
if (
|
|
71
|
+
transfer[0] == WRAPPED_GAS_COIN == transfer.address
|
|
72
|
+
and tx.token == transfer[1]
|
|
73
|
+
and transfer[2] == int_tx["value"]
|
|
74
|
+
):
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
elif tokens[1] == WRAPPED_GAS_COIN:
|
|
78
|
+
if any(
|
|
79
|
+
tokens[0] == transfer.address
|
|
80
|
+
and tx.to_address
|
|
81
|
+
== transfer.values()[:1] # type: ignore [index]
|
|
82
|
+
== [mint["sender"], mint.address]
|
|
83
|
+
for transfer in transfers
|
|
84
|
+
):
|
|
85
|
+
for int_tx in chain.get_transaction(tx.hash).internal_transfers:
|
|
86
|
+
if (
|
|
87
|
+
tx.to_address == int_tx["from"] == mint["sender"]
|
|
88
|
+
and int_tx["to"] in ROUTERS
|
|
89
|
+
):
|
|
90
|
+
for transfer in transfers:
|
|
91
|
+
if (
|
|
92
|
+
transfer[0] == WRAPPED_GAS_COIN == transfer.address
|
|
93
|
+
and tx.token == transfer[1]
|
|
94
|
+
and transfer[2] == int_tx["value"]
|
|
95
|
+
):
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
else:
|
|
99
|
+
print(f"tokens: {tokens}")
|
|
100
|
+
|
|
101
|
+
# Component tokens
|
|
102
|
+
elif tx.to_address == mint.address:
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
if CHAINID == Network.Mainnet:
|
|
106
|
+
return (
|
|
107
|
+
tx.hash == "0x3a000d3aa5d0d83a3ff359de261bfcecdc62cd13500b8ab517802742ac918627"
|
|
108
|
+
) # uni v3
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@uniswap("Remove Liquidity")
|
|
113
|
+
async def is_uniswap_withdrawal(tx: TreasuryTx) -> bool:
|
|
114
|
+
if tx.to_address:
|
|
115
|
+
try:
|
|
116
|
+
events = tx.events
|
|
117
|
+
except KeyError as e:
|
|
118
|
+
if e.args[0] == "components":
|
|
119
|
+
return False
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
if "Burn" in events and "Transfer" in events:
|
|
123
|
+
transfers = events["Transfer"]
|
|
124
|
+
for burn in events["Burn"]:
|
|
125
|
+
event_args = {"sender", "amount0", "amount1", "to"}
|
|
126
|
+
if any(arg not in burn for arg in event_args):
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
# LP token
|
|
130
|
+
if (
|
|
131
|
+
TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
|
|
132
|
+
and tx.from_address == burn["to"]
|
|
133
|
+
and tx.token == tx.to_address == burn.address
|
|
134
|
+
):
|
|
135
|
+
lp = tx.token.contract
|
|
136
|
+
tokens = [await lp.token0, await lp.token1]
|
|
137
|
+
if tx.token == tx.to_address and all(
|
|
138
|
+
any(
|
|
139
|
+
token == transfer.address
|
|
140
|
+
and tx.to_address == transfer[0]
|
|
141
|
+
and tx.from_address == transfer[1] == burn["to"]
|
|
142
|
+
for transfer in transfers
|
|
143
|
+
)
|
|
144
|
+
for token in tokens
|
|
145
|
+
):
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
# Maybe native asset was used instead of wrapped.
|
|
149
|
+
if tokens[0] == WRAPPED_GAS_COIN:
|
|
150
|
+
if any(
|
|
151
|
+
tokens[1] == transfer.address
|
|
152
|
+
and tx.token == tx.to_address == transfer[0]
|
|
153
|
+
and tx.from_address == transfer[1] == burn["to"]
|
|
154
|
+
for transfer in transfers
|
|
155
|
+
):
|
|
156
|
+
for int_tx in chain.get_transaction(tx.hash).internal_transfers:
|
|
157
|
+
if int_tx["from"] in ROUTERS and tx.from_address == int_tx["to"]:
|
|
158
|
+
for transfer in transfers:
|
|
159
|
+
if (
|
|
160
|
+
tx.token == transfer[0]
|
|
161
|
+
and transfer[1] == transfer.address == WRAPPED_GAS_COIN
|
|
162
|
+
and transfer[2] == int_tx["value"]
|
|
163
|
+
):
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
elif tokens[1] == WRAPPED_GAS_COIN:
|
|
167
|
+
if any(
|
|
168
|
+
tokens[0] == transfer.address
|
|
169
|
+
and tx.token == tx.to_address == transfer[0]
|
|
170
|
+
and tx.from_address == transfer[1] == burn["to"]
|
|
171
|
+
for transfer in transfers
|
|
172
|
+
):
|
|
173
|
+
for int_tx in chain.get_transaction(tx.hash).internal_transfers:
|
|
174
|
+
if int_tx["from"] in ROUTERS and tx.from_address == int_tx["to"]:
|
|
175
|
+
for transfer in transfers:
|
|
176
|
+
if (
|
|
177
|
+
transfer[0] == tx.token
|
|
178
|
+
and transfer[1] == transfer.address == WRAPPED_GAS_COIN
|
|
179
|
+
and transfer[2] == int_tx["value"]
|
|
180
|
+
):
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
else:
|
|
184
|
+
print(f"tokens: {tokens}")
|
|
185
|
+
|
|
186
|
+
# Component tokens
|
|
187
|
+
elif tx.from_address == burn.address:
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
return CHAINID == Network.Mainnet and tx.hash in (
|
|
191
|
+
"0xf0723677162cdf8105c0f752a8c03c53803cb9dd9a6649f3b9bc5d26822d531f",
|
|
192
|
+
"0xaf1b7f138fb8bf3f5e13a680cb4a9b7983ec71a75836111c03dee6ae530db176", # v3
|
|
193
|
+
# these use ETH not WETH so they dont match
|
|
194
|
+
"0x5b05dfd3305c471df0ad944237edc2dbb14b268f7415252de566a5ab283002af",
|
|
195
|
+
"0x46ab9b383751f612ea0de8c0c6e9fa86e7324de04b032ecb48161989b7dbdbf7",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@uniswap("Swap")
|
|
200
|
+
async def is_uniswap_swap(tx: TreasuryTx) -> bool:
|
|
201
|
+
# The LP for dumping solidSEX is not verified :( devs blz do something
|
|
202
|
+
# Sell side
|
|
203
|
+
if (
|
|
204
|
+
TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
|
|
205
|
+
and tx.to_nickname == "Non-Verified Contract: 0xa66901D1965F5410dEeB4d0Bb43f7c1B628Cb20b"
|
|
206
|
+
and tx.symbol == "SOLIDsex"
|
|
207
|
+
):
|
|
208
|
+
return True
|
|
209
|
+
# Buy side
|
|
210
|
+
elif (
|
|
211
|
+
tx.from_nickname == "Non-Verified Contract: 0xa66901D1965F5410dEeB4d0Bb43f7c1B628Cb20b"
|
|
212
|
+
and TreasuryWallet._get_instance(tx.to_address.address) # type: ignore [union-attr, arg-type]
|
|
213
|
+
and tx.symbol == "WFTM"
|
|
214
|
+
):
|
|
215
|
+
return True
|
|
216
|
+
|
|
217
|
+
elif CHAINID == Network.Mainnet and tx.hash in (
|
|
218
|
+
# uni v3
|
|
219
|
+
"0x490245ef6e3c60127491415afdea23c13f4ca1a8c04de4fb3a498e7f7574b724",
|
|
220
|
+
"0xf2c6ff1863c60ca9924b611dad5548ffc4fecbab2fee34e2601dd16f0aa8e333",
|
|
221
|
+
):
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
# All other swaps
|
|
225
|
+
for swap in tx.get_events("Swap"):
|
|
226
|
+
# Sell side
|
|
227
|
+
if (
|
|
228
|
+
TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
|
|
229
|
+
and tx.to_address == swap.address
|
|
230
|
+
):
|
|
231
|
+
pool = await Contract.coroutine(swap.address)
|
|
232
|
+
if not is_pool(pool): # type: ignore [arg-type]
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
token0 = await pool.token0 # type: ignore [attr-defined]
|
|
236
|
+
token1 = await pool.token1 # type: ignore [attr-defined]
|
|
237
|
+
if token0 in SKIP_TOKENS or token1 in SKIP_TOKENS:
|
|
238
|
+
# This will be recorded elsewhere
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
# The below code only works for v2 swaps, let's skip v3 swaps
|
|
242
|
+
if "sqrtPriceX96" in swap:
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
if tx.token == token0:
|
|
246
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
247
|
+
event_amount = round(tx.token.scale_value(swap["amount0In"]), 10)
|
|
248
|
+
if event_amount == round(tx.amount, 10):
|
|
249
|
+
return True
|
|
250
|
+
print(
|
|
251
|
+
f"Uniswap sell token0 amount does not match: {round(tx.amount, 10)} {event_amount}"
|
|
252
|
+
)
|
|
253
|
+
elif tx.token == token1:
|
|
254
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
255
|
+
event_amount = round(tx.token.scale_value(swap["amount1In"]), 10)
|
|
256
|
+
if event_amount == round(tx.amount, 10):
|
|
257
|
+
return True
|
|
258
|
+
print(
|
|
259
|
+
f"Uniswap sell token1 amount does not match: {round(tx.amount, 10)} {event_amount}"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Buy side
|
|
263
|
+
elif tx.from_address == swap.address and TreasuryWallet._get_instance(
|
|
264
|
+
tx.to_address.address # type: ignore [union-attr, arg-type]
|
|
265
|
+
):
|
|
266
|
+
pool = await Contract.coroutine(swap.address)
|
|
267
|
+
if not is_pool(pool): # type: ignore [arg-type]
|
|
268
|
+
continue
|
|
269
|
+
token0 = await pool.token0 # type: ignore [attr-defined]
|
|
270
|
+
token1 = await pool.token1 # type: ignore [attr-defined]
|
|
271
|
+
if token0 in SKIP_TOKENS or token1 in SKIP_TOKENS:
|
|
272
|
+
# This will be recorded elsewhere
|
|
273
|
+
continue
|
|
274
|
+
if "amount0Out" in swap and tx.token == token0:
|
|
275
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
276
|
+
event_amount = round(tx.token.scale_value(swap["amount0Out"]), 9)
|
|
277
|
+
if event_amount == round(tx.amount, 9):
|
|
278
|
+
return True
|
|
279
|
+
print(
|
|
280
|
+
f"Uniswap buy token0 amount does not match: {round(tx.amount, 9)} {event_amount}"
|
|
281
|
+
)
|
|
282
|
+
elif "amount1Out" in swap and tx.token == token1:
|
|
283
|
+
# TODO: get rid of this rounding when we migrate to postgres
|
|
284
|
+
event_amount = round(tx.token.scale_value(swap["amount1Out"]), 10)
|
|
285
|
+
if event_amount == round(tx.amount, 10):
|
|
286
|
+
return True
|
|
287
|
+
print(
|
|
288
|
+
f"Uniswap buy token1 amount does not match: {round(tx.amount, 10)} {event_amount}"
|
|
289
|
+
)
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def is_pool(pool: Contract) -> bool:
|
|
294
|
+
return hasattr(pool, "token0") and hasattr(pool, "token1")
|
|
Binary file
|
|
@@ -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]
|
|
Binary file
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
from typing import Final
|
|
2
|
+
|
|
3
|
+
from dao_treasury import TreasuryTx, TreasuryWallet
|
|
4
|
+
from eth_typing import BlockNumber, ChecksumAddress
|
|
5
|
+
from faster_async_lru import alru_cache
|
|
6
|
+
from y import Contract, Network
|
|
7
|
+
from y.prices.yearn import YearnInspiredVault
|
|
8
|
+
|
|
9
|
+
from yearn_treasury.constants import CHAINID, TREASURY_WALLETS
|
|
10
|
+
from yearn_treasury.rules.ignore.swaps import swaps
|
|
11
|
+
from yearn_treasury.rules.constants import ZERO_ADDRESS
|
|
12
|
+
from yearn_treasury.vaults import v1, v2
|
|
13
|
+
|
|
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.address
|
|
55
|
+
|
|
56
|
+
block: BlockNumber = tx.block # type: ignore [assignment]
|
|
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 = tx.from_address.address # type: ignore [union-attr]
|
|
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 = tx.to_address.address # type: ignore [union-attr]
|
|
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: ChecksumAddress = token.address.address # type: ignore [assignment]
|
|
189
|
+
block: BlockNumber = tx.block # type: ignore [assignment]
|
|
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
|
+
)
|
|
Binary file
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
|
|
12
|
+
WOOFY: Final = "0xD0660cD418a64a1d44E9214ad8e459324D8157f1"
|
|
13
|
+
|
|
14
|
+
YFI_SCALE: Final = Decimal(10**18)
|
|
15
|
+
WOOFY_SCALE: Final = Decimal(10**12)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@swaps("WOOFY", Network.Mainnet)
|
|
19
|
+
def is_woofy(tx: TreasuryTx) -> bool:
|
|
20
|
+
"""
|
|
21
|
+
Returns True if the tx involved wrapping or unwrapping WOOFY.
|
|
22
|
+
|
|
23
|
+
https://docs.yearn.fi/resources/deprecated/woofy
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Wrapping, YFI side
|
|
27
|
+
if tx.to_address == WOOFY and tx.symbol == "YFI":
|
|
28
|
+
# Check for WOOFY transfer
|
|
29
|
+
for transfer in tx.get_events("Transfer"):
|
|
30
|
+
if transfer.address != WOOFY:
|
|
31
|
+
continue
|
|
32
|
+
sender, receiver, amount = transfer.values()
|
|
33
|
+
if sender == ZERO_ADDRESS and tx.from_address == receiver:
|
|
34
|
+
scaled = Decimal(amount) / YFI_SCALE
|
|
35
|
+
if scaled == tx.amount:
|
|
36
|
+
return True
|
|
37
|
+
print(f"woofy wrapping woofy side amount no match: [{scaled}, {tx.amount}]")
|
|
38
|
+
|
|
39
|
+
# Wrapping, WOOFY side
|
|
40
|
+
elif tx.from_address == ZERO_ADDRESS and tx.symbol == "WOOFY":
|
|
41
|
+
# Check for YFI transfer
|
|
42
|
+
for transfer in tx.get_events("Transfer"):
|
|
43
|
+
if transfer.address != YFI:
|
|
44
|
+
continue
|
|
45
|
+
sender, receiver, amount = transfer.values()
|
|
46
|
+
if receiver == WOOFY and tx.to_address == sender:
|
|
47
|
+
scaled = Decimal(amount) / WOOFY_SCALE
|
|
48
|
+
if scaled == tx.amount:
|
|
49
|
+
return True
|
|
50
|
+
print(f"woofy wrapping yfi side amount no match: [{scaled}, {tx.amount}]")
|
|
51
|
+
|
|
52
|
+
# Unwrapping, YFI side
|
|
53
|
+
elif tx.from_address == WOOFY and tx.symbol == "YFI":
|
|
54
|
+
# Check for WOOFY transfer
|
|
55
|
+
for transfer in tx.get_events("Transfer"):
|
|
56
|
+
if transfer.address != WOOFY:
|
|
57
|
+
continue
|
|
58
|
+
sender, receiver, amount = transfer.values()
|
|
59
|
+
if tx.to_address == sender and receiver == ZERO_ADDRESS:
|
|
60
|
+
scaled = round(Decimal(amount) / YFI_SCALE, 12)
|
|
61
|
+
rounded = round(tx.amount, 12)
|
|
62
|
+
if scaled == rounded:
|
|
63
|
+
return True
|
|
64
|
+
print(f"woofy unwrapping yfi side amount no match: [{scaled}, {rounded}]")
|
|
65
|
+
|
|
66
|
+
# Unwrapping, WOOFY side
|
|
67
|
+
elif tx.to_address == ZERO_ADDRESS and tx.symbol == "WOOFY":
|
|
68
|
+
# Check for YFI transfer
|
|
69
|
+
for transfer in tx.get_events("Transfer"):
|
|
70
|
+
if transfer.address != YFI:
|
|
71
|
+
continue
|
|
72
|
+
sender, receiver, amount = transfer.values()
|
|
73
|
+
if sender == WOOFY and tx.from_address == receiver:
|
|
74
|
+
# TODO remove this rounding once sqlite is replaced with postgres
|
|
75
|
+
scaled = round(Decimal(amount) / WOOFY_SCALE, 7)
|
|
76
|
+
rounded = round(tx.amount, 7)
|
|
77
|
+
if scaled == rounded:
|
|
78
|
+
return True
|
|
79
|
+
print(f"woofy unwrapping woofy side amount no match: [{scaled}, {rounded}]")
|
|
80
|
+
return False
|
|
Binary file
|