Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump Fee #439

Merged
merged 12 commits into from
Dec 28, 2023
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
Loading