/home/runner/work/creditcoin/creditcoin/pallets/rewards/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | 1 | #![cfg_attr(not(feature = "std"), no_std)]#![cfg_attr(not(feature = "std"), no_std)] |
2 | | |
3 | | use core::convert::TryFrom; |
4 | | use frame_support::traits::Currency; |
5 | | pub use pallet::*; |
6 | | use sp_runtime::{traits::Saturating, FixedPointNumber}; |
7 | | |
8 | | #[cfg(test)] |
9 | | mod mock; |
10 | | |
11 | | #[cfg(test)] |
12 | | mod tests; |
13 | | |
14 | | mod benchmarking; |
15 | | |
16 | | #[allow(clippy::unnecessary_cast)] |
17 | | pub mod weights; |
18 | | |
19 | | pub type BalanceOf<T> = |
20 | | <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; |
21 | | |
22 | | pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId; |
23 | | |
24 | | pub const REWARD_HALF_LIFE: u64 = 2_500_000; |
25 | | pub const BASE_REWARD_IN_CTC: u64 = 28; |
26 | | pub const CREDO_PER_CTC: u64 = 1_000_000_000_000_000_000; |
27 | | pub const SAWTOOTH_PORT_HEIGHT: u64 = 1_123_966; |
28 | | |
29 | 19 | #[frame_support::pallet]18 |
30 | 0 | pub mod pallet { |
31 | 0 | use super::*; |
32 | 0 | use frame_support::{pallet_prelude::*, traits::Currency}; |
33 | 0 | use frame_system::pallet_prelude::*; |
34 | 0 | use sp_runtime::{ |
35 | 0 | traits::{UniqueSaturatedFrom, UniqueSaturatedInto}, |
36 | 0 | FixedU128, |
37 | 0 | }; |
38 | 0 |
|
39 | 0 | #[pallet::config] |
40 | 0 | pub trait Config: frame_system::Config |
41 | 0 | where |
42 | 0 | <Self as frame_system::Config>::BlockNumber: UniqueSaturatedInto<u64>, |
43 | 0 | BalanceOf<Self>: UniqueSaturatedFrom<u128>, |
44 | 0 | { |
45 | 0 | /// Because this pallet emits events, it depends on the runtime's definition of an event. |
46 | 0 | type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; |
47 | 0 |
|
48 | 0 | type Currency: Currency<AccountIdOf<Self>>; |
49 | 0 |
|
50 | 0 | type WeightInfo: WeightInfo; |
51 | 0 | } |
52 | 0 |
|
53 | 0 | pub trait WeightInfo { |
54 | 0 | fn on_finalize() -> Weight; |
55 | 0 | fn on_initialize() -> Weight; |
56 | 0 | } |
57 | 0 |
|
58 | 4 | #[pallet::pallet] |
59 | 0 | #[pallet::generate_store(pub(super) trait Store)] |
60 | 0 | pub struct Pallet<T>(_); |
61 | 0 |
|
62 | 38 | #[pallet::storage] |
63 | 0 | #[pallet::getter(fn block_author)] |
64 | 0 | pub type BlockAuthor<T> = StorageValue<_, AccountIdOf<T>>; |
65 | 0 |
|
66 | 0 | // Pallets use events to inform users when important changes are made. |
67 | 0 | // https://substrate.dev/docs/en/knowledgebase/runtime/events |
68 | 0 | #[pallet::event] |
69 | 17 | #[pallet::generate_deposit(pub(super) fn deposit_event)] |
70 | 0 | pub enum Event<T: Config> { |
71 | 0 | /// Reward was issued. [block_author, amount] |
72 | 0 | RewardIssued(AccountIdOf<T>, BalanceOf<T>), |
73 | 0 | } |
74 | 0 |
|
75 | 0 | #[pallet::hooks] |
76 | 0 | impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { |
77 | 18 | fn on_initialize(_now: BlockNumberFor<T>) -> Weight { |
78 | 18 | let block_author = frame_system::Pallet::<T>::digest().convert_first(|item| { |
79 | 18 | item.pre_runtime_try_to::<AccountIdOf<T>>(&sp_consensus_pow::POW_ENGINE_ID) |
80 | 18 | }); |
81 | 0 |
|
82 | 18 | if let Some(author) = block_author { |
83 | 18 | BlockAuthor::<T>::put(author); |
84 | 18 | }0 |
85 | 0 |
|
86 | 18 | T::WeightInfo::on_finalize().saturating_add(T::WeightInfo::on_initialize()) |
87 | 18 | } |
88 | 0 |
|
89 | 0 | fn on_finalize(block_number: BlockNumberFor<T>) { |
90 | 18 | if let Some(author16 ) = BlockAuthor::<T>::get() { |
91 | 16 | let reward = Self::reward_amount(block_number); |
92 | 16 | Self::issue_reward(author, reward); |
93 | 16 | }2 |
94 | 18 | } |
95 | 0 | } |
96 | 0 |
|
97 | 0 | impl<T: Config> Pallet<T> { |
98 | 22 | pub fn reward_amount(block_number: BlockNumberFor<T>) -> BalanceOf<T> { |
99 | 22 | let block_number: u64 = Self::sawtooth_adjusted_height(block_number); |
100 | 22 | let period = usize::try_from(block_number / REWARD_HALF_LIFE).expect("assuming a 32-bit usize, we would need to be on block number 2^32 * REWARD_HALF_LIFE for this conversion to fail.\ |
101 | 22 | given a 1s block time it would take >340 million years to reach this point; qed"); |
102 | 22 | let decay_rate_inv = FixedU128::saturating_from_rational(19, 20); |
103 | 22 | let multiplier = decay_rate_inv.saturating_pow(period); |
104 | 22 | let reward_in_ctc: u128 = |
105 | 22 | multiplier.saturating_mul_int(CREDO_PER_CTC).unique_saturated_into(); |
106 | 22 | let reward_amount = reward_in_ctc.saturating_mul(BASE_REWARD_IN_CTC.into()); |
107 | 22 | <BalanceOf<T>>::unique_saturated_from(reward_amount) |
108 | 22 | } |
109 | 17 | pub fn issue_reward(recipient: AccountIdOf<T>, amount: BalanceOf<T>) { |
110 | 17 | drop(T::Currency::deposit_creating(&recipient, amount)); |
111 | 17 | Self::deposit_event(Event::<T>::RewardIssued(recipient, amount)); |
112 | 17 | } |
113 | 0 |
|
114 | 22 | pub fn sawtooth_adjusted_height(block_number: BlockNumberFor<T>) -> u64 { |
115 | 22 | let block_number: u64 = block_number.unique_saturated_into(); |
116 | 22 | block_number.saturating_add(SAWTOOTH_PORT_HEIGHT) |
117 | 22 | } |
118 | 0 | } |
119 | 0 | } |