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": [
"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"
}
}

View File

@ -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"

View File

@ -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"

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)]
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::<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!")
}
}
// 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<TraderSettings> {
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<Self> {
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::<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 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<OfferConditions> {
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<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 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(())
}

View File

@ -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)
}
}