Skip to content

Commit

Permalink
Add price improvement protocol fee (#353)
Browse files Browse the repository at this point in the history
This PR implements a protocol fee on price improvement as introduced in
...

A new case was added to the batch rewards SQL query for computing a
price improvement fee.
Price improvement is computed from a quote (depending on order kind). If
price improvement is positive and smaller than surplus, the protocol fee
is a fraction of price improvement. Otherwise it is zero or a fraction
of surplus, respectively.

Four cases are tested
- normal sell order
- normal buy order
- sell order with bad quote, fee capped at fraction of volume
- sell order with negative price improvement, thus zero profotol fee

This PR should be merged and tagged before the end of this accounting
period. The change to the batch rewards query should be mirrored on
dune-sync.
  • Loading branch information
fhenneke authored Mar 29, 2024
1 parent 262e58e commit 0c501ce
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 14 deletions.
28 changes: 28 additions & 0 deletions queries/orderbook/barn_batch_rewards.sql
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ order_surplus AS (
WHEN o.kind = 'sell' THEN t.buy_amount - t.sell_amount * o.buy_amount / (o.sell_amount + o.fee_amount)
WHEN o.kind = 'buy' THEN t.buy_amount * (o.sell_amount + o.fee_amount) / o.buy_amount - t.sell_amount
END AS surplus,
CASE
WHEN o.kind = 'sell' THEN t.buy_amount - t.sell_amount * (oq.buy_amount - oq.buy_amount / oq.sell_amount * oq.gas_amount * oq.gas_price / oq.sell_token_price) / oq.sell_amount
WHEN o.kind = 'buy' THEN t.buy_amount * (oq.sell_amount + oq.gas_amount * oq.gas_price / oq.sell_token_price) / oq.buy_amount - t.sell_amount
END AS price_improvement,
CASE
WHEN o.kind = 'sell' THEN o.buy_token
WHEN o.kind = 'buy' THEN o.sell_token
Expand All @@ -63,6 +67,8 @@ order_surplus AS (
JOIN order_execution oe -- contains surplus fee
ON t.order_uid = oe.order_uid
AND s.auction_id = oe.auction_id
LEFT OUTER JOIN order_quotes oq -- contains quote amounts
ON o.uid = oq.order_uid
WHERE
ss.block_deadline >= {{start_block}}
AND ss.block_deadline <= {{end_block}}
Expand Down Expand Up @@ -96,6 +102,28 @@ order_protocol_fee AS (
fp.surplus_factor / (1 - fp.surplus_factor) * surplus -- charge a fraction of surplus
)
END
WHEN fp.kind = 'priceimprovement' THEN CASE
WHEN os.kind = 'sell' THEN
LEAST(
-- at most charge a fraction of volume
fp.price_improvement_max_volume_factor / (1 - fp.price_improvement_max_volume_factor) * os.buy_amount,
-- charge a fraction of price improvement, at most 0
GREATEST(
fp.price_improvement_factor / (1 - fp.price_improvement_factor) * price_improvement
,
0
)
)
WHEN os.kind = 'buy' THEN LEAST(
-- at most charge a fraction of volume
fp.price_improvement_max_volume_factor / (1 + fp.price_improvement_max_volume_factor) * os.sell_amount,
-- charge a fraction of price improvement
GREATEST(
fp.price_improvement_factor / (1 - fp.price_improvement_factor) * price_improvement,
0
)
)
END
WHEN fp.kind = 'volume' THEN CASE
WHEN os.kind = 'sell' THEN
fp.volume_factor / (1 - fp.volume_factor) * os.buy_amount
Expand Down
28 changes: 28 additions & 0 deletions queries/orderbook/prod_batch_rewards.sql
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ order_surplus AS (
WHEN o.kind = 'sell' THEN t.buy_amount - t.sell_amount * o.buy_amount / (o.sell_amount + o.fee_amount)
WHEN o.kind = 'buy' THEN t.buy_amount * (o.sell_amount + o.fee_amount) / o.buy_amount - t.sell_amount
END AS surplus,
CASE
WHEN o.kind = 'sell' THEN t.buy_amount - t.sell_amount * (oq.buy_amount - oq.buy_amount / oq.sell_amount * oq.gas_amount * oq.gas_price / oq.sell_token_price) / oq.sell_amount
WHEN o.kind = 'buy' THEN t.buy_amount * (oq.sell_amount + oq.gas_amount * oq.gas_price / oq.sell_token_price) / oq.buy_amount - t.sell_amount
END AS price_improvement,
CASE
WHEN o.kind = 'sell' THEN o.buy_token
WHEN o.kind = 'buy' THEN o.sell_token
Expand All @@ -63,6 +67,8 @@ order_surplus AS (
JOIN order_execution oe -- contains surplus fee
ON t.order_uid = oe.order_uid
AND s.auction_id = oe.auction_id
LEFT OUTER JOIN order_quotes oq -- contains quote amounts
ON o.uid = oq.order_uid
WHERE
ss.block_deadline >= {{start_block}}
AND ss.block_deadline <= {{end_block}}
Expand Down Expand Up @@ -96,6 +102,28 @@ order_protocol_fee AS (
fp.surplus_factor / (1 - fp.surplus_factor) * surplus -- charge a fraction of surplus
)
END
WHEN fp.kind = 'priceimprovement' THEN CASE
WHEN os.kind = 'sell' THEN
LEAST(
-- at most charge a fraction of volume
fp.price_improvement_max_volume_factor / (1 - fp.price_improvement_max_volume_factor) * os.buy_amount,
-- charge a fraction of price improvement, at most 0
GREATEST(
fp.price_improvement_factor / (1 - fp.price_improvement_factor) * price_improvement
,
0
)
)
WHEN os.kind = 'buy' THEN LEAST(
-- at most charge a fraction of volume
fp.price_improvement_max_volume_factor / (1 + fp.price_improvement_max_volume_factor) * os.sell_amount,
-- charge a fraction of price improvement
GREATEST(
fp.price_improvement_factor / (1 - fp.price_improvement_factor) * price_improvement,
0
)
)
END
WHEN fp.kind = 'volume' THEN CASE
WHEN os.kind = 'sell' THEN
fp.volume_factor / (1 - fp.volume_factor) * os.buy_amount
Expand Down
90 changes: 76 additions & 14 deletions tests/queries/batch_rewards_test_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ DROP TABLE IF EXISTS auction_prices;
DROP TABLE IF EXISTS orders;
DROP TYPE IF EXISTS OrderKind;
DROP TYPE IF EXISTS OrderClass;
DROP TABLE IF EXISTS order_quotes;
DROP TABLE IF EXISTS trades;
DROP TABLE IF EXISTS order_execution;
DROP TABLE IF EXISTS fee_policies;
Expand Down Expand Up @@ -81,6 +82,17 @@ CREATE TABLE orders (
class OrderClass NOT NULL
);

CREATE TABLE IF NOT EXISTS order_quotes
(
order_uid bytea PRIMARY KEY,
gas_amount numeric(78, 0) NOT NULL,
gas_price numeric(78, 0) NOT NULL,
sell_token_price float NOT NULL,
sell_amount numeric(78, 0) NOT NULL,
buy_amount numeric(78, 0) NOT NULL,
solver bytea NOT NULL
);

CREATE TABLE IF NOT EXISTS trades
(
block_number bigint NOT NULL,
Expand All @@ -104,7 +116,7 @@ CREATE TABLE IF NOT EXISTS order_execution
PRIMARY KEY (order_uid, auction_id)
);

CREATE TYPE PolicyKind AS ENUM ('surplus', 'volume');
CREATE TYPE PolicyKind AS ENUM ('surplus', 'volume', 'priceimprovement');

CREATE TABLE fee_policies (
auction_id bigint NOT NULL,
Expand All @@ -119,6 +131,8 @@ CREATE TABLE fee_policies (
surplus_max_volume_factor double precision,
-- The fee should be taken as a percentage of the order volume. The value is between 0 and 1.
volume_factor double precision,
price_improvement_factor double precision,
price_improvement_max_volume_factor double precision,
PRIMARY KEY (auction_id, order_uid, application_order)
);

Expand All @@ -129,6 +143,7 @@ TRUNCATE settlement_scores;
TRUNCATE settlement_observations;
TRUNCATE auction_prices;
TRUNCATE orders;
TRUNCATE order_quotes;
TRUNCATE trades;
TRUNCATE fee_policies;

Expand All @@ -146,7 +161,11 @@ VALUES (1, 10, '\x5111111111111111111111111111111111111111'::bytea, '\x7111'::by
(53, 10, '\x01'::bytea, '\x03'::bytea, '\x01'::bytea, 2, 53),
(54, 10, '\x02'::bytea, '\x04'::bytea, '\x02'::bytea, 2, 54),
(55, 10, '\x01'::bytea, '\x05'::bytea, '\x01'::bytea, 3, 55),
(56, 10, '\x02'::bytea, '\x06'::bytea, '\x02'::bytea, 3, 56);
(56, 10, '\x02'::bytea, '\x06'::bytea, '\x02'::bytea, 3, 56),
(57, 10, '\x03'::bytea, '\x07'::bytea, '\x03'::bytea, 0, 57),
(58, 10, '\x03'::bytea, '\x08'::bytea, '\x03'::bytea, 1, 58),
(59, 10, '\x03'::bytea, '\x09'::bytea, '\x03'::bytea, 2, 59),
(60, 10, '\x03'::bytea, '\x0a'::bytea, '\x03'::bytea, 3, 60);

INSERT INTO auction_participants (auction_id, participant)
VALUES (1, '\x5222222222222222222222222222222222222222'::bytea),
Expand Down Expand Up @@ -176,7 +195,11 @@ VALUES (1, '\x5222222222222222222222222222222222222222'::bytea),
(53, '\x01'::bytea),
(54, '\x02'::bytea),
(55, '\x01'::bytea),
(56, '\x02'::bytea);
(56, '\x02'::bytea),
(57, '\x03'::bytea),
(58, '\x03'::bytea),
(59, '\x03'::bytea),
(60, '\x03'::bytea);

INSERT INTO settlement_scores (auction_id, winning_score, reference_score, winner, block_deadline, simulation_block)
VALUES (1, 6000000000000000000, 4000000000000000000, '\x5111111111111111111111111111111111111111'::bytea, 10, 0),
Expand All @@ -193,7 +216,11 @@ VALUES (1, 6000000000000000000, 4000000000000000000, '\x511111111111111111111111
(53, 1000000000000000, 0, '\x01'::bytea, 62, 52),
(54, 2000000000000000, 0, '\x02'::bytea, 62, 52),
(55, 500000000000000, 0, '\x01'::bytea, 64, 54),
(56, 500000000000000, 0, '\x02'::bytea, 65, 55); -- score probably wrong, does not take protocol fee into account
(56, 500000000000000, 0, '\x02'::bytea, 65, 55), -- score probably wrong, does not take protocol fee into account
(57, 1000000000000000, 0, '\x03'::bytea, 66, 56),
(58, 1500000000000000, 0, '\x03'::bytea, 67, 57),
(59, 500000000000000, 0, '\x03'::bytea, 68, 58),
(60, 500000000000000, 0, '\x03'::bytea, 69, 59);

INSERT INTO settlement_observations (block_number, log_index, gas_used, effective_gas_price, surplus, fee)
VALUES (1, 10, 100000, 2000000000, 6000000000000000000, 200000000000000),
Expand All @@ -209,7 +236,11 @@ VALUES (1, 10, 100000, 2000000000, 6000000000000000000, 200000000000000),
(53, 10, 100000, 25000000000, 500000000000000, 3000000000000000),
(54, 10, 100000, 25000000000, 500000000000000, 4000000000000000),
(55, 10, 100000, 25000000000, 500000000000000, 2500000000000000),
(56, 10, 100000, 25000000000, 500000000000000, 2500000000000000);
(56, 10, 100000, 25000000000, 500000000000000, 2500000000000000),
(57, 10, 100000, 25000000000, 750000000000000, 2500000000000000),
(58, 10, 100000, 25000000000, 1000000000000000, 2500000000000000),
(59, 10, 100000, 25000000000, 500000000000000, 2500000000000000),
(60, 10, 100000, 25000000000, 250000000000000, 2500000000000000);

INSERT INTO auction_prices (auction_id, token, price)
VALUES (51, '\x01', 500000000000000000000000000),
Expand All @@ -223,32 +254,63 @@ VALUES (51, '\x01', 500000000000000000000000000),
(55, '\x01', 500000000000000000000000000),
(55, '\x02', 500000000000000),
(56, '\x01', 500000000000000000000000000),
(56, '\x02', 500000000000000);
(56, '\x02', 500000000000000),
(57, '\x01', 500000000000000000000000000),
(57, '\x02', 500000000000000),
(58, '\x01', 500000000000000000000000000),
(58, '\x02', 500000000000000),
(59, '\x01', 500000000000000000000000000),
(59, '\x02', 500000000000000),
(60, '\x01', 500000000000000000000000000),
(60, '\x02', 500000000000000);

INSERT INTO orders (uid, sell_token, buy_token, sell_amount, buy_amount, fee_amount, kind, partially_fillable, full_fee_amount, class)
VALUES ('\x01'::bytea, '\x01'::bytea, '\x02'::bytea, 95000000, 94000000000000000000, 5000000, 'sell', 'f', 5000000, 'market'), -- sell market order
('\x02'::bytea, '\x01'::bytea, '\x02'::bytea, 101000000, 100000000000000000000, 5000000, 'buy', 'f', 5000000, 'market'), -- buy market order
('\x03'::bytea, '\x01'::bytea, '\x02'::bytea, 100000000, 100000000000000000000, 0, 'sell', 't', 0, 'limit'), -- partially fillable sell limit order
('\x04'::bytea, '\x01'::bytea, '\x02'::bytea, 100000000, 100000000000000000000, 0, 'buy', 't', 0, 'limit'), -- partially fillable buy limit order
('\x05'::bytea, '\x01'::bytea, '\x02'::bytea, 100000000, 94000000000000000000, 0, 'sell', 'f', 0, 'limit'), -- in market sell limit order
('\x06'::bytea, '\x01'::bytea, '\x02'::bytea, 106000000, 100000000000000000000, 0, 'buy', 'f', 0, 'limit'); -- in market buy limit order
('\x06'::bytea, '\x01'::bytea, '\x02'::bytea, 106000000, 100000000000000000000, 0, 'buy', 'f', 0, 'limit'), -- in market buy limit order
('\x07'::bytea, '\x01'::bytea, '\x02'::bytea, 100000000, 94000000000000000000, 0, 'sell', 'f', 0, 'limit'), -- in market sell limit order
('\x08'::bytea, '\x01'::bytea, '\x02'::bytea, 106000000, 100000000000000000000, 0, 'buy', 'f', 0, 'limit'), -- in market buy limit order
('\x09'::bytea, '\x01'::bytea, '\x02'::bytea, 100000000, 94000000000000000000, 0, 'sell', 'f', 0, 'limit'), -- in market sell limit order
('\x0a'::bytea, '\x01'::bytea, '\x02'::bytea, 100000000, 94000000000000000000, 0, 'sell', 'f', 0, 'limit'); -- in market sell limit order

INSERT INTO order_quotes (order_uid, gas_amount, gas_price, sell_token_price, sell_amount, buy_amount, solver)
VALUES ('\x07'::bytea, 100000, 25000000000, 500000000., 100000000, 100000000000000000000, '\x01'::bytea),
('\x08'::bytea, 100000, 25000000000, 500000000., 100000000, 100000000000000000000, '\x02'::bytea),
('\x09'::bytea, 100000, 25000000000, 500000000., 100000000, 90000000000000000000, '\x02'::bytea),
('\x0a'::bytea, 100000, 25000000000, 500000000., 100000000, 100000000000000000000, '\x02'::bytea);

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, 105000000, 100000000000000000000, 5000000),
(53, 0, '\x03'::bytea, 100000000, 101000000000000000000, 0),
(54, 0, '\x04'::bytea, 99000000, 100000000000000000000, 0),
(55, 0, '\x05'::bytea, 100000000, 95000000000000000000, 0),
(56, 0, '\x06'::bytea, 105000000, 100000000000000000000, 0);
(56, 0, '\x06'::bytea, 105000000, 100000000000000000000, 0),
(57, 0, '\x07'::bytea, 100000000, 95500000000000000000, 0),
(58, 0, '\x08'::bytea, 104000000, 100000000000000000000, 0),
(59, 0, '\x09'::bytea, 100000000, 95000000000000000000, 0),
(60, 0, '\x0a'::bytea, 100000000, 94500000000000000000, 0);

INSERT INTO order_execution (order_uid, auction_id, reward, surplus_fee, solver_fee)
VALUES ('\x03'::bytea, 53, 0, 5931372, NULL),
('\x04'::bytea, 54, 0, 6000000, NULL),
('\x05'::bytea, 55, 0, 6000000, NULL),
('\x06'::bytea, 56, 0, 6000000, NULL);
('\x06'::bytea, 56, 0, 6000000, NULL),
('\x07'::bytea, 57, 0, 6000000, NULL),
('\x08'::bytea, 58, 0, 6000000, NULL),
('\x09'::bytea, 59, 0, 6000000, NULL),
('\x0a'::bytea, 60, 0, 6000000, NULL);

INSERT INTO fee_policies (auction_id, order_uid, application_order, kind, surplus_factor, surplus_max_volume_factor, volume_factor, price_improvement_factor, price_improvement_max_volume_factor)
VALUES (53, '\x03'::bytea, 3, 'surplus', 0.5, 0.02, NULL, NULL, NULL),
(54, '\x04'::bytea, 4, 'surplus', 0.75, 0.1, NULL, NULL, NULL),
(55, '\x05'::bytea, 5, 'volume', NULL, NULL, 0.0015, NULL, NULL),
(56, '\x06'::bytea, 6, 'surplus', 0.9, 0.01, NULL, NULL, NULL),
(57, '\x07'::bytea, 7, 'priceimprovement', NULL, NULL, NULL, 0.5, 0.01),
(58, '\x08'::bytea, 8, 'priceimprovement', NULL, NULL, NULL, 0.5, 0.01),
(59, '\x09'::bytea, 9, 'priceimprovement', NULL, NULL, NULL, 0.5, 0.01),
(60, '\x0a'::bytea, 9, 'priceimprovement', NULL, NULL, NULL, 0.5, 0.01);

INSERT INTO fee_policies (auction_id, order_uid, application_order, kind, surplus_factor, surplus_max_volume_factor, volume_factor)
VALUES (53, '\x03'::bytea, 3, 'surplus', 0.5, 0.02, NULL),
(54, '\x04'::bytea, 4, 'surplus', 0.75, 0.1, NULL),
(55, '\x05'::bytea, 5, 'volume', NULL, NULL, 0.0015),
(56, '\x06'::bytea, 6, 'surplus', 0.9, 0.01, NULL);
5 changes: 5 additions & 0 deletions tests/queries/test_batch_rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def test_get_batch_rewards(self):
"solver": [
"0x01",
"0x02",
"0x03",
"0x5111111111111111111111111111111111111111",
"0x5222222222222222222222222222222222222222",
"0x5333333333333333333333333333333333333333",
Expand All @@ -31,6 +32,7 @@ def test_get_batch_rewards(self):
"primary_reward_eth": [
2071357035553330.0, # 3 * 1e18 * 5e14 / 1e18 (surplus) + 571357035553330 (protocol fee)
3519801980198020.0,
3729797979797980.0, # 1.5e18 * 5e14 / 1e18 + 2e6 * 5e26 / 1e18 + 1e18 * 5e14 / 1e18 + 0.5e18 * 5e14 / 1e18 + 1229797979797980.0 (protocol)
6000000000000000.00000,
12000000000000000.00000,
-10000000000000000.00000,
Expand All @@ -39,6 +41,7 @@ def test_get_batch_rewards(self):
"num_participating_batches": [
3,
3,
4,
7,
2,
7,
Expand All @@ -47,6 +50,7 @@ def test_get_batch_rewards(self):
"protocol_fee_eth": [
571357035553330.0, # 0.5 / (1 - 0.5) * 1e18 * 5e14 / 1e18 + 0.0015 / (1 - 0.0015) * 95e18 * 5e14 / 1e18
2.0198019801980198e15, # 0.75 / (1 - 0.75) * 1e6 * 5e26 / 1e18 + 0.01 / (1 + 0.01) * 105e6 * 5e26 / 1e18
1229797979797980.0, # 0.5 / (1 - 0.5) * 0.5e18 * 5e14 / 1e18 + 0.5 / (1 - 0.5) * 1e6 * 5e26 / 1e18 + 0.01 / (1 - 0.01) * 95e18 * 5e14 / 1e18
0.0,
0.0,
0.0,
Expand All @@ -55,6 +59,7 @@ def test_get_batch_rewards(self):
"network_fee_eth": [
7463707813908982.0, # almost 2500000000000000 + 3000000000000000 + 2500000000000000 - 5.748876684972541e14
6980198019801980.0, # 2500000000000000 + 4000000000000000 + 2500000000000000 - 2.0198019801980198e15
8779179226823198.0,
1050000000000000.0,
400000000000000.0,
0.0,
Expand Down

0 comments on commit 0c501ce

Please sign in to comment.