Skip to content

Commit

Permalink
Speed up balance fetching (#2045)
Browse files Browse the repository at this point in the history
# Description
The driver has a routine to prioritize orders and filter orders based on
the balance the trader currently has.
With the introduction of pre-interactions this check became much
trickier because a user might not have any sell_tokens at the moment but
unstakes the needed tokens in a pre-interaction. Because of that we
introduced a way to get the actual tradable balance by simulating a
balance check using a helper contract.
This is very accurate but unfortunately also very slow. (~10s just for
fetching the balances of all traders in an auction)
Luckily most of our orders don't have pre-interactions so we can fall
back to a simpler and faster way to query the current balance.

# Changes
Reimplement parts of the balance query of
[balances.sol](https://github.com/cowprotocol/services/blob/main/crates/contracts/solidity/Balances.sol#L59-L76)
using simple `eth_call`s and use that for any order group that does not
contain any pre-interactions.
While testing locally this reduced the time to fetch all balances to ~
1.2 - 2 seconds.

## How to test
covered by e2e tests

## Related Issues
#2041
  • Loading branch information
MartinquaXD authored Nov 7, 2023
1 parent 2cab32c commit 327b5eb
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 additions & 0 deletions crates/driver/src/infra/blockchain/token.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use {
super::{Error, Ethereum},
crate::domain::{competition::order, eth},
contracts::{dummy_contract, BalancerV2Vault},
futures::TryFutureExt,
};

/// An ERC-20 token.
Expand Down Expand Up @@ -91,6 +93,23 @@ impl Erc20 {
trader: eth::Address,
source: order::SellTokenBalance,
interactions: &[eth::Interaction],
) -> Result<eth::TokenAmount, Error> {
if interactions.is_empty() {
self.tradable_balance_simple(trader, source).await
} else {
self.tradable_balance_simulated(trader, source, interactions)
.await
}
}

/// Uses a custom helper contract to simulate balances while taking
/// pre-interactions into account. This is the most accurate method to
/// compute tradable balances but is very slow.
async fn tradable_balance_simulated(
&self,
trader: eth::Address,
source: order::SellTokenBalance,
interactions: &[eth::Interaction],
) -> Result<eth::TokenAmount, Error> {
let (_, _, effective_balance, can_transfer) = contracts::storage_accessible::simulate(
contracts::bytecode!(contracts::support::Balances),
Expand Down Expand Up @@ -124,6 +143,59 @@ impl Erc20 {
Ok(eth::TokenAmount(0.into()))
}
}

/// Faster balance query that does not take pre-interactions into account.
async fn tradable_balance_simple(
&self,
trader: eth::Address,
source: order::SellTokenBalance,
) -> Result<eth::TokenAmount, Error> {
use order::SellTokenBalance::*;

let usable_balance = match source {
Erc20 => {
let balance = self.balance(trader);
let allowance = self.allowance(trader, eth::Address(self.vault_relayer.0));
let (balance, allowance) = futures::try_join!(balance, allowance)?;
std::cmp::min(balance.0, allowance.0.amount)
}
External => {
let vault = dummy_contract!(BalancerV2Vault, self.vault);
let balance = self.balance(trader);
let approved = vault
.methods()
.has_approved_relayer(trader.0, self.vault_relayer.0)
.call()
.map_err(Error::from);
let allowance = self.allowance(trader, eth::Address(self.vault.0));
let (balance, approved, allowance) =
futures::try_join!(balance, approved, allowance)?;
match approved {
true => std::cmp::min(balance.0, allowance.0.amount),
false => 0.into(),
}
}
Internal => {
let vault = dummy_contract!(BalancerV2Vault, self.vault);
let balance = vault
.methods()
.get_internal_balance(trader.0, vec![self.token.address()])
.call()
.map_err(Error::from);
let approved = vault
.methods()
.has_approved_relayer(trader.0, self.vault_relayer.0)
.call()
.map_err(Error::from);
let (balance, approved) = futures::try_join!(balance, approved)?;
match approved {
true => balance[0], // internal approvals are always U256::MAX
false => 0.into(),
}
}
};
Ok(eth::TokenAmount(usable_balance))
}
}

/// Returns `true` if a [`ethcontract::errors::MethodError`] is the result of
Expand Down

0 comments on commit 327b5eb

Please sign in to comment.