//! # Unit rewards Pallet
//! <!-- Original author of paragraph: @faiz
//!
//! ## Overview
//!
//! Pallet that allows a user to create a reward , users who have minimum tokens can claim that reward.
//!
//! ### Goals
//!
//! The pallet is designed to make the following possible:
//!
//! * create a reward.
//! * delete a reward.
//! * update a reward.
//!
//! ## Interface
//!
//! ### Permissionless Functions
//!
//! - `create_reward`: create a reward.
//! - `delete_reward` : delete a reward.
//! - `update_reward`: update the reward
//!
//!//! Please refer to the [`Call`] enum and its associated variants for documentation on each
//! function.
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
// Re-export pallet items so that they can be accessed from the crate namespace.
pub use pallet::*;
mod types;
pub use types::*;
use traits::profile::{ ProfileInspect, ProfileInterface };
use traits::subaccounts::{ SubAccounts, AccountOrigin };
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use frame_support::dispatch::Vec;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub type BalanceOf<T> = <T as pallet_assets::Config>::Balance;
pub type Reward<T> = Rewards<
<T as frame_system::Config>::AccountId,
<T as pallet_assets::Config>::AssetId,
BalanceOf<T>,
BoundedVec<u8, <T as pallet::Config>::DataLimit>
>;
// Definition of the pallet logic, to be aggregated at runtime definition through
// `construct_runtime`.
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
/// The module configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config + pallet_assets::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
#[pallet::constant]
type StringLimit: Get<u32>;
/// Type to access the profile pallet
type Profile: ProfileInspect<Self::AccountId, <Self as pallet::Config>::StringLimit> +
ProfileInterface<Self::AccountId, <Self as pallet::Config>::StringLimit>;
/// The maximum length of data stored on-chain.
#[pallet::constant]
type DataLimit: Get<u32>;
/// Type to access the sub account pallet
type SubAccounts: SubAccounts<Self::AccountId, AccountOrigin>;
}
#[pallet::storage]
#[pallet::getter(fn reward_item)]
/// reward item by reward id
pub(super) type RewardItem<T: Config> = StorageMap<_, Blake2_128Concat, u32, Reward<T>>;
#[pallet::storage]
#[pallet::getter(fn reward_id)]
/// current reward id
pub type RewardId<T> = StorageValue<_, u32>;
#[pallet::storage]
#[pallet::getter(fn all_rewards)]
/// all rewards
pub type AllRewards<T: Config> = StorageValue<_, Vec<Reward<T>>>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Reward Created
RewardCreated {
user_address: T::AccountId,
asset_id: T::AssetId,
title: BoundedVec<u8, T::DataLimit>,
url_link: BoundedVec<u8, T::DataLimit>,
min_tokens: BalanceOf<T>,
reward_id: u32,
},
/// Reward Updated
RewardUpdated {
user_address: T::AccountId,
asset_id: T::AssetId,
title: BoundedVec<u8, T::DataLimit>,
url_link: BoundedVec<u8, T::DataLimit>,
min_tokens: BalanceOf<T>,
reward_id: u32,
},
/// Reward Deleted
RewardDeleted {
reward_id: u32,
},
}
#[pallet::error]
pub enum Error<T> {
/// No Rewards Yet
NoRewardsYet,
/// Reward Item Not Found
RewardItemNotFound,
/// Overflow Occured while performing Calculations
Overflow,
/// Storage Id is not Valid
InvalidId,
/// Profile Not Found
ProfileNotFound,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Creates a reward from a public origin.
///
/// This Create's a reward and the owner of that will be the Origin.
///
/// Origin must be Signed.
///
/// Parameters:
/// - `title`: The of that reward
/// - `url_link`: The url link for that reward
/// - `min_tokens`: The Min Tokens to claim reward
/// - `asset_id`: The asset id for that reward
///
/// Emits `RewardCreated` event when successful.
#[pallet::call_index(0)]
#[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())]
pub fn create_reward(
origin: OriginFor<T>,
asset_id: T::AssetId,
title: BoundedVec<u8, T::DataLimit>,
url_link: BoundedVec<u8, T::DataLimit>,
min_tokens: BalanceOf<T>
) -> DispatchResult {
let mut who = ensure_signed(origin.clone())?;
// Mutate the origin to transfer funds from the main account
who = <T as pallet::Config>::SubAccounts::get_main_account(who)?;
// making sure storage value is not null
if RewardId::<T>::get().is_none() {
RewardId::<T>::put(0);
}
// getting current id
let current_reward_id = RewardId::<T>::get().ok_or(Error::<T>::InvalidId)?;
let new_id = current_reward_id.checked_add(1).ok_or(Error::<T>::Overflow)?;
let reward = Rewards {
asset_id,
user_address: who.clone(),
title: title.clone(),
url_link: url_link.clone(),
min_tokens,
reward_id: new_id,
};
// making sure storage value is not null
if AllRewards::<T>::get().is_none() {
let empty_vec: Vec<Reward<T>> = Vec::new();
AllRewards::<T>::put(empty_vec);
}
// add to all rewards
let mut all_rewards = AllRewards::<T>::get().ok_or(Error::<T>::NoRewardsYet)?;
all_rewards.push(reward.clone());
AllRewards::<T>::put(all_rewards);
// add to reward item
RewardItem::<T>::insert(new_id, reward);
// update the reward id
RewardId::<T>::put(new_id);
Self::deposit_event(Event::RewardCreated {
asset_id,
user_address: who,
title,
url_link,
min_tokens,
reward_id: new_id,
});
Ok(())
}
/// Delete's a reward from a public origin.
///
/// This Delete's a reward and the owner of that will be the Origin.
///
/// Origin must be Signed.
///
/// Parameters:
/// - `reward_id`: The id of that reward
///
/// Emits `RewardDeleted` event when successful.
#[pallet::call_index(1)]
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1, 1).ref_time())]
pub fn delete_reward(origin: OriginFor<T>, reward_id: u32) -> DispatchResult {
let mut _who = ensure_signed(origin.clone())?;
// Mutate the origin to transfer funds from the main account
_who = <T as pallet::Config>::SubAccounts::get_main_account(_who)?;
// getting all rewards
let mut all_rewards = AllRewards::<T>::get().ok_or(Error::<T>::NoRewardsYet)?;
// getting the reward item
let old_reward = RewardItem::<T>::get(reward_id).ok_or(Error::<T>::RewardItemNotFound)?;
all_rewards.retain(|x| *x != old_reward);
// updating the rewards
AllRewards::<T>::put(all_rewards);
// removing reward item
RewardItem::<T>::remove(reward_id);
Self::deposit_event(Event::RewardDeleted {
reward_id,
});
Ok(())
}
/// Update's a reward from a public origin.
///
/// This Update's a reward and the owner of that will be the Origin.
///
/// Origin must be Signed.
///
/// Parameters:
/// - `title`: The of that reward
/// - `url_link`: The url link for that reward
/// - `min_tokens`: The Min Tokens to claim reward
/// - `asset_id`: The asset id for that reward
/// - `reward_id`: The id of that reward
///
/// Emits `RewardDeleted` event when successful.
#[pallet::call_index(2)]
#[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())]
pub fn update_reward(
origin: OriginFor<T>,
asset_id: T::AssetId,
title: BoundedVec<u8, T::DataLimit>,
url_link: BoundedVec<u8, T::DataLimit>,
min_tokens: BalanceOf<T>,
reward_id: u32
) -> DispatchResult {
let mut who = ensure_signed(origin.clone())?;
// Mutate the origin to transfer funds from the main account
who = <T as pallet::Config>::SubAccounts::get_main_account(who)?;
// getting from all rewards
let mut all_rewards = AllRewards::<T>::get().ok_or(Error::<T>::NoRewardsYet)?;
// removing old reward
let old_reward = RewardItem::<T>::get(reward_id).ok_or(Error::<T>::RewardItemNotFound)?;
all_rewards.retain(|x| *x != old_reward);
let reward = Rewards {
asset_id,
user_address: who.clone(),
title: title.clone(),
url_link: url_link.clone(),
min_tokens,
reward_id,
};
// adding the updated reward
all_rewards.push(reward.clone());
AllRewards::<T>::put(all_rewards);
// add to reward item
RewardItem::<T>::insert(reward_id, reward);
Self::deposit_event(Event::RewardUpdated {
asset_id,
user_address: who,
title,
url_link,
min_tokens,
reward_id,
});
Ok(())
}
}
}