Skip to content

Commit

Permalink
add protocol fees to transfers
Browse files Browse the repository at this point in the history
- new protocol_fee_eth field in payouts
- subtract protocol fee from eth reimbursement
- update tests: add one new test, update old tests (also for quote rewards)
  • Loading branch information
fhenneke committed Jan 19, 2024
1 parent 604b4a4 commit 34462be
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 19 deletions.
10 changes: 8 additions & 2 deletions src/fetch/payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"reward_cow",
"secondary_reward_cow",
"quote_reward_cow",
"protocol_fee_eth",
}
SLIPPAGE_COLUMNS = {
"solver",
Expand Down Expand Up @@ -79,10 +80,13 @@ def __init__( # pylint: disable=too-many-arguments
primary_reward_cow: int,
secondary_reward_cow: int,
quote_reward_cow: int,
protocol_fee_eth: int,
):
assert exec_cost >= 0, f"invalid execution cost {exec_cost}"
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"
assert protocol_fee_eth >= 0, "invalid protocol_fee_eth"

self.solver = solver
self.solver_name = solver_name
Expand All @@ -94,6 +98,7 @@ def __init__( # pylint: disable=too-many-arguments
self.secondary_reward_eth = secondary_reward_eth
self.secondary_reward_cow = secondary_reward_cow
self.quote_reward_cow = quote_reward_cow
self.protocol_fee_eth = protocol_fee_eth

@classmethod
def from_series(cls, frame: Series) -> RewardAndPenaltyDatum:
Expand All @@ -120,11 +125,12 @@ def from_series(cls, frame: Series) -> RewardAndPenaltyDatum:
secondary_reward_eth=int(frame["secondary_reward_eth"]),
secondary_reward_cow=int(frame["secondary_reward_cow"]),
quote_reward_cow=int(frame["quote_reward_cow"]),
protocol_fee_eth=int(frame["protocol_fee_eth"]),
)

def total_outgoing_eth(self) -> int:
"""Total outgoing amount (including slippage) for the payout."""
return self.payment_eth + self.secondary_reward_eth + self.slippage_eth
return self.payment_eth + self.secondary_reward_eth + self.slippage_eth - self.protocol_fee_eth

def total_cow_reward(self) -> int:
"""Total outgoing COW token reward"""
Expand Down Expand Up @@ -161,7 +167,7 @@ def as_payouts(self) -> list[Transfer]:
total_eth_reward = int(self.total_eth_reward())
total_cow_reward = int(self.total_cow_reward())

reimbursement_eth = int(self.exec_cost + self.slippage_eth)
reimbursement_eth = int(self.exec_cost + self.slippage_eth - self.protocol_fee_eth)
# We do not have access to token conversion here, but we do have other converted values
# x_eth:x_cow = y_eth:y_cow --> y_cow = y_eth * x_cow / x_eth
reimbursement_cow = (
Expand Down
78 changes: 61 additions & 17 deletions tests/unit/test_payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ def setUp(self) -> None:
7,
6,
]
self.protocol_fees = [
300000000000000.0,
200000000000000.0,
100000000000000.0,
0.0,
]
# Mocking TokenConversion!
self.mock_converter = TokenConversion(
eth_to_token=lambda t: int(t * 1000), token_to_eth=lambda t: t // 1000
Expand All @@ -79,6 +85,7 @@ def test_extend_payment_df(self):
"payment_eth": self.eth_payments,
"execution_cost_eth": self.execution_costs,
"num_participating_batches": self.batch_participation,
"protocol_fee_eth": self.protocol_fees
}
base_payout_df = DataFrame(base_data_dict)
result = extend_payment_df(base_payout_df, converter=self.mock_converter)
Expand All @@ -88,6 +95,7 @@ def test_extend_payment_df(self):
"payment_eth": self.eth_payments,
"execution_cost_eth": self.execution_costs,
"num_participating_batches": self.batch_participation,
"protocol_fee_eth": self.protocol_fees,
"reward_eth": [
-200000000000000.00000,
10000000000000000.00000,
Expand Down Expand Up @@ -142,6 +150,7 @@ def test_validate_df_columns(self):
"payment_eth": [],
"execution_cost_eth": [],
"num_participating_batches": [],
"protocol_fee_eth": [],
"reward_eth": [],
"reward_cow": [],
"secondary_reward_cow": [],
Expand Down Expand Up @@ -191,6 +200,7 @@ def test_construct_payouts(self):
"payment_eth": self.eth_payments,
"execution_cost_eth": self.execution_costs,
"num_participating_batches": self.batch_participation,
"protocol_fee_eth": self.protocol_fees,
}
),
converter=self.mock_converter,
Expand Down Expand Up @@ -219,6 +229,7 @@ def test_construct_payouts(self):
"payment_eth": [600000000000000.0, 1.045e16, -1e16, 0.0],
"execution_cost_eth": [800000000000000.0, 450000000000000.0, 0.0, 0.0],
"num_participating_batches": [7, 2, 7, 6],
"protocol_fee_eth": self.protocol_fees,
"reward_eth": [-200000000000000.0, 1e16, -1e16, 0.0],
"reward_cow": [
-200000000000000000,
Expand Down Expand Up @@ -290,6 +301,12 @@ def test_prepare_transfers(self):
90000000000000000000.00000,
180000000000000000000.00000,
],
"protocol_fee_eth": [
300000000000000.0,
200000000000000.0,
100000000000000.0,
0.0,
],
"solver_name": ["S_1", "S_2", "S_3", None],
"eth_slippage_wei": [1.0, 0.0, -1.0, None],
"reward_target": [
Expand All @@ -308,12 +325,12 @@ def test_prepare_transfers(self):
Transfer(
token=None,
recipient=Address(self.solvers[0]),
amount_wei=663636363636364,
amount_wei=363636363636364,
),
Transfer(
token=None,
recipient=Address(self.solvers[1]),
amount_wei=450000000000000,
amount_wei=250000000000000,
),
Transfer(
token=Token(COW_TOKEN_ADDRESS),
Expand Down Expand Up @@ -344,7 +361,7 @@ def test_prepare_transfers(self):
Overdraft(
period,
account=Address(self.solvers[2]),
wei=9936363636363638,
wei=10036363636363638,
name="S_3",
)
],
Expand All @@ -366,6 +383,7 @@ def sample_record(
participation: int,
slippage: int,
num_quotes: int,
protocol_fee: int,
):
"""Assumes a conversion rate of ETH:COW <> 1:self.conversion_rate"""
return RewardAndPenaltyDatum(
Expand All @@ -379,32 +397,39 @@ def sample_record(
secondary_reward_cow=participation * self.conversion_rate,
slippage_eth=slippage,
quote_reward_cow=QUOTE_REWARD * num_quotes,
protocol_fee_eth=protocol_fee,
)

def test_invalid_input(self):
with self.assertRaises(AssertionError):
self.sample_record(0, -1, 0, 0, 0)
self.sample_record(0, -1, 0, 0, 0, 0)

with self.assertRaises(AssertionError):
self.sample_record(0, 0, -1, 0, 0, 0)

with self.assertRaises(AssertionError):
self.sample_record(0, 0, 0, 0, -1, 0)

with self.assertRaises(AssertionError):
self.sample_record(0, 0, -1, 0, 0)
self.sample_record(0, 0, 0, 0, 0, -1)

def test_reward_datum_0_0_0_0(self):
test_datum = self.sample_record(0, 0, 0, 0, 0)
def test_reward_datum_0_0_0_0_0(self):
test_datum = self.sample_record(0, 0, 0, 0, 0, 0)
self.assertFalse(test_datum.is_overdraft())
self.assertEqual(test_datum.as_payouts(), [])

def test_reward_datum_1_1_0_0(self):
def test_reward_datum_1_1_0_0_0_0(self):
cost = 1
test_datum = self.sample_record(1, cost, 0, 0, 0)
test_datum = self.sample_record(1, cost, 0, 0, 0, 0)
self.assertFalse(test_datum.is_overdraft())
self.assertEqual(
test_datum.as_payouts(),
[Transfer(token=None, recipient=self.solver, amount_wei=cost)],
)

def test_reward_datum_3_2_0_minus1(self):
def test_reward_datum_3_2_0_minus1_0_0(self):
payment, cost, participation, slippage = 3, 2, 0, -1
test_datum = self.sample_record(payment, cost, participation, slippage, 0)
test_datum = self.sample_record(payment, cost, participation, slippage, 0, 0)
self.assertFalse(test_datum.is_overdraft())
self.assertEqual(
test_datum.as_payouts(),
Expand All @@ -421,11 +446,30 @@ def test_reward_datum_3_2_0_minus1(self):
),
],
)
def test_reward_datum_8_4_0_minus1_0_2(self):
payment, cost, participation, slippage, protocol_fee = 8, 4, 0, -1, 2
test_datum = self.sample_record(payment, cost, participation, slippage, 0, protocol_fee)
self.assertFalse(test_datum.is_overdraft())
self.assertEqual(
test_datum.as_payouts(),
[
Transfer(
token=None,
recipient=self.solver,
amount_wei=cost + slippage - protocol_fee,
),
Transfer(
token=self.cow_token,
recipient=self.reward_target,
amount_wei=(payment - cost) * self.conversion_rate,
),
],
)

def test_reward_datum_cost_exceeds_payment_degenerate(self):
# Degenerate Case!
payment, cost, participation, slippage = 1, 10, 0, -1
test_datum = self.sample_record(payment, cost, participation, slippage, 0)
test_datum = self.sample_record(payment, cost, participation, slippage, 0, 0)
self.assertFalse(test_datum.is_overdraft())
self.assertEqual(
test_datum.as_payouts(),
Expand All @@ -439,7 +483,7 @@ def test_reward_datum_cost_exceeds_payment_non_degenerate(self):
cost = max(sum(x) for x in triplets) + 1

for payment, participation, slippage in triplets:
test_datum = self.sample_record(payment, cost, participation, slippage, 0)
test_datum = self.sample_record(payment, cost, participation, slippage, 0, 0)
self.assertFalse(test_datum.is_overdraft())
self.assertLess(test_datum.total_outgoing_eth(), cost)
self.assertEqual(
Expand All @@ -462,12 +506,12 @@ def test_reward_datum_overdraft(self):
for payment, participation, slippage in triplets:
for cost in [0, 1, 100]:
# Doesn't matter their costs, they are in overdraft state!
rec = self.sample_record(payment, cost, participation, slippage, 0)
rec = self.sample_record(payment, cost, participation, slippage, 0, 0)
self.assertTrue(rec.is_overdraft())

def test_reward_datum_1_1_1_1(self):
def test_reward_datum_1_1_1_1_0_0(self):
payment, cost, participation, slippage = 1, 1, 1, 1
test_datum = self.sample_record(payment, cost, participation, slippage, 0)
test_datum = self.sample_record(payment, cost, participation, slippage, 0, 0)

self.assertFalse(test_datum.is_overdraft())
self.assertEqual(
Expand All @@ -491,7 +535,7 @@ def test_reward_datum_1_1_1_1(self):

def test_payout_negative_payments(self):
payment, cost, participation, slippage = -1, 1, 1, 1
test_datum = self.sample_record(payment, cost, participation, slippage, 0)
test_datum = self.sample_record(payment, cost, participation, slippage, 0, 0)
self.assertEqual(
test_datum.as_payouts(),
[
Expand Down

0 comments on commit 34462be

Please sign in to comment.