Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Review all `Result` to `Option` conversion in the codebase for silencing errors
Spotted in #590 and #591

As the cause of #590 the following code:
https://github.com/ergoplatform/sigma-rust/blob/26155df207dc88601de1ae06a5bf5b049d8dcabf/ergo-lib/src/wallet/box_selector/simple.rs#L157-L158

shows the danger of converting `Result` to `Option` where the target value is `Option`. 

Keep in mind that such conversion is perfectly safe where the `Option` on the target describes the error case (`None`).


Discussion / hints:

Fix patch:
diff --git a/bindings/ergo-lib-c-core/Cargo.toml b/bindings/ergo-lib-c-core/Cargo.toml
index 39170a4fa..ea836705a 100644
--- a/bindings/ergo-lib-c-core/Cargo.toml
+++ b/bindings/ergo-lib-c-core/Cargo.toml
@@ -27,7 +27,7 @@ version = "1.9.1"
 features = [ "json" ]
 
 [dependencies.bounded-vec]
-version = "^0.6.0"
+version = "^0.7.0"
 features = ["serde"]
 
 [features]
diff --git a/bindings/ergo-lib-c-core/src/wallet.rs b/bindings/ergo-lib-c-core/src/wallet.rs
index 2566a5370..6618fbb83 100644
--- a/bindings/ergo-lib-c-core/src/wallet.rs
+++ b/bindings/ergo-lib-c-core/src/wallet.rs
@@ -78,7 +78,7 @@ pub unsafe fn wallet_from_mnemonic(
     wallet_out: *mut WalletPtr,
 ) -> Result<(), Error> {
     let wallet_out = mut_ptr_as_mut(wallet_out, "wallet_out")?;
-    if let Some(wallet_inner) =
+    if let Ok(wallet_inner) =
         ergo_lib::wallet::Wallet::from_mnemonic(mnemonic_phrase, mnemonic_pass)
     {
         *wallet_out = Box::into_raw(Box::new(Wallet(wallet_inner)));
diff --git a/bindings/ergo-lib-wasm/Cargo.toml b/bindings/ergo-lib-wasm/Cargo.toml
index cc43ae8fa..ab4296edd 100644
--- a/bindings/ergo-lib-wasm/Cargo.toml
+++ b/bindings/ergo-lib-wasm/Cargo.toml
@@ -47,7 +47,7 @@ features = ["serde-serialize"]
 version = "0.4.28"
 
 [dependencies.bounded-vec]
-version = "^0.6.0"
+version = "^0.7.0"
 features = ["serde"]
 
 [dev-dependencies]
diff --git a/bindings/ergo-lib-wasm/src/token.rs b/bindings/ergo-lib-wasm/src/token.rs
index 71f2c72f1..047b60074 100644
--- a/bindings/ergo-lib-wasm/src/token.rs
+++ b/bindings/ergo-lib-wasm/src/token.rs
@@ -187,12 +187,10 @@ impl Tokens {
 
 impl From<Tokens> for Option<chain::ergo_box::BoxTokens> {
     fn from(v: Tokens) -> Self {
-        chain::ergo_box::BoxTokens::from_vec(
-            v.0.iter().flatten().cloned().map(Into::into).collect(),
-        )
-        .ok()
+        v.0.map(|bv| bv.mapped(Into::into))
     }
 }
+
 impl From<Option<chain::ergo_box::BoxTokens>> for Tokens {
     fn from(v: Option<chain::ergo_box::BoxTokens>) -> Self {
         Tokens(v.map(|t| t.mapped(Token)))
diff --git a/bindings/ergo-lib-wasm/src/wallet.rs b/bindings/ergo-lib-wasm/src/wallet.rs
index 39d5a3486..8a50b60d8 100644
--- a/bindings/ergo-lib-wasm/src/wallet.rs
+++ b/bindings/ergo-lib-wasm/src/wallet.rs
@@ -31,8 +31,10 @@ impl Wallet {
     /// Create wallet instance loading secret key from mnemonic
     /// Returns None if a DlogSecretKey cannot be parsed from the provided phrase
     #[wasm_bindgen]
-    pub fn from_mnemonic(mnemonic_phrase: &str, mnemonic_pass: &str) -> Option<Wallet> {
-        Some(ergo_lib::wallet::Wallet::from_mnemonic(mnemonic_phrase, mnemonic_pass)?.into())
+    pub fn from_mnemonic(mnemonic_phrase: &str, mnemonic_pass: &str) -> Result<Wallet, JsValue> {
+        ergo_lib::wallet::Wallet::from_mnemonic(mnemonic_phrase, mnemonic_pass)
+            .map(Wallet)
+            .map_err(to_js)
     }
 
     /// Create wallet using provided secret key
diff --git a/ergo-lib/Cargo.toml b/ergo-lib/Cargo.toml
index 20d7b4185..1cff86b62 100644
--- a/ergo-lib/Cargo.toml
+++ b/ergo-lib/Cargo.toml
@@ -30,7 +30,7 @@ serde = { version = "1.0", features = ["derive"], optional = true }
 serde_json = { version = "1.0", optional = true }
 thiserror = "1"
 derive_more = "0.99"
-bounded-vec = { version = "^0.6.0" }
+bounded-vec = { version = "^0.7.0" }
 num-bigint = { version = "0.4.0", features = ["serde"] }
 proptest-derive = {version = "0.3.0", optional = true }
 sha2 = { version = "0.9.8" }
diff --git a/ergo-lib/src/chain/json/transaction.rs b/ergo-lib/src/chain/json/transaction.rs
index 1bb8c363c..39d9c95c2 100644
--- a/ergo-lib/src/chain/json/transaction.rs
+++ b/ergo-lib/src/chain/json/transaction.rs
@@ -1,4 +1,8 @@
-use crate::chain::transaction::{DataInput, Input, UnsignedInput};
+use std::convert::TryFrom;
+use thiserror::Error;
+
+use crate::chain::transaction::unsigned::UnsignedTransaction;
+use crate::chain::transaction::{DataInput, Input, Transaction, TransactionError, UnsignedInput};
 use ergotree_ir::chain::ergo_box::ErgoBox;
 use ergotree_ir::chain::ergo_box::ErgoBoxCandidate;
 use ergotree_ir::chain::tx_id::TxId;
@@ -35,6 +39,66 @@ pub struct UnsignedTransactionJson {
     pub outputs: Vec<ErgoBoxCandidate>,
 }
 
+impl From<UnsignedTransaction> for UnsignedTransactionJson {
+    fn from(v: UnsignedTransaction) -> Self {
+        UnsignedTransactionJson {
+            inputs: v.inputs.as_vec().clone(),
+            data_inputs: v
+                .data_inputs
+                .map(|di| di.as_vec().clone())
+                .unwrap_or_default(),
+            outputs: v.output_candidates.as_vec().clone(),
+        }
+    }
+}
+
+impl TryFrom<UnsignedTransactionJson> for UnsignedTransaction {
+    // We never return this type but () fails to compile (can't format) and ! is experimental
+    type Error = String;
+    fn try_from(tx_json: UnsignedTransactionJson) -> Result<Self, Self::Error> {
+        UnsignedTransaction::new_from_vec(tx_json.inputs, tx_json.data_inputs, tx_json.outputs)
+            .map_err(|e| format!("TryFrom<UnsignedTransactionJson> error: {0}", e))
+    }
+}
+
+impl From<Transaction> for TransactionJson {
+    fn from(v: Transaction) -> Self {
+        TransactionJson {
+            tx_id: v.id(),
+            inputs: v.inputs.as_vec().clone(),
+            data_inputs: v
+                .data_inputs
+                .map(|di| di.as_vec().clone())
+                .unwrap_or_default(),
+            outputs: v.outputs,
+        }
+    }
+}
+
+/// Errors on parsing Transaction from JSON
+#[derive(Error, PartialEq, Eq, Debug, Clone)]
+#[allow(missing_docs)]
+pub enum TransactionFromJsonError {
+    #[error("Tx id parsed from JSON differs from calculated from serialized bytes")]
+    InvalidTxId,
+    #[error("Tx error: {0}")]
+    TransactionError(#[from] TransactionError),
+}
+
+impl TryFrom<TransactionJson> for Transaction {
+    type Error = TransactionFromJsonError;
+    fn try_from(tx_json: TransactionJson) -> Result<Self, Self::Error> {
+        let output_candidates: Vec<ErgoBoxCandidate> =
+            tx_json.outputs.iter().map(|o| o.clone().into()).collect();
+        let tx = Transaction::new_from_vec(tx_json.inputs, tx_json.data_inputs, output_candidates)?;
+        if tx.tx_id == tx_json.tx_id {
+            Ok(tx)
+        } else {
+            Err(TransactionFromJsonError::InvalidTxId)
+        }
+    }
+}
+
 #[cfg(test)]
 #[allow(clippy::panic)]
 mod tests {
diff --git a/ergo-lib/src/chain/transaction.rs b/ergo-lib/src/chain/transaction.rs
index 43533139d..50090f9ce 100644
--- a/ergo-lib/src/chain/transaction.rs
+++ b/ergo-lib/src/chain/transaction.rs
@@ -5,6 +5,7 @@ pub mod input;
 pub mod reduced;
 pub mod unsigned;
 
+use bounded_vec::BoundedVec;
 use ergo_chain_types::blake2b256_hash;
 pub use ergotree_interpreter::eval::context::TxIoVec;
 use ergotree_ir::chain::ergo_box::ErgoBox;
@@ -25,11 +26,7 @@ pub use input::*;
 
 use self::unsigned::UnsignedTransaction;
 
-#[cfg(feature = "json")]
-use super::json;
 use indexmap::IndexSet;
-#[cfg(feature = "json")]
-use serde::{Deserialize, Serialize};
 
 use std::convert::TryFrom;
 use std::convert::TryInto;
@@ -45,18 +42,18 @@ use std::iter::FromIterator;
  * Transactions are not encrypted, so it is possible to browse and view every transaction ever
  * collected into a block.
  */
-#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(
     feature = "json",
     serde(
-        try_from = "json::transaction::TransactionJson",
-        into = "json::transaction::TransactionJson"
+        try_from = "super::json::transaction::TransactionJson",
+        into = "super::json::transaction::TransactionJson"
     )
 )]
 #[derive(PartialEq, Debug, Clone)]
 pub struct Transaction {
     /// transaction id
-    tx_id: TxId,
+    pub(crate) tx_id: TxId,
     /// inputs, that will be spent by this transaction.
     pub inputs: TxIoVec<Input>,
     /// inputs, that are not going to be spent by transaction, but will be reachable from inputs
@@ -77,6 +74,24 @@ impl Transaction {
     /// Maximum number of outputs
     pub const MAX_OUTPUTS_COUNT: usize = u16::MAX as usize;
 
+    /// Creates new transaction from vectors
+    pub fn new_from_vec(
+        inputs: Vec<Input>,
+        data_inputs: Vec<DataInput>,
+        output_candidates: Vec<ErgoBoxCandidate>,
+    ) -> Result<Transaction, TransactionError> {
+        Ok(Transaction::new(
+            inputs
+                .try_into()
+                .map_err(TransactionError::InvalidInputsCount)?,
+            BoundedVec::opt_empty_vec(data_inputs)
+                .map_err(TransactionError::InvalidDataInputsCount)?,
+            output_candidates
+                .try_into()
+                .map_err(TransactionError::InvalidOutputCandidatesCount)?,
+        )?)
+    }
+
     /// Creates new transaction
     pub fn new(
         inputs: TxIoVec<Input>,
@@ -239,92 +254,30 @@ impl SigmaSerializable for Transaction {
             )?)
         }
 
-        Ok(Transaction::new(
-            inputs.try_into()?,
-            data_inputs.try_into().ok(),
-            outputs
-                .try_into()
-                .map_err(SigmaParsingError::BoundedVecOutOfBounds)?,
-        )?)
+        Transaction::new_from_vec(inputs, data_inputs, outputs)
+            .map_err(|e| SigmaParsingError::Misc(format!("{}", e)))
     }
 }
 
 /// Error when working with Transaction
+#[allow(missing_docs)]
 #[derive(Error, Eq, PartialEq, Debug, Clone)]
 pub enum TransactionError {
-    /// Serialization error
     #[error("Tx serialization error: {0}")]
     SigmaSerializationError(#[from] SigmaSerializationError),
-    /// Invalid argument on tx construction
     #[error("Tx innvalid argument: {0}")]
     InvalidArgument(String),
-}
-
-#[cfg(feature = "json")]
-impl From<Transaction> for json::transaction::TransactionJson {
-    fn from(v: Transaction) -> Self {
-        json::transaction::TransactionJson {
-            tx_id: v.id(),
-            inputs: v.inputs.as_vec().clone(),
-            data_inputs: v
-                .data_inputs
-                .map(|di| di.as_vec().clone())
-                .unwrap_or_default(),
-            outputs: v.outputs,
-        }
-    }
-}
-
-/// Errors on parsing Transaction from JSON
-#[cfg(feature = "json")]
-#[derive(Error, PartialEq, Eq, Debug, Clone)]
-pub enum TransactionFromJsonError {
-    /// Tx id parsed from JSON differs from calculated from serialized bytes
-    #[error("Tx id parsed from JSON differs from calculated from serialized bytes")]
-    InvalidTxId,
-    /// Serialization failed (id calculation)
-    #[error("Serialization failed (id calculation)")]
-    SerializationError,
-    /// Invalid tx input count
     #[error("Invalid Tx inputs: {0:?}")]
     InvalidInputsCount(bounded_vec::BoundedVecOutOfBounds),
-    /// Invalid tx output_candidates count
     #[error("Invalid Tx output_candidates: {0:?}")]
     InvalidOutputCandidatesCount(bounded_vec::BoundedVecOutOfBounds),
-    /// Invalid tx data inputs count
     #[error("Invalid Tx data inputs: {0:?}")]
     InvalidDataInputsCount(bounded_vec::BoundedVecOutOfBounds),
 }
 
-#[cfg(feature = "json")]
-impl TryFrom<json::transaction::TransactionJson> for Transaction {
-    type Error = TransactionFromJsonError;
-    fn try_from(tx_json: json::transaction::TransactionJson) -> Result<Self, Self::Error> {
-        let output_candidates: Vec<ErgoBoxCandidate> =
-            tx_json.outputs.iter().map(|o| o.clone().into()).collect();
-        let tx = Transaction::new(
-            tx_json
-                .inputs
-                .try_into()
-                .map_err(TransactionFromJsonError::InvalidInputsCount)?,
-            tx_json.data_inputs.try_into().ok(),
-            output_candidates
-                .try_into()
-                .map_err(TransactionFromJsonError::InvalidOutputCandidatesCount)?,
-        )
-        .map_err(|_| TransactionFromJsonError::SerializationError)?;
-        if tx.tx_id == tx_json.tx_id {
-            Ok(tx)
-        } else {
-            Err(TransactionFromJsonError::InvalidTxId)
-        }
-    }
-}
-
 #[cfg(test)]
 #[allow(clippy::unwrap_used, clippy::panic)]
 pub mod tests {
-    use std::convert::TryInto;
 
     use super::*;
 
@@ -342,12 +295,7 @@ pub mod tests {
                 vec(any::<ErgoBoxCandidate>(), 1..10),
             )
                 .prop_map(|(inputs, data_inputs, outputs)| {
-                    Self::new(
-                        inputs.try_into().unwrap(),
-                        data_inputs.try_into().ok(),
-                        outputs.try_into().unwrap(),
-                    )
-                    .unwrap()
+                    Self::new_from_vec(inputs, data_inputs, outputs).unwrap()
                 })
                 .boxed()
         }
diff --git a/ergo-lib/src/chain/transaction/unsigned.rs b/ergo-lib/src/chain/transaction/unsigned.rs
index c6df7f1ae..76e65091f 100644
--- a/ergo-lib/src/chain/transaction/unsigned.rs
+++ b/ergo-lib/src/chain/transaction/unsigned.rs
@@ -1,13 +1,12 @@
 //! Unsigned (without proofs) transaction
 
-use super::distinct_token_ids;
 use super::input::{Input, UnsignedInput};
-#[cfg(feature = "json")]
-use super::json;
 use super::prover_result::ProverResult;
 use super::DataInput;
 use super::Transaction;
 use super::TxIoVec;
+use super::{distinct_token_ids, TransactionError};
+use bounded_vec::BoundedVec;
 use ergo_chain_types::blake2b256_hash;
 use ergotree_interpreter::sigma_protocol::prover::ProofBytes;
 use ergotree_ir::chain::ergo_box::ErgoBoxCandidate;
@@ -15,20 +14,15 @@ use ergotree_ir::chain::token::TokenId;
 use ergotree_ir::chain::tx_id::TxId;
 use ergotree_ir::serialization::SigmaSerializationError;
 use indexmap::IndexSet;
-#[cfg(feature = "json")]
-use serde::{Deserialize, Serialize};
-#[cfg(feature = "json")]
-use std::convert::TryFrom;
-#[cfg(feature = "json")]
 use std::convert::TryInto;
 
 /// Unsigned (inputs without proofs) transaction
-#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(
     feature = "json",
     serde(
-        try_from = "json::transaction::UnsignedTransactionJson",
-        into = "json::transaction::UnsignedTransactionJson"
+        try_from = "crate::chain::json::transaction::UnsignedTransactionJson",
+        into = "crate::chain::json::transaction::UnsignedTransactionJson"
     )
 )]
 #[derive(PartialEq, Debug, Clone)]
@@ -45,6 +39,24 @@ pub struct UnsignedTransaction {
 }
 
 impl UnsignedTransaction {
+    /// Creates new transaction from vectors
+    pub fn new_from_vec(
+        inputs: Vec<UnsignedInput>,
+        data_inputs: Vec<DataInput>,
+        output_candidates: Vec<ErgoBoxCandidate>,
+    ) -> Result<UnsignedTransaction, TransactionError> {
+        Ok(UnsignedTransaction::new(
+            inputs
+                .try_into()
+                .map_err(TransactionError::InvalidInputsCount)?,
+            BoundedVec::opt_empty_vec(data_inputs)
+                .map_err(TransactionError::InvalidDataInputsCount)?,
+            output_candidates
+                .try_into()
+                .map_err(TransactionError::InvalidOutputCandidatesCount)?,
+        )?)
+    }
+
     /// Creates new transaction
     pub fn new(
         inputs: TxIoVec<UnsignedInput>,
@@ -69,7 +81,7 @@ impl UnsignedTransaction {
         Ok(TxId(blake2b256_hash(&bytes)))
     }
 
-    fn to_tx_wo_proofs(&self) -> Result<Transaction, SigmaSerializationError> {
+    fn to_tx_without_proofs(&self) -> Transaction {
         let empty_proofs_input = self.inputs.mapped_ref(|ui| {
             Input::new(
                 ui.box_id.clone(),
@@ -79,11 +91,16 @@ impl UnsignedTransaction {
                 },
             )
         });
+
+        #[allow(clippy::unwrap_used)]
+        // safe since the serialization error is impossible here
+        // since we already serialized this unsigned tx (on calc tx id)
         Transaction::new(
             empty_proofs_input,
             self.data_inputs.clone(),
             self.output_candidates.clone(),
         )
+        .unwrap()
     }
 
     /// Get transaction id
@@ -93,7 +110,7 @@ impl UnsignedTransaction {
 
     /// message to be signed by the [`ergotree_interpreter::sigma_protocol::prover::Prover`] (serialized tx)
     pub fn bytes_to_sign(&self) -> Result<Vec<u8>, SigmaSerializationError> {
-        let tx = self.to_tx_wo_proofs()?;
+        let tx = self.to_tx_without_proofs();
         tx.bytes_to_sign()
     }
 
@@ -103,40 +120,6 @@ impl UnsignedTransaction {
     }
 }
 
-#[cfg(feature = "json")]
-impl From<UnsignedTransaction> for json::transaction::UnsignedTransactionJson {
-    fn from(v: UnsignedTransaction) -> Self {
-        json::transaction::UnsignedTransactionJson {
-            inputs: v.inputs.as_vec().clone(),
-            data_inputs: v
-                .data_inputs
-                .map(|di| di.as_vec().clone())
-                .unwrap_or_default(),
-            outputs: v.output_candidates.as_vec().clone(),
-        }
-    }
-}
-
-#[cfg(feature = "json")]
-impl TryFrom<json::transaction::UnsignedTransactionJson> for UnsignedTransaction {
-    // We never return this type but () fails to compile (can't format) and ! is experimental
-    type Error = String;
-    fn try_from(tx_json: json::transaction::UnsignedTransactionJson) -> Result<Self, Self::Error> {
-        UnsignedTransaction::new(
-            tx_json
-                .inputs
-                .try_into()
-                .map_err(|e: bounded_vec::BoundedVecOutOfBounds| e.to_string())?,
-            tx_json.data_inputs.try_into().ok(),
-            tx_json
-                .outputs
-                .try_into()
-                .map_err(|e: bounded_vec::BoundedVecOutOfBounds| e.to_string())?,
-        )
-        .map_err(|e| format!("unsigned tx serialization failed: {0}", e))
-    }
-}
-
 #[cfg(test)]
 #[allow(clippy::unwrap_used, clippy::panic)]
 pub mod tests {
@@ -155,12 +138,7 @@ pub mod tests {
                 vec(any::<ErgoBoxCandidate>(), 1..10),
             )
                 .prop_map(|(inputs, data_inputs, outputs)| {
-                    Self::new(
-                        inputs.try_into().unwrap(),
-                        data_inputs.try_into().ok(),
-                        outputs.try_into().unwrap(),
-                    )
-                    .unwrap()
+                    Self::new_from_vec(inputs, data_inputs, outputs).unwrap()
                 })
                 .boxed()
         }
diff --git a/ergo-lib/src/wallet.rs b/ergo-lib/src/wallet.rs
index 85c4e8c97..2e1e4fdc5 100644
--- a/ergo-lib/src/wallet.rs
+++ b/ergo-lib/src/wallet.rs
@@ -32,6 +32,7 @@ use crate::wallet::multi_sig::{
 };
 
 use self::ext_secret_key::ExtSecretKey;
+use self::ext_secret_key::ExtSecretKeyError;
 use self::signing::sign_message;
 use self::signing::sign_reduced_transaction;
 use self::signing::TransactionContext;
@@ -42,15 +43,20 @@ pub struct Wallet {
 }
 
 /// Wallet errors
+#[allow(missing_docs)]
 #[derive(Error, Debug)]
 pub enum WalletError {
-    /// Error on tx signing
     #[error("Transaction signing error: {0}")]
     TxSigningError(TxSigningError),
 
-    /// Error on proving an input
     #[error("Prover error: {0}")]
     ProverError(ProverError),
+
+    #[error("ExtSecretKeyError: {0}")]
+    ExtSecretKeyError(#[from] ExtSecretKeyError),
+
+    #[error("error parsing SecretKey from ExtSecretKey.bytes")]
+    SecretKeyParsingError,
 }
 
 impl From<TxSigningError> for WalletError {
@@ -68,12 +74,13 @@ impl From<ProverError> for WalletError {
 impl Wallet {
     /// Create wallet instance loading secret key from mnemonic
     /// Returns None if a DlogSecretKey cannot be parsed from the provided phrase
-    pub fn from_mnemonic(mnemonic_phrase: &str, mnemonic_pass: &str) -> Option<Wallet> {
+    pub fn from_mnemonic(
+        mnemonic_phrase: &str,
+        mnemonic_pass: &str,
+    ) -> Result<Wallet, WalletError> {
         let seed = Mnemonic::to_seed(mnemonic_phrase, mnemonic_pass);
-        let ext_sk = ExtSecretKey::derive_master(seed).ok()?;
-        let secret = SecretKey::dlog_from_bytes(&ext_sk.secret_key_bytes())?;
-
-        Some(Wallet::from_secrets(vec![secret]))
+        let ext_sk = ExtSecretKey::derive_master(seed)?;
+        Ok(Wallet::from_secrets(vec![ext_sk.secret_key()]))
     }
 
     /// Create Wallet from secrets
diff --git a/ergo-lib/src/wallet/ext_secret_key.rs b/ergo-lib/src/wallet/ext_secret_key.rs
index 92702a3d3..54aef15b8 100644
--- a/ergo-lib/src/wallet/ext_secret_key.rs
+++ b/ergo-lib/src/wallet/ext_secret_key.rs
@@ -5,6 +5,7 @@ use super::{
     derivation_path::{ChildIndex, ChildIndexError, DerivationPath},
     ext_pub_key::ExtPubKey,
     mnemonic::MnemonicSeed,
+    secret_key::SecretKey,
 };
 use crate::ArrLength;
 use ergotree_interpreter::sigma_protocol::{private_input::DlogProverInput, wscalar::Wscalar};
@@ -78,6 +79,11 @@ impl ExtSecretKey {
         self.derivation_path.clone()
     }
 
+    /// Returns secret key
+    pub fn secret_key(&self) -> SecretKey {
+        self.private_input.clone().into()
+    }
+
     /// Byte representation of the underlying scalar
     pub fn secret_key_bytes(&self) -> SecretKeyBytes {
         self.private_input.to_bytes()
diff --git a/ergo-lib/src/wallet/signing.rs b/ergo-lib/src/wallet/signing.rs
index 18631511c..d069c6add 100644
--- a/ergo-lib/src/wallet/signing.rs
+++ b/ergo-lib/src/wallet/signing.rs
@@ -327,8 +327,7 @@ mod tests {
             let candidate = ErgoBoxCandidateBuilder::new(BoxValue::SAFE_USER_MIN, ergo_tree, 0)
                 .build().unwrap();
             let output_candidates = vec![candidate];
-            let tx = UnsignedTransaction::new(inputs.try_into().unwrap(),
-                None, output_candidates.try_into().unwrap()).unwrap();
+            let tx = UnsignedTransaction::new_from_vec(inputs, vec![], output_candidates).unwrap();
             let tx_context = TransactionContext::new(tx, boxes_to_spend.clone(), vec![]).unwrap();
             let tx_hint_bag=TransactionHintsBag::empty();
             let res = sign_transaction(prover.as_ref(), tx_context.clone(), &force_any_val::<ErgoStateContext>(), Some(&tx_hint_bag));
diff --git a/ergo-lib/src/wallet/tx_builder.rs b/ergo-lib/src/wallet/tx_builder.rs
index 26cacc9fc..7825e4b14 100644
--- a/ergo-lib/src/wallet/tx_builder.rs
+++ b/ergo-lib/src/wallet/tx_builder.rs
@@ -1,5 +1,6 @@
 //! Builder for an UnsignedTransaction
 
+use ergotree_interpreter::eval::context::TxIoVec;
 use ergotree_interpreter::sigma_protocol::prover::ContextExtension;
 use ergotree_ir::chain::token::TokenAmount;
 use ergotree_ir::chain::token::TokenAmountError;
@@ -251,7 +252,7 @@ impl<S: ErgoBoxAssets + ErgoBoxId + Clone> TxBuilder<S> {
         });
         Ok(UnsignedTransaction::new(
             unsigned_inputs,
-            self.data_inputs.clone().try_into().ok(),
+            TxIoVec::opt_empty_vec(self.data_inputs.clone())?,
             output_candidates.try_into()?,
         )?)
     }
diff --git a/ergo-p2p/Cargo.toml b/ergo-p2p/Cargo.toml
index add5af299..917aa7687 100644
--- a/ergo-p2p/Cargo.toml
+++ b/ergo-p2p/Cargo.toml
@@ -19,7 +19,7 @@ sigma-util = { version = "^0.6.0", path = "../sigma-util" }
 ergo-chain-types = { version = "^0.5.0", path = "../ergo-chain-types"}
 thiserror = "1"
 derive_more = "0.99"
-bounded-vec = { version = "^0.6.0" }
+bounded-vec = { version = "^0.7.0" }
 proptest-derive = {version = "0.3.0", optional = true }
 
 futures = "0.3"
diff --git a/ergo-rest/Cargo.toml b/ergo-rest/Cargo.toml
index eaf75256b..c9adf7d23 100644
--- a/ergo-rest/Cargo.toml
+++ b/ergo-rest/Cargo.toml
@@ -16,7 +16,7 @@ crate-type = ["cdylib", "rlib"]
 [dependencies]
 async-trait = "^0.1"
 bounded-integer = { version = "^0.5", features = ["types"] }
-bounded-vec = { version = "^0.6.0", features=["serde"] }
+bounded-vec = { version = "^0.7.0", features=["serde"] }
 sigma-ser = { version = "^0.7.0", path = "../sigma-ser" }
 sigma-util = { version = "^0.6.0", path = "../sigma-util" }
 ergo-chain-types = { version = "^0.5.0", path = "../ergo-chain-types" }
diff --git a/ergotree-interpreter/Cargo.toml b/ergotree-interpreter/Cargo.toml
index de35f08b6..a5d3983ba 100644
--- a/ergotree-interpreter/Cargo.toml
+++ b/ergotree-interpreter/Cargo.toml
@@ -33,7 +33,7 @@ base16 = "0.2.1"
 bytes = "1.1"
 num-bigint = "0.4.0"
 scorex_crypto_avltree = "0.1.0"
-bounded-vec = { version = "^0.6.0", features=["serde"] }
+bounded-vec = { version = "^0.7.0", features=["serde"] }
 serde = { version = "1.0", features = ["derive"], optional = true }
 serde_json = { version = "1.0", optional = true }
 
diff --git a/ergotree-ir/Cargo.toml b/ergotree-ir/Cargo.toml
index 27ff8a193..f4adf71d8 100644
--- a/ergotree-ir/Cargo.toml
+++ b/ergotree-ir/Cargo.toml
@@ -29,7 +29,7 @@ proptest-derive = {version = "0.3.0", optional = true }
 bs58 = "0.4.0"
 base16 = "0.2.1"
 num-bigint = "0.4.0"
-bounded-vec = { version = "^0.6.0", features=["serde"] }
+bounded-vec = { version = "^0.7.0", features=["serde"] }
 num256 = "0.3.1"
 num-traits = "0.2.14"
 num-derive = "0.3.3"
diff --git a/sigma-ser/Cargo.toml b/sigma-ser/Cargo.toml
index dac353f51..53664f62d 100644
--- a/sigma-ser/Cargo.toml
+++ b/sigma-ser/Cargo.toml
@@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"]
 # wasm-bindgen = "0.2"
 thiserror = "1"
 bitvec = "0.22.3"
-bounded-vec = { version = "^0.6.0" }
+bounded-vec = { version = "^0.7.0" }
 # The `console_error_panic_hook` crate provides better debugging of panics by
 # logging them with `console.error`. This is great for development, but requires
 # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for


Test patch: