mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-12-20 03:55:32 +00:00
taker
This commit is contained in:
1
taptrade-cli-demo/.rustfmt.toml
Normal file
1
taptrade-cli-demo/.rustfmt.toml
Normal file
@ -0,0 +1 @@
|
||||
hard_tabs = true
|
||||
@ -1,12 +1,8 @@
|
||||
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,
|
||||
@ -56,10 +52,8 @@ pub async fn webserver() {
|
||||
// .unwrap();
|
||||
let tcp = TcpListener::bind(&addr).await.unwrap();
|
||||
axum::serve(tcp, app).await.unwrap();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// // use axum
|
||||
|
||||
// #[get("/")]
|
||||
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -30,7 +30,7 @@ pub struct TraderSettings {
|
||||
pub enum CliSettings {
|
||||
Coordinator(Coordinator),
|
||||
Taker(TraderSettings),
|
||||
Maker(TraderSettings)
|
||||
Maker(TraderSettings),
|
||||
}
|
||||
|
||||
fn hash256(input: &String) -> [u8; 32] {
|
||||
@ -73,8 +73,16 @@ impl CliSettings {
|
||||
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()),
|
||||
"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"),
|
||||
}
|
||||
}
|
||||
@ -82,11 +90,17 @@ impl CliSettings {
|
||||
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 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 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: ")))?;
|
||||
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,
|
||||
@ -94,7 +108,7 @@ impl CliSettings {
|
||||
trade_type,
|
||||
payout_address,
|
||||
bond_ratio,
|
||||
wallet_xprv
|
||||
wallet_xprv,
|
||||
})
|
||||
}
|
||||
|
||||
@ -135,7 +149,6 @@ impl CliSettings {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// old cli parser using clap
|
||||
|
||||
// use clap::{command, Arg, Command, ArgMatches};
|
||||
|
||||
@ -11,7 +11,7 @@ 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 bond_ratio: u8, // [2, 50]
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
||||
@ -3,8 +3,8 @@ 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 {
|
||||
@ -13,7 +13,7 @@ impl OfferCreationResponse {
|
||||
OfferType::Buy(val) => {
|
||||
amount = *val;
|
||||
"buy"
|
||||
},
|
||||
}
|
||||
OfferType::Sell(val) => {
|
||||
amount = *val;
|
||||
"sell"
|
||||
@ -30,11 +30,14 @@ impl OfferCreationResponse {
|
||||
|
||||
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"))
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}{}",
|
||||
trader_setup.coordinator_endpoint, "/create-offer"
|
||||
))
|
||||
.json(&Self::_format_request(trader_setup))
|
||||
.send()?
|
||||
.json::<OfferCreationResponse>()?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,15 +6,14 @@ 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)?;
|
||||
// trading::run_taker(taker_data)?;
|
||||
} else {
|
||||
Err(anyhow!("Wrong mode selected!"))
|
||||
}
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
// 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
|
||||
@ -28,6 +26,11 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
|
||||
@ -1,39 +1,47 @@
|
||||
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<MemoryDatabase>,
|
||||
pub fn assemble(
|
||||
wallet: &Wallet<MemoryDatabase>,
|
||||
bond_target: &OfferCreationResponse,
|
||||
trader_input: &TraderSettings) -> Result<PartiallySignedTransaction> {
|
||||
trader_input: &TraderSettings,
|
||||
) -> Result<PartiallySignedTransaction> {
|
||||
// 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)?;
|
||||
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)
|
||||
|
||||
@ -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<ExtendedPrivKey>,
|
||||
pub change_descriptor: Option<Bip86<ExtendedPrivKey>>
|
||||
pub change_descriptor: Option<Bip86<ExtendedPrivKey>>,
|
||||
}
|
||||
|
||||
pub fn get_wallet_xprv(xprv_input: Option<String>) -> Result<ExtendedPrivKey> {
|
||||
@ -32,10 +37,16 @@ pub fn get_wallet_xprv(xprv_input: Option<String>) -> Result<ExtendedPrivKey> {
|
||||
Ok(xprv)
|
||||
}
|
||||
|
||||
pub fn load_wallet(trader_config: &TraderSettings, blockchain: &ElectrumBlockchain) -> Result<Wallet<MemoryDatabase>> {
|
||||
pub fn load_wallet(
|
||||
trader_config: &TraderSettings,
|
||||
blockchain: &ElectrumBlockchain,
|
||||
) -> Result<Wallet<MemoryDatabase>> {
|
||||
let wallet = Wallet::new(
|
||||
Bip86(trader_config.wallet_xprv.clone(), KeychainKind::External),
|
||||
Some(Bip86(trader_config.wallet_xprv.clone(), KeychainKind::Internal)),
|
||||
Some(Bip86(
|
||||
trader_config.wallet_xprv.clone(),
|
||||
KeychainKind::Internal,
|
||||
)),
|
||||
bitcoin::Network::Testnet,
|
||||
MemoryDatabase::default(), // non-permanent storage
|
||||
)?;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use musig2::{AggNonce, FirstRound, PartialSignature, PubNonce, SecNonceSpices, SecondRound};
|
||||
|
||||
// https://docs.rs/musig2/latest/musig2/
|
||||
|
||||
|
||||
@ -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] {
|
||||
|
||||
7
taptrade-cli-demo/trader/taker.env
Normal file
7
taptrade-cli-demo/trader/taker.env
Normal file
@ -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(GH<r+!G/!!u^aFTe%!x(iee}R[+vv+S4t^a<mAk!I{ybCQiD2%Idj>G4RB2gvM" # "base91 of hex of sha256 of robot22"
|
||||
TRADE_TYPE="sell"
|
||||
PAYOUT_ADDRESS="tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm"
|
||||
BOND_RATIO=5
|
||||
XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv
|
||||
Reference in New Issue
Block a user