diff --git a/Cargo.lock b/Cargo.lock
index 8e70bf2a0c3826498535a64d81f0d07a5874d3e3..4685faa99276051b91ab88a9cfa1fb727d28eb50 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1110,6 +1110,7 @@ dependencies = [
  "dubp",
  "duniter-dbs",
  "resiter",
+ "smallvec",
 ]
 
 [[package]]
diff --git a/rust-libs/duniter-dbs-read-ops/Cargo.toml b/rust-libs/duniter-dbs-read-ops/Cargo.toml
index 15385297dc2e757616cb36cd7e1fe2ac579fe89b..7e0ac6c62d7d6061175d5eb5609e190af3e1c7e0 100644
--- a/rust-libs/duniter-dbs-read-ops/Cargo.toml
+++ b/rust-libs/duniter-dbs-read-ops/Cargo.toml
@@ -15,3 +15,6 @@ path = "src/lib.rs"
 duniter-dbs = { path = "../duniter-dbs" }
 dubp = { version = "0.30.0" }
 resiter = "0.4.0"
+
+[dev-dependencies]
+smallvec = { version = "1.4.0", features = ["serde", "write"] }
diff --git a/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs b/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs
new file mode 100644
index 0000000000000000000000000000000000000000..bcdd0a333bf886faad345144b87b9f01316459fe
--- /dev/null
+++ b/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs
@@ -0,0 +1,188 @@
+//  Copyright (C) 2020 Éloïs SANCHEZ.
+//
+// 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/>.
+
+use crate::*;
+use dubp::{documents::transaction::TransactionInputV10, wallet::prelude::*};
+
+pub fn find_inputs<BcDb: BcV2DbReadable, GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>(
+    bc_db: &BcDb,
+    gva_db: &GvaDb,
+    txs_mp_db: &TxsMpDb,
+    amount: SourceAmount,
+    script: &WalletScriptV10,
+    max_inputs_count: usize,
+) -> KvResult<(BlockMetaV2, Vec<TransactionInputV10>, SourceAmount)> {
+    if let Some(current_block) = crate::get_current_block_meta(bc_db)? {
+        let (mut inputs, uds_sum) = if script.nodes.is_empty() {
+            if let WalletSubScriptV10::Single(WalletConditionV10::Sig(issuer)) = script.root {
+                let pending_uds_bn = txs_mp_db.uds_ids().iter(.., |it| {
+                    it.keys()
+                        .map_ok(|duniter_dbs::UdIdV2(_pk, bn)| bn)
+                        .collect::<KvResult<_>>()
+                })?;
+
+                let (uds, uds_sum) = crate::uds_of_pubkey::uds_of_pubkey(
+                    bc_db,
+                    issuer,
+                    ..,
+                    Some(&pending_uds_bn),
+                    Some(max_inputs_count),
+                    Some(amount),
+                )?;
+                let inputs = uds
+                    .into_iter()
+                    .map(|(block_number, source_amount)| TransactionInputV10 {
+                        amount: source_amount,
+                        id: SourceIdV10::Ud(UdSourceIdV10 {
+                            issuer,
+                            block_number,
+                        }),
+                    })
+                    .collect::<Vec<_>>();
+                (inputs, uds_sum)
+            } else {
+                (vec![], SourceAmount::ZERO)
+            }
+        } else {
+            (vec![], SourceAmount::ZERO)
+        };
+        if uds_sum < amount {
+            let (utxos, utxos_sum) = crate::utxos::find_script_utxos(
+                gva_db,
+                txs_mp_db,
+                amount - uds_sum,
+                max_inputs_count - inputs.len(),
+                &script,
+            )?;
+            inputs.extend(
+                utxos
+                    .into_iter()
+                    .map(
+                        |(_written_time, utxo_id, source_amount)| TransactionInputV10 {
+                            amount: source_amount,
+                            id: SourceIdV10::Utxo(utxo_id),
+                        },
+                    ),
+            );
+            let inputs_sum = uds_sum + utxos_sum;
+            if inputs_sum < amount {
+                Err(KvError::Custom(
+                    "Amount need too many sources or issuer's account has an insufficient balance."
+                        .into(),
+                ))
+            } else {
+                Ok::<_, KvError>((current_block, inputs, inputs_sum))
+            }
+        } else {
+            Ok::<_, KvError>((current_block, inputs, uds_sum))
+        }
+    } else {
+        Err(KvError::Custom("no blockchain".into()))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::collections::BTreeMap;
+
+    use super::*;
+    use duniter_dbs::{
+        bc_v2::BcV2DbWritable, gva_v1::GvaV1DbWritable, txs_mp_v2::TxsMpV2DbWritable,
+        SourceAmountValV2, UdIdV2, UtxoIdDbV2, UtxosOfScriptV1, WalletConditionsV2,
+    };
+
+    const UD0: i64 = 10;
+
+    #[test]
+    fn test_find_inputs() -> KvResult<()> {
+        let bc_db = duniter_dbs::bc_v2::BcV2Db::<Mem>::open(MemConf::default())?;
+        let gva_db = duniter_dbs::gva_v1::GvaV1Db::<Mem>::open(MemConf::default())?;
+        let txs_mp_db = duniter_dbs::txs_mp_v2::TxsMpV2Db::<Mem>::open(MemConf::default())?;
+
+        let b0 = BlockMetaV2 {
+            dividend: Some(SourceAmount::with_base0(UD0)),
+            ..Default::default()
+        };
+        let pk = PublicKey::default();
+        let script = WalletScriptV10::single(WalletConditionV10::Sig(pk));
+        let mut utxos = BTreeMap::new();
+        utxos.insert(
+            0,
+            smallvec::smallvec![
+                (
+                    UtxoIdV10 {
+                        tx_hash: Hash::default(),
+                        output_index: 0
+                    },
+                    SourceAmount::with_base0(50)
+                ),
+                (
+                    UtxoIdV10 {
+                        tx_hash: Hash::default(),
+                        output_index: 1
+                    },
+                    SourceAmount::with_base0(80)
+                )
+            ],
+        );
+
+        bc_db.blocks_meta_write().upsert(U32BE(0), b0)?;
+        bc_db
+            .uds_reval_write()
+            .upsert(U32BE(0), SourceAmountValV2(SourceAmount::with_base0(UD0)))?;
+        bc_db
+            .uds_write()
+            .upsert(UdIdV2(PublicKey::default(), BlockNumber(0)), ())?;
+        gva_db
+            .utxos_by_script_write()
+            .upsert(WalletConditionsV2(script.clone()), UtxosOfScriptV1(utxos))?;
+
+        // Gen tx1
+        let (cb, inputs, inputs_sum) = find_inputs(
+            &bc_db,
+            &gva_db,
+            &txs_mp_db,
+            SourceAmount::with_base0(55),
+            &script,
+            2,
+        )?;
+        assert_eq!(cb, b0);
+        assert_eq!(inputs.len(), 2);
+        assert_eq!(inputs_sum, SourceAmount::with_base0(60));
+
+        // Insert tx1 inputs in mempool
+        txs_mp_db
+            .uds_ids_write()
+            .upsert(UdIdV2(pk, BlockNumber(0)), ())?;
+        txs_mp_db
+            .utxos_ids_write()
+            .upsert(UtxoIdDbV2(Hash::default(), 0), ())?;
+
+        // Gen tx2
+        let (cb, inputs, inputs_sum) = find_inputs(
+            &bc_db,
+            &gva_db,
+            &txs_mp_db,
+            SourceAmount::with_base0(55),
+            &script,
+            2,
+        )?;
+        assert_eq!(cb, b0);
+        assert_eq!(inputs.len(), 1);
+        assert_eq!(inputs_sum, SourceAmount::with_base0(80));
+
+        Ok(())
+    }
+}
diff --git a/rust-libs/duniter-dbs-read-ops/src/lib.rs b/rust-libs/duniter-dbs-read-ops/src/lib.rs
index 3f7bc6b0a3b36b96f1f8a5111176194284e66ba4..83db8c7d6b3891dae9d7c3572887edf625a9e75f 100644
--- a/rust-libs/duniter-dbs-read-ops/src/lib.rs
+++ b/rust-libs/duniter-dbs-read-ops/src/lib.rs
@@ -22,6 +22,7 @@
     unused_import_braces
 )]
 
+pub mod find_inputs;
 pub mod txs_history;
 pub mod uds_of_pubkey;
 pub mod utxos;
@@ -36,7 +37,10 @@ use duniter_dbs::{
     TxsMpV2DbReadable,
 };
 use resiter::map::Map;
-use std::ops::{Bound, RangeBounds};
+use std::{
+    collections::BTreeSet,
+    ops::{Bound, RangeBounds},
+};
 
 pub fn get_current_block_meta<BcDb: BcV2DbReadable>(bc_db: &BcDb) -> KvResult<Option<BlockMetaV2>> {
     bc_db
diff --git a/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs b/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs
index 498924f0341c09d8e55c0c44327020bc2be22fa0..4d5f8ccf97fd10e5f9db54e1af6c0f01827e1ca1 100644
--- a/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs
+++ b/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs
@@ -17,10 +17,11 @@ use crate::*;
 use duniter_dbs::bc_v2::UdsRevalEvent;
 use duniter_dbs::UdIdV2;
 
-pub fn uds_of_pubkey<DB: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber>>(
-    bc_db: &DB,
+pub fn uds_of_pubkey<BcDb: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber>>(
+    bc_db: &BcDb,
     pubkey: PublicKey,
     range: R,
+    bn_to_exclude_opt: Option<&BTreeSet<BlockNumber>>,
     limit_opt: Option<usize>,
     total_opt: Option<SourceAmount>,
 ) -> KvResult<(Vec<(BlockNumber, SourceAmount)>, SourceAmount)> {
@@ -48,23 +49,38 @@ pub fn uds_of_pubkey<DB: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber>>(
                     it.reverse().keys().next_res()
                 })?
                 .expect("corrupted db");
-            let blocks_numbers = blocks_numbers.into_iter();
+            let blocks_numbers_len = blocks_numbers.len();
+            let blocks_numbers = blocks_numbers.into_iter().filter(|bn| {
+                if let Some(bn_to_exclude) = bn_to_exclude_opt {
+                    !bn_to_exclude.contains(bn)
+                } else {
+                    true
+                }
+            });
             if let Some(limit) = limit_opt {
                 collect_uds(
                     blocks_numbers.take(limit),
+                    blocks_numbers_len,
                     first_reval,
                     uds_reval,
                     total_opt,
                 )
             } else {
-                collect_uds(blocks_numbers, first_reval, uds_reval, total_opt)
+                collect_uds(
+                    blocks_numbers,
+                    blocks_numbers_len,
+                    first_reval,
+                    uds_reval,
+                    total_opt,
+                )
             }
         }
     })
 }
 
-fn collect_uds<BC: BackendCol, I: ExactSizeIterator<Item = BlockNumber>>(
+fn collect_uds<BC: BackendCol, I: Iterator<Item = BlockNumber>>(
     mut blocks_numbers: I,
+    blocks_numbers_len: usize,
     first_reval: U32BE,
     uds_reval: TxColRo<BC, UdsRevalEvent>,
     amount_opt: Option<SourceAmount>,
@@ -75,7 +91,7 @@ fn collect_uds<BC: BackendCol, I: ExactSizeIterator<Item = BlockNumber>>(
         Ok((vec![], SourceAmount::ZERO))
     } else {
         let mut current_ud = (uds_revals[0].1).0;
-        let mut uds = Vec::with_capacity(blocks_numbers.len());
+        let mut uds = Vec::with_capacity(blocks_numbers_len);
         let mut sum = SourceAmount::ZERO;
 
         // Uds before last reval
diff --git a/rust-libs/duniter-dbs-read-ops/src/utxos.rs b/rust-libs/duniter-dbs-read-ops/src/utxos.rs
index d45b96ecff1761840858311c53dcf4160bc8c2dc..d77f6d60097de7711b05c60483aa0a0587a81343 100644
--- a/rust-libs/duniter-dbs-read-ops/src/utxos.rs
+++ b/rust-libs/duniter-dbs-read-ops/src/utxos.rs
@@ -23,6 +23,32 @@ pub type UtxoV10 = (i64, UtxoIdV10, SourceAmount);
 pub fn get_script_utxos<GvaDb: GvaV1DbReadable>(
     gva_db_ro: &GvaDb,
     script: &WalletScriptV10,
+) -> KvResult<(Vec<UtxoV10>, SourceAmount)> {
+    find_script_utxos_inner::<_, duniter_dbs::txs_mp_v2::TxsMpV2DbRo<Mem>>(
+        gva_db_ro, None, script, None, None,
+    )
+}
+
+pub fn find_script_utxos<GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>(
+    gva_db_ro: &GvaDb,
+    txs_mp_db_ro: &TxsMpDb,
+    amount: SourceAmount,
+    limit: usize,
+    script: &WalletScriptV10,
+) -> KvResult<(Vec<UtxoV10>, SourceAmount)> {
+    find_script_utxos_inner(
+        gva_db_ro,
+        Some(txs_mp_db_ro),
+        script,
+        Some(limit),
+        Some(amount),
+    )
+}
+
+fn find_script_utxos_inner<GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>(
+    gva_db_ro: &GvaDb,
+    txs_mp_db_ro: Option<&TxsMpDb>,
+    script: &WalletScriptV10,
     limit_opt: Option<usize>,
     total_opt: Option<SourceAmount>,
 ) -> KvResult<(Vec<UtxoV10>, SourceAmount)> {
@@ -36,17 +62,27 @@ pub fn get_script_utxos<GvaDb: GvaV1DbReadable>(
             Vec::with_capacity(utxos_of_script.0.len() * 2);
         for (written_time, utxos_) in utxos_of_script.0 {
             for (utxo_id, source_amount) in utxos_ {
-                utxos.push((written_time, utxo_id, source_amount));
-                if let Some(limit) = limit_opt {
-                    count += 1;
-                    if count == limit {
-                        return Ok((utxos, total));
+                if txs_mp_db_ro.is_none()
+                    || !txs_mp_db_ro
+                        .expect("unreachable")
+                        .utxos_ids()
+                        .contains_key(&duniter_dbs::UtxoIdDbV2(
+                            utxo_id.tx_hash,
+                            utxo_id.output_index as u32,
+                        ))?
+                {
+                    utxos.push((written_time, utxo_id, source_amount));
+                    total = total + source_amount;
+                    if let Some(limit) = limit_opt {
+                        count += 1;
+                        if count == limit {
+                            return Ok((utxos, total));
+                        }
                     }
-                }
-                total = total + source_amount;
-                if let Some(total_target) = total_opt {
-                    if total >= total_target {
-                        return Ok((utxos, total));
+                    if let Some(total_target) = total_opt {
+                        if total >= total_target {
+                            return Ok((utxos, total));
+                        }
                     }
                 }
             }
diff --git a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
index c5ee49925f4f2385bd61f11f8bf4c786e9bba423..7cf0fdaaa19c616e6559fd0fda64c5b22272936b 100644
--- a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
@@ -55,9 +55,18 @@ pub fn add_pending_tx<
         txs_mp_db.txs_by_issuer_write(),
         txs_mp_db.txs_by_recipient_write(),
         txs_mp_db.txs_write(),
+        txs_mp_db.uds_ids_write(),
+        txs_mp_db.utxos_ids_write(),
     )
         .write(
-            |(mut txs_by_recv_time, mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
+            |(
+                mut txs_by_recv_time,
+                mut txs_by_issuer,
+                mut txs_by_recipient,
+                mut txs,
+                mut uds_ids,
+                mut utxos_ids,
+            )| {
                 control(&tx, &txs)?;
                 // Insert on col `txs_by_recv_time`
                 let mut hashs = txs_by_recv_time.get(&received_time)?.unwrap_or_default();
@@ -77,6 +86,20 @@ pub fn add_pending_tx<
                     hashs.insert(tx.get_hash());
                     txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs);
                 }
+                // Insert tx inputs in cols `uds_ids` and `utxos_ids`
+                for input in tx.get_inputs() {
+                    match input.id {
+                        SourceIdV10::Ud(UdSourceIdV10 {
+                            issuer,
+                            block_number,
+                        }) => uds_ids.upsert(duniter_dbs::UdIdV2(issuer, block_number), ()),
+                        SourceIdV10::Utxo(UtxoIdV10 {
+                            tx_hash,
+                            output_index,
+                        }) => utxos_ids
+                            .upsert(duniter_dbs::UtxoIdDbV2(tx_hash, output_index as u32), ()),
+                    }
+                }
                 // Insert tx itself
                 txs.upsert(HashKeyV2(tx_hash), PendingTxDbV2(tx.into_owned()));
                 Ok(())
@@ -89,6 +112,8 @@ pub fn remove_all_pending_txs<B: Backend>(txs_mp_db: &TxsMpV2Db<B>) -> KvResult<
     txs_mp_db.txs_by_issuer_write().clear()?;
     txs_mp_db.txs_by_recipient_write().clear()?;
     txs_mp_db.txs_write().clear()?;
+    txs_mp_db.uds_ids_write().clear()?;
+    txs_mp_db.utxos_ids_write().clear()?;
 
     Ok(())
 }
@@ -128,26 +153,45 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) ->
             txs_mp_db.txs_by_issuer_write(),
             txs_mp_db.txs_by_recipient_write(),
             txs_mp_db.txs_write(),
+            txs_mp_db.uds_ids_write(),
+            txs_mp_db.utxos_ids_write(),
         )
-            .write(|(mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
-                // Remove tx hash in col `txs_by_issuer`
-                for pubkey in tx.0.issuers() {
-                    let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-                    hashs_.remove(&tx_hash);
-                    txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_)
-                }
-                // Remove tx hash in col `txs_by_recipient`
-                for pubkey in tx.0.recipients_keys() {
-                    let mut hashs_ = txs_by_recipient
-                        .get(&PubKeyKeyV2(pubkey))?
-                        .unwrap_or_default();
-                    hashs_.remove(&tx_hash);
-                    txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_)
-                }
-                // Remove tx itself
-                txs.remove(HashKeyV2(tx_hash));
-                Ok(true)
-            })
+            .write(
+                |(mut txs_by_issuer, mut txs_by_recipient, mut txs, mut uds_ids, mut utxos_ids)| {
+                    // Remove tx inputs in cols `uds_ids` and `utxos_ids`
+                    for input in tx.0.get_inputs() {
+                        match input.id {
+                            SourceIdV10::Ud(UdSourceIdV10 {
+                                issuer,
+                                block_number,
+                            }) => uds_ids.remove(duniter_dbs::UdIdV2(issuer, block_number)),
+                            SourceIdV10::Utxo(UtxoIdV10 {
+                                tx_hash,
+                                output_index,
+                            }) => utxos_ids
+                                .remove(duniter_dbs::UtxoIdDbV2(tx_hash, output_index as u32)),
+                        }
+                    }
+                    // Remove tx hash in col `txs_by_issuer`
+                    for pubkey in tx.0.issuers() {
+                        let mut hashs_ =
+                            txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
+                        hashs_.remove(&tx_hash);
+                        txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_)
+                    }
+                    // Remove tx hash in col `txs_by_recipient`
+                    for pubkey in tx.0.recipients_keys() {
+                        let mut hashs_ = txs_by_recipient
+                            .get(&PubKeyKeyV2(pubkey))?
+                            .unwrap_or_default();
+                        hashs_.remove(&tx_hash);
+                        txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_)
+                    }
+                    // Remove tx itself
+                    txs.remove(HashKeyV2(tx_hash));
+                    Ok(true)
+                },
+            )
     } else {
         Ok(false)
     }
diff --git a/rust-libs/duniter-dbs/src/keys.rs b/rust-libs/duniter-dbs/src/keys.rs
index 35cb4e6dc5aa8456eb1296ae661c9f164e920f2c..70afba42bffbfa157e84cf7f9ba1779a1b204863 100644
--- a/rust-libs/duniter-dbs/src/keys.rs
+++ b/rust-libs/duniter-dbs/src/keys.rs
@@ -23,4 +23,5 @@ pub mod source_key;
 pub mod timestamp;
 pub mod ud_id;
 pub mod uid;
+pub mod utxo_id;
 pub mod wallet_conditions;
diff --git a/rust-libs/duniter-dbs/src/keys/block_number.rs b/rust-libs/duniter-dbs/src/keys/block_number.rs
index fb651ddb1437c99626d1df738cb52226bf368a59..ffb804c674b36b3f8e9037a0610c55ee0fa490f5 100644
--- a/rust-libs/duniter-dbs/src/keys/block_number.rs
+++ b/rust-libs/duniter-dbs/src/keys/block_number.rs
@@ -61,51 +61,6 @@ impl ExplorableKey for BlockNumberKeyV1 {
     }
 }
 
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
-#[repr(transparent)]
-pub struct BlockNumberKeyV2(pub BlockNumber);
-
-impl KeyAsBytes for BlockNumberKeyV2 {
-    fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T {
-        f(&(self.0).0.to_be_bytes()[..])
-    }
-}
-
-impl FromBytes for BlockNumberKeyV2 {
-    type Err = StringErr;
-
-    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(Self(BlockNumber(
-            zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(bytes)
-                .ok_or_else(|| {
-                    StringErr(
-                        "Corrupted DB: BlockNumber bytes are invalid length or unaligned"
-                            .to_owned(),
-                    )
-                })?
-                .get(),
-        )))
-    }
-}
-
-impl ToDumpString for BlockNumberKeyV2 {
-    fn to_dump_string(&self) -> String {
-        todo!()
-    }
-}
-
-#[cfg(feature = "explorer")]
-impl ExplorableKey for BlockNumberKeyV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Ok(Self(
-            BlockNumber::from_str(source).map_err(|e| StringErr(format!("{}", e)))?,
-        ))
-    }
-    fn to_explorer_string(&self) -> KvResult<String> {
-        Ok(format!("{}", (self.0).0))
-    }
-}
-
 #[cfg(test)]
 mod tests {
 
@@ -116,12 +71,4 @@ mod tests {
         BlockNumberKeyV1(BlockNumber(35))
             .as_bytes(|bytes| assert_eq!(bytes, &[48, 48, 48, 48, 48, 48, 48, 48, 51, 53]))
     }
-
-    #[test]
-    fn block_number_key_v2() {
-        let k = BlockNumberKeyV2(BlockNumber(3));
-        k.as_bytes(|bytes| {
-            assert_eq!(bytes, &[0, 0, 0, 3]);
-        });
-    }
 }
diff --git a/rust-libs/duniter-dbs/src/keys/utxo_id.rs b/rust-libs/duniter-dbs/src/keys/utxo_id.rs
new file mode 100644
index 0000000000000000000000000000000000000000..93767ceb88981ebbc004cabfd636c5aad3b2396b
--- /dev/null
+++ b/rust-libs/duniter-dbs/src/keys/utxo_id.rs
@@ -0,0 +1,124 @@
+//  Copyright (C) 2020 Éloïs SANCHEZ.
+//
+// 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/>.
+
+use crate::*;
+use uninit::prelude::*;
+
+type OutputIndex = u32;
+
+#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
+pub struct UtxoIdDbV2(pub Hash, pub OutputIndex);
+
+impl PartialOrd for UtxoIdDbV2 {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        match self.0.partial_cmp(&other.0) {
+            Some(std::cmp::Ordering::Equal) => self.1.partial_cmp(&other.1),
+            o => o,
+        }
+    }
+}
+impl Ord for UtxoIdDbV2 {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match self.0.cmp(&other.0) {
+            std::cmp::Ordering::Equal => self.1.cmp(&other.1),
+            o => o,
+        }
+    }
+}
+
+impl KeyAsBytes for UtxoIdDbV2 {
+    fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T {
+        let mut buffer = uninit_array![u8; 36];
+        let (hash_buffer, index_buffer) = buffer.as_out().split_at_out(32);
+        let hash_buffer = hash_buffer.copy_from_slice(self.0.as_ref());
+        index_buffer.copy_from_slice(&(self.1).to_be_bytes());
+        f(unsafe { std::slice::from_raw_parts_mut(hash_buffer.as_mut_ptr(), 36) })
+    }
+}
+
+impl FromBytes for UtxoIdDbV2 {
+    type Err = StringErr;
+
+    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
+        let hash = zerocopy::LayoutVerified::<_, Hash>::new(&bytes[..32]).ok_or_else(|| {
+            StringErr("Corrupted DB: Hash bytes are invalid length or unaligned".to_owned())
+        })?;
+        let output_index =
+            zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(&bytes[32..])
+                .ok_or_else(|| {
+                    StringErr(
+                        "Corrupted DB: OutputIndex bytes are invalid length or unaligned"
+                            .to_owned(),
+                    )
+                })?
+                .get();
+        Ok(UtxoIdDbV2(*hash, output_index))
+    }
+}
+
+impl ToDumpString for UtxoIdDbV2 {
+    fn to_dump_string(&self) -> String {
+        todo!()
+    }
+}
+
+#[cfg(feature = "explorer")]
+impl ExplorableKey for UtxoIdDbV2 {
+    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+        let mut source = source.split(':');
+        if let Some(hash_str) = source.next() {
+            let hash =
+                Hash::from_hex(&hash_str).map_err(|e| StringErr(format!("{}: {}", e, hash_str)))?;
+            if let Some(output_index_str) = source.next() {
+                Ok(UtxoIdDbV2(
+                    hash,
+                    u32::from_str(output_index_str).map_err(|e| StringErr(format!("{}", e)))?,
+                ))
+            } else {
+                Err(StringErr("UtxoIdDbV2: Invalid format".to_owned()))
+            }
+        } else {
+            Err(StringErr("UtxoIdDbV2: Invalid format".to_owned()))
+        }
+    }
+    fn to_explorer_string(&self) -> KvResult<String> {
+        Ok(format!("{}:{}", self.0.to_hex(), self.1))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn utxo_id_v2_as_bytes() -> std::result::Result<(), StringErr> {
+        let utxo_id = UtxoIdDbV2(Hash::default(), 3);
+
+        let utxo_id_2_res = utxo_id.as_bytes(|bytes| {
+            assert_eq!(
+                bytes,
+                [
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 3
+                ]
+            );
+            UtxoIdDbV2::from_bytes(bytes)
+        });
+
+        assert_eq!(utxo_id_2_res?, utxo_id);
+
+        Ok(())
+    }
+}
diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs
index 710d1fe535f3a4174019b59b360acd21ed5b78a3..3b7251837aab1fd95128aea50eeb167d0a994cbb 100644
--- a/rust-libs/duniter-dbs/src/lib.rs
+++ b/rust-libs/duniter-dbs/src/lib.rs
@@ -58,6 +58,7 @@ pub use crate::errors::Result;
 pub use crate::open_dbs::open_dbs;
 
 // Export profession types
+pub use crate::keys::utxo_id::UtxoIdDbV2;
 pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlocksEvent, UidsEvent};
 pub use gva_v1::{GvaV1Db, GvaV1DbReadable, GvaV1DbRo, GvaV1DbWritable};
 pub use keys::all::AllKeyV1;
diff --git a/rust-libs/duniter-dbs/src/txs_mp_v2.rs b/rust-libs/duniter-dbs/src/txs_mp_v2.rs
index 95b6cbaebd94cbfe1e9b463c3c64616282514780..00574a255e60ea44372e09afd87b6b80d3bb29b0 100644
--- a/rust-libs/duniter-dbs/src/txs_mp_v2.rs
+++ b/rust-libs/duniter-dbs/src/txs_mp_v2.rs
@@ -22,5 +22,7 @@ db_schema!(
         ["txs_by_issuer", TxsByIssuer, PubKeyKeyV2, BTreeSet<Hash>],
         ["txs_by_recipient", TxsByRecipient, PubKeyKeyV2, BTreeSet<Hash>],
         ["txs_by_received_time", TxsByRecvTime, i64, BTreeSet<Hash>],
+        ["uds_ids", UdsIds, UdIdV2, ()],
+        ["utxos_ids", UtxosIds, UtxoIdDbV2, ()],
     ]
 );
diff --git a/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs b/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs
index 8889f7248bc6783355d8ef0b8c693ffdf3f86bd0..df736ad0fe539d78be7c39d24f7cc2d86f196ee7 100644
--- a/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs
+++ b/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs
@@ -15,9 +15,6 @@
 
 use crate::*;
 
-const MAX_UDS_INPUTS: usize = 20;
-const MAX_UTXOS_INPUTS: usize = 20;
-
 #[derive(Default)]
 pub(crate) struct GenTxsQuery;
 #[async_graphql::Object]
@@ -45,62 +42,14 @@ impl GenTxsQuery {
         let (current_block, inputs, inputs_sum) = data
             .dbs_pool
             .execute(move |dbs| {
-                if let Some(current_block) =
-                    duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)?
-                {
-                    let (uds, uds_sum) = duniter_dbs_read_ops::uds_of_pubkey::uds_of_pubkey(
-                        &dbs.bc_db,
-                        issuer,
-                        ..,
-                        Some(MAX_UDS_INPUTS),
-                        Some(amount),
-                    )?;
-                    let mut inputs = uds
-                        .into_iter()
-                        .map(
-                            |(block_number, source_amount)| TransactionInputV10 {
-                                amount: source_amount,
-                                id: SourceIdV10::Ud(UdSourceIdV10 {
-                                    issuer,
-                                    block_number,
-                                }),
-                            },
-                        )
-                        .collect::<Vec<_>>();
-                    if uds_sum < amount {
-                        let (utxos, utxos_sum) = duniter_dbs_read_ops::utxos::get_script_utxos(
-                            &dbs.gva_db,
-                            &WalletScriptV10::single(WalletConditionV10::Sig(issuer)),
-                            Some(MAX_UTXOS_INPUTS),
-                            Some(amount - uds_sum),
-                        )?;
-                        inputs.extend(utxos.into_iter()
-                        .map(
-                            |(_written_time, utxo_id, source_amount)| TransactionInputV10 {
-                                amount: source_amount,
-                                id: SourceIdV10::Utxo(utxo_id),
-                            },
-                        ));
-                        let inputs_sum = uds_sum + utxos_sum;
-                        if inputs_sum < amount {
-                            Err(KvError::Custom("Amount need too many sources or issuer's account has an insufficient balance.".into()))
-                        } else {
-                            Ok::<_, KvError>((
-                                current_block,
-                                inputs,
-                                inputs_sum
-                            ))
-                        }
-                    } else {
-                        Ok::<_, KvError>((
-                            current_block,
-                            inputs,
-                            uds_sum
-                        ))
-                    }
-                } else {
-                    Err(KvError::Custom("no blockchain".into()))
-                }
+                duniter_dbs_read_ops::find_inputs::find_inputs(
+                    &dbs.bc_db,
+                    &dbs.gva_db,
+                    &dbs.txs_mp_db,
+                    amount,
+                    &WalletScriptV10::single(WalletConditionV10::Sig(issuer)),
+                    40,
+                )
             })
             .await??;
 
diff --git a/rust-libs/modules/duniter-gva/src/queries/uds.rs b/rust-libs/modules/duniter-gva/src/queries/uds.rs
index 7cab2002838c19e1422775a411d671e046b9b29e..bc36f3b8f688ad7dba026256dca2c1a4ec857931 100644
--- a/rust-libs/modules/duniter-gva/src/queries/uds.rs
+++ b/rust-libs/modules/duniter-gva/src/queries/uds.rs
@@ -55,6 +55,7 @@ impl UdsQuery {
                     ..,
                     None,
                     None,
+                    None,
                 )
             })
             .await??;
diff --git a/rust-libs/modules/duniter-gva/src/queries/utxos.rs b/rust-libs/modules/duniter-gva/src/queries/utxos.rs
index 60ae3437a2013e96ba1b46b5ed471a580d728bcd..911748f15c8a13771ec2bf729e901266e223fe52 100644
--- a/rust-libs/modules/duniter-gva/src/queries/utxos.rs
+++ b/rust-libs/modules/duniter-gva/src/queries/utxos.rs
@@ -36,9 +36,7 @@ impl UtxosQuery {
 
         let (utxos, _balance) = data
             .dbs_pool
-            .execute(move |dbs| {
-                duniter_dbs_read_ops::utxos::get_script_utxos(&dbs.gva_db, &script, None, None)
-            })
+            .execute(move |dbs| duniter_dbs_read_ops::utxos::get_script_utxos(&dbs.gva_db, &script))
             .await??;
 
         let utxos: Vec<UtxoGva> = utxos