Merge branch 'main' into encoder/hr/ENG-4093-bin
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Tycho Execution
|
# Tycho Execution
|
||||||
|
|
||||||
TODO: add banner
|

|
||||||
|
|
||||||
Tycho Execution makes it easy to trade on different DEXs by handling the complex encoding for you. Instead of creating
|
Tycho Execution makes it easy to trade on different DEXs by handling the complex encoding for you. Instead of creating
|
||||||
custom code for each DEX, you get a simple, ready-to-use tool that generates the necessary data to execute trades. It's
|
custom code for each DEX, you get a simple, ready-to-use tool that generates the necessary data to execute trades. It's
|
||||||
|
|||||||
BIN
banner.png
Normal file
BIN
banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 897 KiB |
@@ -12,6 +12,7 @@ use tokio::runtime::Runtime;
|
|||||||
|
|
||||||
use crate::encoding::{errors::EncodingError, evm::utils::encode_input};
|
use crate::encoding::{errors::EncodingError, evm::utils::encode_input};
|
||||||
|
|
||||||
|
/// A manager for checking if an approval is needed for interacting with a certain spender.
|
||||||
pub struct ProtocolApprovalsManager {
|
pub struct ProtocolApprovalsManager {
|
||||||
client: Arc<RootProvider<BoxTransport>>,
|
client: Arc<RootProvider<BoxTransport>>,
|
||||||
runtime: Runtime,
|
runtime: Runtime,
|
||||||
@@ -23,6 +24,9 @@ impl ProtocolApprovalsManager {
|
|||||||
let client = runtime.block_on(get_client())?;
|
let client = runtime.block_on(get_client())?;
|
||||||
Ok(Self { client, runtime })
|
Ok(Self { client, runtime })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the current allowance for the given token, owner, and spender, and returns true
|
||||||
|
/// if the current allowance is zero.
|
||||||
pub fn approval_needed(
|
pub fn approval_needed(
|
||||||
&self,
|
&self,
|
||||||
token: Address,
|
token: Address,
|
||||||
@@ -56,6 +60,7 @@ impl ProtocolApprovalsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the client used for interacting with the EVM-compatible network.
|
||||||
pub async fn get_client() -> Result<Arc<RootProvider<BoxTransport>>, EncodingError> {
|
pub async fn get_client() -> Result<Arc<RootProvider<BoxTransport>>, EncodingError> {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
let eth_rpc_url = env::var("ETH_RPC_URL")
|
let eth_rpc_url = env::var("ETH_RPC_URL")
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ use crate::encoding::{
|
|||||||
strategy_encoder::{StrategyEncoder, StrategyEncoderRegistry},
|
strategy_encoder::{StrategyEncoder, StrategyEncoderRegistry},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Contains all supported strategies to encode a solution.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `strategies` - A hashmap containing the name of the strategy as a key and the strategy encoder
|
||||||
|
/// as a value.
|
||||||
pub struct EVMStrategyEncoderRegistry {
|
pub struct EVMStrategyEncoderRegistry {
|
||||||
strategies: HashMap<String, Box<dyn StrategyEncoder>>,
|
strategies: HashMap<String, Box<dyn StrategyEncoder>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ use crate::encoding::{
|
|||||||
swap_encoder::SwapEncoder,
|
swap_encoder::SwapEncoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Encodes a solution using a specific strategy for execution on the EVM-compatible network.
|
||||||
pub trait EVMStrategyEncoder: StrategyEncoder {
|
pub trait EVMStrategyEncoder: StrategyEncoder {
|
||||||
|
/// Encodes information necessary for performing a single swap against a given executor for
|
||||||
|
/// a protocol.
|
||||||
fn encode_swap_header(
|
fn encode_swap_header(
|
||||||
&self,
|
&self,
|
||||||
token_in: U8,
|
token_in: U8,
|
||||||
@@ -40,11 +43,17 @@ pub trait EVMStrategyEncoder: StrategyEncoder {
|
|||||||
encoded.extend(protocol_data);
|
encoded.extend(protocol_data);
|
||||||
encoded
|
encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encodes a selector string into its 4-byte representation.
|
||||||
fn encode_executor_selector(&self, selector: &str) -> FixedBytes<4> {
|
fn encode_executor_selector(&self, selector: &str) -> FixedBytes<4> {
|
||||||
let hash = keccak256(selector.as_bytes());
|
let hash = keccak256(selector.as_bytes());
|
||||||
FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]])
|
FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Uses prefix-length encoding to efficient encode action data.
|
||||||
|
///
|
||||||
|
/// Prefix-length encoding is a data encoding method where the beginning of a data segment
|
||||||
|
/// (the "prefix") contains information about the length of the following data.
|
||||||
fn ple_encode(&self, action_data_array: Vec<Vec<u8>>) -> Vec<u8> {
|
fn ple_encode(&self, action_data_array: Vec<Vec<u8>>) -> Vec<u8> {
|
||||||
let mut encoded_action_data: Vec<u8> = Vec::new();
|
let mut encoded_action_data: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
@@ -57,6 +66,13 @@ pub trait EVMStrategyEncoder: StrategyEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the encoder for a swap strategy which supports single, sequential and split swaps.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders
|
||||||
|
/// * `permit2`: Permit2, responsible for managing permit2 operations and providing necessary
|
||||||
|
/// signatures and permit2 objects for calling the router
|
||||||
|
/// * `selector`: String, the selector for the swap function in the router contract
|
||||||
pub struct SplitSwapStrategyEncoder {
|
pub struct SplitSwapStrategyEncoder {
|
||||||
swap_encoder_registry: SwapEncoderRegistry,
|
swap_encoder_registry: SwapEncoderRegistry,
|
||||||
permit2: Permit2,
|
permit2: Permit2,
|
||||||
@@ -81,6 +97,14 @@ impl SplitSwapStrategyEncoder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Raises an error if the split percentages are invalid.
|
||||||
|
///
|
||||||
|
/// Split percentages are considered valid if all the following conditions are met:
|
||||||
|
/// * Each split amount is < 1 (100%)
|
||||||
|
/// * There is exactly one 0% split for each token, and it's the last swap specified, signifying
|
||||||
|
/// to the router to send the remainder of the token to the designated protocol
|
||||||
|
/// * The sum of all non-remainder splits for each token is < 1 (100%)
|
||||||
|
/// * There are no negative split amounts
|
||||||
fn validate_split_percentages(&self, swaps: &[Swap]) -> Result<(), EncodingError> {
|
fn validate_split_percentages(&self, swaps: &[Swap]) -> Result<(), EncodingError> {
|
||||||
let mut swaps_by_token: HashMap<Bytes, Vec<&Swap>> = HashMap::new();
|
let mut swaps_by_token: HashMap<Bytes, Vec<&Swap>> = HashMap::new();
|
||||||
for swap in swaps {
|
for swap in swaps {
|
||||||
@@ -151,6 +175,16 @@ impl SplitSwapStrategyEncoder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Raises an error if swaps do not represent a valid path from the given token to the checked
|
||||||
|
/// token.
|
||||||
|
///
|
||||||
|
/// A path is considered valid if all the following conditions are met:
|
||||||
|
/// * The checked token is reachable from the given token through the swap path
|
||||||
|
/// * There are no tokens which are unconnected from the main path
|
||||||
|
///
|
||||||
|
/// If the given token is the native token and the native action is WRAP, it will be converted
|
||||||
|
/// to the wrapped token before validating the swap path. The same principle applies for the
|
||||||
|
/// checked token and the UNWRAP action.
|
||||||
fn validate_swap_path(
|
fn validate_swap_path(
|
||||||
&self,
|
&self,
|
||||||
swaps: &[Swap],
|
swaps: &[Swap],
|
||||||
@@ -222,7 +256,9 @@ impl SplitSwapStrategyEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EVMStrategyEncoder for SplitSwapStrategyEncoder {}
|
impl EVMStrategyEncoder for SplitSwapStrategyEncoder {}
|
||||||
|
|
||||||
impl StrategyEncoder for SplitSwapStrategyEncoder {
|
impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||||
fn encode_strategy(
|
fn encode_strategy(
|
||||||
&self,
|
&self,
|
||||||
@@ -371,8 +407,11 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This strategy encoder is used for solutions that are sent directly to the pool.
|
/// This strategy encoder is used for solutions that are sent directly to the executor, bypassing
|
||||||
/// Only 1 solution with 1 swap is supported.
|
/// the router. Only one solution with one swap is supported.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders
|
||||||
pub struct ExecutorStrategyEncoder {
|
pub struct ExecutorStrategyEncoder {
|
||||||
swap_encoder_registry: SwapEncoderRegistry,
|
swap_encoder_registry: SwapEncoderRegistry,
|
||||||
}
|
}
|
||||||
@@ -391,7 +430,7 @@ impl StrategyEncoder for ExecutorStrategyEncoder {
|
|||||||
) -> Result<(Vec<u8>, Bytes), EncodingError> {
|
) -> Result<(Vec<u8>, Bytes), EncodingError> {
|
||||||
let router_address = solution.router_address.ok_or_else(|| {
|
let router_address = solution.router_address.ok_or_else(|| {
|
||||||
EncodingError::InvalidInput(
|
EncodingError::InvalidInput(
|
||||||
"Router address is required for straight to pool solutions".to_string(),
|
"Router address is required for straight-to-executor solutions".to_string(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::encoding::{
|
|||||||
swap_encoder::SwapEncoder,
|
swap_encoder::SwapEncoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Builds a `SwapEncoder` for the given protocol system and executor address.
|
||||||
pub struct SwapEncoderBuilder {
|
pub struct SwapEncoderBuilder {
|
||||||
protocol_system: String,
|
protocol_system: String,
|
||||||
executor_address: String,
|
executor_address: String,
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ use crate::encoding::{
|
|||||||
swap_encoder::SwapEncoder,
|
swap_encoder::SwapEncoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Registry containing all supported `SwapEncoders`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SwapEncoderRegistry {
|
pub struct SwapEncoderRegistry {
|
||||||
|
/// A hashmap containing the protocol system as a key and the `SwapEncoder` as a value.
|
||||||
encoders: HashMap<String, Box<dyn SwapEncoder>>,
|
encoders: HashMap<String, Box<dyn SwapEncoder>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SwapEncoderRegistry {
|
impl SwapEncoderRegistry {
|
||||||
|
/// Populates the registry with the `SwapEncoders` for the given blockchain by parsing the
|
||||||
|
/// executors' addresses in the file at the given path.
|
||||||
pub fn new(executors_file_path: &str, blockchain: Chain) -> Result<Self, EncodingError> {
|
pub fn new(executors_file_path: &str, blockchain: Chain) -> Result<Self, EncodingError> {
|
||||||
let config_str = fs::read_to_string(executors_file_path)?;
|
let config_str = fs::read_to_string(executors_file_path)?;
|
||||||
let config: HashMap<String, HashMap<String, String>> = serde_json::from_str(&config_str)?;
|
let config: HashMap<String, HashMap<String, String>> = serde_json::from_str(&config_str)?;
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ use crate::encoding::{
|
|||||||
swap_encoder::SwapEncoder,
|
swap_encoder::SwapEncoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Encodes a swap on a Uniswap V2 pool through the given executor address.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `executor_address` - The address of the executor contract that will perform the swap.
|
||||||
|
/// * `executor_selector` - The selector of the swap function in the executor contract.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UniswapV2SwapEncoder {
|
pub struct UniswapV2SwapEncoder {
|
||||||
executor_address: String,
|
executor_address: String,
|
||||||
@@ -66,6 +71,11 @@ impl SwapEncoder for UniswapV2SwapEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encodes a swap on a Uniswap V3 pool through the given executor address.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `executor_address` - The address of the executor contract that will perform the swap.
|
||||||
|
/// * `executor_selector` - The selector of the swap function in the executor contract.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UniswapV3SwapEncoder {
|
pub struct UniswapV3SwapEncoder {
|
||||||
executor_address: String,
|
executor_address: String,
|
||||||
@@ -140,6 +150,11 @@ impl SwapEncoder for UniswapV3SwapEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encodes a swap on a Balancer V2 pool through the given executor address.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `executor_address` - The address of the executor contract that will perform the swap.
|
||||||
|
/// * `executor_selector` - The selector of the swap function in the executor contract.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BalancerV2SwapEncoder {
|
pub struct BalancerV2SwapEncoder {
|
||||||
executor_address: String,
|
executor_address: String,
|
||||||
|
|||||||
@@ -10,8 +10,17 @@ use crate::encoding::{
|
|||||||
tycho_encoder::TychoEncoder,
|
tycho_encoder::TychoEncoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Represents an encoder for a swap through the given router address using any strategy supported
|
||||||
|
/// by the strategy registry.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `strategy_registry`: S, the strategy registry to use to select the best strategy to encode a
|
||||||
|
/// solution, based on its supported strategies and the solution attributes.
|
||||||
|
/// * `router_address`: Bytes, the address of the router to use to execute the swaps.
|
||||||
|
/// * `native_address`: Address of the chain's native token
|
||||||
|
/// * `wrapped_address`: Address of the chain's wrapped native token
|
||||||
pub struct EVMTychoEncoder<S: StrategyEncoderRegistry> {
|
pub struct EVMTychoEncoder<S: StrategyEncoderRegistry> {
|
||||||
strategy_selector: S,
|
strategy_registry: S,
|
||||||
router_address: Bytes,
|
router_address: Bytes,
|
||||||
native_address: Bytes,
|
native_address: Bytes,
|
||||||
wrapped_address: Bytes,
|
wrapped_address: Bytes,
|
||||||
@@ -19,7 +28,7 @@ pub struct EVMTychoEncoder<S: StrategyEncoderRegistry> {
|
|||||||
|
|
||||||
impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
|
impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
strategy_selector: S,
|
strategy_registry: S,
|
||||||
router_address: String,
|
router_address: String,
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
) -> Result<Self, EncodingError> {
|
) -> Result<Self, EncodingError> {
|
||||||
@@ -31,7 +40,7 @@ impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(EVMTychoEncoder {
|
Ok(EVMTychoEncoder {
|
||||||
strategy_selector,
|
strategy_registry,
|
||||||
router_address,
|
router_address,
|
||||||
native_address: chain.native_token()?,
|
native_address: chain.native_token()?,
|
||||||
wrapped_address: chain.wrapped_token()?,
|
wrapped_address: chain.wrapped_token()?,
|
||||||
@@ -40,6 +49,15 @@ impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
|
impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
|
||||||
|
/// Raises an `EncodingError` if the solution is not considered valid.
|
||||||
|
///
|
||||||
|
/// A solution is considered valid if all the following conditions are met:
|
||||||
|
/// * The solution is not exact out.
|
||||||
|
/// * The solution has at least one swap.
|
||||||
|
/// * If the solution is wrapping, the given token is the chain's native token and the first
|
||||||
|
/// swap's input is the chain's wrapped token.
|
||||||
|
/// * If the solution is unwrapping, the checked token is the chain's native token and the last
|
||||||
|
/// swap's output is the chain's wrapped token.
|
||||||
fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError> {
|
fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError> {
|
||||||
if solution.exact_out {
|
if solution.exact_out {
|
||||||
return Err(EncodingError::FatalError(
|
return Err(EncodingError::FatalError(
|
||||||
@@ -99,7 +117,7 @@ impl<S: StrategyEncoderRegistry> TychoEncoder<S> for EVMTychoEncoder<S> {
|
|||||||
.unwrap_or(self.router_address.clone());
|
.unwrap_or(self.router_address.clone());
|
||||||
|
|
||||||
let strategy = self
|
let strategy = self
|
||||||
.strategy_selector
|
.strategy_registry
|
||||||
.get_encoder(solution)?;
|
.get_encoder(solution)?;
|
||||||
let (contract_interaction, target_address) =
|
let (contract_interaction, target_address) =
|
||||||
strategy.encode_strategy(solution.clone(), router_address)?;
|
strategy.encode_strategy(solution.clone(), router_address)?;
|
||||||
@@ -187,9 +205,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_mocked_tycho_encoder() -> EVMTychoEncoder<MockStrategyRegistry> {
|
fn get_mocked_tycho_encoder() -> EVMTychoEncoder<MockStrategyRegistry> {
|
||||||
let strategy_selector = MockStrategyRegistry::new(eth_chain(), "", None).unwrap();
|
let strategy_registry = MockStrategyRegistry::new(eth_chain(), "", None).unwrap();
|
||||||
EVMTychoEncoder::new(
|
EVMTychoEncoder::new(
|
||||||
strategy_selector,
|
strategy_registry,
|
||||||
"0x1234567890abcdef1234567890abcdef12345678".to_string(),
|
"0x1234567890abcdef1234567890abcdef12345678".to_string(),
|
||||||
eth_chain(),
|
eth_chain(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ pub fn bytes_to_address(address: &Bytes) -> Result<Address, EncodingError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a general `BigUint` to an EVM-specific `U256` value.
|
||||||
pub fn biguint_to_u256(value: &BigUint) -> U256 {
|
pub fn biguint_to_u256(value: &BigUint) -> U256 {
|
||||||
let bytes = value.to_bytes_be();
|
let bytes = value.to_bytes_be();
|
||||||
U256::from_be_slice(&bytes)
|
U256::from_be_slice(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encodes the input data for a function call to the given function selector.
|
||||||
pub fn encode_input(selector: &str, mut encoded_args: Vec<u8>) -> Vec<u8> {
|
pub fn encode_input(selector: &str, mut encoded_args: Vec<u8>) -> Vec<u8> {
|
||||||
let mut hasher = Keccak256::new();
|
let mut hasher = Keccak256::new();
|
||||||
hasher.update(selector.as_bytes());
|
hasher.update(selector.as_bytes());
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ use crate::encoding::{
|
|||||||
serde_primitives::{biguint_string, biguint_string_option},
|
serde_primitives::{biguint_string, biguint_string_option},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Represents a solution containing details describing an order, and instructions for filling
|
||||||
|
/// the order.
|
||||||
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
|
||||||
pub struct Solution {
|
pub struct Solution {
|
||||||
/// Address of the sender.
|
/// Address of the sender.
|
||||||
@@ -28,7 +31,7 @@ pub struct Solution {
|
|||||||
/// supported.
|
/// supported.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub exact_out: bool,
|
pub exact_out: bool,
|
||||||
// If set, it will be applied to expected_amount
|
/// If set, it will be applied to expected_amount
|
||||||
pub slippage: Option<f64>,
|
pub slippage: Option<f64>,
|
||||||
/// Expected amount of the bought token (exact in) or sold token (exact out).
|
/// Expected amount of the bought token (exact in) or sold token (exact out).
|
||||||
#[serde(with = "biguint_string_option")]
|
#[serde(with = "biguint_string_option")]
|
||||||
@@ -39,9 +42,9 @@ pub struct Solution {
|
|||||||
pub check_amount: Option<BigUint>,
|
pub check_amount: Option<BigUint>,
|
||||||
/// List of swaps to fulfill the solution.
|
/// List of swaps to fulfill the solution.
|
||||||
pub swaps: Vec<Swap>,
|
pub swaps: Vec<Swap>,
|
||||||
// If not set, then the Tycho Router will be used
|
/// If not set, then the Tycho Router will be used
|
||||||
pub router_address: Option<Bytes>,
|
pub router_address: Option<Bytes>,
|
||||||
// If set, the corresponding native action will be executed.
|
/// If set, the corresponding native action will be executed.
|
||||||
pub native_action: Option<NativeAction>,
|
pub native_action: Option<NativeAction>,
|
||||||
/// If set to true, the solution will be encoded to be sent directly to the Executor and
|
/// If set to true, the solution will be encoded to be sent directly to the Executor and
|
||||||
/// skip the router. The user is responsible for managing necessary approvals and token
|
/// skip the router. The user is responsible for managing necessary approvals and token
|
||||||
@@ -50,6 +53,12 @@ pub struct Solution {
|
|||||||
pub direct_execution: bool,
|
pub direct_execution: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Represents an action to be performed on the native token either before or after the swap.
|
||||||
|
///
|
||||||
|
/// `Wrap` means that the native token will be wrapped before the first swap, and `Unwrap`
|
||||||
|
/// means that the native token will be unwrapped after the last swap, before being sent to the
|
||||||
|
/// receiver.
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum NativeAction {
|
pub enum NativeAction {
|
||||||
@@ -57,6 +66,9 @@ pub enum NativeAction {
|
|||||||
Unwrap,
|
Unwrap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Represents a swap operation to be performed on a pool.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Swap {
|
pub struct Swap {
|
||||||
/// Protocol component from tycho indexer
|
/// Protocol component from tycho indexer
|
||||||
@@ -65,7 +77,7 @@ pub struct Swap {
|
|||||||
pub token_in: Bytes,
|
pub token_in: Bytes,
|
||||||
/// Token being output from the pool.
|
/// Token being output from the pool.
|
||||||
pub token_out: Bytes,
|
pub token_out: Bytes,
|
||||||
/// Percentage of the amount to be swapped in this operation (for example, 0.5 means 50%)
|
/// Decimal of the amount to be swapped in this operation (for example, 0.5 means 50%)
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub split: f64,
|
pub split: f64,
|
||||||
}
|
}
|
||||||
@@ -81,16 +93,26 @@ impl Swap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a transaction to be executed.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
/// * `to`: Address of the contract to call with the calldata
|
||||||
|
/// * `value`: Native token value to be sent with the transaction.
|
||||||
|
/// * `data`: Encoded calldata for the transaction.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
// Address of the contract to call with the calldata
|
|
||||||
pub to: Bytes,
|
pub to: Bytes,
|
||||||
// ETH value to be sent with the transaction.
|
|
||||||
pub value: BigUint,
|
pub value: BigUint,
|
||||||
// Encoded calldata for the transaction.
|
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents necessary attributes for encoding an order.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
///
|
||||||
|
/// * `receiver`: Address of the receiver of the out token after the swaps are completed.
|
||||||
|
/// * `exact_out`: true if the solution is a buy order, false if it is a sell order.
|
||||||
|
/// * `router_address`: Address of the router contract to be used for the swaps.
|
||||||
pub struct EncodingContext {
|
pub struct EncodingContext {
|
||||||
pub receiver: Bytes,
|
pub receiver: Bytes,
|
||||||
pub exact_out: bool,
|
pub exact_out: bool,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::encoding::{
|
|||||||
swap_encoder::SwapEncoder,
|
swap_encoder::SwapEncoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Encodes a solution using a specific strategy.
|
||||||
pub trait StrategyEncoder {
|
pub trait StrategyEncoder {
|
||||||
fn encode_strategy(
|
fn encode_strategy(
|
||||||
&self,
|
&self,
|
||||||
@@ -17,6 +18,8 @@ pub trait StrategyEncoder {
|
|||||||
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>>;
|
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains the supported strategies to encode a solution, and chooses the best strategy to encode
|
||||||
|
/// a solution based on the solution's attributes.
|
||||||
pub trait StrategyEncoderRegistry {
|
pub trait StrategyEncoderRegistry {
|
||||||
fn new(
|
fn new(
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
@@ -25,6 +28,8 @@ pub trait StrategyEncoderRegistry {
|
|||||||
) -> Result<Self, EncodingError>
|
) -> Result<Self, EncodingError>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
/// Returns the strategy encoder that should be used to encode the given solution.
|
||||||
#[allow(clippy::borrowed_box)]
|
#[allow(clippy::borrowed_box)]
|
||||||
fn get_encoder(&self, solution: &Solution) -> Result<&Box<dyn StrategyEncoder>, EncodingError>;
|
fn get_encoder(&self, solution: &Solution) -> Result<&Box<dyn StrategyEncoder>, EncodingError>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,24 @@ use crate::encoding::{
|
|||||||
errors::EncodingError,
|
errors::EncodingError,
|
||||||
models::{EncodingContext, Swap},
|
models::{EncodingContext, Swap},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This trait must be implemented in order to encode a single swap for a specific protocol.
|
||||||
pub trait SwapEncoder: Sync + Send {
|
pub trait SwapEncoder: Sync + Send {
|
||||||
fn new(executor_address: String) -> Self
|
fn new(executor_address: String) -> Self
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
/// Encodes a swap and its relevant context information into call data for a specific protocol.
|
||||||
fn encode_swap(
|
fn encode_swap(
|
||||||
&self,
|
&self,
|
||||||
swap: Swap,
|
swap: Swap,
|
||||||
encoding_context: EncodingContext,
|
encoding_context: EncodingContext,
|
||||||
) -> Result<Vec<u8>, EncodingError>;
|
) -> Result<Vec<u8>, EncodingError>;
|
||||||
|
|
||||||
|
/// The address of the executor that will be used to swap through a specific protocol.
|
||||||
fn executor_address(&self) -> &str;
|
fn executor_address(&self) -> &str;
|
||||||
|
|
||||||
|
/// The selector of the executor function that will be called in order to perform a swap.
|
||||||
fn executor_selector(&self) -> &str;
|
fn executor_selector(&self) -> &str;
|
||||||
|
|
||||||
/// Clones the swap encoder as a trait object.
|
/// Clones the swap encoder as a trait object.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use crate::encoding::{
|
|||||||
strategy_encoder::StrategyEncoderRegistry,
|
strategy_encoder::StrategyEncoderRegistry,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// An encoder must implement this trait in order to encode a solution into a Transaction for
|
||||||
|
/// execution using a Tycho router or related contracts.
|
||||||
pub trait TychoEncoder<S: StrategyEncoderRegistry> {
|
pub trait TychoEncoder<S: StrategyEncoderRegistry> {
|
||||||
fn encode_router_calldata(
|
fn encode_router_calldata(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
Reference in New Issue
Block a user