yearn-treasury 0.0.12__cp310-cp310-win_amd64.whl → 0.0.48__cp310-cp310-win_amd64.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 (127) hide show
  1. yearn_treasury/__init__.py +16 -0
  2. yearn_treasury/_db.cp310-win_amd64.pyd +0 -0
  3. yearn_treasury/_db.py +41 -0
  4. yearn_treasury/_ens.cp310-win_amd64.pyd +0 -0
  5. yearn_treasury/_ens.py +14 -0
  6. yearn_treasury/_logging.cp310-win_amd64.pyd +0 -0
  7. yearn_treasury/_logging.py +43 -0
  8. yearn_treasury/address_labels.yaml +39 -0
  9. yearn_treasury/budget/__init__.cp310-win_amd64.pyd +0 -0
  10. yearn_treasury/budget/__init__.py +2 -2
  11. yearn_treasury/budget/_request.cp310-win_amd64.pyd +0 -0
  12. yearn_treasury/budget/_request.py +8 -0
  13. yearn_treasury/budget/_requests.cp310-win_amd64.pyd +0 -0
  14. yearn_treasury/budget/_requests.py +45 -14
  15. yearn_treasury/constants.py +34 -6
  16. yearn_treasury/main.py +72 -33
  17. yearn_treasury/rules/__init__.py +15 -0
  18. yearn_treasury/rules/constants.cp310-win_amd64.pyd +0 -0
  19. yearn_treasury/rules/constants.py +1 -1
  20. yearn_treasury/rules/cost_of_revenue/gas.cp310-win_amd64.pyd +0 -0
  21. yearn_treasury/rules/cost_of_revenue/gas.py +9 -1
  22. yearn_treasury/rules/expense/__init__.cp310-win_amd64.pyd +0 -0
  23. yearn_treasury/rules/expense/general.cp310-win_amd64.pyd +0 -0
  24. yearn_treasury/rules/expense/infrastructure.cp310-win_amd64.pyd +0 -0
  25. yearn_treasury/rules/expense/infrastructure.py +31 -1
  26. yearn_treasury/rules/expense/people.cp310-win_amd64.pyd +0 -0
  27. yearn_treasury/rules/expense/people.py +43 -0
  28. yearn_treasury/rules/expense/security.cp310-win_amd64.pyd +0 -0
  29. yearn_treasury/rules/expense/security.py +18 -1
  30. yearn_treasury/rules/ignore/__init__.py +2 -0
  31. yearn_treasury/rules/ignore/general.cp310-win_amd64.pyd +0 -0
  32. yearn_treasury/rules/ignore/general.py +3 -2
  33. yearn_treasury/rules/ignore/maker.py +48 -64
  34. yearn_treasury/rules/ignore/passthru.py +310 -0
  35. yearn_treasury/rules/ignore/staking.py +8 -14
  36. yearn_treasury/rules/ignore/swaps/__init__.py +14 -0
  37. yearn_treasury/rules/ignore/swaps/aave.py +39 -19
  38. yearn_treasury/rules/ignore/swaps/auctions.cp310-win_amd64.pyd +0 -0
  39. yearn_treasury/rules/ignore/swaps/auctions.py +31 -0
  40. yearn_treasury/rules/ignore/swaps/compound.py +38 -36
  41. yearn_treasury/rules/ignore/swaps/conversion_factory.cp310-win_amd64.pyd +0 -0
  42. yearn_treasury/rules/ignore/swaps/conversion_factory.py +21 -0
  43. yearn_treasury/rules/ignore/swaps/cowswap.py +87 -0
  44. yearn_treasury/rules/ignore/swaps/curve.py +170 -0
  45. yearn_treasury/rules/ignore/swaps/gearbox.cp310-win_amd64.pyd +0 -0
  46. yearn_treasury/rules/ignore/swaps/gearbox.py +37 -0
  47. yearn_treasury/rules/ignore/swaps/iearn.cp310-win_amd64.pyd +0 -0
  48. yearn_treasury/rules/ignore/swaps/iearn.py +43 -0
  49. yearn_treasury/rules/ignore/swaps/otc.cp310-win_amd64.pyd +0 -0
  50. yearn_treasury/rules/ignore/swaps/otc.py +58 -0
  51. yearn_treasury/rules/ignore/swaps/pooltogether.cp310-win_amd64.pyd +0 -0
  52. yearn_treasury/rules/ignore/swaps/pooltogether.py +23 -0
  53. yearn_treasury/rules/ignore/swaps/synthetix.cp310-win_amd64.pyd +0 -0
  54. yearn_treasury/rules/ignore/swaps/synthetix.py +10 -0
  55. yearn_treasury/rules/ignore/swaps/uniswap.py +222 -169
  56. yearn_treasury/rules/ignore/swaps/unwrapper.cp310-win_amd64.pyd +0 -0
  57. yearn_treasury/rules/ignore/swaps/unwrapper.py +17 -0
  58. yearn_treasury/rules/ignore/swaps/vaults.cp310-win_amd64.pyd +0 -0
  59. yearn_treasury/rules/ignore/swaps/vaults.py +264 -0
  60. yearn_treasury/rules/ignore/swaps/woofy.cp310-win_amd64.pyd +0 -0
  61. yearn_treasury/rules/ignore/swaps/woofy.py +80 -0
  62. yearn_treasury/rules/ignore/swaps/yfi.cp310-win_amd64.pyd +0 -0
  63. yearn_treasury/rules/ignore/swaps/yfi.py +111 -0
  64. yearn_treasury/rules/ignore/swaps/yla.cp310-win_amd64.pyd +0 -0
  65. yearn_treasury/rules/ignore/swaps/yla.py +28 -0
  66. yearn_treasury/rules/ignore/unit.cp310-win_amd64.pyd +0 -0
  67. yearn_treasury/rules/ignore/unit.py +40 -0
  68. yearn_treasury/rules/ignore/weth.cp310-win_amd64.pyd +0 -0
  69. yearn_treasury/rules/ignore/weth.py +12 -4
  70. yearn_treasury/rules/ignore/ygov.cp310-win_amd64.pyd +0 -0
  71. yearn_treasury/rules/other_expense/__init__.cp310-win_amd64.pyd +0 -0
  72. yearn_treasury/rules/other_expense/__init__.py +7 -0
  73. yearn_treasury/rules/other_expense/boost.cp310-win_amd64.pyd +0 -0
  74. yearn_treasury/rules/other_expense/boost.py +49 -0
  75. yearn_treasury/rules/other_expense/bugs.cp310-win_amd64.pyd +0 -0
  76. yearn_treasury/rules/other_expense/bugs.py +81 -0
  77. yearn_treasury/rules/other_expense/donations.cp310-win_amd64.pyd +0 -0
  78. yearn_treasury/rules/other_expense/donations.py +43 -0
  79. yearn_treasury/rules/other_expense/dyfi.cp310-win_amd64.pyd +0 -0
  80. yearn_treasury/rules/other_expense/dyfi.py +29 -0
  81. yearn_treasury/rules/other_expense/events.cp310-win_amd64.pyd +0 -0
  82. yearn_treasury/rules/other_expense/events.py +21 -0
  83. yearn_treasury/rules/other_expense/match_on_to_address.yaml +3 -2
  84. yearn_treasury/rules/other_expense/misc.cp310-win_amd64.pyd +0 -0
  85. yearn_treasury/rules/other_expense/misc.py +49 -0
  86. yearn_treasury/rules/other_expense/revshare.cp310-win_amd64.pyd +0 -0
  87. yearn_treasury/rules/other_expense/revshare.py +20 -0
  88. yearn_treasury/rules/other_income/__init__.cp310-win_amd64.pyd +0 -0
  89. yearn_treasury/rules/other_income/__init__.py +2 -77
  90. yearn_treasury/rules/other_income/airdrops.cp310-win_amd64.pyd +0 -0
  91. yearn_treasury/rules/other_income/airdrops.py +30 -0
  92. yearn_treasury/rules/other_income/match_on_hash.yaml +2 -0
  93. yearn_treasury/rules/other_income/misc.cp310-win_amd64.pyd +0 -0
  94. yearn_treasury/rules/other_income/misc.py +80 -0
  95. yearn_treasury/rules/revenue/__init__.py +2 -0
  96. yearn_treasury/rules/revenue/bribes.cp310-win_amd64.pyd +0 -0
  97. yearn_treasury/rules/revenue/farming.cp310-win_amd64.pyd +0 -0
  98. yearn_treasury/rules/revenue/farming.py +56 -0
  99. yearn_treasury/rules/revenue/keepcoins.cp310-win_amd64.pyd +0 -0
  100. yearn_treasury/rules/revenue/seasolver.cp310-win_amd64.pyd +0 -0
  101. yearn_treasury/rules/revenue/seasolver.py +7 -1
  102. yearn_treasury/rules/revenue/vaults.py +90 -28
  103. yearn_treasury/rules/revenue/yteams.cp310-win_amd64.pyd +0 -0
  104. yearn_treasury/rules/revenue/yteams.py +17 -0
  105. yearn_treasury/shitcoins.py +93 -2
  106. yearn_treasury/vaults.cp310-win_amd64.pyd +0 -0
  107. yearn_treasury/vaults.py +17 -4
  108. yearn_treasury/wallets.yaml +54 -0
  109. yearn_treasury/yteams.py +208 -0
  110. yearn_treasury-0.0.48.dist-info/METADATA +86 -0
  111. yearn_treasury-0.0.48.dist-info/RECORD +128 -0
  112. yearn_treasury-0.0.48.dist-info/top_level.txt +2 -0
  113. yearn_treasury__mypyc.cp310-win_amd64.pyd +0 -0
  114. f79b89f5f6693162015b__mypyc.cp310-win_amd64.pyd +0 -0
  115. yearn_treasury/rules/ignore/__init__.cp310-win_amd64.pyd +0 -0
  116. yearn_treasury/rules/ignore/maker.cp310-win_amd64.pyd +0 -0
  117. yearn_treasury/rules/ignore/staking.cp310-win_amd64.pyd +0 -0
  118. yearn_treasury/rules/ignore/swaps/__init__.cp310-win_amd64.pyd +0 -0
  119. yearn_treasury/rules/ignore/swaps/_skip_tokens.cp310-win_amd64.pyd +0 -0
  120. yearn_treasury/rules/ignore/swaps/aave.cp310-win_amd64.pyd +0 -0
  121. yearn_treasury/rules/ignore/swaps/compound.cp310-win_amd64.pyd +0 -0
  122. yearn_treasury/rules/ignore/swaps/uniswap.cp310-win_amd64.pyd +0 -0
  123. yearn_treasury-0.0.12.dist-info/METADATA +0 -69
  124. yearn_treasury-0.0.12.dist-info/RECORD +0 -76
  125. yearn_treasury-0.0.12.dist-info/top_level.txt +0 -2
  126. {yearn_treasury-0.0.12.dist-info → yearn_treasury-0.0.48.dist-info}/WHEEL +0 -0
  127. {yearn_treasury-0.0.12.dist-info → yearn_treasury-0.0.48.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,13 @@
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
- from dao_treasury import expense
10
+ from dao_treasury import TreasuryTx, expense
4
11
 
5
12
 
6
13
  infrastructure: Final = expense("Infrastructure")
@@ -15,3 +22,26 @@ infrastructure("Tenderly Subscription").match(
15
22
  infrastructure("Wonderland Jobs").match(
16
23
  symbol="DAI", to_address="0x8bA72884984f669aBBc9a5a7b441AD8E3D9a4fD3"
17
24
  )
25
+
26
+
27
+ @infrastructure("Unspecified Infra")
28
+ def is_generic_infra(tx: TreasuryTx) -> bool:
29
+ txhash = tx.hash
30
+ if tx.symbol == "DAI" and txhash in {
31
+ "0x0c59e87027bcdcaa718e322a28bc436106d73ae8623071930437bdb0706c4d65",
32
+ "0x47035f156d4e6144c144b2ac5e91497e353c9a4e23133587bbf3da2f9d7da596",
33
+ "0x40352e7166bf5196aa1160302cfcc157facf99731af0e11741b8729dd84e131c",
34
+ "0xc269f6fb016a48fe150f689231a73532b631877d1376608df639dad79514904b",
35
+ }:
36
+ return True
37
+
38
+ other = {
39
+ "0x08ef1aacdf7d0f16be5e6fd0a64ebd0ba3b0c3dd0a7884a9a470aa89a7fe1a06": 222,
40
+ "0xeb51cb5a3b4ae618be75bf3e23c2d8e333d93d5e81e869eca7f9612a30079822": 195,
41
+ "0x3e75d22250d87c183824c3b77ddb9cb11935db2061ce7f34df4f024d0646fcfb": 117,
42
+ "0x1621ba5c9b57930c97cc43d5d6d401ee9c69fed435b0b458ee031544a10bfa75": 460,
43
+ "0x5deca5d6c934372c174bbef8be9a1e103e06d8b93fd3bf8d77865dfeb34fe3be": 98,
44
+ "0xfc07ee04d44f8e481f58339b7b8c998d454e4ec427b8021c4e453c8eeee6a9b9": 207,
45
+ }
46
+
47
+ return txhash in other and tx.log_index == other[txhash]
@@ -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,11 +1,20 @@
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
4
12
  from y import Network
5
13
 
6
14
 
7
- security: Final = expense("Secutiry")
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"
@@ -1,6 +1,8 @@
1
1
  from yearn_treasury.rules.ignore.general import *
2
2
  from yearn_treasury.rules.ignore.maker import *
3
+ from yearn_treasury.rules.ignore.passthru import *
3
4
  from yearn_treasury.rules.ignore.staking import *
4
5
  from yearn_treasury.rules.ignore.swaps import *
6
+ from yearn_treasury.rules.ignore.unit import *
5
7
  from yearn_treasury.rules.ignore.weth import *
6
8
  from yearn_treasury.rules.ignore.ygov import *
@@ -4,7 +4,8 @@ from dao_treasury import TreasuryTx, ignore
4
4
  @ignore("Returned Funds")
5
5
  def is_returned_fundus(tx: TreasuryTx) -> bool:
6
6
  """A user accientally refunded their yield to yChad, yChad sent it back."""
7
- return tx.hash == "0x2c2fb9d88a7a25b100ae3ba08bdb1cafbbd6a63386a08fdcfe32d077836defa3" or (
8
- tx.hash == "0xd7e7abe600aad4a3181a3a410bef2539389579d2ed28f3e75dbbf3a7d8613688"
7
+ txhash = tx.hash
8
+ return txhash == "0x2c2fb9d88a7a25b100ae3ba08bdb1cafbbd6a63386a08fdcfe32d077836defa3" or (
9
+ txhash == "0xd7e7abe600aad4a3181a3a410bef2539389579d2ed28f3e75dbbf3a7d8613688"
9
10
  and tx.log_index == 556
10
11
  )
@@ -1,3 +1,11 @@
1
+ """
2
+ Ignore rules for Maker protocol.
3
+
4
+ This module defines matching logic for Maker protocol transactions,
5
+ including DAI minting/burning, DSR deposit/withdrawal, and CDP
6
+ deposit/withdrawal for YFI and USDC.
7
+ """
8
+
1
9
  from decimal import Decimal
2
10
  from typing import Final
3
11
 
@@ -10,10 +18,10 @@ DSPROXY: Final = "0xd42e1Cb8b98382df7Db43e0F09dFE57365659D16"
10
18
  DEPOSIT_EVENT_ARGS: Final = "ilk", "usr", "wad"
11
19
  WITHDRAWAL_EVENT_ARGS: Final = "cdp", "dst", "wad"
12
20
 
13
- maker = ignore("Maker")
14
- dai = maker("DAI")
15
- dsr = maker("DSR")
16
- cdp = maker("CDP")
21
+ maker: Final = ignore("Maker")
22
+ dai: Final = maker("DAI")
23
+ dsr: Final = maker("DSR")
24
+ cdp: Final = maker("CDP")
17
25
 
18
26
 
19
27
  dai("Minting").match(symbol="DAI", from_address=ZERO_ADDRESS)
@@ -26,83 +34,59 @@ dsr("Withdrawal").match(from_nickname="Contract: DsrManager")
26
34
 
27
35
  @cdp("YFI Deposit")
28
36
  def is_yfi_cdp_deposit(tx: TreasuryTx) -> bool:
29
- if (
30
- tx.symbol == "YFI"
31
- and TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
32
- and slip_in_events(tx)
37
+ if tx.symbol == "YFI" and TreasuryWallet.check_membership(
38
+ tx.from_address.address, tx.block # type: ignore [union-attr, arg-type]
33
39
  ):
34
- for event in tx._events["slip"]:
35
- if (
36
- all(arg in event for arg in DEPOSIT_EVENT_ARGS)
37
- and Decimal(event["wad"]) / 10**18 == tx.amount
38
- ):
39
- return True
40
+ for event in tx.get_events("slip"):
41
+ if all(arg in event for arg in DEPOSIT_EVENT_ARGS):
42
+ # TODO: remove this rounding once we move to postgres
43
+ scaled = round(Decimal(event["wad"]) / 10**18, 12)
44
+ rounded = round(tx.amount, 12)
45
+ if scaled == rounded:
46
+ return True
47
+ print(f"yfi cdp deposit amount no match [{scaled}, {rounded}]")
40
48
  return False
41
49
 
42
50
 
43
51
  @cdp("YFI Withdrawal")
44
52
  def is_yfi_cdp_withdrawal(tx: TreasuryTx) -> bool:
45
- if (
46
- tx.symbol == "YFI"
47
- and TreasuryWallet._get_instance(tx.to_address.address) # type: ignore [union-attr, arg-type]
48
- and flux_in_events(tx)
53
+ if tx.symbol == "YFI" and TreasuryWallet.check_membership(
54
+ tx.to_address.address, tx.block # type: ignore [union-attr, arg-type]
49
55
  ):
50
- for event in tx._events["flux"]:
51
- if (
52
- all(arg in event for arg in WITHDRAWAL_EVENT_ARGS)
53
- and Decimal(event["wad"]) / 10**18 == tx.amount
54
- ):
55
- return True
56
+ for event in tx.get_events("flux"):
57
+ if all(arg in event for arg in WITHDRAWAL_EVENT_ARGS):
58
+ # TODO: remove this rounding once we move to postgres
59
+ scaled = round(Decimal(event["wad"]) / 10**18, 12)
60
+ rounded = round(tx.amount, 12)
61
+ if scaled == rounded:
62
+ return True
63
+ print(f"yfi cdp withdrawal amount no match [{scaled}, {rounded}]")
56
64
  return False
57
65
 
58
66
 
59
67
  @cdp("USDC Deposit")
60
68
  def is_usdc_cdp_deposit(tx: TreasuryTx) -> bool:
61
- if (
62
- tx.symbol == "USDC"
63
- and TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
64
- and slip_in_events(tx)
69
+ if tx.symbol == "USDC" and TreasuryWallet.check_membership(
70
+ tx.from_address.address, tx.block # type: ignore [union-attr, arg-type]
65
71
  ):
66
- for event in tx._events["slip"]:
67
- if (
68
- all(arg in event for arg in DEPOSIT_EVENT_ARGS)
69
- and Decimal(event["wad"]) / 10**18 == tx.amount
70
- ):
71
- return True
72
+ for event in tx.get_events("slip"):
73
+ if all(arg in event for arg in DEPOSIT_EVENT_ARGS):
74
+ scaled = Decimal(event["wad"]) / 10**18
75
+ if scaled == tx.amount:
76
+ return True
77
+ print(f"usdc cdp deposit amount no match [{scaled}, {tx.amount}]")
72
78
  return False
73
79
 
74
80
 
75
81
  @cdp("USDC Withdrawal")
76
82
  def is_usdc_cdp_withdrawal(tx: TreasuryTx) -> bool:
77
- if (
78
- tx.symbol == "USDC"
79
- and TreasuryWallet._get_instance(tx.to_address.address) # type: ignore [union-attr, arg-type]
80
- and flux_in_events(tx)
83
+ if tx.symbol == "USDC" and TreasuryWallet.check_membership(
84
+ tx.to_address.address, tx.block # type: ignore [union-attr, arg-type]
81
85
  ):
82
- for event in tx._events["flux"]:
83
- if (
84
- all(arg in event for arg in WITHDRAWAL_EVENT_ARGS)
85
- and Decimal(event["wad"]) / 10**18 == tx.amount
86
- ):
87
- return True
86
+ for event in tx.get_events("flux"):
87
+ if all(arg in event for arg in WITHDRAWAL_EVENT_ARGS):
88
+ scaled = Decimal(event["wad"]) / 10**18
89
+ if scaled == tx.amount:
90
+ return True
91
+ print(f"usdc cdp withdrawal amount no match [{scaled}, {tx.amount}]")
88
92
  return False
89
-
90
-
91
- def flux_in_events(tx: TreasuryTx) -> bool:
92
- try:
93
- return "flux" in tx._events
94
- except KeyError as e:
95
- # This happens sometimes due to a busted abi and shouldnt impact us
96
- if str(e) == "'components'":
97
- return False
98
- raise
99
-
100
-
101
- def slip_in_events(tx: TreasuryTx) -> bool:
102
- try:
103
- return "slip" in tx._events
104
- except KeyError as e:
105
- # This happens sometimes due to a busted abi and shouldnt impact us
106
- if str(e) == "'components'":
107
- return False
108
- raise
@@ -0,0 +1,310 @@
1
+ from decimal import Decimal
2
+ from typing import Final, Tuple
3
+
4
+ from dao_treasury import TreasuryTx, TreasuryWallet, ignore
5
+ from eth_typing import BlockNumber, ChecksumAddress
6
+ from y import Network
7
+
8
+ from yearn_treasury.constants import CHAINID, YSWAP_MULTISIG, ZERO_ADDRESS
9
+
10
+
11
+ passthru: Final = ignore("Pass-Thru")
12
+
13
+ cowswap_router: Final = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
14
+ ycrv: Final = "0xFCc5c47bE19d06BF83eB04298b026F81069ff65b"
15
+
16
+
17
+ @passthru("Sent to dinobots to dump", Network.Mainnet)
18
+ def is_sent_to_dinoswap(tx: TreasuryTx) -> bool:
19
+ """These tokens are dumpped and the proceeds sent back to the origin strategy."""
20
+ return tx.from_nickname == "Contract: Strategy" and tx.to_nickname == "yMechs Multisig"
21
+
22
+
23
+ @passthru("Bribes for yCRV", Network.Mainnet)
24
+ def is_ycrv(tx: TreasuryTx) -> bool:
25
+ """These are routed thru cowswap with dai as the purchase token."""
26
+ ymechs = "0x2C01B4AD51a67E2d8F02208F54dF9aC4c0B778B6"
27
+
28
+ from_address = tx.from_address
29
+ symbol = tx.symbol
30
+ if (from_address == YSWAP_MULTISIG and symbol == "DAI") or (
31
+ from_address == ymechs and symbol == "3Crv"
32
+ ):
33
+ if tx.to_address == cowswap_router:
34
+ for trade in tx.get_events("Trade"):
35
+ (
36
+ owner,
37
+ sell_token,
38
+ buy_token,
39
+ sell_amount,
40
+ buy_amount,
41
+ fee_amount,
42
+ order_uid,
43
+ ) = trade.values()
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}]")
50
+
51
+ elif tx.hash in {
52
+ # one off exception case to correct accounting mix-up
53
+ "0x1578f3b0d3158d305167c39dc29ada08914e1ddb67ef9698e1b0421432f9aed6",
54
+ # A few donations from ySwap
55
+ "0xb2e335500b33b42edd8a97f57db35e0561df9a3a811d0cd73dce9767c23da0c4",
56
+ "0xc02aab3a84b3bbfbc18f0ee6aa742f233d97511f653b4a40e7cd8f822851e10a",
57
+ "0x8a2dba62eac44fdfc7ff189016ac601c9da664f5dea42d647f2e552319db2f7d",
58
+ "0xd2c0a137d03811c5e4c27be19c7893f7fdd5851bdd6f825ee7301f3634033035",
59
+ }:
60
+ return True
61
+ return is_dola_bribe(tx)
62
+
63
+
64
+ def is_dola_bribe(tx: TreasuryTx) -> bool:
65
+ return (
66
+ tx.from_nickname == "ySwap Multisig"
67
+ and tx.to_nickname == "Contract: GPv2Settlement"
68
+ and tx.symbol == "DOLA"
69
+ )
70
+
71
+
72
+ passthru("BAL Rewards", Network.Mainnet).match(
73
+ hash="0xf4677cce1a08ecd54272cdc1b23bc64693450f8bb5d6de59b8e58e288ec3b2a7",
74
+ symbol="BAL",
75
+ )
76
+
77
+
78
+ @passthru("StrategyAuraUSDClonable", Network.Mainnet)
79
+ def is_aura(tx: TreasuryTx) -> bool:
80
+ txhash = tx.hash
81
+ if (
82
+ txhash == "0x1621ba5c9b57930c97cc43d5d6d401ee9c69fed435b0b458ee031544a10bfa75"
83
+ and tx.symbol in ["BAL", "AURA"]
84
+ ):
85
+ return True
86
+ return (
87
+ txhash == "0x996b5911a48319133f50f72904e70ed905c08c81e2c03770e0ccc896be873bd4"
88
+ and tx.symbol == "AURA"
89
+ )
90
+
91
+
92
+ @passthru("yPrisma Strategy Migration", Network.Mainnet)
93
+ def is_yprisma_migration(tx: TreasuryTx) -> bool:
94
+ # strategies were changed a few times
95
+ txhash = tx.hash
96
+ if txhash in (
97
+ "0x4c19259ff9e23c2f23653b7560526c2dbd5adef2d53c297b63d8c1fa6f4906f1",
98
+ "0xed39b66c01e25b053117778c80e544c985d962522233b49ce6f7fe136b1a4474",
99
+ ):
100
+ return True
101
+ return (
102
+ txhash == "0x45bb5d7c25393c5bb8ad9647ae60ff39ddc39d695f0e427eb45f91b04f42c636"
103
+ and tx.symbol == "yPRISMA"
104
+ )
105
+
106
+
107
+ @passthru("rKP3R", Network.Mainnet)
108
+ def is_rkp3r(tx: TreasuryTx) -> bool:
109
+ if tx.symbol == "rKP3R":
110
+ from_nickname = tx.from_nickname
111
+ to_nickname = tx.to_nickname
112
+ if (
113
+ from_nickname == "Contract: StrategyConvexFixedForexClonable"
114
+ and to_nickname == "Yearn yChad Multisig"
115
+ ):
116
+ return True
117
+ elif (
118
+ from_nickname == "Yearn yChad Multisig"
119
+ and to_nickname == "Contract: StrategyConvexFixedForexClonable"
120
+ ):
121
+ return True
122
+ return False
123
+
124
+
125
+ @passthru("Inverse-earned YearnFed Fees", Network.Mainnet)
126
+ def is_inverse_fees_from_yearn_fed(tx: TreasuryTx) -> bool:
127
+ return tx.symbol == "yvDOLA-U" and tx.to_nickname == "Contract: YearnFed"
128
+
129
+
130
+ @passthru("stkAAVE", Network.Mainnet)
131
+ def is_stkaave(tx: TreasuryTx) -> bool:
132
+ """stkAAVE is sent from a strategy to ychad, then to sms for unwrapping."""
133
+ if tx.symbol == "stkAAVE":
134
+ from_nickname = tx.from_nickname
135
+ to_nickname = tx.to_nickname
136
+ if "Strategy" in from_nickname and to_nickname == "Yearn yChad Multisig":
137
+ return True
138
+ elif from_nickname == "Yearn yChad Multisig" and to_nickname == "Yearn Strategist Multisig":
139
+ return True
140
+ return False
141
+
142
+
143
+ @passthru("StrategyIdle", Network.Mainnet)
144
+ def is_idle(tx: TreasuryTx) -> bool:
145
+ return tx.hash in {
146
+ "0x59595773ee4304ba4e7e06d2c02541781d93867f74c6c83056e7295b684036c7",
147
+ "0x4c7685aa3dfa9f375c612a2773951b9edbe059102b505423ed28a97d2692e75a",
148
+ "0xb17317686b57229aeb7f06103097b47dc2eafa34489c40af70d2ac57bcf8f455",
149
+ "0xfd9e6fd303fdbb358207bf3ba069b7f6a21f82f6b082605056d54948127e81e8",
150
+ "0x41c8428fd361c54bb80cdac752e31622915ac626dd1e9270f02af1dc2c84d1f9",
151
+ "0x9c0d169c7362a7fe436ae852c1aee58a5905d10569abbd50261f65cb0574dc3a",
152
+ "0x55d89a5890cfe80da06f6831fdfa3a366c0ed9cf9b7f1b4d53f5007bb9698fa0",
153
+ "0x6c6fc43dca1841af82b517bc5fc53ea8607e3f95512e4dd3009c0dbb425669f7",
154
+ }
155
+
156
+
157
+ _factory_strat_to_yield_tokens = {
158
+ "Contract: StrategyCurveBoostedFactoryClonable": ("CRV", "LDO"),
159
+ "Contract: StrategyConvexFactoryClonable": ("CRV", "CVX"),
160
+ "Contract: StrategyConvexFraxFactoryClonable": ("CRV", "CVX", "FXS"),
161
+ }
162
+
163
+
164
+ @passthru("Factory Vault Yield", Network.Mainnet)
165
+ def is_factory_vault_yield(tx: TreasuryTx) -> bool:
166
+ return tx.to_nickname == "yMechs Multisig" and tx.symbol in _factory_strat_to_yield_tokens.get(
167
+ tx.from_nickname, ()
168
+ )
169
+
170
+
171
+ @passthru("CowSwap Migration", Network.Mainnet)
172
+ def is_cowswap_migration(tx: TreasuryTx) -> bool:
173
+ """A one-time tx that transferred tokens from an old contract to its replacement."""
174
+ return tx.hash == "0xb50341d3db2ff4a39b9bfa21753893035554ae44abb7d104ab650753db1c4855"
175
+
176
+
177
+ def is_curve_bribe(tx: TreasuryTx) -> bool:
178
+ """All present and future curve bribes are committed to yveCRV holders."""
179
+ from_nickname = tx.from_nickname
180
+ if from_nickname == "Contract: CurveYCRVVoter" and tx.hash not in [
181
+ # took place before bribes were committed to yveCRV
182
+ "0x6824345c8a0c1f0b801d8050bb6f587032c4a9fa153faa113d723a2068d844f4",
183
+ # was a whitehat hack of the v1 bribe contract, necessary to safeguard user funds
184
+ "0xfcef3911809770fe351b2b526e4ee0274589a3f7d6ef9408a8f5643fa006b771",
185
+ ]:
186
+ return True
187
+
188
+ # Bribe V3
189
+ elif from_nickname == "Contract: yBribe" and tx.to_nickname in [
190
+ "Yearn Treasury",
191
+ "ySwap Multisig",
192
+ ]:
193
+ return True
194
+
195
+ # NOTE: I added this one-off to capture tokens sent to BribeSplitter 0x527e80008D212E2891C737Ba8a2768a7337D7Fd2
196
+ return tx.hash == "0xce45da7e3a7616ed0c0d356d6dfa8a784606c9a8034bae9faa40abf7b52be114"
197
+
198
+
199
+ _pass_thru_hashes: Tuple[str, ...] = {
200
+ Network.Mainnet: ("0xf662c68817c56a64b801181a3175c8a7e7a5add45f8242990c695d418651e50d",),
201
+ Network.Fantom: (
202
+ "0x411d0aff42c3862d06a0b04b5ffd91f4593a9a8b2685d554fe1fbe5dc7e4fc04",
203
+ "0xa347da365286cc912e4590fc71e97a5bcba9e258c98a301f85918826538aa021",
204
+ ),
205
+ }.get(
206
+ CHAINID, ()
207
+ ) # type: ignore [call-overload]
208
+
209
+
210
+ @passthru("Misc.", Network.Mainnet)
211
+ def is_misc_passthru_mainnet(tx: TreasuryTx) -> bool:
212
+ # not sure if we still need this, TODO figure it out
213
+ # NOTE skipped the hashmatcher to do strange things ... there is probably a better way to do this
214
+ if tx.hash in _pass_thru_hashes and str(tx.log_index).lower() != "nan":
215
+ return True
216
+
217
+ txhash = tx.hash
218
+ if txhash in {
219
+ "0xae6797ad466de75731117df46ccea5c263265dd6258d596b9d6d8cf3a7b1e3c2",
220
+ "0x2a6191ba8426d3ae77e2a6c91de10a6e76d1abdb2d0f831c6c5aad52be3d6246",
221
+ # https://github.com/yearn/chief-multisig-officer/pull/924
222
+ "0x25b54e113e58a3a4bbffc011cdfcb8c07a0424f33b0dbda921803d82b88f1429",
223
+ "0xcb000dd2b623f9924fe0234831800950a3269b2d412ce9eeabb0ec65cd737059",
224
+ }:
225
+ return True
226
+ # do these need hueristics? build real sort logic if these keep reoccurring
227
+ elif txhash == "0x14faeac8ee0734875611e68ce0614eaf39db94a5ffb5bc6f9739da6daf58282a" and (
228
+ tx.symbol in ("CRV", "CVX", "yPRISMA") or tx.log_index == 254
229
+ ):
230
+ return True
231
+ return False
232
+
233
+
234
+ @passthru("Misc.", Network.Fantom)
235
+ def is_misc_passthru_fantom(tx: TreasuryTx) -> bool:
236
+ # not sure if we still need this, TODO figure it out
237
+ # NOTE skipped the hashmatcher to do strange things ... there is probably a better way to do this
238
+ if tx.hash in _pass_thru_hashes and str(tx.log_index).lower() != "nan":
239
+ return True
240
+
241
+ if tx.hash == "0x14faeac8ee0734875611e68ce0614eaf39db94a5ffb5bc6f9739da6daf58282a":
242
+ return True
243
+ # Passing thru to yvWFTM
244
+ if (
245
+ tx.symbol == "WFTM"
246
+ and TreasuryWallet.check_membership(tx.from_address.address, tx.block) # type: ignore [arg-type, union-attr]
247
+ and tx.to_address == "0x0DEC85e74A92c52b7F708c4B10207D9560CEFaf0"
248
+ ):
249
+ # dont want to accidenally sort a vault deposit here
250
+ is_deposit = False
251
+ for event in tx.get_events("Transfer"):
252
+ sender, receiver, _ = event.values()
253
+ if (
254
+ tx.to_address == event.address
255
+ and sender == ZERO_ADDRESS
256
+ and tx.from_address == receiver
257
+ ):
258
+ is_deposit = True
259
+ if not is_deposit:
260
+ return True
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
+ )
@@ -39,9 +39,8 @@ async def is_solidex_staking(tx: TreasuryTx) -> bool:
39
39
  if (
40
40
  TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
41
41
  and tx.to_address == lp_depositor
42
- and "Deposited" in tx._events
43
42
  ):
44
- for event in tx._events["Deposited"]:
43
+ for event in tx.get_events("Deposited"):
45
44
  if (
46
45
  event.address == lp_depositor
47
46
  and "user" in event
@@ -53,12 +52,10 @@ async def is_solidex_staking(tx: TreasuryTx) -> bool:
53
52
 
54
53
  # CLAIMING
55
54
  # Step 2: Get your claim tokens
56
- elif (
57
- tx.from_address == ZERO_ADDRESS
58
- and TreasuryWallet._get_instance(tx.to_address.address) # type: ignore [union-attr, arg-type]
59
- and "Deposited" in tx._events
55
+ elif tx.from_address == ZERO_ADDRESS and TreasuryWallet._get_instance(
56
+ tx.to_address.address # type: ignore [union-attr, arg-type]
60
57
  ):
61
- for event in tx._events["Deposited"]:
58
+ for event in tx.get_events("Deposited"):
62
59
  pool = await tx.token.contract.pool
63
60
  if (
64
61
  event.address == lp_depositor
@@ -74,12 +71,11 @@ async def is_solidex_staking(tx: TreasuryTx) -> bool:
74
71
  elif (
75
72
  TreasuryWallet._get_instance(tx.from_address.address) # type: ignore [union-attr, arg-type]
76
73
  and tx.to_address == ZERO_ADDRESS
77
- and "Withdrawn" in tx._events
78
74
  ):
79
75
  token = tx.token.contract
80
76
  if hasattr(token, "pool"):
81
77
  pool = await token.pool
82
- for event in tx._events["Withdrawn"]:
78
+ for event in tx.get_events("Withdrawn"):
83
79
  if (
84
80
  event.address == lp_depositor
85
81
  and "user" in event
@@ -90,12 +86,10 @@ async def is_solidex_staking(tx: TreasuryTx) -> bool:
90
86
  return True
91
87
 
92
88
  # Step 4: Unstake your tokens
93
- elif (
94
- tx.from_address == lp_depositor
95
- and TreasuryWallet._get_instance(tx.to_address.address) # type: ignore [union-attr, arg-type]
96
- and "Withdrawn" in tx._events
89
+ elif tx.from_address == lp_depositor and TreasuryWallet._get_instance(
90
+ tx.to_address.address # type: ignore [union-attr, arg-type]
97
91
  ):
98
- for event in tx._events["Withdrawn"]:
92
+ for event in tx.get_events("Withdrawn"):
99
93
  if (
100
94
  event.address == lp_depositor
101
95
  and "user" in event