From 4b927bd5d3b296c1dcc2a4b4cc88d73b8f705c88 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 May 2024 10:32:58 +0200 Subject: [PATCH] Revert "Update ethcontract-rs version (#2644)" This reverts commit 98e13aeb794717660d17a3a337f1cf2ca0bebbf1. --- Cargo.lock | 20 +- Cargo.toml | 6 +- crates/autopilot/src/decoded_settlement.rs | 1 - crates/autopilot/src/domain/settlement/mod.rs | 1 - .../domain/settlement/solution/tokenized.rs | 191 ++++++++++++++++++ crates/contracts/src/vault.rs | 6 +- .../src/price_estimation/trade_verifier.rs | 6 +- .../src/sources/balancer_v2/pools/common.rs | 24 +-- .../src/sources/balancer_v2/pools/weighted.rs | 2 +- .../src/sources/uniswap_v3/event_fetching.rs | 3 - 10 files changed, 219 insertions(+), 41 deletions(-) create mode 100644 crates/autopilot/src/domain/settlement/solution/tokenized.rs diff --git a/Cargo.lock b/Cargo.lock index 855af1a2a7..12bc5e8c2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1798,9 +1798,9 @@ dependencies = [ [[package]] name = "ethcontract" -version = "0.25.6" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdea297be3b8157af960773a0df81c239a3c542022191b3fd909001ada670a0c" +checksum = "762a89cc55c19c68066d5510a51df2d03857c99ccccd8a1c72916db669e1d8e2" dependencies = [ "arrayvec", "aws-config 0.55.3", @@ -1825,9 +1825,9 @@ dependencies = [ [[package]] name = "ethcontract-common" -version = "0.25.6" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43c48f35e613bb9955e31851226066e74bedac9c0ed237294293821eb9a7cce" +checksum = "4f1a4ffc732f4496733866edb14201e48ac5b4f5c5360aa02f1966e0a3343ced" dependencies = [ "ethabi", "hex", @@ -1841,9 +1841,9 @@ dependencies = [ [[package]] name = "ethcontract-derive" -version = "0.25.6" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1794a999ce5865be36caa407c3b5213cfa048420074b8053222be1b22ad2fb" +checksum = "53e1ad278c8fcbb25cb9a59fc9f9ef5d613f9f15bb9c87a0b12ee081116a230d" dependencies = [ "anyhow", "ethcontract-common", @@ -1855,9 +1855,9 @@ dependencies = [ [[package]] name = "ethcontract-generate" -version = "0.25.6" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2da9ad91f85983c5352264d4a7b17510f89bed227b04ab641ae64f759d633b4" +checksum = "6d6c427efdae115a35cb1111671b2db9a71e4ca22336019facb5d93d97f9b5b2" dependencies = [ "Inflector", "anyhow", @@ -1871,9 +1871,9 @@ dependencies = [ [[package]] name = "ethcontract-mock" -version = "0.25.6" +version = "0.25.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ef690f92015152e4bea45f140cf08a157031008d92e6c54ecfc9b7dac3e048" +checksum = "0d3804ea8b6c75124e8195a9eb663c3e85cd6e927e03c421050cc88be1668b94" dependencies = [ "ethcontract", "hex", diff --git a/Cargo.toml b/Cargo.toml index a5f68bf032..f7635b3dcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,9 @@ chrono = { version = "0.4.38", default-features = false } clap = { version = "4.5.4", features = ["derive", "env"] } derivative = "2.2.0" derive_more = "0.99.17" -ethcontract = { version = "0.25.6", default-features = false, features = ["aws-kms"] } -ethcontract-generate = { version = "0.25.6", default-features = false } -ethcontract-mock = { version = "0.25.6", default-features = false } +ethcontract = { version = "=0.25.5", default-features = false, features = ["aws-kms"] } +ethcontract-generate = { version = "=0.25.5", default-features = false } +ethcontract-mock = { version = "=0.25.5", default-features = false } ethereum-types = "0.14.1" flate2 = "1.0.28" futures = "0.3.30" diff --git a/crates/autopilot/src/decoded_settlement.rs b/crates/autopilot/src/decoded_settlement.rs index ec427710ff..ba78ac6bd7 100644 --- a/crates/autopilot/src/decoded_settlement.rs +++ b/crates/autopilot/src/decoded_settlement.rs @@ -224,7 +224,6 @@ impl DecodedSettlement { pub fn new(input: &[u8], domain_separator: &DomainSeparator) -> Result { let function = GPv2Settlement::raw_contract() - .interface .abi .function("settle") .unwrap(); diff --git a/crates/autopilot/src/domain/settlement/mod.rs b/crates/autopilot/src/domain/settlement/mod.rs index 49f7bdda00..a5c676e9f6 100644 --- a/crates/autopilot/src/domain/settlement/mod.rs +++ b/crates/autopilot/src/domain/settlement/mod.rs @@ -82,7 +82,6 @@ impl Settlement { domain_separator: ð::DomainSeparator, ) -> Result { let function = contracts::GPv2Settlement::raw_contract() - .interface .abi .function("settle") .unwrap(); diff --git a/crates/autopilot/src/domain/settlement/solution/tokenized.rs b/crates/autopilot/src/domain/settlement/solution/tokenized.rs new file mode 100644 index 0000000000..46e5559759 --- /dev/null +++ b/crates/autopilot/src/domain/settlement/solution/tokenized.rs @@ -0,0 +1,191 @@ +use { + crate::{ + boundary, + domain::{ + self, + auction::{self, order}, + eth, + }, + }, + app_data::AppDataHash, + ethcontract::{common::FunctionExt, tokens::Tokenize, Address, Bytes, U256}, +}; + +// Original type for input of `GPv2Settlement.settle` function. +pub(super) struct Tokenized { + pub tokens: Vec
, + pub clearing_prices: Vec, + pub trades: Vec, + pub interactions: [Vec; 3], + pub auction_id: auction::Id, +} + +impl Tokenized { + /// Number of bytes that may be appended to the calldata to store an auction + /// id. + const META_DATA_LEN: usize = 8; + + pub fn new(calldata: ð::Calldata) -> Result { + let function = contracts::GPv2Settlement::raw_contract() + .abi + .function("settle") + .unwrap(); + let data = calldata + .0 + .strip_prefix(&function.selector()) + .ok_or(error::Decoding::InvalidSelector)?; + + let (calldata, metadata) = data.split_at(data.len() - Self::META_DATA_LEN); + let metadata: Option<[u8; Self::META_DATA_LEN]> = metadata.try_into().ok(); + let auction_id = metadata + .map(auction::Id::from_be_bytes) + .ok_or(error::Decoding::MissingAuctionId)?; + + let tokenized = function + .decode_input(calldata) + .map_err(|err| error::Decoding::Ethabi(err, auction_id))?; + let (tokens, clearing_prices, trades, interactions) = + ::from_token(web3::ethabi::Token::Tuple(tokenized)) + .map_err(|err| error::Decoding::Tokenizing(err, auction_id))?; + Ok(Self { + tokens, + clearing_prices, + trades, + interactions, + auction_id, + }) + } +} + +type Token = Address; +type Trade = ( + U256, // sellTokenIndex + U256, // buyTokenIndex + Address, // receiver + U256, // sellAmount + U256, // buyAmount + u32, // validTo + Bytes<[u8; 32]>, // appData + U256, // feeAmount + U256, // flags + U256, // executedAmount + Bytes>, // signature +); +type Interaction = (Address, U256, Bytes>); +type Solution = (Vec
, Vec, Vec, [Vec; 3]); + +/// Recover order uid from order data and signature +pub fn order_uid( + trade: &Trade, + tokens: &[Token], + domain_separator: ð::DomainSeparator, +) -> Result { + let flags = TradeFlags(trade.8); + let signature = crate::boundary::Signature::from_bytes(flags.signing_scheme(), &trade.10 .0) + .map_err(error::Uid::Signature)?; + + let order = model::order::OrderData { + sell_token: tokens[trade.0.as_u64() as usize], + buy_token: tokens[trade.1.as_u64() as usize], + receiver: Some(trade.2), + sell_amount: trade.3, + buy_amount: trade.4, + valid_to: trade.5, + app_data: AppDataHash(trade.6 .0), + fee_amount: trade.7, + kind: match flags.side() { + domain::auction::order::Side::Buy => model::order::OrderKind::Buy, + domain::auction::order::Side::Sell => model::order::OrderKind::Sell, + }, + partially_fillable: flags.partially_fillable(), + sell_token_balance: flags.sell_token_balance(), + buy_token_balance: flags.buy_token_balance(), + }; + let domain_separator = crate::boundary::DomainSeparator(domain_separator.0); + let owner = signature + .recover_owner(&trade.10 .0, &domain_separator, &order.hash_struct()) + .map_err(error::Uid::RecoverOwner)?; + Ok(order.uid(&domain_separator, &owner).into()) +} + +/// Trade flags are encoded in a 256-bit integer field. For more information on +/// how flags are encoded see: +/// +#[derive(Debug, PartialEq, Eq)] +pub struct TradeFlags(pub U256); + +impl TradeFlags { + fn as_u8(&self) -> u8 { + self.0.byte(0) + } + + pub fn side(&self) -> order::Side { + if self.as_u8() & 0b1 == 0 { + order::Side::Sell + } else { + order::Side::Buy + } + } + + pub fn partially_fillable(&self) -> bool { + self.as_u8() & 0b10 != 0 + } + + pub fn sell_token_balance(&self) -> boundary::SellTokenSource { + if self.as_u8() & 0x08 == 0 { + boundary::SellTokenSource::Erc20 + } else if self.as_u8() & 0x04 == 0 { + boundary::SellTokenSource::External + } else { + boundary::SellTokenSource::Internal + } + } + + pub fn buy_token_balance(&self) -> boundary::BuyTokenDestination { + if self.as_u8() & 0x10 == 0 { + boundary::BuyTokenDestination::Erc20 + } else { + boundary::BuyTokenDestination::Internal + } + } + + pub fn signing_scheme(&self) -> boundary::SigningScheme { + match (self.as_u8() >> 5) & 0b11 { + 0b00 => boundary::SigningScheme::Eip712, + 0b01 => boundary::SigningScheme::EthSign, + 0b10 => boundary::SigningScheme::Eip1271, + 0b11 => boundary::SigningScheme::PreSign, + _ => unreachable!(), + } + } +} + +impl From for TradeFlags { + fn from(value: U256) -> Self { + Self(value) + } +} + +pub mod error { + use crate::domain::auction; + + #[derive(Debug, thiserror::Error)] + pub enum Uid { + #[error("bad signature {0}")] + Signature(anyhow::Error), + #[error("recover owner {0}")] + RecoverOwner(anyhow::Error), + } + + #[derive(Debug, thiserror::Error)] + pub enum Decoding { + #[error("transaction calldata is not a settlement")] + InvalidSelector, + #[error("no auction id found in calldata")] + MissingAuctionId, + #[error("unable to decode settlement calldata: {0}")] + Ethabi(web3::ethabi::Error, auction::Id), + #[error("unable to tokenize calldata into expected format: {0}")] + Tokenizing(ethcontract::tokens::Error, auction::Id), + } +} diff --git a/crates/contracts/src/vault.rs b/crates/contracts/src/vault.rs index b6ec9c278b..eff13412bb 100644 --- a/crates/contracts/src/vault.rs +++ b/crates/contracts/src/vault.rs @@ -7,11 +7,7 @@ use { }; fn role_id(target: H160, function_name: &str) -> Bytes<[u8; 32]> { - let function = match BalancerV2Vault::raw_contract() - .interface - .abi - .function(function_name) - { + let function = match BalancerV2Vault::raw_contract().abi.function(function_name) { Ok(function) => function, Err(_) => return Bytes([0u8; 32]), }; diff --git a/crates/shared/src/price_estimation/trade_verifier.rs b/crates/shared/src/price_estimation/trade_verifier.rs index eb70c46a09..89ac3d7620 100644 --- a/crates/shared/src/price_estimation/trade_verifier.rs +++ b/crates/shared/src/price_estimation/trade_verifier.rs @@ -464,11 +464,7 @@ struct SettleOutput { impl SettleOutput { fn decode(output: &[u8], kind: OrderKind) -> Result { - let function = Solver::raw_contract() - .interface - .abi - .function("swap") - .unwrap(); + let function = Solver::raw_contract().abi.function("swap").unwrap(); let tokens = function.decode_output(output).context("decode")?; let (gas_used, balances): (U256, Vec) = Tokenize::from_token(Token::Tuple(tokens))?; diff --git a/crates/shared/src/sources/balancer_v2/pools/common.rs b/crates/shared/src/sources/balancer_v2/pools/common.rs index 64786404aa..e2c4718b1b 100644 --- a/crates/shared/src/sources/balancer_v2/pools/common.rs +++ b/crates/shared/src/sources/balancer_v2/pools/common.rs @@ -360,11 +360,11 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2BasePool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2BasePool::raw_contract().abi.clone()); pool.expect_call(BalancerV2BasePool::signatures().get_pool_id()) .returns(Bytes(pool_id.0)); - let vault = mock.deploy(BalancerV2Vault::raw_contract().interface.abi.clone()); + let vault = mock.deploy(BalancerV2Vault::raw_contract().abi.clone()); vault .expect_call(BalancerV2Vault::signatures().get_pool_tokens()) .predicate((predicate::eq(Bytes(pool_id.0)),)) @@ -414,13 +414,13 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2BasePool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2BasePool::raw_contract().abi.clone()); pool.expect_call(BalancerV2BasePool::signatures().get_paused_state()) .returns((false, 0.into(), 0.into())); pool.expect_call(BalancerV2BasePool::signatures().get_swap_fee_percentage()) .returns(bfp!("0.003").as_uint256()); - let vault = mock.deploy(BalancerV2Vault::raw_contract().interface.abi.clone()); + let vault = mock.deploy(BalancerV2Vault::raw_contract().abi.clone()); vault .expect_call(BalancerV2Vault::signatures().get_pool_tokens()) .predicate((predicate::eq(Bytes(pool_id.0)),)) @@ -483,13 +483,13 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2BasePool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2BasePool::raw_contract().abi.clone()); pool.expect_call(BalancerV2BasePool::signatures().get_paused_state()) .returns((false, 0.into(), 0.into())); pool.expect_call(BalancerV2BasePool::signatures().get_swap_fee_percentage()) .returns(0.into()); - let vault = mock.deploy(BalancerV2Vault::raw_contract().interface.abi.clone()); + let vault = mock.deploy(BalancerV2Vault::raw_contract().abi.clone()); vault .expect_call(BalancerV2Vault::signatures().get_pool_tokens()) .predicate((predicate::eq(Bytes(Default::default())),)) @@ -532,7 +532,7 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().abi.clone()); pool.expect_call(BalancerV2WeightedPool::signatures().get_paused_state()) .returns((false, 0.into(), 0.into())); pool.expect_call(BalancerV2WeightedPool::signatures().get_swap_fee_percentage()) @@ -576,7 +576,7 @@ mod tests { version: Default::default(), }; - let vault = mock.deploy(BalancerV2Vault::raw_contract().interface.abi.clone()); + let vault = mock.deploy(BalancerV2Vault::raw_contract().abi.clone()); vault .expect_call(BalancerV2Vault::signatures().get_pool_tokens()) .predicate((predicate::eq(Bytes(pool_info.common.id.0)),)) @@ -630,13 +630,13 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().abi.clone()); pool.expect_call(BalancerV2WeightedPool::signatures().get_paused_state()) .returns((true, 0.into(), 0.into())); pool.expect_call(BalancerV2WeightedPool::signatures().get_swap_fee_percentage()) .returns(Default::default()); - let vault = mock.deploy(BalancerV2Vault::raw_contract().interface.abi.clone()); + let vault = mock.deploy(BalancerV2Vault::raw_contract().abi.clone()); vault .expect_call(BalancerV2Vault::signatures().get_pool_tokens()) .returns(Default::default()); @@ -690,13 +690,13 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().abi.clone()); pool.expect_call(BalancerV2WeightedPool::signatures().get_paused_state()) .returns((false, 0.into(), 0.into())); pool.expect_call(BalancerV2WeightedPool::signatures().get_swap_fee_percentage()) .returns(Default::default()); - let vault = mock.deploy(BalancerV2Vault::raw_contract().interface.abi.clone()); + let vault = mock.deploy(BalancerV2Vault::raw_contract().abi.clone()); vault .expect_call(BalancerV2Vault::signatures().get_pool_tokens()) .returns(Default::default()); diff --git a/crates/shared/src/sources/balancer_v2/pools/weighted.rs b/crates/shared/src/sources/balancer_v2/pools/weighted.rs index 6f49572745..1cc821f65f 100644 --- a/crates/shared/src/sources/balancer_v2/pools/weighted.rs +++ b/crates/shared/src/sources/balancer_v2/pools/weighted.rs @@ -224,7 +224,7 @@ mod tests { let mock = Mock::new(42); let web3 = mock.web3(); - let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().interface.abi.clone()); + let pool = mock.deploy(BalancerV2WeightedPool::raw_contract().abi.clone()); pool.expect_call(BalancerV2WeightedPool::signatures().get_normalized_weights()) .returns(weights.iter().copied().map(Bfp::as_uint256).collect()); diff --git a/crates/shared/src/sources/uniswap_v3/event_fetching.rs b/crates/shared/src/sources/uniswap_v3/event_fetching.rs index 9a314f251a..4abd9242ba 100644 --- a/crates/shared/src/sources/uniswap_v3/event_fetching.rs +++ b/crates/shared/src/sources/uniswap_v3/event_fetching.rs @@ -41,7 +41,6 @@ impl ParseLog for UniswapV3Event { H256(BURN_TOPIC) => Ok(UniswapV3Event::Burn( log.clone().decode( UniswapV3Pool::raw_contract() - .interface .abi .event("Burn") .expect("generated event decode"), @@ -50,7 +49,6 @@ impl ParseLog for UniswapV3Event { H256(MINT_TOPIC) => Ok(UniswapV3Event::Mint( log.clone().decode( UniswapV3Pool::raw_contract() - .interface .abi .event("Mint") .expect("generated event decode"), @@ -59,7 +57,6 @@ impl ParseLog for UniswapV3Event { H256(SWAP_TOPIC) => Ok(UniswapV3Event::Swap( log.clone().decode( UniswapV3Pool::raw_contract() - .interface .abi .event("Swap") .expect("generated event decode"),