yearn-treasury 0.0.19__cp311-cp311-macosx_11_0_arm64.whl → 0.0.45__cp311-cp311-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 (103) hide show
  1. yearn_treasury/__init__.py +2 -0
  2. yearn_treasury/_db.cpython-311-darwin.so +0 -0
  3. yearn_treasury/_db.py +21 -0
  4. yearn_treasury/_ens.cpython-311-darwin.so +0 -0
  5. yearn_treasury/_ens.py +14 -0
  6. yearn_treasury/_logging.cpython-311-darwin.so +0 -0
  7. yearn_treasury/_logging.py +43 -0
  8. yearn_treasury/address_labels.yaml +10 -0
  9. yearn_treasury/budget/__init__.cpython-311-darwin.so +0 -0
  10. yearn_treasury/budget/__init__.py +2 -2
  11. yearn_treasury/budget/_request.cpython-311-darwin.so +0 -0
  12. yearn_treasury/budget/_request.py +8 -0
  13. yearn_treasury/budget/_requests.cpython-311-darwin.so +0 -0
  14. yearn_treasury/budget/_requests.py +44 -17
  15. yearn_treasury/constants.py +17 -1
  16. yearn_treasury/main.py +54 -20
  17. yearn_treasury/rules/__init__.py +14 -0
  18. yearn_treasury/rules/constants.cpython-311-darwin.so +0 -0
  19. yearn_treasury/rules/cost_of_revenue/gas.cpython-311-darwin.so +0 -0
  20. yearn_treasury/rules/cost_of_revenue/gas.py +9 -1
  21. yearn_treasury/rules/expense/__init__.cpython-311-darwin.so +0 -0
  22. yearn_treasury/rules/expense/general.cpython-311-darwin.so +0 -0
  23. yearn_treasury/rules/expense/infrastructure.cpython-311-darwin.so +0 -0
  24. yearn_treasury/rules/expense/infrastructure.py +7 -0
  25. yearn_treasury/rules/expense/people.cpython-311-darwin.so +0 -0
  26. yearn_treasury/rules/expense/people.py +43 -0
  27. yearn_treasury/rules/expense/security.cpython-311-darwin.so +0 -0
  28. yearn_treasury/rules/expense/security.py +17 -0
  29. yearn_treasury/rules/ignore/__init__.py +1 -0
  30. yearn_treasury/rules/ignore/general.cpython-311-darwin.so +0 -0
  31. yearn_treasury/rules/ignore/maker.py +40 -28
  32. yearn_treasury/rules/ignore/passthru.py +168 -11
  33. yearn_treasury/rules/ignore/swaps/__init__.py +9 -0
  34. yearn_treasury/rules/ignore/swaps/aave.py +39 -18
  35. yearn_treasury/rules/ignore/swaps/auctions.cpython-311-darwin.so +0 -0
  36. yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
  37. yearn_treasury/rules/ignore/swaps/compound.py +32 -22
  38. yearn_treasury/rules/ignore/swaps/conversion_factory.cpython-311-darwin.so +0 -0
  39. yearn_treasury/rules/ignore/swaps/conversion_factory.py +21 -0
  40. yearn_treasury/rules/ignore/swaps/cowswap.py +87 -0
  41. yearn_treasury/rules/ignore/swaps/curve.py +170 -0
  42. yearn_treasury/rules/ignore/swaps/gearbox.cpython-311-darwin.so +0 -0
  43. yearn_treasury/rules/ignore/swaps/gearbox.py +12 -2
  44. yearn_treasury/rules/ignore/swaps/iearn.cpython-311-darwin.so +0 -0
  45. yearn_treasury/rules/ignore/swaps/iearn.py +43 -0
  46. yearn_treasury/rules/ignore/swaps/otc.cpython-311-darwin.so +0 -0
  47. yearn_treasury/rules/ignore/swaps/otc.py +58 -0
  48. yearn_treasury/rules/ignore/swaps/pooltogether.cpython-311-darwin.so +0 -0
  49. yearn_treasury/rules/ignore/swaps/pooltogether.py +10 -1
  50. yearn_treasury/rules/ignore/swaps/synthetix.cpython-311-darwin.so +0 -0
  51. yearn_treasury/rules/ignore/swaps/synthetix.py +10 -0
  52. yearn_treasury/rules/ignore/swaps/uniswap.py +29 -6
  53. yearn_treasury/rules/ignore/swaps/unwrapper.cpython-311-darwin.so +0 -0
  54. yearn_treasury/rules/ignore/swaps/unwrapper.py +10 -1
  55. yearn_treasury/rules/ignore/swaps/vaults.cpython-311-darwin.so +0 -0
  56. yearn_treasury/rules/ignore/swaps/vaults.py +90 -8
  57. yearn_treasury/rules/ignore/swaps/woofy.cpython-311-darwin.so +0 -0
  58. yearn_treasury/rules/ignore/swaps/woofy.py +80 -0
  59. yearn_treasury/rules/ignore/swaps/yfi.cpython-311-darwin.so +0 -0
  60. yearn_treasury/rules/ignore/swaps/yfi.py +111 -0
  61. yearn_treasury/rules/ignore/swaps/yla.cpython-311-darwin.so +0 -0
  62. yearn_treasury/rules/ignore/swaps/yla.py +10 -5
  63. yearn_treasury/rules/ignore/unit.cpython-311-darwin.so +0 -0
  64. yearn_treasury/rules/ignore/unit.py +40 -0
  65. yearn_treasury/rules/ignore/weth.cpython-311-darwin.so +0 -0
  66. yearn_treasury/rules/ignore/weth.py +12 -4
  67. yearn_treasury/rules/ignore/ygov.cpython-311-darwin.so +0 -0
  68. yearn_treasury/rules/other_expense/__init__.cpython-311-darwin.so +0 -0
  69. yearn_treasury/rules/other_expense/boost.cpython-311-darwin.so +0 -0
  70. yearn_treasury/rules/other_expense/bugs.cpython-311-darwin.so +0 -0
  71. yearn_treasury/rules/other_expense/donations.cpython-311-darwin.so +0 -0
  72. yearn_treasury/rules/other_expense/donations.py +8 -0
  73. yearn_treasury/rules/other_expense/dyfi.cpython-311-darwin.so +0 -0
  74. yearn_treasury/rules/other_expense/events.cpython-311-darwin.so +0 -0
  75. yearn_treasury/rules/other_expense/misc.cpython-311-darwin.so +0 -0
  76. yearn_treasury/rules/other_expense/misc.py +22 -0
  77. yearn_treasury/rules/other_expense/revshare.cpython-311-darwin.so +0 -0
  78. yearn_treasury/rules/other_income/__init__.cpython-311-darwin.so +0 -0
  79. yearn_treasury/rules/other_income/__init__.py +2 -100
  80. yearn_treasury/rules/other_income/airdrops.cpython-311-darwin.so +0 -0
  81. yearn_treasury/rules/other_income/airdrops.py +30 -0
  82. yearn_treasury/rules/other_income/misc.cpython-311-darwin.so +0 -0
  83. yearn_treasury/rules/other_income/misc.py +80 -0
  84. yearn_treasury/rules/revenue/bribes.cpython-311-darwin.so +0 -0
  85. yearn_treasury/rules/revenue/farming.cpython-311-darwin.so +0 -0
  86. yearn_treasury/rules/revenue/keepcoins.cpython-311-darwin.so +0 -0
  87. yearn_treasury/rules/revenue/seasolver.cpython-311-darwin.so +0 -0
  88. yearn_treasury/rules/revenue/vaults.py +1 -1
  89. yearn_treasury/rules/revenue/yteams.cpython-311-darwin.so +0 -0
  90. yearn_treasury/shitcoins.py +92 -2
  91. yearn_treasury/vaults.cpython-311-darwin.so +0 -0
  92. yearn_treasury/vaults.py +17 -4
  93. yearn_treasury/wallets.yaml +14 -1
  94. yearn_treasury/yteams.py +208 -0
  95. {yearn_treasury-0.0.19.dist-info → yearn_treasury-0.0.45.dist-info}/METADATA +3 -3
  96. yearn_treasury-0.0.45.dist-info/RECORD +128 -0
  97. yearn_treasury-0.0.45.dist-info/top_level.txt +2 -0
  98. yearn_treasury__mypyc.cpython-311-darwin.so +0 -0
  99. 8cac1d3857b01c92a42d__mypyc.cpython-311-darwin.so +0 -0
  100. yearn_treasury-0.0.19.dist-info/RECORD +0 -103
  101. yearn_treasury-0.0.19.dist-info/top_level.txt +0 -2
  102. {yearn_treasury-0.0.19.dist-info → yearn_treasury-0.0.45.dist-info}/WHEEL +0 -0
  103. {yearn_treasury-0.0.19.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
@@ -1,3 +1,16 @@
1
+ """
2
+ Database labeling utility for Yearn Treasury.
3
+
4
+ This module provides a helper function to set up address nicknames
5
+ in the database, mapping key Yearn Treasury addresses to human-readable
6
+ labels. It is used during database preparation to ensure that wallet
7
+ addresses are clearly labeled in analytics and reporting.
8
+
9
+ When Yearn Treasury is imported, this module maps important addresses
10
+ to descriptive nicknames within the DAO Treasury database entity system
11
+ for improved data clarity and prettification of reports.
12
+ """
13
+
1
14
  # mypy: disable-error-code="arg-type"
2
15
  from dao_treasury.db import Address
3
16
  from y import Network
@@ -7,6 +20,14 @@ from yearn_treasury import constants
7
20
 
8
21
 
9
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
+ """
10
31
  chad = {Network.Mainnet: "y", Network.Fantom: "f"}[CHAINID] # type: ignore [index]
11
32
 
12
33
  labels = {
Binary file
yearn_treasury/_ens.py CHANGED
@@ -1,3 +1,17 @@
1
+ """
2
+ ENS resolver and event topic utilities for Yearn Treasury.
3
+
4
+ This module defines the ENS resolver contract and event topic set
5
+ used for dynamic vault discovery and event processing. It supports
6
+ vault mapping and analytics by enabling on-chain lookups and event
7
+ filtering for Yearn vaults.
8
+
9
+ Key Responsibilities:
10
+ - Provide the ENS resolver contract for vault registry lookups.
11
+ - Construct event topic sets for AddressChanged events.
12
+ - Used by vault discovery, analytics, and reporting modules.
13
+ """
14
+
1
15
  from typing import Final
2
16
 
3
17
  from brownie import web3
@@ -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))
@@ -24,6 +24,16 @@
24
24
  YFI Buyer Contract:
25
25
  - "0x0000000000884A0E1fB44F9E24Fa3BDB19514fAE"
26
26
  - "0x0000000000051666BBfBB42925C3eE5d50cF6B10"
27
+ Multichain Fantom Bridge:
28
+ - "0xC564EE9f21Ed8A2d8E7e76c085740d5e4c5FaFbE"
29
+ yLockers Multisig:
30
+ - "0x4444AAAACDBa5580282365e25b16309Bd770ce4a"
31
+ yRoboTreasury Treasury Contract:
32
+ - "0xEf77cc176c748d291EfB6CdC982c5744fC7211c8"
33
+ yRoboTreasury Stables Reserve:
34
+ - "0x278374fFb10B7D16E7633444c13e6E565EA57c28"
35
+ yearn.fi Dutch Auctions:
36
+ - "0x861fE45742f70054917B65bE18904662bD0dBd30"
27
37
  250:
28
38
  Yearn Strategist Multisig:
29
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"]
@@ -1,3 +1,11 @@
1
+ """
2
+ Budget request data model for Yearn Treasury.
3
+
4
+ This module defines the BudgetRequest dataclass, which models a single
5
+ budget request and its state. It provides methods for checking approval,
6
+ rejection, streaming, vesting, and payment status.
7
+ """
8
+
1
9
  from dataclasses import dataclass
2
10
  from logging import getLogger
3
11
  from typing import Final, Optional, Set, final
@@ -1,10 +1,22 @@
1
+ """
2
+ Budget request ingestion and filtering for Yearn Treasury.
3
+
4
+ This module fetches budget requests from GitHub, parses them into
5
+ BudgetRequest objects, and provides lists of approved and rejected
6
+ requests.
7
+ """
8
+
1
9
  import os
2
- from requests import get
3
- from typing import Final, List
10
+ import time
11
+ import requests
12
+ from typing import Any, Dict, Final, List
4
13
 
5
14
  from yearn_treasury.budget._request import BudgetRequest
6
15
 
7
16
 
17
+ API_URL: Final = "https://api.github.com/repos/yearn/budget/issues"
18
+ """URL to fetch issues from the repo."""
19
+
8
20
  # Optionally use a GitHub personal access token for higher API rate limits.
9
21
  # TODO move this to envs file and document
10
22
  _TOKEN: Final = os.environ.get("GITHUB_TOKEN")
@@ -12,22 +24,16 @@ _HEADERS: Final = {"Authorization": f"token {_TOKEN}"} if _TOKEN else {}
12
24
 
13
25
 
14
26
  def fetch_brs() -> List[BudgetRequest]:
15
- # URL to fetch issues from the repo
16
- api_url = "https://api.github.com/repos/yearn/budget/issues"
17
27
  # Use parameters to fetch issues in all states, up to 100 per page.
18
- params = {"state": "all", "per_page": 100, "page": 1}
28
+ current_page = 1
29
+ params = {"state": "all", "per_page": 100, "page": current_page}
19
30
 
20
31
  brs = []
21
32
  retries = 0
22
33
  while True:
23
- response = get(api_url, headers=_HEADERS, params=params) # type: ignore [arg-type]
24
- if response.status_code != 200:
25
- if retries < 5:
26
- retries += 1
27
- continue
28
- raise ConnectionError(f"Failed to fetch issues: {response.status_code} {response.text}")
34
+ response = _make_get_request(params=params)
29
35
 
30
- data: List[dict] = response.json()
36
+ data: List[dict] = response.json() # type: ignore [type-arg]
31
37
  if not data: # If the current page is empty, we are done.
32
38
  break
33
39
 
@@ -38,7 +44,7 @@ def fetch_brs() -> List[BudgetRequest]:
38
44
 
39
45
  # TODO labels table in db (also dataclass) with the descriptions included
40
46
  # Extract the label names (tags) from the "labels" key.
41
- label_objs: List[dict] = item.get("labels", [])
47
+ label_objs: List[dict] = item.get("labels", []) # type: ignore [type-arg]
42
48
  labels = {label.get("name") for label in label_objs}
43
49
 
44
50
  if "budget request" not in labels:
@@ -59,11 +65,32 @@ def fetch_brs() -> List[BudgetRequest]:
59
65
  brs.append(br)
60
66
 
61
67
  # Move on to the next page.
62
- params["page"] += 1 # type: ignore [operator]
68
+ current_page += 1
69
+ params["page"] = current_page
63
70
 
64
71
  return brs
65
72
 
66
73
 
67
- requests = fetch_brs()
68
- approved_requests = [r for r in requests if r.is_approved()]
69
- 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
@@ -59,6 +65,16 @@ TREASURY_WALLETS: Final = {
59
65
  }
60
66
 
61
67
 
68
+ YFI: Final = {
69
+ Network.Mainnet: "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
70
+ Network.Fantom: "0x29b0Da86e484E1C0029B56e817912d778aC0EC69",
71
+ Network.Arbitrum: "0x82e3A8F066a6989666b031d916c43672085b1582",
72
+ Network.Polygon: "0xDA537104D6A5edd53c6fBba9A898708E465260b6",
73
+ }.get(
74
+ CHAINID, None
75
+ ) # type: ignore [call-overload]
76
+
77
+
62
78
  class Args:
63
79
  wallets: Final[Path] = _YEARN_TREASURY_ROOT_DIR / "wallets.yaml"
64
80
  # TODO: update dashboard def to use this label (we will need to migrate the provisioning files to yearn-treasury but we need to do this anyway for yearn-specific additions)
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,24 +92,21 @@ 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
  """
109
+ import dao_treasury.db
98
110
  import eth_portfolio
99
111
 
100
112
  from . import constants, rules, shitcoins
@@ -102,10 +114,16 @@ def main() -> None:
102
114
  # Merge local SHITCOINS into eth_portfolio's config to skip tokens we don't care about
103
115
  eth_portfolio.SHITCOINS[constants.CHAINID].update(shitcoins.SHITCOINS) # type: ignore [index]
104
116
 
117
+ # Drop any shitcoin txs that might be in the db
118
+ dao_treasury.db._drop_shitcoin_txs()
119
+
105
120
  @final
106
121
  class Args(constants.Args):
107
122
  """
108
- 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.
109
127
  """
110
128
 
111
129
  network: Final[str] = args.network
@@ -114,6 +132,9 @@ def main() -> None:
114
132
  interval: Final[str] = args.interval
115
133
  """Time interval between snapshots."""
116
134
 
135
+ concurrency: Final[int] = args.concurrency
136
+ """The max number of historical blocks to export concurrently."""
137
+
117
138
  grafana_port: Final[int] = args.grafana_port
118
139
  """Grafana port."""
119
140
 
@@ -136,7 +157,20 @@ def main() -> None:
136
157
  os.environ["DAO_TREASURY_RENDERER_PORT"] = str(Args.renderer_port)
137
158
  os.environ["VICTORIA_PORT"] = str(Args.victoria_port)
138
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
+
139
173
  # Start the balance export routine
140
- asyncio.get_event_loop().run_until_complete(dao_treasury.main.export(Args))
174
+ asyncio.get_event_loop().run_until_complete(yearn_wrapper())
141
175
 
142
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
@@ -1,3 +1,11 @@
1
+ """
2
+ Gas cost rules for Yearn Treasury.
3
+
4
+ This module defines rules and matching logic for classifying gas-related
5
+ transactions as cost of revenue. It includes logic for multisig
6
+ reimbursements, strategist gas, returned gas, and more.
7
+ """
8
+
1
9
  # mypy: disable-error-code="call-overload"
2
10
  from typing import Final, Set
3
11
 
@@ -27,7 +35,7 @@ gas("Other Gas").match(
27
35
 
28
36
  _STRATEGIST_GAS_HASHES: Final[Set[HexStr]] = {}.get(CHAINID, set())
29
37
 
30
- _RETURNED_GAS_HASHES: Final[Set[HexStr]] = {
38
+ _RETURNED_GAS_HASHES: Final[Set[HexStr]] = { # type: ignore [assignment]
31
39
  Network.Mainnet: {
32
40
  "0x86fee63ec8efb0e7320a6d48ac3890b1089b77a3d9ed74cade389f512471c299",
33
41
  "0xa77c4f7596968fef96565a0025cc6f9881622f62cc4c823232f9c9000ba5f981",
@@ -1,3 +1,10 @@
1
+ """
2
+ Expense rules for infrastructure payments.
3
+
4
+ This module defines matching logic for infrastructure-related expenses,
5
+ including Tenderly, Wonderland Jobs, and generic infra payments.
6
+ """
7
+
1
8
  from typing import Final
2
9
 
3
10
  from dao_treasury import TreasuryTx, expense
@@ -39,3 +39,46 @@ def is_coordinape(tx: TreasuryTx) -> bool:
39
39
  def is_ygift_grant(tx: TreasuryTx) -> bool:
40
40
  """Yearn used to use yGift to send team grants but that ended up being too expensive."""
41
41
  return tx.to_nickname == "Contract: yGift" and tx.symbol == "yyDAI+yUSDC+yUSDT+yTUSD"
42
+
43
+
44
+ # TODO: refactor all of this, there's gotta be a better way to handle yteams who have received both one-off and streamed pmnts
45
+ @grants("yHAAS Trinity [BR#263]")
46
+ def is_yhaas_trinity_ii(tx: TreasuryTx) -> bool:
47
+ """https://github.com/yearn/budget/issues/263"""
48
+ return (
49
+ tx.hash == "0xd35c30664f3241ea2ec3df1c70261086247025eb72c2bc919108dfef9b08a450"
50
+ and tx.to_address.address
51
+ in (
52
+ # team
53
+ "0x35a83D4C1305451E0448fbCa96cAb29A7cCD0811",
54
+ # stream
55
+ "0xEC83C8c3156e4f6b95B048066F3b308C93cb5848",
56
+ )
57
+ )
58
+
59
+
60
+ @grants("G-Team [BR#267]")
61
+ def is_gteam(tx: TreasuryTx) -> bool:
62
+ """https://github.com/yearn/budget/issues/267"""
63
+ return (
64
+ tx.hash == "0xd35c30664f3241ea2ec3df1c70261086247025eb72c2bc919108dfef9b08a450"
65
+ and tx.to_address == "0x63E02F93622541CfE41aFedCF96a114DB71Ba4EE"
66
+ )
67
+
68
+
69
+ @grants("Rantom [BR#129]")
70
+ def is_rantom(tx: TreasuryTx) -> bool:
71
+ """https://github.com/yearn/budget/issues/129"""
72
+ return tx.to_address == "0x254b42CaCf7290e72e2C84c0337E36E645784Ce1"
73
+
74
+
75
+ @grants("Worms")
76
+ def is_worms(tx: TreasuryTx) -> bool:
77
+ return tx.to_address == "0xB1d693B77232D88a3C9467eD5619FfE79E80BCCc"
78
+
79
+
80
+ # NOTE: this needs to go at the bottom because there are some streams that will already be caught by above matchers
81
+ @grants("Simple Vesting Escrow")
82
+ def is_simple_vesting_escrow(tx: TreasuryTx) -> bool:
83
+ # TODO: amortize the streamed funds as a daily amount and sort more granularly based on BR
84
+ return tx.to_nickname == "Contract: Simple Vesting Escrow"
@@ -1,3 +1,11 @@
1
+ """
2
+ Expense rules for security, audits, and bug bounties.
3
+
4
+ This module defines matching logic for security-related expenses,
5
+ including audits (yAcademy, ChainSec, StateMind, MixBytes, unspecified),
6
+ bug bounties, and other security-related deliverables.
7
+ """
8
+
1
9
  from typing import Final
2
10
 
3
11
  from dao_treasury import TreasuryTx, expense
@@ -6,6 +14,7 @@ from y import Network
6
14
 
7
15
  security: Final = expense("Security")
8
16
  audits: Final = security("Audits")
17
+ grants: Final = security("Grants")
9
18
 
10
19
 
11
20
  @audits("yAcademy", Network.Mainnet)
@@ -128,3 +137,11 @@ def is_warroom_help(tx: TreasuryTx) -> bool:
128
137
  tx.hash == "0xca61496c32806ba34f0deb331c32969eda11c947fdd6235173e6fa13d9a1c288"
129
138
  and tx.log_index == 152
130
139
  )
140
+
141
+
142
+ @grants("ySecurity", Network.Mainnet)
143
+ def is_ysecurity(tx: TreasuryTx) -> bool:
144
+ """
145
+ https://github.com/yearn/budget/issues/145
146
+ """
147
+ return tx.to_address == "0x4851C7C7163bdF04A22C9e12Ab77e184a5dB8F0E"
@@ -3,5 +3,6 @@ from yearn_treasury.rules.ignore.maker import *
3
3
  from yearn_treasury.rules.ignore.passthru import *
4
4
  from yearn_treasury.rules.ignore.staking import *
5
5
  from yearn_treasury.rules.ignore.swaps import *
6
+ from yearn_treasury.rules.ignore.unit import *
6
7
  from yearn_treasury.rules.ignore.weth import *
7
8
  from yearn_treasury.rules.ignore.ygov import *