diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl
index b90f059d..d3d8df64 100644
--- a/bdk-ffi/src/bdk.udl
+++ b/bdk-ffi/src/bdk.udl
@@ -82,6 +82,12 @@ enum BdkError {
   "Psbt",
 };
 
+[Error]
+enum BdkCalculateFeeError {
+    "MissingTxOut",
+    "NegativeFee",
+};
+
 enum ChangeSpendPolicy {
   "ChangeAllowed",
   "OnlyChange",
@@ -111,6 +117,9 @@ interface Wallet {
   SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
 
   sequence<Transaction> transactions();
+
+  [Throws=BdkCalculateFeeError]
+  u64 calculate_fee([ByRef] Transaction tx);
 };
 
 interface Update {};
diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs
new file mode 100644
index 00000000..705ce162
--- /dev/null
+++ b/bdk-ffi/src/error.rs
@@ -0,0 +1,39 @@
+use std::fmt;
+use bdk::chain::tx_graph::CalculateFeeError;
+use bdk::bitcoin::OutPoint as BdkOutPoint;
+
+#[derive(Debug)]
+pub enum BdkCalculateFeeError {
+    MissingTxOut { out_points: Vec<BdkOutPoint> },
+    NegativeFee { fee: i64 },
+}
+
+impl fmt::Display for BdkCalculateFeeError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            BdkCalculateFeeError::MissingTxOut { out_points } =>
+                write!(f, "Missing transaction output: {:?}", out_points),
+            BdkCalculateFeeError::NegativeFee { fee } =>
+                write!(f, "Negative fee value: {}", fee),
+        }
+    }
+}
+
+impl From<CalculateFeeError> for BdkCalculateFeeError {
+    fn from(error: CalculateFeeError) -> Self {
+        match error {
+            CalculateFeeError::MissingTxOut(out_points) => BdkCalculateFeeError::MissingTxOut {
+                out_points: out_points
+                    .into_iter()
+                    .map(|out_point| BdkOutPoint {
+                        txid: out_point.txid,
+                        vout: out_point.vout,
+                    })
+                    .collect(),
+            },
+            CalculateFeeError::NegativeFee(fee) => BdkCalculateFeeError::NegativeFee { fee },
+        }
+    }
+}
+
+impl std::error::Error for BdkCalculateFeeError {}
\ No newline at end of file
diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs
index b4ce7fda..6c3834a3 100644
--- a/bdk-ffi/src/lib.rs
+++ b/bdk-ffi/src/lib.rs
@@ -4,6 +4,7 @@ mod esplora;
 mod keys;
 mod types;
 mod wallet;
+mod error;
 
 use crate::bitcoin::Address;
 use crate::bitcoin::Network;
@@ -24,6 +25,7 @@ use crate::types::Balance;
 use crate::types::LocalUtxo;
 use crate::types::ScriptAmount;
 use crate::wallet::BumpFeeTxBuilder;
+use crate::error::BdkCalculateFeeError;
 use crate::wallet::SentAndReceivedValues;
 use crate::wallet::TxBuilder;
 use crate::wallet::Update;
diff --git a/bdk-ffi/src/wallet.rs b/bdk-ffi/src/wallet.rs
index e4dcebb3..15732eef 100644
--- a/bdk-ffi/src/wallet.rs
+++ b/bdk-ffi/src/wallet.rs
@@ -1,5 +1,6 @@
 use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
 use crate::descriptor::Descriptor;
+use crate::error::BdkCalculateFeeError;
 use crate::types::Balance;
 use crate::types::ScriptAmount;
 use crate::Script;
@@ -98,6 +99,11 @@ impl Wallet {
             .map(|tx| Arc::new(tx.tx_node.tx.clone().into()))
             .collect()
     }
+
+    pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, BdkCalculateFeeError> {
+        let fee_result = self.get_wallet().calculate_fee(&tx.clone().into());
+        fee_result.map_err(|err| err.into())
+    }
 }
 
 pub struct SentAndReceivedValues {