From a5166f282dfbcd6c161a84dee7b9dac0efe01ba3 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Thu, 6 Feb 2025 18:30:52 +0530 Subject: [PATCH] feat: use clap for cli and resolve pr comments --- Cargo.lock | 115 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 19 ++++--- src/bin/lib/cli.rs | 45 ++++++++++++++++ src/bin/lib/help.rs | 37 ------------- src/bin/tycho-encode.rs | 32 +++-------- src/encoding/models.rs | 1 - 7 files changed, 181 insertions(+), 69 deletions(-) create mode 100644 src/bin/lib/cli.rs delete mode 100644 src/bin/lib/help.rs diff --git a/Cargo.lock b/Cargo.lock index 5446eda..201af18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,6 +647,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.95" @@ -1298,6 +1348,46 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clap" +version = "4.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "coins-ledger" version = "0.12.0" @@ -1321,6 +1411,12 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "const-hex" version = "1.14.0" @@ -2342,6 +2438,12 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -3752,6 +3854,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -4238,6 +4346,7 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "chrono", + "clap", "dotenv", "hex", "lazy_static", @@ -4358,6 +4467,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "utoipa" version = "4.2.3" diff --git a/Cargo.toml b/Cargo.toml index 4110639..da0f0e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ serde_json = "1.0.135" thiserror = "1.0.69" tokio = { version = "1.38.0", features = ["full"] } chrono = "0.4.39" +clap = { version = "4.5.3", features = ["derive"] } alloy = { version = "0.9.2", features = ["providers", "rpc-types-eth", "eip712", "signer-local"], optional = true } alloy-sol-types = { version = "0.8.14", optional = true } diff --git a/README.md b/README.md index acb8368..3cc3dc4 100644 --- a/README.md +++ b/README.md @@ -6,27 +6,34 @@ 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 designed to be safe, straightforward, and quick to set up, so anyone can start trading without extra effort. -## Usage Guide +## Bin Usage Guide ### Encoding Transactions -To encode a transaction, you can pipe a JSON payload to the `tycho-encode` binary: +To encode a transaction, you can pipe a JSON payload to the binary: ```bash -echo '' | cargo run --bin tycho-encode +echo '' | cargo run ``` #### Example Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2: +First build the project: ```bash -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":false}' | cargo run +cargo build --release ``` -#### JSON Payload Structure +After that, you can use the binary directly: +```bash +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}' | cargo run --release +``` -The input JSON payload requires the following fields: + +#### JSON Payload Structure: Solution struct + +The `Solution` struct is composed of the following fields: - `sender`: The address initiating the transaction - `receiver`: The address receiving the output tokens diff --git a/src/bin/lib/cli.rs b/src/bin/lib/cli.rs new file mode 100644 index 0000000..1194ca5 --- /dev/null +++ b/src/bin/lib/cli.rs @@ -0,0 +1,45 @@ +pub use clap::Parser; +pub const DEFAULT_ROUTER_ADDRESS: &str = "0xaa820C29648D5EA543d712cC928377Bd7206a0E7"; + +#[derive(Parser)] +/// Encode swap transactions for the Tycho router +/// +/// Reads a JSON object from stdin with the following structure: +/// ```json +/// { +/// "sender": "0x...", +/// "receiver": "0x...", +/// "given_token": "0x...", +/// "given_amount": "123...", +/// "checked_token": "0x...", +/// "exact_out": false, +/// "slippage": 0.01, +/// "expected_amount": "123...", +/// "check_amount": "123...", +/// "swaps": [{ +/// "component": { +/// "id": "...", +/// "protocol_system": "...", +/// "protocol_type_name": "...", +/// "chain": "ethereum", +/// "tokens": ["0x..."], +/// "contract_ids": ["0x..."], +/// "static_attributes": {"key": "0x..."} +/// }, +/// "token_in": "0x...", +/// "token_out": "0x...", +/// "split": 1.0 +/// }], +/// "router_address": "0x...", +/// "direct_execution": false +/// } +/// ``` +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) + #[arg(short, long)] + pub private_key: Option, +} diff --git a/src/bin/lib/help.rs b/src/bin/lib/help.rs deleted file mode 100644 index a34bd99..0000000 --- a/src/bin/lib/help.rs +++ /dev/null @@ -1,37 +0,0 @@ -pub const HELP_TEXT: &str = "\ -USAGE: - tycho-encode [ROUTER_ADDRESS] [PRIVATE_KEY] - -ARGS: - ROUTER_ADDRESS The address of the router contract [default: 0x1234567890123456789012345678901234567890] - PRIVATE_KEY The private key for signing Permit2 approvals (required when direct_execution is false) - -The program reads a JSON object from stdin containing the swap details and outputs the encoded transaction. -The JSON object should have the following structure: -{ - \"sender\": \"0x...\", - \"receiver\": \"0x...\", - \"given_token\": \"0x...\", - \"given_amount\": \"123...\", - \"checked_token\": \"0x...\", - \"exact_out\": false, - \"slippage\": 0.01, - \"expected_amount\": \"123...\", - \"check_amount\": \"123...\", - \"swaps\": [{ - \"component\": { - \"id\": \"...\", - \"protocol_system\": \"...\", - \"protocol_type_name\": \"...\", - \"chain\": \"ethereum\", - \"tokens\": [\"0x...\"], - \"contract_ids\": [\"0x...\"], - \"static_attributes\": {\"key\": \"0x...\"} - }, - \"token_in\": \"0x...\", - \"token_out\": \"0x...\", - \"split\": 1.0 - }], - \"router_address\": \"0x...\", - \"direct_execution\": false -}"; diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index e038115..8bd518f 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,5 +1,6 @@ use std::io::{self, Read}; +use clap::Parser; use serde_json::Value; use tycho_core::dto::Chain; use tycho_execution::encoding::{ @@ -13,32 +14,15 @@ use tycho_execution::encoding::{ }; mod lib { - pub mod help; + pub mod cli; } -const DEFAULT_ROUTER_ADDRESS: &str = "0xaa820C29648D5EA543d712cC928377Bd7206a0E7"; +use lib::cli::Cli; + const DEFAULT_EXECUTORS_FILE_PATH: &str = "src/encoding/config/executor_addresses.json"; -const DEFAULT_PRIVATE_KEY: &str = - "0x938f4da9d3a947a4a6c53cfd8fcdd876641d6a4519243820b648af0bc3e67f7c"; fn main() -> Result<(), Box> { - let args: Vec = std::env::args().collect(); - - // Show help text if requested - if args.len() > 1 && (args[1] == "-h" || args[1] == "--help") { - println!("{}", lib::help::HELP_TEXT); - return Ok(()); - } - - let router_address = args - .get(1) - .map(|s| s.as_str()) - .unwrap_or(DEFAULT_ROUTER_ADDRESS); - - let private_key = args - .get(2) - .map(|s| s.to_string()) - .or_else(|| Some(DEFAULT_PRIVATE_KEY.to_string())); + let cli = Cli::parse(); // Read from stdin until EOF let mut buffer = String::new(); @@ -47,13 +31,11 @@ fn main() -> Result<(), Box> { .map_err(|e| format!("Failed to read from stdin: {}", e))?; if buffer.trim().is_empty() { - eprintln!("Error: No input provided"); - eprintln!("{}", lib::help::HELP_TEXT); - std::process::exit(1); + return Err("No input provided. Expected JSON input on stdin.".into()); } // Encode the solution - let encoded = encode_swaps(&buffer, router_address, private_key)?; + let encoded = encode_swaps(&buffer, &cli.router_address, cli.private_key)?; // Output the encoded result as JSON to stdout println!( diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 44504b5..dcc7c50 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -5,7 +5,6 @@ use tycho_core::{dto::ProtocolComponent, Bytes}; use crate::encoding::serde_primitives::{biguint_string, biguint_string_option}; #[derive(Clone, Default, Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] pub struct Solution { /// Address of the sender. pub sender: Bytes,