From bab5caa6f8c248dbc0cce8cf9bdc82b73b89e92c Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Fri, 17 Jan 2025 13:18:28 +0000 Subject: [PATCH] feat: Add EncodingError Change method signatures to expect it and raise it where it makes sense --- don't change below this line --- ENG-4076 <#DTT#> --- Cargo.lock | 2 +- Cargo.toml | 11 ++++--- src/encoding/errors.rs | 31 ++++++++++++++++++++ src/encoding/evm/router_encoder.rs | 10 +++++-- src/encoding/evm/strategy_encoder/encoder.rs | 25 +++++++++++----- src/encoding/evm/swap_encoder/encoders.rs | 10 +++++-- src/encoding/evm/swap_encoder/registry.rs | 7 +++-- src/encoding/evm/utils.rs | 9 +++--- src/encoding/mod.rs | 1 + src/encoding/router_encoder.rs | 10 ++++--- src/encoding/strategy_encoder.rs | 9 +++--- src/encoding/swap_encoder.rs | 13 +++++--- 12 files changed, 100 insertions(+), 38 deletions(-) create mode 100644 src/encoding/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 9c30ed2..f2fe43d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3044,7 +3044,6 @@ dependencies = [ "alloy", "alloy-primitives", "alloy-sol-types", - "anyhow", "dotenv", "hex", "lazy_static", @@ -3052,6 +3051,7 @@ dependencies = [ "num-traits", "serde", "serde_json", + "thiserror 1.0.69", "tokio", "tycho-core", ] diff --git a/Cargo.toml b/Cargo.toml index 3708a12..1a1eeec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,18 +7,17 @@ edition = "2021" dotenv = "0.15.0" lazy_static = "1.4.0" num-bigint = "0.4.6" - +hex = "0.4.3" +num-traits = "0.2.19" +serde = { version = "1.0.217", features = ["derive"] } +serde_json = "1.0.135" +thiserror = "1.0.69" tokio = { version = "1.38.0", features = ["full"] } alloy = { version = "0.5.4", features = ["providers"], optional = true } alloy-sol-types = { version = "0.8.14", optional = true } alloy-primitives = { version = "0.8.9", optional = true } tycho-core = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-core", tag = "0.46.0" } -hex = "0.4.3" -anyhow = "1.0.95" -num-traits = "0.2.19" -serde = { version = "1.0.217", features = ["derive"] } -serde_json = "1.0.135" [features] default = ["evm"] diff --git a/src/encoding/errors.rs b/src/encoding/errors.rs new file mode 100644 index 0000000..172d538 --- /dev/null +++ b/src/encoding/errors.rs @@ -0,0 +1,31 @@ +use std::io; + +use thiserror::Error; + +/// Represents the outer-level, user-facing errors of the tycho-execution encoding package. +/// +/// `EncodingError` encompasses all possible errors that can occur in the package, +/// wrapping lower-level errors in a user-friendly way for easier handling and display. +/// Variants: +/// - `InvalidInput`: Indicates that the encoding has failed due to bad input parameters. +/// - `FatalError`: There is problem with the application setup. +#[derive(Error, Debug)] +#[allow(dead_code)] +pub enum EncodingError { + #[error("Invalid input: {0}")] + InvalidInput(String), + #[error("Fatal error: {0}")] + FatalError(String), +} + +impl From for EncodingError { + fn from(err: io::Error) -> Self { + EncodingError::FatalError(err.to_string()) + } +} + +impl From for EncodingError { + fn from(err: serde_json::Error) -> Self { + EncodingError::FatalError(err.to_string()) + } +} diff --git a/src/encoding/evm/router_encoder.rs b/src/encoding/evm/router_encoder.rs index 30b49fc..d5bdd60 100644 --- a/src/encoding/evm/router_encoder.rs +++ b/src/encoding/evm/router_encoder.rs @@ -1,7 +1,8 @@ -use anyhow::Error; +use alloy_sol_types::SolValue; use num_bigint::BigUint; use crate::encoding::{ + errors::EncodingError, evm::utils::encode_input, models::{NativeAction, Solution, Transaction, PROPELLER_ROUTER_ADDRESS}, router_encoder::RouterEncoder, @@ -22,7 +23,10 @@ impl EVMRouterEncoder { } } impl RouterEncoder for EVMRouterEncoder { - fn encode_router_calldata(&self, solutions: Vec) -> Result, Error> { + fn encode_router_calldata( + &self, + solutions: Vec, + ) -> Result, EncodingError> { let _approvals_calldata = self.handle_approvals(&solutions)?; // TODO: where should we append this? let mut transactions: Vec = Vec::new(); for solution in solutions.iter() { @@ -50,7 +54,7 @@ impl RouterEncoder for EVMRo Ok(transactions) } - fn handle_approvals(&self, solutions: &[Solution]) -> Result>, Error> { + fn handle_approvals(&self, solutions: &[Solution]) -> Result>, EncodingError> { let mut approvals = Vec::new(); for solution in solutions.iter() { approvals.push(Approval { diff --git a/src/encoding/evm/strategy_encoder/encoder.rs b/src/encoding/evm/strategy_encoder/encoder.rs index 8240147..2e93a0d 100644 --- a/src/encoding/evm/strategy_encoder/encoder.rs +++ b/src/encoding/evm/strategy_encoder/encoder.rs @@ -1,8 +1,10 @@ use alloy_primitives::Address; use alloy_sol_types::SolValue; -use anyhow::Error; +use num_bigint::BigUint; +use num_traits::Zero; use crate::encoding::{ + errors::EncodingError, evm::swap_encoder::SWAP_ENCODER_REGISTRY, models::{EncodingContext, Solution}, strategy_encoder::StrategyEncoder, @@ -27,7 +29,7 @@ pub trait EVMStrategyEncoder: StrategyEncoder { pub struct SplitSwapStrategyEncoder {} impl EVMStrategyEncoder for SplitSwapStrategyEncoder {} impl StrategyEncoder for SplitSwapStrategyEncoder { - fn encode_strategy(&self, _solution: Solution) -> Result, Error> { + fn encode_strategy(&self, _solution: Solution) -> Result, EncodingError> { todo!() } fn selector(&self, _exact_out: bool) -> &str { @@ -40,17 +42,26 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { pub struct StraightToPoolStrategyEncoder {} impl EVMStrategyEncoder for StraightToPoolStrategyEncoder {} impl StrategyEncoder for StraightToPoolStrategyEncoder { - fn encode_strategy(&self, solution: Solution) -> Result, Error> { + fn encode_strategy(&self, solution: Solution) -> Result, EncodingError> { if solution.router_address.is_none() { - return Err(anyhow::anyhow!( - "Router address is required for straight to pool solutions" + return Err(EncodingError::InvalidInput( + "Router address is required for straight to pool solutions".to_string(), )); } let swap = solution.swaps.first().unwrap(); - let registry = SWAP_ENCODER_REGISTRY.read().unwrap(); + let registry = SWAP_ENCODER_REGISTRY + .read() + .map_err(|_| { + EncodingError::FatalError("Failed to read the swap encoder registry".to_string()) + })?; let swap_encoder = registry .get_encoder(&swap.component.protocol_system) - .expect("Swap encoder not found"); + .ok_or_else(|| { + EncodingError::InvalidInput(format!( + "Swap encoder not found for protocol: {}", + swap.component.protocol_system + )) + })?; let router_address = solution.router_address.unwrap(); let encoding_context = EncodingContext { diff --git a/src/encoding/evm/swap_encoder/encoders.rs b/src/encoding/evm/swap_encoder/encoders.rs index dc90acc..a397be3 100644 --- a/src/encoding/evm/swap_encoder/encoders.rs +++ b/src/encoding/evm/swap_encoder/encoders.rs @@ -2,9 +2,9 @@ use std::str::FromStr; use alloy_primitives::Address; use alloy_sol_types::SolValue; -use anyhow::Error; use crate::encoding::{ + errors::EncodingError, evm::{ approvals::protocol_approvals_manager::ProtocolApprovalsManager, utils::bytes_to_address, }, @@ -25,7 +25,7 @@ impl SwapEncoder for UniswapV2SwapEncoder { &self, _swap: Swap, _encoding_context: EncodingContext, - ) -> Result, Error> { + ) -> Result, EncodingError> { todo!() } @@ -47,7 +47,11 @@ impl SwapEncoder for BalancerV2SwapEncoder { .expect("Invalid string for balancer vault address"), } } - fn encode_swap(&self, swap: Swap, encoding_context: EncodingContext) -> Result, Error> { + fn encode_swap( + &self, + swap: Swap, + encoding_context: EncodingContext, + ) -> Result, EncodingError> { let token_approvals_manager = ProtocolApprovalsManager::new(); let runtime = tokio::runtime::Handle::try_current() .is_err() diff --git a/src/encoding/evm/swap_encoder/registry.rs b/src/encoding/evm/swap_encoder/registry.rs index d78d01c..06e6147 100644 --- a/src/encoding/evm/swap_encoder/registry.rs +++ b/src/encoding/evm/swap_encoder/registry.rs @@ -3,7 +3,10 @@ use std::{collections::HashMap, fs}; use serde::Deserialize; use tycho_core::dto::Chain; -use crate::encoding::{evm::swap_encoder::builder::SwapEncoderBuilder, swap_encoder::SwapEncoder}; +use crate::encoding::{ + errors::EncodingError, evm::swap_encoder::builder::SwapEncoderBuilder, + swap_encoder::SwapEncoder, +}; pub struct SwapEncoderRegistry { encoders: HashMap>, @@ -40,7 +43,7 @@ pub struct Config { } impl Config { - pub fn from_file(path: &str) -> Result { + pub fn from_file(path: &str) -> Result { let config_str = fs::read_to_string(path)?; let config: Config = serde_json::from_str(&config_str)?; Ok(config) diff --git a/src/encoding/evm/utils.rs b/src/encoding/evm/utils.rs index 75ef285..c1b7d56 100644 --- a/src/encoding/evm/utils.rs +++ b/src/encoding/evm/utils.rs @@ -1,18 +1,19 @@ use alloy_primitives::{Address, Keccak256, U256}; use alloy_sol_types::SolValue; -use anyhow::Error; use num_bigint::BigUint; use tycho_core::Bytes; +use crate::encoding::errors::EncodingError; + /// Safely converts a `Bytes` object to an `Address` object. /// -/// Checks the length of the `Bytes` before attempting to convert, and returns a `SimulationError` +/// Checks the length of the `Bytes` before attempting to convert, and returns an `EncodingError` /// if not 20 bytes long. -pub fn bytes_to_address(address: &Bytes) -> Result { +pub fn bytes_to_address(address: &Bytes) -> Result { if address.len() == 20 { Ok(Address::from_slice(address)) } else { - Err(anyhow::format_err!("Invalid ERC20 token address: {:?}", address)) + Err(EncodingError::InvalidInput(format!("Invalid ERC20 token address: {:?}", address))) } } diff --git a/src/encoding/mod.rs b/src/encoding/mod.rs index 12cdf8a..495a30a 100644 --- a/src/encoding/mod.rs +++ b/src/encoding/mod.rs @@ -1,3 +1,4 @@ +mod errors; #[cfg(feature = "evm")] mod evm; mod models; diff --git a/src/encoding/router_encoder.rs b/src/encoding/router_encoder.rs index e653d02..be6d1d5 100644 --- a/src/encoding/router_encoder.rs +++ b/src/encoding/router_encoder.rs @@ -1,6 +1,5 @@ -use anyhow::Error; - use crate::encoding::{ + errors::EncodingError, models::{Solution, Transaction}, strategy_encoder::StrategySelector, user_approvals_manager::UserApprovalsManager, @@ -8,6 +7,9 @@ use crate::encoding::{ #[allow(dead_code)] pub trait RouterEncoder { - fn encode_router_calldata(&self, solutions: Vec) -> Result, Error>; - fn handle_approvals(&self, solutions: &[Solution]) -> Result>, Error>; + fn encode_router_calldata( + &self, + solutions: Vec, + ) -> Result, EncodingError>; + fn handle_approvals(&self, solutions: &[Solution]) -> Result>, EncodingError>; } diff --git a/src/encoding/strategy_encoder.rs b/src/encoding/strategy_encoder.rs index 844311b..80bff23 100644 --- a/src/encoding/strategy_encoder.rs +++ b/src/encoding/strategy_encoder.rs @@ -1,10 +1,11 @@ -use anyhow::Error; - -use crate::encoding::models::Solution; +use crate::encoding::{ + errors::EncodingError, + models::{ActionType, Solution}, +}; #[allow(dead_code)] pub trait StrategyEncoder { - fn encode_strategy(&self, to_encode: Solution) -> Result, Error>; + fn encode_strategy(&self, to_encode: Solution) -> Result, EncodingError>; fn selector(&self, exact_out: bool) -> &str; } diff --git a/src/encoding/swap_encoder.rs b/src/encoding/swap_encoder.rs index c361485..1a1fc0c 100644 --- a/src/encoding/swap_encoder.rs +++ b/src/encoding/swap_encoder.rs @@ -1,12 +1,17 @@ -use anyhow::Error; - -use crate::encoding::models::{EncodingContext, Swap}; +use crate::encoding::{ + errors::EncodingError, + models::{EncodingContext, Swap}, +}; #[allow(dead_code)] pub trait SwapEncoder: Sync + Send { fn new(executor_address: String) -> Self where Self: Sized; - fn encode_swap(&self, swap: Swap, encoding_context: EncodingContext) -> Result, Error>; + fn encode_swap( + &self, + swap: Swap, + encoding_context: EncodingContext, + ) -> Result, EncodingError>; fn executor_address(&self) -> &str; }