-
Notifications
You must be signed in to change notification settings - Fork 38
[Pending] Rebond unlocking chunks #97
base: polkadot-v0.9.29
Are you sure you want to change the base?
Changes from 15 commits
45b53b3
04a6b1f
de3ff1e
f36013d
86a2c09
c391f65
f27a1ad
292cfee
719024d
93286fc
7059ffa
852fb39
0470a5e
7a8f3b5
5c36af9
89cf22b
a3d98b1
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 |
---|---|---|
|
@@ -13,47 +13,47 @@ pub enum DSError { | |
Disabled = 1, | ||
/// No change in maintenance mode | ||
NoMaintenanceModeChange = 2, | ||
/// Upgrade is too heavy, reduce the weight parameter. | ||
/// Upgrade is too heavy, reduce the weight parameter | ||
UpgradeTooHeavy = 3, | ||
/// Can not stake with zero value. | ||
/// Can not stake with zero value | ||
StakingWithNoValue = 4, | ||
/// Can not stake with value less than minimum staking value | ||
InsufficientValue = 5, | ||
/// Number of stakers per contract exceeded. | ||
/// Number of stakers per contract exceeded | ||
MaxNumberOfStakersExceeded = 6, | ||
/// Targets must be operated contracts | ||
NotOperatedContract = 7, | ||
/// Contract isn't staked. | ||
/// Contract isn't staked | ||
NotStakedContract = 8, | ||
/// Contract isn't unregistered. | ||
NotUnregisteredContract = 9, | ||
/// Unclaimed rewards should be claimed before withdrawing stake. | ||
/// Unclaimed rewards should be claimed before withdrawing stake | ||
UnclaimedRewardsRemaining = 10, | ||
/// Unstaking a contract with zero value | ||
UnstakingWithNoValue = 11, | ||
/// There are no previously unbonded funds that can be unstaked and withdrawn. | ||
/// There are no previously unbonded funds that can be unstaked and withdrawn | ||
NothingToWithdraw = 12, | ||
/// The contract is already registered by other account | ||
AlreadyRegisteredContract = 13, | ||
/// User attempts to register with address which is not contract | ||
ContractIsNotValid = 14, | ||
/// This account was already used to register contract | ||
AlreadyUsedDeveloperAccount = 15, | ||
/// Smart contract not owned by the account id. | ||
/// Smart contract not owned by the account id | ||
NotOwnedContract = 16, | ||
/// Report issue on github if this is ever emitted | ||
UnknownEraReward = 17, | ||
/// Report issue on github if this is ever emitted | ||
UnexpectedStakeInfoEra = 18, | ||
/// Contract has too many unlocking chunks. Withdraw the existing chunks if possible | ||
/// or wait for current chunks to complete unlocking process to withdraw them. | ||
/// or wait for current chunks to complete unlocking process to withdraw them | ||
TooManyUnlockingChunks = 19, | ||
/// Contract already claimed in this era and reward is distributed | ||
AlreadyClaimedInThisEra = 20, | ||
/// Era parameter is out of bounds | ||
EraOutOfBounds = 21, | ||
/// Too many active `EraStake` values for (staker, contract) pairing. | ||
/// Claim existing rewards to fix this problem. | ||
/// Too many active `EraStake` values for (staker, contract) pairing | ||
/// Claim existing rewards to fix this problem | ||
TooManyEraStakeValues = 22, | ||
/// To register a contract, pre-approval is needed for this address | ||
RequiredContractPreApproval = 23, | ||
|
@@ -65,6 +65,8 @@ pub enum DSError { | |
NominationTransferToSameContract = 26, | ||
/// Unexpected reward destination value | ||
RewardDestinationValueOutOfBounds = 27, | ||
/// There are no previously unbonded funds that can be reboneded and staked | ||
NothingToRebond = 28, | ||
/// Unknown error | ||
UnknownError = 99, | ||
} | ||
|
@@ -106,6 +108,7 @@ impl TryFrom<DispatchError> for DSError { | |
Some("NominationTransferToSameContract") => { | ||
Ok(DSError::NominationTransferToSameContract) | ||
} | ||
Some("NothingToRebond") => Ok(DSError::NothingToRebond), | ||
_ => Ok(DSError::UnknownError), | ||
}; | ||
} | ||
|
@@ -120,27 +123,29 @@ pub enum Contract<Account> { | |
Wasm(Account), | ||
} | ||
|
||
pub type ContractBytes = [u8; 32]; | ||
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. Since this type is in public it would be a good idea to add some explanatory comment. What is this type, why it 32 bytes long, what is the purpose of that type, etc. |
||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] | ||
pub struct DappsStakingValueInput<Balance> { | ||
pub contract: [u8; 32], | ||
pub contract: ContractBytes, | ||
pub value: Balance, | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] | ||
pub struct DappsStakingAccountInput { | ||
pub contract: [u8; 32], | ||
pub contract: ContractBytes, | ||
pub staker: [u8; 32], | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] | ||
pub struct DappsStakingEraInput { | ||
pub contract: [u8; 32], | ||
pub contract: ContractBytes, | ||
pub era: u32, | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] | ||
pub struct DappsStakingNominationInput<Balance> { | ||
pub origin_contract: [u8; 32], | ||
pub target_contract: [u8; 32], | ||
pub origin_contract: ContractBytes, | ||
pub target_contract: ContractBytes, | ||
pub value: Balance, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -218,6 +218,8 @@ pub mod pallet { | |
BalanceOf<T>, | ||
T::SmartContract, | ||
), | ||
/// Account has rebonded unlocking chunks and staked funds on a smart contract. | ||
RebondAndStake(T::AccountId, T::SmartContract, BalanceOf<T>), | ||
} | ||
|
||
#[pallet::error] | ||
|
@@ -270,6 +272,8 @@ pub mod pallet { | |
NotActiveStaker, | ||
/// Transfering nomination to the same contract | ||
NominationTransferToSameContract, | ||
/// There are no previously unbonded funds that can be rebonded and re-staked. | ||
NothingToRebond, | ||
} | ||
|
||
#[pallet::hooks] | ||
|
@@ -570,6 +574,64 @@ pub mod pallet { | |
Ok(().into()) | ||
} | ||
|
||
/// Lock up and stake unbonded chunks of origin account. | ||
/// | ||
/// All unbonding chunks will be used and staked to the specified contract. | ||
/// | ||
/// The dispatch origin for this call must be _Signed_ by the staker's account. | ||
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. Please also check top of lib.rs file and update comments there if needed. |
||
#[pallet::weight(T::WeightInfo::rebond_and_stake())] | ||
pub fn rebond_and_stake( | ||
origin: OriginFor<T>, | ||
contract_id: T::SmartContract, | ||
) -> DispatchResultWithPostInfo { | ||
Self::ensure_pallet_enabled()?; | ||
let staker = ensure_signed(origin)?; | ||
|
||
// Check that contract is ready for staking. | ||
ensure!( | ||
Self::is_active(&contract_id), | ||
Error::<T>::NotOperatedContract | ||
); | ||
|
||
// Get the staking ledger or create an entry if it doesn't exist. | ||
let mut ledger = Self::ledger(&staker); | ||
let value_to_stake = ledger.unbonding_info.sum(); | ||
ensure!(value_to_stake > Zero::zero(), Error::<T>::NothingToRebond); | ||
|
||
let current_era = Self::current_era(); | ||
let mut staking_info = | ||
Self::contract_stake_info(&contract_id, current_era).unwrap_or_default(); | ||
let mut staker_info = Self::staker_info(&staker, &contract_id); | ||
|
||
Self::stake_on_contract( | ||
&mut staker_info, | ||
&mut staking_info, | ||
value_to_stake, | ||
current_era, | ||
)?; | ||
|
||
ledger.unbonding_info.unlocking_chunks = Vec::<UnlockingChunk<BalanceOf<T>>>::default(); | ||
|
||
GeneralEraInfo::<T>::mutate(¤t_era, |value| { | ||
if let Some(x) = value { | ||
x.staked = x.staked.saturating_add(value_to_stake); | ||
x.locked = x.locked.saturating_add(value_to_stake); | ||
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. This is incorrect - TVL doesn't increase after this since unbonding chunks are still considered to be locked. UT should be updated to catch this. |
||
} | ||
}); | ||
|
||
Self::update_ledger(&staker, ledger); | ||
Self::update_staker_info(&staker, &contract_id, staker_info); | ||
ContractEraStake::<T>::insert(&contract_id, current_era, staking_info); | ||
|
||
Self::deposit_event(Event::<T>::RebondAndStake( | ||
staker, | ||
contract_id, | ||
value_to_stake, | ||
)); | ||
|
||
Ok(().into()) | ||
} | ||
|
||
/// Withdraw all funds that have completed the unbonding process. | ||
/// | ||
/// If there are unbonding chunks which will be fully unbonded in future eras, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -353,6 +353,67 @@ pub(crate) fn assert_unbond_and_unstake( | |
assert_eq!(init_state.era_info.locked, final_state.era_info.locked); | ||
} | ||
|
||
pub(crate) fn assert_rebond_and_stake( | ||
staker: AccountId, | ||
contract_id: &MockSmartContract<AccountId>, | ||
) { | ||
// Get latest staking info | ||
let current_era = DappsStaking::current_era(); | ||
let init_state = MemorySnapshot::all(current_era, &contract_id, staker); | ||
|
||
// Define expected stake amount | ||
let expected_stake_amount = init_state.ledger.unbonding_info.sum(); | ||
|
||
// Ensure op is successful and event is emitted | ||
assert_ok!(DappsStaking::rebond_and_stake( | ||
Origin::signed(staker), | ||
contract_id.clone(), | ||
)); | ||
System::assert_last_event(mock::Event::DappsStaking(Event::RebondAndStake( | ||
staker, | ||
contract_id.clone(), | ||
expected_stake_amount, | ||
))); | ||
|
||
// Fetch the latest unbonding info so we can compare it to initial unbonding info | ||
let final_state = MemorySnapshot::all(current_era, &contract_id, staker); | ||
assert!(final_state.ledger.unbonding_info.is_empty()); | ||
|
||
// locked amount before and after the operation should be the same. | ||
// unlocking chunks are still locked unless it is withdrawn. | ||
assert_eq!(final_state.ledger.locked, init_state.ledger.locked); | ||
|
||
// In case staker hasn't been staking this contract until now | ||
if init_state.staker_info.latest_staked_value() == 0 { | ||
assert!(GeneralStakerInfo::<TestRuntime>::contains_key( | ||
&staker, | ||
contract_id | ||
)); | ||
assert_eq!( | ||
final_state.contract_info.number_of_stakers, | ||
init_state.contract_info.number_of_stakers + 1 | ||
); | ||
} | ||
|
||
// Verify the remaining states | ||
assert_eq!( | ||
final_state.era_info.staked, | ||
init_state.era_info.staked + expected_stake_amount | ||
); | ||
assert_eq!( | ||
final_state.era_info.locked, | ||
init_state.era_info.locked + expected_stake_amount | ||
); | ||
Comment on lines
+403
to
+406
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. This is incorrect, see my comment above. |
||
assert_eq!( | ||
final_state.contract_info.total, | ||
init_state.contract_info.total + expected_stake_amount | ||
); | ||
assert_eq!( | ||
final_state.staker_info.latest_staked_value(), | ||
init_state.staker_info.latest_staked_value() + expected_stake_amount | ||
); | ||
} | ||
|
||
/// Used to perform start_unbonding with success and storage assertions. | ||
pub(crate) fn assert_withdraw_unbonded(staker: AccountId) { | ||
let current_era = DappsStaking::current_era(); | ||
|
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.
Was kinda confused since
rebond_and_stake
is called twice in a row. I do understand that these are two different calls, but still this is a bit confusing. Maybe we should add couple of comments to reduce overall WTF/minute.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.
This can be said for other functions too. it's a more universal topic.
I thought it's very clear they are different and what they do because they are different methods, one is
WeightInfo
's and another isPallet
's.