Skip to content

Commit

Permalink
Implementation of CIP-38 (#345)
Browse files Browse the repository at this point in the history
This PR implements changes to solver rewards scripts for
[CIP-38](https://snapshot.org/#/cow.eth/proposal/0xfb81daea9be89f4f1c251d53fd9d1481129b97c6f38caaddc42af7f3ce5a52ec)
on ranking by surplus.

The main change is that rewards are computed based on surplus (and
protocol fees) and not based on surplus and network fees (and protocol
fees). Additionally, there is no cost reimbursement but the slippage
computation includes a contribution from fees.

The implementation in this PR is quite minimal.
The batch rewards query ignores network fees for payment but explicitly
returns aggregated network fees to add them to slippage. The other
changes are only slight renames (e.g.
from `payment_eth` to `primary_reward_eth`). Test have been adapted
accordingly.
The slippage query does not change. Instead, when generating the final
merged dataframe, network fees are explicitly added to slippage. Test
have been adapted and a few comments were added.

A few things can be implemented differently:
- Instead of computing payments from surplus and protocol fees, one
could have used `winning_score`. This is a bit simpler but would require
checking somewhere that the score is consistent with the on-chain
settlement.
- The slippage computation could have been adapted to not exclude
network fees and use external prices for converting those fees to ETH.
This would require uploading explicit information on fees and their
native prices to dune. This should be done at some point, but maybe not
for the release.

The changes to computing rewards in `batch_rewards.sql` need to be
ported to dune-sync.

---------

Co-authored-by: Haris Angelidakis <[email protected]>
  • Loading branch information
fhenneke and harisang authored Mar 25, 2024
1 parent 471dd16 commit 262e58e
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 216 deletions.
49 changes: 21 additions & 28 deletions queries/orderbook/barn_batch_rewards.sql
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ order_protocol_fee AS (
-- impossible anyways. This query will return a division by
-- zero error in that case.
LEAST(
fp.surplus_max_volume_factor * os.sell_amount * os.buy_amount / (os.sell_amount - os.observed_fee),
fp.surplus_max_volume_factor / (1 - fp.surplus_max_volume_factor) * os.buy_amount,
-- at most charge a fraction of volume
fp.surplus_factor / (1 - fp.surplus_factor) * surplus -- charge a fraction of surplus
)
Expand All @@ -96,12 +96,14 @@ order_protocol_fee AS (
fp.surplus_factor / (1 - fp.surplus_factor) * surplus -- charge a fraction of surplus
)
END
WHEN fp.kind = 'volume' THEN fp.volume_factor / (1 + fp.volume_factor) * os.sell_amount
WHEN fp.kind = 'volume' THEN CASE
WHEN os.kind = 'sell' THEN
fp.volume_factor / (1 - fp.volume_factor) * os.buy_amount
WHEN os.kind = 'buy' THEN
fp.volume_factor / (1 + fp.volume_factor) * os.sell_amount
END
END AS protocol_fee,
CASE
WHEN fp.kind = 'surplus' THEN os.surplus_token
WHEN fp.kind = 'volume' THEN os.sell_token
END AS protocol_fee_token
os.surplus_token AS protocol_fee_token
FROM
order_surplus os
JOIN fee_policies fp -- contains protocol fee policy
Expand Down Expand Up @@ -160,17 +162,17 @@ reward_data AS (
case
when block_number is not null
and block_number > block_deadline then 0
else coalesce(execution_cost, 0)
else coalesce(execution_cost, 0) -- if block_number is null, execution cost is 0
end as execution_cost,
case
when block_number is not null
and block_number > block_deadline then 0
else coalesce(surplus, 0)
else coalesce(surplus, 0) -- if block_number is null, surplus is 0
end as surplus,
case
when block_number is not null
and block_number > block_deadline then 0
else coalesce(fee, 0)
else coalesce(fee, 0) -- if block_number is null, fee is 0
end as fee,
-- scores
winning_score,
Expand Down Expand Up @@ -201,14 +203,14 @@ reward_per_auction as (
surplus,
protocol_fee, -- the protocol fee
fee - network_fee_correction as network_fee, -- the network fee
surplus + protocol_fee + fee - network_fee_correction - reference_score as uncapped_payment_eth,
surplus + protocol_fee - reference_score as uncapped_payment,
-- Capped Reward = CLAMP_[-E, E + exec_cost](uncapped_reward_eth)
LEAST(
GREATEST(
- {{EPSILON_LOWER}},
surplus + protocol_fee + fee - network_fee_correction - reference_score
surplus + protocol_fee - reference_score
),
{{EPSILON_UPPER}} + execution_cost
{{EPSILON_UPPER}}
) as capped_payment,
winning_score,
reference_score,
Expand All @@ -235,17 +237,9 @@ participation_counts as (
primary_rewards as (
SELECT
rpt.solver,
SUM(capped_payment) as payment_wei,
SUM(execution_cost) as exececution_cost_wei
FROM
reward_per_auction rpt
GROUP BY
solver
),
protocol_fees as (
SELECT
solver,
SUM(protocol_fee) as protocol_fee_wei
SUM(capped_payment) as payment,
SUM(protocol_fee) as protocol_fee,
SUM(network_fee) as network_fee
FROM
reward_per_auction rpt
GROUP BY
Expand All @@ -254,18 +248,17 @@ protocol_fees as (
aggregate_results as (
SELECT
concat('0x', encode(pc.solver, 'hex')) as solver,
coalesce(payment_wei, 0) as payment_eth,
coalesce(exececution_cost_wei, 0) as execution_cost_eth,
coalesce(payment, 0) as primary_reward_eth,
num_participating_batches,
coalesce(protocol_fee_wei, 0) as protocol_fee_eth
coalesce(protocol_fee, 0) as protocol_fee_eth,
coalesce(network_fee, 0) as network_fee_eth
FROM
participation_counts pc
LEFT OUTER JOIN primary_rewards pr ON pr.solver = pc.solver
LEFT OUTER JOIN protocol_fees pf ON pf.solver = pc.solver
) --
select
*
from
aggregate_results
order by
solver
solver
33 changes: 12 additions & 21 deletions queries/orderbook/prod_batch_rewards.sql
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,17 @@ reward_data AS (
case
when block_number is not null
and block_number > block_deadline then 0
else coalesce(execution_cost, 0)
else coalesce(execution_cost, 0) -- if block_number is null, execution cost is 0
end as execution_cost,
case
when block_number is not null
and block_number > block_deadline then 0
else coalesce(surplus, 0)
else coalesce(surplus, 0) -- if block_number is null, surplus is 0
end as surplus,
case
when block_number is not null
and block_number > block_deadline then 0
else coalesce(fee, 0)
else coalesce(fee, 0) -- if block_number is null, fee is 0
end as fee,
-- scores
winning_score,
Expand Down Expand Up @@ -203,14 +203,14 @@ reward_per_auction as (
surplus,
protocol_fee, -- the protocol fee
fee - network_fee_correction as network_fee, -- the network fee
surplus + protocol_fee + fee - network_fee_correction - reference_score as uncapped_payment_eth,
surplus + protocol_fee - reference_score as uncapped_payment,
-- Capped Reward = CLAMP_[-E, E + exec_cost](uncapped_reward_eth)
LEAST(
GREATEST(
- {{EPSILON_LOWER}},
surplus + protocol_fee + fee - network_fee_correction - reference_score
surplus + protocol_fee - reference_score
),
{{EPSILON_UPPER}} + execution_cost
{{EPSILON_UPPER}}
) as capped_payment,
winning_score,
reference_score,
Expand All @@ -237,17 +237,9 @@ participation_counts as (
primary_rewards as (
SELECT
rpt.solver,
SUM(capped_payment) as payment_wei,
SUM(execution_cost) as exececution_cost_wei
FROM
reward_per_auction rpt
GROUP BY
solver
),
protocol_fees as (
SELECT
solver,
SUM(protocol_fee) as protocol_fee_wei
SUM(capped_payment) as payment,
SUM(protocol_fee) as protocol_fee,
SUM(network_fee) as network_fee
FROM
reward_per_auction rpt
GROUP BY
Expand All @@ -256,14 +248,13 @@ protocol_fees as (
aggregate_results as (
SELECT
concat('0x', encode(pc.solver, 'hex')) as solver,
coalesce(payment_wei, 0) as payment_eth,
coalesce(exececution_cost_wei, 0) as execution_cost_eth,
coalesce(payment, 0) as primary_reward_eth,
num_participating_batches,
coalesce(protocol_fee_wei, 0) as protocol_fee_eth
coalesce(protocol_fee, 0) as protocol_fee_eth,
coalesce(network_fee, 0) as network_fee_eth
FROM
participation_counts pc
LEFT OUTER JOIN primary_rewards pr ON pr.solver = pc.solver
LEFT OUTER JOIN protocol_fees pf ON pf.solver = pc.solver
) --
select
*
Expand Down
44 changes: 22 additions & 22 deletions src/fetch/payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@

PAYMENT_COLUMNS = {
"solver",
"payment_eth",
"execution_cost_eth",
"primary_reward_eth",
"primary_reward_cow",
"secondary_reward_eth",
"reward_cow",
"secondary_reward_cow",
"quote_reward_cow",
"protocol_fee_eth",
"network_fee_eth",
}
SLIPPAGE_COLUMNS = {
"solver",
Expand All @@ -49,10 +49,8 @@

COMPLETE_COLUMNS = PAYMENT_COLUMNS.union(SLIPPAGE_COLUMNS).union(REWARD_TARGET_COLUMNS)
NUMERICAL_COLUMNS = [
"payment_eth",
"execution_cost_eth",
"reward_eth",
"reward_cow",
"primary_reward_eth",
"primary_reward_cow",
"secondary_reward_cow",
"secondary_reward_eth",
"quote_reward_cow",
Expand All @@ -79,24 +77,22 @@ def __init__( # pylint: disable=too-many-arguments
solver: Address,
solver_name: str,
reward_target: Address,
exec_cost: int,
payment_eth: int,
primary_reward_eth: int,
secondary_reward_eth: int,
slippage_eth: int,
primary_reward_cow: int,
secondary_reward_cow: int,
quote_reward_cow: 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_cow"

self.solver = solver
self.solver_name = solver_name
self.reward_target = reward_target
self.exec_cost = exec_cost
self.payment_eth = payment_eth
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
Expand All @@ -120,26 +116,25 @@ def from_series(cls, frame: Series) -> RewardAndPenaltyDatum:
solver=Address(solver),
solver_name=frame["solver_name"],
reward_target=Address(reward_target),
payment_eth=int(frame["payment_eth"]),
slippage_eth=slippage,
primary_reward_cow=int(frame["reward_cow"]),
exec_cost=int(frame["execution_cost_eth"]),
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"]),
)

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.primary_reward_eth + self.secondary_reward_eth + self.slippage_eth

def total_cow_reward(self) -> int:
"""Total outgoing COW token reward"""
return self.primary_reward_cow + self.secondary_reward_cow

def total_eth_reward(self) -> int:
"""Total outgoing ETH reward"""
return self.payment_eth - self.exec_cost + self.secondary_reward_eth
return self.primary_reward_eth + self.secondary_reward_eth

def is_overdraft(self) -> bool:
"""
Expand Down Expand Up @@ -168,7 +163,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.slippage_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 Expand Up @@ -270,12 +265,11 @@ def extend_payment_df(pdf: DataFrame, converter: TokenConversion) -> DataFrame:
This is evaluated in both ETH and COW (for different use cases).
"""
# Note that this can be negative!
pdf["reward_eth"] = pdf["payment_eth"] - pdf["execution_cost_eth"]
pdf["reward_cow"] = pdf["reward_eth"].apply(converter.eth_to_token)
pdf["primary_reward_cow"] = pdf["primary_reward_eth"].apply(converter.eth_to_token)

secondary_allocation = max(
min(
PERIOD_BUDGET_COW - pdf["reward_cow"].sum(),
PERIOD_BUDGET_COW - pdf["primary_reward_cow"].sum(),
converter.eth_to_token(CONSISTENCY_REWARD_CAP_ETH),
),
0,
Expand Down Expand Up @@ -383,6 +377,12 @@ def construct_payout_dataframe(
merged_df = payment_df.merge(slippage_df, on=join_column, how="left").merge(
reward_target_df, on=join_column, how="left"
)

# 4. Add slippage from fees to slippage
merged_df["eth_slippage_wei"] = (
merged_df["eth_slippage_wei"].fillna(0) + merged_df["network_fee_eth"]
)

return merged_df


Expand Down Expand Up @@ -417,7 +417,7 @@ def construct_payouts(
# Sort by solver before breaking this data frame into Transfer objects.
complete_payout_df = complete_payout_df.sort_values("solver")

performance_reward = complete_payout_df["reward_cow"].sum()
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()
protocol_fee = complete_payout_df["protocol_fee_eth"].sum()
Expand Down
8 changes: 4 additions & 4 deletions tests/queries/batch_rewards_test_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,12 @@ VALUES (1, '\x5222222222222222222222222222222222222222'::bytea),
(56, '\x02'::bytea);

INSERT INTO settlement_scores (auction_id, winning_score, reference_score, winner, block_deadline, simulation_block)
VALUES (1, 5000000000000000000, 4000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 10, 0),
(2, 6000000000000000000, 3000000000000000000, '\x5222222222222222222222222222222222222222'::bytea, 11, 1),
VALUES (1, 6000000000000000000, 4000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 10, 0),
(2, 12000000000000000000, 3000000000000000000, '\x5222222222222222222222222222222222222222'::bytea, 11, 1),
(3, 21000000000000000000, 3000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 12, 2),
(5, 5000000000000000000, 3000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 14, 4), -- jump in auction id
(6, 10000000000000000000, 9000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 15, 5), -- settled too late
(7, 5000000000000000000, 0, '\x5111111111111111111111111111111111111111'::bytea, 30, 6), -- no competition
(7, 6000000000000000000, 0, '\x5111111111111111111111111111111111111111'::bytea, 30, 6), -- no competition
(8, 5000000000000000000, 0, '\x5111111111111111111111111111111111111111'::bytea, 35, 7), -- no competition, failed transaction
(9, 5000000000000000000, 1000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 36, 8), -- score larger than quality
(10, 5000000000000000000, 4000000000000000000, '\x5333333333333333333333333333333333333333'::bytea, 37, 9), -- participant with net negative payment
Expand Down Expand Up @@ -235,7 +235,7 @@ VALUES ('\x01'::bytea, '\x01'::bytea, '\x02'::bytea, 95000000, 94000000000000000

INSERT INTO trades (block_number, log_index, order_uid, sell_amount, buy_amount, fee_amount)
VALUES (51, 0, '\x01'::bytea, 100000000, 95000000000000000000, 5000000),
(52, 0, '\x02'::bytea, 106000000, 100000000000000000000, 5000000),
(52, 0, '\x02'::bytea, 105000000, 100000000000000000000, 5000000),
(53, 0, '\x03'::bytea, 100000000, 101000000000000000000, 0),
(54, 0, '\x04'::bytea, 99000000, 100000000000000000000, 0),
(55, 0, '\x05'::bytea, 100000000, 95000000000000000000, 0),
Expand Down
26 changes: 13 additions & 13 deletions tests/queries/test_batch_rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,14 @@ def test_get_batch_rewards(self):
"0x5333333333333333333333333333333333333333",
"0x5444444444444444444444444444444444444444",
],
"payment_eth": [
9535064849462312.0,
10.5e15,
6600000000000000.00000,
12450000000000000.00000,
"primary_reward_eth": [
2071357035553330.0, # 3 * 1e18 * 5e14 / 1e18 (surplus) + 571357035553330 (protocol fee)
3519801980198020.0,
6000000000000000.00000,
12000000000000000.00000,
-10000000000000000.00000,
0.00000,
],
"execution_cost_eth": [
7500000000000000.0,
7500000000000000.0,
800000000000000.00000,
450000000000000.00000,
0.00000,
0.00000,
],
"num_participating_batches": [
3,
3,
Expand All @@ -60,6 +52,14 @@ def test_get_batch_rewards(self):
0.0,
0.0,
],
"network_fee_eth": [
7463707813908982.0, # almost 2500000000000000 + 3000000000000000 + 2500000000000000 - 5.748876684972541e14
6980198019801980.0, # 2500000000000000 + 4000000000000000 + 2500000000000000 - 2.0198019801980198e15
1050000000000000.0,
400000000000000.0,
0.0,
0.0,
],
}
)
self.assertIsNone(pandas.testing.assert_frame_equal(expected, batch_rewards))
Expand Down
Loading

0 comments on commit 262e58e

Please sign in to comment.