yearn-treasury 0.1.6__cp312-cp312-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.
- yearn_treasury/__init__.py +20 -0
- yearn_treasury/_db.cp312-win32.pyd +0 -0
- yearn_treasury/_db.py +51 -0
- yearn_treasury/_ens.cp312-win32.pyd +0 -0
- yearn_treasury/_ens.py +31 -0
- yearn_treasury/_logging.cp312-win32.pyd +0 -0
- yearn_treasury/_logging.py +43 -0
- yearn_treasury/address_labels.yaml +39 -0
- yearn_treasury/budget/__init__.cp312-win32.pyd +0 -0
- yearn_treasury/budget/__init__.py +6 -0
- yearn_treasury/budget/_request.cp312-win32.pyd +0 -0
- yearn_treasury/budget/_request.py +44 -0
- yearn_treasury/budget/_requests.cp312-win32.pyd +0 -0
- yearn_treasury/budget/_requests.py +96 -0
- yearn_treasury/constants.py +92 -0
- yearn_treasury/main.py +176 -0
- yearn_treasury/py.typed +1 -0
- yearn_treasury/rules/__init__.py +20 -0
- yearn_treasury/rules/constants.cp312-win32.pyd +0 -0
- yearn_treasury/rules/constants.py +17 -0
- yearn_treasury/rules/cost_of_revenue/__init__.py +1 -0
- yearn_treasury/rules/cost_of_revenue/gas.cp312-win32.pyd +0 -0
- yearn_treasury/rules/cost_of_revenue/gas.py +66 -0
- yearn_treasury/rules/cost_of_revenue/match_on_hash.yaml +12 -0
- yearn_treasury/rules/expense/__init__.cp312-win32.pyd +0 -0
- yearn_treasury/rules/expense/__init__.py +4 -0
- yearn_treasury/rules/expense/general.cp312-win32.pyd +0 -0
- yearn_treasury/rules/expense/general.py +13 -0
- yearn_treasury/rules/expense/infrastructure.cp312-win32.pyd +0 -0
- yearn_treasury/rules/expense/infrastructure.py +47 -0
- yearn_treasury/rules/expense/match_on_hash.yaml +48 -0
- yearn_treasury/rules/expense/match_on_to_address.yaml +7 -0
- yearn_treasury/rules/expense/people.cp312-win32.pyd +0 -0
- yearn_treasury/rules/expense/people.py +84 -0
- yearn_treasury/rules/expense/security.cp312-win32.pyd +0 -0
- yearn_treasury/rules/expense/security.py +147 -0
- yearn_treasury/rules/ignore/__init__.py +8 -0
- yearn_treasury/rules/ignore/general.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/general.py +11 -0
- yearn_treasury/rules/ignore/maker.py +92 -0
- yearn_treasury/rules/ignore/passthru.py +310 -0
- yearn_treasury/rules/ignore/staking.py +102 -0
- yearn_treasury/rules/ignore/swaps/__init__.py +25 -0
- yearn_treasury/rules/ignore/swaps/_skip_tokens.py +9 -0
- yearn_treasury/rules/ignore/swaps/aave.py +69 -0
- yearn_treasury/rules/ignore/swaps/auctions.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
- yearn_treasury/rules/ignore/swaps/compound.py +58 -0
- yearn_treasury/rules/ignore/swaps/conversion_factory.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/conversion_factory.py +21 -0
- yearn_treasury/rules/ignore/swaps/cowswap.py +87 -0
- yearn_treasury/rules/ignore/swaps/curve.py +176 -0
- yearn_treasury/rules/ignore/swaps/gearbox.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/gearbox.py +37 -0
- yearn_treasury/rules/ignore/swaps/iearn.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/iearn.py +43 -0
- yearn_treasury/rules/ignore/swaps/otc.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/otc.py +58 -0
- yearn_treasury/rules/ignore/swaps/pooltogether.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/pooltogether.py +23 -0
- yearn_treasury/rules/ignore/swaps/synthetix.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/synthetix.py +10 -0
- yearn_treasury/rules/ignore/swaps/uniswap.py +294 -0
- yearn_treasury/rules/ignore/swaps/unwrapper.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/unwrapper.py +17 -0
- yearn_treasury/rules/ignore/swaps/vaults.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/vaults.py +264 -0
- yearn_treasury/rules/ignore/swaps/woofy.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/woofy.py +80 -0
- yearn_treasury/rules/ignore/swaps/yfi.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/yfi.py +111 -0
- yearn_treasury/rules/ignore/swaps/yla.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/swaps/yla.py +28 -0
- yearn_treasury/rules/ignore/unit.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/unit.py +40 -0
- yearn_treasury/rules/ignore/weth.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/weth.py +48 -0
- yearn_treasury/rules/ignore/ygov.cp312-win32.pyd +0 -0
- yearn_treasury/rules/ignore/ygov.py +16 -0
- yearn_treasury/rules/other_expense/__init__.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/__init__.py +7 -0
- yearn_treasury/rules/other_expense/boost.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/boost.py +49 -0
- yearn_treasury/rules/other_expense/bugs.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/bugs.py +81 -0
- yearn_treasury/rules/other_expense/donations.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/donations.py +43 -0
- yearn_treasury/rules/other_expense/dyfi.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/dyfi.py +29 -0
- yearn_treasury/rules/other_expense/events.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/events.py +21 -0
- yearn_treasury/rules/other_expense/match_on_hash.yaml +43 -0
- yearn_treasury/rules/other_expense/match_on_to_address.yaml +8 -0
- yearn_treasury/rules/other_expense/misc.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/misc.py +49 -0
- yearn_treasury/rules/other_expense/revshare.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_expense/revshare.py +20 -0
- yearn_treasury/rules/other_income/__init__.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_income/__init__.py +2 -0
- yearn_treasury/rules/other_income/airdrops.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_income/airdrops.py +30 -0
- yearn_treasury/rules/other_income/match_on_hash.yaml +21 -0
- yearn_treasury/rules/other_income/misc.cp312-win32.pyd +0 -0
- yearn_treasury/rules/other_income/misc.py +80 -0
- yearn_treasury/rules/revenue/__init__.py +6 -0
- yearn_treasury/rules/revenue/bribes.cp312-win32.pyd +0 -0
- yearn_treasury/rules/revenue/bribes.py +26 -0
- yearn_treasury/rules/revenue/farming.cp312-win32.pyd +0 -0
- yearn_treasury/rules/revenue/farming.py +56 -0
- yearn_treasury/rules/revenue/keepcoins.cp312-win32.pyd +0 -0
- yearn_treasury/rules/revenue/keepcoins.py +63 -0
- yearn_treasury/rules/revenue/match_on_hash.yaml +4 -0
- yearn_treasury/rules/revenue/seasolver.cp312-win32.pyd +0 -0
- yearn_treasury/rules/revenue/seasolver.py +23 -0
- yearn_treasury/rules/revenue/vaults.py +172 -0
- yearn_treasury/rules/revenue/yteams.cp312-win32.pyd +0 -0
- yearn_treasury/rules/revenue/yteams.py +17 -0
- yearn_treasury/shitcoins.py +144 -0
- yearn_treasury/vaults.cp312-win32.pyd +0 -0
- yearn_treasury/vaults.py +50 -0
- yearn_treasury/wallets.yaml +54 -0
- yearn_treasury/yteams.py +208 -0
- yearn_treasury-0.1.6.dist-info/METADATA +85 -0
- yearn_treasury-0.1.6.dist-info/RECORD +128 -0
- yearn_treasury-0.1.6.dist-info/WHEEL +5 -0
- yearn_treasury-0.1.6.dist-info/entry_points.txt +2 -0
- yearn_treasury-0.1.6.dist-info/top_level.txt +2 -0
- yearn_treasury__mypyc.cp312-win32.pyd +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
from yearn_treasury import budget
|
|
4
|
+
from yearn_treasury._db import prepare_db
|
|
5
|
+
from yearn_treasury._logging import setup_eth_portfolio_logging
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
prepare_db()
|
|
9
|
+
setup_eth_portfolio_logging()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
warnings.filterwarnings(
|
|
13
|
+
"ignore",
|
|
14
|
+
message=".Event log does not contain enough topics for the given ABI.",
|
|
15
|
+
category=UserWarning,
|
|
16
|
+
module="brownie.network.event",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
__all__ = ["budget"]
|
|
Binary file
|
yearn_treasury/_db.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
|
|
14
|
+
# mypy: disable-error-code="arg-type"
|
|
15
|
+
import time
|
|
16
|
+
|
|
17
|
+
import dao_treasury._docker
|
|
18
|
+
from dao_treasury.db import Address, init_db
|
|
19
|
+
from y import Network
|
|
20
|
+
from y.constants import CHAINID
|
|
21
|
+
|
|
22
|
+
from yearn_treasury import constants
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def prepare_db() -> None:
|
|
26
|
+
"""
|
|
27
|
+
Set up address nicknames in the Yearn Treasury database.
|
|
28
|
+
|
|
29
|
+
Maps key Yearn Treasury addresses to human-readable labels for improved
|
|
30
|
+
clarity in analytics and reporting. This function is typically called
|
|
31
|
+
during database preparation to ensure wallet addresses are labeled
|
|
32
|
+
within the DAO Treasury database entity system.
|
|
33
|
+
"""
|
|
34
|
+
# We need to start the postgres container earlier than dao-treasury does
|
|
35
|
+
dao_treasury._docker.up("postgres")
|
|
36
|
+
|
|
37
|
+
time.sleep(5)
|
|
38
|
+
|
|
39
|
+
init_db()
|
|
40
|
+
|
|
41
|
+
chad = {Network.Mainnet: "y", Network.Fantom: "f"}[CHAINID] # type: ignore [index]
|
|
42
|
+
|
|
43
|
+
labels = {
|
|
44
|
+
constants.TREASURY_MULTISIG: "Yearn Treasury",
|
|
45
|
+
constants.YCHAD_MULTISIG: f"Yearn {chad}Chad Multisig",
|
|
46
|
+
# constants.STRATEGIST_MULTISIG: "Yearn Strategist Multisig",
|
|
47
|
+
# This wallet is an EOA that has been used to assist in bridging tokens across chains.
|
|
48
|
+
"0x5FcdC32DfC361a32e9d5AB9A384b890C62D0b8AC": "Bridge Assistooor EOA",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Address.set_nicknames(labels)
|
|
Binary file
|
yearn_treasury/_ens.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
|
|
15
|
+
from typing import Final
|
|
16
|
+
|
|
17
|
+
from brownie import web3
|
|
18
|
+
from web3._utils.abi import filter_by_name
|
|
19
|
+
from web3._utils.events import construct_event_topic_set
|
|
20
|
+
from y import Contract
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
resolver: Final[Contract] = Contract("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41")
|
|
24
|
+
|
|
25
|
+
topics: Final = construct_event_topic_set(
|
|
26
|
+
filter_by_name("AddressChanged", resolver.abi)[0], # type: ignore [arg-type]
|
|
27
|
+
web3.codec,
|
|
28
|
+
{"node": web3.ens.namehash("v2.registry.ychad.eth")}, # type: ignore [union-attr]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = ["resolver", "topics"]
|
|
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))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
1:
|
|
2
|
+
Yearn Treasury V1:
|
|
3
|
+
- "0xb99a40fcE04cb740EB79fC04976CA15aF69AaaaE"
|
|
4
|
+
Yearn Strategist Multisig: # refactor this to do from python from constants file
|
|
5
|
+
- "0x16388463d60FFE0661Cf7F1f31a7D658aC790ff7"
|
|
6
|
+
yMechs Multisig:
|
|
7
|
+
- "0x2C01B4AD51a67E2d8F02208F54dF9aC4c0B778B6"
|
|
8
|
+
ySwap Multisig:
|
|
9
|
+
- "0x7d2aB9CA511EBD6F03971Fb417d3492aA82513f0"
|
|
10
|
+
Yearn KP3R Wallet:
|
|
11
|
+
- "0x8d12a197cb00d4747a1fe03395095ce2a5cc6819"
|
|
12
|
+
Token Dumper Multisig:
|
|
13
|
+
- "0xC001d00d425Fa92C4F840baA8f1e0c27c4297a0B"
|
|
14
|
+
Cowswap Multisig:
|
|
15
|
+
- "0xA03be496e67Ec29bC62F01a428683D7F9c204930"
|
|
16
|
+
dYFI Redemption Contract:
|
|
17
|
+
- "0x7dC3A74F0684fc026f9163C6D5c3C99fda2cf60a"
|
|
18
|
+
Yearn veFarming Multisig:
|
|
19
|
+
- "0x4fc1b14cD213e7B6212145Ba4f180C3d53d1A11e"
|
|
20
|
+
CRV Buyer Contract (DAI):
|
|
21
|
+
- "0x0000000000007F150Bd6f54c40A34d7C3d5e9f56"
|
|
22
|
+
CRV Buyer Contract (USDT):
|
|
23
|
+
- "0x0000000000005117Dd3A72E64a705198753FDD54"
|
|
24
|
+
YFI Buyer Contract:
|
|
25
|
+
- "0x0000000000884A0E1fB44F9E24Fa3BDB19514fAE"
|
|
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"
|
|
37
|
+
250:
|
|
38
|
+
Yearn Strategist Multisig:
|
|
39
|
+
- "0x72a34AbafAB09b15E7191822A679f28E067C4a16"
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from logging import getLogger
|
|
11
|
+
from typing import Final, final
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
logger: Final = getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@final
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class BudgetRequest:
|
|
20
|
+
id: int
|
|
21
|
+
number: int
|
|
22
|
+
title: str
|
|
23
|
+
state: str
|
|
24
|
+
url: str
|
|
25
|
+
created_at: str
|
|
26
|
+
updated_at: str
|
|
27
|
+
closed_at: str | None
|
|
28
|
+
body: str | None
|
|
29
|
+
labels: set[str]
|
|
30
|
+
|
|
31
|
+
def is_approved(self) -> bool:
|
|
32
|
+
return "approved" in self.labels
|
|
33
|
+
|
|
34
|
+
def is_rejected(self) -> bool:
|
|
35
|
+
return "rejected" in self.labels
|
|
36
|
+
|
|
37
|
+
def is_stream(self) -> bool:
|
|
38
|
+
return "stream" in self.labels
|
|
39
|
+
|
|
40
|
+
def is_vesting(self) -> bool:
|
|
41
|
+
return "vesting" in self.labels
|
|
42
|
+
|
|
43
|
+
def is_paid(self) -> bool:
|
|
44
|
+
return "paid" in self.labels
|
|
Binary file
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
import requests
|
|
12
|
+
from typing import Any, Final
|
|
13
|
+
|
|
14
|
+
from yearn_treasury.budget._request import BudgetRequest
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
API_URL: Final = "https://api.github.com/repos/yearn/budget/issues"
|
|
18
|
+
"""URL to fetch issues from the repo."""
|
|
19
|
+
|
|
20
|
+
# Optionally use a GitHub personal access token for higher API rate limits.
|
|
21
|
+
# TODO move this to envs file and document
|
|
22
|
+
_TOKEN: Final = os.environ.get("GITHUB_TOKEN")
|
|
23
|
+
_HEADERS: Final = {"Authorization": f"token {_TOKEN}"} if _TOKEN else {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def fetch_brs() -> list[BudgetRequest]:
|
|
27
|
+
# Use parameters to fetch issues in all states, up to 100 per page.
|
|
28
|
+
current_page = 1
|
|
29
|
+
params = {"state": "all", "per_page": 100, "page": current_page}
|
|
30
|
+
|
|
31
|
+
brs = []
|
|
32
|
+
retries = 0
|
|
33
|
+
while True:
|
|
34
|
+
response = _make_get_request(params=params)
|
|
35
|
+
|
|
36
|
+
data: list[dict] = response.json() # type: ignore [type-arg]
|
|
37
|
+
if not data: # If the current page is empty, we are done.
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
for item in data:
|
|
41
|
+
# GitHub's issues API returns pull requests as well.
|
|
42
|
+
if "pull_request" in item:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
# TODO labels table in db (also dataclass) with the descriptions included
|
|
46
|
+
# Extract the label names (tags) from the "labels" key.
|
|
47
|
+
label_objs: list[dict] = item.get("labels", []) # type: ignore [type-arg]
|
|
48
|
+
labels = {label.get("name") for label in label_objs}
|
|
49
|
+
|
|
50
|
+
if "budget request" not in labels:
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
br = BudgetRequest(
|
|
54
|
+
id=item.get("id"), # type: ignore [arg-type]
|
|
55
|
+
number=item.get("number"), # type: ignore [arg-type]
|
|
56
|
+
title=item.get("title"), # type: ignore [arg-type]
|
|
57
|
+
state=item.get("state"), # type: ignore [arg-type]
|
|
58
|
+
url=item.get("html_url"), # type: ignore [arg-type]
|
|
59
|
+
created_at=item.get("created_at"), # type: ignore [arg-type]
|
|
60
|
+
updated_at=item.get("updated_at"), # type: ignore [arg-type]
|
|
61
|
+
closed_at=item.get("closed_at"), # type: ignore [arg-type]
|
|
62
|
+
body=item.get("body"), # type: ignore [arg-type]
|
|
63
|
+
labels=labels, # type: ignore [arg-type]
|
|
64
|
+
)
|
|
65
|
+
brs.append(br)
|
|
66
|
+
|
|
67
|
+
# Move on to the next page.
|
|
68
|
+
current_page += 1
|
|
69
|
+
params["page"] = current_page
|
|
70
|
+
|
|
71
|
+
return brs
|
|
72
|
+
|
|
73
|
+
|
|
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()]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Final, Set
|
|
3
|
+
|
|
4
|
+
import y.constants
|
|
5
|
+
from eth_typing import BlockNumber, ChecksumAddress
|
|
6
|
+
from y import Network, convert
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
CHAINID: Final = y.constants.CHAINID
|
|
10
|
+
|
|
11
|
+
EEE_ADDRESS: Final = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
|
|
12
|
+
ZERO_ADDRESS: Final = "0x0000000000000000000000000000000000000000"
|
|
13
|
+
|
|
14
|
+
_YEARN_TREASURY_ROOT_DIR: Final = Path(__file__).parent
|
|
15
|
+
|
|
16
|
+
TREASURY_MULTISIGS: Final = {
|
|
17
|
+
Network.Mainnet: "0x93A62dA5a14C80f265DAbC077fCEE437B1a0Efde",
|
|
18
|
+
Network.Fantom: "0x89716Ad7EDC3be3B35695789C475F3e7A3Deb12a",
|
|
19
|
+
Network.Arbitrum: "0x1deb47dcc9a35ad454bf7f0fcdb03c09792c08c1",
|
|
20
|
+
Network.Optimism: "0x84654e35E504452769757AAe5a8C7C6599cBf954",
|
|
21
|
+
Network.Base: "0x02ff746D8cb62709aEEc611CeC9B17d7dD1D3480",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
YCHAD_MULTISIGS: Final = {
|
|
25
|
+
Network.Mainnet: "0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52",
|
|
26
|
+
Network.Fantom: "0xC0E2830724C946a6748dDFE09753613cd38f6767",
|
|
27
|
+
Network.Gnosis: "0x22eAe41c7Da367b9a15e942EB6227DF849Bb498C",
|
|
28
|
+
Network.Arbitrum: "0xb6bc033d34733329971b938fef32fad7e98e56ad",
|
|
29
|
+
Network.Optimism: "0xF5d9D6133b698cE29567a90Ab35CfB874204B3A7",
|
|
30
|
+
Network.Base: "0xbfAABa9F56A39B814281D68d2Ad949e88D06b02E",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
YSWAP_MULTISIGS: Final = {
|
|
34
|
+
Network.Mainnet: "0x7d2aB9CA511EBD6F03971Fb417d3492aA82513f0",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if CHAINID not in TREASURY_MULTISIGS or CHAINID not in YCHAD_MULTISIGS:
|
|
39
|
+
raise RuntimeError(f"{Network(CHAINID)} is not supported")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
TREASURY_MULTISIG: Final = convert.to_address(TREASURY_MULTISIGS[CHAINID]) # type: ignore [index]
|
|
43
|
+
|
|
44
|
+
YCHAD_MULTISIG: Final = convert.to_address(YCHAD_MULTISIGS[CHAINID]) # type: ignore [index]
|
|
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
|
|
48
|
+
|
|
49
|
+
_TREASURY_WALLETS: Final = {
|
|
50
|
+
Network.Mainnet: {
|
|
51
|
+
TREASURY_MULTISIG,
|
|
52
|
+
YCHAD_MULTISIG,
|
|
53
|
+
"0xb99a40fcE04cb740EB79fC04976CA15aF69AaaaE", # Yearn Treasury V1
|
|
54
|
+
"0x5f0845101857d2A91627478e302357860b1598a1", # Yearn KP3R Wallet
|
|
55
|
+
YSWAP_MULTISIG,
|
|
56
|
+
"0x2C01B4AD51a67E2d8F02208F54dF9aC4c0B778B6", # yMechs Multisig
|
|
57
|
+
"0xE376e8e8E3B0793CD61C6F1283bA18548b726C2e", # Fee Reimbursement Stash
|
|
58
|
+
"0xC001d00d425Fa92C4F840baA8f1e0c27c4297a0B", # New token dumping wallet
|
|
59
|
+
"0x4fc1b14cD213e7B6212145Ba4f180C3d53d1A11e", # veFarming wallet
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
TREASURY_WALLETS: Final = {
|
|
64
|
+
convert.to_address(address) for address in _TREASURY_WALLETS.get(CHAINID, set()) # type: ignore [call-overload]
|
|
65
|
+
}
|
|
66
|
+
|
|
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
|
+
|
|
78
|
+
class Args:
|
|
79
|
+
wallets: Final[Path] = _YEARN_TREASURY_ROOT_DIR / "wallets.yaml"
|
|
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)
|
|
81
|
+
label: Final[str] = "Treasury" # "Yearn"
|
|
82
|
+
first_tx_block: Final = BlockNumber({Network.Mainnet: 10_502_337}.get(CHAINID, 0)) # type: ignore [call-overload]
|
|
83
|
+
export_start_block: Final = first_tx_block
|
|
84
|
+
|
|
85
|
+
sort_rules: Final[Path] = _YEARN_TREASURY_ROOT_DIR / "rules"
|
|
86
|
+
"""The path where the sort rules for dao-treasury are defined."""
|
|
87
|
+
|
|
88
|
+
nicknames: Final[Path] = _YEARN_TREASURY_ROOT_DIR / "address_labels.yaml"
|
|
89
|
+
"""The path where yearn-treasury's address nicknames are defined."""
|
|
90
|
+
|
|
91
|
+
custom_bucket: Final[list[str]] = [f"{YFI}:YFI"]
|
|
92
|
+
"""Custom bucket mapping for token addresses, tells DAO Treasury to categorize YFI and YFI wrappers as special bucket 'YFI'."""
|
yearn_treasury/main.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for the Yearn Treasury exporter.
|
|
3
|
+
|
|
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 metrics, and pushes them to Victoria Metrics.
|
|
7
|
+
It also launches Yearn Treasury's Grafana dashboard and an optional renderer for visual reports.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
Run export every 12 hours on mainnet:
|
|
11
|
+
|
|
12
|
+
.. code-block:: bash
|
|
13
|
+
|
|
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)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import asyncio
|
|
28
|
+
import os
|
|
29
|
+
from argparse import ArgumentParser
|
|
30
|
+
from typing import Final, final
|
|
31
|
+
|
|
32
|
+
from yearn_treasury import yteams
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
parser = ArgumentParser(description="Treasury CLI")
|
|
36
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
37
|
+
|
|
38
|
+
run_parser = subparsers.add_parser("run", help="Run the treasury export tool")
|
|
39
|
+
run_parser.add_argument(
|
|
40
|
+
"--network",
|
|
41
|
+
type=str,
|
|
42
|
+
help="The brownie network identifier for the RPC to use. Overrides BROWNIE_NETWORK_ID env var if unset.",
|
|
43
|
+
default="mainnet",
|
|
44
|
+
)
|
|
45
|
+
run_parser.add_argument(
|
|
46
|
+
"--interval",
|
|
47
|
+
type=str,
|
|
48
|
+
help="The time interval between datapoints. Default: 12h",
|
|
49
|
+
default="12h",
|
|
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
|
+
)
|
|
57
|
+
run_parser.add_argument(
|
|
58
|
+
"--daemon",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Run as a background daemon (currently unsupported).",
|
|
61
|
+
)
|
|
62
|
+
run_parser.add_argument(
|
|
63
|
+
"--grafana-port",
|
|
64
|
+
type=int,
|
|
65
|
+
help="Port for the Grafana dashboard where you can view your data. Default: 3004",
|
|
66
|
+
default=3004,
|
|
67
|
+
)
|
|
68
|
+
run_parser.add_argument(
|
|
69
|
+
"--victoria-port",
|
|
70
|
+
type=int,
|
|
71
|
+
help="Port for the Victoria metrics reporting endpoint. Default: 8430",
|
|
72
|
+
default=8430,
|
|
73
|
+
)
|
|
74
|
+
run_parser.add_argument(
|
|
75
|
+
"--start-renderer",
|
|
76
|
+
action="store_true",
|
|
77
|
+
help="If set, the Grafana renderer container will be started for dashboard image export. By default, only the grafana container is started.",
|
|
78
|
+
)
|
|
79
|
+
run_parser.add_argument(
|
|
80
|
+
"--renderer-port",
|
|
81
|
+
type=int,
|
|
82
|
+
help="Port for the service that renders visual reports. Default: 8080",
|
|
83
|
+
default=8080,
|
|
84
|
+
)
|
|
85
|
+
args = run_parser.parse_args()
|
|
86
|
+
|
|
87
|
+
# Set BROWNIE_NETWORK_ID from --network flag if not already set
|
|
88
|
+
os.environ.setdefault("BROWNIE_NETWORK_ID", args.network)
|
|
89
|
+
BROWNIE_NETWORK = os.environ["BROWNIE_NETWORK_ID"]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# TODO: run forever arg
|
|
93
|
+
def main() -> None:
|
|
94
|
+
"""
|
|
95
|
+
Connect to the configured Brownie network, clean up the database, and start the export loop.
|
|
96
|
+
|
|
97
|
+
This function is registered as a console script entrypoint under
|
|
98
|
+
``yearn-treasury``. It performs the following steps:
|
|
99
|
+
1. Reads the ``BROWNIE_NETWORK_ID`` environment variable (populated from
|
|
100
|
+
the ``--network`` option or existing env var).
|
|
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.
|
|
108
|
+
"""
|
|
109
|
+
import dao_treasury.db
|
|
110
|
+
import eth_portfolio
|
|
111
|
+
|
|
112
|
+
from . import constants, rules, shitcoins
|
|
113
|
+
|
|
114
|
+
# Merge local SHITCOINS into eth_portfolio's config to skip tokens we don't care about
|
|
115
|
+
eth_portfolio.SHITCOINS[constants.CHAINID].update(shitcoins.SHITCOINS) # type: ignore [index]
|
|
116
|
+
|
|
117
|
+
# Drop any shitcoin txs that might be in the db
|
|
118
|
+
dao_treasury.db._drop_shitcoin_txs()
|
|
119
|
+
|
|
120
|
+
@final
|
|
121
|
+
class Args(constants.Args):
|
|
122
|
+
"""
|
|
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.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
network: Final[str] = args.network
|
|
130
|
+
"""Brownie network to connect to."""
|
|
131
|
+
|
|
132
|
+
interval: Final[str] = args.interval
|
|
133
|
+
"""Time interval between snapshots."""
|
|
134
|
+
|
|
135
|
+
concurrency: Final[int] = args.concurrency
|
|
136
|
+
"""The max number of historical blocks to export concurrently."""
|
|
137
|
+
|
|
138
|
+
grafana_port: Final[int] = args.grafana_port
|
|
139
|
+
"""Grafana port."""
|
|
140
|
+
|
|
141
|
+
victoria_port: Final[int] = args.victoria_port
|
|
142
|
+
"""Victoria metrics port."""
|
|
143
|
+
|
|
144
|
+
start_renderer: Final[bool] = args.start_renderer
|
|
145
|
+
"""Boolean indicating whether to start the renderer container."""
|
|
146
|
+
|
|
147
|
+
renderer_port: Final[int] = args.renderer_port
|
|
148
|
+
"""Report renderer port."""
|
|
149
|
+
|
|
150
|
+
daemon: Final[bool] = args.daemon
|
|
151
|
+
"""Whether to run in daemon mode."""
|
|
152
|
+
|
|
153
|
+
import dao_treasury.main
|
|
154
|
+
|
|
155
|
+
# Export ports for external services (must come after import)
|
|
156
|
+
os.environ["DAO_TREASURY_GRAFANA_PORT"] = str(Args.grafana_port)
|
|
157
|
+
os.environ["DAO_TREASURY_RENDERER_PORT"] = str(Args.renderer_port)
|
|
158
|
+
os.environ["VICTORIA_PORT"] = str(Args.victoria_port)
|
|
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
|
+
|
|
173
|
+
# Start the balance export routine
|
|
174
|
+
asyncio.get_event_loop().run_until_complete(yearn_wrapper())
|
|
175
|
+
|
|
176
|
+
rules # I just put this here so the import isn't flagged as unused
|
yearn_treasury/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +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
|
+
|
|
7
|
+
from yearn_treasury.rules.cost_of_revenue import *
|
|
8
|
+
from yearn_treasury.rules.expense import *
|
|
9
|
+
from yearn_treasury.rules.ignore import *
|
|
10
|
+
from yearn_treasury.rules.other_expense import *
|
|
11
|
+
from yearn_treasury.rules.other_income import *
|
|
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
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# mypy: disable-error-code="index"
|
|
2
|
+
from typing import Final
|
|
3
|
+
|
|
4
|
+
from y import Network
|
|
5
|
+
from y.constants import CHAINID
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
EEE_ADDRESS: Final = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
|
|
9
|
+
ZERO_ADDRESS: Final = "0x0000000000000000000000000000000000000000"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
YFI: Final = {
|
|
13
|
+
Network.Mainnet: "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
|
|
14
|
+
Network.Fantom: "0x29b0Da86e484E1C0029B56e817912d778aC0EC69",
|
|
15
|
+
Network.Arbitrum: "0x82e3A8F066a6989666b031d916c43672085b1582",
|
|
16
|
+
Network.Polygon: "0xDA537104D6A5edd53c6fBba9A898708E465260b6",
|
|
17
|
+
}[CHAINID]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from yearn_treasury.rules.cost_of_revenue.gas import *
|
|
Binary file
|