From 3d733da1b2ec9b32c0e66a14a626b399670ddd96 Mon Sep 17 00:00:00 2001 From: Haris Angelidakis <64154020+harisang@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:38:50 +0300 Subject: [PATCH] Remove consistency rewards and dune parameters cleanup and fixing (#395) As part of cleaning up and restructuring some Dune queries (see relevant PRs [here](https://github.com/cowprotocol/dune-queries/pulls)), some parameters needed to be dropped or renamed. This branch is what we used to run the script for the payouts of Sep 3-10, 2024. Moreover, we decided to remove consistency rewards altogether from the main script, as we are moving away from those and it doesn't seem likely that they will come back. Note however that the main sql query that computes rewards, one of the core components of the script, is not changed and consistency rewards are still computed there. This will be addressed in a follow-up PR as that query is an important part of the script and thus we thought it is better to address it separately. --- src/constants.py | 9 --- src/fetch/dune.py | 26 ++----- src/fetch/payouts.py | 53 ++------------ src/models/accounting_period.py | 4 +- tests/unit/test_payouts.py | 122 ++++++++------------------------ 5 files changed, 41 insertions(+), 173 deletions(-) diff --git a/src/constants.py b/src/constants.py index 4ff3884d..d1d9d1e4 100644 --- a/src/constants.py +++ b/src/constants.py @@ -54,12 +54,3 @@ # Real Web3 Instance web3 = Web3(Web3.HTTPProvider(NODE_URL)) - -RECOGNIZED_BONDING_POOLS = [ - "('0x8353713b6D2F728Ed763a04B886B16aAD2b16eBD', 'Gnosis', " - "'0x6c642cafcbd9d8383250bb25f67ae409147f78b2')", - "('0x5d4020b9261F01B6f8a45db929704b0Ad6F5e9E6', 'CoW Services', " - "'0x423cec87f19f0778f549846e0801ee267a917935')", - "('0xC96569Dc132ebB6694A5f0b781B33f202Da8AcE8', 'Project Blanc', " - "'0xCa99e3Fc7B51167eaC363a3Af8C9A185852D1622')", -] diff --git a/src/fetch/dune.py b/src/fetch/dune.py index 62b75c0c..7d2e8edd 100644 --- a/src/fetch/dune.py +++ b/src/fetch/dune.py @@ -6,8 +6,6 @@ from dune_client.query import QueryBase from dune_client.types import QueryParameter, DuneRecord -from src.constants import RECOGNIZED_BONDING_POOLS -from src.fetch.token_list import get_trusted_tokens from src.logger import set_log from src.models.accounting_period import AccountingPeriod from src.queries import QUERIES, QueryData @@ -85,14 +83,12 @@ def get_vouches(self) -> list[DuneRecord]: """ Fetches & Returns Parsed Results for VouchRegistry query. """ - pool_values = ",\n".join(RECOGNIZED_BONDING_POOLS) return self._get_query_results( query=self._parameterized_query( query_data=QUERIES["VOUCH_REGISTRY"], params=[ - QueryParameter.date_type("EndTime", self.period.end), - QueryParameter.text_type("BondingPoolData", pool_values), - QueryParameter.enum_type("VOUCH_CTE_NAME", "valid_vouches"), + QueryParameter.date_type("end_time", self.period.end), + QueryParameter.enum_type("vouch_cte_name", "valid_vouches"), ], ) ) @@ -102,11 +98,7 @@ def get_period_slippage(self, job_id: Optional[str] = None) -> list[DuneRecord]: Executes & Fetches results of slippage query per solver for specified accounting period. Returns a class representation of the results as two lists (positive & negative). """ - token_list = get_trusted_tokens() - params = self._period_params() + [ - QueryParameter.text_type("TxHash", "0x"), - QueryParameter.text_type("TokenList", ",".join(token_list)), - ] + params = self._period_params() # trigger dashboard update self.dune.execute( self._parameterized_query(QUERIES["DASHBOARD_SLIPPAGE"], params=params) @@ -114,12 +106,7 @@ def get_period_slippage(self, job_id: Optional[str] = None) -> list[DuneRecord]: return self._get_query_results( self._parameterized_query( - QUERIES["PERIOD_SLIPPAGE"], - params=self._period_params() - + [ - QueryParameter.text_type("TxHash", "0x"), - QueryParameter.text_type("TokenList", ",".join(token_list)), - ], + QUERIES["PERIOD_SLIPPAGE"], params=self._period_params() ), job_id, ) @@ -128,12 +115,9 @@ def get_service_fee_status(self) -> list[DuneRecord]: """ Fetches & Returns Parsed Results for VouchRegistry query. """ - pool_values = ",\n".join(RECOGNIZED_BONDING_POOLS) return self._get_query_results( query=self._parameterized_query( query_data=QUERIES["SERVICE_FEE_STATUS"], - params=[ - QueryParameter.text_type("BondingPoolData", pool_values), - ], + params=[], ) ) diff --git a/src/fetch/payouts.py b/src/fetch/payouts.py index 46ec7a8d..a291393a 100644 --- a/src/fetch/payouts.py +++ b/src/fetch/payouts.py @@ -23,8 +23,6 @@ from src.pg_client import MultiInstanceDBFetcher from src.utils.print_store import Category -PERIOD_BUDGET_COW = 250000 * 10**18 -CONSISTENCY_REWARD_CAP_ETH = 6 * 10**18 QUOTE_REWARD_COW = 6 * 10**18 QUOTE_REWARD_CAP_ETH = 6 * 10**14 SERVICE_FEE_FACTOR = Fraction(15, 100) @@ -35,8 +33,6 @@ "solver", "primary_reward_eth", "primary_reward_cow", - "secondary_reward_eth", - "secondary_reward_cow", "quote_reward_cow", "protocol_fee_eth", "network_fee_eth", @@ -53,8 +49,6 @@ NUMERICAL_COLUMNS = [ "primary_reward_eth", "primary_reward_cow", - "secondary_reward_cow", - "secondary_reward_eth", "quote_reward_cow", "protocol_fee_eth", ] @@ -81,15 +75,12 @@ def __init__( # pylint: disable=too-many-arguments reward_target: Address, bonding_pool: Address, primary_reward_eth: int, - secondary_reward_eth: int, slippage_eth: int, primary_reward_cow: int, - secondary_reward_cow: int, quote_reward_cow: int, service_fee: bool, ): - assert secondary_reward_eth >= 0, "invalid secondary_reward_eth" - assert secondary_reward_cow >= 0, "invalid secondary_reward_cow" + assert quote_reward_cow >= 0, "invalid quote_reward_cow" self.solver = solver @@ -99,8 +90,6 @@ def __init__( # pylint: disable=too-many-arguments self.slippage_eth = slippage_eth self.primary_reward_eth = primary_reward_eth self.primary_reward_cow = primary_reward_cow - self.secondary_reward_eth = secondary_reward_eth - self.secondary_reward_cow = secondary_reward_cow self.quote_reward_cow = quote_reward_cow self.service_fee = service_fee @@ -114,7 +103,7 @@ def from_series(cls, frame: Series) -> RewardAndPenaltyDatum: ) solver = frame["solver"] reward_target = frame["reward_target"] - bonding_pool = frame["pool"] + bonding_pool = frame["pool_address"] if reward_target is None: logging.warning(f"solver {solver} without reward_target. Using solver") reward_target = solver @@ -127,8 +116,6 @@ def from_series(cls, frame: Series) -> RewardAndPenaltyDatum: slippage_eth=slippage, primary_reward_eth=int(frame["primary_reward_eth"]), primary_reward_cow=int(frame["primary_reward_cow"]), - secondary_reward_eth=int(frame["secondary_reward_eth"]), - secondary_reward_cow=int(frame["secondary_reward_cow"]), quote_reward_cow=int(frame["quote_reward_cow"]), service_fee=bool(frame["service_fee"]), ) @@ -139,17 +126,11 @@ def total_outgoing_eth(self) -> int: def total_cow_reward(self) -> int: """Total outgoing COW token reward""" - return int( - self.reward_scaling() - * (self.primary_reward_cow + self.secondary_reward_cow) - ) + return int(self.reward_scaling() * self.primary_reward_cow) def total_eth_reward(self) -> int: """Total outgoing ETH reward""" - return int( - self.reward_scaling() - * (self.primary_reward_eth + self.secondary_reward_eth) - ) + return int(self.reward_scaling() * self.primary_reward_eth) def reward_scaling(self) -> Fraction: """Scaling factor for service fee @@ -198,7 +179,7 @@ def as_payouts(self) -> list[Transfer]: # Note that; # reimbursement_eth + reward_eth # = self.total_eth_reward() + self.exec_cost + self.slippage_eth - # = self.payment_eth + self.secondary_reward_eth + self.slippage_eth + # = self.payment_eth + self.slippage_eth # = self.total_outgoing_eth() # >= 0 (because not self.is_overdraft()) try: @@ -288,32 +269,10 @@ def extend_payment_df(pdf: DataFrame, converter: TokenConversion) -> DataFrame: Extending the basic columns returned by SQL Query with some after-math: - reward_eth as difference of payment and execution_cost - reward_cow as conversion from ETH to cow. - - secondary_reward (as the remaining reward after all has been distributed) - This is evaluated in both ETH and COW (for different use cases). """ # Note that this can be negative! pdf["primary_reward_cow"] = pdf["primary_reward_eth"].apply(converter.eth_to_token) - secondary_allocation = max( - min( - PERIOD_BUDGET_COW - pdf["primary_reward_cow"].sum(), - converter.eth_to_token(CONSISTENCY_REWARD_CAP_ETH), - ), - 0, - ) - participation_total = pdf["num_participating_batches"].sum() - if participation_total == 0: - # Due to CIP-48 we will stop counting participation. This workaround avoids - # division by zero as the num_participation_batches is set to zero for all - # solvers after CIP-48. - participation_total = 1 - pdf["secondary_reward_cow"] = ( - secondary_allocation * pdf["num_participating_batches"] / participation_total - ) - pdf["secondary_reward_eth"] = pdf["secondary_reward_cow"].apply( - converter.token_to_eth - ) - # Pandas has poor support for large integers, must cast the constant to float here, # otherwise the dtype would be inferred as int64 (which overflows). pdf["quote_reward_cow"] = ( @@ -545,13 +504,11 @@ def construct_payouts( partner_fee_tax_wei = total_partner_fee_wei_untaxed - total_partner_fee_wei_taxed performance_reward = complete_payout_df["primary_reward_cow"].sum() - participation_reward = complete_payout_df["secondary_reward_cow"].sum() quote_reward = complete_payout_df["quote_reward_cow"].sum() dune.log_saver.print( "Payment breakdown (ignoring service fees):\n" f"Performance Reward: {performance_reward / 10 ** 18:.4f}\n" - f"Participation Reward: {participation_reward / 10 ** 18:.4f}\n" f"Quote Reward: {quote_reward / 10 ** 18:.4f}\n" f"Protocol Fees: {final_protocol_fee_wei / 10 ** 18:.4f}\n" f"Partner Fees Tax: {partner_fee_tax_wei / 10 ** 18:.4f}\n" diff --git a/src/models/accounting_period.py b/src/models/accounting_period.py index 8c7f7247..ac02a67e 100644 --- a/src/models/accounting_period.py +++ b/src/models/accounting_period.py @@ -33,8 +33,8 @@ def __hash__(self) -> int: def as_query_params(self) -> list[QueryParameter]: """Returns commonly used (StartTime, EndTime) query parameters""" return [ - QueryParameter.date_type("StartTime", self.start), - QueryParameter.date_type("EndTime", self.end), + QueryParameter.date_type("start_time", self.start), + QueryParameter.date_type("end_time", self.end), ] def dashboard_url(self) -> str: diff --git a/tests/unit/test_payouts.py b/tests/unit/test_payouts.py index 2802acad..e52f1c17 100644 --- a/tests/unit/test_payouts.py +++ b/tests/unit/test_payouts.py @@ -58,12 +58,7 @@ def setUp(self) -> None: -10000000000000000.00000, 0.00000, ] - self.batch_participation = [ - 7, - 2, - 7, - 6, - ] + self.protocol_fee_eth = [ 1000000000000000.0, 2000000000000000.0, @@ -86,7 +81,6 @@ def test_extend_payment_df(self): "solver": self.solvers, "num_quotes": self.num_quotes, "primary_reward_eth": self.primary_reward_eth, - "num_participating_batches": self.batch_participation, "protocol_fee_eth": self.protocol_fee_eth, "network_fee_eth": self.network_fee_eth, } @@ -101,7 +95,6 @@ def test_extend_payment_df(self): -10000000000000000.00000, 0.00000, ], - "num_participating_batches": self.batch_participation, "protocol_fee_eth": self.protocol_fee_eth, "network_fee_eth": self.network_fee_eth, "primary_reward_cow": [ @@ -110,18 +103,6 @@ def test_extend_payment_df(self): -10000000000000000000.0, 0.0, ], - "secondary_reward_cow": [ - 1909090909090909000000.00000, - 545454545454545440000.00000, - 1909090909090909000000.00000, - 1636363636363636200000.00000, - ], - "secondary_reward_eth": [ - 1909090909090909000.00000, - 545454545454545440.00000, - 1909090909090909000.00000, - 1636363636363636200.00000, - ], "quote_reward_cow": [ 0.00000, 0.00000, @@ -150,13 +131,10 @@ def test_validate_df_columns(self): { "solver": [], "payment_eth": [], - "num_participating_batches": [], "protocol_fee_eth": [], "network_fee_eth": [], "primary_reward_eth": [], "primary_reward_cow": [], - "secondary_reward_cow": [], - "secondary_reward_eth": [], "quote_reward_cow": [], } ) @@ -213,7 +191,6 @@ def test_construct_payouts(self): "solver": self.solvers, "num_quotes": self.num_quotes, "primary_reward_eth": self.primary_reward_eth, - "num_participating_batches": self.batch_participation, "protocol_fee_eth": self.protocol_fee_eth, "network_fee_eth": self.network_fee_eth, } @@ -250,7 +227,6 @@ def test_construct_payouts(self): "solver": self.solvers, "num_quotes": self.num_quotes, "primary_reward_eth": [600000000000000.0, 1.2e16, -1e16, 0.0], - "num_participating_batches": [7, 2, 7, 6], "protocol_fee_eth": [ 1000000000000000.0, 2000000000000000.0, @@ -269,18 +245,6 @@ def test_construct_payouts(self): -10000000000000000000.0, 0.0, ], - "secondary_reward_cow": [ - 1909090909090909000000.00000, - 545454545454545440000.00000, - 1909090909090909000000.00000, - 1636363636363636200000.00000, - ], - "secondary_reward_eth": [ - 1909090909090909000.00000, - 545454545454545440.00000, - 1909090909090909000.00000, - 1636363636363636200.00000, - ], "quote_reward_cow": [ 0.00000, 0.00000, @@ -321,18 +285,6 @@ def test_prepare_transfers(self): -10000000000000000000.0, 0.0, ], - "secondary_reward_cow": [ - 6.363636363636364e16, - 1.818181818181818e16, - 6.363636363636364e16, - 5.454545454545454e16, - ], - "secondary_reward_eth": [ - 63636363636363.0, - 18181818181818.0, - 63636363636363.0, - 54545454545454.0, - ], "quote_reward_cow": [ 0.00000, 0.00000, @@ -347,7 +299,7 @@ def test_prepare_transfers(self): "0x0000000000000000000000000000000000000007", "0x0000000000000000000000000000000000000008", ], - "pool": [ + "pool_address": [ "0x0000000000000000000000000000000000000025", "0x0000000000000000000000000000000000000026", "0x0000000000000000000000000000000000000026", @@ -376,12 +328,12 @@ def test_prepare_transfers(self): Transfer( token=Token(COW_TOKEN_ADDRESS), recipient=Address(self.reward_targets[0]), - amount_wei=663636363636363640, + amount_wei=600000000000000000, ), Transfer( token=Token(COW_TOKEN_ADDRESS), recipient=Address(self.reward_targets[1]), - amount_wei=12018181818181818180, + amount_wei=12000000000000000000, ), Transfer( token=Token(COW_TOKEN_ADDRESS), @@ -393,11 +345,6 @@ def test_prepare_transfers(self): recipient=Address(self.reward_targets[3]), amount_wei=int(180000000000000000000 * (1 - SERVICE_FEE_FACTOR)), ), - Transfer( - token=Token(COW_TOKEN_ADDRESS), - recipient=Address(self.reward_targets[3]), - amount_wei=int(54545454545454544 * (1 - SERVICE_FEE_FACTOR)), - ), Transfer( token=None, recipient=PROTOCOL_FEE_SAFE, @@ -413,7 +360,7 @@ def test_prepare_transfers(self): Overdraft( period, account=Address(self.solvers[2]), - wei=9936363636363638, + wei=10000000000000001, name="S_3", ) ], @@ -432,7 +379,6 @@ def setUp(self) -> None: def sample_record( self, primary_reward: int, - secondary_reward: int, slippage: int, num_quotes: int, service_fee: bool = False, @@ -445,36 +391,30 @@ def sample_record( bonding_pool=self.bonding_pool, primary_reward_eth=primary_reward, primary_reward_cow=primary_reward * self.conversion_rate, - secondary_reward_eth=secondary_reward, - secondary_reward_cow=secondary_reward * self.conversion_rate, slippage_eth=slippage, quote_reward_cow=QUOTE_REWARD_COW * num_quotes, service_fee=service_fee, ) def test_invalid_input(self): - """Test that negative and secondary and quote rewards throw an error.""" - - # invalid secondary reward - with self.assertRaises(AssertionError): - self.sample_record(0, -1, 0, 0) + """Test that negative and quote rewards throw an error.""" # invalid quote reward with self.assertRaises(AssertionError): - self.sample_record(0, 0, 0, -1) + self.sample_record(0, 0, -1) - def test_reward_datum_0_0_0_0(self): + def test_reward_datum_0_0_0(self): """Without data there is no payout and no overdraft.""" - test_datum = self.sample_record(0, 0, 0, 0) + test_datum = self.sample_record(0, 0, 0) self.assertFalse(test_datum.is_overdraft()) self.assertEqual(test_datum.as_payouts(), []) - def test_reward_datum_pm1_0_0_0(self): + def test_reward_datum_pm1_0_0(self): """Primary reward only.""" # positive reward is paid in COW primary_reward = 1 - test_datum = self.sample_record(primary_reward, 0, 0, 0) + test_datum = self.sample_record(primary_reward, 0, 0) self.assertFalse(test_datum.is_overdraft()) self.assertEqual( test_datum.as_payouts(), @@ -489,16 +429,16 @@ def test_reward_datum_pm1_0_0_0(self): # negative reward gives overdraft primary_reward = -1 - test_datum = self.sample_record(primary_reward, 0, 0, 0) + test_datum = self.sample_record(primary_reward, 0, 0) self.assertTrue(test_datum.is_overdraft()) self.assertEqual(test_datum.as_payouts(), []) - def test_reward_datum_0_0_pm1_0(self): + def test_reward_datum_0_pm1_0(self): """Slippag only.""" # positive slippage is paid in ETH slippage = 1 - test_datum = self.sample_record(0, 0, slippage, 0) + test_datum = self.sample_record(0, slippage, 0) self.assertFalse(test_datum.is_overdraft()) self.assertEqual( test_datum.as_payouts(), @@ -507,14 +447,14 @@ def test_reward_datum_0_0_pm1_0(self): # negative slippage gives overdraft slippage = -1 - test_datum = self.sample_record(0, 0, slippage, 0) + test_datum = self.sample_record(0, slippage, 0) self.assertTrue(test_datum.is_overdraft()) self.assertEqual(test_datum.as_payouts(), []) - def test_reward_datum_0_0_0_1(self): + def test_reward_datum_0_0_1(self): """Quote rewards only.""" num_quotes = 1 - test_datum = self.sample_record(0, 0, 0, num_quotes) + test_datum = self.sample_record(0, 0, num_quotes) self.assertFalse(test_datum.is_overdraft()) self.assertEqual( test_datum.as_payouts(), @@ -527,10 +467,10 @@ def test_reward_datum_0_0_0_1(self): ], ) - def test_reward_datum_4_2_1_0(self): + def test_reward_datum_4_1_0(self): """COW payment for rewards, ETH payment for slippage.""" - primary_reward, secondary_reward, slippage, num_quotes = 4, 2, 1, 0 - test_datum = self.sample_record(primary_reward, secondary_reward, slippage, 0) + primary_reward, slippage, num_quotes = 4, 1, 0 + test_datum = self.sample_record(primary_reward, slippage, 0) self.assertFalse(test_datum.is_overdraft()) self.assertEqual( test_datum.as_payouts(), @@ -543,16 +483,15 @@ def test_reward_datum_4_2_1_0(self): Transfer( token=self.cow_token, recipient=self.reward_target, - amount_wei=(primary_reward + secondary_reward) - * self.conversion_rate, + amount_wei=(primary_reward) * self.conversion_rate, ), ], ) def test_reward_datum_slippage_reduces_reward(self): """Negative slippage reduces COW reward.""" - primary_reward, secondary_reward, slippage, num_quotes = 4, 2, -1, 0 - test_datum = self.sample_record(primary_reward, secondary_reward, slippage, 0) + primary_reward, slippage, num_quotes = 4, -1, 0 + test_datum = self.sample_record(primary_reward, slippage, 0) self.assertFalse(test_datum.is_overdraft()) self.assertEqual( test_datum.as_payouts(), @@ -560,26 +499,25 @@ def test_reward_datum_slippage_reduces_reward(self): Transfer( token=self.cow_token, recipient=self.reward_target, - amount_wei=(primary_reward + secondary_reward + slippage) - * self.conversion_rate, + amount_wei=(primary_reward + slippage) * self.conversion_rate, ), ], ) def test_reward_datum_slippage_exceeds_reward(self): """Negative slippage leads to overtraft.""" - primary_reward, participation, slippage = 1, 2, -4 - test_datum = self.sample_record(primary_reward, participation, slippage, 0) + primary_reward, slippage = 1, -4 + test_datum = self.sample_record(primary_reward, slippage, 0) self.assertTrue(test_datum.is_overdraft()) self.assertEqual(test_datum.as_payouts(), []) def test_reward_datum_reward_reduces_slippage(self): """Negative reward reduces ETH slippage payment.""" - primary_reward, secondary_reward, slippage = -2, 1, 3 - test_datum = self.sample_record(primary_reward, secondary_reward, slippage, 0) + primary_reward, slippage = -2, 3 + test_datum = self.sample_record(primary_reward, slippage, 0) self.assertEqual( test_datum.total_outgoing_eth(), - primary_reward + secondary_reward + slippage, + primary_reward + slippage, ) self.assertEqual( test_datum.as_payouts(), @@ -597,7 +535,6 @@ def test_performance_reward_service_fee(self): primary_reward, num_quotes, service_fee = 100, 0, True test_datum = self.sample_record( primary_reward=primary_reward, - secondary_reward=0, slippage=0, num_quotes=num_quotes, service_fee=service_fee, @@ -620,7 +557,6 @@ def test_quote_reward_service_fee(self): primary_reward, num_quotes, service_fee = 0, 100, True test_datum = self.sample_record( primary_reward=primary_reward, - secondary_reward=0, slippage=0, num_quotes=num_quotes, service_fee=service_fee,