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#>
This commit is contained in:
Diana Carvalho
2025-01-17 13:18:28 +00:00
parent e77a50b2bb
commit bab5caa6f8
12 changed files with 100 additions and 38 deletions

31
src/encoding/errors.rs Normal file
View File

@@ -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<io::Error> for EncodingError {
fn from(err: io::Error) -> Self {
EncodingError::FatalError(err.to_string())
}
}
impl From<serde_json::Error> for EncodingError {
fn from(err: serde_json::Error) -> Self {
EncodingError::FatalError(err.to_string())
}
}

View File

@@ -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<S: StrategySelector, A: UserApprovalsManager> EVMRouterEncoder<S, A> {
}
}
impl<S: StrategySelector, A: UserApprovalsManager> RouterEncoder<S, A> for EVMRouterEncoder<S, A> {
fn encode_router_calldata(&self, solutions: Vec<Solution>) -> Result<Vec<Transaction>, Error> {
fn encode_router_calldata(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<Transaction>, EncodingError> {
let _approvals_calldata = self.handle_approvals(&solutions)?; // TODO: where should we append this?
let mut transactions: Vec<Transaction> = Vec::new();
for solution in solutions.iter() {
@@ -50,7 +54,7 @@ impl<S: StrategySelector, A: UserApprovalsManager> RouterEncoder<S, A> for EVMRo
Ok(transactions)
}
fn handle_approvals(&self, solutions: &[Solution]) -> Result<Vec<Vec<u8>>, Error> {
fn handle_approvals(&self, solutions: &[Solution]) -> Result<Vec<Vec<u8>>, EncodingError> {
let mut approvals = Vec::new();
for solution in solutions.iter() {
approvals.push(Approval {

View File

@@ -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<Vec<u8>, Error> {
fn encode_strategy(&self, _solution: Solution) -> Result<Vec<u8>, 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<Vec<u8>, Error> {
fn encode_strategy(&self, solution: Solution) -> Result<Vec<u8>, 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 {

View File

@@ -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<Vec<u8>, Error> {
) -> Result<Vec<u8>, 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<Vec<u8>, Error> {
fn encode_swap(
&self,
swap: Swap,
encoding_context: EncodingContext,
) -> Result<Vec<u8>, EncodingError> {
let token_approvals_manager = ProtocolApprovalsManager::new();
let runtime = tokio::runtime::Handle::try_current()
.is_err()

View File

@@ -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<String, Box<dyn SwapEncoder>>,
@@ -40,7 +43,7 @@ pub struct Config {
}
impl Config {
pub fn from_file(path: &str) -> Result<Self, anyhow::Error> {
pub fn from_file(path: &str) -> Result<Self, EncodingError> {
let config_str = fs::read_to_string(path)?;
let config: Config = serde_json::from_str(&config_str)?;
Ok(config)

View File

@@ -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<Address, Error> {
pub fn bytes_to_address(address: &Bytes) -> Result<Address, EncodingError> {
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)))
}
}

View File

@@ -1,3 +1,4 @@
mod errors;
#[cfg(feature = "evm")]
mod evm;
mod models;

View File

@@ -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<S: StrategySelector, A: UserApprovalsManager> {
fn encode_router_calldata(&self, solutions: Vec<Solution>) -> Result<Vec<Transaction>, Error>;
fn handle_approvals(&self, solutions: &[Solution]) -> Result<Vec<Vec<u8>>, Error>;
fn encode_router_calldata(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<Transaction>, EncodingError>;
fn handle_approvals(&self, solutions: &[Solution]) -> Result<Vec<Vec<u8>>, EncodingError>;
}

View File

@@ -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<Vec<u8>, Error>;
fn encode_strategy(&self, to_encode: Solution) -> Result<Vec<u8>, EncodingError>;
fn selector(&self, exact_out: bool) -> &str;
}

View File

@@ -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<Vec<u8>, Error>;
fn encode_swap(
&self,
swap: Swap,
encoding_context: EncodingContext,
) -> Result<Vec<u8>, EncodingError>;
fn executor_address(&self) -> &str;
}