diff --git a/pallets/duniter-account/src/lib.rs b/pallets/duniter-account/src/lib.rs
index 1b7f59f8a19933cae41c313f5600e9663060c4d5..bfe802c11b9ef7d92f1b59589a78414e10fb443d 100644
--- a/pallets/duniter-account/src/lib.rs
+++ b/pallets/duniter-account/src/lib.rs
@@ -148,8 +148,8 @@ pub mod pallet {
         },
         OneshotAccountConsumed {
             account: T::AccountId,
-            balance: T::Balance,
-            dest: T::AccountId,
+            dest1: (T::AccountId, T::Balance),
+            dest2: Option<(T::AccountId, T::Balance)>,
         },
         /// Random id assigned
         /// [account_id, random_id]
@@ -160,15 +160,19 @@ pub mod pallet {
 
     #[pallet::error]
     pub enum Error<T> {
-        /// DestAccountNotExist
+        /// Block height is in the future
+        BlockHeightInFuture,
+        /// Block height is too old
+        BlockHeightTooOld,
+        /// Destination account does not exist
         DestAccountNotExist,
-        /// ExistentialDeposit
+        /// Destination account has balance less than existential deposit
         ExistentialDeposit,
-        /// InsufficientBalance
+        /// Source account has insufficient balance
         InsufficientBalance,
-        /// OneshotAccouncAlreadyCreated
+        /// Destination oneshot account already exists
         OneshotAccountAlreadyCreated,
-        /// OneshotAccountNotExist
+        /// Source oneshot account does not exist
         OneshotAccountNotExist,
     }
 
@@ -254,6 +258,10 @@ pub mod pallet {
     // CALLS //
     #[pallet::call]
     impl<T: Config> Pallet<T> {
+        /// Create an account that can only be consumed once
+        ///
+        /// - `dest`: The oneshot account to be created.
+        /// - `balance`: The balance to be transfered to this oneshot account.
         #[pallet::weight(500_000_000)]
         pub fn create_oneshot_account(
             origin: OriginFor<T>,
@@ -292,9 +300,14 @@ pub mod pallet {
 
             Ok(())
         }
+        /// Consume a oneshot account and transfer its balance to an account
+        ///
+        /// - `block_height`: must be a recent block number. The limit is `BlockHashCount` in the past. (this is to prevent replay attacks)
+        /// - `dest`: `dest.1` is the destination account. If `dest.0` is `true`, then a oneshot account is created at `dest.1`. Else, `dest.1` has to be an existing account.
         #[pallet::weight(500_000_000)]
         pub fn consume_oneshot_account(
             origin: OriginFor<T>,
+            block_height: T::BlockNumber,
             dest: (bool, <T::Lookup as StaticLookup>::Source),
         ) -> DispatchResult {
             let transactor = ensure_signed(origin)?;
@@ -304,31 +317,121 @@ pub mod pallet {
             let value = OneshotAccounts::<T>::take(&transactor)
                 .ok_or(Error::<T>::OneshotAccountNotExist)?;
 
+            ensure!(
+                block_height <= frame_system::Pallet::<T>::block_number(),
+                Error::<T>::BlockHeightInFuture
+            );
+            ensure!(
+                frame_system::pallet::BlockHash::<T>::contains_key(block_height),
+                Error::<T>::BlockHeightTooOld
+            );
             if dest_is_oneshot {
                 ensure!(
                     OneshotAccounts::<T>::get(&dest).is_none(),
                     Error::<T>::OneshotAccountAlreadyCreated
                 );
                 OneshotAccounts::<T>::insert(&dest, value);
-                Self::deposit_event(Event::OneshotAccountConsumed {
-                    account: transactor.clone(),
-                    balance: value,
-                    dest: dest.clone(),
-                });
                 Self::deposit_event(Event::OneshotAccountCreated {
-                    account: dest,
+                    account: dest.clone(),
                     balance: value,
-                    creator: transactor,
+                    creator: transactor.clone(),
                 });
             } else {
                 let dest_data = frame_system::Account::<T>::get(&dest).data;
                 ensure!(dest_data.was_providing(), Error::<T>::DestAccountNotExist);
-                Self::deposit_event(Event::OneshotAccountConsumed {
-                    account: transactor,
-                    balance: value,
-                    dest,
+                frame_system::Account::<T>::mutate(&dest, |a| a.data.add_free(value));
+            }
+            OneshotAccounts::<T>::remove(&transactor);
+            Self::deposit_event(Event::OneshotAccountConsumed {
+                account: transactor,
+                dest1: (dest, value),
+                dest2: None,
+            });
+
+            Ok(())
+        }
+        /// Consume a oneshot account and transfer its balance to two accounts
+        ///
+        /// - `block_height`: must be a recent block number. The limit is `BlockHashCount` in the past. (this is to prevent replay attacks)
+        /// - `dest1`: `dest1.1` is the destination account. If `dest1.0` is `true`, then a oneshot account is created at `dest1.1`. Else, `dest1.1` has to be an existing account.
+        /// - `dest2`: Idem.
+        /// - `balance1`: The amount transfered to `dest1`, the leftover being transfered to `dest2`.
+        #[pallet::weight(500_000_000)]
+        pub fn consume_oneshot_account_two_dests(
+            origin: OriginFor<T>,
+            block_height: T::BlockNumber,
+            dest1: (bool, <T::Lookup as StaticLookup>::Source),
+            dest2: (bool, <T::Lookup as StaticLookup>::Source),
+            #[pallet::compact] balance1: T::Balance,
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+            let dest1_is_oneshot = dest1.0;
+            let dest1 = T::Lookup::lookup(dest1.1)?;
+            let dest2_is_oneshot = dest2.0;
+            let dest2 = T::Lookup::lookup(dest2.1)?;
+
+            let value = OneshotAccounts::<T>::take(&transactor)
+                .ok_or(Error::<T>::OneshotAccountNotExist)?;
+
+            ensure!(value > balance1, Error::<T>::InsufficientBalance);
+            let balance2 = value.saturating_sub(balance1);
+            ensure!(
+                block_height <= frame_system::Pallet::<T>::block_number(),
+                Error::<T>::BlockHeightInFuture
+            );
+            ensure!(
+                frame_system::pallet::BlockHash::<T>::contains_key(block_height),
+                Error::<T>::BlockHeightTooOld
+            );
+            if dest1_is_oneshot {
+                ensure!(
+                    OneshotAccounts::<T>::get(&dest1).is_none(),
+                    Error::<T>::OneshotAccountAlreadyCreated
+                );
+                ensure!(
+                    balance1 >= T::ExistentialDeposit::get(),
+                    Error::<T>::ExistentialDeposit
+                );
+            } else {
+                let dest1_data = frame_system::Account::<T>::get(&dest1).data;
+                ensure!(dest1_data.was_providing(), Error::<T>::DestAccountNotExist);
+            }
+            if dest2_is_oneshot {
+                ensure!(
+                    OneshotAccounts::<T>::get(&dest2).is_none(),
+                    Error::<T>::OneshotAccountAlreadyCreated
+                );
+                ensure!(
+                    balance2 >= T::ExistentialDeposit::get(),
+                    Error::<T>::ExistentialDeposit
+                );
+                OneshotAccounts::<T>::insert(&dest2, balance2);
+                Self::deposit_event(Event::OneshotAccountCreated {
+                    account: dest2.clone(),
+                    balance: balance2,
+                    creator: transactor.clone(),
                 });
+            } else {
+                let dest2_data = frame_system::Account::<T>::get(&dest2).data;
+                ensure!(dest2_data.was_providing(), Error::<T>::DestAccountNotExist);
+                frame_system::Account::<T>::mutate(&dest2, |a| a.data.add_free(balance2));
             }
+            if dest1_is_oneshot {
+                OneshotAccounts::<T>::insert(&dest1, balance1);
+                Self::deposit_event(Event::OneshotAccountCreated {
+                    account: dest1.clone(),
+                    balance: balance1,
+                    creator: transactor.clone(),
+                });
+            } else {
+                frame_system::Account::<T>::mutate(&dest1, |a| a.data.add_free(balance1));
+            }
+            OneshotAccounts::<T>::remove(&transactor);
+            Self::deposit_event(Event::OneshotAccountConsumed {
+                account: transactor,
+                dest1: (dest1, balance1),
+                dest2: Some((dest2, balance2)),
+            });
 
             Ok(())
         }
diff --git a/pallets/duniter-account/src/types.rs b/pallets/duniter-account/src/types.rs
index 8f1a6b16bbba8ac057724c09b5de640a12d70d64..486be537bf5b7b2797f089304e4d37b0f29b21a9 100644
--- a/pallets/duniter-account/src/types.rs
+++ b/pallets/duniter-account/src/types.rs
@@ -29,6 +29,9 @@ pub struct AccountData<Balance> {
 }
 
 impl<Balance: Copy + Saturating + Zero> AccountData<Balance> {
+    pub fn add_free(&mut self, amount: Balance) {
+        self.free = self.free.saturating_add(amount);
+    }
     pub fn free_and_reserved(&self) -> Balance {
         self.free.saturating_add(self.reserved)
     }
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index 6ffc2f586ac041a0875188afb00441dcc62a753b..014b52940796e2c50e939e64db85daaa2cce19a1 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -344,3 +344,78 @@ fn test_create_new_idty() {
             );
         });
 }
+
+#[test]
+fn test_oneshot_accounts() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![(AccountKeyring::Alice.to_account_id(), 1_000)])
+        .build()
+        .execute_with(|| {
+            run_to_block(6);
+
+            assert_ok!(Account::create_oneshot_account(
+                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
+                400
+            ));
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                600
+            );
+            run_to_block(7);
+
+            assert_ok!(Account::consume_oneshot_account_two_dests(
+                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                0,
+                (
+                    true,
+                    MultiAddress::Id(AccountKeyring::Ferdie.to_account_id())
+                ),
+                (
+                    false,
+                    MultiAddress::Id(AccountKeyring::Alice.to_account_id())
+                ),
+                300
+            ));
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                700
+            );
+            assert_err!(
+                Account::consume_oneshot_account(
+                    frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                    0,
+                    (
+                        true,
+                        MultiAddress::Id(AccountKeyring::Ferdie.to_account_id())
+                    ),
+                ),
+                pallet_duniter_account::Error::<Runtime>::OneshotAccountNotExist
+            );
+            run_to_block(8);
+
+            assert_ok!(Account::consume_oneshot_account(
+                frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into(),
+                0,
+                (
+                    false,
+                    MultiAddress::Id(AccountKeyring::Alice.to_account_id())
+                ),
+            ));
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                1000
+            );
+            assert_err!(
+                Account::consume_oneshot_account(
+                    frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                    0,
+                    (
+                        false,
+                        MultiAddress::Id(AccountKeyring::Alice.to_account_id())
+                    ),
+                ),
+                pallet_duniter_account::Error::<Runtime>::OneshotAccountNotExist
+            );
+        });
+}