diff --git a/Cargo.lock b/Cargo.lock
index 0029d6e7fe7320d7135d04c34c0b2c8783f572a5..6e5f707fe33d400603fff74b42dead30f4f1b6be 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -887,6 +887,7 @@ dependencies = [
  "pallet-identity",
  "pallet-membership",
  "pallet-multisig",
+ "pallet-oneshot-account",
  "pallet-provide-randomness",
  "pallet-proxy",
  "pallet-scheduler",
@@ -2426,6 +2427,7 @@ dependencies = [
  "pallet-membership",
  "pallet-multisig",
  "pallet-offences",
+ "pallet-oneshot-account",
  "pallet-preimage",
  "pallet-provide-randomness",
  "pallet-proxy",
@@ -2494,6 +2496,7 @@ dependencies = [
  "pallet-membership",
  "pallet-multisig",
  "pallet-offences",
+ "pallet-oneshot-account",
  "pallet-preimage",
  "pallet-provide-randomness",
  "pallet-proxy",
@@ -2776,6 +2779,7 @@ dependencies = [
  "pallet-membership",
  "pallet-multisig",
  "pallet-offences",
+ "pallet-oneshot-account",
  "pallet-preimage",
  "pallet-provide-randomness",
  "pallet-proxy",
@@ -5309,6 +5313,24 @@ dependencies = [
  "sp-std",
 ]
 
+[[package]]
+name = "pallet-oneshot-account"
+version = "3.0.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "pallet-balances",
+ "pallet-transaction-payment",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
 [[package]]
 name = "pallet-preimage"
 version = "4.0.0-dev"
diff --git a/Cargo.toml b/Cargo.toml
index d6ddf72f3984686bf5c16d07f08ce5e3b521f074..63920b57750e58d55e124865b7cc561004d98560 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -133,6 +133,7 @@ members = [
     'pallets/duniter-wot',
     'pallets/identity',
     'pallets/membership',
+    'pallets/oneshot-account',
     'pallets/authority-members',
     'pallets/universal-dividend',
     'pallets/upgrade-origin',
diff --git a/end2end-tests/cucumber-features/oneshot_account.feature b/end2end-tests/cucumber-features/oneshot_account.feature
new file mode 100644
index 0000000000000000000000000000000000000000..f3d74b7906b9892d0933d86f29dba00c3b9ff6ef
--- /dev/null
+++ b/end2end-tests/cucumber-features/oneshot_account.feature
@@ -0,0 +1,21 @@
+Feature: Oneshot account
+
+  Scenario: Simple oneshot consumption
+    When alice sends 7 ÄžD to oneshot dave
+    Then alice should have 3 ÄžD
+    Then dave should have oneshot 7 ÄžD
+    When oneshot dave consumes into account bob
+    Then dave should have oneshot 0 ÄžD
+    Then bob should have 1699 cÄžD
+    Then bob should have oneshot 0 ÄžD
+
+  Scenario: Double oneshot consumption
+    When alice sends 7 ÄžD to oneshot dave
+    Then alice should have 3 ÄžD
+    Then dave should have oneshot 7 ÄžD
+    When oneshot dave consumes 4 ÄžD into account bob and the rest into oneshot charlie
+    Then dave should have oneshot 0 ÄžD
+    Then bob should have 14 ÄžD
+    Then bob should have oneshot 0 ÄžD
+    Then charlie should have 10 ÄžD
+    Then charlie should have oneshot 299 cÄžD
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index 56317abf66a6b6f4e2265204f78dae385bffcf81..fb464d5a71c74687a31ca8fadb8f69f3a2a11979 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -18,6 +18,7 @@
 
 pub mod balances;
 pub mod cert;
+pub mod oneshot;
 
 #[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")]
 pub mod node_runtime {}
diff --git a/end2end-tests/tests/common/oneshot.rs b/end2end-tests/tests/common/oneshot.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b8827fe8858dd84edad54142c64e2a0545373577
--- /dev/null
+++ b/end2end-tests/tests/common/oneshot.rs
@@ -0,0 +1,116 @@
+// 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::node_runtime::runtime_types::pallet_oneshot_account;
+use super::*;
+use sp_keyring::AccountKeyring;
+use subxt::{
+    sp_runtime::{AccountId32, MultiAddress},
+    PairSigner,
+};
+
+pub enum Account {
+    Normal(AccountKeyring),
+    Oneshot(AccountKeyring),
+}
+
+impl Account {
+    fn to_account_id(
+        &self,
+    ) -> pallet_oneshot_account::types::Account<MultiAddress<AccountId32, ()>> {
+        match self {
+            Account::Normal(account) => {
+                pallet_oneshot_account::types::Account::Normal(account.to_account_id().into())
+            }
+            Account::Oneshot(account) => {
+                pallet_oneshot_account::types::Account::Oneshot(account.to_account_id().into())
+            }
+        }
+    }
+}
+
+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()
+            .oneshot_account()
+            .create_oneshot_account(to.into(), amount)?
+            .create_signed(&from, BaseExtrinsicParamsBuilder::new())
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
+
+pub async fn consume_oneshot_account(
+    api: &Api,
+    client: &Client,
+    from: AccountKeyring,
+    to: Account,
+) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+    let to = to.to_account_id();
+
+    let _events = create_block_with_extrinsic(
+        client,
+        api.tx()
+            .oneshot_account()
+            .consume_oneshot_account(0, to)?
+            .create_signed(&from, BaseExtrinsicParamsBuilder::new())
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn consume_oneshot_account_with_remaining(
+    api: &Api,
+    client: &Client,
+    from: AccountKeyring,
+    amount: u64,
+    to: Account,
+    remaining_to: Account,
+) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+    let to = to.to_account_id();
+    let remaining_to = remaining_to.to_account_id();
+
+    let _events = create_block_with_extrinsic(
+        client,
+        api.tx()
+            .oneshot_account()
+            .consume_oneshot_account_with_remaining(0, to, remaining_to, amount)?
+            .create_signed(&from, BaseExtrinsicParamsBuilder::new())
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index 265b782956af207df08e1768c147c0ff565cb6b8..303787361b6d295853c0458e31989c3ef4dcd912 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -155,7 +155,7 @@ async fn n_blocks_later(world: &mut DuniterWorld, n: usize) -> Result<()> {
     Ok(())
 }
 
-#[when(regex = r"([a-zA-Z]+) sends? (\d+) (ÄžD|cÄžD|UD|mUD) to ([a-zA-Z]+)")]
+#[when(regex = r"([a-zA-Z]+) sends? (\d+) (ÄžD|cÄžD|UD|mUD) to ([a-zA-Z]+)$")]
 async fn transfer(
     world: &mut DuniterWorld,
     from: String,
@@ -181,6 +181,86 @@ 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 to = match is_dest_oneshot.as_str() {
+        "oneshot" => common::oneshot::Account::Oneshot(to),
+        "account" => common::oneshot::Account::Normal(to),
+        _ => unreachable!(),
+    };
+
+    common::oneshot::consume_oneshot_account(world.api(), world.client(), from, to).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]+)"
+)]
+#[allow(clippy::too_many_arguments)]
+async fn consume_oneshot_account_with_remaining(
+    world: &mut DuniterWorld,
+    from: String,
+    amount: u64,
+    unit: String,
+    is_dest_oneshot: String,
+    to: String,
+    is_remaining_to_oneshot: String,
+    remaining_to: String,
+) -> Result<()> {
+    // Parse inputs
+    let from = AccountKeyring::from_str(&from).expect("unknown from");
+    let to = AccountKeyring::from_str(&to).expect("unknown to");
+    let remaining_to = AccountKeyring::from_str(&remaining_to).expect("unknown remaining_to");
+    let to = match is_dest_oneshot.as_str() {
+        "oneshot" => common::oneshot::Account::Oneshot(to),
+        "account" => common::oneshot::Account::Normal(to),
+        _ => unreachable!(),
+    };
+    let remaining_to = match is_remaining_to_oneshot.as_str() {
+        "oneshot" => common::oneshot::Account::Oneshot(remaining_to),
+        "account" => common::oneshot::Account::Normal(remaining_to),
+        _ => unreachable!(),
+    };
+    let (amount, is_ud) = parse_amount(amount, &unit);
+
+    assert!(!is_ud);
+
+    common::oneshot::consume_oneshot_account_with_remaining(
+        world.api(),
+        world.client(),
+        from,
+        amount,
+        to,
+        remaining_to,
+    )
+    .await
+}
+
 #[when(regex = r"([a-zA-Z]+) sends? all (?:his|her) (?:ÄžDs?|DUs?|UDs?) to ([a-zA-Z]+)")]
 async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Result<()> {
     // Parse inputs
@@ -219,6 +299,29 @@ async fn should_have(
     Ok(())
 }
 
+#[then(regex = r"([a-zA-Z]+) should have oneshot (\d+) (ÄžD|cÄžD)")]
+async fn should_have_oneshot(
+    world: &mut DuniterWorld,
+    who: String,
+    amount: u64,
+    unit: String,
+) -> Result<()> {
+    // Parse inputs
+    let who = AccountKeyring::from_str(&who)
+        .expect("unknown to")
+        .to_account_id();
+    let (amount, _is_ud) = parse_amount(amount, &unit);
+
+    let oneshot_amount = world
+        .api()
+        .storage()
+        .oneshot_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/oneshot-account/Cargo.toml b/pallets/oneshot-account/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..cde1128d8733d39a5a64d90f3af6bd05fa607036
--- /dev/null
+++ b/pallets/oneshot-account/Cargo.toml
@@ -0,0 +1,94 @@
+[package]
+authors = ['librelois <c@elo.tf>']
+description = 'FRAME pallet oneshot account.'
+edition = '2018'
+homepage = 'https://substrate.dev'
+license = 'AGPL-3.0'
+name = 'pallet-oneshot-account'
+readme = 'README.md'
+repository = 'https://git.duniter.org/nodes/rust/duniter-v2s'
+version = '3.0.0'
+
+[features]
+default = ['std']
+runtime-benchmarks = [
+	"frame-benchmarking/runtime-benchmarks",
+	"pallet-balances",
+]
+std = [
+    'codec/std',
+    'frame-support/std',
+    'frame-system/std',
+    'frame-benchmarking/std',
+    'sp-core/std',
+    'sp-io/std',
+    'sp-runtime/std',
+    'sp-std/std',
+]
+try-runtime = ['frame-support/try-runtime']
+
+[dependencies]
+# crates.io
+codec = { package = 'parity-scale-codec', version = "3.1.5", default-features = false, features = ["derive"] }
+log = { version = "0.4.14", default-features = false }
+scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
+
+# benchmarks
+pallet-balances = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.23', optional = true, default-features = false }
+
+# substrate
+[dependencies.frame-benchmarking]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+optional = true
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.frame-support]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.frame-system]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.pallet-transaction-payment]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.sp-core]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.sp-io]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.sp-runtime]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dependencies.sp-std]
+default-features = false
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+### DOC ###
+
+[package.metadata.docs.rs]
+targets = ['x86_64-unknown-linux-gnu']
+
+### DEV ###
+
+[dev-dependencies.sp-io]
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
+
+[dev-dependencies.pallet-balances]
+git = 'https://github.com/duniter/substrate'
+branch = 'duniter-substrate-v0.9.23'
diff --git a/pallets/oneshot-account/src/benchmarking.rs b/pallets/oneshot-account/src/benchmarking.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a72c6585106da25fc5b8e8107cfb3fbbe96276ba
--- /dev/null
+++ b/pallets/oneshot-account/src/benchmarking.rs
@@ -0,0 +1,138 @@
+// Copyright 2021-2022 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S 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.
+//
+// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+
+use frame_benchmarking::{account, benchmarks, whitelisted_caller};
+use frame_support::pallet_prelude::IsType;
+use frame_support::traits::Get;
+use frame_system::RawOrigin;
+use pallet_balances::Pallet as Balances;
+
+use crate::Pallet;
+
+const SEED: u32 = 0;
+
+benchmarks! {
+    where_clause { where
+        T: pallet_balances::Config,
+        T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>
+    }
+    create_oneshot_account {
+        let existential_deposit = T::ExistentialDeposit::get();
+        let caller = whitelisted_caller();
+
+        // Give some multiple of the existential deposit
+        let balance = existential_deposit.saturating_mul((2).into());
+        let _ = T::Currency::make_free_balance_be(&caller, balance.into());
+
+        let recipient: T::AccountId = account("recipient", 0, SEED);
+        let recipient_lookup: <T::Lookup as StaticLookup>::Source =
+            T::Lookup::unlookup(recipient.clone());
+        let transfer_amount = existential_deposit;
+    }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount.into())
+    verify {
+        assert_eq!(Balances::<T>::free_balance(&caller), transfer_amount);
+        assert_eq!(OneshotAccounts::<T>::get(&recipient), Some(transfer_amount.into()));
+    }
+    where_clause { where
+        T: pallet_balances::Config,
+        T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>+From<T::Balance>
+    }
+    consume_oneshot_account {
+        let existential_deposit = T::ExistentialDeposit::get();
+        let caller: T::AccountId = whitelisted_caller();
+
+        // Give some multiple of the existential deposit
+        let balance = existential_deposit.saturating_mul((2).into());
+        OneshotAccounts::<T>::insert(
+            caller.clone(),
+            Into::<<T::Currency as Currency<T::AccountId>>::Balance>::into(balance)
+        );
+
+        // Deposit into a normal account is more expensive than into a oneshot account
+        // so we create the recipient account with an existential deposit.
+        let recipient: T::AccountId = account("recipient", 0, SEED);
+        let recipient_lookup: <T::Lookup as StaticLookup>::Source =
+            T::Lookup::unlookup(recipient.clone());
+        let _ = T::Currency::make_free_balance_be(&recipient, existential_deposit.into());
+    }: _(
+        RawOrigin::Signed(caller.clone()),
+        T::BlockNumber::zero(),
+        Account::<<T::Lookup as StaticLookup>::Source>::Normal(recipient_lookup)
+    )
+    verify {
+        assert_eq!(OneshotAccounts::<T>::get(&caller), None);
+        assert_eq!(
+            Balances::<T>::free_balance(&recipient),
+            existential_deposit.saturating_mul((3).into())
+        );
+    }
+    where_clause { where
+        T: pallet_balances::Config,
+        T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>+From<T::Balance>
+    }
+    consume_oneshot_account_with_remaining {
+        let existential_deposit = T::ExistentialDeposit::get();
+        let caller: T::AccountId = whitelisted_caller();
+
+        // Give some multiple of the existential deposit
+        let balance = existential_deposit.saturating_mul((2).into());
+        OneshotAccounts::<T>::insert(
+            caller.clone(),
+            Into::<<T::Currency as Currency<T::AccountId>>::Balance>::into(balance)
+        );
+
+        // Deposit into a normal account is more expensive than into a oneshot account
+        // so we create the recipient accounts with an existential deposits.
+        let recipient1: T::AccountId = account("recipient1", 0, SEED);
+        let recipient1_lookup: <T::Lookup as StaticLookup>::Source =
+            T::Lookup::unlookup(recipient1.clone());
+        let _ = T::Currency::make_free_balance_be(&recipient1, existential_deposit.into());
+        let recipient2: T::AccountId = account("recipient2", 1, SEED);
+        let recipient2_lookup: <T::Lookup as StaticLookup>::Source =
+            T::Lookup::unlookup(recipient2.clone());
+        let _ = T::Currency::make_free_balance_be(&recipient2, existential_deposit.into());
+    }: _(
+        RawOrigin::Signed(caller.clone()),
+        T::BlockNumber::zero(),
+        Account::<<T::Lookup as StaticLookup>::Source>::Normal(recipient1_lookup),
+        Account::<<T::Lookup as StaticLookup>::Source>::Normal(recipient2_lookup),
+        existential_deposit.into()
+    )
+    verify {
+        assert_eq!(OneshotAccounts::<T>::get(&caller), None);
+        assert_eq!(
+            Balances::<T>::free_balance(&recipient1),
+            existential_deposit.saturating_mul((2).into())
+        );
+        assert_eq!(
+            Balances::<T>::free_balance(&recipient2),
+            existential_deposit.saturating_mul((2).into())
+        );
+    }
+
+    impl_benchmark_test_suite!(
+        Pallet,
+        crate::mock::new_test_ext(),
+        crate::mock::Test
+    );
+}
diff --git a/pallets/oneshot-account/src/check_nonce.rs b/pallets/oneshot-account/src/check_nonce.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d6325ab4d1146bc2c9f3af54f23f9ebd0626b37d
--- /dev/null
+++ b/pallets/oneshot-account/src/check_nonce.rs
@@ -0,0 +1,86 @@
+// 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::Config;
+
+use codec::{Decode, Encode};
+use frame_support::traits::IsSubType;
+use frame_support::weights::DispatchInfo;
+//use frame_system::Config;
+use scale_info::TypeInfo;
+use sp_runtime::{
+    traits::{DispatchInfoOf, Dispatchable, SignedExtension},
+    transaction_validity::{TransactionValidity, TransactionValidityError},
+};
+
+#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
+#[scale_info(skip_type_params(Runtime))]
+pub struct CheckNonce<T: Config>(pub frame_system::CheckNonce<T>);
+
+impl<T: Config> sp_std::fmt::Debug for CheckNonce<T> {
+    #[cfg(feature = "std")]
+    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+        write!(f, "CheckNonce({})", self.0 .0)
+    }
+
+    #[cfg(not(feature = "std"))]
+    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+        Ok(())
+    }
+}
+
+impl<T: Config + TypeInfo> SignedExtension for CheckNonce<T>
+where
+    T::Call: Dispatchable<Info = DispatchInfo> + IsSubType<crate::Call<T>>,
+{
+    type AccountId = <T as frame_system::Config>::AccountId;
+    type Call = <T as frame_system::Config>::Call;
+    type AdditionalSigned = ();
+    type Pre = ();
+    const IDENTIFIER: &'static str = "CheckNonce";
+
+    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 let Some(
+            crate::Call::consume_oneshot_account { .. }
+            | crate::Call::consume_oneshot_account_with_remaining { .. },
+        ) = call.is_sub_type()
+        {
+            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/pallets/oneshot-account/src/lib.rs b/pallets/oneshot-account/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0a65c42ba06563d4f86ca7f8b08526b339b0bdb3
--- /dev/null
+++ b/pallets/oneshot-account/src/lib.rs
@@ -0,0 +1,380 @@
+// 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/>.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+mod benchmarking;
+mod check_nonce;
+#[cfg(test)]
+mod mock;
+mod types;
+
+pub use check_nonce::CheckNonce;
+pub use pallet::*;
+pub use types::*;
+
+use frame_support::pallet_prelude::*;
+use frame_support::traits::{
+    Currency, ExistenceRequirement, Imbalance, IsSubType, WithdrawReasons,
+};
+use frame_system::pallet_prelude::*;
+use pallet_transaction_payment::OnChargeTransaction;
+use sp_runtime::traits::{DispatchInfoOf, PostDispatchInfoOf, Saturating, StaticLookup, Zero};
+use sp_std::convert::TryInto;
+
+#[frame_support::pallet]
+pub mod pallet {
+    use super::*;
+    use frame_support::traits::StorageVersion;
+
+    /// The current storage version.
+    const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
+
+    #[pallet::pallet]
+    #[pallet::generate_store(pub(super) trait Store)]
+    #[pallet::storage_version(STORAGE_VERSION)]
+    #[pallet::without_storage_info]
+    pub struct Pallet<T>(_);
+
+    // CONFIG //
+
+    #[pallet::config]
+    pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
+        type Currency: Currency<Self::AccountId>;
+        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+        type InnerOnChargeTransaction: OnChargeTransaction<Self>;
+    }
+
+    // STORAGE //
+
+    #[pallet::storage]
+    #[pallet::getter(fn oneshot_account)]
+    pub type OneshotAccounts<T: Config> = StorageMap<
+        _,
+        Blake2_128Concat,
+        T::AccountId,
+        <T::Currency as Currency<T::AccountId>>::Balance,
+        OptionQuery,
+    >;
+
+    // EVENTS //
+
+    #[allow(clippy::type_complexity)]
+    #[pallet::event]
+    #[pallet::generate_deposit(pub(super) fn deposit_event)]
+    pub enum Event<T: Config> {
+        OneshotAccountCreated {
+            account: T::AccountId,
+            balance: <T::Currency as Currency<T::AccountId>>::Balance,
+            creator: T::AccountId,
+        },
+        OneshotAccountConsumed {
+            account: T::AccountId,
+            dest1: (
+                T::AccountId,
+                <T::Currency as Currency<T::AccountId>>::Balance,
+            ),
+            dest2: Option<(
+                T::AccountId,
+                <T::Currency as Currency<T::AccountId>>::Balance,
+            )>,
+        },
+        Withdraw {
+            account: T::AccountId,
+            balance: <T::Currency as Currency<T::AccountId>>::Balance,
+        },
+    }
+
+    // ERRORS //
+
+    #[pallet::error]
+    pub enum Error<T> {
+        /// Block height is in the future
+        BlockHeightInFuture,
+        /// Block height is too old
+        BlockHeightTooOld,
+        /// Destination account does not exist
+        DestAccountNotExist,
+        /// Destination account has balance less than existential deposit
+        ExistentialDeposit,
+        /// Source account has insufficient balance
+        InsufficientBalance,
+        /// Destination oneshot account already exists
+        OneshotAccountAlreadyCreated,
+        /// Source oneshot account does not exist
+        OneshotAccountNotExist,
+    }
+
+    // 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.
+        ///
+        /// Origin account is kept alive.
+        #[pallet::weight(500_000_000)]
+        pub fn create_oneshot_account(
+            origin: OriginFor<T>,
+            dest: <T::Lookup as StaticLookup>::Source,
+            #[pallet::compact] value: <T::Currency as Currency<T::AccountId>>::Balance,
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+            let dest = T::Lookup::lookup(dest)?;
+
+            ensure!(
+                value >= <T::Currency as Currency<T::AccountId>>::minimum_balance(),
+                Error::<T>::ExistentialDeposit
+            );
+            ensure!(
+                OneshotAccounts::<T>::get(&dest).is_none(),
+                Error::<T>::OneshotAccountAlreadyCreated
+            );
+
+            <T::Currency as Currency<T::AccountId>>::withdraw(
+                &transactor,
+                value,
+                WithdrawReasons::TRANSFER,
+                ExistenceRequirement::KeepAlive,
+            )?;
+            OneshotAccounts::<T>::insert(&dest, value);
+            Self::deposit_event(Event::OneshotAccountCreated {
+                account: dest,
+                balance: value,
+                creator: transactor,
+            });
+
+            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>,
+            block_height: T::BlockNumber,
+            dest: Account<<T::Lookup as StaticLookup>::Source>,
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+
+            let (dest, dest_is_oneshot) = match dest {
+                Account::Normal(account) => (account, false),
+                Account::Oneshot(account) => (account, true),
+            };
+            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::OneshotAccountCreated {
+                    account: dest.clone(),
+                    balance: value,
+                    creator: transactor.clone(),
+                });
+            } else {
+                <T::Currency as Currency<T::AccountId>>::deposit_into_existing(&dest, value)?;
+            }
+            OneshotAccounts::<T>::remove(&transactor);
+            Self::deposit_event(Event::OneshotAccountConsumed {
+                account: transactor,
+                dest1: (dest, value),
+                dest2: None,
+            });
+
+            Ok(())
+        }
+        /// Consume a oneshot account then transfer some amount to an account,
+        /// and the remaining amount to another 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.
+        /// - `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_with_remaining(
+            origin: OriginFor<T>,
+            block_height: T::BlockNumber,
+            dest: Account<<T::Lookup as StaticLookup>::Source>,
+            remaining_to: Account<<T::Lookup as StaticLookup>::Source>,
+            #[pallet::compact] balance: <T::Currency as Currency<T::AccountId>>::Balance,
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+
+            let (dest1, dest1_is_oneshot) = match dest {
+                Account::Normal(account) => (account, false),
+                Account::Oneshot(account) => (account, true),
+            };
+            let dest1 = T::Lookup::lookup(dest1)?;
+            let (dest2, dest2_is_oneshot) = match remaining_to {
+                Account::Normal(account) => (account, false),
+                Account::Oneshot(account) => (account, true),
+            };
+            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 dest1_is_oneshot {
+                ensure!(
+                    OneshotAccounts::<T>::get(&dest1).is_none(),
+                    Error::<T>::OneshotAccountAlreadyCreated
+                );
+                ensure!(
+                    balance1 >= <T::Currency as Currency<T::AccountId>>::minimum_balance(),
+                    Error::<T>::ExistentialDeposit
+                );
+            } else {
+                ensure!(
+                    !<T::Currency as Currency<T::AccountId>>::free_balance(&dest1).is_zero(),
+                    Error::<T>::DestAccountNotExist
+                );
+            }
+            if dest2_is_oneshot {
+                ensure!(
+                    OneshotAccounts::<T>::get(&dest2).is_none(),
+                    Error::<T>::OneshotAccountAlreadyCreated
+                );
+                ensure!(
+                    balance2 >= <T::Currency as Currency<T::AccountId>>::minimum_balance(),
+                    Error::<T>::ExistentialDeposit
+                );
+                OneshotAccounts::<T>::insert(&dest2, balance2);
+                Self::deposit_event(Event::OneshotAccountCreated {
+                    account: dest2.clone(),
+                    balance: balance2,
+                    creator: transactor.clone(),
+                });
+            } else {
+                <T::Currency as Currency<T::AccountId>>::deposit_into_existing(&dest2, balance2)?;
+            }
+            if dest1_is_oneshot {
+                OneshotAccounts::<T>::insert(&dest1, balance1);
+                Self::deposit_event(Event::OneshotAccountCreated {
+                    account: dest1.clone(),
+                    balance: balance1,
+                    creator: transactor.clone(),
+                });
+            } else {
+                <T::Currency as Currency<T::AccountId>>::deposit_into_existing(&dest1, balance1)?;
+            }
+            OneshotAccounts::<T>::remove(&transactor);
+            Self::deposit_event(Event::OneshotAccountConsumed {
+                account: transactor,
+                dest1: (dest1, balance1),
+                dest2: Some((dest2, balance2)),
+            });
+
+            Ok(())
+        }
+    }
+}
+
+impl<T: Config> OnChargeTransaction<T> for Pallet<T>
+where
+    T::Call: IsSubType<Call<T>>,
+    T::InnerOnChargeTransaction: OnChargeTransaction<
+        T,
+        Balance = <T::Currency as Currency<T::AccountId>>::Balance,
+        LiquidityInfo = Option<<T::Currency as Currency<T::AccountId>>::NegativeImbalance>,
+    >,
+{
+    type Balance = <T::Currency as Currency<T::AccountId>>::Balance;
+    type LiquidityInfo = Option<<T::Currency as Currency<T::AccountId>>::NegativeImbalance>;
+    fn withdraw_fee(
+        who: &T::AccountId,
+        call: &T::Call,
+        dispatch_info: &DispatchInfoOf<T::Call>,
+        fee: Self::Balance,
+        tip: Self::Balance,
+    ) -> Result<Self::LiquidityInfo, TransactionValidityError> {
+        if let Some(
+            Call::consume_oneshot_account { .. }
+            | Call::consume_oneshot_account_with_remaining { .. },
+        ) = call.is_sub_type()
+        {
+            if fee.is_zero() {
+                return Ok(None);
+            }
+
+            if let Some(balance) = OneshotAccounts::<T>::get(&who) {
+                if balance >= fee {
+                    OneshotAccounts::<T>::insert(who, balance.saturating_sub(fee));
+                    Self::deposit_event(Event::Withdraw {
+                        account: who.clone(),
+                        balance: fee,
+                    });
+                    // TODO
+                    return Ok(Some(
+                        <T::Currency as Currency<T::AccountId>>::NegativeImbalance::zero(),
+                    ));
+                }
+            }
+            Err(TransactionValidityError::Invalid(
+                InvalidTransaction::Payment,
+            ))
+        } else {
+            T::InnerOnChargeTransaction::withdraw_fee(who, call, dispatch_info, fee, tip)
+        }
+    }
+    fn correct_and_deposit_fee(
+        who: &T::AccountId,
+        dispatch_info: &DispatchInfoOf<T::Call>,
+        post_info: &PostDispatchInfoOf<T::Call>,
+        corrected_fee: Self::Balance,
+        tip: Self::Balance,
+        already_withdrawn: Self::LiquidityInfo,
+    ) -> Result<(), TransactionValidityError> {
+        T::InnerOnChargeTransaction::correct_and_deposit_fee(
+            who,
+            dispatch_info,
+            post_info,
+            corrected_fee,
+            tip,
+            already_withdrawn,
+        )
+    }
+}
diff --git a/pallets/oneshot-account/src/mock.rs b/pallets/oneshot-account/src/mock.rs
new file mode 100644
index 0000000000000000000000000000000000000000..35b69d7ae55d610f3db44d9b5d3b8752f64e6e00
--- /dev/null
+++ b/pallets/oneshot-account/src/mock.rs
@@ -0,0 +1,124 @@
+// 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::{self as pallet_oneshot_account};
+use frame_support::{parameter_types, traits::Everything, weights::IdentityFee};
+use frame_system as system;
+use pallet_transaction_payment::CurrencyAdapter;
+use sp_core::H256;
+use sp_runtime::{
+    testing::Header,
+    traits::{BlakeTwo256, IdentityLookup},
+    BuildStorage,
+};
+use sp_std::convert::{TryFrom, TryInto};
+
+type Balance = u64;
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+type Block = frame_system::mocking::MockBlock<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>},
+        Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
+        TransactionPayment: pallet_transaction_payment::{Pallet, Storage},
+        OneshotAccount: pallet_oneshot_account::{Pallet, Call, Storage, Event<T>},
+    }
+);
+
+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 = u64;
+    type Lookup = IdentityLookup<Self::AccountId>;
+    type Header = Header;
+    type Event = Event;
+    type BlockHashCount = BlockHashCount;
+    type Version = ();
+    type PalletInfo = PalletInfo;
+    type AccountData = pallet_balances::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 = 10;
+    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];
+}
+impl pallet_transaction_payment::Config for Test {
+    type OnChargeTransaction = OneshotAccount;
+    type OperationalFeeMultiplier = frame_support::traits::ConstU8<5>;
+    type WeightToFee = IdentityFee<u64>;
+    type LengthToFee = IdentityFee<u64>;
+    type FeeMultiplierUpdate = ();
+}
+impl pallet_oneshot_account::Config for Test {
+    type Currency = Balances;
+    type Event = Event;
+    type InnerOnChargeTransaction = CurrencyAdapter<Balances, HandleFees>;
+}
+
+pub struct HandleFees;
+type NegativeImbalance = <Balances as frame_support::traits::Currency<u64>>::NegativeImbalance;
+impl frame_support::traits::OnUnbalanced<NegativeImbalance> for HandleFees {
+    fn on_nonzero_unbalanced(_amount: NegativeImbalance) {}
+}
+
+// Build genesis storage according to the mock runtime.
+#[allow(dead_code)]
+pub fn new_test_ext() -> sp_io::TestExternalities {
+    GenesisConfig {
+        system: SystemConfig::default(),
+        balances: BalancesConfig::default(),
+    }
+    .build_storage()
+    .unwrap()
+    .into()
+}
diff --git a/pallets/oneshot-account/src/types.rs b/pallets/oneshot-account/src/types.rs
new file mode 100644
index 0000000000000000000000000000000000000000..75344846552bc8fb87db7bc250b7b25d1d2e3ff2
--- /dev/null
+++ b/pallets/oneshot-account/src/types.rs
@@ -0,0 +1,24 @@
+// 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 codec::{Decode, Encode};
+use frame_support::pallet_prelude::*;
+
+#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)]
+pub enum Account<AccountId> {
+    Normal(AccountId),
+    Oneshot(AccountId),
+}
diff --git a/pallets/oneshot-account/src/weights.rs b/pallets/oneshot-account/src/weights.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a4cbfbe78a9f76a68879cdc9e675a8518987bb7f
--- /dev/null
+++ b/pallets/oneshot-account/src/weights.rs
@@ -0,0 +1,52 @@
+// Copyright 2021-2022 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S 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.
+//
+// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::weights::{constants::RocksDbWeight, Weight};
+
+/// Weight functions needed for pallet_universal_dividend.
+pub trait WeightInfo {
+    fn create_oneshot_account() -> Weight;
+    fn consume_oneshot_account() -> Weight;
+    fn consume_oneshot_account_with_remaining() -> Weight;
+}
+
+// Insecure weights implementation, use it for tests only!
+impl WeightInfo for () {
+    // Storage: OneshotAccount OneshotAccounts (r:1 w:1)
+    fn create_oneshot_account() -> Weight {
+        (45_690_000 as Weight)
+            .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+            .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+    }
+    // Storage: OneshotAccount OneshotAccounts (r:1 w:1)
+    // Storage: System BlockHash (r:1 w:0)
+    // Storage: System Account (r:1 w:1)
+    fn consume_oneshot_account() -> Weight {
+        (50_060_000 as Weight)
+            .saturating_add(RocksDbWeight::get().reads(3 as Weight))
+            .saturating_add(RocksDbWeight::get().writes(2 as Weight))
+    }
+    // Storage: OneshotAccount OneshotAccounts (r:1 w:1)
+    // Storage: System BlockHash (r:1 w:0)
+    // Storage: System Account (r:2 w:2)
+    fn consume_oneshot_account_with_remaining() -> Weight {
+        (69_346_000 as Weight)
+            .saturating_add(RocksDbWeight::get().reads(4 as Weight))
+            .saturating_add(RocksDbWeight::get().writes(3 as Weight))
+    }
+}
diff --git a/resources/metadata.scale b/resources/metadata.scale
index 53eb7112a2ef328bbe23045d60c5ef402470ae39..10f4232185e31dcb87b88cd33a9731c8efca1265 100644
Binary files a/resources/metadata.scale and b/resources/metadata.scale differ
diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml
index 523f684043594cacff62d11844e036fbf33bebac..660fa3223c3eac2d4ee2c656df6ca823b0ed5e83 100644
--- a/runtime/common/Cargo.toml
+++ b/runtime/common/Cargo.toml
@@ -39,6 +39,7 @@ std = [
     'pallet-identity/std',
     'pallet-membership/std',
     'pallet-multisig/std',
+    'pallet-oneshot-account/std',
     'pallet-provide-randomness/std',
     'pallet-proxy/std',
     'pallet-scheduler/std',
@@ -68,6 +69,7 @@ pallet-duniter-account = { path = '../../pallets/duniter-account', default-featu
 pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false }
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
+pallet-oneshot-account = { path = '../../pallets/oneshot-account', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
diff --git a/runtime/common/src/apis.rs b/runtime/common/src/apis.rs
index e6002a5d1756c2d494750e70af8d08074f3e13d5..1aaf5690c44c70a9cf6fad756da0e1cee6d6971e 100644
--- a/runtime/common/src/apis.rs
+++ b/runtime/common/src/apis.rs
@@ -238,6 +238,7 @@ macro_rules! runtime_apis {
                     list_benchmark!(list, extra, frame_system, SystemBench::<Runtime>);
 					list_benchmark!(list, extra, pallet_balances, Balances);
 					list_benchmark!(list, extra, pallet_multisig, Multisig);
+					list_benchmark!(list, extra, pallet_oneshot_account, OneshotAccount);
 					list_benchmark!(list, extra, pallet_proxy, Proxy);
 					list_benchmark!(list, extra, pallet_scheduler, Scheduler);
 					list_benchmark!(list, extra, pallet_timestamp, Timestamp);
@@ -284,6 +285,7 @@ macro_rules! runtime_apis {
 					add_benchmark!(params, batches, frame_system, SystemBench::<Runtime>);
 					add_benchmark!(params, batches, pallet_balances, Balances);
 					add_benchmark!(params, batches, pallet_multisig, Multisig);
+					add_benchmark!(params, batches, pallet_oneshot_account, OneshotAccount);
 					add_benchmark!(params, batches, pallet_proxy, Proxy);
 					add_benchmark!(params, batches, pallet_scheduler, Scheduler);
 					add_benchmark!(params, batches, pallet_timestamp, Timestamp);
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index f9e7531c7461bda911827c1a7d7a4e57db549a56..10f5feb5c8acb22c21b1147501cb22d83a6f9185 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -173,13 +173,19 @@ macro_rules! pallets_config {
                 }
             }
         }
+        pub struct OnChargeTransaction;
         impl pallet_transaction_payment::Config for Runtime {
-            type OnChargeTransaction = CurrencyAdapter<Balances, HandleFees>;
+            type OnChargeTransaction = OneshotAccount;
             type OperationalFeeMultiplier = frame_support::traits::ConstU8<5>;
             type WeightToFee = common_runtime::fees::WeightToFeeImpl<Balance>;
             type LengthToFee = common_runtime::fees::LengthToFeeImpl<Balance>;
             type FeeMultiplierUpdate = ();
         }
+        impl pallet_oneshot_account::Config for Runtime {
+            type Currency = Balances;
+            type Event = Event;
+            type InnerOnChargeTransaction = CurrencyAdapter<Balances, HandleFees>;
+        }
 
         // CONSENSUS  //
 
diff --git a/runtime/common/src/weights/pallet_oneshot_account.rs b/runtime/common/src/weights/pallet_oneshot_account.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8a50c766419bb3d029d79538aefe8cb54ab40ff4
--- /dev/null
+++ b/runtime/common/src/weights/pallet_oneshot_account.rs
@@ -0,0 +1,71 @@
+// Copyright 2021-2022 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S 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.
+//
+// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+//! Autogenerated weights for `pallet_oneshot_account`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-08-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: Some(Wasm), WASM-EXECUTION: Interpreted, CHAIN: Some("dev"), DB CACHE: 1024
+
+// Executed Command:
+// ./duniter
+// benchmark
+// pallet
+// --chain=dev
+// --steps=50
+// --repeat=20
+// --pallet=pallet_oneshot_account
+// --extrinsic=*
+// --execution=wasm
+// --wasm-execution=interpreted-i-know-what-i-do
+// --heap-pages=4096
+// --header=./file_header.txt
+// --output=.
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::Weight};
+use sp_std::marker::PhantomData;
+
+/// Weight functions for `pallet_oneshot_account`.
+pub struct WeightInfo<T>(PhantomData<T>);
+impl<T: frame_system::Config> pallet_oneshot_account::WeightInfo for WeightInfo<T> {
+	// Storage: OneshotAccount OneshotAccounts (r:1 w:1)
+	fn create_oneshot_account() -> Weight {
+		(941_602_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: OneshotAccount OneshotAccounts (r:1 w:1)
+	// Storage: System BlockHash (r:1 w:0)
+	// Storage: System Account (r:1 w:1)
+	fn consume_oneshot_account() -> Weight {
+		(971_453_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(3 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+	}
+	// Storage: OneshotAccount OneshotAccounts (r:1 w:1)
+	// Storage: System BlockHash (r:1 w:0)
+	// Storage: System Account (r:2 w:2)
+	fn consume_oneshot_account_with_remaining() -> Weight {
+		(1_500_781_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(4 as Weight))
+			.saturating_add(T::DbWeight::get().writes(3 as Weight))
+	}
+}
diff --git a/runtime/g1/Cargo.toml b/runtime/g1/Cargo.toml
index ee9d7049bb0c306cb081934b5eedac81ba4b3151..2ce176950be39d8c872d6f8aac56e7f9dd0c8f06 100644
--- a/runtime/g1/Cargo.toml
+++ b/runtime/g1/Cargo.toml
@@ -52,6 +52,7 @@ std = [
     'pallet-provide-randomness/std',
     'pallet-im-online/std',
     'pallet-multisig/std',
+    'pallet-oneshot-account/std',
     'pallet-preimage/std',
     'pallet-proxy/std',
     'pallet-session/std',
@@ -117,6 +118,7 @@ pallet-duniter-account = { path = '../../pallets/duniter-account', default-featu
 pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false }
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
+pallet-oneshot-account = { path = '../../pallets/oneshot-account', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
diff --git a/runtime/g1/src/lib.rs b/runtime/g1/src/lib.rs
index ec455d7fad4eac0e9e16631e69c74c80e48f103d..a944041c72c15722b42f318dd04e663316614b80 100644
--- a/runtime/g1/src/lib.rs
+++ b/runtime/g1/src/lib.rs
@@ -111,7 +111,7 @@ pub type SignedExtra = (
     frame_system::CheckTxVersion<Runtime>,
     frame_system::CheckGenesis<Runtime>,
     frame_system::CheckEra<Runtime>,
-    frame_system::CheckNonce<Runtime>,
+    pallet_oneshot_account::CheckNonce<Runtime>,
     frame_system::CheckWeight<Runtime>,
     pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
 );
@@ -215,6 +215,7 @@ construct_runtime!(
         // Money management
         Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 6,
         TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 32,
+        OneshotAccount: pallet_oneshot_account::{Pallet, Call, Storage, Event<T>} = 7,
 
         // Consensus support.
         AuthorityMembers: pallet_authority_members::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,
diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml
index bfdc5a59a1359da72f035967c178603f9dfc0ffe..77ff1f259b8350a6314cc04ae4da8812093863f5 100644
--- a/runtime/gdev/Cargo.toml
+++ b/runtime/gdev/Cargo.toml
@@ -36,6 +36,7 @@ runtime-benchmarks = [
     'pallet-provide-randomness/runtime-benchmarks',
     'pallet-im-online/runtime-benchmarks',
     'pallet-multisig/runtime-benchmarks',
+    'pallet-oneshot-account/runtime-benchmarks',
     'pallet-preimage/runtime-benchmarks',
     'pallet-proxy/runtime-benchmarks',
     'pallet-scheduler/runtime-benchmarks',
@@ -68,6 +69,7 @@ std = [
     'pallet-grandpa/std',
     'pallet-identity/std',
     'pallet-membership/std',
+    'pallet-oneshot-account/std',
     'pallet-provide-randomness/std',
     'pallet-im-online/std',
     'pallet-multisig/std',
@@ -139,6 +141,7 @@ pallet-duniter-account = { path = '../../pallets/duniter-account', default-featu
 pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false }
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
+pallet-oneshot-account = { path = '../../pallets/oneshot-account', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs
index 666469fa6295944b9408fadab618d0cbeebcb8e6..1c494414a5154922bec981e47ca84aeadc6a9445 100644
--- a/runtime/gdev/src/lib.rs
+++ b/runtime/gdev/src/lib.rs
@@ -115,7 +115,7 @@ pub type SignedExtra = (
     frame_system::CheckTxVersion<Runtime>,
     frame_system::CheckGenesis<Runtime>,
     frame_system::CheckEra<Runtime>,
-    frame_system::CheckNonce<Runtime>,
+    pallet_oneshot_account::CheckNonce<Runtime>,
     frame_system::CheckWeight<Runtime>,
     pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
 );
@@ -284,6 +284,7 @@ construct_runtime!(
         // Money management
         Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 6,
         TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 32,
+        OneshotAccount: pallet_oneshot_account::{Pallet, Call, Storage, Event<T>} = 7,
 
         // Consensus support
         AuthorityMembers: pallet_authority_members::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index 9a1ca4833c8cfe78dd4e4a727ca122bb6895dd5d..c3c43dc8b506a9f70e0c04ba5965fc994f44f95b 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -430,3 +430,81 @@ fn test_create_new_idty_without_founds() {
             );
         });
 }
+
+#[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!(OneshotAccount::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!(OneshotAccount::consume_oneshot_account_with_remaining(
+                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                0,
+                pallet_oneshot_account::Account::Oneshot(MultiAddress::Id(
+                    AccountKeyring::Ferdie.to_account_id()
+                )),
+                pallet_oneshot_account::Account::Normal(MultiAddress::Id(
+                    AccountKeyring::Alice.to_account_id()
+                )),
+                300
+            ));
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                700
+            );
+            assert_noop!(
+                OneshotAccount::consume_oneshot_account(
+                    frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                    0,
+                    pallet_oneshot_account::Account::Oneshot(MultiAddress::Id(
+                        AccountKeyring::Ferdie.to_account_id()
+                    )),
+                ),
+                pallet_oneshot_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!(OneshotAccount::consume_oneshot_account(
+                frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into(),
+                0,
+                pallet_oneshot_account::Account::Normal(MultiAddress::Id(
+                    AccountKeyring::Alice.to_account_id()
+                )),
+            ));
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                1000
+            );
+            assert_noop!(
+                OneshotAccount::consume_oneshot_account(
+                    frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                    0,
+                    pallet_oneshot_account::Account::Normal(MultiAddress::Id(
+                        AccountKeyring::Alice.to_account_id()
+                    )),
+                ),
+                pallet_oneshot_account::Error::<Runtime>::OneshotAccountNotExist
+            );
+        });
+}
diff --git a/runtime/gtest/Cargo.toml b/runtime/gtest/Cargo.toml
index 30be9e5ebb1cef2990d976df0576031588775901..4d40f3b25d5cd06ab13eb7cc12853abddfaccab9 100644
--- a/runtime/gtest/Cargo.toml
+++ b/runtime/gtest/Cargo.toml
@@ -49,6 +49,7 @@ std = [
     'pallet-grandpa/std',
     'pallet-identity/std',
     'pallet-membership/std',
+    'pallet-oneshot-account/std',
     'pallet-provide-randomness/std',
     'pallet-im-online/std',
     'pallet-multisig/std',
@@ -117,6 +118,7 @@ pallet-duniter-account = { path = '../../pallets/duniter-account', default-featu
 pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false }
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
+pallet-oneshot-account = { path = '../../pallets/oneshot-account', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
diff --git a/runtime/gtest/src/lib.rs b/runtime/gtest/src/lib.rs
index fd21f986d2969fe69a03acb45788f81a4d1763de..efb4fb2f48631cea8b5bc0c820e84502275be3f1 100644
--- a/runtime/gtest/src/lib.rs
+++ b/runtime/gtest/src/lib.rs
@@ -216,6 +216,7 @@ construct_runtime!(
         // Money management
         Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 6,
         TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 32,
+        OneshotAccount: pallet_oneshot_account::{Pallet, Call, Storage, Event<T>} = 7,
 
         // Consensus support.
         AuthorityMembers: pallet_authority_members::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,