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
197 changes: 115 additions & 82 deletions crates/cli-client/src/cli/swap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::cli::interactive::{
SWAP_COLLATERAL_TAG, current_timestamp, extract_entries_from_result, format_relative_time,
SWAP_COLLATERAL_TAG, current_timestamp, extract_entries_from_result, format_relative_time, format_settlement_asset,
get_grantor_tokens_from_wallet, parse_expiry, prompt_amount, select_enriched_token_interactive,
truncate_with_ellipsis,
};
use crate::cli::tables::{
display_active_swaps_table, display_cancellable_swaps_table, display_withdrawable_swaps_table,
};
use crate::cli::{Cli, SwapCommand};
use crate::config::Config;
Expand All @@ -22,6 +26,51 @@ use simplicityhl::elements::pset::serialize::Serialize;
use simplicityhl::simplicity::hex::DisplayHex;
use simplicityhl_core::{LIQUID_TESTNET_BITCOIN_ASSET, LIQUID_TESTNET_GENESIS};

pub struct LocalSwapData {
pub(crate) swap_args: SwapWithChangeArguments,
pub(crate) taproot_pubkey_gen: contracts::sdk::taproot_pubkey_gen::TaprootPubkeyGen,
pub(crate) metadata: ContractMetadata,
pub(crate) current_outpoint: simplicityhl::elements::OutPoint,
pub(crate) current_value: u64,
}

pub struct LocalCancellableSwap {
pub(crate) swap_args: SwapWithChangeArguments,
pub(crate) taproot_pubkey_gen: contracts::sdk::taproot_pubkey_gen::TaprootPubkeyGen,
pub(crate) metadata: ContractMetadata,
}

pub struct LocalWithdrawableSwap {
pub(crate) swap_args: SwapWithChangeArguments,
pub(crate) taproot_pubkey_gen: contracts::sdk::taproot_pubkey_gen::TaprootPubkeyGen,
pub(crate) metadata: ContractMetadata,
pub(crate) settlement_amount: u64,
}

pub struct ActiveSwapDisplay {
pub(crate) index: usize,
pub(crate) offering: String,
pub(crate) price: String,
pub(crate) wants: String,
pub(crate) expires: String,
pub(crate) seller: String,
}

pub struct CancellableSwapDisplay {
pub(crate) index: usize,
pub(crate) collateral: String,
pub(crate) asset: String,
pub(crate) expired: String,
pub(crate) contract: String,
}

pub struct WithdrawableSwapDisplay {
pub(crate) index: usize,
pub(crate) settlement: String,
pub(crate) asset: String,
pub(crate) contract: String,
}

impl Cli {
#[allow(clippy::too_many_lines)]
pub(crate) async fn run_swap(&self, config: Config, command: &SwapCommand) -> Result<(), Error> {
Expand Down Expand Up @@ -197,14 +246,6 @@ impl Cli {
fee,
broadcast,
} => {
struct LocalSwapData {
swap_args: SwapWithChangeArguments,
taproot_pubkey_gen: contracts::sdk::taproot_pubkey_gen::TaprootPubkeyGen,
metadata: ContractMetadata,
current_outpoint: simplicityhl::elements::OutPoint,
current_value: u64,
}

println!("Taking swap offer...");

let swap_contracts =
Expand Down Expand Up @@ -280,24 +321,8 @@ impl Cli {
));
}

println!(
" {:<3} | {:<12} | {:<10} | {:<14} | {:<15} | Seller",
"#", "Offering", "Price", "Wants", "Expires"
);
println!("{}", "-".repeat(90));
for (idx, swap) in active_swaps.iter().enumerate() {
let seller = swap.metadata.nostr_author.as_deref().unwrap_or("unknown");
let price = swap.swap_args.collateral_per_contract();
println!(
" {:<3} | {:<12} | {:<10} | {:<14} | {:<15} | {}",
idx + 1,
swap.current_value,
price,
crate::cli::interactive::format_settlement_asset(&swap.swap_args.get_settlement_asset_id()),
format_relative_time(i64::from(swap.swap_args.expiry_time())),
crate::cli::interactive::truncate_with_ellipsis(seller, 12)
);
}
let active_swap_displays = build_active_swaps_displays(&active_swaps);
display_active_swaps_table(&active_swap_displays);
println!();

let selection =
Expand Down Expand Up @@ -500,12 +525,6 @@ impl Cli {
fee,
broadcast,
} => {
struct LocalCancellableSwap {
swap_args: SwapWithChangeArguments,
taproot_pubkey_gen: contracts::sdk::taproot_pubkey_gen::TaprootPubkeyGen,
metadata: ContractMetadata,
}

println!("Cancelling swap offer (reclaiming collateral after expiry)...");

let swap_contracts =
Expand Down Expand Up @@ -578,28 +597,8 @@ impl Cli {
));
}

println!(
" {:<3} | {:<12} | {:<14} | {:<20} | Contract",
"#", "Collateral", "Asset", "Expired"
);
println!("{}", "-".repeat(80));
for (idx, cs) in cancellable_swaps.iter().enumerate() {
let asset_display =
crate::cli::interactive::format_settlement_asset(&cs.swap_args.get_collateral_asset_id());
let expiry_time = cs.swap_args.expiry_time();
let contract_short = cs.metadata.nostr_event_id.as_ref().map_or_else(
|| crate::cli::interactive::truncate_with_ellipsis(&cs.taproot_pubkey_gen.to_string(), 16),
|id| crate::cli::interactive::truncate_with_ellipsis(id, 16),
);
println!(
" {:<3} | {:<12} | {:<14} | {:<20} | {}",
idx + 1,
"available",
asset_display,
format!("expired ({})", expiry_time),
contract_short
);
}
let cancellable_swap_displays = build_cancellable_swaps_displays(&cancellable_swaps);
display_cancellable_swaps_table(&cancellable_swap_displays);
println!();

let selected = if let Some(event_id_str) = swap_event {
Expand Down Expand Up @@ -800,13 +799,6 @@ impl Cli {
fee,
broadcast,
} => {
struct LocalWithdrawableSwap {
swap_args: SwapWithChangeArguments,
taproot_pubkey_gen: contracts::sdk::taproot_pubkey_gen::TaprootPubkeyGen,
metadata: ContractMetadata,
settlement_amount: u64,
}

println!("Withdrawing settlement from swap (claiming payment after swap was taken)...");

let swap_contracts =
Expand Down Expand Up @@ -879,26 +871,8 @@ impl Cli {
));
}

println!(
" {:<3} | {:<20} | {:<14} | Contract",
"#", "Settlement Available", "Asset"
);
println!("{}", "-".repeat(70));
for (idx, ws) in withdrawable_swaps.iter().enumerate() {
let asset_display =
crate::cli::interactive::format_settlement_asset(&ws.swap_args.get_settlement_asset_id());
let contract_short = ws.metadata.nostr_event_id.as_ref().map_or_else(
|| crate::cli::interactive::truncate_with_ellipsis(&ws.taproot_pubkey_gen.to_string(), 16),
|id| crate::cli::interactive::truncate_with_ellipsis(id, 16),
);
println!(
" {:<3} | {:<20} | {:<14} | {}",
idx + 1,
ws.settlement_amount,
asset_display,
contract_short
);
}
let withdrawable_swap_displays = build_withdrawable_swaps_displays(&withdrawable_swaps);
display_withdrawable_swaps_table(&withdrawable_swap_displays);
println!();

let selected = if let Some(event_id_str) = swap_event {
Expand Down Expand Up @@ -1095,3 +1069,62 @@ impl Cli {
}
}
}

fn build_active_swaps_displays(active_swaps: &[LocalSwapData]) -> Vec<ActiveSwapDisplay> {
active_swaps
.iter()
.enumerate()
.map(|(idx, swap)| {
let seller = swap.metadata.nostr_author.as_deref().unwrap_or("unknown");
let price = swap.swap_args.collateral_per_contract();
ActiveSwapDisplay {
index: idx + 1,
offering: swap.current_value.to_string(),
price: price.to_string(),
wants: format_settlement_asset(&swap.swap_args.get_settlement_asset_id()),
expires: format_relative_time(i64::from(swap.swap_args.expiry_time())),
seller: truncate_with_ellipsis(seller, 12),
}
})
.collect()
}

fn build_cancellable_swaps_displays(cancellable_swaps: &[LocalCancellableSwap]) -> Vec<CancellableSwapDisplay> {
cancellable_swaps
.iter()
.enumerate()
.map(|(idx, cs)| {
let expiry_time = cs.swap_args.expiry_time();
let contract_short = cs.metadata.nostr_event_id.as_ref().map_or_else(
|| truncate_with_ellipsis(&cs.taproot_pubkey_gen.to_string(), 16),
|id| truncate_with_ellipsis(id, 16),
);
CancellableSwapDisplay {
index: idx + 1,
collateral: "available".to_string(),
asset: format_settlement_asset(&cs.swap_args.get_collateral_asset_id()),
expired: format!("expired ({expiry_time})"),
contract: contract_short,
}
})
.collect()
}

fn build_withdrawable_swaps_displays(withdrawable_swaps: &[LocalWithdrawableSwap]) -> Vec<WithdrawableSwapDisplay> {
withdrawable_swaps
.iter()
.enumerate()
.map(|(idx, ws)| {
let contract_short = ws.metadata.nostr_event_id.as_ref().map_or_else(
|| truncate_with_ellipsis(&ws.taproot_pubkey_gen.to_string(), 16),
|id| truncate_with_ellipsis(id, 16),
);
WithdrawableSwapDisplay {
index: idx + 1,
settlement: ws.settlement_amount.to_string(),
asset: format_settlement_asset(&ws.swap_args.get_settlement_asset_id()),
contract: contract_short,
}
})
.collect()
}
89 changes: 89 additions & 0 deletions crates/cli-client/src/cli/tables.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::cli::interactive::{SwapDisplay, TokenDisplay};
use crate::cli::positions::{CollateralDisplay, UserTokenDisplay};
use crate::cli::swap::{ActiveSwapDisplay, CancellableSwapDisplay, WithdrawableSwapDisplay};
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Attribute, Cell, Table};

Expand Down Expand Up @@ -81,6 +82,78 @@ impl TableData for UserTokenDisplay {
}
}

impl TableData for ActiveSwapDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Offering", "Price", "Wants", "Expires", "Seller"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.offering.clone(),
self.price.clone(),
self.wants.clone(),
self.expires.clone(),
self.seller.clone(),
]
}
}

impl TableData for CancellableSwapDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Collateral", "Asset", "Expired", "Contract"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.collateral.clone(),
self.asset.clone(),
self.expired.clone(),
self.contract.clone(),
]
}
}

impl TableData for WithdrawableSwapDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Settlement Available", "Asset", "Contract"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.settlement.clone(),
self.asset.clone(),
self.contract.clone(),
]
}
}

pub struct UtxoDisplay {
pub outpoint: String,
pub asset: String,
pub value: String,
}

impl TableData for UtxoDisplay {
fn get_header() -> Vec<String> {
vec!["Outpoint", "Asset", "Value"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![self.outpoint.clone(), self.asset.clone(), self.value.clone()]
}
}

fn render_table<T: TableData>(items: &[T], empty_msg: &str) {
if items.is_empty() {
println!(" ({empty_msg})");
Expand Down Expand Up @@ -121,3 +194,19 @@ pub fn display_collateral_table(displays: &[CollateralDisplay]) {
pub fn display_user_token_table(displays: &[UserTokenDisplay]) {
render_table(displays, "No option/grantor tokens found");
}

pub fn display_active_swaps_table(active_swaps: &[ActiveSwapDisplay]) {
render_table(active_swaps, "No swaps found");
}

pub fn display_cancellable_swaps_table(cancellable_swaps: &[CancellableSwapDisplay]) {
render_table(cancellable_swaps, "No cancellable swaps found");
}

pub fn display_withdrawable_swaps_table(withdrawable_swaps: &[WithdrawableSwapDisplay]) {
render_table(withdrawable_swaps, "No withdrawable swaps found");
}

pub fn display_utxo_table(utxos: &[UtxoDisplay]) {
render_table(utxos, "No UTXOs found");
}
Loading