mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-07-28 05:33:19 +00:00
begin with coordinator
This commit is contained in:
1
taptrade-cli-demo/coordinator/.env
Normal file
1
taptrade-cli-demo/coordinator/.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
ELECTRUM_BACKEND=127.0.0.1:50001
|
14
taptrade-cli-demo/coordinator/Cargo.lock
generated
14
taptrade-cli-demo/coordinator/Cargo.lock
generated
@ -66,6 +66,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.80"
|
version = "0.1.80"
|
||||||
@ -380,9 +386,11 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
|||||||
name = "coordinator"
|
name = "coordinator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"bdk",
|
"bdk",
|
||||||
"clap",
|
"clap",
|
||||||
|
"dotenv",
|
||||||
"frost-secp256k1",
|
"frost-secp256k1",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@ -514,6 +522,12 @@ dependencies = [
|
|||||||
"litrs",
|
"litrs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenv"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecdsa"
|
name = "ecdsa"
|
||||||
version = "0.16.9"
|
version = "0.16.9"
|
||||||
|
@ -4,10 +4,17 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.86"
|
||||||
axum = "0.7.5"
|
axum = "0.7.5"
|
||||||
bdk = "0.29.0"
|
bdk = "0.29.0"
|
||||||
clap = { version = "4.5.4", features = ["derive", "cargo"] }
|
clap = { version = "4.5.4", features = ["derive", "cargo"] }
|
||||||
|
dotenv = "0.15.0"
|
||||||
frost-secp256k1 = "1.0.0"
|
frost-secp256k1 = "1.0.0"
|
||||||
reqwest = { version = "0.12.4", features = ["blocking", "json"] }
|
reqwest = { version = "0.12.4", features = ["blocking", "json"] }
|
||||||
serde = "1.0.203"
|
serde = "1.0.203"
|
||||||
tokio = "1.38.0"
|
tokio = "1.38.0"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
opt-level = 3
|
||||||
|
strip = true
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
use clap::{command, Arg, Command, ArgMatches};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Coordinator;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TraderSettings {
|
|
||||||
pub electrum_endpoint: String,
|
|
||||||
pub coordinator_endpoint: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum CliSettings {
|
|
||||||
Coordinator(Coordinator),
|
|
||||||
Taker(TraderSettings),
|
|
||||||
Maker(TraderSettings)
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
@ -1,17 +1,14 @@
|
|||||||
pub mod api;
|
mod api;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use api::{BondRequirementResponse, BondSubmissionRequest, OrderActivatedResponse, OrderRequest};
|
||||||
|
use axum::{routing::post, Json, Router};
|
||||||
|
|
||||||
use reqwest::StatusCode;
|
|
||||||
use axum::{routing::post, Json, Router, response::{IntoResponse, Response}, };
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use api::{
|
|
||||||
BondRequirementResponse, BondSubmissionRequest, OrderActivatedResponse, OrderRequest,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Handler function to process the received data
|
// Handler function to process the received data
|
||||||
async fn receive_order(Json(order): Json<OrderRequest>)-> Json<BondRequirementResponse> {
|
async fn receive_order(Json(order): Json<OrderRequest>) -> Json<BondRequirementResponse> {
|
||||||
// Print the received data to the console
|
// Print the received data to the console
|
||||||
println!("Received order: {:?}", order);
|
println!("Received order: {:?}", order);
|
||||||
|
|
||||||
@ -23,61 +20,41 @@ async fn receive_order(Json(order): Json<OrderRequest>)-> Json<BondRequirementRe
|
|||||||
// let offer_duration= order.offer_duration_ts;
|
// let offer_duration= order.offer_duration_ts;
|
||||||
|
|
||||||
// Create a response struct
|
// Create a response struct
|
||||||
let response = BondRequirementResponse {
|
let response = BondRequirementResponse {
|
||||||
bond_address: "Order received successfully".to_string(),
|
bond_address: "Order received successfully".to_string(),
|
||||||
// Add any other fields you want to include in your response
|
// Add any other fields you want to include in your response
|
||||||
locking_amount_sat: (amount * bond_ratio as u64 / 100),
|
locking_amount_sat: (amount * bond_ratio as u64 / 100),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return the response as JSON
|
|
||||||
Json(response)
|
|
||||||
|
|
||||||
|
// Return the response as JSON
|
||||||
|
Json(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn submit_maker_bond(
|
async fn submit_maker_bond(
|
||||||
Json(payload): Json<BondSubmissionRequest>,
|
Json(payload): Json<BondSubmissionRequest>,
|
||||||
) -> Json<OrderActivatedResponse> {
|
) -> Json<OrderActivatedResponse> {
|
||||||
// Process the payload
|
// Process the payload
|
||||||
// For now, we'll just return a dummy success response
|
// For now, we'll just return a dummy success response
|
||||||
let response = OrderActivatedResponse {
|
let response = OrderActivatedResponse {
|
||||||
bond_locked_until_timestamp: 0 as u128,
|
bond_locked_until_timestamp: 0 as u128,
|
||||||
order_id_hex: "Bond submitted successfully".to_string(),
|
order_id_hex: "Bond submitted successfully".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the JSON response
|
// Create the JSON response
|
||||||
Json(response)
|
Json(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
pub async fn api_server() -> Result<()> {
|
||||||
pub async fn webserver() {
|
|
||||||
// Build our application with a single route
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/create-offer", post(receive_order))
|
.route("/create-offer", post(receive_order))
|
||||||
.route("/submit-maker-bond", post(submit_maker_bond));
|
.route("/submit-maker-bond", post(submit_maker_bond));
|
||||||
|
// add other routes here
|
||||||
|
|
||||||
// Run the server on localhost:3000
|
// Run the server on localhost:9999
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 9999));
|
||||||
println!("Listening on {}", addr);
|
println!("Listening on {}", addr);
|
||||||
// axum::Server::bind(&addr)
|
|
||||||
// .serve(app.into_make_service())
|
|
||||||
// .await
|
|
||||||
// .unwrap();
|
|
||||||
let tcp = TcpListener::bind(&addr).await.unwrap();
|
let tcp = TcpListener::bind(&addr).await.unwrap();
|
||||||
axum::serve(tcp, app).await.unwrap();
|
axum::serve(tcp, app).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// // use axum
|
|
||||||
|
|
||||||
// #[get("/")]
|
|
||||||
// fn index() -> &'static str {
|
|
||||||
// "Hello, world!"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[launch]
|
|
||||||
// pub fn webserver() -> Rocket<build> {
|
|
||||||
// rocket::build().mount("/", routes![index])
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // serde to parse json
|
|
||||||
// // https://www.youtube.com/watch?v=md-ecvXBGzI BDK + Webserver video
|
|
||||||
// // https://github.com/tokio-rs/axum
|
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
mod coordinator;
|
|
||||||
mod cli;
|
|
||||||
mod communication;
|
mod communication;
|
||||||
|
mod coordinator;
|
||||||
|
|
||||||
use cli::parse_cli_args;
|
use anyhow::{anyhow, Result};
|
||||||
use communication::webserver;
|
use communication::api_server;
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
fn main() {
|
// populate .env with values before starting
|
||||||
webserver();
|
#[tokio::main]
|
||||||
let mode = parse_cli_args();
|
async fn main() -> Result<()> {
|
||||||
dbg!(mode);
|
dotenv().ok();
|
||||||
|
|
||||||
|
api_server().await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// test with cargo run -- trader --maker --endpoint "taptrade-coordinator.com:5432" --electrum "electrum-server.com:50002"
|
|
||||||
|
@ -15,3 +15,8 @@ rand_core = "0.6.4"
|
|||||||
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"
|
sha2 = "0.10.8"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
|
strip = true
|
||||||
|
@ -100,3 +100,8 @@ pub struct TradeObligationsSatisfied {
|
|||||||
pub robohash_hex: String,
|
pub robohash_hex: String,
|
||||||
pub offer_id_hex: String,
|
pub offer_id_hex: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct PayoutPsbtResponse {
|
||||||
|
pub payout_psbt_hex: String,
|
||||||
|
}
|
||||||
|
@ -8,8 +8,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use api::{
|
use api::{
|
||||||
BondRequirementResponse, BondSubmissionRequest, OfferTakenRequest, OfferTakenResponse,
|
BondRequirementResponse, BondSubmissionRequest, IsOfferReadyRequest, OfferTakenRequest,
|
||||||
OrderActivatedResponse, OrderRequest, PsbtSubmissionRequest, TradeObligationsSatisfied,
|
OfferTakenResponse, OrderActivatedResponse, OrderRequest, PayoutPsbtResponse,
|
||||||
|
PsbtSubmissionRequest, TradeObligationsSatisfied,
|
||||||
};
|
};
|
||||||
use bdk::bitcoin::consensus::encode::serialize_hex;
|
use bdk::bitcoin::consensus::encode::serialize_hex;
|
||||||
use bdk::{
|
use bdk::{
|
||||||
@ -17,7 +18,7 @@ use bdk::{
|
|||||||
wallet::AddressInfo,
|
wallet::AddressInfo,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{thread::sleep, time::Duration};
|
use std::{str::FromStr, thread::sleep, time::Duration};
|
||||||
|
|
||||||
impl BondRequirementResponse {
|
impl BondRequirementResponse {
|
||||||
fn _format_request(trader_setup: &TraderSettings) -> OrderRequest {
|
fn _format_request(trader_setup: &TraderSettings) -> OrderRequest {
|
||||||
@ -183,3 +184,77 @@ impl TradeObligationsSatisfied {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IsOfferReadyRequest {
|
||||||
|
pub fn poll(taker_config: &TraderSettings, offer: &ActiveOffer) -> Result<()> {
|
||||||
|
let request = IsOfferReadyRequest {
|
||||||
|
robohash_hex: taker_config.robosats_robohash_hex.clone(),
|
||||||
|
offer_id_hex: offer.offer_id_hex.clone(),
|
||||||
|
};
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
loop {
|
||||||
|
let res = client
|
||||||
|
.post(format!(
|
||||||
|
"{}{}",
|
||||||
|
taker_config.coordinator_endpoint, "/poll-offer-status"
|
||||||
|
))
|
||||||
|
.json(&request)
|
||||||
|
.send()?;
|
||||||
|
if res.status() == 200 {
|
||||||
|
return Ok(());
|
||||||
|
} else if res.status() != 204 {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Requesting offer status when waiting on other party failed: {}",
|
||||||
|
res.status()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Sleep for 10 sec and poll again
|
||||||
|
sleep(Duration::from_secs(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll_payout(
|
||||||
|
trader_config: &TraderSettings,
|
||||||
|
offer: &ActiveOffer,
|
||||||
|
) -> Result<Option<PartiallySignedTransaction>> {
|
||||||
|
let request = IsOfferReadyRequest {
|
||||||
|
robohash_hex: trader_config.robosats_robohash_hex.clone(),
|
||||||
|
offer_id_hex: offer.offer_id_hex.clone(),
|
||||||
|
};
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let mut res: reqwest::blocking::Response;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Sleep for 10 sec and poll
|
||||||
|
sleep(Duration::from_secs(10));
|
||||||
|
|
||||||
|
res = client
|
||||||
|
.post(format!(
|
||||||
|
"{}{}",
|
||||||
|
trader_config.coordinator_endpoint, "/poll-final-payout"
|
||||||
|
))
|
||||||
|
.json(&request)
|
||||||
|
.send()?;
|
||||||
|
if res.status() == 200 {
|
||||||
|
// good case, psbt is returned
|
||||||
|
break;
|
||||||
|
} else if res.status() == 204 {
|
||||||
|
// still waiting, retry
|
||||||
|
continue;
|
||||||
|
} else if res.status() == 201 {
|
||||||
|
// other party initiated escrow
|
||||||
|
return Ok(None);
|
||||||
|
} else {
|
||||||
|
// unintended response
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Requesting final payout when waiting on other party failed: {}",
|
||||||
|
res.status()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let final_psbt = PartiallySignedTransaction::from_str(
|
||||||
|
&res.json::<PayoutPsbtResponse>()?.payout_psbt_hex,
|
||||||
|
)?;
|
||||||
|
Ok(Some(final_psbt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -83,32 +83,3 @@ impl OfferPsbtRequest {
|
|||||||
Ok(psbt)
|
Ok(psbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsOfferReadyRequest {
|
|
||||||
pub fn poll(taker_config: &TraderSettings, offer: &ActiveOffer) -> Result<()> {
|
|
||||||
let request = IsOfferReadyRequest {
|
|
||||||
robohash_hex: taker_config.robosats_robohash_hex.clone(),
|
|
||||||
offer_id_hex: offer.offer_id_hex.clone(),
|
|
||||||
};
|
|
||||||
let client = reqwest::blocking::Client::new();
|
|
||||||
loop {
|
|
||||||
let res = client
|
|
||||||
.post(format!(
|
|
||||||
"{}{}",
|
|
||||||
taker_config.coordinator_endpoint, "/poll-offer-status-taker"
|
|
||||||
))
|
|
||||||
.json(&request)
|
|
||||||
.send()?;
|
|
||||||
if res.status() == 200 {
|
|
||||||
return Ok(());
|
|
||||||
} else if res.status() != 201 {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Requesting offer status when waiting on maker to sign psbt failed: {}",
|
|
||||||
res.status()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Sleep for 10 sec and poll again
|
|
||||||
sleep(Duration::from_secs(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -45,9 +45,10 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> {
|
|||||||
// wait for confirmation
|
// wait for confirmation
|
||||||
offer.wait_on_trade_ready_confirmation(maker_config)?;
|
offer.wait_on_trade_ready_confirmation(maker_config)?;
|
||||||
if offer.fiat_confirmation_cli_input(maker_config)? {
|
if offer.fiat_confirmation_cli_input(maker_config)? {
|
||||||
|
// this represents the "confirm payment" / "confirm fiat recieved" button
|
||||||
TradeObligationsSatisfied::submit(&offer.offer_id_hex, maker_config)?;
|
TradeObligationsSatisfied::submit(&offer.offer_id_hex, maker_config)?;
|
||||||
println!("Waiting for other party to confirm the trade.");
|
println!("Waiting for other party to confirm the trade.");
|
||||||
// poll for other party
|
let payout_keyspend_psbt = IsOfferReadyRequest::poll_payout(maker_config, &offer)?;
|
||||||
} else {
|
} else {
|
||||||
println!("Trade failed.");
|
println!("Trade failed.");
|
||||||
panic!("Escrow to be implemented!");
|
panic!("Escrow to be implemented!");
|
||||||
@ -71,9 +72,12 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> {
|
|||||||
accepted_offer.wait_on_trade_ready_confirmation(taker_config)?;
|
accepted_offer.wait_on_trade_ready_confirmation(taker_config)?;
|
||||||
|
|
||||||
if accepted_offer.fiat_confirmation_cli_input(taker_config)? {
|
if accepted_offer.fiat_confirmation_cli_input(taker_config)? {
|
||||||
|
// this represents the "confirm payment" / "confirm fiat recieved" button
|
||||||
TradeObligationsSatisfied::submit(&accepted_offer.offer_id_hex, taker_config)?;
|
TradeObligationsSatisfied::submit(&accepted_offer.offer_id_hex, taker_config)?;
|
||||||
println!("Waiting for other party to confirm the trade.");
|
println!("Waiting for other party to confirm the trade.");
|
||||||
// poll for other party
|
// pull for other parties confirmation, then receive the transaction to create MuSig signature for (keyspend) to payout address
|
||||||
|
let payout_keyspend_psbt = IsOfferReadyRequest::poll_payout(taker_config, &accepted_offer)?;
|
||||||
|
// here we need to handle if the other party is not cooperating
|
||||||
} else {
|
} else {
|
||||||
println!("Trade failed.");
|
println!("Trade failed.");
|
||||||
panic!("Escrow to be implemented!");
|
panic!("Escrow to be implemented!");
|
||||||
|
@ -1 +1,2 @@
|
|||||||
tbd
|
Thinks to improve when implementing the production ready library:
|
||||||
|
* make api more generic (smaller)
|
||||||
|
Reference in New Issue
Block a user