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.
- yearn_treasury/__init__.py +2 -0
- yearn_treasury/_db.cpython-312-darwin.so +0 -0
- yearn_treasury/_db.py +8 -0
- yearn_treasury/_ens.cpython-312-darwin.so +0 -0
- yearn_treasury/_logging.cpython-312-darwin.so +0 -0
- yearn_treasury/_logging.py +43 -0
- yearn_treasury/address_labels.yaml +6 -0
- yearn_treasury/budget/__init__.cpython-312-darwin.so +0 -0
- yearn_treasury/budget/__init__.py +2 -2
- yearn_treasury/budget/_request.cpython-312-darwin.so +0 -0
- yearn_treasury/budget/_requests.cpython-312-darwin.so +0 -0
- yearn_treasury/budget/_requests.py +34 -15
- yearn_treasury/constants.py +7 -1
- yearn_treasury/main.py +50 -20
- yearn_treasury/rules/__init__.py +14 -0
- yearn_treasury/rules/constants.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/cost_of_revenue/gas.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/expense/__init__.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/expense/general.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/expense/infrastructure.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/expense/people.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/expense/security.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/general.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/maker.py +14 -16
- yearn_treasury/rules/ignore/passthru.py +58 -10
- yearn_treasury/rules/ignore/swaps/__init__.py +1 -0
- yearn_treasury/rules/ignore/swaps/aave.py +17 -10
- yearn_treasury/rules/ignore/swaps/auctions.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
- yearn_treasury/rules/ignore/swaps/compound.py +32 -22
- yearn_treasury/rules/ignore/swaps/conversion_factory.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/cowswap.py +16 -13
- yearn_treasury/rules/ignore/swaps/curve.py +46 -15
- yearn_treasury/rules/ignore/swaps/gearbox.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/iearn.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/otc.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/pooltogether.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/synthetix.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/uniswap.py +26 -6
- yearn_treasury/rules/ignore/swaps/unwrapper.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/vaults.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/vaults.py +5 -4
- yearn_treasury/rules/ignore/swaps/woofy.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/woofy.py +23 -24
- yearn_treasury/rules/ignore/swaps/yfi.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/swaps/yfi.py +18 -7
- yearn_treasury/rules/ignore/swaps/yla.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/unit.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/weth.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/ignore/ygov.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/__init__.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/boost.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/bugs.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/donations.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/dyfi.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/events.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/misc.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_expense/revshare.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_income/__init__.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_income/airdrops.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_income/misc.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/other_income/misc.py +1 -1
- yearn_treasury/rules/revenue/bribes.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/revenue/farming.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/revenue/keepcoins.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/revenue/seasolver.cpython-312-darwin.so +0 -0
- yearn_treasury/rules/revenue/vaults.py +1 -1
- yearn_treasury/rules/revenue/yteams.cpython-312-darwin.so +0 -0
- yearn_treasury/shitcoins.py +33 -3
- yearn_treasury/vaults.cpython-312-darwin.so +0 -0
- yearn_treasury/vaults.py +3 -4
- yearn_treasury/yteams.py +208 -0
- {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/METADATA +3 -3
- {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/RECORD +78 -71
- yearn_treasury-0.0.45.dist-info/top_level.txt +2 -0
- yearn_treasury__mypyc.cpython-312-darwin.so +0 -0
- e3e435302880fbc14075__mypyc.cpython-312-darwin.so +0 -0
- yearn_treasury-0.0.26.dist-info/top_level.txt +0 -2
- {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/WHEEL +0 -0
- {yearn_treasury-0.0.26.dist-info → yearn_treasury-0.0.45.dist-info}/entry_points.txt +0 -0
yearn_treasury/__init__.py
CHANGED
|
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
|
|
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"
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from yearn_treasury.budget._request import BudgetRequest
|
|
2
|
-
from yearn_treasury.budget._requests import approved_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", "
|
|
6
|
+
__all__ = ["BudgetRequest", "budget_requests", "approved_requests", "rejected_requests"]
|
|
Binary file
|
|
Binary file
|
|
@@ -7,12 +7,16 @@ requests.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
68
|
+
current_page += 1
|
|
69
|
+
params["page"] = current_page
|
|
71
70
|
|
|
72
71
|
return brs
|
|
73
72
|
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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()]
|
yearn_treasury/constants.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
6
|
-
periodically snapshots treasury
|
|
7
|
-
|
|
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
|
|
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
|
|
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
|
|
89
|
-
3.
|
|
90
|
-
4.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
6.
|
|
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
|
|
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(
|
|
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
|
yearn_treasury/rules/__init__.py
CHANGED
|
@@ -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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ==
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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"
|
|
Binary file
|
|
@@ -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
|