Skip to content

Commit

Permalink
chore: Cleanup Storage Accessible Simulations (#1904)
Browse files Browse the repository at this point in the history
# Description

We recently fixed an issue in `ethcontract` that made using
`StorageAccessible` simulations more convenient. This PR refactors
existing code that made use of these simulations to use the more
ergonomic `simulate` method which is now possible.

# Changes

- Update `ethcontract` to the latest v0.25.2 release
- Refactor existing `StorageAccessible` simulation code to use the more
ergonomic `simulate` function.

## How to test

Existing tests cover both balance and signature simulations with
pre-interactions.
  • Loading branch information
Nicholas Rodrigues Lordello authored Oct 2, 2023
1 parent 986613c commit f6f4f9a
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 138 deletions.
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cached = { version = "0.44", default-features = false }
chrono = { version = "0.4", default-features = false }
clap = { version = "4", features = ["derive", "env"] }
derivative = "2"
ethcontract = { version = "0.25.1", default-features = false, features = ["aws-kms"] }
ethcontract = { version = "0.25.2", default-features = false, features = ["aws-kms"] }
ethcontract-generate = { version = "0.25", default-features = false }
ethcontract-mock = { version = "0.25", default-features = false }
ethereum-types = "0.14"
Expand Down
4 changes: 2 additions & 2 deletions crates/autopilot/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,22 @@ pub async fn run(args: Arguments) {
.expect("unknown network block interval");

let signature_validator = signature_validator::validator(
&web3,
signature_validator::Contracts {
chain_id,
settlement: settlement_contract.address(),
vault_relayer,
},
web3.clone(),
);

let balance_fetcher = account_balances::cached(
&web3,
account_balances::Contracts {
chain_id,
settlement: settlement_contract.address(),
vault_relayer,
vault: vault.as_ref().map(|contract| contract.address()),
},
web3.clone(),
current_block_stream.clone(),
);

Expand Down
25 changes: 4 additions & 21 deletions crates/contracts/src/storage_accessible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use {
web3::{
types::{Bytes, CallRequest},
Transport,
Web3,
},
H160,
},
Expand Down Expand Up @@ -55,28 +54,12 @@ pub fn call(target: H160, code: Bytes, call: Bytes) -> CallRequest {
/// - The method doesn't specify a target address or calldata
/// - The function name doesn't exist or match the method signature
/// - The contract does not have deployment code
pub async fn simulate<T, R>(
web3: &Web3<T>,
contract: &ethcontract::Contract,
function_name: &str,
method: MethodBuilder<T, R>,
) -> Result<R, MethodError>
pub async fn simulate<T, R>(code: Bytes, mut method: MethodBuilder<T, R>) -> Result<R, MethodError>
where
T: Transport,
R: Tokenize,
{
let function = contract.abi.function(function_name).unwrap();
let code = contract.bytecode.to_bytes().unwrap();

let call = call(method.tx.to.unwrap(), code, method.tx.data.clone().unwrap());
let output = web3
.eth()
.call(call, None)
.await
.map_err(|err| MethodError::new(function, err))?;

let tokens = function
.decode_output(&output.0)
.map_err(|err| MethodError::new(function, err))?;
R::from_token(abi::Token::Tuple(tokens)).map_err(|err| MethodError::new(function, err))
method.tx.data = call(method.tx.to.unwrap(), code, method.tx.data.take().unwrap()).data;
method.tx.to = None;
method.call().await
}
4 changes: 1 addition & 3 deletions crates/driver/src/infra/blockchain/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ impl Erc20 {
interactions: &[eth::Interaction],
) -> Result<eth::TokenAmount, Error> {
let (_, _, effective_balance, can_transfer) = contracts::storage_accessible::simulate(
&self.balances.raw_instance().web3(),
contracts::support::Balances::raw_contract(),
"balance",
contracts::bytecode!(contracts::support::Balances),
self.balances.balance(
(
self.balances.address(),
Expand Down
4 changes: 2 additions & 2 deletions crates/orderbook/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ pub async fn run(args: Arguments) {
let network_name = network_name(&network, chain_id);

let signature_validator = signature_validator::validator(
&web3,
signature_validator::Contracts {
chain_id,
settlement: settlement_contract.address(),
vault_relayer,
},
web3.clone(),
);

let vault = match args.shared.balancer_v2_vault_address {
Expand Down Expand Up @@ -161,13 +161,13 @@ pub async fn run(args: Arguments) {
let postgres = Postgres::new(args.db_url.as_str()).expect("failed to create database");

let balance_fetcher = account_balances::fetcher(
&web3,
account_balances::Contracts {
chain_id,
settlement: settlement_contract.address(),
vault_relayer,
vault: vault.as_ref().map(|contract| contract.address()),
},
web3.clone(),
);

let gas_price_estimator = Arc::new(InstrumentedGasEstimator::new(
Expand Down
6 changes: 3 additions & 3 deletions crates/shared/src/account_balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub struct Contracts {
}

/// Create the default [`BalanceFetching`] instance.
pub fn fetcher(contracts: Contracts, web3: Web3) -> Arc<dyn BalanceFetching> {
pub fn fetcher(web3: &Web3, contracts: Contracts) -> Arc<dyn BalanceFetching> {
Arc::new(simulation::Balances::new(
web3,
contracts.settlement,
Expand All @@ -85,11 +85,11 @@ pub fn fetcher(contracts: Contracts, web3: Web3) -> Arc<dyn BalanceFetching> {

/// Create a cached [`BalanceFetching`] instance.
pub fn cached(
web3: &Web3,
contracts: Contracts,
web3: Web3,
blocks: CurrentBlockStream,
) -> Arc<dyn BalanceFetching> {
let cached = Arc::new(cached::Balances::new(fetcher(contracts, web3)));
let cached = Arc::new(cached::Balances::new(fetcher(web3, contracts)));
cached.spawn_background_task(blocks);
cached
}
77 changes: 28 additions & 49 deletions crates/shared/src/account_balances/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@
use {
super::{BalanceFetching, Query, TransferSimulationError},
anyhow::{Context, Result},
ethcontract::{tokens::Tokenize, Bytes, H160, U256},
anyhow::Result,
ethcontract::{Bytes, H160, U256},
ethrpc::Web3,
futures::future,
web3::ethabi::Token,
};

pub struct Balances {
web3: Web3,
balances: contracts::support::Balances,
settlement: H160,
vault_relayer: H160,
vault: H160,
}

impl Balances {
pub fn new(web3: Web3, settlement: H160, vault_relayer: H160, vault: Option<H160>) -> Self {
pub fn new(web3: &Web3, settlement: H160, vault_relayer: H160, vault: Option<H160>) -> Self {
// Note that the balances simulation **will fail** if the `vault`
// address is not a contract and the `source` is set to one of
// `SellTokenSource::{External, Internal}` (i.e. the Vault contract is
Expand All @@ -30,7 +29,7 @@ impl Balances {
let vault = vault.unwrap_or_default();

Self {
web3,
balances: contracts::support::Balances::at(web3, settlement),
settlement,
vault_relayer,
vault,
Expand All @@ -45,31 +44,30 @@ impl Balances {
// settlement
//
// This allows us to end up with very accurate balance simulations.
let balances = contracts::dummy_contract!(contracts::support::Balances, self.settlement);
let tx = balances
.methods()
.balance(
(self.settlement, self.vault_relayer, self.vault),
query.owner,
query.token,
amount.unwrap_or_default(),
Bytes(query.source.as_bytes()),
query
.interactions
.iter()
.map(|i| (i.target, i.value, Bytes(i.call_data.clone())))
.collect(),
let (token_balance, allowance, effective_balance, can_transfer) =
contracts::storage_accessible::simulate(
contracts::bytecode!(contracts::support::Balances),
self.balances.methods().balance(
(self.settlement, self.vault_relayer, self.vault),
query.owner,
query.token,
amount.unwrap_or_default(),
Bytes(query.source.as_bytes()),
query
.interactions
.iter()
.map(|i| (i.target, i.value, Bytes(i.call_data.clone())))
.collect(),
),
)
.tx;

let call = contracts::storage_accessible::call(
self.settlement,
contracts::bytecode!(contracts::support::Balances),
tx.data.unwrap(),
);
.await?;

let output = self.web3.eth().call(call, None).await?;
let simulation = Simulation::decode(&output.0)?;
let simulation = Simulation {
token_balance,
allowance,
effective_balance,
can_transfer,
};

tracing::trace!(?query, ?amount, ?simulation, "simulated balances");
Ok(simulation)
Expand All @@ -84,25 +82,6 @@ struct Simulation {
can_transfer: bool,
}

impl Simulation {
fn decode(output: &[u8]) -> Result<Self> {
let function = contracts::support::Balances::raw_contract()
.abi
.function("balance")
.unwrap();
let tokens = function.decode_output(output).context("decode")?;
let (token_balance, allowance, effective_balance, can_transfer) =
Tokenize::from_token(Token::Tuple(tokens))?;

Ok(Self {
token_balance,
allowance,
effective_balance,
can_transfer,
})
}
}

#[async_trait::async_trait]
impl BalanceFetching for Balances {
async fn get_balances(&self, queries: &[Query]) -> Vec<Result<U256>> {
Expand Down Expand Up @@ -155,7 +134,7 @@ mod tests {
#[tokio::test]
async fn test_for_user() {
let balances = Balances::new(
Web3::new(ethrpc::create_env_test_transport()),
&Web3::new(ethrpc::create_env_test_transport()),
addr!("9008d19f58aabd9ed0d60971565aa8510560ab41"),
addr!("C92E8bdf79f0507f65a392b0ab4667716BFE0110"),
Some(addr!("BA12222222228d8Ba445958a75a0704d566BF2C8")),
Expand Down
2 changes: 1 addition & 1 deletion crates/shared/src/signature_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub struct Contracts {
}

/// Creates the default [`SignatureValidating`] instance.
pub fn validator(contracts: Contracts, web3: Web3) -> Arc<dyn SignatureValidating> {
pub fn validator(web3: &Web3, contracts: Contracts) -> Arc<dyn SignatureValidating> {
Arc::new(simulation::Validator::new(
web3,
contracts.settlement,
Expand Down
Loading

0 comments on commit f6f4f9a

Please sign in to comment.