yearn-treasury 0.0.39__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.

Files changed (72) hide show
  1. yearn_treasury/_db.cp310-win32.pyd +0 -0
  2. yearn_treasury/_ens.cp310-win32.pyd +0 -0
  3. yearn_treasury/_logging.cp310-win32.pyd +0 -0
  4. yearn_treasury/_logging.py +2 -0
  5. yearn_treasury/address_labels.yaml +6 -0
  6. yearn_treasury/budget/__init__.cp310-win32.pyd +0 -0
  7. yearn_treasury/budget/_request.cp310-win32.pyd +0 -0
  8. yearn_treasury/budget/_requests.cp310-win32.pyd +0 -0
  9. yearn_treasury/main.py +43 -22
  10. yearn_treasury/rules/constants.cp310-win32.pyd +0 -0
  11. yearn_treasury/rules/cost_of_revenue/gas.cp310-win32.pyd +0 -0
  12. yearn_treasury/rules/expense/__init__.cp310-win32.pyd +0 -0
  13. yearn_treasury/rules/expense/general.cp310-win32.pyd +0 -0
  14. yearn_treasury/rules/expense/infrastructure.cp310-win32.pyd +0 -0
  15. yearn_treasury/rules/expense/people.cp310-win32.pyd +0 -0
  16. yearn_treasury/rules/expense/security.cp310-win32.pyd +0 -0
  17. yearn_treasury/rules/ignore/general.cp310-win32.pyd +0 -0
  18. yearn_treasury/rules/ignore/swaps/__init__.py +1 -0
  19. yearn_treasury/rules/ignore/swaps/aave.py +15 -8
  20. yearn_treasury/rules/ignore/swaps/auctions.cp310-win32.pyd +0 -0
  21. yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
  22. yearn_treasury/rules/ignore/swaps/compound.py +28 -18
  23. yearn_treasury/rules/ignore/swaps/conversion_factory.cp310-win32.pyd +0 -0
  24. yearn_treasury/rules/ignore/swaps/cowswap.py +16 -13
  25. yearn_treasury/rules/ignore/swaps/curve.py +41 -13
  26. yearn_treasury/rules/ignore/swaps/gearbox.cp310-win32.pyd +0 -0
  27. yearn_treasury/rules/ignore/swaps/iearn.cp310-win32.pyd +0 -0
  28. yearn_treasury/rules/ignore/swaps/iearn.py +4 -4
  29. yearn_treasury/rules/ignore/swaps/otc.cp310-win32.pyd +0 -0
  30. yearn_treasury/rules/ignore/swaps/pooltogether.cp310-win32.pyd +0 -0
  31. yearn_treasury/rules/ignore/swaps/synthetix.cp310-win32.pyd +0 -0
  32. yearn_treasury/rules/ignore/swaps/uniswap.py +26 -6
  33. yearn_treasury/rules/ignore/swaps/unwrapper.cp310-win32.pyd +0 -0
  34. yearn_treasury/rules/ignore/swaps/vaults.cp310-win32.pyd +0 -0
  35. yearn_treasury/rules/ignore/swaps/vaults.py +8 -7
  36. yearn_treasury/rules/ignore/swaps/woofy.cp310-win32.pyd +0 -0
  37. yearn_treasury/rules/ignore/swaps/yfi.cp310-win32.pyd +0 -0
  38. yearn_treasury/rules/ignore/swaps/yfi.py +2 -1
  39. yearn_treasury/rules/ignore/swaps/yla.cp310-win32.pyd +0 -0
  40. yearn_treasury/rules/ignore/unit.cp310-win32.pyd +0 -0
  41. yearn_treasury/rules/ignore/weth.cp310-win32.pyd +0 -0
  42. yearn_treasury/rules/ignore/ygov.cp310-win32.pyd +0 -0
  43. yearn_treasury/rules/other_expense/__init__.cp310-win32.pyd +0 -0
  44. yearn_treasury/rules/other_expense/boost.cp310-win32.pyd +0 -0
  45. yearn_treasury/rules/other_expense/bugs.cp310-win32.pyd +0 -0
  46. yearn_treasury/rules/other_expense/donations.cp310-win32.pyd +0 -0
  47. yearn_treasury/rules/other_expense/dyfi.cp310-win32.pyd +0 -0
  48. yearn_treasury/rules/other_expense/events.cp310-win32.pyd +0 -0
  49. yearn_treasury/rules/other_expense/misc.cp310-win32.pyd +0 -0
  50. yearn_treasury/rules/other_expense/revshare.cp310-win32.pyd +0 -0
  51. yearn_treasury/rules/other_income/__init__.cp310-win32.pyd +0 -0
  52. yearn_treasury/rules/other_income/airdrops.cp310-win32.pyd +0 -0
  53. yearn_treasury/rules/other_income/misc.cp310-win32.pyd +0 -0
  54. yearn_treasury/rules/other_income/misc.py +1 -1
  55. yearn_treasury/rules/revenue/bribes.cp310-win32.pyd +0 -0
  56. yearn_treasury/rules/revenue/farming.cp310-win32.pyd +0 -0
  57. yearn_treasury/rules/revenue/keepcoins.cp310-win32.pyd +0 -0
  58. yearn_treasury/rules/revenue/seasolver.cp310-win32.pyd +0 -0
  59. yearn_treasury/rules/revenue/vaults.py +13 -9
  60. yearn_treasury/rules/revenue/yteams.cp310-win32.pyd +0 -0
  61. yearn_treasury/shitcoins.py +7 -0
  62. yearn_treasury/vaults.cp310-win32.pyd +0 -0
  63. yearn_treasury/vaults.py +3 -4
  64. yearn_treasury/yteams.py +208 -0
  65. {yearn_treasury-0.0.39.dist-info → yearn_treasury-0.0.46.dist-info}/METADATA +2 -3
  66. {yearn_treasury-0.0.39.dist-info → yearn_treasury-0.0.46.dist-info}/RECORD +70 -65
  67. yearn_treasury-0.0.46.dist-info/top_level.txt +2 -0
  68. yearn_treasury__mypyc.cp310-win32.pyd +0 -0
  69. 4536608332a31e0815c4__mypyc.cp310-win32.pyd +0 -0
  70. yearn_treasury-0.0.39.dist-info/top_level.txt +0 -2
  71. {yearn_treasury-0.0.39.dist-info → yearn_treasury-0.0.46.dist-info}/WHEEL +0 -0
  72. {yearn_treasury-0.0.39.dist-info → yearn_treasury-0.0.46.dist-info}/entry_points.txt +0 -0
Binary file
Binary file
Binary file
@@ -25,6 +25,8 @@ suppress_logs_for: Final[Dict[Network, List[HexAddress]]] = {
25
25
  "0xBF7AA989192b020a8d3e1C65a558e123834325cA", # unpriceable yvWBTC - This vault had a bug and does not have a pricePerShare
26
26
  "0x5aFE3855358E112B5647B952709E6165e1c1eEEe", # SAFE - This was not tradeable at the time of the first airdrops
27
27
  "0x718AbE90777F5B778B52D553a5aBaa148DD0dc5D", # yvCurve-alETH - The underlying curve pool had an issue and is unpriceable
28
+ "0x3819f64f282bf135d62168C1e513280dAF905e06", # HDRN
29
+ "0x5fAa989Af96Af85384b8a938c2EdE4A7378D9875", # GAL
28
30
  ],
29
31
  }
30
32
 
@@ -28,6 +28,12 @@
28
28
  - "0xC564EE9f21Ed8A2d8E7e76c085740d5e4c5FaFbE"
29
29
  yLockers Multisig:
30
30
  - "0x4444AAAACDBa5580282365e25b16309Bd770ce4a"
31
+ yRoboTreasury Treasury Contract:
32
+ - "0xEf77cc176c748d291EfB6CdC982c5744fC7211c8"
33
+ yRoboTreasury Stables Reserve:
34
+ - "0x278374fFb10B7D16E7633444c13e6E565EA57c28"
35
+ yearn.fi Dutch Auctions:
36
+ - "0x861fE45742f70054917B65bE18904662bD0dBd30"
31
37
  250:
32
38
  Yearn Strategist Multisig:
33
39
  - "0x72a34AbafAB09b15E7191822A679f28E067C4a16"
yearn_treasury/main.py CHANGED
@@ -2,9 +2,9 @@
2
2
  Command-line interface for the Yearn Treasury exporter.
3
3
 
4
4
  This module provides the `yearn-treasury` CLI, which connects to a Brownie network
5
- based on the `--network` option (or the `BROWNIE_NETWORK_ID` environment variable),
6
- periodically snapshots treasury balances via :func:`export_balances`, and
7
- pushes metrics to Victoria Metrics.
5
+ (based on the `--network` option or the `BROWNIE_NETWORK_ID` environment variable),
6
+ periodically snapshots treasury metrics, and pushes them to Victoria Metrics.
7
+ It also launches Yearn Treasury's Grafana dashboard and an optional renderer for visual reports.
8
8
 
9
9
  Example:
10
10
  Run export every 12 hours on mainnet:
@@ -12,15 +12,24 @@ Example:
12
12
  .. code-block:: bash
13
13
 
14
14
  yearn-treasury run --network mainnet --interval 12h
15
+
16
+ CLI Options:
17
+ --network Brownie network identifier (default: mainnet)
18
+ --interval Time interval between datapoints (default: 12h)
19
+ --concurrency Max number of historical blocks to export concurrently (default: 30)
20
+ --daemon Run as a background daemon (currently unsupported)
21
+ --grafana-port Port for the Grafana dashboard (default: 3004)
22
+ --victoria-port Port for the Victoria metrics endpoint (default: 8430)
23
+ --start-renderer Start the Grafana renderer container for dashboard image export
24
+ --renderer-port Port for the renderer service (default: 8080)
15
25
  """
16
26
 
17
27
  import asyncio
18
28
  import os
19
29
  from argparse import ArgumentParser
20
- from pathlib import Path
21
30
  from typing import Final, final
22
31
 
23
- from eth_portfolio_scripts.balances import export_balances
32
+ from yearn_treasury import yteams
24
33
 
25
34
 
26
35
  parser = ArgumentParser(description="Treasury CLI")
@@ -42,8 +51,8 @@ run_parser.add_argument(
42
51
  run_parser.add_argument(
43
52
  "--concurrency",
44
53
  type=int,
45
- help="The max number of historical blocks to export concurrently. default: 40",
46
- default=40,
54
+ help="The max number of historical blocks to export concurrently. default: 30",
55
+ default=30,
47
56
  )
48
57
  run_parser.add_argument(
49
58
  "--daemon",
@@ -83,23 +92,19 @@ BROWNIE_NETWORK = os.environ["BROWNIE_NETWORK_ID"]
83
92
  # TODO: run forever arg
84
93
  def main() -> None:
85
94
  """
86
- Connect to the configured Brownie network and start the export loop.
95
+ Connect to the configured Brownie network, clean up the database, and start the export loop.
87
96
 
88
97
  This function is registered as a console script entrypoint under
89
- ``yearn-treasury`` and delegates execution to Brownie's script runner.
90
-
91
- Steps:
98
+ ``yearn-treasury``. It performs the following steps:
92
99
  1. Reads the ``BROWNIE_NETWORK_ID`` environment variable (populated from
93
100
  the ``--network`` option or existing env var).
94
- 2. Connects to that Brownie network.
95
- 3. Patches the global SHITCOINS mapping with local tokens.
96
- 4. Constructs a frozen Args subclass to pass CLI parameters to
97
- :func:`export_balances`.
98
- 5. Exports ports for external services into environment variables.
99
- 6. Runs :func:`eth_portfolio_scripts.balances.export_balances` under asyncio.
100
-
101
- Raises:
102
- RuntimeError: If the Brownie network cannot be determined.
101
+ 2. Connects to the specified Brownie network.
102
+ 3. Merges local SHITCOINS into eth_portfolio's config to skip tokens we don't care about.
103
+ 4. Drops any shitcoin transactions that might be in the database.
104
+ 5. Constructs an immutable Args subclass to pass CLI parameters to
105
+ :func:`dao_treasury.main.export`.
106
+ 6. Exports ports for external services into environment variables.
107
+ 7. Runs both the DAO Treasury export and Yearn team revenue/expenses calculation concurrently under asyncio.
103
108
  """
104
109
  import dao_treasury.db
105
110
  import eth_portfolio
@@ -115,7 +120,10 @@ def main() -> None:
115
120
  @final
116
121
  class Args(constants.Args):
117
122
  """
118
- Immutable container of CLI arguments for :func:`~export_balances`.
123
+ Immutable container of CLI arguments for export and dashboard/renderer configuration.
124
+
125
+ Inherits from DAO Treasury's :class:`constants.Args` and is used to pass runtime
126
+ parameters to the DAO Treasury export and related services.
119
127
  """
120
128
 
121
129
  network: Final[str] = args.network
@@ -149,7 +157,20 @@ def main() -> None:
149
157
  os.environ["DAO_TREASURY_RENDERER_PORT"] = str(Args.renderer_port)
150
158
  os.environ["VICTORIA_PORT"] = str(Args.victoria_port)
151
159
 
160
+ async def yearn_wrapper():
161
+ """
162
+ Run the DAO Treasury export and Yearn team revenue/expenses calculation concurrently.
163
+
164
+ This coroutine gathers:
165
+ - The main DAO Treasury export process (dao_treasury.main.export)
166
+ - The Yearn teams' revenue and expenses calculation (yteams.calculate_teams_revenue_expenses)
167
+ """
168
+ return await asyncio.gather(
169
+ dao_treasury.main.export(Args),
170
+ yteams.calculate_teams_revenue_expenses(),
171
+ )
172
+
152
173
  # Start the balance export routine
153
- asyncio.get_event_loop().run_until_complete(dao_treasury.main.export(Args))
174
+ asyncio.get_event_loop().run_until_complete(yearn_wrapper())
154
175
 
155
176
  rules # I just put this here so the import isn't flagged as unused
@@ -7,6 +7,7 @@ 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 *
11
12
  from yearn_treasury.rules.ignore.swaps.conversion_factory import *
12
13
  from yearn_treasury.rules.ignore.swaps.cowswap import *
@@ -43,20 +43,27 @@ async def is_aave_withdrawal(tx: TreasuryTx) -> bool:
43
43
  if (
44
44
  from_address == event["_user"]
45
45
  and await token.contract.underlyingAssetAddress == event["_reserve"]
46
- and token.scale_value(event["_amount"]) == tx.amount
47
46
  ):
48
- return True
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
+ )
49
54
 
50
55
  # Underlying side
51
56
  if TreasuryWallet.check_membership(tx.to_address.address, tx.block): # type: ignore [union-attr, arg-type]
52
57
  token = tx.token
53
58
  for event in await tx.get_events("RedeemUnderlying", sync=False):
54
- if (
55
- token == event["_reserve"]
56
- and to_address == event["_user"]
57
- and token.scale_value(event["_amount"]) == tx.amount
58
- ):
59
- return True
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
+ )
60
67
 
61
68
  # TODO: If these end up becoming more frequent, figure out sorting hueristics.
62
69
  return tx.hash == "0x36ee5631859a15f57b44e41b8590023cf6f0c7b12d28ea760e9d8f8003f4fc50"
@@ -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
@@ -15,17 +15,21 @@ async def is_compound_deposit(tx: TreasuryTx) -> bool:
15
15
  minter = event["minter"]
16
16
  minted = tx.token.scale_value(event["mintTokens"])
17
17
  # cToken side
18
- if (
19
- tx.token == tx.from_address == event.address
20
- and tx.to_address == minter
21
- and minted == tx.amount
22
- ):
23
- return True
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
- tx.to_address == event.address and tx.from_address == minter and minted == tx.amount
27
- ):
28
- return True
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
 
@@ -36,13 +40,19 @@ async def is_compound_withdrawal(tx: TreasuryTx) -> bool:
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 and redeemed == tx.amount:
40
- return True
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
- tx.to_address == redeemer
44
- and tx.from_address == event.address
45
- and redeemed == tx.amount
46
- ):
47
- return True
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
@@ -10,7 +10,7 @@ from yearn_treasury.rules.ignore.swaps import swaps
10
10
  from yearn_treasury.rules.ignore.swaps._skip_tokens import SKIP_TOKENS
11
11
 
12
12
 
13
- YSWAPS: Final = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
13
+ COWSWAP: Final = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
14
14
 
15
15
 
16
16
  @swaps("Cowswap", Network.Mainnet)
@@ -34,23 +34,26 @@ def is_cowswap_swap(tx: TreasuryTx) -> bool:
34
34
 
35
35
  for trade in tx.events["Trade"]:
36
36
  if (
37
- trade.address == YSWAPS
37
+ trade.address == COWSWAP
38
38
  and TreasuryWallet.check_membership(trade["owner"], block)
39
39
  and trade["buyToken"] not in SKIP_TOKENS
40
40
  ):
41
41
  # buy side
42
- if (
43
- token_address == trade["buyToken"]
44
- and TreasuryWallet.check_membership(tx.to_address.address, block) # type: ignore [union-attr, arg-type]
45
- and amount == token.scale_value(trade["buyAmount"])
42
+ if token_address == trade["buyToken"] and TreasuryWallet.check_membership(
43
+ tx.to_address.address, block # type: ignore [union-attr, arg-type]
46
44
  ):
47
- return True
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}")
48
50
  # sell side
49
- elif (
50
- token_address == trade["sellToken"]
51
- and tx.from_address == trade["owner"]
52
- and amount == token.scale_value(trade["sellAmount"])
53
- ):
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
54
57
  # Did Yearn actually receive the other side of the trade?
55
58
  for address in TREASURY_WALLETS:
56
59
  if TreasuryWallet.check_membership(address, block):
@@ -59,7 +62,7 @@ def is_cowswap_swap(tx: TreasuryTx) -> bool:
59
62
  for t in TreasuryTx # type: ignore [attr-defined]
60
63
  if t.hash == tx.hash
61
64
  and t.token.address.address == trade["buyToken"]
62
- and t.from_address.address == YSWAPS
65
+ and t.from_address.address == COWSWAP
63
66
  and t.to_address.address == address
64
67
  )
65
68
 
@@ -50,11 +50,19 @@ async def is_curve_deposit(tx: TreasuryTx) -> bool:
50
50
  # Tokens sent
51
51
  elif tx.to_address == event.address:
52
52
  try:
53
+ tx_amount = round(tx.amount, 8)
53
54
  for i, amount in enumerate(event["token_amounts"]):
54
- if tx.amount == tx.token.scale_value(amount):
55
+ # TODO: get rid of this rounding when we migrate to postgres
56
+ event_amount = round(tx.token.scale_value(amount), 8)
57
+ if tx_amount == event_amount:
55
58
  pool = await Contract.coroutine(event.address) # type: ignore [assignment]
56
59
  if tx.token == await _get_coin_at_index(pool, i):
57
60
  return True
61
+ return True
62
+ else:
63
+ print(
64
+ f"Curve AddLiquidity sent amount does not match: {tx_amount} {event_amount}"
65
+ )
58
66
  except EventLookupError:
59
67
  pass
60
68
 
@@ -67,10 +75,16 @@ async def is_curve_deposit(tx: TreasuryTx) -> bool:
67
75
  print(f"AddLiquidity-3crv: {event}")
68
76
  token = tx.token
69
77
  for i, amount in enumerate(event["token_amounts"]):
70
- if tx.amount == token.scale_value(amount):
78
+ event_amount = token.scale_value(amount)
79
+ # TODO: get rid of this rounding when we migrate to postgres
80
+ if round(tx.amount, 14) == round(event_amount, 14):
71
81
  pool = await Contract.coroutine(event.address) # type: ignore [assignment]
72
82
  if token == await _get_coin_at_index(pool, i):
73
83
  return True
84
+ else:
85
+ print(
86
+ f"AddLiquidity-3crv amount does not match: {round(tx.amount, 14)} {round(event_amount)}"
87
+ )
74
88
 
75
89
  # TODO: see if we can remove these with latest hueristics
76
90
  return CHAINID == Network.Mainnet and tx.hash in (
@@ -98,17 +112,24 @@ async def is_curve_withdrawal(tx: TreasuryTx) -> bool:
98
112
  def _is_curve_withdrawal_one(tx: TreasuryTx) -> bool:
99
113
  for event in tx.get_events("RemoveLiquidityOne"):
100
114
  # LP Token Side
101
- if (
102
- tx.to_address == ZERO_ADDRESS
103
- and _token_is_curvey(tx)
104
- and tx.amount == tx.token.scale_value(event["token_amount"])
105
- ):
106
- return True
115
+ if tx.to_address == ZERO_ADDRESS and _token_is_curvey(tx):
116
+ # TODO: get rid of this rounding when we migrate to postgres
117
+ event_amount = round(tx.token.scale_value(event["token_amount"]), 9)
118
+ if round(tx.amount, 9) == event_amount:
119
+ return True
120
+ print(
121
+ f"Curve withdrawal one curvey amount does not match: {round(tx.amount, 9)} {event_amount}"
122
+ )
107
123
  # Tokens rec'd
108
- elif tx.from_address == event.address and tx.amount == tx.token.scale_value(
109
- event["coin_amount"]
110
- ):
124
+ if tx.from_address != event.address:
125
+ continue
126
+ # TODO: get rid of this rounding when we migrate to postgres
127
+ event_amount = tx.token.scale_value(event["coin_amount"])
128
+ if round(tx.amount, 9) == round(event_amount, 9):
111
129
  return True
130
+ print(
131
+ f"Curve withdrawal one amount does not match: {round(tx.amount, 9)} {round(event_amount, 9)}"
132
+ )
112
133
  return False
113
134
 
114
135
 
@@ -121,21 +142,28 @@ async def _is_curve_withdrawal_multi(tx: TreasuryTx) -> bool:
121
142
  if await _is_old_style(tx, pool) or _is_new_style(tx, pool):
122
143
  return True
123
144
  print(
124
- f"unhandled curve pool: {tx} symbol={tx.symbol} name={tx.token.name} address={tx.token.address}"
145
+ f"unhandled curve pool: {tx} symbol={tx.symbol} name={tx.token.name} address={tx.token_address}"
125
146
  )
126
147
  # Tokens rec'd
127
148
  elif tx.from_address == event.address and TreasuryWallet.check_membership(
128
149
  tx.to_address.address, tx.block # type: ignore [union-attr, arg-type]
129
150
  ):
151
+ tx_amount = round(tx.amount, 7)
130
152
  try:
131
153
  for i, amount in enumerate(event["token_amounts"]):
132
- if tx.amount == tx.token.scale_value(amount):
154
+ # TODO: get rid of this rounding when we migrate to postgres
155
+ event_amount = round(tx.token.scale_value(amount), 7)
156
+ if tx_amount == event_amount:
133
157
  pool = await Contract.coroutine(event.address) # type: ignore [assignment]
134
158
  if hasattr(pool, "underlying_coins"):
135
159
  coin: ChecksumAddress = await pool.underlying_coins.coroutine(i)
136
160
  return tx.token == coin
137
161
  else:
138
162
  return tx.token == await _get_coin_at_index(pool, i)
163
+ else:
164
+ print(
165
+ f"Curve withdrawal multi amount does not match: {tx_amount} {event_amount}"
166
+ )
139
167
  except EventLookupError:
140
168
  # some other event has different keys, maybe we need to implement logic to capture these. time will tell.
141
169
  pass
@@ -1,5 +1,5 @@
1
1
  # mypy: disable-error-code=dict-item
2
- from typing import Dict, Final
2
+ from typing import Dict, Final, cast
3
3
 
4
4
  from dao_treasury import TreasuryTx
5
5
  from eth_typing import ChecksumAddress
@@ -36,8 +36,8 @@ POOL_TO_UNDERLYING: Final[Dict[ChecksumAddress, ChecksumAddress]] = {
36
36
  def is_iearn_withdrawal(tx: TreasuryTx) -> bool:
37
37
  # Vault side
38
38
  if tx.to_address == ZERO_ADDRESS:
39
- return tx.token.address.address in POOLS
39
+ return tx.token_address in POOLS
40
40
  # Token side
41
- from_address: ChecksumAddress = tx.from_address.address # type: ignore [union-attr, assignment]
42
- token_address = tx.token.address.address
41
+ from_address = cast(ChecksumAddress, tx.from_address.address)
42
+ token_address = tx.token_address
43
43
  return POOL_TO_UNDERLYING.get(from_address) == token_address
@@ -57,7 +57,7 @@ async def is_uniswap_deposit(tx: TreasuryTx) -> bool:
57
57
  if any(
58
58
  tokens[1] == transfer.address
59
59
  and tx.to_address
60
- == transfer.values()[:1]
60
+ == transfer.values()[:1] # type: ignore [index]
61
61
  == [mint["sender"], mint.address]
62
62
  for transfer in transfers
63
63
  ):
@@ -78,7 +78,7 @@ async def is_uniswap_deposit(tx: TreasuryTx) -> bool:
78
78
  if any(
79
79
  tokens[0] == transfer.address
80
80
  and tx.to_address
81
- == transfer.values()[:1]
81
+ == transfer.values()[:1] # type: ignore [index]
82
82
  == [mint["sender"], mint.address]
83
83
  for transfer in transfers
84
84
  ):
@@ -243,11 +243,21 @@ async def is_uniswap_swap(tx: TreasuryTx) -> bool:
243
243
  continue
244
244
 
245
245
  if tx.token == token0:
246
- if tx.token.scale_value(swap["amount0In"]) == tx.amount:
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):
247
249
  return True
250
+ print(
251
+ f"Uniswap sell token0 amount does not match: {round(tx.amount, 10)} {event_amount}"
252
+ )
248
253
  elif tx.token == token1:
249
- if tx.token.scale_value(swap["amount1In"]) == tx.amount:
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):
250
257
  return True
258
+ print(
259
+ f"Uniswap sell token1 amount does not match: {round(tx.amount, 10)} {event_amount}"
260
+ )
251
261
 
252
262
  # Buy side
253
263
  elif tx.from_address == swap.address and TreasuryWallet._get_instance(
@@ -262,11 +272,21 @@ async def is_uniswap_swap(tx: TreasuryTx) -> bool:
262
272
  # This will be recorded elsewhere
263
273
  continue
264
274
  if "amount0Out" in swap and tx.token == token0:
265
- if tx.token.scale_value(swap["amount0Out"]) == tx.amount:
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):
266
278
  return True
279
+ print(
280
+ f"Uniswap buy token0 amount does not match: {round(tx.amount, 9)} {event_amount}"
281
+ )
267
282
  elif "amount1Out" in swap and tx.token == token1:
268
- if tx.token.scale_value(swap["amount1Out"]) == tx.amount:
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):
269
286
  return True
287
+ print(
288
+ f"Uniswap buy token1 amount does not match: {round(tx.amount, 10)} {event_amount}"
289
+ )
270
290
  return False
271
291
 
272
292
 
@@ -1,6 +1,7 @@
1
- from typing import Final
1
+ from typing import Final, cast
2
2
 
3
3
  from dao_treasury import TreasuryTx, TreasuryWallet
4
+ from dao_treasury.db import Address
4
5
  from eth_typing import BlockNumber, ChecksumAddress
5
6
  from faster_async_lru import alru_cache
6
7
  from y import Contract, Network
@@ -51,9 +52,9 @@ async def is_v1_or_v2_vault_deposit(tx: TreasuryTx) -> bool:
51
52
 
52
53
  transfer_events = tx.events["Transfer"]
53
54
 
54
- tx_token = tx.token.address.address
55
+ tx_token = tx.token_address
55
56
 
56
- block: BlockNumber = tx.block # type: ignore [assignment]
57
+ block = BlockNumber(tx.block)
57
58
  sender: ChecksumAddress
58
59
  receiver: ChecksumAddress
59
60
  underlying_address: ChecksumAddress
@@ -147,7 +148,7 @@ def is_v3_vault_deposit(tx: TreasuryTx) -> bool:
147
148
 
148
149
  # Token side
149
150
  elif deposits := [d for d in deposits if to_address == d.address]:
150
- from_address = tx.from_address.address # type: ignore [union-attr]
151
+ from_address = cast(Address, tx.from_address).address
151
152
  for deposit in deposits:
152
153
  if from_address != deposit["sender"]:
153
154
  print("sender doesnt match")
@@ -170,7 +171,7 @@ async def _get_underlying(vault: Contract) -> ChecksumAddress:
170
171
 
171
172
  @vaults("Withdrawal")
172
173
  async def is_vault_withdrawal(tx: TreasuryTx) -> bool:
173
- to_address = tx.to_address.address # type: ignore [union-attr]
174
+ to_address = cast(Address, tx.to_address).address
174
175
  if to_address not in TREASURY_AND_ZERO:
175
176
  return False
176
177
 
@@ -185,8 +186,8 @@ async def is_vault_withdrawal(tx: TreasuryTx) -> bool:
185
186
  transfer_events = tx.events["Transfer"]
186
187
 
187
188
  token = tx.token
188
- token_address: ChecksumAddress = token.address.address # type: ignore [assignment]
189
- block: BlockNumber = tx.block # type: ignore [assignment]
189
+ token_address = cast(ChecksumAddress, token.address.address)
190
+ block = BlockNumber(tx.block)
190
191
 
191
192
  underlying: ChecksumAddress
192
193