Skip to content

Commit

Permalink
Bump Fee (#439)
Browse files Browse the repository at this point in the history
  • Loading branch information
cryptoquick committed Dec 28, 2023
1 parent 11edf38 commit 377c605
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"files.autoSave": "onFocusChange",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"rust-analyzer.cargo.features": "all", // Enable only for desktop
"rust-analyzer.check.allTargets": true,
"rust-analyzer.cargo.features": "all", // Enable only for desktop
// "rust-analyzer.cargo.target": "wasm32-unknown-unknown", // Enable only for web
// "rust-analyzer.check.noDefaultFeatures": true, // Enable for web
// "rust-analyzer.runnables.extraArgs": ["--release"], // Enable for web
Expand Down
41 changes: 30 additions & 11 deletions lib/web/bitcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,6 @@ export const sendSats = async (
await BMC.send_sats(descriptor, changeDescriptor, address, amount, feeRate)
);

export const drainWallet = async (
destination: string,
descriptor: string,
changeDescriptor?: string,
feeRate?: number
): Promise<TransactionData> =>
JSON.parse(
await BMC.drain_wallet(destination, descriptor, changeDescriptor, feeRate)
);

export const fundVault = async (
descriptor: string,
changeDescriptor: string,
Expand All @@ -92,6 +82,27 @@ export const getAssetsVault = async (
await BMC.get_assets_vault(rgbAssetsDescriptorXpub, rgbUdasDescriptorXpub)
);

export const drainWallet = async (
destination: string,
descriptor: string,
changeDescriptor?: string,
feeRate?: number
): Promise<TransactionData> =>
JSON.parse(
await BMC.drain_wallet(destination, descriptor, changeDescriptor, feeRate)
);

export const bumpFee = async (
txid: string,
feeRate: number,
broadcast: boolean,
descriptor: string,
changeDescriptor?: string,
): Promise<TransactionData> =>
JSON.parse(
await BMC.bump_fee(txid, feeRate, descriptor, changeDescriptor, broadcast)
);

// Core type interfaces based on structs defined within the bitmask-core Rust crate:
// https://github.com/diba-io/bitmask-core/blob/development/src/structs.rs

Expand Down Expand Up @@ -123,7 +134,7 @@ export interface Vault {
public: PublicWalletData;
}

export interface Transaction {
export interface Transaction extends WalletTransaction {
amount: number;
asset?: string;
assetType: string;
Expand Down Expand Up @@ -162,6 +173,12 @@ export interface TransactionDetails extends Transaction {
}

export interface TransactionData {
details: TransactionDataDetails;
vsize: number;
feeRate: number;
}

export interface TransactionDataDetails {
transaction?: Transaction;
txid: string;
received: number;
Expand All @@ -183,6 +200,8 @@ export interface WalletTransaction {
fee: number;
confirmed: boolean;
confirmationTime: ConfirmationTime;
vsize: number;
feeRate: number;
}

export interface WalletBalance {
Expand Down
4 changes: 2 additions & 2 deletions lib/web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"Francisco Calderón <[email protected]>"
],
"description": "Core functionality for the BitMask wallet",
"version": "0.7.0-beta.8",
"version": "0.7.0-beta.10",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
8 changes: 4 additions & 4 deletions lib/web/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -819,10 +819,10 @@ export interface RgbOfferRequest {
expireAt?: number;
}

export interface RgbSwapStrategy {
auction?: string,
p2p?: string,
hotswap?: string,
export enum RgbSwapStrategy {
Auction = "auction",
P2P = "p2p",
HotSwap = "hotswap",
}

export interface RgbAuctionStrategy {
Expand Down
117 changes: 102 additions & 15 deletions src/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ::psbt::Psbt;
use amplify::hex::ToHex;
use argon2::Argon2;
use bdk::{wallet::AddressIndex, FeeRate, LocalUtxo, SignOptions, TransactionDetails};
use bitcoin::{consensus::encode, psbt::PartiallySignedTransaction};
use bitcoin::{consensus::encode, psbt::PartiallySignedTransaction, Txid};
use rand::{rngs::StdRng, Rng, SeedableRng};
use serde_encrypt::{
serialize::impls::BincodeSerializer, shared_key::SharedKey, traits::SerdeEncryptSharedKey,
Expand Down Expand Up @@ -40,7 +40,7 @@ use crate::{
structs::{
DecryptedWalletData, EncryptedWalletDataV04, FundVaultDetails, PublishPsbtRequest,
PublishedPsbtResponse, SatsInvoice, SecretString, SignPsbtRequest, SignedPsbtResponse,
WalletData, WalletTransaction,
TransactionData, WalletData, WalletTransaction,
},
trace,
};
Expand Down Expand Up @@ -113,6 +113,9 @@ pub enum BitcoinError {
/// PSBT decode error
#[error(transparent)]
BitcoinPsbtDecodeError(#[from] bitcoin::consensus::encode::Error),
/// Txid parse error
#[error(transparent)]
TxidParseError(#[from] bitcoin::hashes::hex::Error),
}

/// Bitcoin Wallet Operations
Expand Down Expand Up @@ -270,7 +273,7 @@ pub async fn get_wallet_data(
let mut transactions = wallet
.lock()
.await
.list_transactions(false)
.list_transactions(true)
.unwrap_or_default();
trace!(format!("transactions: {transactions:#?}"));

Expand All @@ -283,13 +286,23 @@ pub async fn get_wallet_data(

let transactions: Vec<WalletTransaction> = transactions
.into_iter()
.map(|tx| WalletTransaction {
txid: tx.txid,
received: tx.received,
sent: tx.sent,
fee: tx.fee,
confirmed: tx.confirmation_time.is_some(),
confirmation_time: tx.confirmation_time,
.map(|tx| {
let vsize = match &tx.transaction {
Some(tx_details) => tx_details.vsize(),
None => 1,
};
let fee_rate = tx.fee.expect("tx fee exists") as f32 / vsize as f32;

WalletTransaction {
txid: tx.txid,
received: tx.received,
sent: tx.sent,
fee: tx.fee,
confirmed: tx.confirmation_time.is_some(),
confirmation_time: tx.confirmation_time,
vsize,
fee_rate,
}
})
.collect();

Expand Down Expand Up @@ -341,13 +354,13 @@ pub async fn send_sats(
destination: &str, // bip21 uri or address
amount: u64,
fee_rate: Option<f32>,
) -> Result<TransactionDetails, BitcoinError> {
) -> Result<TransactionData, BitcoinError> {
use payjoin::UriExt;

let wallet = get_wallet(descriptor, Some(change_descriptor)).await?;
let fee_rate = fee_rate.map(FeeRate::from_sat_per_vb);

let transaction = match payjoin::Uri::try_from(destination) {
let details = match payjoin::Uri::try_from(destination) {
Ok(uri) => {
let address = uri.address.clone();
validate_address(&address).await?;
Expand All @@ -370,7 +383,17 @@ pub async fn send_sats(
}
};

Ok(transaction)
let vsize = match &details.transaction {
Some(tx_details) => tx_details.vsize(),
None => 1,
};
let fee_rate = details.fee.expect("fee is present on tx") as f32 / vsize as f32;

Ok(TransactionData {
details,
vsize,
fee_rate,
})
}

pub async fn fund_vault(
Expand Down Expand Up @@ -537,7 +560,7 @@ pub async fn drain_wallet(
descriptor: &SecretString,
change_descriptor: Option<&SecretString>,
fee_rate: Option<f32>,
) -> Result<TransactionDetails, BitcoinError> {
) -> Result<TransactionData, BitcoinError> {
let address = Address::from_str(destination)?;
validate_address(&address).await?;
debug!(format!("Create drain wallet tx to: {address:#?}"));
Expand Down Expand Up @@ -593,8 +616,72 @@ pub async fn drain_wallet(
"Drain wallet transaction submitted with details: {details:#?}"
));

Ok(details)
let vsize = match &details.transaction {
Some(tx_details) => tx_details.vsize(),
None => 1,
};
let fee_rate = details.fee.expect("fee is present on tx") as f32 / vsize as f32;

Ok(TransactionData {
details,
vsize,
fee_rate,
})
} else {
Err(BitcoinError::DrainWalletNoTxDetails)
}
}

pub async fn bump_fee(
txid: String,
fee_rate: f32,
descriptor: &SecretString,
change_descriptor: Option<&SecretString>,
broadcast: bool,
) -> Result<TransactionData, BitcoinError> {
let txid = Txid::from_str(&txid)?;

let wallet = get_wallet(descriptor, change_descriptor).await?;

if broadcast {
sync_wallet(&wallet).await?;
}

let (mut psbt, details) = {
let wallet_lock = wallet.lock().await;
let mut builder = wallet_lock.build_fee_bump(txid)?;
builder.fee_rate(FeeRate::from_sat_per_vb(fee_rate));
builder.finish()?
};

let _finalized = wallet
.lock()
.await
.sign(&mut psbt, SignOptions::default())?;
let tx = psbt.extract_tx();

if broadcast {
let blockchain = get_blockchain().await;
blockchain.broadcast(&tx).await?;
}

let sent = tx.output.iter().fold(0, |sum, output| output.value + sum);

let txid = tx.txid();
let vsize = tx.vsize();

let details = TransactionDetails {
txid,
transaction: Some(tx),
received: 0,
sent,
fee: details.fee,
confirmation_time: None,
};

Ok(TransactionData {
details,
vsize,
fee_rate,
})
}
12 changes: 11 additions & 1 deletion src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct WalletData {
pub utxos: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WalletTransaction {
pub txid: Txid,
Expand All @@ -40,6 +40,16 @@ pub struct WalletTransaction {
pub fee: Option<u64>,
pub confirmed: bool,
pub confirmation_time: Option<BlockTime>,
pub vsize: usize,
pub fee_rate: f32,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TransactionData {
pub details: TransactionDetails,
pub vsize: usize,
pub fee_rate: f32,
}

#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, ZeroizeOnDrop, Display, Default)]
Expand Down
Loading

0 comments on commit 377c605

Please sign in to comment.