Merge pull request #57 from propeller-heads/quickstart/dc/ENG-4088-add-execution-to-quickstart

feat: Misc changes needed to add execution to quickstart
This commit is contained in:
dianacarvalho1
2025-02-07 16:19:16 +00:00
committed by GitHub
20 changed files with 174 additions and 159 deletions

4
Cargo.lock generated
View File

@@ -4317,8 +4317,8 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "tycho-core" name = "tycho-core"
version = "0.46.0" version = "0.55.2"
source = "git+https://github.com/propeller-heads/tycho-indexer.git?tag=0.46.0#481b2f252eddb98442eab78069fa427068b5050d" source = "git+https://github.com/propeller-heads/tycho-indexer.git?tag=0.55.2#dfa50d5e318253001938655a49aa3e05f958d89e"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@@ -14,8 +14,8 @@ lazy_static = "1.4.0"
num-bigint = { version = "0.4.6", features = ["serde"] } num-bigint = { version = "0.4.6", features = ["serde"] }
hex = "0.4.3" hex = "0.4.3"
num-traits = "0.2.19" num-traits = "0.2.19"
serde = { version = "1.0.217", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.135" serde_json = "1.0.105"
thiserror = "1.0.69" thiserror = "1.0.69"
tokio = { version = "1.38.0", features = ["full"] } tokio = { version = "1.38.0", features = ["full"] }
chrono = "0.4.39" chrono = "0.4.39"
@@ -24,7 +24,7 @@ clap = { version = "4.5.3", features = ["derive"] }
alloy = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true } alloy = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true }
alloy-sol-types = { version = "0.8.14", optional = true } alloy-sol-types = { version = "0.8.14", optional = true }
alloy-primitives = { version = "0.8.9", 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" } tycho-core = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-core", tag = "0.55.2" }
once_cell = "1.20.2" once_cell = "1.20.2"
[dev-dependencies] [dev-dependencies]

View File

@@ -6,11 +6,16 @@ Tycho Execution makes it easy to trade on different DEXs by handling the complex
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
designed to be safe, straightforward, and quick to set up, so anyone can start trading without extra effort. designed to be safe, straightforward, and quick to set up, so anyone can start trading without extra effort.
## Quickstart
To get started, have a look at our [Quickstart example](examples/quickstart/README.md).
## Bin Usage Guide ## Bin Usage Guide
### Installation ### Installation
First, build and install the binary: First, build and install the binary:
```bash ```bash
# Build the project # Build the project
cargo build --release cargo build --release
@@ -19,12 +24,11 @@ cargo build --release
cargo install --path . cargo install --path .
``` ```
After installation, the `tycho-encode` command will be available to use from any directory in your terminal. The command accepts the following options: After installation, the `tycho-encode` command will be available to use from any directory in your terminal. The command
accepts the following options:
- `-c`: Path to the executor addresses configuration file (defaults to `src/encoding/config/executor_addresses.json`) - `-c`: Path to the executor addresses configuration file (defaults to `src/encoding/config/executor_addresses.json`)
- `-p`: Private key for signing approvals (required when direct_execution is false) - `-p`: Private key for signing approvals (required when direct_execution is false)
- `ROUTER_ADDRESS`: Router contract address (defaults to `0xaa820C29648D5EA543d712cC928377Bd7206a0E7`)
### Encoding Transactions ### Encoding Transactions
@@ -36,9 +40,6 @@ echo '<solution_payload>' | tycho-encode
# Using custom config path # Using custom config path
echo '<solution_payload>' | tycho-encode -c /path/to/your/config.json echo '<solution_payload>' | tycho-encode -c /path/to/your/config.json
# Using custom router address and config path
echo '<solution_payload>' | tycho-encode -c /path/to/your/config.json 0x1234...5678
``` ```
#### Example #### Example
@@ -49,7 +50,6 @@ Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2:
echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","check_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":1.0}],"direct_execution":true}' | tycho-encode echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","check_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":1.0}],"direct_execution":true}' | tycho-encode
``` ```
#### JSON Payload Structure: Solution struct #### JSON Payload Structure: Solution struct
The `Solution` struct is composed of the following fields: The `Solution` struct is composed of the following fields:
@@ -64,10 +64,10 @@ The `Solution` struct is composed of the following fields:
- `expected_amount`: The expected output amount - `expected_amount`: The expected output amount
- `check_amount`: The minimum acceptable output amount (accounting for slippage) - `check_amount`: The minimum acceptable output amount (accounting for slippage)
- `swaps`: Array of swap steps, each containing: - `swaps`: Array of swap steps, each containing:
- `component`: Details about the DEX/protocol being used - `component`: Details about the DEX/protocol being used
- `token_in`: Input token address for this step - `token_in`: Input token address for this step
- `token_out`: Output token address for this step - `token_out`: Output token address for this step
- `split`: Proportion of tokens to route through this step (1.0 = 100%) - `split`: Proportion of tokens to route through this step (1.0 = 100%)
- `router_address`: The address of the protocol's router contract - `router_address`: The address of the protocol's router contract
- `direct_execution`: Boolean indicating if the transaction should be executed directly - `direct_execution`: Boolean indicating if the transaction should be executed directly

View File

@@ -10,26 +10,25 @@ use tycho_execution::encoding::{
strategy_encoder::strategy_encoder_registry::EVMStrategyEncoderRegistry, strategy_encoder::strategy_encoder_registry::EVMStrategyEncoderRegistry,
tycho_encoder::EVMTychoEncoder, tycho_encoder::EVMTychoEncoder,
}, },
models::{Chain, Solution, Swap}, models::{Solution, Swap},
strategy_encoder::StrategyEncoderRegistry, strategy_encoder::StrategyEncoderRegistry,
tycho_encoder::TychoEncoder, tycho_encoder::TychoEncoder,
}; };
fn main() { fn main() {
// Setup variables // Setup variables
let router_address = "0x1234567890abcdef1234567890abcdef12345678".to_string(); let router_address = Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678")
.expect("Failed to create router address");
let signer_pk = let signer_pk =
Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()); Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string());
let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2")
.expect("Failed to create user address"); .expect("Failed to create user address");
let executors_file_path = "src/encoding/config/executor_addresses.json";
let eth_chain = Chain::from(TychoCoreChain::Ethereum);
// Initialize the encoder // Initialize the encoder
let strategy_encoder_registry = let strategy_encoder_registry =
EVMStrategyEncoderRegistry::new(eth_chain.clone(), executors_file_path, signer_pk.clone()) EVMStrategyEncoderRegistry::new(TychoCoreChain::Ethereum, None, signer_pk.clone())
.expect("Failed to create strategy encoder registry"); .expect("Failed to create strategy encoder registry");
let encoder = EVMTychoEncoder::new(strategy_encoder_registry, router_address, eth_chain) let encoder = EVMTychoEncoder::new(strategy_encoder_registry, TychoCoreChain::Ethereum)
.expect("Failed to create encoder"); .expect("Failed to create encoder");
// ------------------- Encode a simple swap ------------------- // ------------------- Encode a simple swap -------------------
@@ -65,6 +64,7 @@ fn main() {
exact_out: false, // it's an exact in solution exact_out: false, // it's an exact in solution
check_amount: None, // the amount out will not be checked in execution check_amount: None, // the amount out will not be checked in execution
swaps: vec![simple_swap], swaps: vec![simple_swap],
router_address,
..Default::default() ..Default::default()
}; };

View File

@@ -1,6 +1,4 @@
pub use clap::Parser; pub use clap::Parser;
pub const DEFAULT_ROUTER_ADDRESS: &str = "0xaa820C29648D5EA543d712cC928377Bd7206a0E7";
pub const DEFAULT_CONFIG_PATH: &str = "src/encoding/config/executor_addresses.json";
#[derive(Parser)] #[derive(Parser)]
/// Encode swap transactions for the Tycho router /// Encode swap transactions for the Tycho router
@@ -36,15 +34,11 @@ pub const DEFAULT_CONFIG_PATH: &str = "src/encoding/config/executor_addresses.js
/// } /// }
/// ``` /// ```
pub struct Cli { pub struct Cli {
/// Router contract address to use for encoding transactions
#[arg(default_value = DEFAULT_ROUTER_ADDRESS)]
pub router_address: String,
/// Private key for signing approvals (required when direct_execution is false) /// Private key for signing approvals (required when direct_execution is false)
#[arg(short)] #[arg(short)]
pub private_key: Option<String>, pub private_key: Option<String>,
/// Path to the executor addresses configuration file /// Path to the executor addresses configuration file
#[arg(short, default_value = DEFAULT_CONFIG_PATH)] #[arg(short)]
pub config_path: String, pub config_path: Option<String>,
} }

View File

@@ -33,7 +33,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
// Encode the solution // Encode the solution
let encoded = encode_swaps(&buffer, &cli.router_address, &cli.config_path, cli.private_key)?; let encoded = encode_swaps(&buffer, cli.config_path, cli.private_key)?;
// Output the encoded result as JSON to stdout // Output the encoded result as JSON to stdout
println!( println!(
@@ -47,17 +47,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
fn encode_swaps( fn encode_swaps(
input: &str, input: &str,
router_address: &str, config_path: Option<String>,
config_path: &str,
private_key: Option<String>, private_key: Option<String>,
) -> Result<Value, Box<dyn std::error::Error>> { ) -> Result<Value, Box<dyn std::error::Error>> {
let solution: Solution = serde_json::from_str(input)?; let solution: Solution = serde_json::from_str(input)?;
let chain = Chain::Ethereum; let chain = Chain::Ethereum;
let strategy_selector = let strategy_selector = EVMStrategyEncoderRegistry::new(chain, config_path, private_key)?;
EVMStrategyEncoderRegistry::new(chain.into(), config_path, private_key)?; let encoder = EVMTychoEncoder::new(strategy_selector, chain)?;
let encoder =
EVMTychoEncoder::new(strategy_selector, router_address.to_string(), chain.into())?;
let transactions = encoder.encode_router_calldata(vec![solution])?; let transactions = encoder.encode_router_calldata(vec![solution])?;
Ok(serde_json::json!({ Ok(serde_json::json!({

View File

@@ -1,6 +1,7 @@
{ {
"ethereum": { "ethereum": {
"uniswap_v2": "0x5C2F5a71f67c01775180ADc06909288B4C329308", "uniswap_v2": "0x5C2F5a71f67c01775180ADc06909288B4C329308",
"uniswap_v3": "0x5C2F5a71f67c01775180ADc06909288B4C329308",
"vm:balancer_v2": "0x543778987b293C7E8Cf0722BB2e935ba6f4068D4" "vm:balancer_v2": "0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"
} }
} }

View File

@@ -11,7 +11,10 @@ use alloy_primitives::{PrimitiveSignature as Signature, B256};
use alloy_sol_types::{eip712_domain, sol, SolStruct, SolValue}; use alloy_sol_types::{eip712_domain, sol, SolStruct, SolValue};
use chrono::Utc; use chrono::Utc;
use num_bigint::BigUint; use num_bigint::BigUint;
use tokio::runtime::Runtime; use tokio::{
runtime::{Handle, Runtime},
task::block_in_place,
};
use tycho_core::Bytes; use tycho_core::Bytes;
use crate::encoding::{ use crate::encoding::{
@@ -25,12 +28,19 @@ use crate::encoding::{
/// Struct for managing Permit2 operations, including encoding approvals and fetching allowance /// Struct for managing Permit2 operations, including encoding approvals and fetching allowance
/// data. /// data.
#[derive(Clone)]
pub struct Permit2 { pub struct Permit2 {
address: Address, address: Address,
client: Arc<RootProvider<BoxTransport>>, client: Arc<RootProvider<BoxTransport>>,
runtime: Runtime,
signer: PrivateKeySigner, signer: PrivateKeySigner,
chain_id: ChainId, chain_id: ChainId,
runtime_handle: Handle,
// Store the runtime to prevent it from being dropped before use.
// This is required since tycho-execution does not have a pre-existing runtime.
// However, if the library is used in a context where a runtime already exists, it is not
// necessary to store it.
#[allow(dead_code)]
runtime: Option<Arc<Runtime>>,
} }
/// Type alias for representing allowance data as a tuple of (amount, expiration, nonce). Used for /// Type alias for representing allowance data as a tuple of (amount, expiration, nonce). Used for
@@ -60,9 +70,16 @@ sol! {
impl Permit2 { impl Permit2 {
pub fn new(signer_pk: String, chain: Chain) -> Result<Self, EncodingError> { pub fn new(signer_pk: String, chain: Chain) -> Result<Self, EncodingError> {
let runtime = Runtime::new() let (handle, runtime) = match Handle::try_current() {
.map_err(|_| EncodingError::FatalError("Failed to create runtime".to_string()))?; Ok(h) => (h, None),
let client = runtime.block_on(get_client())?; Err(_) => {
let rt = Arc::new(Runtime::new().map_err(|_| {
EncodingError::FatalError("Failed to create a new tokio runtime".to_string())
})?);
(rt.handle().clone(), Some(rt))
}
};
let client = block_in_place(|| handle.block_on(get_client()))?;
let pk = B256::from_str(&signer_pk).map_err(|_| { let pk = B256::from_str(&signer_pk).map_err(|_| {
EncodingError::FatalError("Failed to convert signer private key to B256".to_string()) EncodingError::FatalError("Failed to convert signer private key to B256".to_string())
})?; })?;
@@ -73,9 +90,10 @@ impl Permit2 {
address: Address::from_str("0x000000000022D473030F116dDEE9F6B43aC78BA3") address: Address::from_str("0x000000000022D473030F116dDEE9F6B43aC78BA3")
.map_err(|_| EncodingError::FatalError("Permit2 address not valid".to_string()))?, .map_err(|_| EncodingError::FatalError("Permit2 address not valid".to_string()))?,
client, client,
runtime, runtime_handle: handle,
signer, signer,
chain_id: chain.id, chain_id: chain.id,
runtime,
}) })
} }
@@ -94,9 +112,10 @@ impl Permit2 {
..Default::default() ..Default::default()
}; };
let output = self let output = block_in_place(|| {
.runtime self.runtime_handle
.block_on(async { self.client.call(&tx).await }); .block_on(async { self.client.call(&tx).await })
});
match output { match output {
Ok(response) => { Ok(response) => {
let allowance: Allowance = let allowance: Allowance =
@@ -283,14 +302,16 @@ mod tests {
input: TransactionInput { input: Some(AlloyBytes::from(data)), data: None }, input: TransactionInput { input: Some(AlloyBytes::from(data)), data: None },
..Default::default() ..Default::default()
}; };
let receipt = permit2.runtime.block_on(async { let receipt = block_in_place(|| {
let pending_tx = permit2 permit2.runtime_handle.block_on(async {
.client let pending_tx = permit2
.send_transaction(tx) .client
.await .send_transaction(tx)
.unwrap(); .await
// Wait for the transaction to be mined .unwrap();
pending_tx.get_receipt().await.unwrap() // Wait for the transaction to be mined
pending_tx.get_receipt().await.unwrap()
})
}); });
assert!(receipt.status(), "Approve transaction failed"); assert!(receipt.status(), "Approve transaction failed");
@@ -314,7 +335,7 @@ mod tests {
..Default::default() ..Default::default()
}; };
let result = permit2.runtime.block_on(async { let result = permit2.runtime_handle.block_on(async {
let pending_tx = permit2 let pending_tx = permit2
.client .client
.send_transaction(tx) .send_transaction(tx)

View File

@@ -0,0 +1,2 @@
pub const DEFAULT_EXECUTORS_JSON: &str =
include_str!("../../../src/encoding/config/executor_addresses.json");

View File

@@ -1,4 +1,5 @@
pub mod approvals; pub mod approvals;
mod constants;
pub mod strategy_encoder; pub mod strategy_encoder;
mod swap_encoder; mod swap_encoder;
pub mod tycho_encoder; pub mod tycho_encoder;

View File

View File

@@ -21,10 +21,11 @@ pub struct EVMStrategyEncoderRegistry {
impl StrategyEncoderRegistry for EVMStrategyEncoderRegistry { impl StrategyEncoderRegistry for EVMStrategyEncoderRegistry {
fn new( fn new(
chain: Chain, chain: tycho_core::dto::Chain,
executors_file_path: &str, executors_file_path: Option<String>,
signer_pk: Option<String>, signer_pk: Option<String>,
) -> Result<Self, EncodingError> { ) -> Result<Self, EncodingError> {
let chain = Chain::from(chain);
let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain.clone())?; let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain.clone())?;
let mut strategies: HashMap<String, Box<dyn StrategyEncoder>> = HashMap::new(); let mut strategies: HashMap<String, Box<dyn StrategyEncoder>> = HashMap::new();
@@ -54,3 +55,15 @@ impl StrategyEncoderRegistry for EVMStrategyEncoderRegistry {
} }
} }
} }
impl Clone for EVMStrategyEncoderRegistry {
fn clone(&self) -> Self {
Self {
strategies: self
.strategies
.iter()
.map(|(k, v)| (k.clone(), v.clone_box()))
.collect(),
}
}
}

View File

@@ -73,6 +73,7 @@ pub trait EVMStrategyEncoder: StrategyEncoder {
/// * `permit2`: Permit2, responsible for managing permit2 operations and providing necessary /// * `permit2`: Permit2, responsible for managing permit2 operations and providing necessary
/// signatures and permit2 objects for calling the router /// signatures and permit2 objects for calling the router
/// * `selector`: String, the selector for the swap function in the router contract /// * `selector`: String, the selector for the swap function in the router contract
#[derive(Clone)]
pub struct SplitSwapStrategyEncoder { pub struct SplitSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry, swap_encoder_registry: SwapEncoderRegistry,
permit2: Permit2, permit2: Permit2,
@@ -260,11 +261,7 @@ 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, solution: Solution) -> Result<(Vec<u8>, Bytes), EncodingError> {
&self,
solution: Solution,
router_address: Bytes,
) -> Result<(Vec<u8>, Bytes), EncodingError> {
self.validate_split_percentages(&solution.swaps)?; self.validate_split_percentages(&solution.swaps)?;
self.validate_swap_path( self.validate_swap_path(
&solution.swaps, &solution.swaps,
@@ -273,7 +270,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
&solution.native_action, &solution.native_action,
)?; )?;
let (permit, signature) = self.permit2.get_permit( let (permit, signature) = self.permit2.get_permit(
&router_address, &solution.router_address,
&solution.sender, &solution.sender,
&solution.given_token, &solution.given_token,
&solution.given_amount, &solution.given_amount,
@@ -345,9 +342,9 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
})?; })?;
let encoding_context = EncodingContext { let encoding_context = EncodingContext {
receiver: router_address.clone(), receiver: solution.router_address.clone(),
exact_out: solution.exact_out, exact_out: solution.exact_out,
router_address: router_address.clone(), router_address: solution.router_address.clone(),
}; };
let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?; let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?;
let swap_data = self.encode_swap_header( let swap_data = self.encode_swap_header(
@@ -398,13 +395,17 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
.abi_encode(); .abi_encode();
let contract_interaction = encode_input(&self.selector, method_calldata); let contract_interaction = encode_input(&self.selector, method_calldata);
Ok((contract_interaction, router_address)) Ok((contract_interaction, solution.router_address))
} }
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> { fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
self.swap_encoder_registry self.swap_encoder_registry
.get_encoder(protocol_system) .get_encoder(protocol_system)
} }
fn clone_box(&self) -> Box<dyn StrategyEncoder> {
Box::new(self.clone())
}
} }
/// This strategy encoder is used for solutions that are sent directly to the executor, bypassing /// This strategy encoder is used for solutions that are sent directly to the executor, bypassing
@@ -412,6 +413,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
/// ///
/// # Fields /// # Fields
/// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders
#[derive(Clone)]
pub struct ExecutorStrategyEncoder { pub struct ExecutorStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry, swap_encoder_registry: SwapEncoderRegistry,
} }
@@ -423,17 +425,7 @@ impl ExecutorStrategyEncoder {
} }
impl EVMStrategyEncoder for ExecutorStrategyEncoder {} impl EVMStrategyEncoder for ExecutorStrategyEncoder {}
impl StrategyEncoder for ExecutorStrategyEncoder { impl StrategyEncoder for ExecutorStrategyEncoder {
fn encode_strategy( fn encode_strategy(&self, solution: Solution) -> Result<(Vec<u8>, Bytes), EncodingError> {
&self,
solution: Solution,
_router_address: Bytes,
) -> Result<(Vec<u8>, Bytes), EncodingError> {
let router_address = solution.router_address.ok_or_else(|| {
EncodingError::InvalidInput(
"Router address is required for straight-to-executor solutions".to_string(),
)
})?;
let swap = solution let swap = solution
.swaps .swaps
.first() .first()
@@ -451,7 +443,7 @@ impl StrategyEncoder for ExecutorStrategyEncoder {
let encoding_context = EncodingContext { let encoding_context = EncodingContext {
receiver: solution.receiver, receiver: solution.receiver,
exact_out: solution.exact_out, exact_out: solution.exact_out,
router_address, router_address: solution.router_address,
}; };
let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?; let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?;
@@ -459,10 +451,15 @@ impl StrategyEncoder for ExecutorStrategyEncoder {
.map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?; .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?;
Ok((protocol_data, executor_address)) Ok((protocol_data, executor_address))
} }
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> { fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
self.swap_encoder_registry self.swap_encoder_registry
.get_encoder(protocol_system) .get_encoder(protocol_system)
} }
fn clone_box(&self) -> Box<dyn StrategyEncoder> {
Box::new(self.clone())
}
} }
#[cfg(test)] #[cfg(test)]
@@ -495,7 +492,7 @@ mod tests {
fn get_swap_encoder_registry() -> SwapEncoderRegistry { fn get_swap_encoder_registry() -> SwapEncoderRegistry {
let eth_chain = eth_chain(); let eth_chain = eth_chain();
SwapEncoderRegistry::new("src/encoding/config/executor_addresses.json", eth_chain).unwrap() SwapEncoderRegistry::new(None, eth_chain).unwrap()
} }
#[test] #[test]
@@ -529,13 +526,13 @@ mod tests {
receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(),
swaps: vec![swap], swaps: vec![swap],
direct_execution: true, direct_execution: true,
router_address: Some(Bytes::zero(20)), router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
slippage: None, slippage: None,
native_action: None, native_action: None,
}; };
let (protocol_data, executor_address) = encoder let (protocol_data, executor_address) = encoder
.encode_strategy(solution, Bytes::zero(20)) .encode_strategy(solution)
.unwrap(); .unwrap();
let hex_protocol_data = encode(&protocol_data); let hex_protocol_data = encode(&protocol_data);
assert_eq!( assert_eq!(
@@ -620,13 +617,13 @@ mod tests {
check_amount, check_amount,
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
swaps: vec![swap], swaps: vec![swap],
..Default::default() ..Default::default()
}; };
let router_address = Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap();
let (calldata, _) = encoder let (calldata, _) = encoder
.encode_strategy(solution, router_address) .encode_strategy(solution)
.unwrap(); .unwrap();
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
let expected_input = [ let expected_input = [
@@ -720,14 +717,14 @@ mod tests {
check_amount: None, check_amount: None,
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
swaps: vec![swap], swaps: vec![swap],
native_action: Some(NativeAction::Wrap), native_action: Some(NativeAction::Wrap),
..Default::default() ..Default::default()
}; };
let router_address = Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap();
let (calldata, _) = encoder let (calldata, _) = encoder
.encode_strategy(solution, router_address) .encode_strategy(solution)
.unwrap(); .unwrap();
let hex_calldata = encode(&calldata); let hex_calldata = encode(&calldata);
@@ -768,14 +765,14 @@ mod tests {
check_amount: None, check_amount: None,
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
swaps: vec![swap], swaps: vec![swap],
native_action: Some(NativeAction::Unwrap), native_action: Some(NativeAction::Unwrap),
..Default::default() ..Default::default()
}; };
let router_address = Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap();
let (calldata, _) = encoder let (calldata, _) = encoder
.encode_strategy(solution, router_address) .encode_strategy(solution)
.unwrap(); .unwrap();
let hex_calldata = encode(&calldata); let hex_calldata = encode(&calldata);
@@ -857,13 +854,13 @@ mod tests {
check_amount: None, check_amount: None,
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc], swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc],
..Default::default() ..Default::default()
}; };
let router_address = Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap();
let (calldata, _) = encoder let (calldata, _) = encoder
.encode_strategy(solution, router_address) .encode_strategy(solution)
.unwrap(); .unwrap();
let _hex_calldata = encode(&calldata); let _hex_calldata = encode(&calldata);

View File

@@ -1,6 +1,8 @@
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
evm::swap_encoder::swap_encoders::{BalancerV2SwapEncoder, UniswapV2SwapEncoder}, evm::swap_encoder::swap_encoders::{
BalancerV2SwapEncoder, UniswapV2SwapEncoder, UniswapV3SwapEncoder,
},
swap_encoder::SwapEncoder, swap_encoder::SwapEncoder,
}; };
@@ -22,6 +24,7 @@ impl SwapEncoderBuilder {
match self.protocol_system.as_str() { match self.protocol_system.as_str() {
"uniswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))), "uniswap_v2" => Ok(Box::new(UniswapV2SwapEncoder::new(self.executor_address))),
"vm:balancer_v2" => Ok(Box::new(BalancerV2SwapEncoder::new(self.executor_address))), "vm:balancer_v2" => Ok(Box::new(BalancerV2SwapEncoder::new(self.executor_address))),
"uniswap_v3" => Ok(Box::new(UniswapV3SwapEncoder::new(self.executor_address))),
_ => Err(EncodingError::FatalError(format!( _ => Err(EncodingError::FatalError(format!(
"Unknown protocol system: {}", "Unknown protocol system: {}",
self.protocol_system self.protocol_system

View File

@@ -1,7 +1,9 @@
use std::{collections::HashMap, fs}; use std::{collections::HashMap, fs};
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, evm::swap_encoder::builder::SwapEncoderBuilder, models::Chain, errors::EncodingError,
evm::{constants::DEFAULT_EXECUTORS_JSON, swap_encoder::builder::SwapEncoderBuilder},
models::Chain,
swap_encoder::SwapEncoder, swap_encoder::SwapEncoder,
}; };
@@ -15,8 +17,20 @@ pub struct SwapEncoderRegistry {
impl SwapEncoderRegistry { impl SwapEncoderRegistry {
/// Populates the registry with the `SwapEncoders` for the given blockchain by parsing the /// Populates the registry with the `SwapEncoders` for the given blockchain by parsing the
/// executors' addresses in the file at the given path. /// executors' addresses in the file at the given path.
pub fn new(executors_file_path: &str, blockchain: Chain) -> Result<Self, EncodingError> { pub fn new(
let config_str = fs::read_to_string(executors_file_path)?; executors_file_path: Option<String>,
blockchain: Chain,
) -> Result<Self, EncodingError> {
let config_str = if let Some(ref path) = executors_file_path {
fs::read_to_string(path).map_err(|e| {
EncodingError::FatalError(format!(
"Error reading executors file from {:?}: {}",
executors_file_path, e
))
})?
} else {
DEFAULT_EXECUTORS_JSON.to_string()
};
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)?;
let mut encoders = HashMap::new(); let mut encoders = HashMap::new();
let executors = config let executors = config

View File

@@ -104,28 +104,25 @@ impl SwapEncoder for UniswapV3SwapEncoder {
let zero_to_one = Self::get_zero_to_one(token_in_address, token_out_address); let zero_to_one = Self::get_zero_to_one(token_in_address, token_out_address);
let component_id = Address::from_str(&swap.component.id) let component_id = Address::from_str(&swap.component.id)
.map_err(|_| EncodingError::FatalError("Invalid USV3 component id".to_string()))?; .map_err(|_| EncodingError::FatalError("Invalid USV3 component id".to_string()))?;
let mut pool_fee_bytes = swap let pool_fee_bytes = swap
.component .component
.static_attributes .static_attributes
.get("pool_fee") .get("fee")
.ok_or_else(|| { .ok_or_else(|| {
EncodingError::FatalError( EncodingError::FatalError(
"Pool fee not found in Uniswap v3 static attributes".to_string(), "Pool fee not found in Uniswap v3 static attributes".to_string(),
) )
})? })?
.as_ref()
.to_vec(); .to_vec();
// Reverse to get be bytes, since this is encoded as le bytes // this is necessary to pad on the left with zeros if the fee is less than 3 bytes
pool_fee_bytes.reverse(); let mut padded_fee_bytes = [0u8; 3];
let start = 3 - pool_fee_bytes.len();
padded_fee_bytes[start..].copy_from_slice(&pool_fee_bytes);
let pool_fee_u24: [u8; 3] = pool_fee_bytes[pool_fee_bytes.len() - 3..] let pool_fee_u24: [u8; 3] = padded_fee_bytes[(padded_fee_bytes.len() - 3)..]
.try_into() .try_into()
.map_err(|_| { .map_err(|_| EncodingError::FatalError("Failed to extract fee bytes".to_string()))?;
EncodingError::FatalError(
"Pool fee static attribute must be at least 3 bytes".to_string(),
)
})?;
let args = ( let args = (
token_in_address, token_in_address,
@@ -214,6 +211,7 @@ mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use alloy::hex::encode; use alloy::hex::encode;
use num_bigint::BigInt;
use tycho_core::{dto::ProtocolComponent, Bytes}; use tycho_core::{dto::ProtocolComponent, Bytes};
use super::*; use super::*;
@@ -257,9 +255,10 @@ mod tests {
} }
#[test] #[test]
fn test_encode_uniswap_v3() { fn test_encode_uniswap_v3() {
let encoded_pool_fee: [u8; 4] = 500u32.to_le_bytes(); let fee = BigInt::from(500);
let encoded_pool_fee = Bytes::from(fee.to_signed_bytes_be());
let mut static_attributes: HashMap<String, Bytes> = HashMap::new(); let mut static_attributes: HashMap<String, Bytes> = HashMap::new();
static_attributes.insert("pool_fee".into(), Bytes::from(encoded_pool_fee[..3].to_vec())); static_attributes.insert("fee".into(), Bytes::from(encoded_pool_fee.to_vec()));
let usv3_pool = ProtocolComponent { let usv3_pool = ProtocolComponent {
id: String::from("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"), id: String::from("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),

View File

@@ -1,5 +1,3 @@
use std::str::FromStr;
use num_bigint::BigUint; use num_bigint::BigUint;
use tycho_core::Bytes; use tycho_core::Bytes;
@@ -10,30 +8,23 @@ use crate::encoding::{
tycho_encoder::TychoEncoder, tycho_encoder::TychoEncoder,
}; };
/// Represents an encoder for a swap through the given router address using any strategy supported /// Represents an encoder for a swap using any strategy supported by the strategy registry.
/// by the strategy registry.
/// ///
/// # Fields /// # Fields
/// * `strategy_registry`: S, the strategy registry to use to select the best strategy to encode a /// * `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. /// 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 /// * `native_address`: Address of the chain's native token
/// * `wrapped_address`: Address of the chain's wrapped native token /// * `wrapped_address`: Address of the chain's wrapped native token
#[derive(Clone)]
pub struct EVMTychoEncoder<S: StrategyEncoderRegistry> { pub struct EVMTychoEncoder<S: StrategyEncoderRegistry> {
strategy_registry: S, strategy_registry: S,
router_address: Bytes,
native_address: Bytes, native_address: Bytes,
wrapped_address: Bytes, wrapped_address: Bytes,
} }
impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> { impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
pub fn new( pub fn new(strategy_registry: S, chain: tycho_core::dto::Chain) -> Result<Self, EncodingError> {
strategy_registry: S, let chain: Chain = Chain::from(chain);
router_address: String,
chain: Chain,
) -> Result<Self, EncodingError> {
let router_address = Bytes::from_str(&router_address)
.map_err(|_| EncodingError::FatalError("Invalid router address".to_string()))?;
if chain.name != *"ethereum" { if chain.name != *"ethereum" {
return Err(EncodingError::InvalidInput( return Err(EncodingError::InvalidInput(
"Currently only Ethereum is supported".to_string(), "Currently only Ethereum is supported".to_string(),
@@ -41,7 +32,6 @@ impl<S: StrategyEncoderRegistry> EVMTychoEncoder<S> {
} }
Ok(EVMTychoEncoder { Ok(EVMTychoEncoder {
strategy_registry, strategy_registry,
router_address,
native_address: chain.native_token()?, native_address: chain.native_token()?,
wrapped_address: chain.wrapped_token()?, wrapped_address: chain.wrapped_token()?,
}) })
@@ -111,16 +101,11 @@ impl<S: StrategyEncoderRegistry> TychoEncoder<S> for EVMTychoEncoder<S> {
for solution in solutions.iter() { for solution in solutions.iter() {
self.validate_solution(solution)?; self.validate_solution(solution)?;
let router_address = solution
.router_address
.clone()
.unwrap_or(self.router_address.clone());
let strategy = self let strategy = self
.strategy_registry .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())?;
let value = match solution.native_action.as_ref() { let value = match solution.native_action.as_ref() {
Some(NativeAction::Wrap) => solution.given_amount.clone(), Some(NativeAction::Wrap) => solution.given_amount.clone(),
@@ -139,6 +124,8 @@ impl<S: StrategyEncoderRegistry> TychoEncoder<S> for EVMTychoEncoder<S> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use tycho_core::dto::{Chain as TychoCoreChain, ProtocolComponent}; use tycho_core::dto::{Chain as TychoCoreChain, ProtocolComponent};
use super::*; use super::*;
@@ -150,10 +137,6 @@ mod tests {
strategy: Box<dyn StrategyEncoder>, strategy: Box<dyn StrategyEncoder>,
} }
fn eth_chain() -> Chain {
TychoCoreChain::Ethereum.into()
}
fn dai() -> Bytes { fn dai() -> Bytes {
Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap() Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap()
} }
@@ -168,8 +151,8 @@ mod tests {
impl StrategyEncoderRegistry for MockStrategyRegistry { impl StrategyEncoderRegistry for MockStrategyRegistry {
fn new( fn new(
_chain: Chain, _chain: tycho_core::dto::Chain,
_executors_file_path: &str, _executors_file_path: Option<String>,
_signer_pk: Option<String>, _signer_pk: Option<String>,
) -> Result<MockStrategyRegistry, EncodingError> { ) -> Result<MockStrategyRegistry, EncodingError> {
Ok(Self { strategy: Box::new(MockStrategy) }) Ok(Self { strategy: Box::new(MockStrategy) })
@@ -183,14 +166,11 @@ mod tests {
} }
} }
#[derive(Clone)]
struct MockStrategy; struct MockStrategy;
impl StrategyEncoder for MockStrategy { impl StrategyEncoder for MockStrategy {
fn encode_strategy( fn encode_strategy(&self, _solution: Solution) -> Result<(Vec<u8>, Bytes), EncodingError> {
&self,
_solution: Solution,
_router_address: Bytes,
) -> Result<(Vec<u8>, Bytes), EncodingError> {
Ok(( Ok((
Bytes::from_str("0x1234") Bytes::from_str("0x1234")
.unwrap() .unwrap()
@@ -202,16 +182,15 @@ mod tests {
fn get_swap_encoder(&self, _protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> { fn get_swap_encoder(&self, _protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
None None
} }
fn clone_box(&self) -> Box<dyn StrategyEncoder> {
Box::new(self.clone())
}
} }
fn get_mocked_tycho_encoder() -> EVMTychoEncoder<MockStrategyRegistry> { fn get_mocked_tycho_encoder() -> EVMTychoEncoder<MockStrategyRegistry> {
let strategy_registry = MockStrategyRegistry::new(eth_chain(), "", None).unwrap(); let strategy_registry =
EVMTychoEncoder::new( MockStrategyRegistry::new(TychoCoreChain::Ethereum, None, None).unwrap();
strategy_registry, EVMTychoEncoder::new(strategy_registry, TychoCoreChain::Ethereum).unwrap()
"0x1234567890abcdef1234567890abcdef12345678".to_string(),
eth_chain(),
)
.unwrap()
} }
#[test] #[test]
@@ -233,7 +212,7 @@ mod tests {
exact_out: false, exact_out: false,
given_amount: eth_amount_in.clone(), given_amount: eth_amount_in.clone(),
given_token: eth(), given_token: eth(),
router_address: None, router_address: Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(),
swaps: vec![swap], swaps: vec![swap],
native_action: Some(NativeAction::Wrap), native_action: Some(NativeAction::Wrap),
..Default::default() ..Default::default()

View File

@@ -41,8 +41,8 @@ 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 /// Address of the router contract to be used for the swaps.
pub router_address: Option<Bytes>, pub router_address: 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
@@ -137,6 +137,7 @@ impl From<TychoCoreChain> for ChainId {
TychoCoreChain::ZkSync => ChainId(324), TychoCoreChain::ZkSync => ChainId(324),
TychoCoreChain::Arbitrum => ChainId(42161), TychoCoreChain::Arbitrum => ChainId(42161),
TychoCoreChain::Starknet => ChainId(0), TychoCoreChain::Starknet => ChainId(0),
TychoCoreChain::Base => ChainId(8453),
} }
} }
} }

View File

@@ -1,21 +1,14 @@
use tycho_core::Bytes; use tycho_core::{dto::Chain, Bytes};
use crate::encoding::{ use crate::encoding::{errors::EncodingError, models::Solution, swap_encoder::SwapEncoder};
errors::EncodingError,
models::{Chain, Solution},
swap_encoder::SwapEncoder,
};
/// Encodes a solution using a specific strategy. /// Encodes a solution using a specific strategy.
pub trait StrategyEncoder { pub trait StrategyEncoder {
fn encode_strategy( fn encode_strategy(&self, to_encode: Solution) -> Result<(Vec<u8>, Bytes), EncodingError>;
&self,
to_encode: Solution,
router_address: Bytes,
) -> Result<(Vec<u8>, Bytes), EncodingError>;
#[allow(clippy::borrowed_box)] #[allow(clippy::borrowed_box)]
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>>; fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>>;
fn clone_box(&self) -> Box<dyn StrategyEncoder>;
} }
/// Contains the supported strategies to encode a solution, and chooses the best strategy to encode /// Contains the supported strategies to encode a solution, and chooses the best strategy to encode
@@ -23,7 +16,7 @@ pub trait StrategyEncoder {
pub trait StrategyEncoderRegistry { pub trait StrategyEncoderRegistry {
fn new( fn new(
chain: Chain, chain: Chain,
executors_file_path: &str, executors_file_path: Option<String>,
signer_pk: Option<String>, signer_pk: Option<String>,
) -> Result<Self, EncodingError> ) -> Result<Self, EncodingError>
where where