feat: Separate encoding swaps from encoding txs

This way the user is responsible for encoding the Tycho Router method inputs that are used as guardrails in execution.

Interface changes:
- Create EncodedSolution
- StrategyEncoder
  - don't need to know have permit2 or token_in_already_in_router as attributes anymore
  - encode_strategy returns EncodedSolution now (no method encoding done here now)
- TychoEncoder
  - add encode_solution() method. This is the recommended method for users
  - needs to have permit2, token_in_already_in_router and router_address as attributes
  - permit creation is made in the router now

Also:
- create encoding_utils.rs
- update all tests

Took 2 hours 42 minutes


Took 3 minutes

Took 13 minutes
This commit is contained in:
Diana Carvalho
2025-05-21 13:33:46 +01:00
parent 6a6955d31d
commit 4e8c6ddc8c
12 changed files with 869 additions and 420 deletions

View File

@@ -1,20 +1,75 @@
use crate::encoding::{
errors::EncodingError,
models::{Solution, Transaction},
models::{EncodedSolution, Solution, Transaction},
};
/// A high-level encoder that converts solutions into executable transactions. Allows for modularity
/// in the encoding process.
/// A high-level interface for encoding solutions into Tycho-compatible transactions or raw call
/// data.
///
/// This trait is designed to abstract the encoding logic required to prepare swap transactions for
/// the Tycho Router. It enables modular and customizable construction of transactions, allowing
/// integrators to maintain full control over the execution constraints.
///
/// # User Responsibility
///
/// While this trait provides convenience methods, it is **strongly recommended** that users favor
/// [`encode_solutions`] over [`encode_calldata`]. This is because:
///
/// - `encode_solutions` returns raw [`EncodedSolution`] objects, which include Tychos swap path
/// encoding, but leave **function argument encoding entirely in the users hands**.
/// - The function arguments to the router (e.g., `minAmountOut`, `receiver`, `unwrap`, `permit2`,
/// etc.) are used as **guardrails** to ensure safe on-chain execution.
/// - Automatically constructing full transactions via [`encode_calldata`] can obscure these
/// important safeguards and may result in unexpected behavior or vulnerability to MEV.
///
/// Tycho is only responsible for generating the internal swap plan. **The user must encode the
/// outer function call arguments themselves** and verify that they enforce correct and secure
/// behavior.
pub trait TychoEncoder {
/// Encodes solutions into transactions that can be executed by the Tycho router.
/// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the selector and
/// internal swap call data.
///
/// # Arguments
/// * `solutions` - Vector of solutions to encode, each potentially using different setups (swap
/// paths, protocols, wrapping, etc.)
/// This method gives users maximum flexibility and control. It **does not** produce full
/// transaction objects. Users are responsible for:
/// - Constructing the full calldata using their own encoding logic.
/// - Managing execution-critical parameters like `minAmountOut`.
///
/// # Returns
/// * `Result<Vec<Transaction>, EncodingError>` - Vector of executable transactions
/// A vector of encoded solutions, each containing:
/// - The Tycho method selector
/// - The encoded swap path
/// - Additional metadata (e.g., permit2 information)
///
/// # Recommendation
/// Use this method if you care about execution safety and want to avoid surprises.
fn encode_solutions(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<EncodedSolution>, EncodingError>;
/// Encodes a list of [`Solution`]s directly into executable transactions for the Tycho router.
///
/// This method wraps around Tychos example encoding logic (see [`encode_tycho_router_call`])
/// and should only be used for **prototyping or development**.
///
/// # Warning
/// This implementation uses default logic to construct the outer calldata (e.g., for setting
/// `minAmountOut`). This might not be optimal or safe for production use.
///
/// To ensure correctness, **users should implement their own encoding pipeline** using
/// [`encode_solutions`].
///
/// # Returns
/// A vector of fully constructed [`Transaction`]s that can be submitted to a node or bundler.
fn encode_calldata(&self, solutions: Vec<Solution>) -> Result<Vec<Transaction>, EncodingError>;
/// Performs solution-level validation and sanity checks.
///
/// This function can be used to verify whether a proposed solution is structurally sound and
/// ready for encoding.
///
/// # Returns
/// - `Ok(())` if the solution is valid.
/// - `Err(EncodingError)` if the solution is malformed or unsupported.
fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError>;
}