Merge pull request #8 from propeller-heads/encoding/dc/ENG-4076-error-handling
feat: Add EncodingError
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
11
Cargo.toml
11
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"]
|
||||
|
||||
36
src/encoding/errors.rs
Normal file
36
src/encoding/errors.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
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.
|
||||
/// - `RecoverableError`: Indicates that the encoding has failed with a recoverable error. Retrying
|
||||
/// at a later time may succeed. It may have failed due to a temporary issue, such as a network
|
||||
/// problem.
|
||||
#[derive(Error, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum EncodingError {
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
#[error("Fatal error: {0}")]
|
||||
FatalError(String),
|
||||
#[error("Recoverable error: {0}")]
|
||||
RecoverableError(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())
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::Error;
|
||||
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 +22,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 +53,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 {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use alloy_primitives::Address;
|
||||
use alloy_sol_types::SolValue;
|
||||
use anyhow::Error;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::swap_encoder::SWAP_ENCODER_REGISTRY,
|
||||
models::{EncodingContext, Solution},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
@@ -27,7 +27,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 +40,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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod errors;
|
||||
#[cfg(feature = "evm")]
|
||||
mod evm;
|
||||
mod models;
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use anyhow::Error;
|
||||
|
||||
use crate::encoding::models::Solution;
|
||||
use crate::encoding::{errors::EncodingError, models::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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user