diff --git a/lib/dubp/block-doc/src/block/v10.rs b/lib/dubp/block-doc/src/block/v10.rs
index 4877671bffcdbe926c6a22a9c89d1d312e3fdd12..206f6db8229a9768f28f8f781e9e653698a63146 100644
--- a/lib/dubp/block-doc/src/block/v10.rs
+++ b/lib/dubp/block-doc/src/block/v10.rs
@@ -31,7 +31,9 @@ use dubp_user_docs::documents::membership::v10::{
 };
 use dubp_user_docs::documents::revocation::v10::CompactRevocationDocumentV10Stringified;
 use dubp_user_docs::documents::revocation::RevocationDocumentV10;
-use dubp_user_docs::documents::transaction::{TransactionDocument, TransactionDocumentStringified};
+use dubp_user_docs::documents::transaction::v10::{
+    TransactionDocumentV10, TransactionDocumentV10Stringified,
+};
 use dup_crypto::hashs::Hash;
 use dup_crypto::keys::*;
 use durs_common_tools::fatal_error;
@@ -100,7 +102,7 @@ pub struct BlockDocumentV10 {
     /// Certifications
     pub certifications: Vec<TextDocumentFormat<CertificationDocumentV10>>,
     /// Transactions
-    pub transactions: Vec<TransactionDocument>,
+    pub transactions: Vec<TransactionDocumentV10>,
 }
 
 impl BlockDocumentTrait for BlockDocumentV10 {
@@ -502,7 +504,7 @@ pub struct BlockDocumentV10Stringified {
     /// Certifications
     pub certifications: Vec<CompactCertificationDocumentV10Stringified>,
     /// Transactions
-    pub transactions: Vec<TransactionDocumentStringified>,
+    pub transactions: Vec<TransactionDocumentV10Stringified>,
 }
 
 impl ToStringObject for BlockDocumentV10 {
@@ -594,7 +596,7 @@ mod tests {
         CertificationDocument, CertificationDocumentParser,
     };
     use dubp_user_docs::documents::membership::{MembershipDocument, MembershipDocumentParser};
-    use dubp_user_docs::documents::transaction::TransactionDocumentParser;
+    use dubp_user_docs::documents::transaction::{TransactionDocument, TransactionDocumentParser};
     use unwrap::unwrap;
 
     #[test]
@@ -670,7 +672,7 @@ UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVG
             CertificationDocument::V10(cert_v10) => cert_v10,
         };
 
-        let tx1 = TransactionDocumentParser::parse("Version: 10
+        let TransactionDocument::V10(tx1) = TransactionDocumentParser::parse("Version: 10
 Type: Transaction
 Currency: g1
 Blockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B
@@ -686,7 +688,7 @@ Outputs:
 Comment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025  Merci
 T0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==").expect("Fail to parse tx1");
 
-        let tx2 = TransactionDocumentParser::parse("Version: 10
+        let TransactionDocument::V10(tx2) = TransactionDocumentParser::parse("Version: 10
 Type: Transaction
 Currency: g1
 Blockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B
@@ -821,7 +823,7 @@ gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8
             MembershipDocument::V10(ms_v10) => ms_v10,
         };
 
-        let tx1 = TransactionDocumentParser::parse(
+        let TransactionDocument::V10(tx1) = TransactionDocumentParser::parse(
             "Version: 10
 Type: Transaction
 Currency: g1
@@ -853,7 +855,7 @@ Comment: Panier mixte plus 40 pommes merci
         )
         .expect("Fail to parse tx1");
 
-        let tx2 = TransactionDocumentParser::parse(
+        let TransactionDocument::V10(tx2) = TransactionDocumentParser::parse(
             "Version: 10
 Type: Transaction
 Currency: g1
diff --git a/lib/dubp/block-doc/src/parser.rs b/lib/dubp/block-doc/src/parser.rs
index 8e8ae54db0168805e5e36d2b4382a30d5a15e2f7..eb64513fe55f7153dbe737ff2d61605462ace4ba 100644
--- a/lib/dubp/block-doc/src/parser.rs
+++ b/lib/dubp/block-doc/src/parser.rs
@@ -20,7 +20,6 @@ use dubp_common_doc::{BlockHash, BlockNumber};
 use dubp_currency_params::genesis_block_params::v10::BlockV10Parameters;
 use dubp_currency_params::CurrencyName;
 use dubp_user_docs::documents::membership::v10::MembershipType;
-use dubp_user_docs::documents::transaction::TransactionDocument;
 use dubp_user_docs::parsers::{serde_json_value_to_pest_json_value, DefaultHasher};
 use dup_crypto::bases::BaseConvertionError;
 use dup_crypto::hashs::Hash;
@@ -121,19 +120,10 @@ pub fn parse_json_block(json_block: &JSONValue<DefaultHasher>) -> Result<BlockDo
         certifications: dubp_user_docs::parsers::certifications::parse_certifications_into_compact(
             &get_str_array(json_block, "certifications")?,
         ),
-        transactions: json_block
-            .get("transactions")
-            .ok_or_else(|| ParseJsonError {
-                cause: "Fail to parse json block : field 'transactions' must exist !".to_owned(),
-            })?
-            .to_array()
-            .ok_or_else(|| ParseJsonError {
-                cause: "Fail to parse json block : field 'transactions' must be an array !"
-                    .to_owned(),
-            })?
-            .iter()
-            .map(|tx| dubp_user_docs::parsers::transactions::parse_json_transaction(tx))
-            .collect::<Result<Vec<TransactionDocument>, Error>>()?,
+        transactions: dubp_user_docs::parsers::transactions::parse_json_transactions(&get_array(
+            json_block,
+            "transactions",
+        )?)?,
     }))
 }
 
@@ -141,6 +131,7 @@ pub fn parse_json_block(json_block: &JSONValue<DefaultHasher>) -> Result<BlockDo
 mod tests {
     use super::*;
     use crate::block::*;
+    use dubp_user_docs::documents::transaction::TransactionDocument;
 
     #[test]
     fn parse_empty_json_block() {
@@ -351,7 +342,9 @@ mod tests {
                 revoked: vec![],
                 excluded: vec![],
                 certifications: vec![],
-                transactions: vec![dubp_user_docs_tests_tools::mocks::tx::first_g1_tx_doc()],
+                transactions: vec![match dubp_user_docs_tests_tools::mocks::tx::first_g1_tx_doc() {
+                    TransactionDocument::V10(tx_doc) => tx_doc,
+                }],
             });
         assert_eq!(
             expected_block,
diff --git a/lib/dubp/user-docs/src/documents/transaction.rs b/lib/dubp/user-docs/src/documents/transaction.rs
index ea2d33a57ab86500179077b48966c59eddb16cb8..8b9425af9bc96fb41078715ef338ad38c07bc19d 100644
--- a/lib/dubp/user-docs/src/documents/transaction.rs
+++ b/lib/dubp/user-docs/src/documents/transaction.rs
@@ -15,22 +15,23 @@
 
 //! Wrappers around Transaction documents.
 
+pub mod v10;
+
 use crate::documents::*;
 use dubp_common_doc::blockstamp::Blockstamp;
 use dubp_common_doc::parser::{DocumentsParser, TextDocumentParseError, TextDocumentParser};
 use dubp_common_doc::traits::text::*;
 use dubp_common_doc::traits::{Document, DocumentBuilder, ToStringObject};
-use dubp_common_doc::{BlockHash, BlockNumber};
 use dup_crypto::hashs::*;
 use dup_crypto::keys::*;
-use durs_common_tools::fatal_error;
-use pest::iterators::Pair;
-use pest::iterators::Pairs;
-use pest::Parser;
 use std::ops::{Add, Deref, Sub};
-use std::str::FromStr;
 use unwrap::unwrap;
 
+pub use v10::{
+    TransactionDocumentV10, TransactionDocumentV10Builder, TransactionDocumentV10Parser,
+    TransactionDocumentV10Stringified, TransactionInputV10, TransactionOutputV10,
+};
+
 /// Wrap a transaction amount
 #[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Hash, Serialize)]
 pub struct TxAmount(pub isize);
@@ -57,113 +58,6 @@ pub struct TxBase(pub usize);
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
 pub struct OutputIndex(pub usize);
 
-/// Wrap a transaction input
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub enum TransactionInput {
-    /// Universal Dividend Input
-    D(TxAmount, TxBase, PubKey, BlockNumber),
-    /// Previous Transaction Input
-    T(TxAmount, TxBase, Hash, OutputIndex),
-}
-
-impl ToString for TransactionInput {
-    fn to_string(&self) -> String {
-        match *self {
-            TransactionInput::D(amount, base, pubkey, block_number) => {
-                format!("{}:{}:D:{}:{}", amount.0, base.0, pubkey, block_number.0)
-            }
-            TransactionInput::T(amount, base, ref tx_hash, tx_index) => {
-                format!("{}:{}:T:{}:{}", amount.0, base.0, tx_hash, tx_index.0)
-            }
-        }
-    }
-}
-
-impl TransactionInput {
-    fn from_pest_pair(mut pairs: Pairs<Rule>) -> TransactionInput {
-        let tx_input_type_pair = unwrap!(pairs.next());
-        match tx_input_type_pair.as_rule() {
-            Rule::tx_input_du => {
-                let mut inner_rules = tx_input_type_pair.into_inner(); // ${ tx_amount ~ ":" ~ tx_amount_base ~ ":D:" ~ pubkey ~ ":" ~ du_block_id }
-
-                TransactionInput::D(
-                    TxAmount(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
-                    TxBase(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
-                    PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
-                        unwrap!(inner_rules.next()).as_str()
-                    ))),
-                    BlockNumber(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
-                )
-            }
-            Rule::tx_input_tx => {
-                let mut inner_rules = tx_input_type_pair.into_inner(); // ${ tx_amount ~ ":" ~ tx_amount_base ~ ":D:" ~ pubkey ~ ":" ~ du_block_id }
-
-                TransactionInput::T(
-                    TxAmount(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
-                    TxBase(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
-                    unwrap!(Hash::from_hex(unwrap!(inner_rules.next()).as_str())),
-                    OutputIndex(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
-                )
-            }
-            _ => fatal_error!("unexpected rule: {:?}", tx_input_type_pair.as_rule()), // Grammar ensures that we never reach this line
-        }
-    }
-}
-
-impl FromStr for TransactionInput {
-    type Err = TextDocumentParseError;
-
-    fn from_str(source: &str) -> Result<Self, Self::Err> {
-        match DocumentsParser::parse(Rule::tx_input, source) {
-            Ok(mut pairs) => Ok(TransactionInput::from_pest_pair(
-                unwrap!(pairs.next()).into_inner(),
-            )),
-            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
-                "Invalid unlocks !".to_owned(),
-            )),
-        }
-    }
-}
-
-/*impl TransactionInput {
-    /// Parse Transaction Input from string.
-    pub fn from_str(source: &str) -> Result<TransactionInput, TextDocumentParseError> {
-        if let Some(caps) = D_INPUT_REGEX.captures(source) {
-            let amount = &caps["amount"];
-            let base = &caps["base"];
-            let pubkey = &caps["pubkey"];
-            let block_number = &caps["block_number"];
-            Ok(TransactionInput::D(
-                TxAmount(amount.parse().expect("fail to parse input amount !")),
-                TxBase(base.parse().expect("fail to parse input base !")),
-                PubKey::Ed25519(
-                    ed25519::PublicKey::from_base58(pubkey).expect("fail to parse input pubkey !"),
-                ),
-                BlockNumber(
-                    block_number
-                        .parse()
-                        .expect("fail to parse input block_number !"),
-                ),
-            ))
-        //Ok(TransactionInput::D(10, 0, PubKey::Ed25519(ed25519::PublicKey::from_base58("FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa").unwrap(), 0)))
-        } else if let Some(caps) = T_INPUT_REGEX.captures(source) {
-            let amount = &caps["amount"];
-            let base = &caps["base"];
-            let tx_hash = &caps["tx_hash"];
-            let tx_index = &caps["tx_index"];
-            Ok(TransactionInput::T(
-                TxAmount(amount.parse().expect("fail to parse input amount")),
-                TxBase(base.parse().expect("fail to parse base amount")),
-                Hash::from_hex(tx_hash).expect("fail to parse tx_hash"),
-                OutputIndex(tx_index.parse().expect("fail to parse tx_index amount")),
-            ))
-        } else {
-            println!("Fail to parse this input = {:?}", source);
-            Err(TextDocumentParseError::InvalidInnerFormat("Transaction2"))
-        }
-    }
-}*/
-
 /// Wrap a transaction unlock proof
 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
 pub enum TransactionUnlockProof {
@@ -182,70 +76,6 @@ impl ToString for TransactionUnlockProof {
     }
 }
 
-/// Wrap a transaction unlocks input
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub struct TransactionInputUnlocks {
-    /// Input index
-    pub index: usize,
-    /// List of proof to unlock funds
-    pub unlocks: Vec<TransactionUnlockProof>,
-}
-
-impl ToString for TransactionInputUnlocks {
-    fn to_string(&self) -> String {
-        let mut result: String = format!("{}:", self.index);
-        for unlock in &self.unlocks {
-            result.push_str(&format!("{} ", unlock.to_string()));
-        }
-        let new_size = result.len() - 1;
-        result.truncate(new_size);
-        result
-    }
-}
-
-impl TransactionInputUnlocks {
-    fn from_pest_pair(pairs: Pairs<Rule>) -> TransactionInputUnlocks {
-        let mut input_index = 0;
-        let mut unlock_conds = Vec::new();
-        for unlock_field in pairs {
-            // ${ input_index ~ ":" ~ unlock_cond ~ (" " ~ unlock_cond)* }
-            match unlock_field.as_rule() {
-                Rule::input_index => input_index = unwrap!(unlock_field.as_str().parse()),
-                Rule::unlock_sig => {
-                    unlock_conds.push(TransactionUnlockProof::Sig(unwrap!(unwrap!(unlock_field
-                        .into_inner()
-                        .next())
-                    .as_str()
-                    .parse())))
-                }
-                Rule::unlock_xhx => unlock_conds.push(TransactionUnlockProof::Xhx(String::from(
-                    unwrap!(unlock_field.into_inner().next()).as_str(),
-                ))),
-                _ => fatal_error!("unexpected rule: {:?}", unlock_field.as_rule()), // Grammar ensures that we never reach this line
-            }
-        }
-        TransactionInputUnlocks {
-            index: input_index,
-            unlocks: unlock_conds,
-        }
-    }
-}
-
-impl FromStr for TransactionInputUnlocks {
-    type Err = TextDocumentParseError;
-
-    fn from_str(source: &str) -> Result<Self, Self::Err> {
-        match DocumentsParser::parse(Rule::tx_unlock, source) {
-            Ok(mut pairs) => Ok(TransactionInputUnlocks::from_pest_pair(
-                unwrap!(pairs.next()).into_inner(),
-            )),
-            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
-                "Invalid unlocks !".to_owned(),
-            )),
-        }
-    }
-}
-
 /// Wrap a transaction ouput condition
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
 pub enum TransactionOutputCondition {
@@ -351,13 +181,12 @@ impl UTXOConditionsGroup {
     utxo_conds_wrap_op_chain!(UTXOConditionsGroup::Or, new_or_chain);
 
     /// Wrap UTXO conditions
-    pub fn wrap_utxo_conds(pair: Pair<Rule>) -> UTXOConditionsGroup {
+    pub fn from_pest_pair(pair: Pair<Rule>) -> UTXOConditionsGroup {
         match pair.as_rule() {
             Rule::output_and_group => {
                 let and_pairs = pair.into_inner();
-                let mut conds_subgroups: Vec<UTXOConditionsGroup> = and_pairs
-                    .map(UTXOConditionsGroup::wrap_utxo_conds)
-                    .collect();
+                let mut conds_subgroups: Vec<UTXOConditionsGroup> =
+                    and_pairs.map(UTXOConditionsGroup::from_pest_pair).collect();
                 UTXOConditionsGroup::Brackets(Box::new(UTXOConditionsGroup::new_and_chain(
                     &mut conds_subgroups,
                 )))
@@ -365,7 +194,7 @@ impl UTXOConditionsGroup {
             Rule::output_or_group => {
                 let or_pairs = pair.into_inner();
                 let mut conds_subgroups: Vec<UTXOConditionsGroup> =
-                    or_pairs.map(UTXOConditionsGroup::wrap_utxo_conds).collect();
+                    or_pairs.map(UTXOConditionsGroup::from_pest_pair).collect();
                 UTXOConditionsGroup::Brackets(Box::new(UTXOConditionsGroup::new_or_chain(
                     &mut conds_subgroups,
                 )))
@@ -416,189 +245,37 @@ impl ToString for UTXOConditionsGroup {
     }
 }
 
-/// Wrap a transaction ouput
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub struct TransactionOutput {
-    /// Amount
-    pub amount: TxAmount,
-    /// Base
-    pub base: TxBase,
-    /// List of conditions for consum this output
-    pub conditions: UTXOConditions,
-}
-
-impl TransactionOutput {
-    /// Lightens the TransactionOutput (for example to store it while minimizing the space required)
-    fn reduce(&mut self) {
-        self.conditions.reduce()
-    }
-    /// Check validity of this output
-    pub fn check(&self) -> bool {
-        self.conditions.check()
-    }
-}
-
-impl ToString for TransactionOutput {
-    fn to_string(&self) -> String {
-        format!(
-            "{}:{}:{}",
-            self.amount.0,
-            self.base.0,
-            self.conditions.to_string()
-        )
-    }
-}
-
-impl TransactionOutput {
-    fn from_pest_pair(mut utxo_pairs: Pairs<Rule>) -> TransactionOutput {
-        let amount = TxAmount(unwrap!(unwrap!(utxo_pairs.next()).as_str().parse()));
-        let base = TxBase(unwrap!(unwrap!(utxo_pairs.next()).as_str().parse()));
-        let conditions_pairs = unwrap!(utxo_pairs.next());
-        let conditions_origin_str = conditions_pairs.as_str();
-        TransactionOutput {
-            amount,
-            base,
-            conditions: UTXOConditions {
-                origin_str: Some(String::from(conditions_origin_str)),
-                conditions: UTXOConditionsGroup::wrap_utxo_conds(conditions_pairs),
-            },
-        }
-    }
-}
-
-impl FromStr for TransactionOutput {
-    type Err = TextDocumentParseError;
-
-    fn from_str(source: &str) -> Result<Self, Self::Err> {
-        let output_parts: Vec<&str> = source.split(':').collect();
-        let amount = output_parts.get(0);
-        let base = output_parts.get(1);
-        let conditions_origin_str = output_parts.get(2);
-
-        let str_to_parse = if amount.is_some() && base.is_some() && conditions_origin_str.is_some()
-        {
-            format!(
-                "{}:{}:({})",
-                unwrap!(amount),
-                unwrap!(base),
-                unwrap!(conditions_origin_str)
-            )
-        } else {
-            source.to_owned()
-        };
-
-        match DocumentsParser::parse(Rule::tx_output, &str_to_parse) {
-            Ok(mut utxo_pairs) => {
-                let mut output =
-                    TransactionOutput::from_pest_pair(unwrap!(utxo_pairs.next()).into_inner());
-                output.conditions.origin_str = conditions_origin_str.map(ToString::to_string);
-                Ok(output)
-            }
-            Err(_) => match DocumentsParser::parse(Rule::tx_output, source) {
-                Ok(mut utxo_pairs) => {
-                    let mut output =
-                        TransactionOutput::from_pest_pair(unwrap!(utxo_pairs.next()).into_inner());
-                    output.conditions.origin_str = conditions_origin_str.map(ToString::to_string);
-                    Ok(output)
-                }
-                Err(e) => Err(TextDocumentParseError::InvalidInnerFormat(format!(
-                    "Invalid output : {}",
-                    e
-                ))),
-            },
-        }
-    }
+pub trait TransactionDocumentTrait<'a> {
+    type Input: 'a;
+    type Inputs: AsRef<[Self::Input]>;
+    type Output: 'a;
+    type Outputs: AsRef<[Self::Output]>;
+    fn get_inputs(&'a self) -> Self::Inputs;
+    fn get_outputs(&'a self) -> Self::Outputs;
 }
 
 /// Wrap a Transaction document.
 ///
 /// Must be created by parsing a text document or using a builder.
 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub struct TransactionDocument {
-    /// Document as text.
-    ///
-    /// Is used to check signatures, and other values
-    /// must be extracted from it.
-    text: Option<String>,
-
-    /// Currency.
-    currency: String,
-    /// Blockstamp
-    blockstamp: Blockstamp,
-    /// Locktime
-    locktime: u64,
-    /// Document issuer (there should be only one).
-    issuers: Vec<PubKey>,
-    /// Transaction inputs.
-    inputs: Vec<TransactionInput>,
-    /// Inputs unlocks.
-    unlocks: Vec<TransactionInputUnlocks>,
-    /// Transaction outputs.
-    outputs: Vec<TransactionOutput>,
-    /// Transaction comment
-    comment: String,
-    /// Document signature (there should be only one).
-    signatures: Vec<Sig>,
-    /// Transaction hash
-    hash: Option<Hash>,
+pub enum TransactionDocument {
+    V10(TransactionDocumentV10),
 }
 
 #[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
 /// Transaction document stringifed
-pub struct TransactionDocumentStringified {
-    /// Currency.
-    pub currency: String,
-    /// Blockstamp
-    pub blockstamp: String,
-    /// Locktime
-    pub locktime: u64,
-    /// Document issuer (there should be only one).
-    pub issuers: Vec<String>,
-    /// Transaction inputs.
-    pub inputs: Vec<String>,
-    /// Inputs unlocks.
-    pub unlocks: Vec<String>,
-    /// Transaction outputs.
-    pub outputs: Vec<String>,
-    /// Transaction comment
-    pub comment: String,
-    /// Document signatures
-    pub signatures: Vec<String>,
-    /// Transaction hash
-    pub hash: Option<String>,
+pub enum TransactionDocumentStringified {
+    V10(TransactionDocumentV10Stringified),
 }
 
 impl ToStringObject for TransactionDocument {
     type StringObject = TransactionDocumentStringified;
 
     fn to_string_object(&self) -> TransactionDocumentStringified {
-        TransactionDocumentStringified {
-            currency: self.currency.clone(),
-            blockstamp: format!("{}", self.blockstamp),
-            locktime: self.locktime,
-            issuers: self.issuers.iter().map(|p| format!("{}", p)).collect(),
-            inputs: self
-                .inputs
-                .iter()
-                .map(TransactionInput::to_string)
-                .collect(),
-            unlocks: self
-                .unlocks
-                .iter()
-                .map(TransactionInputUnlocks::to_string)
-                .collect(),
-            outputs: self
-                .outputs
-                .iter()
-                .map(TransactionOutput::to_string)
-                .collect(),
-            comment: self.comment.clone(),
-            signatures: self.signatures.iter().map(|s| format!("{}", s)).collect(),
-            hash: if let Some(hash) = self.hash {
-                Some(hash.to_string())
-            } else {
-                None
-            },
+        match self {
+            TransactionDocument::V10(tx_v10) => {
+                TransactionDocumentStringified::V10(tx_v10.to_string_object())
+            }
         }
     }
 }
@@ -606,109 +283,28 @@ impl ToStringObject for TransactionDocument {
 impl TransactionDocument {
     /// Compute transaction hash
     pub fn compute_hash(&self) -> Hash {
-        let mut hashing_text = if let Some(ref text) = self.text {
-            text.clone()
-        } else {
-            fatal_error!("Try to compute_hash of tx with None text !")
-        };
-        for sig in &self.signatures {
-            hashing_text.push_str(&sig.to_string());
-            hashing_text.push_str("\n");
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.compute_hash(),
         }
-        //println!("tx_text_hasing={}", hashing_text);
-        Hash::compute_str(&hashing_text)
     }
     /// get transaction hash option
     pub fn get_hash_opt(&self) -> Option<Hash> {
-        self.hash
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.get_hash_opt(),
+        }
     }
     /// Get transaction hash
     pub fn get_hash(&mut self) -> Hash {
-        if let Some(hash) = self.hash {
-            hash
-        } else {
-            self.hash = Some(self.compute_hash());
-            self.hash.expect("unreach")
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.get_hash(),
         }
     }
-    /// Get transaction inputs
-    pub fn get_inputs(&self) -> &[TransactionInput] {
-        &self.inputs
-    }
-    /// Get transaction outputs
-    pub fn get_outputs(&self) -> &[TransactionOutput] {
-        &self.outputs
-    }
     /// Lightens the transaction (for example to store it while minimizing the space required)
     /// WARNING: do not remove the hash as it's necessary to reverse the transaction !
     pub fn reduce(&mut self) {
-        self.hash = Some(self.compute_hash());
-        self.text = None;
-        for output in &mut self.outputs {
-            output.reduce()
-        }
-    }
-    /// from pest parser pair
-    pub fn from_pest_pair(pair: Pair<Rule>) -> Result<TransactionDocument, TextDocumentParseError> {
-        let doc = pair.as_str();
-        let mut currency = "";
-        let mut blockstamp = Blockstamp::default();
-        let mut locktime = 0;
-        let mut issuers = Vec::new();
-        let mut inputs = Vec::new();
-        let mut unlocks = Vec::new();
-        let mut outputs = Vec::new();
-        let mut comment = "";
-        let mut sigs = Vec::new();
-
-        for field in pair.into_inner() {
-            match field.as_rule() {
-                Rule::currency => currency = field.as_str(),
-                Rule::blockstamp => {
-                    let mut inner_rules = field.into_inner(); // ${ block_id ~ "-" ~ hash }
-
-                    let block_id: &str = unwrap!(inner_rules.next()).as_str();
-                    let block_hash: &str = unwrap!(inner_rules.next()).as_str();
-                    blockstamp = Blockstamp {
-                        id: BlockNumber(unwrap!(block_id.parse())), // Grammar ensures that we have a digits string.
-                        hash: BlockHash(unwrap!(Hash::from_hex(block_hash))), // Grammar ensures that we have an hexadecimal string.
-                    };
-                }
-                Rule::tx_locktime => locktime = unwrap!(field.as_str().parse()), // Grammar ensures that we have digits characters.
-                Rule::pubkey => issuers.push(PubKey::Ed25519(
-                    unwrap!(ed25519::PublicKey::from_base58(field.as_str())), // Grammar ensures that we have a base58 string.
-                )),
-                Rule::tx_input => inputs.push(TransactionInput::from_pest_pair(field.into_inner())),
-                Rule::tx_unlock => {
-                    unlocks.push(TransactionInputUnlocks::from_pest_pair(field.into_inner()))
-                }
-                Rule::tx_output => {
-                    outputs.push(TransactionOutput::from_pest_pair(field.into_inner()))
-                }
-                Rule::tx_comment => comment = field.as_str(),
-                Rule::ed25519_sig => {
-                    sigs.push(Sig::Ed25519(
-                        unwrap!(ed25519::Signature::from_base64(field.as_str())), // Grammar ensures that we have a base64 string.
-                    ));
-                }
-                Rule::EOI => (),
-                _ => fatal_error!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
-            }
-        }
-
-        Ok(TransactionDocument {
-            text: Some(doc.to_owned()),
-            currency: currency.to_owned(),
-            blockstamp,
-            locktime,
-            issuers,
-            inputs,
-            unlocks,
-            outputs,
-            comment: comment.to_owned(),
-            signatures: sigs,
-            hash: None,
-        })
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.reduce(),
+        };
     }
 }
 
@@ -716,80 +312,47 @@ impl Document for TransactionDocument {
     type PublicKey = PubKey;
 
     fn version(&self) -> usize {
-        10
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.version(),
+        }
     }
 
     fn currency(&self) -> &str {
-        &self.currency
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.currency(),
+        }
     }
 
     fn blockstamp(&self) -> Blockstamp {
-        self.blockstamp
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.blockstamp(),
+        }
     }
 
     fn issuers(&self) -> &Vec<PubKey> {
-        &self.issuers
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.issuers(),
+        }
     }
 
     fn signatures(&self) -> &Vec<Sig> {
-        &self.signatures
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.signatures(),
+        }
     }
 
     fn as_bytes(&self) -> &[u8] {
-        self.as_text_without_signature().as_bytes()
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.as_bytes(),
+        }
     }
 }
 
 impl CompactTextDocument for TransactionDocument {
     fn as_compact_text(&self) -> String {
-        let mut issuers_str = String::from("");
-        for issuer in self.issuers.clone() {
-            issuers_str.push_str("\n");
-            issuers_str.push_str(&issuer.to_string());
-        }
-        let mut inputs_str = String::from("");
-        for input in self.inputs.clone() {
-            inputs_str.push_str("\n");
-            inputs_str.push_str(&input.to_string());
-        }
-        let mut unlocks_str = String::from("");
-        for unlock in self.unlocks.clone() {
-            unlocks_str.push_str("\n");
-            unlocks_str.push_str(&unlock.to_string());
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.as_compact_text(),
         }
-        let mut outputs_str = String::from("");
-        for output in self.outputs.clone() {
-            outputs_str.push_str("\n");
-            outputs_str.push_str(&output.to_string());
-        }
-        let mut comment_str = self.comment.clone();
-        if !comment_str.is_empty() {
-            comment_str.push_str("\n");
-        }
-        let mut signatures_str = String::from("");
-        for sig in self.signatures.clone() {
-            signatures_str.push_str(&sig.to_string());
-            signatures_str.push_str("\n");
-        }
-        // Remove end line step
-        signatures_str.pop();
-        format!(
-            "TX:10:{issuers_count}:{inputs_count}:{unlocks_count}:{outputs_count}:{has_comment}:{locktime}
-{blockstamp}{issuers}{inputs}{unlocks}{outputs}\n{comment}{signatures}",
-            issuers_count = self.issuers.len(),
-            inputs_count = self.inputs.len(),
-            unlocks_count = self.unlocks.len(),
-            outputs_count = self.outputs.len(),
-            has_comment = if self.comment.is_empty() { 0 } else { 1 },
-            locktime = self.locktime,
-            blockstamp = self.blockstamp,
-            issuers = issuers_str,
-            inputs = inputs_str,
-            unlocks = unlocks_str,
-            outputs = outputs_str,
-            comment = comment_str,
-            signatures = signatures_str,
-        )
     }
 }
 
@@ -797,10 +360,8 @@ impl TextDocument for TransactionDocument {
     type CompactTextDocument_ = TransactionDocument;
 
     fn as_text(&self) -> &str {
-        if let Some(ref text) = self.text {
-            text
-        } else {
-            fatal_error!("Try to get text of tx with None text !")
+        match self {
+            TransactionDocument::V10(tx_v10) => tx_v10.as_text(),
         }
     }
 
@@ -811,43 +372,8 @@ impl TextDocument for TransactionDocument {
 
 /// Transaction document builder.
 #[derive(Debug, Copy, Clone)]
-pub struct TransactionDocumentBuilder<'a> {
-    /// Document currency.
-    pub currency: &'a str,
-    /// Reference blockstamp.
-    pub blockstamp: &'a Blockstamp,
-    /// Locktime
-    pub locktime: &'a u64,
-    /// Transaction Document issuers.
-    pub issuers: &'a Vec<PubKey>,
-    /// Transaction inputs.
-    pub inputs: &'a Vec<TransactionInput>,
-    /// Inputs unlocks.
-    pub unlocks: &'a Vec<TransactionInputUnlocks>,
-    /// Transaction ouputs.
-    pub outputs: &'a Vec<TransactionOutput>,
-    /// Transaction comment
-    pub comment: &'a str,
-    /// Transaction hash
-    pub hash: Option<Hash>,
-}
-
-impl<'a> TransactionDocumentBuilder<'a> {
-    fn build_with_text_and_sigs(self, text: String, signatures: Vec<Sig>) -> TransactionDocument {
-        TransactionDocument {
-            text: Some(text),
-            currency: self.currency.to_string(),
-            blockstamp: *self.blockstamp,
-            locktime: *self.locktime,
-            issuers: self.issuers.clone(),
-            inputs: self.inputs.clone(),
-            unlocks: self.unlocks.clone(),
-            outputs: self.outputs.clone(),
-            comment: String::from(self.comment),
-            signatures,
-            hash: self.hash,
-        }
-    }
+pub enum TransactionDocumentBuilder<'a> {
+    V10(TransactionDocumentV10Builder<'a>),
 }
 
 impl<'a> DocumentBuilder for TransactionDocumentBuilder<'a> {
@@ -855,60 +381,34 @@ impl<'a> DocumentBuilder for TransactionDocumentBuilder<'a> {
     type Signator = SignatorEnum;
 
     fn build_with_signature(&self, signatures: Vec<Sig>) -> TransactionDocument {
-        self.build_with_text_and_sigs(self.generate_text(), signatures)
+        match self {
+            TransactionDocumentBuilder::V10(tx_v10_builder) => {
+                TransactionDocument::V10(tx_v10_builder.build_with_signature(signatures))
+            }
+        }
     }
-
     fn build_and_sign(&self, private_keys: Vec<SignatorEnum>) -> TransactionDocument {
-        let (text, signatures) = self.build_signed_text(private_keys);
-        self.build_with_text_and_sigs(text, signatures)
+        match self {
+            TransactionDocumentBuilder::V10(tx_v10_builder) => {
+                TransactionDocument::V10(tx_v10_builder.build_and_sign(private_keys))
+            }
+        }
     }
 }
 
 impl<'a> TextDocumentBuilder for TransactionDocumentBuilder<'a> {
     fn generate_text(&self) -> String {
-        let mut issuers_string: String = "".to_owned();
-        let mut inputs_string: String = "".to_owned();
-        let mut unlocks_string: String = "".to_owned();
-        let mut outputs_string: String = "".to_owned();
-        for issuer in self.issuers {
-            issuers_string.push_str(&format!("{}\n", issuer.to_string()))
-        }
-        for input in self.inputs {
-            inputs_string.push_str(&format!("{}\n", input.to_string()))
-        }
-        for unlock in self.unlocks {
-            unlocks_string.push_str(&format!("{}\n", unlock.to_string()))
-        }
-        for output in self.outputs {
-            outputs_string.push_str(&format!("{}\n", output.to_string()))
+        match self {
+            TransactionDocumentBuilder::V10(tx_v10_builder) => tx_v10_builder.generate_text(),
         }
-        format!(
-            "Version: 10
-Type: Transaction
-Currency: {currency}
-Blockstamp: {blockstamp}
-Locktime: {locktime}
-Issuers:
-{issuers}Inputs:
-{inputs}Unlocks:
-{unlocks}Outputs:
-{outputs}Comment: {comment}
-",
-            currency = self.currency,
-            blockstamp = self.blockstamp,
-            locktime = self.locktime,
-            issuers = issuers_string,
-            inputs = inputs_string,
-            unlocks = unlocks_string,
-            outputs = outputs_string,
-            comment = self.comment,
-        )
     }
 }
 
 /// Transaction document parser
 #[derive(Debug, Clone, Copy)]
-pub struct TransactionDocumentParser;
+pub enum TransactionDocumentParser {
+    V10(TransactionDocumentV10Parser),
+}
 
 impl TextDocumentParser<Rule> for TransactionDocumentParser {
     type DocumentType = TransactionDocument;
@@ -923,7 +423,9 @@ impl TextDocumentParser<Rule> for TransactionDocumentParser {
         let tx_vx_pair = unwrap!(pair.into_inner().next()); // get and unwrap the `tx_vX` rule; never fails
 
         match tx_vx_pair.as_rule() {
-            Rule::tx_v10 => TransactionDocument::from_pest_pair(tx_vx_pair),
+            Rule::tx_v10 => {
+                TransactionDocumentV10::from_pest_pair(tx_vx_pair).map(TransactionDocument::V10)
+            }
             _ => Err(TextDocumentParseError::UnexpectedRule(format!(
                 "{:#?}",
                 tx_vx_pair.as_rule()
@@ -936,7 +438,8 @@ impl TextDocumentParser<Rule> for TransactionDocumentParser {
         pair: Pair<Rule>,
     ) -> Result<Self::DocumentType, TextDocumentParseError> {
         match version {
-            10 => Ok(TransactionDocument::from_pest_pair(pair)?),
+            10 => TransactionDocumentV10Parser::from_versioned_pest_pair(version, pair)
+                .map(TransactionDocument::V10),
             v => Err(TextDocumentParseError::UnexpectedVersion(format!(
                 "Unsupported version: {}",
                 v
@@ -949,6 +452,9 @@ impl TextDocumentParser<Rule> for TransactionDocumentParser {
 mod tests {
     use super::*;
     use dubp_common_doc::traits::Document;
+    use dubp_common_doc::BlockNumber;
+    use std::str::FromStr;
+    use v10::{TransactionInputUnlocksV10, TransactionOutputV10};
 
     #[test]
     fn generate_real_document() {
@@ -971,12 +477,12 @@ mod tests {
             "Fail to parse blockstamp"
         );
 
-        let builder = TransactionDocumentBuilder {
+        let builder = TransactionDocumentV10Builder {
             currency: "duniter_unit_test_currency",
             blockstamp: &block,
             locktime: &0,
             issuers: &vec![pubkey],
-            inputs: &vec![TransactionInput::D(
+            inputs: &vec![TransactionInputV10::D(
                 TxAmount(10),
                 TxBase(0),
                 PubKey::Ed25519(unwrap!(
@@ -985,25 +491,17 @@ mod tests {
                 )),
                 BlockNumber(0),
             )],
-            unlocks: &vec![TransactionInputUnlocks {
+            unlocks: &vec![TransactionInputUnlocksV10 {
                 index: 0,
                 unlocks: vec![TransactionUnlockProof::Sig(0)],
             }],
-            outputs: &vec![TransactionOutput::from_str(
+            outputs: &vec![TransactionOutputV10::from_str(
                 "10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)",
             )
             .expect("fail to parse output !")],
             comment: "test",
             hash: None,
         };
-        /*println!(
-            "Signatures = {:?}",
-            builder
-                .build_and_sign(vec![SignatorEnum::Ed25519(
-                    keypair.generate_signator().expect("fail to gen signator")
-                )])
-                .signatures()
-        );*/
         assert!(builder
             .build_with_signature(vec![sig])
             .verify_signatures()
@@ -1032,12 +530,12 @@ mod tests {
             "Fail to parse Blockstamp"
         );
 
-        let builder = TransactionDocumentBuilder {
+        let builder = TransactionDocumentV10Builder {
             currency: "g1",
             blockstamp: &block,
             locktime: &0,
             issuers: &vec![pubkey],
-            inputs: &vec![TransactionInput::T(
+            inputs: &vec![TransactionInputV10::T(
                 TxAmount(950),
                 TxBase(0),
                 unwrap!(
@@ -1049,14 +547,14 @@ mod tests {
                 OutputIndex(1),
             )],
             unlocks: &vec![
-                TransactionInputUnlocks::from_str("0:SIG(0)").expect("fail to parse unlock !")
+                TransactionInputUnlocksV10::from_str("0:SIG(0)").expect("fail to parse unlock !")
             ],
             outputs: &vec![
-                TransactionOutput::from_str(
+                TransactionOutputV10::from_str(
                     "30:0:SIG(38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE)",
                 )
                 .expect("fail to parse output !"),
-                TransactionOutput::from_str(
+                TransactionOutputV10::from_str(
                     "920:0:SIG(FEkbc4BfJukSWnCU6Hed6dgwwTuPFTVdgz5LpL4iHr9J)",
                 )
                 .expect("fail to parse output !"),
@@ -1064,9 +562,9 @@ mod tests {
             comment: "Pour cesium merci",
             hash: None,
         };
-        let mut tx_doc = builder.build_with_signature(vec![sig]);
-        tx_doc.hash = None;
+        let mut tx_doc = TransactionDocument::V10(builder.build_with_signature(vec![sig]));
         assert!(tx_doc.verify_signatures().is_ok());
+        assert!(tx_doc.get_hash_opt().is_none());
         assert_eq!(
             tx_doc.get_hash(),
             Hash::from_hex("876D2430E0B66E2CE4467866D8F923D68896CACD6AA49CDD8BDD0096B834DEF1")
diff --git a/lib/dubp/user-docs/src/documents/transaction/v10.rs b/lib/dubp/user-docs/src/documents/transaction/v10.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9f1c7666ccf4a9680c973e6ec41026ab24a14f15
--- /dev/null
+++ b/lib/dubp/user-docs/src/documents/transaction/v10.rs
@@ -0,0 +1,922 @@
+//  Copyright (C) 2017-2019  The AXIOM TEAM Association.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Wrappers around Transaction documents.
+
+use crate::documents::*;
+use dubp_common_doc::blockstamp::Blockstamp;
+use dubp_common_doc::parser::{DocumentsParser, TextDocumentParseError, TextDocumentParser};
+use dubp_common_doc::traits::text::*;
+use dubp_common_doc::traits::{Document, DocumentBuilder, ToStringObject};
+use dubp_common_doc::{BlockHash, BlockNumber};
+use dup_crypto::hashs::*;
+use dup_crypto::keys::*;
+use durs_common_tools::fatal_error;
+use pest::iterators::Pair;
+use pest::iterators::Pairs;
+use pest::Parser;
+use std::str::FromStr;
+use unwrap::unwrap;
+
+/// Wrap a transaction input
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub enum TransactionInputV10 {
+    /// Universal Dividend Input
+    D(TxAmount, TxBase, PubKey, BlockNumber),
+    /// Previous Transaction Input
+    T(TxAmount, TxBase, Hash, OutputIndex),
+}
+
+impl ToString for TransactionInputV10 {
+    fn to_string(&self) -> String {
+        match *self {
+            TransactionInputV10::D(amount, base, pubkey, block_number) => {
+                format!("{}:{}:D:{}:{}", amount.0, base.0, pubkey, block_number.0)
+            }
+            TransactionInputV10::T(amount, base, ref tx_hash, tx_index) => {
+                format!("{}:{}:T:{}:{}", amount.0, base.0, tx_hash, tx_index.0)
+            }
+        }
+    }
+}
+
+impl TransactionInputV10 {
+    fn from_pest_pair(mut pairs: Pairs<Rule>) -> TransactionInputV10 {
+        let tx_input_type_pair = unwrap!(pairs.next());
+        match tx_input_type_pair.as_rule() {
+            Rule::tx_input_du => {
+                let mut inner_rules = tx_input_type_pair.into_inner(); // ${ tx_amount ~ ":" ~ tx_amount_base ~ ":D:" ~ pubkey ~ ":" ~ du_block_id }
+
+                TransactionInputV10::D(
+                    TxAmount(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
+                    TxBase(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
+                    PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        unwrap!(inner_rules.next()).as_str()
+                    ))),
+                    BlockNumber(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
+                )
+            }
+            Rule::tx_input_tx => {
+                let mut inner_rules = tx_input_type_pair.into_inner(); // ${ tx_amount ~ ":" ~ tx_amount_base ~ ":D:" ~ pubkey ~ ":" ~ du_block_id }
+
+                TransactionInputV10::T(
+                    TxAmount(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
+                    TxBase(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
+                    unwrap!(Hash::from_hex(unwrap!(inner_rules.next()).as_str())),
+                    OutputIndex(unwrap!(unwrap!(inner_rules.next()).as_str().parse())),
+                )
+            }
+            _ => fatal_error!("unexpected rule: {:?}", tx_input_type_pair.as_rule()), // Grammar ensures that we never reach this line
+        }
+    }
+}
+
+impl FromStr for TransactionInputV10 {
+    type Err = TextDocumentParseError;
+
+    fn from_str(source: &str) -> Result<Self, Self::Err> {
+        match DocumentsParser::parse(Rule::tx_input, source) {
+            Ok(mut pairs) => Ok(TransactionInputV10::from_pest_pair(
+                unwrap!(pairs.next()).into_inner(),
+            )),
+            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
+                "Invalid unlocks !".to_owned(),
+            )),
+        }
+    }
+}
+
+impl<'a> TransactionDocumentTrait<'a> for TransactionDocumentV10 {
+    type Input = TransactionInputV10;
+    type Inputs = &'a [TransactionInputV10];
+    type Output = TransactionOutputV10;
+    type Outputs = &'a [TransactionOutputV10];
+    fn get_inputs(&'a self) -> Self::Inputs {
+        &self.inputs
+    }
+    fn get_outputs(&'a self) -> Self::Outputs {
+        &self.outputs
+    }
+}
+
+/// Wrap a transaction unlocks input
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct TransactionInputUnlocksV10 {
+    /// Input index
+    pub index: usize,
+    /// List of proof to unlock funds
+    pub unlocks: Vec<TransactionUnlockProof>,
+}
+
+impl ToString for TransactionInputUnlocksV10 {
+    fn to_string(&self) -> String {
+        let mut result: String = format!("{}:", self.index);
+        for unlock in &self.unlocks {
+            result.push_str(&format!("{} ", unlock.to_string()));
+        }
+        let new_size = result.len() - 1;
+        result.truncate(new_size);
+        result
+    }
+}
+
+impl TransactionInputUnlocksV10 {
+    fn from_pest_pair(pairs: Pairs<Rule>) -> TransactionInputUnlocksV10 {
+        let mut input_index = 0;
+        let mut unlock_conds = Vec::new();
+        for unlock_field in pairs {
+            // ${ input_index ~ ":" ~ unlock_cond ~ (" " ~ unlock_cond)* }
+            match unlock_field.as_rule() {
+                Rule::input_index => input_index = unwrap!(unlock_field.as_str().parse()),
+                Rule::unlock_sig => {
+                    unlock_conds.push(TransactionUnlockProof::Sig(unwrap!(unwrap!(unlock_field
+                        .into_inner()
+                        .next())
+                    .as_str()
+                    .parse())))
+                }
+                Rule::unlock_xhx => unlock_conds.push(TransactionUnlockProof::Xhx(String::from(
+                    unwrap!(unlock_field.into_inner().next()).as_str(),
+                ))),
+                _ => fatal_error!("unexpected rule: {:?}", unlock_field.as_rule()), // Grammar ensures that we never reach this line
+            }
+        }
+        TransactionInputUnlocksV10 {
+            index: input_index,
+            unlocks: unlock_conds,
+        }
+    }
+}
+
+impl FromStr for TransactionInputUnlocksV10 {
+    type Err = TextDocumentParseError;
+
+    fn from_str(source: &str) -> Result<Self, Self::Err> {
+        match DocumentsParser::parse(Rule::tx_unlock, source) {
+            Ok(mut pairs) => Ok(TransactionInputUnlocksV10::from_pest_pair(
+                unwrap!(pairs.next()).into_inner(),
+            )),
+            Err(_) => Err(TextDocumentParseError::InvalidInnerFormat(
+                "Invalid unlocks !".to_owned(),
+            )),
+        }
+    }
+}
+
+/// Wrap a transaction ouput
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct TransactionOutputV10 {
+    /// Amount
+    pub amount: TxAmount,
+    /// Base
+    pub base: TxBase,
+    /// List of conditions for consum this output
+    pub conditions: UTXOConditions,
+}
+
+impl TransactionOutputV10 {
+    /// Lightens the TransactionOutputV10 (for example to store it while minimizing the space required)
+    fn reduce(&mut self) {
+        self.conditions.reduce()
+    }
+    /// Check validity of this output
+    pub fn check(&self) -> bool {
+        self.conditions.check()
+    }
+}
+
+impl ToString for TransactionOutputV10 {
+    fn to_string(&self) -> String {
+        format!(
+            "{}:{}:{}",
+            self.amount.0,
+            self.base.0,
+            self.conditions.to_string()
+        )
+    }
+}
+
+impl TransactionOutputV10 {
+    fn from_pest_pair(mut utxo_pairs: Pairs<Rule>) -> TransactionOutputV10 {
+        let amount = TxAmount(unwrap!(unwrap!(utxo_pairs.next()).as_str().parse()));
+        let base = TxBase(unwrap!(unwrap!(utxo_pairs.next()).as_str().parse()));
+        let conditions_pairs = unwrap!(utxo_pairs.next());
+        let conditions_origin_str = conditions_pairs.as_str();
+        TransactionOutputV10 {
+            amount,
+            base,
+            conditions: UTXOConditions {
+                origin_str: Some(String::from(conditions_origin_str)),
+                conditions: UTXOConditionsGroup::from_pest_pair(conditions_pairs),
+            },
+        }
+    }
+}
+
+impl FromStr for TransactionOutputV10 {
+    type Err = TextDocumentParseError;
+
+    fn from_str(source: &str) -> Result<Self, Self::Err> {
+        let output_parts: Vec<&str> = source.split(':').collect();
+        let amount = output_parts.get(0);
+        let base = output_parts.get(1);
+        let conditions_origin_str = output_parts.get(2);
+
+        let str_to_parse = if amount.is_some() && base.is_some() && conditions_origin_str.is_some()
+        {
+            format!(
+                "{}:{}:({})",
+                unwrap!(amount),
+                unwrap!(base),
+                unwrap!(conditions_origin_str)
+            )
+        } else {
+            source.to_owned()
+        };
+
+        match DocumentsParser::parse(Rule::tx_output, &str_to_parse) {
+            Ok(mut utxo_pairs) => {
+                let mut output =
+                    TransactionOutputV10::from_pest_pair(unwrap!(utxo_pairs.next()).into_inner());
+                output.conditions.origin_str = conditions_origin_str.map(ToString::to_string);
+                Ok(output)
+            }
+            Err(_) => match DocumentsParser::parse(Rule::tx_output, source) {
+                Ok(mut utxo_pairs) => {
+                    let mut output = TransactionOutputV10::from_pest_pair(
+                        unwrap!(utxo_pairs.next()).into_inner(),
+                    );
+                    output.conditions.origin_str = conditions_origin_str.map(ToString::to_string);
+                    Ok(output)
+                }
+                Err(e) => Err(TextDocumentParseError::InvalidInnerFormat(format!(
+                    "Invalid output : {}",
+                    e
+                ))),
+            },
+        }
+    }
+}
+
+/// Wrap a Transaction document.
+///
+/// Must be created by parsing a text document or using a builder.
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct TransactionDocumentV10 {
+    /// Document as text.
+    ///
+    /// Is used to check signatures, and other values
+    /// must be extracted from it.
+    text: Option<String>,
+
+    /// Currency.
+    currency: String,
+    /// Blockstamp
+    blockstamp: Blockstamp,
+    /// Locktime
+    locktime: u64,
+    /// Document issuers.
+    issuers: Vec<PubKey>,
+    /// Transaction inputs.
+    inputs: Vec<TransactionInputV10>,
+    /// Inputs unlocks.
+    unlocks: Vec<TransactionInputUnlocksV10>,
+    /// Transaction outputs.
+    outputs: Vec<TransactionOutputV10>,
+    /// Transaction comment
+    comment: String,
+    /// Document signatures.
+    signatures: Vec<Sig>,
+    /// Transaction hash
+    hash: Option<Hash>,
+}
+
+#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
+/// Transaction document stringifed
+pub struct TransactionDocumentV10Stringified {
+    /// Currency.
+    pub currency: String,
+    /// Blockstamp
+    pub blockstamp: String,
+    /// Locktime
+    pub locktime: u64,
+    /// Document issuers.
+    pub issuers: Vec<String>,
+    /// Transaction inputs.
+    pub inputs: Vec<String>,
+    /// Inputs unlocks.
+    pub unlocks: Vec<String>,
+    /// Transaction outputs.
+    pub outputs: Vec<String>,
+    /// Transaction comment
+    pub comment: String,
+    /// Document signatures
+    pub signatures: Vec<String>,
+    /// Transaction hash
+    pub hash: Option<String>,
+}
+
+impl ToStringObject for TransactionDocumentV10 {
+    type StringObject = TransactionDocumentV10Stringified;
+
+    fn to_string_object(&self) -> TransactionDocumentV10Stringified {
+        TransactionDocumentV10Stringified {
+            currency: self.currency.clone(),
+            blockstamp: format!("{}", self.blockstamp),
+            locktime: self.locktime,
+            issuers: self.issuers.iter().map(|p| format!("{}", p)).collect(),
+            inputs: self
+                .inputs
+                .iter()
+                .map(TransactionInputV10::to_string)
+                .collect(),
+            unlocks: self
+                .unlocks
+                .iter()
+                .map(TransactionInputUnlocksV10::to_string)
+                .collect(),
+            outputs: self
+                .outputs
+                .iter()
+                .map(TransactionOutputV10::to_string)
+                .collect(),
+            comment: self.comment.clone(),
+            signatures: self.signatures.iter().map(|s| format!("{}", s)).collect(),
+            hash: if let Some(hash) = self.hash {
+                Some(hash.to_string())
+            } else {
+                None
+            },
+        }
+    }
+}
+
+impl TransactionDocumentV10 {
+    /// Compute transaction hash
+    pub fn compute_hash(&self) -> Hash {
+        let mut hashing_text = if let Some(ref text) = self.text {
+            text.clone()
+        } else {
+            fatal_error!("Try to compute_hash of tx with None text !")
+        };
+        for sig in &self.signatures {
+            hashing_text.push_str(&sig.to_string());
+            hashing_text.push_str("\n");
+        }
+        Hash::compute_str(&hashing_text)
+    }
+    /// get transaction hash option
+    pub fn get_hash_opt(&self) -> Option<Hash> {
+        self.hash
+    }
+    /// Get transaction hash
+    pub fn get_hash(&mut self) -> Hash {
+        if let Some(hash) = self.hash {
+            hash
+        } else {
+            self.hash = Some(self.compute_hash());
+            self.hash.expect("unreach")
+        }
+    }
+    /// Lightens the transaction (for example to store it while minimizing the space required)
+    /// WARNING: do not remove the hash as it's necessary to reverse the transaction !
+    pub fn reduce(&mut self) {
+        self.hash = Some(self.compute_hash());
+        self.text = None;
+        for output in &mut self.outputs {
+            output.reduce()
+        }
+    }
+    /// from pest parser pair
+    pub fn from_pest_pair(
+        pair: Pair<Rule>,
+    ) -> Result<TransactionDocumentV10, TextDocumentParseError> {
+        let doc = pair.as_str();
+        let mut currency = "";
+        let mut blockstamp = Blockstamp::default();
+        let mut locktime = 0;
+        let mut issuers = Vec::new();
+        let mut inputs = Vec::new();
+        let mut unlocks = Vec::new();
+        let mut outputs = Vec::new();
+        let mut comment = "";
+        let mut sigs = Vec::new();
+
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency = field.as_str(),
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // ${ block_id ~ "-" ~ hash }
+
+                    let block_id: &str = unwrap!(inner_rules.next()).as_str();
+                    let block_hash: &str = unwrap!(inner_rules.next()).as_str();
+                    blockstamp = Blockstamp {
+                        id: BlockNumber(unwrap!(block_id.parse())), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(unwrap!(Hash::from_hex(block_hash))), // Grammar ensures that we have an hexadecimal string.
+                    };
+                }
+                Rule::tx_locktime => locktime = unwrap!(field.as_str().parse()), // Grammar ensures that we have digits characters.
+                Rule::pubkey => issuers.push(PubKey::Ed25519(
+                    unwrap!(ed25519::PublicKey::from_base58(field.as_str())), // Grammar ensures that we have a base58 string.
+                )),
+                Rule::tx_input => {
+                    inputs.push(TransactionInputV10::from_pest_pair(field.into_inner()))
+                }
+                Rule::tx_unlock => unlocks.push(TransactionInputUnlocksV10::from_pest_pair(
+                    field.into_inner(),
+                )),
+                Rule::tx_output => {
+                    outputs.push(TransactionOutputV10::from_pest_pair(field.into_inner()))
+                }
+                Rule::tx_comment => comment = field.as_str(),
+                Rule::ed25519_sig => {
+                    sigs.push(Sig::Ed25519(
+                        unwrap!(ed25519::Signature::from_base64(field.as_str())), // Grammar ensures that we have a base64 string.
+                    ));
+                }
+                Rule::EOI => (),
+                _ => fatal_error!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
+            }
+        }
+
+        Ok(TransactionDocumentV10 {
+            text: Some(doc.to_owned()),
+            currency: currency.to_owned(),
+            blockstamp,
+            locktime,
+            issuers,
+            inputs,
+            unlocks,
+            outputs,
+            comment: comment.to_owned(),
+            signatures: sigs,
+            hash: None,
+        })
+    }
+}
+
+impl Document for TransactionDocumentV10 {
+    type PublicKey = PubKey;
+
+    fn version(&self) -> usize {
+        10
+    }
+
+    fn currency(&self) -> &str {
+        &self.currency
+    }
+
+    fn blockstamp(&self) -> Blockstamp {
+        self.blockstamp
+    }
+
+    fn issuers(&self) -> &Vec<PubKey> {
+        &self.issuers
+    }
+
+    fn signatures(&self) -> &Vec<Sig> {
+        &self.signatures
+    }
+
+    fn as_bytes(&self) -> &[u8] {
+        self.as_text_without_signature().as_bytes()
+    }
+}
+
+impl CompactTextDocument for TransactionDocumentV10 {
+    fn as_compact_text(&self) -> String {
+        let mut issuers_str = String::from("");
+        for issuer in self.issuers.clone() {
+            issuers_str.push_str("\n");
+            issuers_str.push_str(&issuer.to_string());
+        }
+        let mut inputs_str = String::from("");
+        for input in self.inputs.clone() {
+            inputs_str.push_str("\n");
+            inputs_str.push_str(&input.to_string());
+        }
+        let mut unlocks_str = String::from("");
+        for unlock in self.unlocks.clone() {
+            unlocks_str.push_str("\n");
+            unlocks_str.push_str(&unlock.to_string());
+        }
+        let mut outputs_str = String::from("");
+        for output in self.outputs.clone() {
+            outputs_str.push_str("\n");
+            outputs_str.push_str(&output.to_string());
+        }
+        let mut comment_str = self.comment.clone();
+        if !comment_str.is_empty() {
+            comment_str.push_str("\n");
+        }
+        let mut signatures_str = String::from("");
+        for sig in self.signatures.clone() {
+            signatures_str.push_str(&sig.to_string());
+            signatures_str.push_str("\n");
+        }
+        // Remove end line step
+        signatures_str.pop();
+        format!(
+            "TX:10:{issuers_count}:{inputs_count}:{unlocks_count}:{outputs_count}:{has_comment}:{locktime}
+{blockstamp}{issuers}{inputs}{unlocks}{outputs}\n{comment}{signatures}",
+            issuers_count = self.issuers.len(),
+            inputs_count = self.inputs.len(),
+            unlocks_count = self.unlocks.len(),
+            outputs_count = self.outputs.len(),
+            has_comment = if self.comment.is_empty() { 0 } else { 1 },
+            locktime = self.locktime,
+            blockstamp = self.blockstamp,
+            issuers = issuers_str,
+            inputs = inputs_str,
+            unlocks = unlocks_str,
+            outputs = outputs_str,
+            comment = comment_str,
+            signatures = signatures_str,
+        )
+    }
+}
+
+impl TextDocument for TransactionDocumentV10 {
+    type CompactTextDocument_ = TransactionDocumentV10;
+
+    fn as_text(&self) -> &str {
+        if let Some(ref text) = self.text {
+            text
+        } else {
+            fatal_error!("Try to get text of tx with None text !")
+        }
+    }
+
+    fn to_compact_document(&self) -> Self::CompactTextDocument_ {
+        self.clone()
+    }
+}
+
+/// Transaction document builder.
+#[derive(Debug, Copy, Clone)]
+pub struct TransactionDocumentV10Builder<'a> {
+    /// Document currency.
+    pub currency: &'a str,
+    /// Reference blockstamp.
+    pub blockstamp: &'a Blockstamp,
+    /// Locktime
+    pub locktime: &'a u64,
+    /// Transaction Document issuers.
+    pub issuers: &'a Vec<PubKey>,
+    /// Transaction inputs.
+    pub inputs: &'a Vec<TransactionInputV10>,
+    /// Inputs unlocks.
+    pub unlocks: &'a Vec<TransactionInputUnlocksV10>,
+    /// Transaction ouputs.
+    pub outputs: &'a Vec<TransactionOutputV10>,
+    /// Transaction comment
+    pub comment: &'a str,
+    /// Transaction hash
+    pub hash: Option<Hash>,
+}
+
+impl<'a> TransactionDocumentV10Builder<'a> {
+    fn build_with_text_and_sigs(
+        self,
+        text: String,
+        signatures: Vec<Sig>,
+    ) -> TransactionDocumentV10 {
+        TransactionDocumentV10 {
+            text: Some(text),
+            currency: self.currency.to_string(),
+            blockstamp: *self.blockstamp,
+            locktime: *self.locktime,
+            issuers: self.issuers.clone(),
+            inputs: self.inputs.clone(),
+            unlocks: self.unlocks.clone(),
+            outputs: self.outputs.clone(),
+            comment: String::from(self.comment),
+            signatures,
+            hash: self.hash,
+        }
+    }
+}
+
+impl<'a> DocumentBuilder for TransactionDocumentV10Builder<'a> {
+    type Document = TransactionDocumentV10;
+    type Signator = SignatorEnum;
+
+    fn build_with_signature(&self, signatures: Vec<Sig>) -> TransactionDocumentV10 {
+        self.build_with_text_and_sigs(self.generate_text(), signatures)
+    }
+
+    fn build_and_sign(&self, private_keys: Vec<SignatorEnum>) -> TransactionDocumentV10 {
+        let (text, signatures) = self.build_signed_text(private_keys);
+        self.build_with_text_and_sigs(text, signatures)
+    }
+}
+
+impl<'a> TextDocumentBuilder for TransactionDocumentV10Builder<'a> {
+    fn generate_text(&self) -> String {
+        let mut issuers_string: String = "".to_owned();
+        let mut inputs_string: String = "".to_owned();
+        let mut unlocks_string: String = "".to_owned();
+        let mut outputs_string: String = "".to_owned();
+        for issuer in self.issuers {
+            issuers_string.push_str(&format!("{}\n", issuer.to_string()))
+        }
+        for input in self.inputs {
+            inputs_string.push_str(&format!("{}\n", input.to_string()))
+        }
+        for unlock in self.unlocks {
+            unlocks_string.push_str(&format!("{}\n", unlock.to_string()))
+        }
+        for output in self.outputs {
+            outputs_string.push_str(&format!("{}\n", output.to_string()))
+        }
+        format!(
+            "Version: 10
+Type: Transaction
+Currency: {currency}
+Blockstamp: {blockstamp}
+Locktime: {locktime}
+Issuers:
+{issuers}Inputs:
+{inputs}Unlocks:
+{unlocks}Outputs:
+{outputs}Comment: {comment}
+",
+            currency = self.currency,
+            blockstamp = self.blockstamp,
+            locktime = self.locktime,
+            issuers = issuers_string,
+            inputs = inputs_string,
+            unlocks = unlocks_string,
+            outputs = outputs_string,
+            comment = self.comment,
+        )
+    }
+}
+
+/// Transaction document parser
+#[derive(Debug, Clone, Copy)]
+pub struct TransactionDocumentV10Parser;
+
+impl TextDocumentParser<Rule> for TransactionDocumentV10Parser {
+    type DocumentType = TransactionDocumentV10;
+
+    fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
+        let mut tx_pairs = DocumentsParser::parse(Rule::tx, doc)?;
+        let tx_pair = unwrap!(tx_pairs.next()); // get and unwrap the `tx` rule; never fails
+        Self::from_pest_pair(tx_pair)
+    }
+    #[inline]
+    fn from_pest_pair(pair: Pair<Rule>) -> Result<Self::DocumentType, TextDocumentParseError> {
+        let tx_vx_pair = unwrap!(pair.into_inner().next()); // get and unwrap the `tx_vX` rule; never fails
+
+        match tx_vx_pair.as_rule() {
+            Rule::tx_v10 => TransactionDocumentV10::from_pest_pair(tx_vx_pair),
+            _ => Err(TextDocumentParseError::UnexpectedRule(format!(
+                "{:#?}",
+                tx_vx_pair.as_rule()
+            ))),
+        }
+    }
+    #[inline]
+    fn from_versioned_pest_pair(
+        version: u16,
+        pair: Pair<Rule>,
+    ) -> Result<Self::DocumentType, TextDocumentParseError> {
+        match version {
+            10 => Ok(TransactionDocumentV10::from_pest_pair(pair)?),
+            v => Err(TextDocumentParseError::UnexpectedVersion(format!(
+                "Unsupported version: {}",
+                v
+            ))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use dubp_common_doc::traits::Document;
+
+    #[test]
+    fn generate_real_document() {
+        let keypair = ed25519::KeyPairFromSeed32Generator::generate(unwrap!(
+            Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
+            "Fail to parse Seed32"
+        ));
+        let pubkey = PubKey::Ed25519(keypair.public_key());
+        let signator =
+            SignatorEnum::Ed25519(keypair.generate_signator().expect("fail to gen signator"));
+
+        let sig = Sig::Ed25519(unwrap!(ed25519::Signature::from_base64(
+            "cq86RugQlqAEyS8zFkB9o0PlWPSb+a6D/MEnLe8j+okyFYf/WzI6pFiBkQ9PSOVn5I0dwzVXg7Q4N1apMWeGAg==",
+        ), "Fail to parse Signature"));
+
+        let block = unwrap!(
+            Blockstamp::from_string(
+                "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
+            ),
+            "Fail to parse blockstamp"
+        );
+
+        let builder = TransactionDocumentV10Builder {
+            currency: "duniter_unit_test_currency",
+            blockstamp: &block,
+            locktime: &0,
+            issuers: &vec![pubkey],
+            inputs: &vec![TransactionInputV10::D(
+                TxAmount(10),
+                TxBase(0),
+                PubKey::Ed25519(unwrap!(
+                    ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
+                    "Fail to parse PublicKey"
+                )),
+                BlockNumber(0),
+            )],
+            unlocks: &vec![TransactionInputUnlocksV10 {
+                index: 0,
+                unlocks: vec![TransactionUnlockProof::Sig(0)],
+            }],
+            outputs: &vec![TransactionOutputV10::from_str(
+                "10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)",
+            )
+            .expect("fail to parse output !")],
+            comment: "test",
+            hash: None,
+        };
+        assert!(builder
+            .build_with_signature(vec![sig])
+            .verify_signatures()
+            .is_ok());
+        assert!(builder
+            .build_and_sign(vec![signator])
+            .verify_signatures()
+            .is_ok());
+    }
+
+    #[test]
+    fn compute_transaction_hash() {
+        let pubkey = PubKey::Ed25519(unwrap!(
+            ed25519::PublicKey::from_base58("FEkbc4BfJukSWnCU6Hed6dgwwTuPFTVdgz5LpL4iHr9J"),
+            "Fail to parse PublicKey"
+        ));
+
+        let sig = Sig::Ed25519(unwrap!(ed25519::Signature::from_base64(
+            "XEwKwKF8AI1gWPT7elR4IN+bW3Qn02Dk15TEgrKtY/S2qfZsNaodsLofqHLI24BBwZ5aadpC88ntmjo/UW9oDQ==",
+        ), "Fail to parse Signature"));
+
+        let block = unwrap!(
+            Blockstamp::from_string(
+                "60-00001FE00410FCD5991EDD18AA7DDF15F4C8393A64FA92A1DB1C1CA2E220128D",
+            ),
+            "Fail to parse Blockstamp"
+        );
+
+        let builder = TransactionDocumentV10Builder {
+            currency: "g1",
+            blockstamp: &block,
+            locktime: &0,
+            issuers: &vec![pubkey],
+            inputs: &vec![TransactionInputV10::T(
+                TxAmount(950),
+                TxBase(0),
+                unwrap!(
+                    Hash::from_hex(
+                        "2CF1ACD8FE8DC93EE39A1D55881C50D87C55892AE8E4DB71D4EBAB3D412AA8FD"
+                    ),
+                    "Fail to parse Hash"
+                ),
+                OutputIndex(1),
+            )],
+            unlocks: &vec![
+                TransactionInputUnlocksV10::from_str("0:SIG(0)").expect("fail to parse unlock !")
+            ],
+            outputs: &vec![
+                TransactionOutputV10::from_str(
+                    "30:0:SIG(38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE)",
+                )
+                .expect("fail to parse output !"),
+                TransactionOutputV10::from_str(
+                    "920:0:SIG(FEkbc4BfJukSWnCU6Hed6dgwwTuPFTVdgz5LpL4iHr9J)",
+                )
+                .expect("fail to parse output !"),
+            ],
+            comment: "Pour cesium merci",
+            hash: None,
+        };
+        let mut tx_doc = builder.build_with_signature(vec![sig]);
+        assert!(tx_doc.verify_signatures().is_ok());
+        assert!(tx_doc.get_hash_opt().is_none());
+        assert_eq!(
+            tx_doc.get_hash(),
+            Hash::from_hex("876D2430E0B66E2CE4467866D8F923D68896CACD6AA49CDD8BDD0096B834DEF1")
+                .expect("fail to parse hash")
+        );
+    }
+
+    #[test]
+    fn parse_transaction_document() {
+        let doc = "Version: 10
+Type: Transaction
+Currency: duniter_unit_test_currency
+Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
+Locktime: 0
+Issuers:
+DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR
+FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa
+Inputs:
+40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
+70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
+20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46
+70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
+20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
+15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46
+Unlocks:
+0:SIG(0)
+1:XHX(7665798292)
+2:SIG(0)
+3:SIG(0) SIG(2)
+4:SIG(0) SIG(1) SIG(2)
+5:SIG(2)
+Outputs:
+120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
+146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA))
+Comment: -----@@@----- (why not this comment?)
+kL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw==
+e3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw==
+w69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw==";
+
+        let doc = TransactionDocumentV10Parser::parse(doc)
+            .expect("fail to parse test transaction document !");
+        assert!(doc.verify_signatures().is_ok());
+        assert_eq!(
+            doc.generate_compact_text(),
+            "TX:10:3:6:6:3:1:0
+204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
+DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR
+FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa
+40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
+70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
+20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46
+70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
+20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
+15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46
+0:SIG(0)
+1:XHX(7665798292)
+2:SIG(0)
+3:SIG(0) SIG(2)
+4:SIG(0) SIG(1) SIG(2)
+5:SIG(2)
+120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
+146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA))
+-----@@@----- (why not this comment?)
+kL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw==
+e3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw==
+w69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw=="
+        );
+    }
+
+    #[test]
+    fn transaction_input_str() {
+        let expected_du = TransactionInputV10::D(
+            TxAmount(10),
+            TxBase(0),
+            PubKey::Ed25519(unwrap!(
+                ed25519::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
+                "Fail to parse PublicKey"
+            )),
+            BlockNumber(0),
+        );
+        let du = TransactionInputV10::from_str(&expected_du.to_string());
+        assert!(du.is_ok());
+        assert_eq!(expected_du, unwrap!(du));
+
+        let expected_tx = TransactionInputV10::T(
+            TxAmount(950),
+            TxBase(0),
+            unwrap!(
+                Hash::from_hex("2CF1ACD8FE8DC93EE39A1D55881C50D87C55892AE8E4DB71D4EBAB3D412AA8FD"),
+                "Fail to parse Hash"
+            ),
+            OutputIndex(1),
+        );
+        let tx = TransactionInputV10::from_str(&expected_tx.to_string());
+        assert!(tx.is_ok());
+        assert_eq!(expected_tx, unwrap!(tx));
+    }
+}
diff --git a/lib/dubp/user-docs/src/parsers/transactions.rs b/lib/dubp/user-docs/src/parsers/transactions.rs
index c9736e998036f5b7379de3b6f6aadd86e97bd187..bd553a8ba048b19f6e7373cb48cebabdb64e0435 100644
--- a/lib/dubp/user-docs/src/parsers/transactions.rs
+++ b/lib/dubp/user-docs/src/parsers/transactions.rs
@@ -13,6 +13,7 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+use crate::documents::transaction::v10::*;
 use crate::documents::transaction::*;
 use crate::parsers::DefaultHasher;
 use crate::*;
@@ -32,6 +33,20 @@ pub enum ParseTxError {
     WrongFormat,
 }
 
+/// Parse transactions documents from array of str
+pub fn parse_json_transactions(
+    array_transactions: &[&JSONValue<DefaultHasher>],
+) -> Result<Vec<TransactionDocumentV10>, Error> {
+    array_transactions
+        .iter()
+        .map(|tx| {
+            parse_json_transaction(tx).map(|tx_doc| match tx_doc {
+                TransactionDocument::V10(tx_doc_v10) => tx_doc_v10,
+            })
+        })
+        .collect::<Result<Vec<TransactionDocumentV10>, Error>>()
+}
+
 /// Parse transaction from json value
 pub fn parse_json_transaction(
     json_tx: &JSONValue<DefaultHasher>,
@@ -45,40 +60,49 @@ pub fn parse_json_transaction(
 
     let json_tx = json_tx.to_object().expect("safe unwrap");
 
-    let tx_doc_builder = TransactionDocumentBuilder {
-        currency: get_str(json_tx, "currency")?,
-        blockstamp: &Blockstamp::from_string(get_str(json_tx, "blockstamp")?)?,
-        locktime: &(get_number(json_tx, "locktime")?.trunc() as u64),
-        issuers: &get_str_array(json_tx, "issuers")?
-            .iter()
-            .map(|p| ed25519::PublicKey::from_base58(p))
-            .map(|p| p.map(PubKey::Ed25519))
-            .collect::<Result<Vec<PubKey>, BaseConvertionError>>()?,
-        inputs: &get_str_array(json_tx, "inputs")?
-            .iter()
-            .map(|i| TransactionInput::from_str(i))
-            .collect::<Result<Vec<TransactionInput>, TextDocumentParseError>>()?,
-        unlocks: &get_str_array(json_tx, "unlocks")?
-            .iter()
-            .map(|i| TransactionInputUnlocks::from_str(i))
-            .collect::<Result<Vec<TransactionInputUnlocks>, TextDocumentParseError>>()?,
-        outputs: &get_str_array(json_tx, "outputs")?
-            .iter()
-            .map(|i| TransactionOutput::from_str(i))
-            .collect::<Result<Vec<TransactionOutput>, TextDocumentParseError>>()?,
-        comment: &durs_common_tools::fns::str_escape::unescape_str(get_str(json_tx, "comment")?),
-        hash: get_optional_str(json_tx, "hash")?
-            .map(Hash::from_hex)
-            .transpose()?,
-    };
-
-    Ok(tx_doc_builder.build_with_signature(
-        get_str_array(json_tx, "signatures")?
-            .iter()
-            .map(|p| ed25519::Signature::from_base64(p))
-            .map(|p| p.map(Sig::Ed25519))
-            .collect::<Result<Vec<Sig>, BaseConvertionError>>()?,
-    ))
+    match get_u64(json_tx, "version")? {
+        10 => Ok(
+            TransactionDocumentBuilder::V10(TransactionDocumentV10Builder {
+                currency: get_str(json_tx, "currency")?,
+                blockstamp: &Blockstamp::from_string(get_str(json_tx, "blockstamp")?)?,
+                locktime: &(get_number(json_tx, "locktime")?.trunc() as u64),
+                issuers: &get_str_array(json_tx, "issuers")?
+                    .iter()
+                    .map(|p| ed25519::PublicKey::from_base58(p))
+                    .map(|p| p.map(PubKey::Ed25519))
+                    .collect::<Result<Vec<PubKey>, BaseConvertionError>>()?,
+                inputs: &get_str_array(json_tx, "inputs")?
+                    .iter()
+                    .map(|i| TransactionInputV10::from_str(i))
+                    .collect::<Result<Vec<TransactionInputV10>, TextDocumentParseError>>()?,
+                unlocks: &get_str_array(json_tx, "unlocks")?
+                    .iter()
+                    .map(|i| TransactionInputUnlocksV10::from_str(i))
+                    .collect::<Result<Vec<TransactionInputUnlocksV10>, TextDocumentParseError>>()?,
+                outputs: &get_str_array(json_tx, "outputs")?
+                    .iter()
+                    .map(|i| TransactionOutputV10::from_str(i))
+                    .collect::<Result<Vec<TransactionOutputV10>, TextDocumentParseError>>()?,
+                comment: &durs_common_tools::fns::str_escape::unescape_str(get_str(
+                    json_tx, "comment",
+                )?),
+                hash: get_optional_str(json_tx, "hash")?
+                    .map(Hash::from_hex)
+                    .transpose()?,
+            })
+            .build_with_signature(
+                get_str_array(json_tx, "signatures")?
+                    .iter()
+                    .map(|p| ed25519::Signature::from_base64(p))
+                    .map(|p| p.map(Sig::Ed25519))
+                    .collect::<Result<Vec<Sig>, BaseConvertionError>>()?,
+            ),
+        ),
+        version => Err(ParseJsonError {
+            cause: format!("Unhandled json transaction version: {} !", version),
+        }
+        .into()),
+    }
 }
 
 #[cfg(test)]
@@ -89,7 +113,7 @@ mod tests {
     use std::str::FromStr;
 
     pub fn first_g1_tx_doc() -> TransactionDocument {
-        let expected_tx_builder = TransactionDocumentBuilder {
+        let expected_tx_builder = TransactionDocumentV10Builder {
             currency: &"g1",
             blockstamp: &Blockstamp::from_string(
                 "50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7",
@@ -100,19 +124,19 @@ mod tests {
                 ed25519::PublicKey::from_base58("2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ")
                     .expect("Fail to parse issuer !"),
             )],
-            inputs: &vec![TransactionInput::from_str(
+            inputs: &vec![TransactionInputV10::from_str(
                 "1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1",
             )
             .expect("Fail to parse inputs")],
             unlocks: &vec![
-                TransactionInputUnlocks::from_str("0:SIG(0)").expect("Fail to parse unlocks")
+                TransactionInputUnlocksV10::from_str("0:SIG(0)").expect("Fail to parse unlocks")
             ],
             outputs: &vec![
-                TransactionOutput::from_str(
+                TransactionOutputV10::from_str(
                     "1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)",
                 )
                 .expect("Fail to parse outputs"),
-                TransactionOutput::from_str(
+                TransactionOutputV10::from_str(
                     "999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)",
                 )
                 .expect("Fail to parse outputs"),
@@ -121,7 +145,7 @@ mod tests {
             hash: None,
         };
 
-        expected_tx_builder.build_with_signature(vec![Sig::Ed25519(
+        TransactionDocumentBuilder::V10(expected_tx_builder).build_with_signature(vec![Sig::Ed25519(
                 ed25519::Signature::from_base64("fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==").expect("Fail to parse sig !")
             )])
     }
diff --git a/lib/modules-lib/bc-db-reader/src/indexes/sources.rs b/lib/modules-lib/bc-db-reader/src/indexes/sources.rs
index 073b89962e958e9d70d54f30fa2ebd2fb13c8ff1..30b84528f889253962295992e61b02ae7dc4d009 100644
--- a/lib/modules-lib/bc-db-reader/src/indexes/sources.rs
+++ b/lib/modules-lib/bc-db-reader/src/indexes/sources.rs
@@ -85,7 +85,7 @@ impl Sub for SourceAmount {
 
 #[derive(Debug, Clone, Deserialize, Serialize)]
 /// V10 Unused Transaction Output
-pub struct UTXOV10(pub UniqueIdUTXOv10, pub TransactionOutput);
+pub struct UTXOV10(pub UniqueIdUTXOv10, pub TransactionOutputV10);
 
 impl UTXOV10 {
     /// UTXO conditions
@@ -128,7 +128,7 @@ impl UTXO {
 pub fn get_utxo_v10<DB: BcDbInReadTx>(
     db: &DB,
     utxo_id: UniqueIdUTXOv10,
-) -> Result<Option<TransactionOutput>, DbError> {
+) -> Result<Option<TransactionOutputV10>, DbError> {
     let utxo_id_bytes: Vec<u8> = utxo_id.into();
     db.db()
         .get_store(UTXOS)
@@ -141,7 +141,7 @@ pub fn get_utxo_v10<DB: BcDbInReadTx>(
 pub fn get_block_consumed_sources_<DB: BcDbInReadTx>(
     db: &DB,
     block_number: BlockNumber,
-) -> Result<Option<HashMap<UniqueIdUTXOv10, TransactionOutput>>, DbError> {
+) -> Result<Option<HashMap<UniqueIdUTXOv10, TransactionOutputV10>>, DbError> {
     db.db()
         .get_int_store(CONSUMED_UTXOS)
         .get(db.r(), block_number.0)?
diff --git a/lib/modules/blockchain/bc-db-writer/src/indexes/transactions.rs b/lib/modules/blockchain/bc-db-writer/src/indexes/transactions.rs
index 260dc8b9158ea9771e6fdf50f6f176cfacdb6ca1..bdaa05e5cb0f7bbc6ba112caa3a2bfbeaa748c7d 100644
--- a/lib/modules/blockchain/bc-db-writer/src/indexes/transactions.rs
+++ b/lib/modules/blockchain/bc-db-writer/src/indexes/transactions.rs
@@ -44,14 +44,16 @@ pub fn revert_tx<S: std::hash::BuildHasher>(
     db: &Db,
     w: &mut DbWriter,
     tx_doc: &TransactionDocument,
-    block_consumed_sources: &mut HashMap<UniqueIdUTXOv10, TransactionOutput, S>,
+    block_consumed_sources: &mut HashMap<UniqueIdUTXOv10, TransactionOutputV10, S>,
 ) -> Result<(), DbError> {
     let tx_hash = tx_doc
         .get_hash_opt()
         .unwrap_or_else(|| tx_doc.compute_hash());
 
+    let TransactionDocument::V10(tx_doc_v10) = tx_doc;
+
     // Index created utxos
-    let created_utxos: Vec<UTXOV10> = tx_doc
+    let created_utxos: Vec<UTXOV10> = tx_doc_v10
         .get_outputs()
         .iter()
         .enumerate()
@@ -68,14 +70,14 @@ pub fn revert_tx<S: std::hash::BuildHasher>(
         db.get_store(UTXOS).delete(w.as_mut(), &utxo_id_bytes)?;
     }
     // Index consumed sources
-    let consumed_sources_ids: HashSet<SourceUniqueIdV10> = tx_doc
+    let consumed_sources_ids: HashSet<SourceUniqueIdV10> = tx_doc_v10
         .get_inputs()
         .iter()
         .map(|input| match *input {
-            TransactionInput::D(_tx_amout, _tx_amout_base, pubkey, block_id) => {
+            TransactionInputV10::D(_tx_amout, _tx_amout_base, pubkey, block_id) => {
                 SourceUniqueIdV10::UD(pubkey, block_id)
             }
-            TransactionInput::T(_tx_amout, _tx_amout_base, hash, tx_index) => {
+            TransactionInputV10::T(_tx_amout, _tx_amout_base, hash, tx_index) => {
                 SourceUniqueIdV10::UTXO(UniqueIdUTXOv10(hash, tx_index))
             }
         })
@@ -118,15 +120,17 @@ pub fn apply_and_write_tx(
     let tx_hash = tx_doc
         .get_hash_opt()
         .unwrap_or_else(|| tx_doc.compute_hash());
+
+    let TransactionDocument::V10(tx_doc_v10) = tx_doc;
     // Index consumed sources
-    let consumed_sources_ids: HashSet<SourceUniqueIdV10> = tx_doc
+    let consumed_sources_ids: HashSet<SourceUniqueIdV10> = tx_doc_v10
         .get_inputs()
         .iter()
         .map(|input| match *input {
-            TransactionInput::D(_tx_amout, _tx_amout_base, pubkey, block_id) => {
+            TransactionInputV10::D(_tx_amout, _tx_amout_base, pubkey, block_id) => {
                 SourceUniqueIdV10::UD(pubkey, block_id)
             }
-            TransactionInput::T(_tx_amout, _tx_amout_base, hash, tx_index) => {
+            TransactionInputV10::T(_tx_amout, _tx_amout_base, hash, tx_index) => {
                 SourceUniqueIdV10::UTXO(UniqueIdUTXOv10(hash, tx_index))
             }
         })
@@ -145,13 +149,13 @@ pub fn apply_and_write_tx(
             .map(|utxo_id| {
                 let utxo_id_bytes: Vec<u8> = (*utxo_id).into();
                 if let Some(value) = db.get_store(UTXOS).get(w.as_ref(), &utxo_id_bytes)? {
-                    let utxo_content: TransactionOutput = from_db_value(value)?;
+                    let utxo_content: TransactionOutputV10 = from_db_value(value)?;
                     Ok((*utxo_id, utxo_content))
                 } else {
                     fatal_error!("Try to persist unexist consumed source.");
                 }
             })
-            .collect::<Result<HashMap<UniqueIdUTXOv10, TransactionOutput>, DbError>>()?;
+            .collect::<Result<HashMap<UniqueIdUTXOv10, TransactionOutputV10>, DbError>>()?;
         let consumed_sources_bytes = durs_dbs_tools::to_bytes(&consumed_sources)?;
         let block_number =
             durs_bc_db_reader::current_metadata::get_current_blockstamp(&BcDbRwWithWriter {
@@ -189,7 +193,7 @@ pub fn apply_and_write_tx(
                 })?;
         }
     }
-    let created_utxos: Vec<UTXOV10> = tx_doc
+    let created_utxos: Vec<UTXOV10> = tx_doc_v10
         .get_outputs()
         .iter()
         .enumerate()
@@ -218,6 +222,7 @@ mod tests {
     use super::*;
     use dubp_common_doc::traits::{Document, DocumentBuilder};
     use dubp_common_doc::BlockHash;
+    use dubp_user_docs::documents::transaction::v10::TransactionInputUnlocksV10;
     use durs_bc_db_reader::current_metadata::CurrentMetaDataKey;
     use durs_bc_db_reader::indexes::sources::SourceAmount;
     use std::str::FromStr;
@@ -233,24 +238,24 @@ mod tests {
         let block = unwrap!(Blockstamp::from_string(
             "50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7",
         ));
-        let builder = TransactionDocumentBuilder {
+        let builder = TransactionDocumentV10Builder {
             currency: "g1",
             blockstamp: &block,
             locktime: &0,
             issuers: &vec![pubkey],
-            inputs: &vec![TransactionInput::from_str(
+            inputs: &vec![TransactionInputV10::from_str(
                 "1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1",
             )
             .expect("fail to parse input !")],
             unlocks: &vec![
-                TransactionInputUnlocks::from_str("0:SIG(0)").expect("fail to parse unlock !")
+                TransactionInputUnlocksV10::from_str("0:SIG(0)").expect("fail to parse unlock !")
             ],
             outputs: &vec![
-                TransactionOutput::from_str(
+                TransactionOutputV10::from_str(
                     "1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)",
                 )
                 .expect("fail to parse output !"),
-                TransactionOutput::from_str(
+                TransactionOutputV10::from_str(
                     "999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)",
                 )
                 .expect("fail to parse output !"),
@@ -258,7 +263,7 @@ mod tests {
             comment: "TEST",
             hash: None,
         };
-        builder.build_with_signature(vec![sig])
+        TransactionDocumentBuilder::V10(builder).build_with_signature(vec![sig])
     }
 
     #[test]
diff --git a/lib/modules/blockchain/bc-db-writer/src/writers/requests.rs b/lib/modules/blockchain/bc-db-writer/src/writers/requests.rs
index 031954da43f1d3b5de393a60a0deefdba1f54095..09f8f9a13a3d3cf1de430517dcde64194b4c9080 100644
--- a/lib/modules/blockchain/bc-db-writer/src/writers/requests.rs
+++ b/lib/modules/blockchain/bc-db-writer/src/writers/requests.rs
@@ -264,7 +264,7 @@ impl CurrencyDBsWriteQuery {
         &self,
         db: &Db,
         w: &mut DbWriter,
-        block_consumed_sources_opt: Option<&mut HashMap<UniqueIdUTXOv10, TransactionOutput>>,
+        block_consumed_sources_opt: Option<&mut HashMap<UniqueIdUTXOv10, TransactionOutputV10>>,
         in_fork_window: bool,
     ) -> Result<(), DbError> {
         match *self {
diff --git a/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs b/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs
index bd07fc2e41ec86092ec335a9f0e72aed9b9c2c51..f3fdee17d56bbc6f610b0755503aaf28dbe65427 100644
--- a/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs
+++ b/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs
@@ -18,7 +18,7 @@
 use dubp_block_doc::block::{BlockDocument, BlockDocumentV10};
 use dubp_common_doc::traits::Document;
 use dubp_common_doc::BlockNumber;
-use dubp_user_docs::documents::transaction::{TxAmount, TxBase};
+use dubp_user_docs::documents::transaction::{TransactionDocument, TxAmount, TxBase};
 use dup_crypto::keys::*;
 use durs_bc_db_reader::blocks::BlockDb;
 use durs_bc_db_reader::indexes::sources::get_block_consumed_sources_;
@@ -230,7 +230,9 @@ pub fn apply_valid_block_v10<W: WebOfTrust>(
     }
 
     for tx in &block.transactions {
-        currency_dbs_requests.push(CurrencyDBsWriteQuery::WriteTx(Box::new(tx.clone())));
+        currency_dbs_requests.push(CurrencyDBsWriteQuery::WriteTx(Box::new(
+            TransactionDocument::V10(tx.clone()),
+        )));
     }
 
     /*// Calculate the state of the wot
diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/local.rs b/lib/modules/blockchain/blockchain/src/dubp/check/local.rs
index 233e62f42e40b0202850df8f2e42b2cd1b228e03..d5925e037d41f2b0fe1b968cf5ca5cb33defa013 100644
--- a/lib/modules/blockchain/blockchain/src/dubp/check/local.rs
+++ b/lib/modules/blockchain/blockchain/src/dubp/check/local.rs
@@ -154,7 +154,7 @@ pub fn verify_local_validity_block_v10(
 
     // Check transactions
     for tx in &block.transactions {
-        self::tx_doc::local_verify_tx_doc(block.version(), tx)?;
+        self::tx_doc::local_verify_tx_doc_v10(block.version(), tx)?;
     }
 
     Ok(())
diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs b/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs
index 33adb27f7d8b7677bd7fd9b1ea0ad55e6a282a44..731c5c4696f78284d1351f43787df94a10b05a4b 100644
--- a/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs
+++ b/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs
@@ -18,7 +18,8 @@
 use dubp_common_doc::errors::DocumentSigsErr;
 use dubp_common_doc::traits::text::CompactTextDocument;
 use dubp_common_doc::traits::Document;
-use dubp_user_docs::documents::transaction::TransactionDocument;
+use dubp_user_docs::documents::transaction::v10::TransactionDocumentV10;
+use dubp_user_docs::documents::transaction::TransactionDocumentTrait;
 use durs_common_tools::traits::bool_ext::BoolExt;
 
 #[derive(Debug, PartialEq)]
@@ -36,9 +37,9 @@ pub enum TransactionDocumentError {
 }
 
 /// Local verification of a Tx Document
-pub fn local_verify_tx_doc(
+pub fn local_verify_tx_doc_v10(
     dubp_version: usize,
-    tx_doc: &TransactionDocument,
+    tx_doc: &TransactionDocumentV10,
 ) -> Result<(), TransactionDocumentError> {
     // A transaction in compact format must measure less than 100 lines
     (tx_doc.as_compact_text().lines().count() < 100).or_err(TransactionDocumentError::TooLong {
@@ -69,11 +70,12 @@ mod tests {
     use super::*;
     use dubp_common_doc::traits::DocumentBuilder;
     use dubp_common_doc::Blockstamp;
+    use dubp_user_docs::documents::transaction::v10::TransactionDocumentV10Builder;
+    use dubp_user_docs::documents::transaction::v10::TransactionInputUnlocksV10;
+    use dubp_user_docs::documents::transaction::v10::TransactionInputV10;
+    use dubp_user_docs::documents::transaction::v10::TransactionOutputV10;
     use dubp_user_docs::documents::transaction::OutputIndex;
-    use dubp_user_docs::documents::transaction::TransactionDocumentBuilder;
-    use dubp_user_docs::documents::transaction::TransactionInput;
-    use dubp_user_docs::documents::transaction::TransactionInputUnlocks;
-    use dubp_user_docs::documents::transaction::TransactionOutput;
+    use dubp_user_docs::documents::transaction::TransactionDocument;
     use dubp_user_docs::documents::transaction::TransactionUnlockProof;
     use dubp_user_docs::documents::transaction::TxAmount;
     use dubp_user_docs::documents::transaction::TxBase;
@@ -107,8 +109,8 @@ mod tests {
     }
 
     #[inline]
-    fn input1() -> TransactionInput {
-        TransactionInput::T(
+    fn input1() -> TransactionInputV10 {
+        TransactionInputV10::T(
             TxAmount(950),
             TxBase(0),
             Hash::from_hex("2CF1ACD8FE8DC93EE39A1D55881C50D87C55892AE8E4DB71D4EBAB3D412AA8FD")
@@ -118,29 +120,29 @@ mod tests {
     }
 
     #[inline]
-    fn unlocks() -> Vec<TransactionInputUnlocks> {
-        vec![TransactionInputUnlocks {
+    fn unlocks() -> Vec<TransactionInputUnlocksV10> {
+        vec![TransactionInputUnlocksV10 {
             index: 0,
             unlocks: vec![TransactionUnlockProof::Sig(0)],
         }]
     }
 
     #[inline]
-    fn outputs() -> Vec<TransactionOutput> {
-        vec![
-            TransactionOutput::from_str("10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)")
-                .expect("fail to parse output !"),
-        ]
+    fn outputs() -> Vec<TransactionOutputV10> {
+        vec![TransactionOutputV10::from_str(
+            "10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)",
+        )
+        .expect("fail to parse output !")]
     }
 
     fn tx_builder<'a>(
         blockstamp: &'a Blockstamp,
         issuers: &'a Vec<PubKey>,
-        inputs: &'a Vec<TransactionInput>,
-        unlocks: &'a Vec<TransactionInputUnlocks>,
-        outputs: &'a Vec<TransactionOutput>,
-    ) -> TransactionDocumentBuilder<'a> {
-        TransactionDocumentBuilder {
+        inputs: &'a Vec<TransactionInputV10>,
+        unlocks: &'a Vec<TransactionInputUnlocksV10>,
+        outputs: &'a Vec<TransactionOutputV10>,
+    ) -> TransactionDocumentV10Builder<'a> {
+        TransactionDocumentV10Builder {
             currency: "duniter_unit_test_currency",
             blockstamp,
             locktime: &0,
@@ -155,8 +157,8 @@ mod tests {
 
     #[test]
     fn test_tx_valid() {
-        let tx = gen_mock_tx_doc();
-        assert_eq!(Ok(()), local_verify_tx_doc(10, &tx));
+        let TransactionDocument::V10(tx) = gen_mock_tx_doc();
+        assert_eq!(Ok(()), local_verify_tx_doc_v10(10, &tx));
     }
 
     #[test]
@@ -170,7 +172,7 @@ mod tests {
         let tx = tx_builder.build_with_signature(vec![sig1()]);
 
         let expected = Err(TransactionDocumentError::MissingInput);
-        let actual = local_verify_tx_doc(10, &tx);
+        let actual = local_verify_tx_doc_v10(10, &tx);
         assert_eq!(expected, actual);
     }
 
@@ -188,7 +190,7 @@ mod tests {
             expected_max_length: 100,
             actual_length: 107,
         });
-        let actual = local_verify_tx_doc(10, &tx);
+        let actual = local_verify_tx_doc_v10(10, &tx);
         assert_eq!(expected, actual);
     }
 
diff --git a/lib/modules/blockchain/blockchain/src/fork/revert_block.rs b/lib/modules/blockchain/blockchain/src/fork/revert_block.rs
index a8f1b2e7453bd300db66b2b25ea1ee773fe0640a..0bea8a77b6ae5ca8911a3dc88c60b9b406db7b4c 100644
--- a/lib/modules/blockchain/blockchain/src/fork/revert_block.rs
+++ b/lib/modules/blockchain/blockchain/src/fork/revert_block.rs
@@ -18,7 +18,7 @@
 use dubp_block_doc::block::{BlockDocument, BlockDocumentTrait, BlockDocumentV10};
 use dubp_common_doc::traits::Document;
 use dubp_common_doc::{BlockNumber, Blockstamp};
-use dubp_user_docs::documents::transaction::{TxAmount, TxBase};
+use dubp_user_docs::documents::transaction::{TransactionDocument, TxAmount, TxBase};
 use dup_crypto::keys::*;
 use durs_bc_db_reader::blocks::BlockDb;
 use durs_bc_db_reader::indexes::sources::SourceAmount;
@@ -82,7 +82,9 @@ pub fn revert_block_v10<W: WebOfTrust>(
     let mut currency_dbs_requests = Vec::new();
     // Revert transactions
     for tx_doc in block.transactions.iter().rev() {
-        currency_dbs_requests.push(CurrencyDBsWriteQuery::RevertTx(Box::new(tx_doc.clone())));
+        currency_dbs_requests.push(CurrencyDBsWriteQuery::RevertTx(Box::new(
+            TransactionDocument::V10(tx_doc.clone()),
+        )));
     }
     // Revert UD
     if let Some(du_amount) = block.dividend {
diff --git a/lib/modules/ws2p-v1-legacy/src/serializers/transaction.rs b/lib/modules/ws2p-v1-legacy/src/serializers/transaction.rs
index fce889544472c7a21661253d0cbd88996c46dbe2..506a706c817dfcafc8ed07a94661a5633234a798 100644
--- a/lib/modules/ws2p-v1-legacy/src/serializers/transaction.rs
+++ b/lib/modules/ws2p-v1-legacy/src/serializers/transaction.rs
@@ -16,9 +16,18 @@
 //! Sub-module that serialize TransactionDocument into WS2Pv1 json format
 
 use super::IntoWS2Pv1Json;
+use dubp_user_docs::documents::transaction::v10::TransactionDocumentV10Stringified;
 use dubp_user_docs::documents::transaction::TransactionDocumentStringified;
 
 impl IntoWS2Pv1Json for TransactionDocumentStringified {
+    fn into_ws2p_v1_json(self) -> serde_json::Value {
+        match self {
+            TransactionDocumentStringified::V10(tx_doc_v10) => tx_doc_v10.into_ws2p_v1_json(),
+        }
+    }
+}
+
+impl IntoWS2Pv1Json for TransactionDocumentV10Stringified {
     fn into_ws2p_v1_json(self) -> serde_json::Value {
         json!( {
             "blockstamp": self.blockstamp,
diff --git a/lib/tests-tools/blocks-tests-tools/src/mocks.rs b/lib/tests-tools/blocks-tests-tools/src/mocks.rs
index dbf497f8d27e4741aec8f6552dfcef7aff239016..aa316541d4cb8a24c890c9de49897ef8d44fa081 100644
--- a/lib/tests-tools/blocks-tests-tools/src/mocks.rs
+++ b/lib/tests-tools/blocks-tests-tools/src/mocks.rs
@@ -156,8 +156,8 @@ CertTimestamp: 106669-000003682E6FE38C44433DCE92E8B2A26C69B6D7867A2BAED231E788DD
 UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==").expect("Fail to parse cert1");
     let CertificationDocument::V10(cert1) = cert1;
 
-    let tx1 = dubp_user_docs_tests_tools::mocks::tx::gen_mock_tx_doc();
-    let tx2 = TransactionDocumentParser::parse("Version: 10
+    let TransactionDocument::V10(tx1) = dubp_user_docs_tests_tools::mocks::tx::gen_mock_tx_doc();
+    let TransactionDocument::V10(tx2) = TransactionDocumentParser::parse("Version: 10
 Type: Transaction
 Currency: g1
 Blockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B
diff --git a/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs b/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs
index f6abdd0beebafdf3e3641183361c5826da321a38..2b84391ab4a93f5b55ba83ab141b36022fa848ef 100644
--- a/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs
+++ b/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs
@@ -18,13 +18,14 @@
 use dubp_common_doc::parser::TextDocumentParser;
 use dubp_common_doc::traits::DocumentBuilder;
 use dubp_common_doc::Blockstamp;
+use dubp_user_docs::documents::transaction::v10::TransactionInputUnlocksV10;
 use dubp_user_docs::documents::transaction::*;
 use dup_crypto::keys::*;
 use std::str::FromStr;
 
 /// Generate first G1 transaction !
 pub fn first_g1_tx_doc() -> TransactionDocument {
-    let expected_tx_builder = TransactionDocumentBuilder {
+    let expected_tx_builder = TransactionDocumentV10Builder {
         currency: &"g1",
         blockstamp: &Blockstamp::from_string(
             "50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7",
@@ -35,24 +36,26 @@ pub fn first_g1_tx_doc() -> TransactionDocument {
             ed25519::PublicKey::from_base58("2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ")
                 .expect("Fail to parse issuer !"),
         )],
-        inputs: &vec![TransactionInput::from_str(
+        inputs: &vec![TransactionInputV10::from_str(
             "1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1",
         )
         .expect("Fail to parse inputs")],
         unlocks: &vec![
-            TransactionInputUnlocks::from_str("0:SIG(0)").expect("Fail to parse unlocks")
+            TransactionInputUnlocksV10::from_str("0:SIG(0)").expect("Fail to parse unlocks")
         ],
         outputs: &vec![
-            TransactionOutput::from_str("1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)")
-                .expect("Fail to parse outputs"),
-            TransactionOutput::from_str("999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)")
+            TransactionOutputV10::from_str("1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)")
                 .expect("Fail to parse outputs"),
+            TransactionOutputV10::from_str(
+                "999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)",
+            )
+            .expect("Fail to parse outputs"),
         ],
         comment: "TEST",
         hash: None,
     };
 
-    expected_tx_builder.build_with_signature(vec![Sig::Ed25519(
+    TransactionDocumentBuilder::V10(expected_tx_builder).build_with_signature(vec![Sig::Ed25519(
                 ed25519::Signature::from_base64("fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==").expect("Fail to parse sig !")
             )])
 }
diff --git a/lib/tools/json-pest-parser/src/lib.rs b/lib/tools/json-pest-parser/src/lib.rs
index 97f7273f7287125d1ea1347bad6153c32e6e2883..9b2fd8db8c991a99747b90f4ec92c4f69fcd8237 100644
--- a/lib/tools/json-pest-parser/src/lib.rs
+++ b/lib/tools/json-pest-parser/src/lib.rs
@@ -408,6 +408,24 @@ pub fn get_str_array<'a, S: std::hash::BuildHasher>(
         .collect()
 }
 
+pub fn get_array<'a, S: std::hash::BuildHasher>(
+    json_block: &'a HashMap<&str, JSONValue<S>, S>,
+    field: &str,
+) -> Result<Vec<&'a JSONValue<'a, S>>, ParseJsonError> {
+    Ok(json_block
+        .get(field)
+        .ok_or_else(|| ParseJsonError {
+            cause: format!("Fail to parse json : field '{}' must exist !", field),
+        })?
+        .to_array()
+        .ok_or_else(|| ParseJsonError {
+            cause: format!("Fail to parse json : field '{}' must be an array !", field),
+        })?
+        .iter()
+        .map(|v| v)
+        .collect())
+}
+
 pub fn get_object_array<'a, S: std::hash::BuildHasher>(
     json_block: &'a JsonObject<'a, S>,
     field: &str,