yearn-treasury 0.0.26__cp312-cp312-macosx_11_0_arm64.whl → 0.0.45__cp312-cp312-macosx_11_0_arm64.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 (80) hide show
  1. yearn_treasury/__init__.py +2 -0
  2. yearn_treasury/_db.cpython-312-darwin.so +0 -0
  3. yearn_treasury/_db.py +8 -0
  4. yearn_treasury/_ens.cpython-312-darwin.so +0 -0
  5. yearn_treasury/_logging.cpython-312-darwin.so +0 -0
  6. yearn_treasury/_logging.py +43 -0
  7. yearn_treasury/address_labels.yaml +6 -0
  8. yearn_treasury/budget/__init__.cpython-312-darwin.so +0 -0
  9. yearn_treasury/budget/__init__.py +2 -2
  10. yearn_treasury/budget/_request.cpython-312-darwin.so +0 -0
  11. yearn_treasury/budget/_requests.cpython-312-darwin.so +0 -0
  12. yearn_treasury/budget/_requests.py +34 -15
  13. yearn_treasury/constants.py +7 -1
  14. yearn_treasury/main.py +50 -20
  15. yearn_treasury/rules/__init__.py +14 -0
  16. yearn_treasury/rules/constants.cpython-312-darwin.so +0 -0
  17. yearn_treasury/rules/cost_of_revenue/gas.cpython-312-darwin.so +0 -0
  18. yearn_treasury/rules/expense/__init__.cpython-312-darwin.so +0 -0
  19. yearn_treasury/rules/expense/general.cpython-312-darwin.so +0 -0
  20. yearn_treasury/rules/expense/infrastructure.cpython-312-darwin.so +0 -0
  21. yearn_treasury/rules/expense/people.cpython-312-darwin.so +0 -0
  22. yearn_treasury/rules/expense/security.cpython-312-darwin.so +0 -0
  23. yearn_treasury/rules/ignore/general.cpython-312-darwin.so +0 -0
  24. yearn_treasury/rules/ignore/maker.py +14 -16
  25. yearn_treasury/rules/ignore/passthru.py +58 -10
  26. yearn_treasury/rules/ignore/swaps/__init__.py +1 -0
  27. yearn_treasury/rules/ignore/swaps/aave.py +17 -10
  28. yearn_treasury/rules/ignore/swaps/auctions.cpython-312-darwin.so +0 -0
  29. yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
  30. yearn_treasury/rules/ignore/swaps/compound.py +32 -22
  31. yearn_treasury/rules/ignore/swaps/conversion_factory.cpython-312-darwin.so +0 -0
  32. yearn_treasury/rules/ignore/swaps/cowswap.py +16 -13
  33. yearn_treasury/rules/ignore/swaps/curve.py +46 -15
  34. yearn_treasury/rules/ignore/swaps/gearbox.cpython-312-darwin.so +0 -0
  35. yearn_treasury/rules/ignore/swaps/iearn.cpython-312-darwin.so +0 -0
  36. yearn_treasury/rules/ignore/swaps/otc.cpython-312-darwin.so +0 -0
  37. yearn_treasury/rules/ignore/swaps/pooltogether.cpython-312-darwin.so +0 -0
  38. yearn_treasury/rules/ignore/swaps/synthetix.cpython-312-darwin.so +0 -0
  39. yearn_treasury/rules/ignore/swaps/uniswap.py +26 -6
  40. yearn_treasury/rules/ignore/swaps/unwrapper.cpython-312-darwin.so +0 -0
  41. yearn_treasury/rules/ignore/swaps/vaults.cpython-312-darwin.so +0 -0
  42. yearn_treasury/rules/ignore/swaps/vaults.py +5 -4
  43. yearn_treasury/rules/ignore/swaps/woofy.cpython-312-darwin.so +0 -0
  44. yearn_treasury/rules/ignore/swaps/woofy.py +23 -24
  45. yearn_treasury/rules/ignore/swaps/yfi.cpython-312-darwin.so +0 -0
  46. yearn_treasury/rules/ignore/swaps/yfi.py +18 -7
  47. yearn_treasury/rules/ignore/swaps/yla.cpython-312-darwin.so +0 -0
  48. yearn_treasury/rules/ignore/unit.cpython-312-darwin.so +0 -0
  49. yearn_treasury/rules/ignore/weth.cpython-312-darwin.so +0 -0
  50. yearn_treasury/rules/ignore/ygov.cpython-312-darwin.so +0 -0
  51. yearn_treasury/rules/other_expense/__init__.cpython-312-darwin.so +0 -0
  52. yearn_treasury/rules/other_expense/boost.cpython-312-darwin.so +0 -0
  53. yearn_treasury/rules/other_expense/bugs.cpython-312-darwin.so +0 -0
  54. yearn_treasury/rules/other_expense/donations.cpython-312-darwin.so +0 -0
  55. yearn_treasury/rules/other_expense/dyfi.cpython-312-darwin.so +0 -0
  56. yearn_treasury/rules/other_expense/events.cpython-312-darwin.so +0 -0
  57. yearn_treasury/rules/other_expense/misc.cpython-312-darwin.so +0 -0
  58. yearn_treasury/rules/other_expense/revshare.cpython-312-darwin.so +0 -0
  59. yearn_treasury/rules/other_income/__init__.cpython-312-darwin.so +0 -0
  60. yearn_treasury/rules/other_income/airdrops.cpython-312-darwin.so +0 -0
  61. yearn_treasury/rules/other_income/misc.cpython-312-darwin.so +0 -0
  62. yearn_treasury/rules/other_income/misc.py +1 -1
  63. yearn_treasury/rules/revenue/bribes.cpython-312-darwin.so +0 -0
  64. yearn_treasury/rules/revenue/farming.cpython-312-darwin.so +0 -0
  65. yearn_treasury/rules/revenue/keepcoins.cpython-312-darwin.so +0 -0
  66. yearn_treasury/rules/revenue/seasolver.cpython-312-darwin.so +0 -0
  67. yearn_treasury/rules/revenue/vaults.py +1 -1
  68. yearn_treasury/rules/revenue/yteams.cpython-312-darwin.so +0 -0
  69. yearn_treasury/shitcoins.py +33 -3
  70. yearn_treasury/vaults.cpython-312-darwin.so +0 -0
  71. yearn_treasury/vaults.py +3 -4
  72. yearn_treasury/yteams.py +208 -0
  73. {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/METADATA +3 -3
  74. {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/RECORD +78 -71
  75. yearn_treasury-0.0.45.dist-info/top_level.txt +2 -0
  76. yearn_treasury__mypyc.cpython-312-darwin.so +0 -0
  77. e3e435302880fbc14075__mypyc.cpython-312-darwin.so +0 -0
  78. yearn_treasury-0.0.26.dist-info/top_level.txt +0 -2
  79. {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/WHEEL +0 -0
  80. {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/entry_points.txt +0 -0
@@ -2,9 +2,11 @@ import warnings
2
2
 
3
3
  from yearn_treasury import budget
4
4
  from yearn_treasury._db import prepare_db
5
+ from yearn_treasury._logging import setup_eth_portfolio_logging
5
6
 
6
7
 
7
8
  prepare_db()
9
+ setup_eth_portfolio_logging()
8
10
 
9
11
 
10
12
  warnings.filterwarnings(
Binary file
yearn_treasury/_db.py CHANGED
@@ -20,6 +20,14 @@ from yearn_treasury import constants
20
20
 
21
21
 
22
22
  def prepare_db() -> None:
23
+ """
24
+ Set up address nicknames in the Yearn Treasury database.
25
+
26
+ Maps key Yearn Treasury addresses to human-readable labels for improved
27
+ clarity in analytics and reporting. This function is typically called
28
+ during database preparation to ensure wallet addresses are labeled
29
+ within the DAO Treasury database entity system.
30
+ """
23
31
  chad = {Network.Mainnet: "y", Network.Fantom: "f"}[CHAINID] # type: ignore [index]
24
32
 
25
33
  labels = {
Binary file
@@ -0,0 +1,43 @@
1
+ # mypy: disable-error-code="list-item"
2
+ """
3
+ Error log suppression utilities for the Yearn Treasury exporter.
4
+
5
+ This module suppresses noisy or irrelevant eth-portfolio error logs for specific
6
+ token addresses that are known to be deprecated or otherwise unpricable.
7
+
8
+ To suppress logs for additional tokens, add their addresses to the
9
+ `suppress_logs_for[Network.<chain>]` mapping. The rest will be done
10
+ automatically on package import.
11
+ """
12
+
13
+ from typing import Dict, Final, List
14
+
15
+ from cchecksum import to_checksum_address
16
+ from eth_portfolio._utils import SUPPRESS_ERROR_LOGS
17
+ from eth_typing import HexAddress
18
+ from y import Network
19
+
20
+ from yearn_treasury.constants import CHAINID
21
+
22
+
23
+ suppress_logs_for: Final[Dict[Network, List[HexAddress]]] = {
24
+ Network.Mainnet: [
25
+ "0xBF7AA989192b020a8d3e1C65a558e123834325cA", # unpriceable yvWBTC - This vault had a bug and does not have a pricePerShare
26
+ "0x5aFE3855358E112B5647B952709E6165e1c1eEEe", # SAFE - This was not tradeable at the time of the first airdrops
27
+ "0x718AbE90777F5B778B52D553a5aBaa148DD0dc5D", # yvCurve-alETH - The underlying curve pool had an issue and is unpriceable
28
+ "0x3819f64f282bf135d62168C1e513280dAF905e06", # HDRN
29
+ "0x5fAa989Af96Af85384b8a938c2EdE4A7378D9875", # GAL
30
+ ],
31
+ }
32
+
33
+
34
+ def setup_eth_portfolio_logging() -> None:
35
+ """
36
+ Suppress eth-portfolio error logs for specific tokens on the current chain.
37
+
38
+ Appends token addresses from the suppress_logs_for mapping (for the current
39
+ CHAINID) to the SUPPRESS_ERROR_LOGS list, preventing error logs for these
40
+ tokens from being emitted during analytics and reporting.
41
+ """
42
+ for token in suppress_logs_for.get(CHAINID, []): # type: ignore [call-overload]
43
+ SUPPRESS_ERROR_LOGS.append(to_checksum_address(token))
@@ -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"
@@ -1,6 +1,6 @@
1
1
  from yearn_treasury.budget._request import BudgetRequest
2
- from yearn_treasury.budget._requests import approved_requests, rejected_requests, requests
2
+ from yearn_treasury.budget._requests import approved_requests, budget_requests, rejected_requests
3
3
 
4
4
  # TODO test
5
5
 
6
- __all__ = ["BudgetRequest", "requests", "approved_requests", "rejected_requests"]
6
+ __all__ = ["BudgetRequest", "budget_requests", "approved_requests", "rejected_requests"]
@@ -7,12 +7,16 @@ requests.
7
7
  """
8
8
 
9
9
  import os
10
- from requests import get
11
- from typing import Final, List
10
+ import time
11
+ import requests
12
+ from typing import Any, Dict, Final, List
12
13
 
13
14
  from yearn_treasury.budget._request import BudgetRequest
14
15
 
15
16
 
17
+ API_URL: Final = "https://api.github.com/repos/yearn/budget/issues"
18
+ """URL to fetch issues from the repo."""
19
+
16
20
  # Optionally use a GitHub personal access token for higher API rate limits.
17
21
  # TODO move this to envs file and document
18
22
  _TOKEN: Final = os.environ.get("GITHUB_TOKEN")
@@ -20,20 +24,14 @@ _HEADERS: Final = {"Authorization": f"token {_TOKEN}"} if _TOKEN else {}
20
24
 
21
25
 
22
26
  def fetch_brs() -> List[BudgetRequest]:
23
- # URL to fetch issues from the repo
24
- api_url = "https://api.github.com/repos/yearn/budget/issues"
25
27
  # Use parameters to fetch issues in all states, up to 100 per page.
26
- params = {"state": "all", "per_page": 100, "page": 1}
28
+ current_page = 1
29
+ params = {"state": "all", "per_page": 100, "page": current_page}
27
30
 
28
31
  brs = []
29
32
  retries = 0
30
33
  while True:
31
- response = get(api_url, headers=_HEADERS, params=params) # type: ignore [arg-type]
32
- if response.status_code != 200:
33
- if retries < 5:
34
- retries += 1
35
- continue
36
- raise ConnectionError(f"Failed to fetch issues: {response.status_code} {response.text}")
34
+ response = _make_get_request(params=params)
37
35
 
38
36
  data: List[dict] = response.json() # type: ignore [type-arg]
39
37
  if not data: # If the current page is empty, we are done.
@@ -67,11 +65,32 @@ def fetch_brs() -> List[BudgetRequest]:
67
65
  brs.append(br)
68
66
 
69
67
  # Move on to the next page.
70
- params["page"] += 1 # type: ignore [operator]
68
+ current_page += 1
69
+ params["page"] = current_page
71
70
 
72
71
  return brs
73
72
 
74
73
 
75
- requests = fetch_brs()
76
- approved_requests = [r for r in requests if r.is_approved()]
77
- rejected_requests = [r for r in requests if r.is_rejected()]
74
+ def _make_get_request(params: Dict[str, Any]) -> Any:
75
+ retries = 0
76
+ while True:
77
+ try:
78
+ response = requests.get(API_URL, headers=_HEADERS, params=params)
79
+ response.raise_for_status()
80
+ return response
81
+ except requests.HTTPError as e:
82
+ if "rate limit exceeded" in str(e):
83
+ print("Github API rate limited...")
84
+ elif retries < 5:
85
+ print(e)
86
+ else:
87
+ raise ConnectionError(
88
+ f"Failed to fetch issues: {response.status_code} {response.text}"
89
+ ) from e
90
+ retries += 1
91
+ time.sleep(5 * (retries + 1))
92
+
93
+
94
+ budget_requests: Final = fetch_brs()
95
+ approved_requests: Final = [r for r in budget_requests if r.is_approved()]
96
+ rejected_requests: Final = [r for r in budget_requests if r.is_rejected()]
@@ -30,6 +30,10 @@ YCHAD_MULTISIGS: Final = {
30
30
  Network.Base: "0xbfAABa9F56A39B814281D68d2Ad949e88D06b02E",
31
31
  }
32
32
 
33
+ YSWAP_MULTISIGS: Final = {
34
+ Network.Mainnet: "0x7d2aB9CA511EBD6F03971Fb417d3492aA82513f0",
35
+ }
36
+
33
37
 
34
38
  if CHAINID not in TREASURY_MULTISIGS or CHAINID not in YCHAD_MULTISIGS:
35
39
  raise RuntimeError(f"{Network(CHAINID)} is not supported")
@@ -39,6 +43,8 @@ TREASURY_MULTISIG: Final = convert.to_address(TREASURY_MULTISIGS[CHAINID]) # ty
39
43
 
40
44
  YCHAD_MULTISIG: Final = convert.to_address(YCHAD_MULTISIGS[CHAINID]) # type: ignore [index]
41
45
 
46
+ __yswap_multisig = YSWAP_MULTISIGS.get(CHAINID) # type: ignore [call-overload]
47
+ YSWAP_MULTISIG: Final = convert.to_address(__yswap_multisig) if __yswap_multisig else None
42
48
 
43
49
  _TREASURY_WALLETS: Final = {
44
50
  Network.Mainnet: {
@@ -46,7 +52,7 @@ _TREASURY_WALLETS: Final = {
46
52
  YCHAD_MULTISIG,
47
53
  "0xb99a40fcE04cb740EB79fC04976CA15aF69AaaaE", # Yearn Treasury V1
48
54
  "0x5f0845101857d2A91627478e302357860b1598a1", # Yearn KP3R Wallet
49
- "0x7d2aB9CA511EBD6F03971Fb417d3492aA82513f0", # ySwap Multisig
55
+ YSWAP_MULTISIG,
50
56
  "0x2C01B4AD51a67E2d8F02208F54dF9aC4c0B778B6", # yMechs Multisig
51
57
  "0xE376e8e8E3B0793CD61C6F1283bA18548b726C2e", # Fee Reimbursement Stash
52
58
  "0xC001d00d425Fa92C4F840baA8f1e0c27c4297a0B", # New token dumping wallet
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")
@@ -39,6 +48,12 @@ run_parser.add_argument(
39
48
  help="The time interval between datapoints. Default: 12h",
40
49
  default="12h",
41
50
  )
51
+ run_parser.add_argument(
52
+ "--concurrency",
53
+ type=int,
54
+ help="The max number of historical blocks to export concurrently. default: 30",
55
+ default=30,
56
+ )
42
57
  run_parser.add_argument(
43
58
  "--daemon",
44
59
  action="store_true",
@@ -77,23 +92,19 @@ BROWNIE_NETWORK = os.environ["BROWNIE_NETWORK_ID"]
77
92
  # TODO: run forever arg
78
93
  def main() -> None:
79
94
  """
80
- 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.
81
96
 
82
97
  This function is registered as a console script entrypoint under
83
- ``yearn-treasury`` and delegates execution to Brownie's script runner.
84
-
85
- Steps:
98
+ ``yearn-treasury``. It performs the following steps:
86
99
  1. Reads the ``BROWNIE_NETWORK_ID`` environment variable (populated from
87
100
  the ``--network`` option or existing env var).
88
- 2. Connects to that Brownie network.
89
- 3. Patches the global SHITCOINS mapping with local tokens.
90
- 4. Constructs a frozen Args subclass to pass CLI parameters to
91
- :func:`export_balances`.
92
- 5. Exports ports for external services into environment variables.
93
- 6. Runs :func:`eth_portfolio_scripts.balances.export_balances` under asyncio.
94
-
95
- Raises:
96
- 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.
97
108
  """
98
109
  import dao_treasury.db
99
110
  import eth_portfolio
@@ -109,7 +120,10 @@ def main() -> None:
109
120
  @final
110
121
  class Args(constants.Args):
111
122
  """
112
- 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.
113
127
  """
114
128
 
115
129
  network: Final[str] = args.network
@@ -118,6 +132,9 @@ def main() -> None:
118
132
  interval: Final[str] = args.interval
119
133
  """Time interval between snapshots."""
120
134
 
135
+ concurrency: Final[int] = args.concurrency
136
+ """The max number of historical blocks to export concurrently."""
137
+
121
138
  grafana_port: Final[int] = args.grafana_port
122
139
  """Grafana port."""
123
140
 
@@ -140,7 +157,20 @@ def main() -> None:
140
157
  os.environ["DAO_TREASURY_RENDERER_PORT"] = str(Args.renderer_port)
141
158
  os.environ["VICTORIA_PORT"] = str(Args.victoria_port)
142
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
+
143
173
  # Start the balance export routine
144
- asyncio.get_event_loop().run_until_complete(dao_treasury.main.export(Args))
174
+ asyncio.get_event_loop().run_until_complete(yearn_wrapper())
145
175
 
146
176
  rules # I just put this here so the import isn't flagged as unused
@@ -1,6 +1,20 @@
1
+ import os
2
+
3
+ __ALRU_ENV_NAME = "ASYNC_LRU_ALLOW_SYNC"
4
+ __ALRU_ENV_VAL = os.environ.get(__ALRU_ENV_NAME)
5
+ os.environ[__ALRU_ENV_NAME] = "1"
6
+
1
7
  from yearn_treasury.rules.cost_of_revenue import *
2
8
  from yearn_treasury.rules.expense import *
3
9
  from yearn_treasury.rules.ignore import *
4
10
  from yearn_treasury.rules.other_expense import *
5
11
  from yearn_treasury.rules.other_income import *
6
12
  from yearn_treasury.rules.revenue import *
13
+
14
+ if __ALRU_ENV_VAL is None:
15
+ os.environ.pop(__ALRU_ENV_NAME)
16
+ else:
17
+ os.environ[__ALRU_ENV_NAME] = __ALRU_ENV_VAL
18
+
19
+ del __ALRU_ENV_NAME
20
+ del __ALRU_ENV_VAL
@@ -40,11 +40,11 @@ def is_yfi_cdp_deposit(tx: TreasuryTx) -> bool:
40
40
  for event in tx.get_events("slip"):
41
41
  if all(arg in event for arg in DEPOSIT_EVENT_ARGS):
42
42
  # TODO: remove this rounding once we move to postgres
43
- if Decimal(event["wad"]) / 10**18 == tx.amount:
43
+ scaled = round(Decimal(event["wad"]) / 10**18, 12)
44
+ rounded = round(tx.amount, 12)
45
+ if scaled == rounded:
44
46
  return True
45
- print(
46
- f"yfi cdp deposit amount no match [{Decimal(event['wad']) / 10**18}, {tx.amount}"
47
- )
47
+ print(f"yfi cdp deposit amount no match [{scaled}, {rounded}]")
48
48
  return False
49
49
 
50
50
 
@@ -56,11 +56,11 @@ def is_yfi_cdp_withdrawal(tx: TreasuryTx) -> bool:
56
56
  for event in tx.get_events("flux"):
57
57
  if all(arg in event for arg in WITHDRAWAL_EVENT_ARGS):
58
58
  # TODO: remove this rounding once we move to postgres
59
- if Decimal(event["wad"]) / 10**18 == tx.amount:
59
+ scaled = round(Decimal(event["wad"]) / 10**18, 12)
60
+ rounded = round(tx.amount, 12)
61
+ if scaled == rounded:
60
62
  return True
61
- print(
62
- f"yfi cdp withdrawal amount no match [{Decimal(event['wad']) / 10**18}, {tx.amount}"
63
- )
63
+ print(f"yfi cdp withdrawal amount no match [{scaled}, {rounded}]")
64
64
  return False
65
65
 
66
66
 
@@ -71,11 +71,10 @@ def is_usdc_cdp_deposit(tx: TreasuryTx) -> bool:
71
71
  ):
72
72
  for event in tx.get_events("slip"):
73
73
  if all(arg in event for arg in DEPOSIT_EVENT_ARGS):
74
- if Decimal(event["wad"]) / 10**18 == tx.amount:
74
+ scaled = Decimal(event["wad"]) / 10**18
75
+ if scaled == tx.amount:
75
76
  return True
76
- print(
77
- f"usdc cdp deposit amount no match [{Decimal(event['wad']) / 10**18}, {tx.amount}"
78
- )
77
+ print(f"usdc cdp deposit amount no match [{scaled}, {tx.amount}]")
79
78
  return False
80
79
 
81
80
 
@@ -86,9 +85,8 @@ def is_usdc_cdp_withdrawal(tx: TreasuryTx) -> bool:
86
85
  ):
87
86
  for event in tx.get_events("flux"):
88
87
  if all(arg in event for arg in WITHDRAWAL_EVENT_ARGS):
89
- if Decimal(event["wad"]) / 10**18 == tx.amount:
88
+ scaled = Decimal(event["wad"]) / 10**18
89
+ if scaled == tx.amount:
90
90
  return True
91
- print(
92
- f"usdc cdp withdrawal amount no match [{Decimal(event['wad']) / 10**18}, {tx.amount}"
93
- )
91
+ print(f"usdc cdp withdrawal amount no match [{scaled}, {tx.amount}]")
94
92
  return False
@@ -2,9 +2,10 @@ from decimal import Decimal
2
2
  from typing import Final, Tuple
3
3
 
4
4
  from dao_treasury import TreasuryTx, TreasuryWallet, ignore
5
+ from eth_typing import BlockNumber, ChecksumAddress
5
6
  from y import Network
6
7
 
7
- from yearn_treasury.constants import CHAINID, ZERO_ADDRESS
8
+ from yearn_treasury.constants import CHAINID, YSWAP_MULTISIG, ZERO_ADDRESS
8
9
 
9
10
 
10
11
  passthru: Final = ignore("Pass-Thru")
@@ -22,12 +23,11 @@ def is_sent_to_dinoswap(tx: TreasuryTx) -> bool:
22
23
  @passthru("Bribes for yCRV", Network.Mainnet)
23
24
  def is_ycrv(tx: TreasuryTx) -> bool:
24
25
  """These are routed thru cowswap with dai as the purchase token."""
25
- yswaps = "0x7d2aB9CA511EBD6F03971Fb417d3492aA82513f0"
26
26
  ymechs = "0x2C01B4AD51a67E2d8F02208F54dF9aC4c0B778B6"
27
27
 
28
28
  from_address = tx.from_address
29
29
  symbol = tx.symbol
30
- if (from_address == yswaps and symbol == "DAI") or (
30
+ if (from_address == YSWAP_MULTISIG and symbol == "DAI") or (
31
31
  from_address == ymechs and symbol == "3Crv"
32
32
  ):
33
33
  if tx.to_address == cowswap_router:
@@ -41,13 +41,12 @@ def is_ycrv(tx: TreasuryTx) -> bool:
41
41
  fee_amount,
42
42
  order_uid,
43
43
  ) = trade.values()
44
- if (
45
- tx.from_address == owner
46
- and tx.token == sell_token
47
- and buy_token == ycrv
48
- and Decimal(sell_amount) / 10**18 == tx.amount
49
- ):
50
- return True
44
+ if tx.from_address == owner and tx.token == sell_token and buy_token == ycrv:
45
+ scaled = Decimal(sell_amount) / 10**18
46
+ # TODO: remove this rounding when we implement postgres
47
+ if round(scaled, 11) == round(tx.amount, 11):
48
+ return True
49
+ print(f"bribes for ycrv amount no match: [{scaled}, {tx.amount}]")
51
50
 
52
51
  elif tx.hash in {
53
52
  # one off exception case to correct accounting mix-up
@@ -260,3 +259,52 @@ def is_misc_passthru_fantom(tx: TreasuryTx) -> bool:
260
259
  if not is_deposit:
261
260
  return True
262
261
  return False
262
+
263
+
264
+ @passthru("yvBoost INCOMPLETE", Network.Mainnet)
265
+ def is_buying_yvboost(tx: TreasuryTx) -> bool:
266
+ """Bought back yvBoost is unwrapped and sent back to vault holders."""
267
+ symbol = tx.symbol
268
+ block: BlockNumber = tx.block # type: ignore [assignment]
269
+ from_address: ChecksumAddress = tx.from_address.address # type: ignore [union-attr, assignment]
270
+ to_address: ChecksumAddress = tx.to_address.address # type: ignore [union-attr, assignment]
271
+ if (
272
+ symbol == "SPELL"
273
+ and TreasuryWallet.check_membership(from_address, block)
274
+ and to_address == cowswap_router
275
+ ):
276
+ return True
277
+
278
+ elif (
279
+ symbol == "yveCRV-DAO"
280
+ and TreasuryWallet.check_membership(from_address, block)
281
+ and to_address
282
+ in (
283
+ "0xd7240B32d24B814fE52946cD44d94a2e3532E63d",
284
+ "0x7fe508eE30316e3261079e2C81f4451E0445103b",
285
+ )
286
+ ):
287
+ return True
288
+
289
+ elif (
290
+ symbol == "3Crv"
291
+ and from_address == "0xd7240B32d24B814fE52946cD44d94a2e3532E63d"
292
+ and TreasuryWallet.check_membership(to_address, block)
293
+ ):
294
+ return True
295
+
296
+ # SPELL bribe handling
297
+ elif symbol == "SPELL":
298
+ if tx.to_nickname in ("Abracadabra Treasury", "Contract: BribeSplitter"):
299
+ return True
300
+
301
+ return tx in (
302
+ "0x9eabdf110efbfb44aab7a50eb4fe187f68deae7c8f28d78753c355029f2658d3",
303
+ "0x5a80f5ff90fc6f4f4597290b2432adbb62ab4154ead68b515accdf19b01c1086",
304
+ "0x848b4d629e137ad8d8eefe5db40eab895c9959b9c210d0ae0fef16a04bfaaee1",
305
+ "0x896663aa9e2633b5d152028bdf84d7f4b1137dd27a8e61daca3863db16bebc4f",
306
+ "0xd8aa1e5d093a89515530b7267a9fd216b97fddb6478b3027b2f5c1d53070cd5f",
307
+ "0x169aab84b408fce76e0b776ebf412c796240300c5610f0263d5c09d0d3f1b062",
308
+ "0xe6fefbf061f4489cd967cdff6aa8aca616f0c709e08c3696f12b0027e9e166c9",
309
+ "0x10be8a3345660f3c51b695e8716f758b1a91628bd612093784f0516a604f79c1",
310
+ )
@@ -7,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 *
@@ -39,24 +39,31 @@ async def is_aave_withdrawal(tx: TreasuryTx) -> bool:
39
39
  ):
40
40
  token = tx.token
41
41
  if hasattr(token.contract, "underlyingAssetAddress"):
42
- for event in tx.get_events("RedeemUnderlying"):
42
+ for event in await tx.get_events("RedeemUnderlying", sync=False):
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
- for event in tx.get_events("RedeemUnderlying"):
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
58
+ for event in await tx.get_events("RedeemUnderlying", sync=False):
59
+ if token == event["_reserve"] and to_address == event["_user"]:
60
+ # TODO get rid of this rounding when we migrate the db to postgres
61
+ event_amount = round(token.scale_value(event["_amount"]), 11)
62
+ if event_amount == round(tx.amount, 11):
63
+ return True
64
+ print(
65
+ f"Aave Withdrawal underlying side does not match: {round(tx.amount, 14)} {event_amount}"
66
+ )
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