use std::{
fs,
io::{self, Read},
};
use alloy::sol_types::SolValue;
use clap::{Parser, Subcommand};
use tycho_common::{hex_bytes::Bytes, models::Chain};
use tycho_execution::encoding::{
errors::EncodingError,
evm::{
approvals::permit2::PermitSingle,
encoder_builders::{TychoExecutorEncoderBuilder, TychoRouterEncoderBuilder},
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
},
models::{Solution, UserTransferType},
tycho_encoder::TychoEncoder,
};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(short, long)]
chain: Chain,
#[arg(short, long)]
executors_file_path: Option<String>,
#[arg(short, long)]
router_address: Option<Bytes>,
#[arg(short, long)]
user_transfer_type: Option<UserTransferType>,
}
#[derive(Subcommand)]
pub enum Commands {
TychoRouter,
TychoExecutor,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let mut buffer = String::new();
io::stdin()
.read_to_string(&mut buffer)
.map_err(|e| format!("Failed to read from stdin: {e}"))?;
if buffer.trim().is_empty() {
return Err("No input provided. Expected JSON input on stdin.".into());
}
let solution: Solution = serde_json::from_str(&buffer)?;
let chain = cli.chain;
let encoder: Box<dyn TychoEncoder> = match cli.command {
Commands::TychoRouter => {
let executors_addresses = if let Some(config_path) = cli.executors_file_path {
Some(fs::read_to_string(&config_path).map_err(|e| {
EncodingError::FatalError(format!(
"Error reading executors file from {config_path:?}: {e}",
))
})?)
} else {
None
};
let swap_encoder_registry =
SwapEncoderRegistry::new(chain).add_default_encoders(executors_addresses)?;
let mut builder = TychoRouterEncoderBuilder::new()
.chain(chain)
.swap_encoder_registry(swap_encoder_registry);
if let Some(router_address) = cli.router_address {
builder = builder.router_address(router_address);
}
builder.build()?
}
Commands::TychoExecutor => {
let swap_encoder_registry =
SwapEncoderRegistry::new(chain).add_default_encoders(None)?;
TychoExecutorEncoderBuilder::new()
.swap_encoder_registry(swap_encoder_registry)
.build()?
}
};
let encoded_solutions = encoder.encode_solutions(vec![solution])?;
let encoded = serde_json::json!({
"swaps": format!("0x{}", hex::encode(encoded_solutions[0].swaps())),
"interacting_with": format!("0x{}", hex::encode(encoded_solutions[0].interacting_with())),
"function_signature": encoded_solutions[0].function_signature(),
"n_tokens": encoded_solutions[0].n_tokens().to_string(),
"permit": match encoded_solutions[0].permit() {
Some(permit) => {
match PermitSingle::try_from(permit) {
Ok(sol_permit) => format!("0x{}", hex::encode(sol_permit.abi_encode())),
Err(_) => String::new(),
}
}
None => String::new(),
},
});
println!(
"{}",
serde_json::to_string(&encoded).map_err(|e| format!("Failed to serialize output: {e}"))?
);
Ok(())
}