-
Notifications
You must be signed in to change notification settings - Fork 36
Beneficiary #113
Beneficiary #113
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -180,6 +180,19 @@ pub mod pallet { | |
| ValueQuery, | ||
| >; | ||
|
|
||
| /// Beneficiary of staking rewards on perticular contract. | ||
| /// `(staker, contract_id) -> beneficiary_account_id` | ||
| #[pallet::storage] | ||
| #[pallet::getter(fn staking_beneficiary)] | ||
| pub type RewardsBeneficiary<T: Config> = StorageDoubleMap< | ||
| _, | ||
| Twox64Concat, | ||
| T::AccountId, | ||
| Blake2_128Concat, | ||
| T::SmartContract, | ||
| T::AccountId, | ||
| >; | ||
|
|
||
| /// Stores the current pallet storage version. | ||
| #[pallet::storage] | ||
| #[pallet::getter(fn storage_version)] | ||
|
|
@@ -233,6 +246,12 @@ pub mod pallet { | |
| BalanceOf<T>, | ||
| T::SmartContract, | ||
| ), | ||
| /// Staking rewards beneficiary is set | ||
| BeneficiarySet(T::AccountId, T::SmartContract, T::AccountId), | ||
| /// Staking rewards beneficiary is removed | ||
| BeneficiaryRemoved(T::AccountId, T::SmartContract), | ||
| /// Staking rewards beneficiary is updated | ||
| BeneficiaryUpdated(T::AccountId, T::SmartContract, T::AccountId), | ||
| } | ||
|
|
||
| #[pallet::error] | ||
|
|
@@ -291,6 +310,12 @@ pub mod pallet { | |
| NotActiveStaker, | ||
| /// Transfering nomination to the same contract | ||
| NominationTransferToSameContract, | ||
| /// There is no beneficiary set for the staker per contract | ||
| BeneficiaryNotSet, | ||
| /// Not allow non-beneficiary to update | ||
| UpdateBeneficiaryNotAllowed, | ||
| /// Beneficiary used is not valid | ||
| InvalidBeneficiary, | ||
| } | ||
|
|
||
| #[pallet::hooks] | ||
|
|
@@ -709,8 +734,10 @@ pub mod pallet { | |
| /// Claim earned staker rewards for the oldest unclaimed era. | ||
| /// In order to claim multiple eras, this call has to be called multiple times. | ||
| /// | ||
| /// The rewards are always added to the staker's free balance (account) but depending on the reward destination configuration, | ||
| /// they might be immediately re-staked. | ||
| /// When [`RewardsBeneficiary`] is set, the rewards are added to the beneficiary's free balance and unlocked. | ||
| /// When RewardsBeneficiary is not set, the rewards are added to, | ||
| /// - staker's free balance but locked with [`RewardDestination`] is StakeBalance | ||
| /// - staker's free balance and unlocked with [`RewardDestination`] is FreeBalance | ||
| #[pallet::weight(T::WeightInfo::claim_staker_with_restake().max(T::WeightInfo::claim_staker_without_restake()))] | ||
| pub fn claim_staker( | ||
| origin: OriginFor<T>, | ||
|
|
@@ -751,7 +778,10 @@ pub mod pallet { | |
| staker_info.latest_staked_value(), | ||
| ); | ||
|
|
||
| if should_restake_reward { | ||
| let beneficiary = RewardsBeneficiary::<T>::get(&staker, &contract_id); | ||
| let mut weight_info = T::WeightInfo::claim_staker_without_restake(); | ||
|
|
||
| if beneficiary.is_none() && should_restake_reward { | ||
| staker_info | ||
| .stake(current_era, staker_reward) | ||
| .map_err(|_| Error::<T>::UnexpectedStakeInfoEra)?; | ||
|
|
@@ -762,17 +792,7 @@ pub mod pallet { | |
| staker_info.len() <= T::MaxEraStakeValues::get(), | ||
| Error::<T>::TooManyEraStakeValues | ||
| ); | ||
| } | ||
|
|
||
| // Withdraw reward funds from the dapps staking pot | ||
| let reward_imbalance = T::Currency::withdraw( | ||
| &Self::account_id(), | ||
| staker_reward, | ||
| WithdrawReasons::TRANSFER, | ||
| ExistenceRequirement::AllowDeath, | ||
| )?; | ||
|
|
||
| if should_restake_reward { | ||
| ledger.locked = ledger.locked.saturating_add(staker_reward); | ||
| Self::update_ledger(&staker, ledger); | ||
|
|
||
|
|
@@ -795,18 +815,24 @@ pub mod pallet { | |
| contract_id.clone(), | ||
| staker_reward, | ||
| )); | ||
|
|
||
| weight_info = T::WeightInfo::claim_staker_with_restake(); | ||
| } | ||
|
|
||
| T::Currency::resolve_creating(&staker, reward_imbalance); | ||
| // Withdraw reward funds from the dapps staking pot | ||
| let reward_imbalance = T::Currency::withdraw( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if this fails at this point?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The failed call will not affect the chain state, since the call by default is transactional.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, after paritytech/substrate#11431. |
||
| &Self::account_id(), | ||
| staker_reward, | ||
| WithdrawReasons::TRANSFER, | ||
| ExistenceRequirement::AllowDeath, | ||
| )?; | ||
|
|
||
| let reward_account = beneficiary.clone().unwrap_or(staker.clone()); | ||
| T::Currency::resolve_creating(&reward_account, reward_imbalance); | ||
| Self::update_staker_info(&staker, &contract_id, staker_info); | ||
| Self::deposit_event(Event::<T>::Reward(staker, contract_id, era, staker_reward)); | ||
| Self::deposit_event(Event::<T>::Reward(reward_account, contract_id, era, staker_reward)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this is confusing a bit - Right now, beneficiary could be an account that's not even part of dapps-staking, so it'd be even more confusing. Could you share your opinion on this?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Current Reward event reads as For UX, most users don't need to bother this beneficiary settings, by default the Instead of adding an extra storage, make the reward destination to include beneficiary seems more straight forward, but require more thorough design and changes.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I like the scenarios, even though this is just a dummy-pre-screening-task feature :) Differentiating when stakers receive rewards and when it's just via a reward beneficiary is important if we want to be aware how much someone earned as a staker. With the modified event, we cannot tell whether the user received rewards as a beneficiary or as a nomination reward. And love the proposal about reward destination 👍 😄 , was hoping for it! |
||
|
|
||
| Ok(Some(if should_restake_reward { | ||
| T::WeightInfo::claim_staker_with_restake() | ||
| } else { | ||
| T::WeightInfo::claim_staker_without_restake() | ||
| }) | ||
| .into()) | ||
| Ok(Some(weight_info).into()) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a personal preference or was the approach before incorrect?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a personal preference, trying to make return logic simple and default based. |
||
| } | ||
|
|
||
| /// Claim earned dapp rewards for the specified era. | ||
|
|
@@ -961,6 +987,79 @@ pub mod pallet { | |
| Ok(().into()) | ||
| } | ||
|
|
||
| /// Sets the beneficiary of staking rewards. | ||
| /// | ||
| /// The dispatch origin is staker. | ||
| #[pallet::weight(10_000)] | ||
| pub fn set_rewards_beneficiary( | ||
| origin: OriginFor<T>, | ||
| contract_id: T::SmartContract, | ||
| beneficiary: T::AccountId, | ||
| ) -> DispatchResultWithPostInfo { | ||
| Self::ensure_pallet_enabled()?; | ||
| let staker = ensure_signed(origin)?; | ||
|
|
||
| ensure!( | ||
| Self::is_active(&contract_id), | ||
| Error::<T>::NotOperatedContract | ||
| ); | ||
| ensure!(beneficiary != staker, Error::<T>::InvalidBeneficiary); | ||
|
|
||
| RewardsBeneficiary::<T>::insert(&staker, &contract_id, &beneficiary); | ||
|
|
||
| Self::deposit_event(Event::<T>::BeneficiarySet(staker, contract_id, beneficiary)); | ||
| Ok(().into()) | ||
| } | ||
|
|
||
| /// Removes the beneficiary of staking rewards. | ||
| /// | ||
| /// The dispatch origin is staker. | ||
| #[pallet::weight(10_000)] | ||
| pub fn remove_rewards_beneficiary( | ||
| origin: OriginFor<T>, | ||
| contract_id: T::SmartContract, | ||
| ) -> DispatchResultWithPostInfo { | ||
| Self::ensure_pallet_enabled()?; | ||
| let staker = ensure_signed(origin)?; | ||
|
|
||
| RewardsBeneficiary::<T>::remove(&staker, &contract_id); | ||
|
|
||
| Self::deposit_event(Event::<T>::BeneficiaryRemoved(staker, contract_id)); | ||
| Ok(().into()) | ||
| } | ||
|
|
||
| /// Updates the beneficiary to a new account. | ||
| /// | ||
| /// The dispatch origin is the current beneficiary. | ||
| #[pallet::weight(10_000)] | ||
| pub fn update_rewards_beneficiary( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps |
||
| origin: OriginFor<T>, | ||
| staker: T::AccountId, | ||
| contract_id: T::SmartContract, | ||
| new_beneficiary: T::AccountId, | ||
| ) -> DispatchResultWithPostInfo { | ||
| Self::ensure_pallet_enabled()?; | ||
| let sender = ensure_signed(origin)?; | ||
|
|
||
| ensure!( | ||
| Self::is_active(&contract_id), | ||
| Error::<T>::NotOperatedContract | ||
| ); | ||
|
|
||
| RewardsBeneficiary::<T>::try_mutate(&staker, &contract_id, |maybe_beneficiary| -> DispatchResultWithPostInfo { | ||
| let beneficiary = maybe_beneficiary.as_mut().ok_or(Error::<T>::BeneficiaryNotSet)?; | ||
| ensure!(sender == *beneficiary, Error::<T>::UpdateBeneficiaryNotAllowed); | ||
| ensure!(new_beneficiary != staker, Error::<T>::InvalidBeneficiary); | ||
|
|
||
| *beneficiary = new_beneficiary.clone(); | ||
|
|
||
| Ok(().into()) | ||
| })?; | ||
|
|
||
| Self::deposit_event(Event::<T>::BeneficiaryUpdated(staker, contract_id, new_beneficiary)); | ||
| Ok(().into()) | ||
| } | ||
|
|
||
| /// Used to force set `ContractEraStake` storage values. | ||
| /// The purpose of this call is only for fixing one of the issues detected with dapps-staking. | ||
| /// | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not fond of argument bloat, but perhaps the added check would be better suited inside
fn should_restake_reward?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!