Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ anyhow = { version = "1.0.100" }

tracing = { version = "0.1.41" }

contracts = { git = "https://github.com/BlockstreamResearch/simplicity-contracts.git", rev = "6a53bf7", package = "contracts" }
cli-helper = { git = "https://github.com/BlockstreamResearch/simplicity-contracts.git", rev = "6a53bf7", package = "cli" }
simplicityhl-core = { version = "0.3.0", features = ["encoding"] }
contracts = { git = "https://github.com/BlockstreamResearch/simplicity-contracts.git", rev = "b3d1ae9", package = "contracts" }
cli-helper = { git = "https://github.com/BlockstreamResearch/simplicity-contracts.git", rev = "b3d1ae9", package = "cli" }
simplicityhl-core = { version = "0.3.1", features = ["encoding"] }

simplicityhl = { version = "0.4.0" }
9 changes: 4 additions & 5 deletions crates/cli-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ categories.workspace = true

[[bin]]
name = "simplicity-dex"
path = "src/bin/main.rs"
path = "src/main.rs"

[dependencies]
signer = { path = "../signer" }
Expand All @@ -37,7 +37,6 @@ tracing = { workspace = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

serde = { version = "1", features = ["derive"] }
toml = "0.8"
hex = "0.4"
dotenvy = "0.15"

toml = { version = "0.8" }
hex = { version = "0.4" }
dotenvy = { version = "0.15" }
624 changes: 624 additions & 0 deletions crates/cli-client/src/cli/basic.rs

Large diffs are not rendered by default.

46 changes: 25 additions & 21 deletions crates/cli-client/src/cli/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::Subcommand;
use simplicityhl::elements::OutPoint;
use simplicityhl::elements::{Address, AssetId, OutPoint};

#[derive(Debug, Subcommand)]
pub enum Command {
Expand Down Expand Up @@ -75,7 +75,7 @@ pub enum HelperCommand {
/// Show wallet balance
Balance,

/// List UTXOs
/// List all UTXOs stored in wallet
Utxos,

/// Import a UTXO into the wallet
Expand All @@ -88,16 +88,26 @@ pub enum HelperCommand {
#[arg(long, short = 'b')]
blinding_key: Option<String>,
},

/// Mark a specific output as spent
Spend {
/// Outpoint to mark as spent (txid:vout)
#[arg(long, short = 'o')]
outpoint: OutPoint,
},
}

#[derive(Debug, Subcommand)]
pub enum BasicCommand {
/// Transfer LBTC to a recipient
TransferNative {
/// Transfer an asset to a recipient
Transfer {
/// Asset ID (defaults to native LBTC if not specified)
#[arg(long)]
asset_id: Option<AssetId>,
/// Recipient address
#[arg(long)]
to: String,
/// Amount to send in satoshis
to: Address,
/// Amount to send
#[arg(long)]
amount: u64,
/// Fee amount in satoshis
Expand All @@ -121,17 +131,14 @@ pub enum BasicCommand {
broadcast: bool,
},

/// Transfer an asset to a recipient
TransferAsset {
/// Asset ID (hex)
#[arg(long)]
asset: String,
/// Recipient address
/// Merge multiple UTXOs of the same asset into one
Merge {
/// Asset ID to merge (defaults to native LBTC if not specified)
#[arg(long)]
to: String,
/// Amount to send
asset_id: Option<AssetId>,
/// Number of UTXOs to merge
#[arg(long)]
amount: u64,
count: usize,
/// Fee amount in satoshis
#[arg(long)]
fee: u64,
Expand All @@ -142,9 +149,6 @@ pub enum BasicCommand {

/// Issue a new asset
IssueAsset {
/// Asset name (local reference)
#[arg(long)]
name: String,
/// Amount to issue
#[arg(long)]
amount: u64,
Expand All @@ -156,11 +160,11 @@ pub enum BasicCommand {
broadcast: bool,
},

/// Reissue an existing asset
/// Reissue an existing asset using reissuance token
ReissueAsset {
/// Asset name (local reference)
/// Asset ID to reissue
#[arg(long)]
name: String,
asset_id: AssetId,
/// Amount to reissue
#[arg(long)]
amount: u64,
Expand Down
32 changes: 32 additions & 0 deletions crates/cli-client/src/cli/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::error::Error;
use simplicityhl::elements::Transaction;
use simplicityhl::elements::pset::serialize::Serialize;
use simplicityhl::simplicity::hex::DisplayHex;

#[derive(Debug, Clone, Copy)]
pub enum Broadcaster {
Offline,
Online,
}

impl From<bool> for Broadcaster {
fn from(b: bool) -> Self {
if b { Broadcaster::Online } else { Broadcaster::Offline }
}
}

impl Broadcaster {
pub async fn broadcast_tx(&self, tx: &Transaction) -> Result<(), Error> {
match self {
Broadcaster::Offline => {
println!("{}", tx.serialize().to_lower_hex_string());
}
Broadcaster::Online => {
cli_helper::explorer::broadcast_tx(tx).await?;
let txid = tx.txid();
println!("Broadcasted: {txid}");
}
}
Ok(())
}
}
119 changes: 119 additions & 0 deletions crates/cli-client/src/cli/helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::cli::{Cli, HelperCommand};
use crate::config::Config;
use crate::error::Error;
use crate::wallet::Wallet;

use coin_store::UtxoStore;
use simplicityhl::elements::bitcoin::secp256k1;

impl Cli {
pub(crate) async fn run_helper(&self, config: Config, command: &HelperCommand) -> Result<(), Error> {
match command {
HelperCommand::Init => {
let seed = self.parse_seed()?;
let db_path = config.database_path();

std::fs::create_dir_all(&config.storage.data_dir)?;
Wallet::create(&seed, &db_path, config.address_params()).await?;

println!("Wallet initialized at {}", db_path.display());
Ok(())
}
HelperCommand::Address => {
let wallet = self.get_wallet(&config).await?;

wallet.signer().print_details()?;

Ok(())
}
HelperCommand::Balance => {
let wallet = self.get_wallet(&config).await?;

let filter = coin_store::UtxoFilter::new()
.script_pubkey(wallet.signer().p2pk_address(config.address_params())?.script_pubkey());
let results = <_ as UtxoStore>::query_utxos(wallet.store(), &[filter]).await?;

let mut balances: std::collections::HashMap<simplicityhl::elements::AssetId, u64> =
std::collections::HashMap::new();

if let Some(coin_store::UtxoQueryResult::Found(entries, _)) = results.into_iter().next() {
for entry in entries {
let (Some(asset), Some(value)) = (entry.asset(), entry.value()) else {
continue;
};

*balances.entry(asset).or_insert(0) += value;
}
}

if balances.is_empty() {
println!("No UTXOs found");
} else {
for (asset, value) in &balances {
println!("{asset}: {value}");
}
}
Ok(())
}
HelperCommand::Utxos => {
let wallet = self.get_wallet(&config).await?;

let filter = coin_store::UtxoFilter::new();
let results = wallet.store().query_utxos(&[filter]).await?;

if let Some(coin_store::UtxoQueryResult::Found(entries, _)) = results.into_iter().next() {
for entry in &entries {
let (Some(asset), Some(value)) = (entry.asset(), entry.value()) else {
println!(
"{:<76} | {:<64} | {:<25}",
entry.outpoint(),
"Confidential",
"Confidential"
);

continue;
};

println!("{:<76} | {:<64} | {:<25}", entry.outpoint(), asset, value);
}

println!("Total: {} UTXOs", entries.len());
} else {
println!("No UTXOs found");
}
Ok(())
}
HelperCommand::Import { outpoint, blinding_key } => {
let wallet = self.get_wallet(&config).await?;

let txout = cli_helper::explorer::fetch_utxo(*outpoint).await?;

let blinder = match blinding_key {
Some(key_hex) => {
let bytes: [u8; secp256k1::constants::SECRET_KEY_SIZE] = hex::decode(key_hex)
.map_err(|e| Error::Config(format!("Invalid blinding key hex: {e}")))?
.try_into()
.map_err(|_| Error::Config("Blinding key must be 32 bytes".to_string()))?;
Some(bytes)
}
None => None,
};

wallet.store().insert(*outpoint, txout, blinder).await?;

println!("Imported {outpoint}");

Ok(())
}
HelperCommand::Spend { outpoint } => {
let wallet = self.get_wallet(&config).await?;

wallet.store().mark_as_spent(*outpoint).await?;

println!("Marked {outpoint} as spent");

Ok(())
}
}
}
}
17 changes: 17 additions & 0 deletions crates/cli-client/src/cli/maker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use crate::cli::{Cli, MakerCommand};
use crate::config::Config;
use crate::error::Error;

impl Cli {
pub(crate) async fn run_maker(&self, config: Config, command: &MakerCommand) -> Result<(), Error> {
match command {
MakerCommand::Create => {}
MakerCommand::Fund => {}
MakerCommand::Exercise => {}
MakerCommand::Cancel => {}
MakerCommand::List => {}
}

Ok(())
}
}
Loading