#![cfg_attr(not(feature = "std"), no_std)]
mod benchmarking;
#[cfg(test)]
mod tests;
use frame_support::{
dispatch::GetDispatchInfo,
traits::{Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, StorageVersion},
PalletId,
};
use frame_system::pallet_prelude::*;
use sp_runtime::traits::{AccountIdConversion, Dispatchable};
use sp_std::prelude::Box;
use support::WithAccountId;
pub mod weights;
pub use weights::WeightInfo;
pub use pallet::*;
type BalanceOf<T, I> = <<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type NegativeImbalanceOf<T, I> =
<<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*};
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
type Currency: Currency<Self::AccountId>;
type RuntimeCall: Parameter
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
+ GetDispatchInfo;
type PalletId: Get<PalletId>;
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {}
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::spend())]
pub fn spend(origin: OriginFor<T>, to: T::AccountId, amount: BalanceOf<T, I>) -> DispatchResultWithPostInfo {
T::ExternalOrigin::try_origin(origin).map(|_| ()).or_else(ensure_root)?;
T::Currency::transfer(&Self::account_id(), &to, amount, ExistenceRequirement::KeepAlive)?;
Self::deposit_event(Event::SpentFunds(to, amount));
Ok(().into())
}
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::tip())]
pub fn tip(origin: OriginFor<T>, amount: BalanceOf<T, I>) -> DispatchResultWithPostInfo {
let tipper = ensure_signed(origin)?;
T::Currency::transfer(&tipper, &Self::account_id(), amount, ExistenceRequirement::AllowDeath)?;
Self::deposit_event(Event::TipReceived(tipper, amount));
Ok(().into())
}
#[allow(clippy::boxed_local)]
#[pallet::call_index(2)]
#[pallet::weight({
let dispatch_info = call.get_dispatch_info();
(
dispatch_info.weight.saturating_add(Weight::from_parts(10_000, 0)),
dispatch_info.class,
)
})]
pub fn apply_as(origin: OriginFor<T>, call: Box<<T as Config<I>>::RuntimeCall>) -> DispatchResultWithPostInfo {
T::ExternalOrigin::try_origin(origin).map(|_| ()).or_else(ensure_root)?;
call.dispatch(frame_system::RawOrigin::Signed(Self::account_id()).into())?;
Self::deposit_event(Event::ReserveOpExecuted);
Ok(().into())
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
Deposit(BalanceOf<T, I>),
SpentFunds(T::AccountId, BalanceOf<T, I>),
TipReceived(T::AccountId, BalanceOf<T, I>),
ReserveOpExecuted,
}
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
pub phantom: sp_std::marker::PhantomData<(T, I)>,
}
impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
fn default() -> Self {
Self {
phantom: Default::default(),
}
}
}
#[pallet::genesis_build]
impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
fn build(&self) {
let our_account = &<Pallet<T, I>>::account_id();
if T::Currency::free_balance(our_account) < T::Currency::minimum_balance() {
let _ = T::Currency::make_free_balance_be(our_account, T::Currency::minimum_balance());
}
}
}
}
impl<T: Config<I>, I: 'static> WithAccountId<T::AccountId> for Pallet<T, I> {
fn account_id() -> T::AccountId {
T::PalletId::get().into_account_truncating()
}
}
impl<T: Config<I>, I: 'static> OnUnbalanced<NegativeImbalanceOf<T, I>> for Pallet<T, I> {
fn on_nonzero_unbalanced(amount: NegativeImbalanceOf<T, I>) {
let numeric_amount = amount.peek();
T::Currency::resolve_creating(&Self::account_id(), amount);
Self::deposit_event(Event::Deposit(numeric_amount));
}
}