Skip to content
This repository was archived by the owner on Sep 28, 2023. It is now read-only.

Commit 0532a53

Browse files
authored
Migrated dapps staking PR (#1)
1 parent 02203f8 commit 0532a53

File tree

10 files changed

+1138
-290
lines changed

10 files changed

+1138
-290
lines changed

frame/dapps-staking/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pallet-dapps-staking"
3-
version = "1.1.2"
3+
version = "2.0.0"
44
authors = ["Stake Technologies <[email protected]>"]
55
edition = "2018"
66
homepage = "https://astar.network/"

frame/dapps-staking/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ EraRewardAndStake {
7474
## Events
7575

7676
* `BondAndStake(AccountId, SmartContract, Balance):` Account has bonded and staked funds on a smart contract.
77-
* `UnbondUnstakeAndWithdraw(AccountId, SmartContract, Balance):` Account has unbonded, unstaked and withdrawn funds.
77+
* `UnbondAndUnstake(AccountId, SmartContract, Balance):` Account has unbonded & unstaked some funds. Unbonding process begins.
78+
* `Withdrawn(AccountId, Balance):` Account has withdrawn unbonded funds.
7879
* `NewContract(AccountId, SmartContract):` New contract added for staking.
7980
* `ContractRemoved(AccountId, SmartContract):` Contract removed from dapps staking.
8081
* `NewDappStakingEra(EraIndex):` New dapps staking era. Distribute era rewards to contracts.
81-
* `ContractClaimed(SmartContract, EraIndex, Balance):` The contract's reward has been claimed for an era
8282
* `Reward(AccountId, SmartContract, EraIndex, Balance):` Reward paid to staker or developer.
8383

8484

@@ -90,6 +90,9 @@ EraRewardAndStake {
9090
* `NotOperatedContract`, Targets must be operated contracts
9191
* `NotStakedContract`, Contract isn't staked.
9292
* `UnstakingWithNoValue`, Unstaking a contract with zero value.
93+
* `NothingToWithdraw`, There are no previously unbonded funds that can be unstaked and withdrawn.
94+
* `TooManyUnlockingChunks`, Contract has too many unlocking chunks. Withdraw the existing chunks if possible or wait for
95+
current chunks to complete unlocking process to withdraw them.
9396
* `AlreadyRegisteredContract`, The contract is already registered by other account.
9497
* `ContractIsNotValid`, User attempts to register with address which is not contract.
9598
* `AlreadyUsedDeveloperAccount`, This account was already used to register contract.

frame/dapps-staking/src/benchmarking.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,6 @@ benchmarks! {
106106
initialize::<T>();
107107
let (developer_id, contract_id) = register_contract::<T>()?;
108108
prepare_bond_and_stake::<T>(n, &contract_id, SEED)?;
109-
for id in 0..n {
110-
let claimer_id: T::AccountId = account("claimer", id, SEED);
111-
let balance: BalanceOf<T> = 100000u32.into();
112-
}
113109

114110
}: _(RawOrigin::Signed(developer_id.clone()), contract_id.clone())
115111
verify {
@@ -145,7 +141,7 @@ benchmarks! {
145141
assert_last_event::<T>(Event::<T>::BondAndStake(staker, contract_id, amount).into());
146142
}
147143

148-
unbond_unstake_and_withdraw {
144+
unbond_and_unstake {
149145
initialize::<T>();
150146

151147
let (_, contract_id) = register_contract::<T>()?;
@@ -156,11 +152,32 @@ benchmarks! {
156152
let amount = BalanceOf::<T>::max_value() / 2u32.into();
157153

158154
DappsStaking::<T>::bond_and_stake(RawOrigin::Signed(staker.clone()).into(), contract_id.clone(), amount.clone())?;
159-
advance_to_era::<T>(2);
160155

161156
}: _(RawOrigin::Signed(staker.clone()), contract_id.clone(), amount.clone())
162157
verify {
163-
assert_last_event::<T>(Event::<T>::UnbondUnstakeAndWithdraw(staker, contract_id, amount).into());
158+
assert_last_event::<T>(Event::<T>::UnbondAndUnstake(staker, contract_id, amount).into());
159+
}
160+
161+
withdraw_unbonded {
162+
initialize::<T>();
163+
164+
let (_, contract_id) = register_contract::<T>()?;
165+
prepare_bond_and_stake::<T>(T::MaxNumberOfStakersPerContract::get() - 1, &contract_id, SEED)?;
166+
167+
let staker = whitelisted_caller();
168+
let _ = T::Currency::make_free_balance_be(&staker, BalanceOf::<T>::max_value());
169+
let stake_amount = BalanceOf::<T>::max_value() / 2u32.into();
170+
let unstake_amount = stake_amount / 2u32.into();
171+
172+
DappsStaking::<T>::bond_and_stake(RawOrigin::Signed(staker.clone()).into(), contract_id.clone(), stake_amount.clone())?;
173+
DappsStaking::<T>::unbond_and_unstake(RawOrigin::Signed(staker.clone()).into(), contract_id.clone(), unstake_amount.clone())?;
174+
175+
let current_era = DappsStaking::<T>::current_era();
176+
advance_to_era::<T>(current_era + 1 + T::UnbondingPeriod::get());
177+
178+
}: _(RawOrigin::Signed(staker.clone()))
179+
verify {
180+
assert_last_event::<T>(Event::<T>::Withdrawn(staker, unstake_amount).into());
164181
}
165182

166183
claim {

frame/dapps-staking/src/lib.rs

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use frame_support::traits::Currency;
88
use frame_system::{self as system};
99
use scale_info::TypeInfo;
1010
use sp_runtime::RuntimeDebug;
11-
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
11+
use sp_std::{collections::btree_map::BTreeMap, ops::Add, prelude::*};
1212

1313
pub mod pallet;
1414
pub mod traits;
@@ -17,6 +17,7 @@ pub use traits::*;
1717

1818
#[cfg(any(feature = "runtime-benchmarks"))]
1919
pub mod benchmarking;
20+
pub mod migrations;
2021
#[cfg(test)]
2122
mod mock;
2223
#[cfg(test)]
@@ -58,10 +59,12 @@ impl Default for Forcing {
5859

5960
/// A record for total rewards and total amount staked for an era
6061
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
61-
pub struct EraRewardAndStake<Balance> {
62+
pub struct EraRewardAndStake<Balance: HasCompact> {
6263
/// Total amount of rewards for an era
64+
#[codec(compact)]
6365
rewards: Balance,
6466
/// Total staked amount for an era
67+
#[codec(compact)]
6568
staked: Balance,
6669
}
6770

@@ -71,11 +74,134 @@ pub struct EraRewardAndStake<Balance> {
7174
#[derive(Clone, PartialEq, Encode, Decode, Default, RuntimeDebug, TypeInfo)]
7275
pub struct EraStakingPoints<AccountId: Ord, Balance: HasCompact> {
7376
/// Total staked amount.
77+
#[codec(compact)]
7478
total: Balance,
7579
/// The map of stakers and the amount they staked.
7680
stakers: BTreeMap<AccountId, Balance>,
77-
// TODO: Get rid of this
78-
_former_staked_era: EraIndex,
7981
/// Accrued and claimed rewards on this contract both for stakers and the developer
82+
#[codec(compact)]
8083
claimed_rewards: Balance,
8184
}
85+
86+
/// Storage value representing the current Dapps staking pallet storage version.
87+
/// Used by `on_runtime_upgrade` to determine whether a storage migration is needed or not.
88+
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
89+
pub enum Version {
90+
V1_0_0,
91+
V2_0_0,
92+
}
93+
94+
impl Default for Version {
95+
fn default() -> Self {
96+
Version::V1_0_0
97+
}
98+
}
99+
100+
/// Represents an balance amount undergoing the unbonding process.
101+
/// Since unbonding takes time, it's important to keep track of when and how much was unbonded.
102+
#[derive(Clone, Copy, PartialEq, Encode, Decode, Default, RuntimeDebug, TypeInfo)]
103+
pub struct UnlockingChunk<Balance> {
104+
/// Amount being unlocked
105+
#[codec(compact)]
106+
amount: Balance,
107+
/// Era in which the amount will become unlocked and can be withdrawn.
108+
#[codec(compact)]
109+
unlock_era: EraIndex,
110+
}
111+
112+
impl<Balance> UnlockingChunk<Balance>
113+
where
114+
Balance: Add<Output = Balance> + Copy,
115+
{
116+
// Adds the specified amount to this chunk
117+
fn add_amount(&mut self, amount: Balance) {
118+
self.amount = self.amount + amount
119+
}
120+
}
121+
122+
/// Contains unlocking chunks.
123+
/// This is a convenience struct that provides various utility methods to help with unbonding handling.
124+
#[derive(Clone, PartialEq, Encode, Decode, Default, RuntimeDebug, TypeInfo)]
125+
pub struct UnbondingInfo<Balance> {
126+
// Vector of unlocking chunks. Sorted in ascending order in respect to unlock_era.
127+
unlocking_chunks: Vec<UnlockingChunk<Balance>>,
128+
}
129+
130+
impl<Balance> UnbondingInfo<Balance>
131+
where
132+
Balance: Add<Output = Balance> + Default + Copy,
133+
{
134+
/// Returns total number of unlocking chunks.
135+
fn len(&self) -> u32 {
136+
self.unlocking_chunks.len() as u32
137+
}
138+
139+
/// True if no unlocking chunks exist, false otherwise.
140+
fn is_empty(&self) -> bool {
141+
self.unlocking_chunks.is_empty()
142+
}
143+
144+
/// Returns sum of all unlocking chunks.
145+
fn sum(&self) -> Balance {
146+
self.unlocking_chunks
147+
.iter()
148+
.map(|chunk| chunk.amount)
149+
.reduce(|c1, c2| c1 + c2)
150+
.unwrap_or_default()
151+
}
152+
153+
/// Adds a new unlocking chunk to the vector, preserving the unlock_era based ordering.
154+
fn add(&mut self, chunk: UnlockingChunk<Balance>) {
155+
// It is possible that the unbonding period changes so we need to account for that
156+
match self
157+
.unlocking_chunks
158+
.binary_search_by(|x| x.unlock_era.cmp(&chunk.unlock_era))
159+
{
160+
// Merge with existing chunk if unlock_eras match
161+
Ok(pos) => self.unlocking_chunks[pos].add_amount(chunk.amount),
162+
// Otherwise insert where it should go. Note that this will in almost all cases return the last index.
163+
Err(pos) => self.unlocking_chunks.insert(pos, chunk),
164+
}
165+
}
166+
167+
/// Partitions the unlocking chunks into two groups:
168+
///
169+
/// First group includes all chunks which have unlock era lesser or equal to the specified era.
170+
/// Second group includes all the rest.
171+
///
172+
/// Order of chunks is preserved in the two new structs.
173+
fn partition(self, era: EraIndex) -> (Self, Self) {
174+
let (matching_chunks, other_chunks): (
175+
Vec<UnlockingChunk<Balance>>,
176+
Vec<UnlockingChunk<Balance>>,
177+
) = self
178+
.unlocking_chunks
179+
.iter()
180+
.partition(|chunk| chunk.unlock_era <= era);
181+
182+
(
183+
Self {
184+
unlocking_chunks: matching_chunks,
185+
},
186+
Self {
187+
unlocking_chunks: other_chunks,
188+
},
189+
)
190+
}
191+
192+
#[cfg(test)]
193+
/// Return clone of the internal vector. Should only be used for testing.
194+
fn vec(&self) -> Vec<UnlockingChunk<Balance>> {
195+
self.unlocking_chunks.clone()
196+
}
197+
}
198+
199+
/// Contains information about account's locked & unbonding balances.
200+
#[derive(Clone, PartialEq, Encode, Decode, Default, RuntimeDebug, TypeInfo)]
201+
pub struct AccountLedger<Balance: HasCompact> {
202+
/// Total balance locked.
203+
#[codec(compact)]
204+
locked: Balance,
205+
/// Information about unbonding chunks.
206+
unbonding_info: UnbondingInfo<Balance>,
207+
}

0 commit comments

Comments
 (0)