Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added counter for individual rewards #450

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions frame/dapps-staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub struct EraStakingPoints<AccountId: Ord, Balance: HasCompact> {
/// Era when this contract was staked last time before this one.
/// In case only a single staking era exists, it will be set to that one. This indicates the final element in the chain.
former_staked_era: EraIndex,
/// Accrued and claimed rewards on this contract both for stakers and the developer
claimed_rewards: Balance,
}

/// Multi-VM pointer to smart contract instance.
Expand All @@ -82,16 +84,3 @@ pub enum SmartContract<AccountId> {
/// Wasm smart contract instance.
Wasm(AccountId),
}

/// The ledger of a (bonded) stash.
#[derive(PartialEq, Eq, Clone, Encode, Decode, Default, RuntimeDebug)]
pub struct StakingLedger<Balance: HasCompact + Default> {
/// The total amount of the stash's balance that we are currently accounting for.
/// It's just `active` plus all the `unlocking` balances.
#[codec(compact)]
pub total: Balance,
/// The total amount of the stash's balance that will be at stake in any forthcoming
/// rounds.
#[codec(compact)]
pub active: Balance,
}
73 changes: 49 additions & 24 deletions frame/dapps-staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,27 +75,19 @@ pub mod pallet {
84u32
}

/// Map from all (unlocked) "controller" accounts to the info regarding the staking.
/// Bonded amount for the staker
#[pallet::storage]
#[pallet::getter(fn ledger)]
pub(crate) type Ledger<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<BalanceOf<T>>>;
pub(crate) type Ledger<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, BalanceOf<T>>;

/// Number of eras to keep in history.
///
/// Information is kept for eras in `[current_era - history_depth; current_era]`.
///
/// Must be more than the number of eras delayed by session otherwise. I.e. active era must
/// always be in history. I.e. `active_era > current_era - history_depth` must be
/// guaranteed.
#[pallet::storage]
#[pallet::getter(fn history_depth)]
pub(crate) type HistoryDepth<T> = StorageValue<_, u32, ValueQuery, HistoryDepthOnEmpty>;

/// The current era index.
///
/// This is the latest planned era, depending on how the Session pallet queues the validator
/// set, it might be active or not.
#[pallet::storage]
#[pallet::getter(fn current_era)]
pub type CurrentEra<T> = StorageValue<_, EraIndex, ValueQuery>;
Expand Down Expand Up @@ -132,6 +124,19 @@ pub mod pallet {
pub(crate) type EraRewardsAndStakes<T: Config> =
StorageMap<_, Twox64Concat, EraIndex, EraRewardAndStake<BalanceOf<T>>>;

/// Reward counter for individual stakers and the developer
#[pallet::storage]
#[pallet::getter(fn rewards_claimed)]
pub(crate) type RewardsClaimed<T: Config> = StorageDoubleMap<
_,
Twox64Concat,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since both key types are supplied from the outside, shouldn't we use secure hashing?

Also applies to the rest of the maps in dapps staking pallet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's take this out of the scope of this PR.
I have created new todo for this
https://github.com/PlasmNetwork/Astar/projects/5#card-69947102

SmartContract<T::AccountId>,
Twox64Concat,
T::AccountId,
BalanceOf<T>,
ValueQuery,
>;

/// Stores amount staked and stakers for a contract per era
#[pallet::storage]
#[pallet::getter(fn contract_era_stake)]
Expand Down Expand Up @@ -387,13 +392,12 @@ pub mod pallet {
// Ensure that staker has enough balance to bond & stake.
let free_balance = T::Currency::free_balance(&staker);
// Remove already locked funds from the free balance
let available_balance = free_balance.saturating_sub(ledger.total);
let available_balance = free_balance.saturating_sub(ledger);
let value_to_stake = value.min(available_balance);
ensure!(!value_to_stake.is_zero(), Error::<T>::StakingWithNoValue);

// update the ledger value by adding the newly bonded funds
ledger.total += value_to_stake;
ledger.active += value_to_stake;
ledger += value_to_stake;

// Get the latest era staking point info or create it if contract hasn't been staked yet so far.
let era_when_contract_last_staked = Self::contract_last_staked(&contract_id);
Expand All @@ -407,6 +411,7 @@ pub mod pallet {
total: Zero::zero(),
stakers: BTreeMap::<T::AccountId, BalanceOf<T>>::new(),
former_staked_era: 0 as EraIndex,
claimed_rewards: BalanceOf::<T>::default(),
}
};

Expand Down Expand Up @@ -527,6 +532,8 @@ pub mod pallet {
// if staked value would fall below threshold, unstake everything
era_staking_points.stakers.remove(&staker);
value_to_unstake = staked_value;
// remove reward counter
RewardsClaimed::<T>::remove(&contract_id, &staker);
} else {
era_staking_points
.stakers
Expand All @@ -538,8 +545,7 @@ pub mod pallet {

// Get the staking ledger and update it
let mut ledger = Self::ledger(&staker).ok_or(Error::<T>::UnexpectedState)?;
ledger.total = ledger.total.saturating_sub(value_to_unstake);
ledger.active = ledger.active.saturating_sub(value_to_unstake);
ledger = ledger.saturating_sub(value_to_unstake);
Self::update_ledger(&staker, &ledger);

let current_era = Self::current_era();
Expand Down Expand Up @@ -672,10 +678,19 @@ pub mod pallet {
// continue and process the next era interval
}

// send rewards to stakers
Self::payout_stakers(&rewards_for_stakers_map);
// send rewards to developer
// send rewards to stakers' accounts and update reward counter individual staker
let reward_for_stakers =
Self::payout_stakers_and_get_total_reward(&contract_id, &rewards_for_stakers_map);

// send rewards to developer's account and update reward counter for developer's account
T::Currency::deposit_into_existing(&developer, reward_for_developer).ok();
RewardsClaimed::<T>::mutate(&contract_id, &developer, |balance| {
*balance += reward_for_developer
});

// updated counter for total rewards paid to the contract
contract_staking_info.claimed_rewards += reward_for_stakers + reward_for_developer;

// if !unclaimed_rewards.is_zero() { TODO!
// T::Currency::deposit_into_existing(&treasury, unclaimed_rewards).ok();
// }
Expand Down Expand Up @@ -772,12 +787,12 @@ pub mod pallet {
impl<T: Config> Pallet<T> {
/// Update the ledger for a staker. This will also update the stash lock.
/// This lock will lock the entire funds except paying for further transactions.
fn update_ledger(staker: &T::AccountId, ledger: &StakingLedger<BalanceOf<T>>) {
if ledger.active.is_zero() {
fn update_ledger(staker: &T::AccountId, ledger: &BalanceOf<T>) {
if ledger.is_zero() {
Ledger::<T>::remove(&staker);
T::Currency::remove_lock(STAKING_ID, &staker);
} else {
T::Currency::set_lock(STAKING_ID, &staker, ledger.total, WithdrawReasons::all());
T::Currency::set_lock(STAKING_ID, &staker, ledger.clone(), WithdrawReasons::all());
Ledger::<T>::insert(staker, ledger);
}
}
Expand Down Expand Up @@ -810,15 +825,25 @@ pub mod pallet {
}
}

/// Execute payout for stakers
fn payout_stakers(staker_map: &BTreeMap<T::AccountId, BalanceOf<T>>) {
/// Execute payout for stakers.
/// Return total rewards claimed by stakers on this contract.
fn payout_stakers_and_get_total_reward(
contract: &SmartContract<T::AccountId>,
staker_map: &BTreeMap<T::AccountId, BalanceOf<T>>,
) -> BalanceOf<T> {
let mut reward_for_stakers = Zero::zero();

for (s, b) in staker_map {
RewardsClaimed::<T>::mutate(contract, s, |balance| *balance += *b);
T::Currency::deposit_into_existing(&s, *b).ok();
reward_for_stakers += *b;
}

reward_for_stakers
}

/// The block rewards are accumulated on the pallets's account during an era.
/// This function takes a snapshot of the pallet's balance accured during current era
/// This function takes a snapshot of the pallet's balance accrued during current era
/// and stores it for future distribution
///
/// This is called just at the beginning of an era.
Expand Down
37 changes: 35 additions & 2 deletions frame/dapps-staking/src/testing_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ pub(crate) fn unbond_unstake_and_withdraw_with_verification(
pub(crate) fn verify_ledger(staker_id: AccountId, staked_value: Balance) {
// Verify that ledger storage values are as expected.
let ledger = Ledger::<TestRuntime>::get(staker_id).unwrap();
assert_eq!(staked_value, ledger.total);
assert_eq!(staked_value, ledger.active);
assert_eq!(staked_value, ledger);
}

/// Used to verify era staking points content.
Expand Down Expand Up @@ -159,3 +158,37 @@ pub(crate) fn calc_expected_developer_reward(
let contract_reward = Perbill::from_rational(contract_stake, era_stake) * era_reward;
Perbill::from_percent(DEVELOPER_REWARD_PERCENTAGE) * contract_reward
}

/// Check staker/dev Balance after reward distribution.
/// Check that claimed rewards for staker/dev are updated.
pub(crate) fn check_rewards_on_balance_and_storage(
contract: &SmartContract<mock::AccountId>,
user: &AccountId,
free_balance: mock::Balance,
eras: EraIndex,
expected_era_reward: mock::Balance,
) {
let total_reward_per_era = expected_era_reward * eras as u128;
assert_eq!(
<mock::TestRuntime as Config>::Currency::free_balance(user),
free_balance + total_reward_per_era
);
assert_eq!(
mock::DappsStaking::rewards_claimed(contract, user),
total_reward_per_era
);
}

/// Check that claimed rewards on this contract are updated
pub(crate) fn check_paidout_rewards_for_contract(
contract: &SmartContract<mock::AccountId>,
expected_contract_reward: mock::Balance,
) {
let era_last_claimed = mock::DappsStaking::contract_last_claimed(contract).unwrap_or(0);
let contract_staking_info =
mock::DappsStaking::contract_era_stake(contract, era_last_claimed).unwrap_or_default();
assert_eq!(
contract_staking_info.claimed_rewards,
expected_contract_reward
)
}
Loading