Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breakdown fees protocol 3/3: DB field and returning on /get_trades #2910

Merged
merged 32 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
03d92da
Initial commit
sunce86 Aug 15, 2024
d1b74ad
Add executed protocol fees to get_trades
sunce86 Aug 15, 2024
12c2cfd
self cr
sunce86 Aug 15, 2024
c3c059b
unit tests
sunce86 Aug 15, 2024
4734065
update db docs
sunce86 Aug 19, 2024
5062b98
update openapi
sunce86 Aug 19, 2024
dac52c7
optional executed fees
sunce86 Aug 19, 2024
92b8929
update tests
sunce86 Aug 19, 2024
917522c
revert optionallity
sunce86 Aug 19, 2024
bee9fda
Merge branch 'main' into breakdown-fees-protocol
sunce86 Sep 3, 2024
15385d0
merge conflict
sunce86 Sep 3, 2024
7e22acb
refactor scheme
sunce86 Sep 3, 2024
6d25a2e
small refactor
sunce86 Sep 3, 2024
b6027ed
remove option
sunce86 Sep 3, 2024
c2e15d6
save protocol fee
sunce86 Sep 3, 2024
2508c1f
fix tests
sunce86 Sep 3, 2024
6e91f19
revert ignore
sunce86 Sep 3, 2024
f2853c6
Merge branch 'main' into breakdown-fees-protocol
sunce86 Sep 4, 2024
f8ae556
protocol fees are in surplus token
sunce86 Sep 4, 2024
0910437
refactor
sunce86 Sep 4, 2024
f3f2f8b
ExecutedProtocolFee object
sunce86 Sep 4, 2024
2dc555d
docs update
sunce86 Sep 4, 2024
6dcb6ae
add comment for ordering
sunce86 Sep 4, 2024
ab183c9
fix e2e test
sunce86 Sep 4, 2024
fa6fe2a
fmt
sunce86 Sep 4, 2024
0baf80c
small fixes
sunce86 Sep 4, 2024
adaf2e0
martin's review comments
sunce86 Sep 5, 2024
43d809f
cr fixes
sunce86 Sep 6, 2024
1dc1960
remove comment
sunce86 Sep 6, 2024
24cb713
Merge branch 'main' into breakdown-fees-protocol
sunce86 Sep 6, 2024
1236347
fix conflict
sunce86 Sep 6, 2024
a2a2c6b
fix conflicts
sunce86 Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions crates/autopilot/src/domain/settlement/solution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ impl Solution {
match (total, protocol) {
(Ok(total), Ok(protocol)) => {
let network =
total.saturating_sub(protocol.iter().map(|(fee, _)| *fee).sum());
total.saturating_sub(protocol.iter().map(|(_, fee, _)| *fee).sum());
let protocol = protocol
.into_iter()
.map(|(token, amount, policy)| {
(ExecutedProtocolFee { token, amount }, policy)
})
.collect();
Some(ExecutedFee { protocol, network })
}
_ => None,
Expand Down Expand Up @@ -198,13 +204,21 @@ pub struct ExecutedFee {
pub network: eth::SellTokenAmount,
/// Breakdown of protocol fees. Executed protocol fees are in the same order
/// as policies are defined for an order.
pub protocol: Vec<(eth::SellTokenAmount, fee::Policy)>,
pub protocol: Vec<(ExecutedProtocolFee, fee::Policy)>,
}

#[derive(Debug, Clone)]
pub struct ExecutedProtocolFee {
/// Token in which the fee was paid
pub token: eth::TokenAddress,
/// Amount of the fee
pub amount: eth::SellTokenAmount,
}

impl ExecutedFee {
/// Total fee paid for the trade.
pub fn total(&self) -> eth::SellTokenAmount {
self.network + self.protocol.iter().map(|(fee, _)| *fee).sum()
self.network + self.protocol.iter().map(|(fee, _)| fee.amount).sum()
}
}

Expand Down
10 changes: 8 additions & 2 deletions crates/autopilot/src/domain/settlement/solution/trade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,16 @@ impl Trade {
pub fn protocol_fees_in_sell_token(
&self,
auction: &settlement::Auction,
) -> Result<Vec<(eth::SellTokenAmount, fee::Policy)>, Error> {
) -> Result<Vec<(eth::TokenAddress, eth::SellTokenAmount, fee::Policy)>, Error> {
self.protocol_fees(auction)?
.into_iter()
.map(|(fee, policy)| Ok((self.fee_into_sell_token(fee.amount)?, policy)))
.map(|(fee, policy)| {
Ok((
self.sell.token,
self.fee_into_sell_token(fee.amount)?,
policy,
))
})
.collect()
}

Expand Down
22 changes: 18 additions & 4 deletions crates/autopilot/src/infra/persistence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,15 +472,29 @@ impl Persistence {
)
.await;

for (order, executed_fee) in order_fees {
for (order, order_fee) in order_fees {
let executed_fee = order_fee
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
.as_ref()
.map(|fee| u256_to_big_decimal(&fee.total().0))
.unwrap_or_default();
let executed_protocol_fees = order_fee
.map(|fee| {
fee.protocol
.into_iter()
.map(|(fee, _)| database::order_execution::FeeAsset {
token: ByteArray(fee.token.0 .0),
amount: u256_to_big_decimal(&fee.amount.0),
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
database::order_execution::save(
&mut ex,
&ByteArray(order.0),
auction_id,
block_number,
&u256_to_big_decimal(
&executed_fee.map(|fee| fee.total()).unwrap_or_default().0,
),
&executed_fee,
&executed_protocol_fees,
)
.await?;
}
Expand Down
114 changes: 108 additions & 6 deletions crates/database/src/order_execution.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,103 @@
use {
crate::{auction::AuctionId, OrderUid},
crate::{auction::AuctionId, Address, OrderUid},
bigdecimal::BigDecimal,
sqlx::PgConnection,
sqlx::{PgConnection, QueryBuilder},
std::collections::HashMap,
};

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FeeAsset {
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
pub amount: BigDecimal,
pub token: Address,
}

pub async fn save(
ex: &mut PgConnection,
order: &OrderUid,
auction: AuctionId,
block_number: i64,
executed_fee: &BigDecimal,
executed_protocol_fees: &[FeeAsset],
) -> Result<(), sqlx::Error> {
let (protocol_fee_tokens, protocol_fee_amounts) = executed_protocol_fees.iter().fold(
(Vec::new(), Vec::new()),
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
|(mut tokens, mut amounts), fee| {
tokens.push(fee.token);
amounts.push(fee.amount.clone());
(tokens, amounts)
},
);

const QUERY: &str = r#"
INSERT INTO order_execution (order_uid, auction_id, reward, surplus_fee, block_number)
VALUES ($1, $2, $3, $4, $5)
INSERT INTO order_execution (order_uid, auction_id, reward, surplus_fee, block_number, protocol_fee_tokens, protocol_fee_amounts)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (order_uid, auction_id)
DO UPDATE SET reward = $3, surplus_fee = $4, block_number = $5
DO UPDATE SET reward = $3, surplus_fee = $4, block_number = $5, protocol_fee_tokens = $6, protocol_fee_amounts = $7
;"#;
sqlx::query(QUERY)
.bind(order)
.bind(auction)
.bind(0.) // reward is deprecated but saved for historical analysis
.bind(Some(executed_fee))
.bind(block_number)
.bind(protocol_fee_tokens)
.bind(protocol_fee_amounts)
.execute(ex)
.await?;
Ok(())
}

// fetch protocol fees for all keys in the filter
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
pub async fn executed_protocol_fees(
ex: &mut PgConnection,
keys_filter: &[(AuctionId, OrderUid)],
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<HashMap<(AuctionId, OrderUid), Vec<FeeAsset>>, sqlx::Error> {
if keys_filter.is_empty() {
return Ok(HashMap::new());
}

let mut query_builder = QueryBuilder::new(
"SELECT order_uid, auction_id, protocol_fee_tokens, protocol_fee_amounts FROM \
order_execution WHERE ",
);

for (i, (auction_id, order_uid)) in keys_filter.iter().enumerate() {
if i > 0 {
query_builder.push(" OR ");
}
query_builder
.push("(order_uid = ")
.push_bind(order_uid)
.push(" AND auction_id = ")
.push_bind(auction_id)
.push(")");
}

#[derive(Clone, Debug, Eq, PartialEq, sqlx::Type, sqlx::FromRow)]
struct ProtocolFees {
pub order_uid: OrderUid,
pub auction_id: AuctionId,
pub protocol_fee_tokens: Vec<Address>,
pub protocol_fee_amounts: Vec<BigDecimal>,
Comment on lines +78 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it guarantee in the query that we are retrieving the token and its corresponding amount in the same order? it could be dangerous in case the first token doesn't correspond with the first amount, etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caller is responsible for storing the values in proper order.

}
let query = query_builder.build_query_as::<ProtocolFees>();
let rows: Vec<ProtocolFees> = query.fetch_all(ex).await?;

let mut fees = HashMap::new();
for row in rows {
fees.insert(
(row.auction_id, row.order_uid),
row.protocol_fee_tokens
.into_iter()
.zip(row.protocol_fee_amounts)
.map(|(token, amount)| FeeAsset { token, amount })
.collect(),
);
}

Ok(fees)
}

#[cfg(test)]
mod tests {
use {super::*, sqlx::Connection};
Expand All @@ -39,8 +109,40 @@ mod tests {
let mut db = db.begin().await.unwrap();
crate::clear_DANGER_(&mut db).await.unwrap();

save(&mut db, &Default::default(), 1, 0, &Default::default())
// save entry with protocol fees
save(
&mut db,
&Default::default(),
1,
0,
&Default::default(),
&[
FeeAsset {
amount: Default::default(),
token: Default::default(),
},
FeeAsset {
amount: Default::default(),
token: Default::default(),
},
],
)
.await
.unwrap();

// save entry without protocol fees (simulate case when we are still not
// calculating them)
save(&mut db, &Default::default(), 2, 0, &Default::default(), &[])
.await
.unwrap();

let keys: Vec<(AuctionId, OrderUid)> = vec![
(1, Default::default()),
(2, Default::default()),
(3, Default::default()),
];

let protocol_fees = executed_protocol_fees(&mut db, &keys).await.unwrap();
assert_eq!(protocol_fees.len(), 2);
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
}
}
2 changes: 1 addition & 1 deletion crates/database/src/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1854,7 +1854,7 @@ mod tests {
assert_eq!(order.executed_surplus_fee, 0.into());

let fee: BigDecimal = 1.into();
crate::order_execution::save(&mut db, &order_uid, 1, 0, &fee)
crate::order_execution::save(&mut db, &order_uid, 1, 0, &fee, &[])
.await
.unwrap();

Expand Down
9 changes: 6 additions & 3 deletions crates/e2e/tests/e2e/limit_orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,11 +819,14 @@ async fn no_liquidity_limit_order(web3: Web3) {
assert!(balance_after.checked_sub(balance_before).unwrap() >= to_wei(5));

let trades = services.get_trades(&order_id).await.unwrap();
let (policy, executed_protocol_fee) = trades.first().unwrap().fee_policies.first().unwrap();
assert_eq!(
trades.first().unwrap().fee_policies,
vec![model::fee_policy::FeePolicy::Surplus {
policy,
&model::fee_policy::FeePolicy::Surplus {
factor: 0.5,
max_volume_factor: 0.01
}],
}
);
assert_eq!(executed_protocol_fee.token, token_a.address());
assert!(executed_protocol_fee.amount > U256::zero());
}
12 changes: 11 additions & 1 deletion crates/model/src/fee_policy.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
number::serialization::HexOrDecimalU256,
primitive_types::U256,
primitive_types::{H160, U256},
serde::Serialize,
serde_with::serde_as,
};
Expand Down Expand Up @@ -34,3 +34,13 @@ pub struct Quote {
#[serde_as(as = "HexOrDecimalU256")]
pub fee: U256,
}

#[serde_as]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[cfg_attr(any(test, feature = "e2e"), derive(serde::Deserialize))]
#[serde(rename_all = "camelCase")]
pub struct ExecutedFee {
#[serde_as(as = "HexOrDecimalU256")]
pub amount: U256,
pub token: H160,
}
Loading
Loading