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

dapp staking v3 - part 2 #991

Merged
merged 13 commits into from
Sep 21, 2023
Prev Previous commit
Next Next commit
Further modifications
Dinonard committed Aug 31, 2023
commit 892dab22ccbfae9a4309c1ec55e26e49eacad369
20 changes: 10 additions & 10 deletions pallets/dapp-staking-v3/src/lib.rs
Original file line number Diff line number Diff line change
@@ -76,6 +76,7 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// Currency used for staking.
/// TODO: remove usage of deprecated LockableCurrency trait and use the new freeze approach
type Currency: LockableCurrency<
Self::AccountId,
Moment = Self::BlockNumber,
@@ -94,6 +95,7 @@ pub mod pallet {
type MaxNumberOfContracts: Get<DAppId>;

/// Maximum number of locked chunks that can exist per account at a time.
// TODO: should this just be hardcoded to 2? Nothing else makes sense really - current era and next era are required.
#[pallet::constant]
type MaxLockedChunks: Get<u32>;

@@ -108,6 +110,10 @@ pub mod pallet {
/// Amount of blocks that need to pass before unlocking chunks can be claimed by the owner.
#[pallet::constant]
type UnlockingPeriod: Get<BlockNumberFor<Self>>;

/// Maximum number of staking chunks that can exist per account at a time.
#[pallet::constant]
type MaxStakingChunks: Get<u32>;
}

#[pallet::event]
@@ -137,13 +143,13 @@ pub mod pallet {
/// Account has locked some amount into dApp staking.
Locked {
account: T::AccountId,
amount: BalanceOf<T>,
amount: Balance,
},
// TODO: do we also add unlocking block info to the event?
/// Account has started the unlocking process for some amount.
Unlocking {
account: T::AccountId,
amount: BalanceOf<T>,
amount: Balance,
},
}

@@ -395,10 +401,7 @@ pub mod pallet {
/// caller should claim pending rewards, before retrying to lock additional funds.
#[pallet::call_index(5)]
#[pallet::weight(Weight::zero())]
pub fn lock(
origin: OriginFor<T>,
#[pallet::compact] amount: BalanceOf<T>,
) -> DispatchResult {
pub fn lock(origin: OriginFor<T>, #[pallet::compact] amount: Balance) -> DispatchResult {
Self::ensure_pallet_enabled()?;
let account = ensure_signed(origin)?;

@@ -441,10 +444,7 @@ pub mod pallet {
/// If the remaining locked amount would take the account below the minimum locked amount, everything is unlocked.
#[pallet::call_index(6)]
#[pallet::weight(Weight::zero())]
pub fn unlock(
origin: OriginFor<T>,
#[pallet::compact] amount: BalanceOf<T>,
) -> DispatchResult {
pub fn unlock(origin: OriginFor<T>, #[pallet::compact] amount: Balance) -> DispatchResult {
Self::ensure_pallet_enabled()?;
let account = ensure_signed(origin)?;

1 change: 1 addition & 0 deletions pallets/dapp-staking-v3/src/test/mock.rs
Original file line number Diff line number Diff line change
@@ -112,6 +112,7 @@ impl pallet_dapp_staking::Config for Test {
type MaxNumberOfContracts = ConstU16<10>;
type MaxLockedChunks = ConstU32<5>;
type MaxUnlockingChunks = ConstU32<5>;
type MaxStakingChunks = ConstU32<8>;
type MinimumLockedAmount = ConstU128<MINIMUM_LOCK_AMOUNT>;
type UnlockingPeriod = ConstU64<20>;
}
46 changes: 29 additions & 17 deletions pallets/dapp-staking-v3/src/test/tests.rs
Original file line number Diff line number Diff line change
@@ -20,10 +20,10 @@ use crate::test::mock::*;
use crate::test::testing_utils::*;
use crate::{
pallet as pallet_dapp_staking, ActiveProtocolState, DAppId, Error, IntegratedDApps, Ledger,
NextDAppId, StakeInfo,
NextDAppId, SparseBoundedAmountEraVec, StakeChunk,
};

use frame_support::{assert_noop, assert_ok, error::BadOrigin, traits::Get};
use frame_support::{assert_noop, assert_ok, error::BadOrigin, traits::Get, BoundedVec};
use sp_runtime::traits::Zero;

#[test]
@@ -422,13 +422,17 @@ fn unlock_with_amount_higher_than_avaiable_is_ok() {
advance_to_era(ActiveProtocolState::<Test>::get().era + 1);
assert_lock(account, lock_amount);

// Hacky, maybe improve later when staking is implemented?
// TODO: Hacky, maybe improve later when staking is implemented?
let stake_amount = 91;
Ledger::<Test>::mutate(&account, |ledger| {
ledger.staked = StakeInfo {
amount: stake_amount,
period: ActiveProtocolState::<Test>::get().period,
}
ledger.staked = SparseBoundedAmountEraVec(
BoundedVec::try_from(vec![StakeChunk {
amount: stake_amount,
era: ActiveProtocolState::<Test>::get().era,
}])
.expect("Only one chunk so creation should succeed."),
);
ledger.staked_period = Some(ActiveProtocolState::<Test>::get().period);
});

// Try to unlock more than is available, due to active staked amount
@@ -483,12 +487,16 @@ fn unlock_everything_with_active_stake_fails() {
let minimum_locked_amount: Balance =
<Test as pallet_dapp_staking::Config>::MinimumLockedAmount::get();
let stake_amount = minimum_locked_amount - 1;
// Hacky, maybe improve later when staking is implemented?
// TODO: Hacky, maybe improve later when staking is implemented?
Ledger::<Test>::mutate(&account, |ledger| {
ledger.staked = StakeInfo {
amount: stake_amount,
period: ActiveProtocolState::<Test>::get().period,
}
ledger.staked = SparseBoundedAmountEraVec(
BoundedVec::try_from(vec![StakeChunk {
amount: stake_amount,
era: ActiveProtocolState::<Test>::get().era,
}])
.expect("Only one chunk so creation should succeed."),
);
ledger.staked_period = Some(ActiveProtocolState::<Test>::get().period);
});

// Try to unlock more than is available, due to active staked amount
@@ -514,12 +522,16 @@ fn unlock_with_zero_amount_fails() {
);

// Stake everything, so available unlock amount is always zero
// Hacky, maybe improve later when staking is implemented?
// TODO: Hacky, maybe improve later when staking is implemented?
Ledger::<Test>::mutate(&account, |ledger| {
ledger.staked = StakeInfo {
amount: lock_amount,
period: ActiveProtocolState::<Test>::get().period,
}
ledger.staked = SparseBoundedAmountEraVec(
BoundedVec::try_from(vec![StakeChunk {
amount: lock_amount,
era: ActiveProtocolState::<Test>::get().era,
}])
.expect("Only one chunk so creation should succeed."),
);
ledger.staked_period = Some(ActiveProtocolState::<Test>::get().period);
});

// Try to unlock anything, expect zero amount error
66 changes: 49 additions & 17 deletions pallets/dapp-staking-v3/src/test/tests_types.rs
Original file line number Diff line number Diff line change
@@ -47,7 +47,9 @@ fn protocol_state_default() {
fn account_ledger_default() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

assert!(acc_ledger.is_empty());
assert!(acc_ledger.active_locked_amount().is_zero());
@@ -59,7 +61,9 @@ fn account_ledger_default() {
fn account_ledger_add_lock_amount_works() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// First step, sanity checks
let first_era = 1;
@@ -115,7 +119,9 @@ fn account_ledger_add_lock_amount_works() {
fn account_ledger_subtract_lock_amount_basic_usage_works() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Sanity check scenario
// Cannot reduce if there is nothing locked, should be a noop
@@ -199,7 +205,9 @@ fn account_ledger_subtract_lock_amount_basic_usage_works() {
fn account_ledger_subtract_lock_amount_overflow_fails() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

let first_lock_amount = 17 * 19;
let era = 1;
@@ -245,7 +253,9 @@ fn account_ledger_subtract_lock_amount_overflow_fails() {
fn account_ledger_subtract_lock_amount_advanced_example_works() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Prepare an example where we have two non-consecutive entries, and we unlock in the era right before the second entry.
// This covers a scenario where user has called `lock` in the current era,
@@ -291,7 +301,9 @@ fn account_ledger_subtract_lock_amount_advanced_example_works() {
fn account_ledger_subtract_lock_amount_with_only_one_locked_chunk() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Scenario: user locks for era 2 while era 1 is active, immediately followed by unlock call.
// Locked amount should be updated for the next era, but active locked amount should be unchanged (zero).
@@ -318,7 +330,9 @@ fn account_ledger_subtract_lock_amount_with_only_one_locked_chunk() {
fn account_ledger_subtract_lock_amount_correct_zero_cleanup() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Ensure that zero entries are cleaned up correctly when required.
// There are a couple of distinct scenarios:
@@ -364,7 +378,9 @@ fn account_ledger_subtract_lock_amount_correct_zero_cleanup() {
fn account_ledger_subtract_lock_amount_zero_entry_between_two_non_zero() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

let (first_lock_amount, second_lock_amount, third_lock_amount) = (17, 23, 29);
let (first_lock_era, second_lock_era, third_lock_era) = (1, 3, 7);
@@ -423,7 +439,9 @@ fn account_ledger_subtract_lock_amount_zero_entry_between_two_non_zero() {
fn account_ledger_subtract_lock_amount_consecutive_zeroes_merged() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Prepare scenario with 3 locked chunks, where the middle one is zero
let lock_amount = 61;
@@ -446,7 +464,9 @@ fn account_ledger_subtract_lock_amount_consecutive_zeroes_merged() {
fn account_ledger_add_unlocking_chunk_works() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Sanity check scenario
// Cannot reduce if there is nothing locked, should be a noop
@@ -509,7 +529,9 @@ fn account_ledger_add_unlocking_chunk_works() {
fn active_stake_works() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Sanity check
assert!(acc_ledger.active_stake(0).is_zero());
@@ -518,7 +540,11 @@ fn active_stake_works() {
// Period matches
let amount = 29;
let period = 5;
acc_ledger.staked = StakeInfo { amount, period };
acc_ledger.staked = SparseBoundedAmountEraVec(
BoundedVec::try_from(vec![StakeChunk { amount, era: 1 }])
.expect("Only one chunk so creation should succeed."),
);
acc_ledger.staked_period = Some(period);
assert_eq!(acc_ledger.active_stake(period), amount);

// Period doesn't match
@@ -530,7 +556,9 @@ fn active_stake_works() {
fn unlockable_amount_works() {
get_u32_type!(LockedDummy, 5);
get_u32_type!(UnlockingDummy, 5);
let mut acc_ledger = AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy>::default();
get_u32_type!(StakingDummy, 8);
let mut acc_ledger =
AccountLedger::<BlockNumber, LockedDummy, UnlockingDummy, StakingDummy>::default();

// Sanity check scenario
assert!(acc_ledger.unlockable_amount(0).is_zero());
@@ -544,10 +572,14 @@ fn unlockable_amount_works() {
// Some amount is staked, period matches
let stake_period = 5;
let stake_amount = 17;
acc_ledger.staked = StakeInfo {
amount: stake_amount,
period: stake_period,
};
acc_ledger.staked = SparseBoundedAmountEraVec(
BoundedVec::try_from(vec![StakeChunk {
amount: stake_amount,
era: lock_era,
}])
.expect("Only one chunk so creation should succeed."),
);
acc_ledger.staked_period = Some(stake_period);
assert_eq!(
acc_ledger.unlockable_amount(stake_period),
lock_amount - stake_amount
Loading