From 3bed20dc401cf8e5b8ce074f5ea43ab83de6d1ad Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:55:08 +0000 Subject: [PATCH] add method to request order creation --- .vscode/settings.json | 6 +- taptrade-cli-demo/trader/Cargo.lock | 79 +++++++ taptrade-cli-demo/trader/Cargo.toml | 2 + taptrade-cli-demo/trader/src/cli/mod.rs | 218 ++++++++++++------ .../trader/src/communication/mod.rs | 49 +++- taptrade-cli-demo/trader/src/main.rs | 24 +- taptrade-cli-demo/trader/src/trading/mod.rs | 6 +- 7 files changed, 290 insertions(+), 94 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3af7e1f..cd5ce5d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,8 @@ { "rust-analyzer.linkedProjects": [ - "taptrade-cli-demo/coordinator/Cargo.toml", - "taptrade-cli-demo/trader/Cargo.toml", + "${workspaceFolder}/taptrade-cli-demo/coordinator/Cargo.toml", + "${workspaceFolder}/taptrade-cli-demo/trader/Cargo.toml", ], "nixEnvSelector.suggestion": true, "nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix" -} \ No newline at end of file +} diff --git a/taptrade-cli-demo/trader/Cargo.lock b/taptrade-cli-demo/trader/Cargo.lock index b9500f9..9b9c3fa 100644 --- a/taptrade-cli-demo/trader/Cargo.lock +++ b/taptrade-cli-demo/trader/Cargo.lock @@ -122,6 +122,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base91" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb5fbae7b5ee422f239444a3dca9bdf5ecb3abf3af1bf87c8097db3f7bc025" + [[package]] name = "bdk" version = "0.29.0" @@ -203,6 +209,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -295,6 +310,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -319,6 +343,26 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "electrum-client" version = "0.18.0" @@ -468,6 +512,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1202,6 +1256,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "slab" version = "0.4.9" @@ -1433,10 +1498,12 @@ name = "trader" version = "0.1.0" dependencies = [ "anyhow", + "base91", "bdk", "clap", "reqwest", "serde", + "sha2", ] [[package]] @@ -1445,6 +1512,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -1495,6 +1568,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "want" version = "0.3.1" diff --git a/taptrade-cli-demo/trader/Cargo.toml b/taptrade-cli-demo/trader/Cargo.toml index 3b4a6d7..0d457e9 100644 --- a/taptrade-cli-demo/trader/Cargo.toml +++ b/taptrade-cli-demo/trader/Cargo.toml @@ -5,7 +5,9 @@ edition = "2021" [dependencies] anyhow = "1.0.86" +base91 = "0.1.0" bdk = "0.29.0" clap = { version = "4.5.4", features = ["derive", "cargo"] } reqwest = { version = "0.12.4", features = ["blocking", "json"] } serde = "1.0.203" +sha2 = "0.10.8" diff --git a/taptrade-cli-demo/trader/src/cli/mod.rs b/taptrade-cli-demo/trader/src/cli/mod.rs index 22b6a5c..10e161e 100755 --- a/taptrade-cli-demo/trader/src/cli/mod.rs +++ b/taptrade-cli-demo/trader/src/cli/mod.rs @@ -1,12 +1,25 @@ -use clap::{command, Arg, Command, ArgMatches}; +use std::io::{self, Write}; +use anyhow::{anyhow, Result}; +use sha2::{Sha256, Digest}; +use base91; #[derive(Debug)] pub struct Coordinator; +#[derive(Debug)] +pub enum OfferType { + Buy(u32), + Sell(u32), +} + #[derive(Debug)] pub struct TraderSettings { pub electrum_endpoint: String, pub coordinator_endpoint: String, + pub robosats_robohash_base91: String, + pub trade_type: OfferType, + pub payout_address: String, + pub bond_ratio: u8, } #[derive(Debug)] @@ -16,76 +29,143 @@ pub enum CliSettings { Maker(TraderSettings) } -trait ArgMatchesParser { - fn parse_into_enum(&self) -> CliSettings; +fn hash256(input: &String) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(input.as_bytes()); + hasher.finalize().into() } -impl ArgMatchesParser for ArgMatches { - fn parse_into_enum(&self) -> CliSettings { - if let Some(_mode) = self.subcommand_matches("coordinator") { - CliSettings::Coordinator(Coordinator { }) - } else if let Some(mode) = self.subcommand_matches("trader") { - let trader_settings = TraderSettings { - coordinator_endpoint: mode.get_one::("coordinator-ep") - .expect("Coordinator endpoint not provided!").clone(), - electrum_endpoint: mode.get_one::("electrum-ep") - .expect("Electrum endpoint not provided").clone() - }; - if mode.contains_id("maker") { - CliSettings::Maker( trader_settings ) - } else if mode.contains_id("taker") { - CliSettings::Taker( trader_settings ) - } else { - panic!("Wrong arguments for Trader mode!") - } - } else { - panic!("Select either coordinator or trader mode!") - } - } +// Robosats uses base91 encoded sha256 hash of the private robot key +fn bytes_to_base91(input: &[u8; 32]) -> String { + let encoded_robohash: String = base91::EncodeIterator::new(input.iter().copied()) + .as_char_iter() + .collect(); + encoded_robohash } -pub fn parse_cli_args() -> CliSettings { - command!() - .about("RoboSats taproot onchain trade pipeline CLI demonstrator. Don't use with real funds.") - .subcommand( - Command::new("coordinator") - .about("Run in coordinator mode.") - ) - .subcommand( - Command::new("trader") - .about("Two available trader modes: Maker and Taker. Select one and provide Coordinator and Electum endpoint") - .arg( - Arg::new("taker") - .short('t') - .long("taker") - .help("Run program as taker") - .num_args(0) - .conflicts_with("maker") - ) - .arg ( - Arg::new("maker") - .short('m') - .long("maker") - .num_args(0) - .help("Run program as maker") - .conflicts_with("taker") - ) - .arg( - Arg::new("coordinator-ep") - .short('p') - .long("endpoint") - .required(true) - .help("Communication endpoint of the coordinator to connect to") - ) - .arg( - Arg::new("electrum-ep") - .short('e') - .long("electrum") - .required(true) - .help("URL of the electrum endpoint") - ) - ) - .arg_required_else_help(true) - .get_matches() - .parse_into_enum() +impl CliSettings { + fn get_user_input(prompt: &str) -> String { + let mut buffer = String::new(); + print!("{}", prompt); + io::stdout().flush().unwrap(); + io::stdin() + .read_line(&mut buffer) + .expect("Failed to read line!"); + buffer.trim().to_string() + } + + fn get_trade_type() -> OfferType { + let trade_type = Self::get_user_input("Do you want to buy or sell satoshis: "); + match trade_type.as_str() { + "buy" => OfferType::Buy(Self::get_user_input("How many satoshi do you want to buy: ").parse().unwrap()), + "sell" => OfferType::Sell(Self::get_user_input("How many satoshi do you want to sell: ").parse().unwrap()), + _ => panic!("Wrong offer type, you can only buy or sell"), + } + } + + fn get_trader_settings() -> Result { + let electrum_endpoint = Self::get_user_input("Enter electrum endpoint: "); + let coordinator_endpoint = Self::get_user_input("Enter coordinator endpoint: "); + let robosats_robohash_base91 = bytes_to_base91(&hash256(&Self::get_user_input("Enter your robosats robot key: "))); + let trade_type = Self::get_trade_type(); + let payout_address = Self::get_user_input("Enter a payout address for refunded bonds or your trade payout: "); // bdk can be used for validation + let bond_ratio: u8 = Self::get_user_input("Enter bond ration in [2, 50]%: ").parse()?; + Ok(TraderSettings { + electrum_endpoint, + coordinator_endpoint, + robosats_robohash_base91, + trade_type, + payout_address, + bond_ratio + }) + } + + pub fn parse_cli_args() -> Result { + let mode = Self::get_user_input("Enter mode, 'taker' or 'maker': "); + let trader_settings = Self::get_trader_settings()?; + match mode.to_lowercase().as_str() { + "maker" => Ok(Self::Maker(trader_settings)), + "taker" => Ok(Self::Taker(trader_settings)), + _ => Err(anyhow!("Either select maker or taker!")), + } + } } + + +// old cli parser using clap + +// use clap::{command, Arg, Command, ArgMatches}; + +// trait ArgMatchesParser { +// fn parse_into_enum(&self) -> CliSettings; +// } + +// impl ArgMatchesParser for ArgMatches { +// fn parse_into_enum(&self) -> CliSettings { +// if let Some(_mode) = self.subcommand_matches("coordinator") { +// CliSettings::Coordinator(Coordinator { }) +// } else if let Some(mode) = self.subcommand_matches("trader") { +// let trader_settings = TraderSettings { +// coordinator_endpoint: mode.get_one::("coordinator-ep") +// .expect("Coordinator endpoint not provided!").clone(), +// electrum_endpoint: mode.get_one::("electrum-ep") +// .expect("Electrum endpoint not provided").clone() +// }; +// if mode.contains_id("maker") { +// CliSettings::Maker( trader_settings ) +// } else if mode.contains_id("taker") { +// CliSettings::Taker( trader_settings ) +// } else { +// panic!("Wrong arguments for Trader mode!") +// } +// } else { +// panic!("Select either coordinator or trader mode!") +// } +// } +// } + +// pub fn parse_cli_args() -> CliSettings { +// command!() +// .about("RoboSats taproot onchain trade pipeline CLI demonstrator. Don't use with real funds.") +// .subcommand( +// Command::new("coordinator") +// .about("Run in coordinator mode.") +// ) +// .subcommand( +// Command::new("trader") +// .about("Two available trader modes: Maker and Taker. Select one and provide Coordinator and Electum endpoint") +// .arg( +// Arg::new("taker") +// .short('t') +// .long("taker") +// .help("Run program as taker") +// .num_args(0) +// .conflicts_with("maker") +// ) +// .arg ( +// Arg::new("maker") +// .short('m') +// .long("maker") +// .num_args(0) +// .help("Run program as maker") +// .conflicts_with("taker") +// ) +// .arg( +// Arg::new("coordinator-ep") +// .short('p') +// .long("endpoint") +// .required(true) +// .help("Communication endpoint of the coordinator to connect to") +// ) +// .arg( +// Arg::new("electrum-ep") +// .short('e') +// .long("electrum") +// .required(true) +// .help("URL of the electrum endpoint") +// ) +// ) +// .arg_required_else_help(true) +// .get_matches() +// .parse_into_enum() +// } diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index fab6549..1088009 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -1,14 +1,51 @@ use reqwest; use anyhow::Result; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; + +use crate::cli::{TraderSettings, OfferType}; #[derive(Debug, Deserialize)] -pub struct OfferConditions { +pub struct OfferCreationResponse { pub locking_address: String, } -pub fn fetch_offer(coordinator_ep: &String) -> Result { - let res = reqwest::blocking::get(format!("{}{}", coordinator_ep, "/create-offer"))?; - let offer_conditions: OfferConditions = res.json()?; - Ok(offer_conditions) +#[derive(Serialize)] +struct OrderRequest { + robohash_base91: String, + amount_satoshi: u32, + order_type: String, // buy or sell + bond_ratio: u8 // [x > 2, 50] } + +impl OfferCreationResponse { + fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { + let amount: u32; + let trade_type = match &trader_setup.trade_type { + OfferType::Buy(val) => { + amount = *val; + "buy" + }, + OfferType::Sell(val) => { + amount = *val; + "sell" + } + }; + + OrderRequest { + robohash_base91: trader_setup.robosats_robohash_base91.clone(), + amount_satoshi: amount, + order_type: trade_type.to_string(), + bond_ratio: trader_setup.bond_ratio, + } + } + + pub fn fetch(trader_setup: &TraderSettings) -> Result { + let client = reqwest::blocking::Client::new(); + let res = client.post(format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer")) + .json(&Self::_format_request(trader_setup)) + .send()? + .json::()?; + Ok(res) + } +} + diff --git a/taptrade-cli-demo/trader/src/main.rs b/taptrade-cli-demo/trader/src/main.rs index 796c2b1..5aaa8e1 100644 --- a/taptrade-cli-demo/trader/src/main.rs +++ b/taptrade-cli-demo/trader/src/main.rs @@ -5,22 +5,20 @@ mod trading; use core::panic; -use cli::{parse_cli_args, CliSettings}; +use cli::CliSettings; use anyhow::Result; use communication::fetch_offer; fn main() -> Result<()> { - let mode = parse_cli_args(); - dbg!(mode); - - if let CliSettings::Maker(maker_config) = mode { - trading::maker::run_maker(maker_config)?; - } else if CliSettings::Taker(taker_data) = mode { - trading::taker::run_taker(taker_data)?; - } else { - panic!("Wrong mode selected!") - } - - + let mode = CliSettings::parse_cli_args()?; + dbg!("CLI input :", &mode); + + // if let CliSettings::Maker(maker_data) = &mode { + // trading::maker::run_maker(maker_data)?; + // } else if let CliSettings::Taker(taker_data) = &mode { + // trading::taker::run_taker(taker_data)?; + // } else { + // panic!("Wrong mode selected!") + // } Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 455f1a6..6fd5efc 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -3,9 +3,9 @@ // use utils; use anyhow::Result; use crate::cli::TraderSettings; -use crate::communication::fetch_offer; +use crate::communication::create_offer; pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { - let offer_conditions = fetch_offer(&maker_config.coordinator_endpoint)?; + let offer_conditions = create_offer(maker_config)?; // maker_utils::maker(offer_conditions, maker_config) -} \ No newline at end of file +}