Skip to content

Commit

Permalink
Surplus_fee Calculation Cleanup (#1797)
Browse files Browse the repository at this point in the history
Follow-up PR for #1688

This PR does a cleanup of `surplus_fee` calculation in `autopilot` and
all dependent code.
  • Loading branch information
sunce86 authored Aug 31, 2023
1 parent c339014 commit e722256
Show file tree
Hide file tree
Showing 31 changed files with 58 additions and 1,338 deletions.
26 changes: 3 additions & 23 deletions crates/alerter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,6 @@ impl Order {
fn is_liquidity_order(&self) -> bool {
matches!(self.class, OrderClass::Liquidity)
}

fn effective_sell_amount(&self) -> Option<U256> {
let amount = match &self.class {
OrderClass::Limit(limit) => {
// Use wrapping arithmetic. The orderbook should guarantee that
// the effective sell amount fits in a `U256`.
self.sell_amount
.overflowing_add(self.fee_amount)
.0
.overflowing_sub(limit.surplus_fee?)
.0
}
OrderClass::Market | OrderClass::Liquidity => self.sell_amount,
};

Some(amount)
}
}

struct OrderBookApi {
Expand Down Expand Up @@ -135,12 +118,9 @@ impl ZeroExApi {
pub async fn can_be_settled(&self, order: &Order) -> Result<bool> {
let mut url = shared::url::join(&self.base, "swap/v1/price");

let effective_sell_amount = order
.effective_sell_amount()
.context("surplus fee not computed")?;
let (amount_name, amount) = match order.kind {
OrderKind::Buy => ("buyAmount", order.buy_amount),
OrderKind::Sell => ("sellAmount", effective_sell_amount),
OrderKind::Sell => ("sellAmount", order.sell_amount),
};

let buy_token = convert_eth_to_weth(order.buy_token);
Expand Down Expand Up @@ -169,8 +149,8 @@ impl ZeroExApi {

tracing::debug!(url = url.as_str(), ?response, "0x");

let can_settle = response.sell_amount <= effective_sell_amount
&& response.buy_amount >= order.buy_amount;
let can_settle =
response.sell_amount <= order.sell_amount && response.buy_amount >= order.buy_amount;
if can_settle {
tracing::debug!(%order.uid, "marking order as settleable");
}
Expand Down
44 changes: 0 additions & 44 deletions crates/autopilot/src/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use {
crate::fok_limit_orders::QuotingStrategy,
primitive_types::H160,
shared::{
arguments::{display_list, display_option},
Expand Down Expand Up @@ -108,39 +107,13 @@ pub struct Arguments {
)]
pub max_auction_age: Duration,

/// If a limit order surplus fee is older than this, it will get refreshed.
/// Expects a value in seconds.
#[clap(
long,
env,
default_value = "180",
value_parser = shared::arguments::duration_from_seconds,
)]
pub max_surplus_fee_age: Duration,

#[clap(long, env, default_value = "0")]
pub limit_order_price_factor: f64,

#[clap(long, env, action = clap::ArgAction::Set, default_value = "true")]
pub process_fill_or_kill_limit_orders: bool,

/// How many quotes the limit order quoter updates in parallel.
#[clap(long, env, default_value = "5")]
pub limit_order_quoter_parallelism: usize,

/// How many quotes the limit order quoter updates per update cycle.
#[clap(long, env, default_value = "25")]
pub limit_order_quoter_batch_size: usize,

/// The time between auction updates.
#[clap(long, env, default_value = "10", value_parser = shared::arguments::duration_from_seconds)]
pub auction_update_interval: Duration,

/// Defines which strategies to apply when updating the `surplus_fee` of
/// limit orders.
#[clap(long, env, use_value_delimiter = true)]
pub quoting_strategies: Vec<QuotingStrategy>,

/// Fee scaling factor for objective value. This controls the constant
/// factor by which order fees are multiplied with. Setting this to a value
/// greater than 1.0 makes settlements with negative objective values less
Expand Down Expand Up @@ -225,28 +198,11 @@ impl std::fmt::Display for Arguments {
)?;
writeln!(f, "banned_users: {:?}", self.banned_users)?;
writeln!(f, "max_auction_age: {:?}", self.max_auction_age)?;
writeln!(f, "max_surplus_fee_age: {:?}", self.max_surplus_fee_age)?;
writeln!(
f,
"limit_order_price_factor: {:?}",
self.limit_order_price_factor
)?;
writeln!(
f,
"process_fill_or_kill_limit_orders: {:?}",
self.process_fill_or_kill_limit_orders
)?;
writeln!(
f,
"limit_order_quoter_parallelism: {:?}",
self.limit_order_quoter_parallelism
)?;
writeln!(
f,
"limit_order_quoter_batch_size: {:?}",
self.limit_order_quoter_batch_size,
)?;
writeln!(f, "quoting_strategies: {:?}", self.quoting_strategies)?;
writeln!(
f,
"fee_objective_scaling_factor: {}",
Expand Down
24 changes: 8 additions & 16 deletions crates/autopilot/src/database/auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,7 @@ impl QuoteStoring for Postgres {
}

impl Postgres {
pub async fn solvable_orders(
&self,
min_valid_to: u32,
min_surplus_fee_timestamp: DateTime<Utc>,
) -> Result<SolvableOrders> {
pub async fn solvable_orders(&self, min_valid_to: u32) -> Result<SolvableOrders> {
let _timer = super::Metrics::get()
.database_queries
.with_label_values(&["solvable_orders"])
Expand All @@ -85,17 +81,13 @@ impl Postgres {
sqlx::query("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
.execute(ex.deref_mut())
.await?;
let orders = database::orders::solvable_orders(
&mut ex,
min_valid_to as i64,
min_surplus_fee_timestamp,
)
.map(|result| match result {
Ok(order) => full_order_into_model_order(order),
Err(err) => Err(anyhow::Error::from(err)),
})
.try_collect()
.await?;
let orders = database::orders::solvable_orders(&mut ex, min_valid_to as i64)
.map(|result| match result {
Ok(order) => full_order_into_model_order(order),
Err(err) => Err(anyhow::Error::from(err)),
})
.try_collect()
.await?;
let latest_settlement_block =
database::orders::latest_settlement_block(&mut ex).await? as u64;
Ok(SolvableOrders {
Expand Down
6 changes: 0 additions & 6 deletions crates/autopilot/src/database/onchain_order_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,6 @@ fn convert_onchain_order_placement(
full_fee_amount: u256_to_big_decimal(&full_fee_amount),
cancellation_timestamp: None,
class,
surplus_fee: Default::default(),
surplus_fee_timestamp: Default::default(),
};
let onchain_order_placement_event = OnchainOrderPlacement {
order_uid: ByteArray(order_uid.0),
Expand Down Expand Up @@ -959,7 +957,6 @@ mod test {
buy_token_balance: buy_token_destination_into(expected_order_data.buy_token_balance),
full_fee_amount: u256_to_big_decimal(&U256::zero()),
cancellation_timestamp: None,
..Default::default()
};
assert_eq!(onchain_order_placement, expected_onchain_order_placement);
assert_eq!(order, expected_order);
Expand Down Expand Up @@ -1072,7 +1069,6 @@ mod test {
buy_token_balance: buy_token_destination_into(expected_order_data.buy_token_balance),
full_fee_amount: u256_to_big_decimal(&U256::zero()),
cancellation_timestamp: None,
..Default::default()
};
assert_eq!(onchain_order_placement, expected_onchain_order_placement);
assert_eq!(order, expected_order);
Expand Down Expand Up @@ -1185,8 +1181,6 @@ mod test {
buy_token_balance: buy_token_destination_into(expected_order_data.buy_token_balance),
full_fee_amount: u256_to_big_decimal(&U256::zero()),
cancellation_timestamp: None,
surplus_fee: Default::default(),
..Default::default()
};
assert_eq!(onchain_order_placement, expected_onchain_order_placement);
assert_eq!(order, expected_order);
Expand Down
169 changes: 4 additions & 165 deletions crates/autopilot/src/database/orders.rs
Original file line number Diff line number Diff line change
@@ -1,176 +1,15 @@
use {
super::Postgres,
crate::decoded_settlement::OrderExecution,
anyhow::{Context, Result},
chrono::{DateTime, Duration, Utc},
database::{
byte_array::ByteArray,
orders::{OrderFeeSpecifier, OrderQuotingData, Quote},
},
ethcontract::{H256, U256},
anyhow::Result,
database::byte_array::ByteArray,
ethcontract::H256,
futures::{StreamExt, TryStreamExt},
model::{auction::AuctionId, time::now_in_epoch_seconds},
number_conversions::u256_to_big_decimal,
primitive_types::H160,
shared::fee_subsidy::FeeParameters,
model::auction::AuctionId,
sqlx::PgConnection,
};

/// New fee data to update a limit order with.
///
/// Both success and failure to calculate the new fee are recorded in the
/// database.
pub enum FeeUpdate {
Success {
timestamp: DateTime<Utc>,
/// The actual fee amount to charge the order from its surplus.
surplus_fee: U256,
/// The full unsubsidized fee amount that settling the order is expected
/// to actually cost. This is used for objective value
/// computation so that fee subsidies do not change the
/// objective value.
full_fee_amount: U256,
quote: LimitOrderQuote,
},
Failure {
timestamp: DateTime<Utc>,
},
}

/// Data required to compute risk adjusted rewards for limit orders.
pub struct LimitOrderQuote {
/// Everything required to compute the fee amount in sell token
pub fee_parameters: FeeParameters,

/// The `sell_amount` of the quote associated with the `surplus_fee`
/// estimation.
pub sell_amount: U256,

/// The `buy_amount` of the quote associated with the `surplus_fee`
/// estimation.
pub buy_amount: U256,

/// The solver that provided the quote.
pub solver: H160,
}

impl Postgres {
/// Returns all limit orders that are waiting to be filled.
pub async fn open_fok_limit_orders(&self, age: Duration) -> Result<Vec<OrderQuotingData>> {
let _timer = super::Metrics::get()
.database_queries
.with_label_values(&["open_limit_orders"])
.start_timer();

let mut ex = self.0.acquire().await?;
let timestamp = Utc::now() - age;
database::orders::open_fok_limit_orders(&mut ex, timestamp, now_in_epoch_seconds().into())
.map(|result| result.map_err(anyhow::Error::from))
.try_collect()
.await
}

/// Updates the `surplus_fee` of all limit orders matching the
/// [`OrderFeeSpecifier`] and stores a quote for each one.
pub async fn update_fok_limit_order_fees(
&self,
order_spec: &OrderFeeSpecifier,
update: &FeeUpdate,
) -> Result<()> {
let (update, quote) = match update {
FeeUpdate::Success {
timestamp,
surplus_fee,
full_fee_amount,
quote,
} => (
database::orders::FeeUpdate {
surplus_fee: Some(u256_to_big_decimal(surplus_fee)),
surplus_fee_timestamp: *timestamp,
full_fee_amount: u256_to_big_decimal(full_fee_amount),
},
Some(database::orders::Quote {
// for every order we update we copy this struct and set the order_uid later
order_uid: Default::default(),
gas_amount: quote.fee_parameters.gas_amount,
gas_price: quote.fee_parameters.gas_price,
sell_token_price: quote.fee_parameters.sell_token_price,
sell_amount: u256_to_big_decimal(&quote.sell_amount),
buy_amount: u256_to_big_decimal(&quote.buy_amount),
solver: ByteArray(quote.solver.0),
}),
),
FeeUpdate::Failure { timestamp } => (
// Note that the surplus fee must be removed so that the order does not count as
// solvable. In order to be solvable the timestamp must be recent and the fee must
// be set. We don't reset the timestamp because it indicates the last update time
// (regardless of error or success). This is needed so that we can query the least
// recently updated limit orders. See #965 .
//
// Note that we'll do a bulk update of orders so technically it's possible that an
// error during the price estimation invalidates a multiple orders. But errors are
// very rare and it's not very common to have identical orders anyway so we don't
// have to protect against bulk invalidations.
database::orders::FeeUpdate {
surplus_fee: None,
surplus_fee_timestamp: *timestamp,
full_fee_amount: 0.into(),
},
None,
),
};

let _timer = super::Metrics::get()
.database_queries
.with_label_values(&["update_limit_order_fees"])
.start_timer();
let mut ex = self.0.begin().await?;
let updated_order_uids =
database::orders::update_fok_limit_order_fees(&mut ex, order_spec, &update)
.await
.context("update_limit_order_fee")?;
if let Some(quote) = quote {
for order_uid in updated_order_uids {
let quote = Quote {
order_uid,
..quote.clone()
};
database::orders::insert_quote_and_update_on_conflict(&mut ex, &quote)
.await
.context("insert_quote_and_update_on_conflict")?;
}
}
ex.commit().await.context("commit")?;
Ok(())
}

pub async fn count_fok_limit_orders(&self) -> Result<i64> {
let _timer = super::Metrics::get()
.database_queries
.with_label_values(&["count_limit_orders"])
.start_timer();
let mut ex = self.0.acquire().await?;
Ok(
database::orders::count_fok_limit_orders(&mut ex, now_in_epoch_seconds().into())
.await?,
)
}

pub async fn count_limit_orders_with_outdated_fees(&self, age: Duration) -> Result<i64> {
let _timer = super::Metrics::get()
.database_queries
.with_label_values(&["count_limit_orders_with_outdated_fees"])
.start_timer();
let mut ex = self.0.acquire().await?;
let timestamp = Utc::now() - age;
Ok(database::orders::count_fok_limit_orders_with_outdated_fees(
&mut ex,
timestamp,
now_in_epoch_seconds().into(),
)
.await?)
}

pub async fn order_executions_for_tx(
ex: &mut PgConnection,
tx_hash: &H256,
Expand Down
Loading

0 comments on commit e722256

Please sign in to comment.