From 125bf4ddcf9b4018ab53ba1d4ec0a11d236549cf Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:05:16 +0000 Subject: [PATCH] taker --- taptrade-cli-demo/.rustfmt.toml | 1 + .../coordinator/src/communication/mod.rs | 86 ++++---- taptrade-cli-demo/trader/{.env => maker.env} | 2 +- taptrade-cli-demo/trader/src/cli/mod.rs | 201 ++++++++++-------- .../trader/src/communication/api.rs | 20 +- .../trader/src/communication/mod.rs | 47 ++-- taptrade-cli-demo/trader/src/main.rs | 27 ++- taptrade-cli-demo/trader/src/trading/mod.rs | 39 ++-- taptrade-cli-demo/trader/src/trading/utils.rs | 1 + taptrade-cli-demo/trader/src/wallet/bond.rs | 46 ++-- taptrade-cli-demo/trader/src/wallet/mod.rs | 49 +++-- taptrade-cli-demo/trader/src/wallet/musig2.rs | 1 - .../trader/src/wallet/wallet_utils.rs | 2 +- taptrade-cli-demo/trader/taker.env | 7 + 14 files changed, 284 insertions(+), 245 deletions(-) create mode 100644 taptrade-cli-demo/.rustfmt.toml rename taptrade-cli-demo/trader/{.env => maker.env} (85%) create mode 100644 taptrade-cli-demo/trader/taker.env diff --git a/taptrade-cli-demo/.rustfmt.toml b/taptrade-cli-demo/.rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/taptrade-cli-demo/.rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index 84c71ed..e7205a5 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -1,68 +1,62 @@ -use axum::{ - routing::post, - Json, Router, -}; +use axum::{routing::post, Json, Router}; use serde::{Deserialize, Serialize}; use std::net::SocketAddr; use tokio::net::TcpListener; - #[derive(Deserialize, Serialize, Debug)] struct OrderRequest { - robohash_base91: String, - amount_satoshi: u64, - order_type: String, - bond_ratio: u8, + robohash_base91: String, + amount_satoshi: u64, + order_type: String, + bond_ratio: u8, } // Handler function to process the received data async fn receive_order(Json(order): Json) { - // Print the received data to the console - println!("Received order: {:?}", order); - - // Access individual fields - let robohash = &order.robohash_base91; - let amount = order.amount_satoshi; - let order_type = &order.order_type; - let bond_ratio = order.bond_ratio; + // Print the received data to the console + println!("Received order: {:?}", order); - // Process the data as needed - // For example, you can log the data, save it to a database, etc. - println!("Robohash: {}", robohash); - println!("Amount (satoshi): {}", amount); - println!("Order type: {}", order_type); - println!("Bond ratio: {}", bond_ratio); + // Access individual fields + let robohash = &order.robohash_base91; + let amount = order.amount_satoshi; + let order_type = &order.order_type; + let bond_ratio = order.bond_ratio; - // Example of further processing - if order_type == "buy" { - println!("Processing a buy order..."); - // Add your buy order logic here - } else if order_type == "sell" { - println!("Processing a sell order..."); - // Add your sell order logic here - } + // Process the data as needed + // For example, you can log the data, save it to a database, etc. + println!("Robohash: {}", robohash); + println!("Amount (satoshi): {}", amount); + println!("Order type: {}", order_type); + println!("Bond ratio: {}", bond_ratio); + + // Example of further processing + if order_type == "buy" { + println!("Processing a buy order..."); + // Add your buy order logic here + } else if order_type == "sell" { + println!("Processing a sell order..."); + // Add your sell order logic here + } } #[tokio::main] pub async fn webserver() { - // Build our application with a single route - let app = Router::new().route("/receive-order", post(receive_order)); - - // Run the server on localhost:3000 - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); - println!("Listening on {}", addr); - // axum::Server::bind(&addr) - // .serve(app.into_make_service()) - // .await - // .unwrap(); - let tcp = TcpListener::bind(&addr).await.unwrap(); - axum::serve(tcp, app).await.unwrap(); + // Build our application with a single route + let app = Router::new().route("/receive-order", post(receive_order)); + // Run the server on localhost:3000 + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); + println!("Listening on {}", addr); + // axum::Server::bind(&addr) + // .serve(app.into_make_service()) + // .await + // .unwrap(); + let tcp = TcpListener::bind(&addr).await.unwrap(); + axum::serve(tcp, app).await.unwrap(); } - // // use axum -// #[get("/")] +// #[get("/")] // fn index() -> &'static str { // "Hello, world!" // } @@ -74,4 +68,4 @@ pub async fn webserver() { // // serde to parse json // // https://www.youtube.com/watch?v=md-ecvXBGzI BDK + Webserver video -// // https://github.com/tokio-rs/axum \ No newline at end of file +// // https://github.com/tokio-rs/axum diff --git a/taptrade-cli-demo/trader/.env b/taptrade-cli-demo/trader/maker.env similarity index 85% rename from taptrade-cli-demo/trader/.env rename to taptrade-cli-demo/trader/maker.env index 4c22fc6..b485306 100644 --- a/taptrade-cli-demo/trader/.env +++ b/taptrade-cli-demo/trader/maker.env @@ -1,6 +1,6 @@ ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server COORDINATOR_ENDPOINT="http://127.0.0.1:3000" -ROBOHASH_BASE91="o!kfNkee;RF?m6FT87:1bjASE2/(1NFTnR;ILmHB<1p/vP%STz~IWolNDJa.#PmTI)sfBk+Gs!,(w8M" # "robot21" +ROBOHASH_BASE91="o!kfNkee;RF?m6FT87:1bjASE2/(1NFTnR;ILmHB<1p/vP%STz~IWolNDJa.#PmTI)sfBk+Gs!,(w8M" # "base91 of hex of sha256 of robot21" TRADE_TYPE="buy" PAYOUT_ADDRESS="tb1p45daj2eaza6drcd85c3wvn0zrpqxuduk3rzcmla4eu7a02cep9kqjzkc64" BOND_RATIO=5 diff --git a/taptrade-cli-demo/trader/src/cli/mod.rs b/taptrade-cli-demo/trader/src/cli/mod.rs index 5295235..5467451 100755 --- a/taptrade-cli-demo/trader/src/cli/mod.rs +++ b/taptrade-cli-demo/trader/src/cli/mod.rs @@ -1,7 +1,7 @@ -use std::io::{self, Write}; use anyhow::{anyhow, Result}; -use sha2::{Sha256, Digest}; +use sha2::{Digest, Sha256}; use std::env; +use std::io::{self, Write}; use crate::wallet::{get_wallet_xprv, WalletDescriptors}; use bdk::bitcoin::bip32::ExtendedPrivKey; @@ -11,131 +11,144 @@ pub struct Coordinator; #[derive(Debug)] pub enum OfferType { - Buy(u64), - Sell(u64), + Buy(u64), + Sell(u64), } #[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, - pub wallet_xprv: ExtendedPrivKey, + pub robosats_robohash_base91: String, + pub trade_type: OfferType, + pub payout_address: String, + pub bond_ratio: u8, + pub wallet_xprv: ExtendedPrivKey, } #[derive(Debug)] pub enum CliSettings { Coordinator(Coordinator), Taker(TraderSettings), - Maker(TraderSettings) + Maker(TraderSettings), } fn hash256(input: &String) -> [u8; 32] { - let mut hasher = Sha256::new(); - hasher.update(input.as_bytes()); - hasher.finalize().into() + let mut hasher = Sha256::new(); + hasher.update(input.as_bytes()); + hasher.finalize().into() } // 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 + let encoded_robohash: String = base91::EncodeIterator::new(input.iter().copied()) + .as_char_iter() + .collect(); + encoded_robohash } impl OfferType { - pub fn value(&self) -> u64 { - match self { - OfferType::Buy(value) => *value, - OfferType::Sell(value) => *value, - } - } + pub fn value(&self) -> u64 { + match self { + OfferType::Buy(value) => *value, + OfferType::Sell(value) => *value, + } + } } impl CliSettings { - fn get_user_input(prompt: &str) -> String { - let mut buffer = String::new(); - print!("{}", prompt); + 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() - } + io::stdin() + .read_line(&mut buffer) + .expect("Failed to read line!"); + buffer.trim().to_string() + } - fn get_trade_type(trade_type: Option) -> OfferType { - let trade_type = match trade_type { - Some(value) => value, - None => 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_trade_type(trade_type: Option) -> OfferType { + let trade_type = match trade_type { + Some(value) => value, + None => 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: OfferType = Self::get_trade_type(None); - 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()?; - let wallet_xprv = Self::check_xprv_input(Some(Self::get_user_input("Enter funded testnet wallet xprv or leave empty to generate: ")))?; - Ok(TraderSettings { - electrum_endpoint, - coordinator_endpoint, - robosats_robohash_base91, - trade_type, - payout_address, - bond_ratio, - wallet_xprv - }) - } + 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: OfferType = Self::get_trade_type(None); + 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()?; + let wallet_xprv = Self::check_xprv_input(Some(Self::get_user_input( + "Enter funded testnet wallet xprv or leave empty to generate: ", + )))?; + Ok(TraderSettings { + electrum_endpoint, + coordinator_endpoint, + robosats_robohash_base91, + trade_type, + payout_address, + bond_ratio, + wallet_xprv, + }) + } - fn check_xprv_input(cli_input: Option) -> Result { - if let Some(user_input) = cli_input { - if !(user_input.is_empty()) { - return get_wallet_xprv(Some(user_input)); - } - }; - get_wallet_xprv(None) - } + fn check_xprv_input(cli_input: Option) -> Result { + if let Some(user_input) = cli_input { + if !(user_input.is_empty()) { + return get_wallet_xprv(Some(user_input)); + } + }; + get_wallet_xprv(None) + } - fn load_from_env() -> Result { - dotenv::from_filename(".env")?; - Ok(TraderSettings { - electrum_endpoint: env::var("ELECTRUM_ENDPOINT")?, - coordinator_endpoint: env::var("COORDINATOR_ENDPOINT")?, - robosats_robohash_base91: env::var("ROBOHASH_BASE91")?, - trade_type: Self::get_trade_type(Some(env::var("TRADE_TYPE")?)), - payout_address: env::var("PAYOUT_ADDRESS")?, - bond_ratio: env::var("BOND_RATIO")?.parse()?, - wallet_xprv: Self::check_xprv_input(Some(env::var("XPRV")?))?, - }) - } + fn load_from_env() -> Result { + dotenv::from_filename(".env")?; + Ok(TraderSettings { + electrum_endpoint: env::var("ELECTRUM_ENDPOINT")?, + coordinator_endpoint: env::var("COORDINATOR_ENDPOINT")?, + robosats_robohash_base91: env::var("ROBOHASH_BASE91")?, + trade_type: Self::get_trade_type(Some(env::var("TRADE_TYPE")?)), + payout_address: env::var("PAYOUT_ADDRESS")?, + bond_ratio: env::var("BOND_RATIO")?.parse()?, + wallet_xprv: Self::check_xprv_input(Some(env::var("XPRV")?))?, + }) + } - pub fn parse_cli_args() -> Result { - let mode = Self::get_user_input("Enter mode, 'taker' or 'maker': "); - let trader_settings = match Self::get_user_input("Load from .env (y/N): ").trim() { - "y" => Self::load_from_env()?, - "N" => Self::get_trader_settings()?, - _ => return Err(anyhow!("Not a valid input!")), - }; - 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!")), - } - } + pub fn parse_cli_args() -> Result { + let mode = Self::get_user_input("Enter mode, 'taker' or 'maker': "); + let trader_settings = match Self::get_user_input("Load from .env (y/N): ").trim() { + "y" => Self::load_from_env()?, + "N" => Self::get_trader_settings()?, + _ => return Err(anyhow!("Not a valid input!")), + }; + 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}; diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 2b3be61..1e55812 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -2,22 +2,22 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize)] pub struct OfferCreationResponse { - pub bond_address: String, - pub locking_amount: u64, // validate + pub bond_address: String, + pub locking_amount: u64, // validate } #[derive(Serialize)] pub struct OrderRequest { - pub robohash_base91: String, - pub amount_satoshi: u64, - pub order_type: String, // buy or sell - pub bond_ratio: u8 // [2, 50] + pub robohash_base91: String, + pub amount_satoshi: u64, + pub order_type: String, // buy or sell + pub bond_ratio: u8, // [2, 50] } #[derive(Serialize)] pub struct BondSubmissionRequest { - pub serialized_bond_tx_hex: String, - pub robohash_base91: String, - pub payout_address: String, - pub frost_public_nonce_hex: String, + pub serialized_bond_tx_hex: String, + pub robohash_base91: String, + pub payout_address: String, + pub frost_public_nonce_hex: String, } diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index 4d96ecb..5e207f8 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -3,38 +3,41 @@ pub mod api; use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::cli::{TraderSettings, OfferType}; -use api::{OrderRequest, OfferCreationResponse}; +use crate::cli::{OfferType, TraderSettings}; +use api::{OfferCreationResponse, OrderRequest}; impl OfferCreationResponse { fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { let amount: u64; let trade_type = match &trader_setup.trade_type { - OfferType::Buy(val) => { - amount = *val; - "buy" - }, - OfferType::Sell(val) => { - amount = *val; - "sell" - } - }; + 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, - } + 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::()?; + 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 380d87c..a53ab6c 100644 --- a/taptrade-cli-demo/trader/src/main.rs +++ b/taptrade-cli-demo/trader/src/main.rs @@ -6,26 +6,25 @@ mod wallet; use core::panic; -use cli::CliSettings; use anyhow::{anyhow, Result}; +use cli::CliSettings; fn start_trade_pipeline(cli_input: &CliSettings) -> Result<()> { - if let CliSettings::Maker(maker_data) = cli_input { - Ok(trading::run_maker(maker_data)?) - } else if let CliSettings::Taker(taker_data) = cli_input { - Err(anyhow!("not implemented!")) - // trading::taker::run_taker(taker_data)?; - } else { - Err(anyhow!("Wrong mode selected!")) - } + if let CliSettings::Maker(maker_data) = cli_input { + Ok(trading::run_maker(maker_data)?) + } else if let CliSettings::Taker(taker_data) = cli_input { + // trading::run_taker(taker_data)?; + } else { + Err(anyhow!("Wrong mode selected!")) + } } fn main() -> Result<()> { - // env_logger::builder().filter_level(log::LevelFilter::Debug).init(); // enable to show extended BDK debug messages + // env_logger::builder().filter_level(log::LevelFilter::Debug).init(); // enable to show extended BDK debug messages - let mode = CliSettings::parse_cli_args()?; - dbg!("CLI input :", &mode); - start_trade_pipeline(&mode)?; + let mode = CliSettings::parse_cli_args()?; + dbg!("CLI input :", &mode); + start_trade_pipeline(&mode)?; - Ok(()) + Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 2b35ecc..f77842c 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -1,33 +1,36 @@ // use maker_utils; // use taker_utils; -// use utils; +// mod utils; use std::borrow::Borrow; -use anyhow::Result; -use bdk::bitcoin::block; use crate::cli::TraderSettings; use crate::communication::api::OfferCreationResponse; -use crate::wallet::{load_wallet, bond::Bond}; +use crate::wallet::{bond::Bond, load_wallet}; +use anyhow::Result; +use bdk::bitcoin::block; use bdk::blockchain::{Blockchain, ElectrumBlockchain}; use bdk::electrum_client::Client; - pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { - let client = Client::new(&maker_config.electrum_endpoint)?; - let blockchain = ElectrumBlockchain::from(client); + let blockchain = ElectrumBlockchain::from(Client::new(&maker_config.electrum_endpoint)?); - let offer_conditions = OfferCreationResponse::fetch(maker_config)?; - // let offer_conditions = OfferCreationResponse { // hardcoded for testing, locking_address is owned by .env xprv - // locking_amount: 90000, - // bond_address: "tb1pfdvgfzwp8vhmelpv8w9kezz7nsmxw68jz6yehgze6mzx0t6r9t2qv9ynmm".to_string(), - // }; + let offer_conditions = OfferCreationResponse::fetch(maker_config)?; + // let offer_conditions = OfferCreationResponse { // hardcoded for testing, locking_address is owned by .env xprv + // locking_amount: 90000, + // bond_address: "tb1pfdvgfzwp8vhmelpv8w9kezz7nsmxw68jz6yehgze6mzx0t6r9t2qv9ynmm".to_string(), + // }; - let wallet = load_wallet(maker_config, &blockchain)?; // initialize the wallet with xprv + let wallet = load_wallet(maker_config, &blockchain)?; // initialize the wallet with xprv - let bond = Bond::assemble(&wallet, &offer_conditions, maker_config)?; // assemble the Bond transaction for offer creation - // blockchain.broadcast(&bond.extract_tx())?; // publish bond to be mined for testing - dbg!(&bond.extract_tx().txid()); - - Ok(()) + let bond = Bond::assemble(&wallet, &offer_conditions, maker_config)?; // assemble the Bond transaction for offer creation + // blockchain.broadcast(&bond.extract_tx())?; // publish bond to be mined for testing + dbg!(&bond.extract_tx().txid()); + Ok(()) +} + +pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { + let blockchain = ElectrumBlockchain::from(Client::new(&taker_config.electrum_endpoint)?); + + Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/utils.rs b/taptrade-cli-demo/trader/src/trading/utils.rs index e69de29..8b13789 100644 --- a/taptrade-cli-demo/trader/src/trading/utils.rs +++ b/taptrade-cli-demo/trader/src/trading/utils.rs @@ -0,0 +1 @@ + diff --git a/taptrade-cli-demo/trader/src/wallet/bond.rs b/taptrade-cli-demo/trader/src/wallet/bond.rs index a2ff5f3..a557e0d 100644 --- a/taptrade-cli-demo/trader/src/wallet/bond.rs +++ b/taptrade-cli-demo/trader/src/wallet/bond.rs @@ -1,47 +1,55 @@ -use anyhow::{Result, anyhow}; +use anyhow::{anyhow, Result}; +use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked}; use bdk::bitcoin::psbt::PartiallySignedTransaction; use bdk::bitcoin::ScriptBuf; -use bdk::{database::MemoryDatabase, wallet::coin_selection::BranchAndBoundCoinSelection, Wallet, SignOptions, FeeRate}; +use bdk::bitcoin::{Address, Network}; +use bdk::{ + database::MemoryDatabase, wallet::coin_selection::BranchAndBoundCoinSelection, FeeRate, + SignOptions, Wallet, +}; use serde::de::value; use std::str::FromStr; -use bdk::bitcoin::{Address, Network}; -use bdk::bitcoin::address::{NetworkUnchecked, NetworkChecked}; use crate::communication::api::OfferCreationResponse; use crate::wallet::TraderSettings; pub struct Outpoint { pub txid_hex: String, - pub index: u32 + pub index: u32, } pub struct Bond { pub signed_bond_tx_hex: String, - pub used_outpoint: Outpoint + pub used_outpoint: Outpoint, } impl Bond { - pub fn assemble(wallet: &Wallet, - bond_target: &OfferCreationResponse, - trader_input: &TraderSettings) -> Result { + pub fn assemble( + wallet: &Wallet, + bond_target: &OfferCreationResponse, + trader_input: &TraderSettings, + ) -> Result { // parse bond locking address as Address struct and verify network is testnet - let address: Address = Address::from_str(&bond_target.bond_address)? - .require_network(Network::Testnet)?; - - // build bond locking transaction. Use coin selection to add at least enough outputs - // to have the full trading sum as change as evidence for the coordinator that the maker owns + let address: Address = + Address::from_str(&bond_target.bond_address)?.require_network(Network::Testnet)?; + + // build bond locking transaction. Use coin selection to add at least enough outputs + // to have the full trading sum as change as evidence for the coordinator that the maker owns // enough funds to cover the trade let (mut psbt, details) = { - let mut builder = wallet.build_tx() - .coin_selection(BranchAndBoundCoinSelection::new(trader_input.trade_type.value())); - + let mut builder = wallet + .build_tx() + .coin_selection(BranchAndBoundCoinSelection::new( + trader_input.trade_type.value(), + )); + builder .add_recipient(address.script_pubkey(), bond_target.locking_amount) .do_not_spend_change() .fee_rate(FeeRate::from_sat_per_vb(201.0)); - + builder.finish()? - }; + }; let finalized = wallet.sign(&mut psbt, SignOptions::default())?; if !finalized { return Err(anyhow!("Transaction could not be finalized")); diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 1d53b2f..8776f55 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -1,21 +1,26 @@ pub mod bond; -pub mod wallet_utils; pub mod musig2; +pub mod wallet_utils; -use bdk::{bitcoin, keys::DescriptorPublicKey, miniscript::Descriptor, template::{Bip86, DescriptorTemplate}, KeychainKind, SyncOptions, Wallet}; -use bdk::database::MemoryDatabase; -use bdk::blockchain::ElectrumBlockchain; -use bdk::bitcoin::{Network, bip32::ExtendedPrivKey}; -use bdk::electrum_client::Client; -use anyhow::Result; -use wallet_utils::get_seed; -use std::str::FromStr; use crate::cli::TraderSettings; - +use anyhow::Result; +use bdk::bitcoin::{bip32::ExtendedPrivKey, Network}; +use bdk::blockchain::ElectrumBlockchain; +use bdk::database::MemoryDatabase; +use bdk::electrum_client::Client; +use bdk::{ + bitcoin, + keys::DescriptorPublicKey, + miniscript::Descriptor, + template::{Bip86, DescriptorTemplate}, + KeychainKind, SyncOptions, Wallet, +}; +use std::str::FromStr; +use wallet_utils::get_seed; pub struct WalletDescriptors { pub descriptor: Bip86, - pub change_descriptor: Option> + pub change_descriptor: Option>, } pub fn get_wallet_xprv(xprv_input: Option) -> Result { @@ -32,15 +37,21 @@ pub fn get_wallet_xprv(xprv_input: Option) -> Result { Ok(xprv) } -pub fn load_wallet(trader_config: &TraderSettings, blockchain: &ElectrumBlockchain) -> Result> { +pub fn load_wallet( + trader_config: &TraderSettings, + blockchain: &ElectrumBlockchain, +) -> Result> { let wallet = Wallet::new( Bip86(trader_config.wallet_xprv.clone(), KeychainKind::External), - Some(Bip86(trader_config.wallet_xprv.clone(), KeychainKind::Internal)), - bitcoin::Network::Testnet, - MemoryDatabase::default(), // non-permanent storage - )?; + Some(Bip86( + trader_config.wallet_xprv.clone(), + KeychainKind::Internal, + )), + bitcoin::Network::Testnet, + MemoryDatabase::default(), // non-permanent storage + )?; - wallet.sync(blockchain, SyncOptions::default())?; - println!("Descriptor balance: {} SAT", wallet.get_balance()?); - Ok(wallet) + wallet.sync(blockchain, SyncOptions::default())?; + println!("Descriptor balance: {} SAT", wallet.get_balance()?); + Ok(wallet) } diff --git a/taptrade-cli-demo/trader/src/wallet/musig2.rs b/taptrade-cli-demo/trader/src/wallet/musig2.rs index 7d34e58..48183ac 100644 --- a/taptrade-cli-demo/trader/src/wallet/musig2.rs +++ b/taptrade-cli-demo/trader/src/wallet/musig2.rs @@ -1,4 +1,3 @@ use musig2::{AggNonce, FirstRound, PartialSignature, PubNonce, SecNonceSpices, SecondRound}; // https://docs.rs/musig2/latest/musig2/ - diff --git a/taptrade-cli-demo/trader/src/wallet/wallet_utils.rs b/taptrade-cli-demo/trader/src/wallet/wallet_utils.rs index 268ee70..4ac1edf 100644 --- a/taptrade-cli-demo/trader/src/wallet/wallet_utils.rs +++ b/taptrade-cli-demo/trader/src/wallet/wallet_utils.rs @@ -1,4 +1,4 @@ -use rand_core::{RngCore, OsRng}; +use rand_core::{OsRng, RngCore}; // uses operating system rng which is secure for cryptography pub fn get_seed() -> [u8; 32] { diff --git a/taptrade-cli-demo/trader/taker.env b/taptrade-cli-demo/trader/taker.env new file mode 100644 index 0000000..318761a --- /dev/null +++ b/taptrade-cli-demo/trader/taker.env @@ -0,0 +1,7 @@ +ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server +COORDINATOR_ENDPOINT="http://127.0.0.1:3000" +ROBOHASH_BASE91="n!DyWmzXG3"1"*$S(GHG4RB2gvM" # "base91 of hex of sha256 of robot22" +TRADE_TYPE="sell" +PAYOUT_ADDRESS="tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" +BOND_RATIO=5 +XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv