add method to request order creation

This commit is contained in:
Felix
2024-06-05 14:55:08 +00:00
parent 6434ae1aae
commit 3bed20dc40
7 changed files with 290 additions and 94 deletions

View File

@ -1,8 +1,8 @@
{ {
"rust-analyzer.linkedProjects": [ "rust-analyzer.linkedProjects": [
"taptrade-cli-demo/coordinator/Cargo.toml", "${workspaceFolder}/taptrade-cli-demo/coordinator/Cargo.toml",
"taptrade-cli-demo/trader/Cargo.toml", "${workspaceFolder}/taptrade-cli-demo/trader/Cargo.toml",
], ],
"nixEnvSelector.suggestion": true, "nixEnvSelector.suggestion": true,
"nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix" "nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix"
} }

View File

@ -122,6 +122,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base91"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eb5fbae7b5ee422f239444a3dca9bdf5ecb3abf3af1bf87c8097db3f7bc025"
[[package]] [[package]]
name = "bdk" name = "bdk"
version = "0.29.0" version = "0.29.0"
@ -203,6 +209,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 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]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.16.0" version = "3.16.0"
@ -295,6 +310,15 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.2" version = "1.4.2"
@ -319,6 +343,26 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 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]] [[package]]
name = "electrum-client" name = "electrum-client"
version = "0.18.0" version = "0.18.0"
@ -468,6 +512,16 @@ dependencies = [
"byteorder", "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]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -1202,6 +1256,17 @@ dependencies = [
"serde", "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]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -1433,10 +1498,12 @@ name = "trader"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base91",
"bdk", "bdk",
"clap", "clap",
"reqwest", "reqwest",
"serde", "serde",
"sha2",
] ]
[[package]] [[package]]
@ -1445,6 +1512,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.15" version = "0.3.15"
@ -1495,6 +1568,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.1" version = "0.3.1"

View File

@ -5,7 +5,9 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"
base91 = "0.1.0"
bdk = "0.29.0" bdk = "0.29.0"
clap = { version = "4.5.4", features = ["derive", "cargo"] } clap = { version = "4.5.4", features = ["derive", "cargo"] }
reqwest = { version = "0.12.4", features = ["blocking", "json"] } reqwest = { version = "0.12.4", features = ["blocking", "json"] }
serde = "1.0.203" serde = "1.0.203"
sha2 = "0.10.8"

View File

@ -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)] #[derive(Debug)]
pub struct Coordinator; pub struct Coordinator;
#[derive(Debug)]
pub enum OfferType {
Buy(u32),
Sell(u32),
}
#[derive(Debug)] #[derive(Debug)]
pub struct TraderSettings { pub struct TraderSettings {
pub electrum_endpoint: String, pub electrum_endpoint: String,
pub coordinator_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)] #[derive(Debug)]
@ -16,76 +29,143 @@ pub enum CliSettings {
Maker(TraderSettings) Maker(TraderSettings)
} }
trait ArgMatchesParser { fn hash256(input: &String) -> [u8; 32] {
fn parse_into_enum(&self) -> CliSettings; let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
hasher.finalize().into()
} }
impl ArgMatchesParser for ArgMatches { // Robosats uses base91 encoded sha256 hash of the private robot key
fn parse_into_enum(&self) -> CliSettings { fn bytes_to_base91(input: &[u8; 32]) -> String {
if let Some(_mode) = self.subcommand_matches("coordinator") { let encoded_robohash: String = base91::EncodeIterator::new(input.iter().copied())
CliSettings::Coordinator(Coordinator { }) .as_char_iter()
} else if let Some(mode) = self.subcommand_matches("trader") { .collect();
let trader_settings = TraderSettings { encoded_robohash
coordinator_endpoint: mode.get_one::<String>("coordinator-ep")
.expect("Coordinator endpoint not provided!").clone(),
electrum_endpoint: mode.get_one::<String>("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 { impl CliSettings {
command!() fn get_user_input(prompt: &str) -> String {
.about("RoboSats taproot onchain trade pipeline CLI demonstrator. Don't use with real funds.") let mut buffer = String::new();
.subcommand( print!("{}", prompt);
Command::new("coordinator") io::stdout().flush().unwrap();
.about("Run in coordinator mode.") io::stdin()
) .read_line(&mut buffer)
.subcommand( .expect("Failed to read line!");
Command::new("trader") buffer.trim().to_string()
.about("Two available trader modes: Maker and Taker. Select one and provide Coordinator and Electum endpoint") }
.arg(
Arg::new("taker") fn get_trade_type() -> OfferType {
.short('t') let trade_type = Self::get_user_input("Do you want to buy or sell satoshis: ");
.long("taker") match trade_type.as_str() {
.help("Run program as taker") "buy" => OfferType::Buy(Self::get_user_input("How many satoshi do you want to buy: ").parse().unwrap()),
.num_args(0) "sell" => OfferType::Sell(Self::get_user_input("How many satoshi do you want to sell: ").parse().unwrap()),
.conflicts_with("maker") _ => panic!("Wrong offer type, you can only buy or sell"),
) }
.arg ( }
Arg::new("maker")
.short('m') fn get_trader_settings() -> Result<TraderSettings> {
.long("maker") let electrum_endpoint = Self::get_user_input("Enter electrum endpoint: ");
.num_args(0) let coordinator_endpoint = Self::get_user_input("Enter coordinator endpoint: ");
.help("Run program as maker") let robosats_robohash_base91 = bytes_to_base91(&hash256(&Self::get_user_input("Enter your robosats robot key: ")));
.conflicts_with("taker") 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
.arg( let bond_ratio: u8 = Self::get_user_input("Enter bond ration in [2, 50]%: ").parse()?;
Arg::new("coordinator-ep") Ok(TraderSettings {
.short('p') electrum_endpoint,
.long("endpoint") coordinator_endpoint,
.required(true) robosats_robohash_base91,
.help("Communication endpoint of the coordinator to connect to") trade_type,
) payout_address,
.arg( bond_ratio
Arg::new("electrum-ep") })
.short('e') }
.long("electrum")
.required(true) pub fn parse_cli_args() -> Result<Self> {
.help("URL of the electrum endpoint") let mode = Self::get_user_input("Enter mode, 'taker' or 'maker': ");
) let trader_settings = Self::get_trader_settings()?;
) match mode.to_lowercase().as_str() {
.arg_required_else_help(true) "maker" => Ok(Self::Maker(trader_settings)),
.get_matches() "taker" => Ok(Self::Taker(trader_settings)),
.parse_into_enum() _ => 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::<String>("coordinator-ep")
// .expect("Coordinator endpoint not provided!").clone(),
// electrum_endpoint: mode.get_one::<String>("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()
// }

View File

@ -1,14 +1,51 @@
use reqwest; use reqwest;
use anyhow::Result; use anyhow::Result;
use serde::Deserialize; use serde::{Deserialize, Serialize};
use crate::cli::{TraderSettings, OfferType};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct OfferConditions { pub struct OfferCreationResponse {
pub locking_address: String, pub locking_address: String,
} }
pub fn fetch_offer(coordinator_ep: &String) -> Result<OfferConditions> { #[derive(Serialize)]
let res = reqwest::blocking::get(format!("{}{}", coordinator_ep, "/create-offer"))?; struct OrderRequest {
let offer_conditions: OfferConditions = res.json()?; robohash_base91: String,
Ok(offer_conditions) 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<OfferCreationResponse> {
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::<OfferCreationResponse>()?;
Ok(res)
}
}

View File

@ -5,22 +5,20 @@ mod trading;
use core::panic; use core::panic;
use cli::{parse_cli_args, CliSettings}; use cli::CliSettings;
use anyhow::Result; use anyhow::Result;
use communication::fetch_offer; use communication::fetch_offer;
fn main() -> Result<()> { fn main() -> Result<()> {
let mode = parse_cli_args(); let mode = CliSettings::parse_cli_args()?;
dbg!(mode); dbg!("CLI input :", &mode);
if let CliSettings::Maker(maker_config) = mode { // if let CliSettings::Maker(maker_data) = &mode {
trading::maker::run_maker(maker_config)?; // trading::maker::run_maker(maker_data)?;
} else if CliSettings::Taker(taker_data) = mode { // } else if let CliSettings::Taker(taker_data) = &mode {
trading::taker::run_taker(taker_data)?; // trading::taker::run_taker(taker_data)?;
} else { // } else {
panic!("Wrong mode selected!") // panic!("Wrong mode selected!")
} // }
Ok(()) Ok(())
} }

View File

@ -3,9 +3,9 @@
// use utils; // use utils;
use anyhow::Result; use anyhow::Result;
use crate::cli::TraderSettings; use crate::cli::TraderSettings;
use crate::communication::fetch_offer; use crate::communication::create_offer;
pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { 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) // maker_utils::maker(offer_conditions, maker_config)
} }