Skip to content

Commit 5bfaedd

Browse files
committed
Separated on different files. Completed split native command
1 parent f68d947 commit 5bfaedd

File tree

7 files changed

+256
-133
lines changed

7 files changed

+256
-133
lines changed

crates/cli-client/src/cli/basic.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use crate::cli::{BasicCommand, Cli};
2+
use crate::config::Config;
3+
use crate::error::Error;
4+
5+
use simplicityhl::elements::pset::serialize::Serialize;
6+
use simplicityhl::simplicity::hex::DisplayHex;
7+
8+
use simplicityhl_core::{LIQUID_TESTNET_GENESIS, finalize_p2pk_transaction};
9+
10+
impl Cli {
11+
pub(crate) async fn run_basic(&self, config: Config, command: &BasicCommand) -> Result<(), Error> {
12+
match command {
13+
BasicCommand::SplitNative { parts, fee, broadcast } => {
14+
let wallet = self.get_wallet(&config).await?;
15+
16+
let native_asset = simplicityhl_core::LIQUID_TESTNET_BITCOIN_ASSET;
17+
let filter = coin_store::Filter::new()
18+
.asset_id(native_asset)
19+
.script_pubkey(wallet.signer().p2pk_address(config.address_params())?.script_pubkey());
20+
21+
let results = wallet.store().query(&[filter]).await?;
22+
23+
let entry = results
24+
.into_iter()
25+
.next()
26+
.and_then(|r| match r {
27+
coin_store::QueryResult::Found(entries) => entries.into_iter().next(),
28+
coin_store::QueryResult::InsufficientValue(_) | coin_store::QueryResult::Empty => None,
29+
})
30+
.ok_or_else(|| Error::Config("No native UTXO found".to_string()))?;
31+
32+
let outpoint = entry.outpoint();
33+
let txout = entry.txout().clone();
34+
35+
let pst = contracts::sdk::split_native_any((*outpoint, txout.clone()), *parts, *fee)?;
36+
37+
let tx = pst.extract_tx()?;
38+
let utxos = &[txout];
39+
40+
let signature =
41+
wallet
42+
.signer()
43+
.sign_p2pk(&tx, utxos, 0, config.address_params(), *LIQUID_TESTNET_GENESIS)?;
44+
45+
let tx = finalize_p2pk_transaction(
46+
tx,
47+
utxos,
48+
&wallet.signer().public_key(),
49+
&signature,
50+
0,
51+
config.address_params(),
52+
*LIQUID_TESTNET_GENESIS,
53+
)?;
54+
55+
if *broadcast {
56+
cli_helper::explorer::broadcast_tx(&tx).await?;
57+
58+
wallet.store().mark_as_spent(*outpoint).await?;
59+
60+
let txid = tx.txid();
61+
for (vout, output) in tx.output.iter().enumerate() {
62+
if output.is_fee() {
63+
continue;
64+
}
65+
66+
#[allow(clippy::cast_possible_truncation)]
67+
let new_outpoint = simplicityhl::elements::OutPoint::new(txid, vout as u32);
68+
69+
wallet.store().insert(new_outpoint, output.clone(), None).await?;
70+
}
71+
72+
println!("Broadcasted: {txid}");
73+
} else {
74+
println!("{}", tx.serialize().to_lower_hex_string());
75+
}
76+
77+
Ok(())
78+
}
79+
BasicCommand::TransferNative { .. } => todo!(),
80+
BasicCommand::TransferAsset { .. } => todo!(),
81+
BasicCommand::IssueAsset { .. } => todo!(),
82+
BasicCommand::ReissueAsset { .. } => todo!(),
83+
}
84+
}
85+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use crate::cli::{Cli, HelperCommand};
2+
use crate::config::Config;
3+
use crate::error::Error;
4+
use crate::wallet::Wallet;
5+
6+
impl Cli {
7+
pub(crate) async fn run_helper(&self, config: Config, command: &HelperCommand) -> Result<(), Error> {
8+
match command {
9+
HelperCommand::Init => {
10+
let seed = self.parse_seed()?;
11+
let db_path = config.database_path();
12+
13+
std::fs::create_dir_all(&config.storage.data_dir)?;
14+
Wallet::create(&seed, &db_path, config.address_params()).await?;
15+
16+
println!("Wallet initialized at {}", db_path.display());
17+
Ok(())
18+
}
19+
HelperCommand::Address => {
20+
let wallet = self.get_wallet(&config).await?;
21+
22+
wallet.signer().print_details()?;
23+
24+
Ok(())
25+
}
26+
HelperCommand::Balance => {
27+
let wallet = self.get_wallet(&config).await?;
28+
29+
let filter = coin_store::Filter::new()
30+
.script_pubkey(wallet.signer().p2pk_address(config.address_params())?.script_pubkey());
31+
let results = wallet.store().query(&[filter]).await?;
32+
33+
let mut balances: std::collections::HashMap<simplicityhl::elements::AssetId, u64> =
34+
std::collections::HashMap::new();
35+
36+
if let Some(coin_store::QueryResult::Found(entries)) = results.into_iter().next() {
37+
for entry in entries {
38+
let (asset, value) = match entry {
39+
coin_store::UtxoEntry::Confidential { secrets, .. } => (secrets.asset, secrets.value),
40+
coin_store::UtxoEntry::Explicit { txout, .. } => {
41+
let asset = txout.asset.explicit().unwrap();
42+
let value = txout.value.explicit().unwrap();
43+
(asset, value)
44+
}
45+
};
46+
*balances.entry(asset).or_insert(0) += value;
47+
}
48+
}
49+
50+
if balances.is_empty() {
51+
println!("No UTXOs found");
52+
} else {
53+
for (asset, value) in &balances {
54+
println!("{asset}: {value}");
55+
}
56+
}
57+
Ok(())
58+
}
59+
HelperCommand::Utxos => {
60+
let wallet = self.get_wallet(&config).await?;
61+
62+
let filter = coin_store::Filter::new();
63+
let results = wallet.store().query(&[filter]).await?;
64+
65+
if let Some(coin_store::QueryResult::Found(entries)) = results.into_iter().next() {
66+
for entry in &entries {
67+
let outpoint = entry.outpoint();
68+
let (asset, value) = match entry {
69+
coin_store::UtxoEntry::Confidential { secrets, .. } => (secrets.asset, secrets.value),
70+
coin_store::UtxoEntry::Explicit { txout, .. } => {
71+
let asset = txout.asset.explicit().unwrap();
72+
let value = txout.value.explicit().unwrap();
73+
(asset, value)
74+
}
75+
};
76+
println!("{outpoint} | {asset} | {value}");
77+
}
78+
println!("Total: {} UTXOs", entries.len());
79+
} else {
80+
println!("No UTXOs found");
81+
}
82+
Ok(())
83+
}
84+
HelperCommand::Import { outpoint, blinding_key } => {
85+
let wallet = self.get_wallet(&config).await?;
86+
87+
let txout = cli_helper::explorer::fetch_utxo(*outpoint).await?;
88+
89+
let blinder = match blinding_key {
90+
Some(key_hex) => {
91+
let bytes: [u8; 32] = hex::decode(key_hex)
92+
.map_err(|e| Error::Config(format!("Invalid blinding key hex: {e}")))?
93+
.try_into()
94+
.map_err(|_| Error::Config("Blinding key must be 32 bytes".to_string()))?;
95+
Some(bytes)
96+
}
97+
None => None,
98+
};
99+
100+
wallet.store().insert(*outpoint, txout, blinder).await?;
101+
102+
println!("Imported {outpoint}");
103+
104+
Ok(())
105+
}
106+
}
107+
}
108+
}

crates/cli-client/src/cli/mod.rs

Lines changed: 12 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
mod basic;
12
mod commands;
3+
mod helper;
24

35
use std::path::PathBuf;
46

57
use clap::Parser;
68

79
use crate::config::{Config, default_config_path};
810
use crate::error::Error;
9-
use crate::wallet::Wallet;
1011

11-
pub use commands::{Command, HelperCommand, MakerCommand, TakerCommand};
12+
use crate::wallet::Wallet;
13+
pub use commands::{BasicCommand, Command, HelperCommand, MakerCommand, TakerCommand};
1214

1315
#[derive(Debug, Parser)]
1416
#[command(name = "simplicity-dex")]
@@ -43,11 +45,18 @@ impl Cli {
4345
.map_err(|_| Error::Config("Seed must be exactly 32 bytes (64 hex chars)".to_string()))
4446
}
4547

48+
async fn get_wallet(&self, config: &Config) -> Result<Wallet, Error> {
49+
let seed = self.parse_seed()?;
50+
let db_path = config.database_path();
51+
52+
Wallet::open(&seed, &db_path, config.address_params()).await
53+
}
54+
4655
pub async fn run(&self) -> Result<(), Error> {
4756
let config = self.load_config();
4857

4958
match &self.command {
50-
Command::Basic { command: _ } => todo!(),
59+
Command::Basic { command } => self.run_basic(config, command).await,
5160
Command::Maker { command: _ } => todo!(),
5261
Command::Taker { command: _ } => todo!(),
5362
Command::Helper { command } => self.run_helper(config, command).await,
@@ -57,113 +66,4 @@ impl Cli {
5766
}
5867
}
5968
}
60-
61-
async fn run_helper(&self, config: Config, command: &HelperCommand) -> Result<(), Error> {
62-
match command {
63-
HelperCommand::Init => {
64-
let seed = self.parse_seed()?;
65-
let db_path = config.database_path();
66-
67-
std::fs::create_dir_all(&config.storage.data_dir)?;
68-
Wallet::create(&seed, &db_path, config.address_params()).await?;
69-
70-
println!("Wallet initialized at {}", db_path.display());
71-
Ok(())
72-
}
73-
HelperCommand::Address => {
74-
let seed = self.parse_seed()?;
75-
let db_path = config.database_path();
76-
let wallet = Wallet::open(&seed, &db_path, config.address_params()).await?;
77-
78-
wallet.signer().print_details()?;
79-
80-
Ok(())
81-
}
82-
HelperCommand::Balance => {
83-
let seed = self.parse_seed()?;
84-
let db_path = config.database_path();
85-
let wallet = Wallet::open(&seed, &db_path, config.address_params()).await?;
86-
87-
let filter = coin_store::Filter::new()
88-
.script_pubkey(wallet.signer().p2pk_address(config.address_params())?.script_pubkey());
89-
let results = wallet.store().query(&[filter]).await?;
90-
91-
let mut balances: std::collections::HashMap<simplicityhl::elements::AssetId, u64> =
92-
std::collections::HashMap::new();
93-
94-
if let Some(coin_store::QueryResult::Found(entries)) = results.into_iter().next() {
95-
for entry in entries {
96-
let (asset, value) = match entry {
97-
coin_store::UtxoEntry::Confidential { secrets, .. } => (secrets.asset, secrets.value),
98-
coin_store::UtxoEntry::Explicit { txout, .. } => {
99-
let asset = txout.asset.explicit().unwrap();
100-
let value = txout.value.explicit().unwrap();
101-
(asset, value)
102-
}
103-
};
104-
*balances.entry(asset).or_insert(0) += value;
105-
}
106-
}
107-
108-
if balances.is_empty() {
109-
println!("No UTXOs found");
110-
} else {
111-
for (asset, value) in &balances {
112-
println!("{asset}: {value}");
113-
}
114-
}
115-
Ok(())
116-
}
117-
HelperCommand::Utxos => {
118-
let seed = self.parse_seed()?;
119-
let db_path = config.database_path();
120-
let wallet = Wallet::open(&seed, &db_path, config.address_params()).await?;
121-
122-
let filter = coin_store::Filter::new();
123-
let results = wallet.store().query(&[filter]).await?;
124-
125-
if let Some(coin_store::QueryResult::Found(entries)) = results.into_iter().next() {
126-
for entry in &entries {
127-
let outpoint = entry.outpoint();
128-
let (asset, value) = match entry {
129-
coin_store::UtxoEntry::Confidential { secrets, .. } => (secrets.asset, secrets.value),
130-
coin_store::UtxoEntry::Explicit { txout, .. } => {
131-
let asset = txout.asset.explicit().unwrap();
132-
let value = txout.value.explicit().unwrap();
133-
(asset, value)
134-
}
135-
};
136-
println!("{outpoint} | {asset} | {value}");
137-
}
138-
println!("Total: {} UTXOs", entries.len());
139-
} else {
140-
println!("No UTXOs found");
141-
}
142-
Ok(())
143-
}
144-
HelperCommand::Import { outpoint, blinding_key } => {
145-
let seed = self.parse_seed()?;
146-
let db_path = config.database_path();
147-
let wallet = Wallet::open(&seed, &db_path, config.address_params()).await?;
148-
149-
let txout = cli_helper::explorer::fetch_utxo(*outpoint).await?;
150-
151-
let blinder = match blinding_key {
152-
Some(key_hex) => {
153-
let bytes: [u8; 32] = hex::decode(key_hex)
154-
.map_err(|e| Error::Config(format!("Invalid blinding key hex: {e}")))?
155-
.try_into()
156-
.map_err(|_| Error::Config("Blinding key must be 32 bytes".to_string()))?;
157-
Some(bytes)
158-
}
159-
None => None,
160-
};
161-
162-
wallet.store().insert(*outpoint, txout, blinder).await?;
163-
164-
println!("Imported {outpoint}");
165-
Ok(())
166-
}
167-
}
168-
}
16969
}

crates/cli-client/src/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,16 @@ pub enum Error {
1717

1818
#[error("Explorer error: {0}")]
1919
Explorer(#[from] cli_helper::explorer::ExplorerError),
20+
21+
#[error("Contract error: {0}")]
22+
Contract(#[from] contracts::error::TransactionBuildError),
23+
24+
#[error("Program error: {0}")]
25+
Program(#[from] simplicityhl_core::ProgramError),
26+
27+
#[error("PSET error: {0}")]
28+
Pset(#[from] simplicityhl::elements::pset::Error),
29+
30+
#[error("Hex error: {0}")]
31+
Hex(#[from] hex::FromHexError),
2032
}

crates/coin-store/src/entry.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use simplicityhl::elements::{OutPoint, TxOut, TxOutSecrets};
22

3+
#[derive(Debug)]
34
pub enum UtxoEntry {
45
Confidential {
56
outpoint: OutPoint,
@@ -36,6 +37,7 @@ impl UtxoEntry {
3637
}
3738
}
3839

40+
#[derive(Debug)]
3941
pub enum QueryResult {
4042
Found(Vec<UtxoEntry>),
4143
InsufficientValue(Vec<UtxoEntry>),

0 commit comments

Comments
 (0)