diff --git a/end2end-tests/cucumber-features/oneshot_account.feature b/end2end-tests/cucumber-features/oneshot_account.feature
new file mode 100644
index 0000000000000000000000000000000000000000..f3a92de27bf2d6f405682361bbbb2d0efbd5ab9d
--- /dev/null
+++ b/end2end-tests/cucumber-features/oneshot_account.feature
@@ -0,0 +1,21 @@
+Feature: Oneshot account
+
+  Scenario: Simple oneshot consumption
+    When alice sends 8 ÄžD to oneshot dave
+    Then alice should have 2 ÄžD
+    Then dave should have oneshot 8 ÄžD
+    When oneshot dave consumes into account bob
+    Then dave should have oneshot 0 ÄžD
+    Then bob should have 18 ÄžD
+    Then bob should have oneshot 0 ÄžD
+
+  Scenario: Double oneshot consumption
+    When alice sends 8 ÄžD to oneshot dave
+    Then alice should have 2 ÄžD
+    Then dave should have oneshot 8 ÄžD
+    When oneshot dave consumes 6 ÄžD into account bob and the rest into oneshot charlie
+    Then dave should have oneshot 0 ÄžD
+    Then bob should have 16 ÄžD
+    Then bob should have oneshot 0 ÄžD
+    Then charlie should have 10 ÄžD
+    Then charlie should have oneshot 2 ÄžD
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index 078abd747617b2e2c04a6392e796d5e5ccd75507..8ee8431042481dc3f208d32f7d9764e330423c01 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -17,6 +17,7 @@
 #![allow(clippy::enum_variant_names, dead_code, unused_imports)]
 
 pub mod balances;
+pub mod oneshot;
 
 #[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")]
 pub mod node_runtime {}
@@ -28,7 +29,7 @@ use std::path::PathBuf;
 use std::process::Command;
 use std::str::FromStr;
 use subxt::rpc::{rpc_params, ClientT, SubscriptionClientT};
-use subxt::{ClientBuilder, DefaultConfig, DefaultExtra};
+use subxt::{ClientBuilder, DefaultConfig, DefaultExtra, PairSigner};
 
 pub type Api = node_runtime::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>;
 pub type Client = subxt::Client<DefaultConfig>;
diff --git a/end2end-tests/tests/common/oneshot.rs b/end2end-tests/tests/common/oneshot.rs
new file mode 100644
index 0000000000000000000000000000000000000000..acb8a99f33e986d501a182aed3bdf108b450ec85
--- /dev/null
+++ b/end2end-tests/tests/common/oneshot.rs
@@ -0,0 +1,101 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency 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, version 3 of the License.
+//
+// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use super::node_runtime::runtime_types::gdev_runtime;
+use super::node_runtime::runtime_types::pallet_balances;
+use super::*;
+use sp_keyring::AccountKeyring;
+use subxt::{sp_runtime::MultiAddress, PairSigner};
+
+pub async fn create_oneshot_account(
+    api: &Api,
+    client: &Client,
+    from: AccountKeyring,
+    amount: u64,
+    to: AccountKeyring,
+) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+    let to = to.to_account_id();
+
+    let _events = create_block_with_extrinsic(
+        client,
+        api.tx()
+            .account()
+            .create_oneshot_account(to.clone().into(), amount)
+            .create_signed(&from, ())
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
+
+pub async fn consume_oneshot_account(
+    api: &Api,
+    client: &Client,
+    from: AccountKeyring,
+    to: AccountKeyring,
+    is_dest_oneshot: bool,
+) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+    let to = to.to_account_id();
+
+    let _events = create_block_with_extrinsic(
+        client,
+        api.tx()
+            .account()
+            .consume_oneshot_account(0, to.clone().into(), is_dest_oneshot)
+            .create_signed(&from, ())
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
+
+pub async fn consume_oneshot_account_two_dests(
+    api: &Api,
+    client: &Client,
+    from: AccountKeyring,
+    amount: u64,
+    to1: AccountKeyring,
+    is_dest1_oneshot: bool,
+    to2: AccountKeyring,
+    is_dest2_oneshot: bool,
+) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+    let to1 = to1.to_account_id();
+    let to2 = to2.to_account_id();
+
+    let _events = create_block_with_extrinsic(
+        client,
+        api.tx()
+            .account()
+            .consume_oneshot_account_two_dests(
+                0,
+                to1.into(),
+                is_dest1_oneshot,
+                to2.into(),
+                is_dest2_oneshot,
+                amount,
+            )
+            .create_signed(&from, ())
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index ffe54030a61315e4f916611d0b95c6003594f379..5ea462fffbe920c9b6c97de22da4970fafb81f9c 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -155,6 +155,94 @@ async fn transfer(
     }
 }
 
+#[when(regex = r"([a-zA-Z]+) sends (\d+) (ÄžD|cÄžD) to oneshot ([a-zA-Z]+)")]
+async fn create_oneshot_account(
+    world: &mut DuniterWorld,
+    from: String,
+    amount: u64,
+    unit: String,
+    to: String,
+) -> Result<()> {
+    // Parse inputs
+    let from = AccountKeyring::from_str(&from).expect("unknown from");
+    let to = AccountKeyring::from_str(&to).expect("unknown to");
+    let (amount, is_ud) = parse_amount(amount, &unit);
+
+    assert!(!is_ud);
+
+    common::oneshot::create_oneshot_account(&world.api(), &world.client(), from, amount, to).await
+}
+
+#[when(regex = r"oneshot ([a-zA-Z]+) consumes into (oneshot|account) ([a-zA-Z]+)")]
+async fn consume_oneshot_account(
+    world: &mut DuniterWorld,
+    from: String,
+    is_dest_oneshot: String,
+    to: String,
+) -> Result<()> {
+    // Parse inputs
+    let from = AccountKeyring::from_str(&from).expect("unknown from");
+    let to = AccountKeyring::from_str(&to).expect("unknown to");
+    let is_dest_oneshot = match is_dest_oneshot.as_str() {
+        "oneshot" => true,
+        "account" => false,
+        _ => unreachable!(),
+    };
+
+    common::oneshot::consume_oneshot_account(
+        &world.api(),
+        &world.client(),
+        from,
+        to,
+        is_dest_oneshot,
+    )
+    .await
+}
+
+#[when(
+    regex = r"oneshot ([a-zA-Z]+) consumes (\d+) (ÄžD|cÄžD) into (oneshot|account) ([a-zA-Z]+) and the rest into (oneshot|account) ([a-zA-Z]+)"
+)]
+async fn consume_oneshot_account_two_dests(
+    world: &mut DuniterWorld,
+    from: String,
+    amount: u64,
+    unit: String,
+    is_dest1_oneshot: String,
+    to1: String,
+    is_dest2_oneshot: String,
+    to2: String,
+) -> Result<()> {
+    // Parse inputs
+    let from = AccountKeyring::from_str(&from).expect("unknown from");
+    let to1 = AccountKeyring::from_str(&to1).expect("unknown to1");
+    let to2 = AccountKeyring::from_str(&to2).expect("unknown to2");
+    let is_dest1_oneshot = match is_dest1_oneshot.as_str() {
+        "oneshot" => true,
+        "account" => false,
+        _ => unreachable!(),
+    };
+    let is_dest2_oneshot = match is_dest2_oneshot.as_str() {
+        "oneshot" => true,
+        "account" => false,
+        _ => unreachable!(),
+    };
+    let (amount, is_ud) = parse_amount(amount, &unit);
+
+    assert!(!is_ud);
+
+    common::oneshot::consume_oneshot_account_two_dests(
+        &world.api(),
+        &world.client(),
+        from,
+        amount,
+        to1,
+        is_dest1_oneshot,
+        to2,
+        is_dest2_oneshot,
+    )
+    .await
+}
+
 #[when(regex = r"([a-zA-Z]+) sends all (?:his|her) (?:ÄžDs?|DUs?) to ([a-zA-Z]+)")]
 async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Result<()> {
     // Parse inputs
@@ -177,6 +265,24 @@ async fn should_have(world: &mut DuniterWorld, who: String, amount: u64) -> Resu
     Ok(())
 }
 
+#[then(regex = r"([a-zA-Z]+) should have oneshot (\d+) ÄžD")]
+async fn should_have_oneshot(world: &mut DuniterWorld, who: String, amount: u64) -> Result<()> {
+    // Parse inputs
+    let who = AccountKeyring::from_str(&who)
+        .expect("unknown to")
+        .to_account_id();
+    let amount = amount * 100;
+
+    let oneshot_amount = world
+        .api()
+        .storage()
+        .account()
+        .oneshot_accounts(who, None)
+        .await?;
+    assert_eq!(oneshot_amount.unwrap_or(0), amount);
+    Ok(())
+}
+
 #[then(regex = r"Current UD amount should be (\d+).(\d+)")]
 async fn current_ud_amount_should_be(
     world: &mut DuniterWorld,
diff --git a/pallets/duniter-account/src/lib.rs b/pallets/duniter-account/src/lib.rs
index 1b7f59f8a19933cae41c313f5600e9663060c4d5..b848a4b51d2f7a1e0c56769ab5344ca11c986c7c 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,43 +300,142 @@ 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`: The destination account.
+        /// - `dest_is_oneshot`: If set to `true`, then a oneshot account is created at `dest`. Else, `dest` has to be an existing account.
         #[pallet::weight(500_000_000)]
         pub fn consume_oneshot_account(
             origin: OriginFor<T>,
-            dest: (bool, <T::Lookup as StaticLookup>::Source),
+            block_height: T::BlockNumber,
+            dest: <T::Lookup as StaticLookup>::Source,
+            dest_is_oneshot: bool,
         ) -> DispatchResult {
             let transactor = ensure_signed(origin)?;
-            let dest_is_oneshot = dest.0;
-            let dest = T::Lookup::lookup(dest.1)?;
+            let dest = T::Lookup::lookup(dest)?;
 
             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)
+        /// - `dest`: The first destination account.
+        /// - `dest_is_oneshot`: If set to `true`, then a oneshot account is created at `dest`. Else, `dest` has to be an existing account.
+        /// - `dest2`: The second destination account.
+        /// - `dest2_is_oneshot`: If set to `true`, then a oneshot account is created at `dest2`. Else, `dest2` has to be an existing account.
+        /// - `balance1`: The amount transfered to `dest`, 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,
+            dest: <T::Lookup as StaticLookup>::Source,
+            dest_is_oneshot: bool,
+            dest2: <T::Lookup as StaticLookup>::Source,
+            dest2_is_oneshot: bool,
+            #[pallet::compact] balance: T::Balance,
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+            let dest1 = T::Lookup::lookup(dest)?;
+            let dest2 = T::Lookup::lookup(dest2)?;
+
+            let value = OneshotAccounts::<T>::take(&transactor)
+                .ok_or(Error::<T>::OneshotAccountNotExist)?;
+
+            let balance1 = balance;
+            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 dest_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 dest_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/mock.rs b/pallets/duniter-account/src/mock.rs
new file mode 100644
index 0000000000000000000000000000000000000000..aaffbb4cb9056f15a0a2d63cedfdb25be1c29856
--- /dev/null
+++ b/pallets/duniter-account/src/mock.rs
@@ -0,0 +1,142 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency 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, version 3 of the License.
+//
+// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use super::*;
+use crate::{self as pallet_duniter_account};
+use frame_support::{parameter_types, traits::Everything, PalletId};
+use frame_system as system;
+use sp_core::H256;
+use sp_runtime::{
+	testing::{Header, TestSignature, UintAuthorityId},
+	traits::{BlakeTwo256, IdentityLookup},
+	{Perbill, Permill},
+};
+use std::collections::BTreeMap;
+
+type AccountId = u64;
+type Balance = u64;
+type Block = frame_system::mocking::MockBlock<Test>;
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+
+// Configure a mock runtime to test the pallet.
+frame_support::construct_runtime!(
+	pub enum Test where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic,
+	{
+		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
+		Account: pallet_duniter_account::{Pallet, Storage, Config<T>, Event<T>},
+		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
+		ProvideRandomness: pallet_provide_randomness::{Pallet, Call, Storage, Event},
+	}
+);
+
+// System
+parameter_types! {
+	pub const BlockHashCount: u64 = 250;
+	pub const SS58Prefix: u8 = 42;
+}
+
+impl system::Config for Test {
+	type BaseCallFilter = Everything;
+	type BlockWeights = ();
+	type BlockLength = ();
+	type DbWeight = ();
+	type Origin = Origin;
+	type Call = Call;
+	type Index = u64;
+	type BlockNumber = u64;
+	type Hash = H256;
+	type Hashing = BlakeTwo256;
+	type AccountId = AccountId;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Header = Header;
+	type Event = Event;
+	type BlockHashCount = BlockHashCount;
+	type Version = ();
+	type PalletInfo = PalletInfo;
+	type AccountData = types::AccountData<Balance>;
+	type OnNewAccount = ();
+	type OnKilledAccount = ();
+	type SystemWeightInfo = ();
+	type SS58Prefix = SS58Prefix;
+	type OnSetCode = ();
+	type MaxConsumers = frame_support::traits::ConstU32<16>;
+}
+
+parameter_types! {
+	pub const ExistentialDeposit: Balance = 1;
+	pub const MaxLocks: u32 = 50;
+}
+
+impl pallet_balances::Config for Test {
+	type Balance = Balance;
+	type DustRemoval = ();
+	type Event = Event;
+	type ExistentialDeposit = ExistentialDeposit;
+	type AccountStore = System;
+	type WeightInfo = pallet_balances::weights::SubstrateWeight<Test>;
+	type MaxLocks = MaxLocks;
+	type MaxReserves = ();
+	type ReserveIdentifier = [u8; 8];
+}
+
+parameter_types! {
+	pub const Burn: Permill = Permill::zero();
+	pub const ProposalBond: Permill = Permill::from_percent(1);
+	pub const ProposalBondMaximum: Option<Balance> = None;
+	pub const SpendPeriod: <Test as system::Config>::BlockNumber = 10;
+	// Treasury account address:
+	// gdev/gtest: 5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z
+	pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
+}
+impl pallet_treasury::Config for Test {
+	type ApproveOrigin = ();
+	type Burn = Burn;
+	type BurnDestination = ();
+	type Currency = Balances;
+	type Event = Event;
+	type OnSlash = pallet_treasury::Treasury;
+	type ProposalBond = ProposalBond;
+	type ProposalBondMinimum = frame_support::traits::ConstU64<10_000>;
+	type ProposalBondMaximum = ProposalBondMaximum;
+	type MaxApprovals = frame_support::traits::ConstU32<100>;
+	type PalletId = TreasuryPalletId;
+	type RejectOrigin = ();
+	type SpendFunds = ();
+	type SpendPeriod = SpendPeriod;
+	type WeightInfo = pallet_treasury::weights::SubstrateWeight<Self>;
+}
+
+impl pallet_provide_randomness::Config for Runtime {
+	type Currency = Balances;
+	type Event = Event;
+	type GetCurrentEpochIndex = GetCurrentEpochIndex<Self>;
+	type MaxRequests = frame_support::traits::ConstU32<100>;
+	type RequestPrice = frame_support::traits::ConstU64<2_000>;
+	type OnFilledRandomness = Account;
+	type OnUnbalanced = Treasury;
+	type CurrentBlockRandomness = pallet_babe::CurrentBlockRandomness<Self>;
+	type RandomnessFromOneEpochAgo = pallet_babe::RandomnessFromOneEpochAgo<Self>;
+}
+
+impl pallet_duniter_account::Config for Test {
+	type AccountIdToSalt = sp_runtime::traits::ConvertInto;
+	type Event = Event;
+	type MaxNewAccountsPerBlock = frame_support::pallet_prelude::ConstU32<1>;
+	type NewAccountPrice = frame_support::traits::ConstU64<300>;
+}
diff --git a/pallets/duniter-account/src/tests.rs b/pallets/duniter-account/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..86934326a4adfd636207a7b2abba999efebdd401
--- /dev/null
+++ b/pallets/duniter-account/src/tests.rs
@@ -0,0 +1,15 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency 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, version 3 of the License.
+//
+// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
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/resources/metadata.scale b/resources/metadata.scale
index 1f01cc4a16551841453d80d1d36da2e23037db3f..098f7c83f194cb5a3a6da8537a6ded2a4ff2e3c1 100644
Binary files a/resources/metadata.scale and b/resources/metadata.scale differ
diff --git a/runtime/gdev/src/check_nonce.rs b/runtime/gdev/src/check_nonce.rs
new file mode 100644
index 0000000000000000000000000000000000000000..52d24c7ba4a05c76282a5cdc6526a58d096f45e5
--- /dev/null
+++ b/runtime/gdev/src/check_nonce.rs
@@ -0,0 +1,83 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency 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, version 3 of the License.
+//
+// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use crate::Runtime;
+
+use codec::{Decode, Encode};
+use frame_system::Config;
+use scale_info::TypeInfo;
+use sp_runtime::{
+    traits::{DispatchInfoOf, SignedExtension},
+    transaction_validity::{TransactionValidity, TransactionValidityError},
+};
+
+#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
+#[scale_info(skip_type_params(Runtime))]
+pub struct DuniterCheckNonce(pub frame_system::CheckNonce<Runtime>);
+
+impl sp_std::fmt::Debug for DuniterCheckNonce {
+    #[cfg(feature = "std")]
+    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+        write!(f, "DuniterCheckNonce({})", self.0 .0)
+    }
+
+    #[cfg(not(feature = "std"))]
+    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+        Ok(())
+    }
+}
+
+impl SignedExtension for DuniterCheckNonce {
+    type AccountId = <Runtime as Config>::AccountId;
+    type Call = <Runtime as Config>::Call;
+    type AdditionalSigned = ();
+    type Pre = ();
+    const IDENTIFIER: &'static str = "DuniterCheckNonce";
+
+    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {
+        self.0.additional_signed()
+    }
+
+    fn pre_dispatch(
+        self,
+        who: &Self::AccountId,
+        call: &Self::Call,
+        info: &DispatchInfoOf<Self::Call>,
+        len: usize,
+    ) -> Result<(), TransactionValidityError> {
+        if matches!(
+            call,
+            Self::Call::Account(
+                pallet_duniter_account::Call::consume_oneshot_account { .. }
+                    | pallet_duniter_account::Call::consume_oneshot_account_two_dests { .. }
+            )
+        ) {
+            Ok(())
+        } else {
+            self.0.pre_dispatch(who, call, info, len)
+        }
+    }
+
+    fn validate(
+        &self,
+        who: &Self::AccountId,
+        call: &Self::Call,
+        info: &DispatchInfoOf<Self::Call>,
+        len: usize,
+    ) -> TransactionValidity {
+        self.0.validate(who, call, info, len)
+    }
+}
diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs
index 3b57eda4a73b771865b7ee71b7c4ea50476a1b65..fe26de099f4c1458e9c0740dde0109ed5167be9e 100644
--- a/runtime/gdev/src/lib.rs
+++ b/runtime/gdev/src/lib.rs
@@ -22,6 +22,7 @@
 #[cfg(feature = "std")]
 include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
 
+mod check_nonce;
 pub mod parameters;
 
 pub use self::parameters::*;
@@ -110,7 +111,7 @@ pub type SignedExtra = (
     frame_system::CheckTxVersion<Runtime>,
     frame_system::CheckGenesis<Runtime>,
     frame_system::CheckEra<Runtime>,
-    frame_system::CheckNonce<Runtime>,
+    check_nonce::DuniterCheckNonce,
     frame_system::CheckWeight<Runtime>,
     pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
 );
@@ -250,7 +251,7 @@ construct_runtime!(
     {
         // Basic stuff
         System: frame_system::{Pallet, Call, Config, Storage, Event<T>} = 0,
-        Account: pallet_duniter_account::{Pallet, Storage, Config<T>, Event<T>} = 1,
+        Account: pallet_duniter_account::{Pallet, Call, Storage, Config<T>, Event<T>} = 1,
         Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>} = 2,
 
         // Block creation
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index 6ffc2f586ac041a0875188afb00441dcc62a753b..e28b616b3bf911f8b4b31d68796174b513eceec3 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -344,3 +344,76 @@ 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),
+            (AccountKeyring::Eve.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,
+                MultiAddress::Id(AccountKeyring::Ferdie.to_account_id()),
+                true,
+                MultiAddress::Id(AccountKeyring::Alice.to_account_id()),
+                false,
+                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,
+                    MultiAddress::Id(AccountKeyring::Ferdie.to_account_id()),
+                    true
+                ),
+                pallet_duniter_account::Error::<Runtime>::OneshotAccountNotExist
+            );
+            run_to_block(8);
+            // Oneshot account consumption should not increment the nonce
+            assert_eq!(
+                System::account(AccountKeyring::Eve.to_account_id()).nonce,
+                0
+            );
+
+            assert_ok!(Account::consume_oneshot_account(
+                frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into(),
+                0,
+                MultiAddress::Id(AccountKeyring::Alice.to_account_id()),
+                false,
+            ));
+            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,
+                    MultiAddress::Id(AccountKeyring::Alice.to_account_id()),
+                    false
+                ),
+                pallet_duniter_account::Error::<Runtime>::OneshotAccountNotExist
+            );
+        });
+}