From 6d4f4bfb7d527f22eaf7cb10605715cab7f567b0 Mon Sep 17 00:00:00 2001
From: librelois <c@elo.tf>
Date: Thu, 19 May 2022 18:37:33 +0200
Subject: [PATCH] PoC about oneshot accounts

---
 pallets/duniter-account/src/lib.rs   | 116 ++++++++++++++++++++++++++-
 pallets/duniter-account/src/types.rs |  10 ++-
 2 files changed, 123 insertions(+), 3 deletions(-)

diff --git a/pallets/duniter-account/src/lib.rs b/pallets/duniter-account/src/lib.rs
index 0615fbd99..9cf1cf363 100644
--- a/pallets/duniter-account/src/lib.rs
+++ b/pallets/duniter-account/src/lib.rs
@@ -26,7 +26,7 @@ use frame_support::traits::{OnUnbalanced, StoredMap};
 use frame_system::pallet_prelude::*;
 use pallet_provide_randomness::RequestId;
 use sp_core::H256;
-use sp_runtime::traits::{Convert, Saturating, Zero};
+use sp_runtime::traits::{Convert, Saturating, StaticLookup, Zero};
 
 #[frame_support::pallet]
 pub mod pallet {
@@ -60,6 +60,11 @@ pub mod pallet {
 
     // STORAGE //
 
+    #[pallet::storage]
+    #[pallet::getter(fn oneshot_account)]
+    pub type OneshotAccounts<T: Config> =
+        StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance, OptionQuery>;
+
     #[pallet::storage]
     #[pallet::getter(fn pending_random_id_assignments)]
     pub type PendingRandomIdAssignments<T: Config> =
@@ -133,14 +138,40 @@ pub mod pallet {
         /// the account creation price.
         /// [who, balance]
         ForceDestroy {
+            balance: T::Balance,
             who: T::AccountId,
+        },
+        OneshotAccountCreated {
+            account: T::AccountId,
             balance: T::Balance,
+            creator: T::AccountId,
+        },
+        OneshotAccountConsumed {
+            account: T::AccountId,
+            balance: T::Balance,
+            dest: T::AccountId,
         },
         /// Random id assigned
         /// [account_id, random_id]
         RandomIdAssigned { who: T::AccountId, random_id: H256 },
     }
 
+    // ERRORS //
+
+    #[pallet::error]
+    pub enum Error<T> {
+        /// DestAccountNotExist
+        DestAccountNotExist,
+        /// ExistentialDeposit
+        ExistentialDeposit,
+        /// InsufficientBalance
+        InsufficientBalance,
+        /// OneshotAccouncAlreadyCreated
+        OneshotAccountAlreadyCreated,
+        /// OneshotAccountNotExist
+        OneshotAccountNotExist,
+    }
+
     // HOOKS //
     #[pallet::hooks]
     impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
@@ -217,6 +248,89 @@ pub mod pallet {
             total_weight
         }
     }
+
+    // CALLS //
+    #[pallet::call]
+    impl<T: Config> Pallet<T> {
+        #[pallet::weight(500_000_000)]
+        pub fn create_oneshot_account(
+            origin: OriginFor<T>,
+            dest: <T::Lookup as StaticLookup>::Source,
+            #[pallet::compact] value: T::Balance,
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+            let dest = T::Lookup::lookup(dest)?;
+
+            ensure!(
+                value >= T::ExistentialDeposit::get(),
+                Error::<T>::ExistentialDeposit
+            );
+            ensure!(
+                OneshotAccounts::<T>::get(&dest).is_none(),
+                Error::<T>::OneshotAccountAlreadyCreated
+            );
+
+            let transactor_data = frame_system::Account::<T>::get(&transactor).data;
+            let transactor_free_and_reserved = transactor_data
+                .free
+                .saturating_add(transactor_data.reserved);
+            let sufficient_balance = value.saturating_add(T::ExistentialDeposit::get());
+            ensure!(
+                transactor_data.free >= value && transactor_free_and_reserved >= sufficient_balance,
+                Error::<T>::InsufficientBalance
+            );
+
+            frame_system::Account::<T>::mutate(&transactor, |a| a.data.sub_free(value));
+            OneshotAccounts::<T>::insert(&dest, value);
+            Self::deposit_event(Event::OneshotAccountCreated {
+                account: dest,
+                balance: value,
+                creator: transactor,
+            });
+
+            Ok(())
+        }
+        #[pallet::weight(500_000_000)]
+        pub fn consume_oneshot_account(
+            origin: OriginFor<T>,
+            dest: (bool, <T::Lookup as StaticLookup>::Source),
+        ) -> DispatchResult {
+            let transactor = ensure_signed(origin)?;
+            let dest_is_oneshot = dest.0;
+            let dest = T::Lookup::lookup(dest.1)?;
+
+            let value = OneshotAccounts::<T>::take(&transactor)
+                .ok_or(Error::<T>::OneshotAccountNotExist)?;
+
+            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,
+                    balance: value,
+                    creator: transactor,
+                });
+            } 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,
+                });
+            }
+
+            Ok(())
+        }
+    }
 }
 
 impl<T> pallet_provide_randomness::OnFilledRandomness for Pallet<T>
diff --git a/pallets/duniter-account/src/types.rs b/pallets/duniter-account/src/types.rs
index 25135c3f6..8f1a6b16b 100644
--- a/pallets/duniter-account/src/types.rs
+++ b/pallets/duniter-account/src/types.rs
@@ -18,7 +18,7 @@ use codec::{Decode, Encode, MaxEncodedLen};
 use frame_support::pallet_prelude::*;
 use scale_info::TypeInfo;
 use sp_core::H256;
-use sp_runtime::traits::Zero;
+use sp_runtime::traits::{Saturating, Zero};
 
 #[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
 pub struct AccountData<Balance> {
@@ -28,12 +28,18 @@ pub struct AccountData<Balance> {
     fee_frozen: Balance,
 }
 
-impl<Balance: Zero> AccountData<Balance> {
+impl<Balance: Copy + Saturating + Zero> AccountData<Balance> {
+    pub fn free_and_reserved(&self) -> Balance {
+        self.free.saturating_add(self.reserved)
+    }
     pub fn set_balances(&mut self, new_balances: pallet_balances::AccountData<Balance>) {
         self.free = new_balances.free;
         self.reserved = new_balances.reserved;
         self.fee_frozen = new_balances.fee_frozen;
     }
+    pub fn sub_free(&mut self, amount: Balance) {
+        self.free = self.free.saturating_sub(amount);
+    }
     pub fn was_providing(&self) -> bool {
         !self.free.is_zero() || !self.reserved.is_zero()
     }
-- 
GitLab