-
Notifications
You must be signed in to change notification settings - Fork 45
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
[FIP10] Add new delegation lockin #98
base: master
Are you sure you want to change the base?
[FIP10] Add new delegation lockin #98
Conversation
current lockin = 5days
contracts/ConsensusUtils.sol
Outdated
@@ -94,6 +95,9 @@ contract ConsensusUtils is EternalStorage, ValidatorSet { | |||
|
|||
_delegatedAmountAdd(_staker, _validator, _amount); | |||
_stakeAmountAdd(_validator, _amount); | |||
if (_staker != _validator) { | |||
_setUnboundingPeriod(_staker); |
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.
what about the case where staker = validator
It means that the validator stakes himself, right? I think it also need an unbounding period.
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.
Correct, I will change this actually should really lock everyone in. Sorry for the massive delay in this comment only just seen it!
@leonprou @Andrew-Pohl |
Hey, the logic of this has changed slightly (will push the changes today/tomorrow). The unbounding block has moved from stake to withdraw. Now when a user request a withdraw it will move their tokens out of the staking pool (so they no longer receive interest) and then start the block countdown. Once the countdown is over they can then claim their fuse from the "unstaking pool". This frees up the problems you mentioned. |
@Andrew-Pohl |
On first withdraw request, tokens are taken off the pool (they no longer receive interest) the subsequent withdraw request will check that unbounding has passed before release the tokens.
contracts/ConsensusUtils.sol
Outdated
function _withdraw(address _staker, uint256 _amount, address _validator) internal { | ||
require(_validator != address(0)); | ||
require(_amount > 0); | ||
require(_amount <= stakeAmount(_validator)); | ||
require(_amount <= delegatedAmount(_staker, _validator)); | ||
if (unBoundingAmount(_staker) != 0) |
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.
@Andrew-Pohl @leonprou
this logic prevents multiple withdraw requests, and still breaks wrapping fuse staking for DEFI.
- user A requests to withdraw from the defi contract
- defi contract calls withdraw -> now unbounding amount of contract > 0
- user B requests to withdraw BEFORE unbounding period is over
- defi contract calls withdraw, this IF resolves to true and _unboundStake is called and reverted because unbounding block has not reached yet
User B needs to wait for unbounding period before he can request a withdraw
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 still suggest to implement an average when staking. so your staking needs to be locked for an average of X days before you can withdraw.
If I staked for a month,a year why do I deserve this unbounding period?
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.
w
contracts/ConsensusUtils.sol
Outdated
@@ -21,6 +21,7 @@ contract ConsensusUtils is EternalStorage, ValidatorSet { | |||
uint256 public constant SNAPSHOTS_PER_CYCLE = 0; // snapshot each 288 minutes [34560/10/60*5] | |||
uint256 public constant DEFAULT_VALIDATOR_FEE = 15e16; // 15% | |||
uint256 public constant UNBOUNDING_PERIOD = CYCLE_DURATION_BLOCKS; | |||
uint256 public constant MAX_WITHDRAW_QUEUE_LENGTH = 500; |
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.
we have at the moment 40K daily users. if they start using this, it could easily reach that.
i dont think this should be limited. how can this be used to attack anything?
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 guess you would need to add a way to limit the amount of records we iterate over when withdrawing, cause it might get stuck forever with out of gas
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.
perhaps a withdraw helper that stops if gasLeft<100K and also returns a bool indicating if reached gaslimit
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.
Yes you might be right here, maybe put the limit on the amount claimed in one transaction rather than the queue itself...
We will need some limit on the queue because it could be an attack vector by withdrawing 1X10-18 repeatedly causing a massive array! (but this could be bigger than 500)
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.
- well most of the times you would only attack yourself as the array is per address.
- in our case, the contract itself can limit user withdrawals to min X% of their staked tokens, so a user wouldnt be able to DDOS other withdrawals.
- as mentioned, in any case we require a solution that would let you withdraw in chunks or until a specific amount of gas is left, in case of a large array
• Increased withdraw queue size to 20k • Add limit on unbounding chunk size to reduce gas over spending • Add view function to track available unbounding amount
@@ -571,4 +576,22 @@ contract ConsensusUtils is EternalStorage, ValidatorSet { | |||
function _setValidatorFee(address _validator, uint256 _amount) internal { | |||
uintStorage[keccak256(abi.encodePacked("validatorFee", _validator))] = _amount; | |||
} | |||
|
|||
function unboundingAmount() public view returns(uint256) { |
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.
if queue is long this will fail if used inside a smart contract.
we can keep this, but add another view helper canWithdraw() which simply returns true if first in queue can be withdrawn.
so some contract can easily call unbound() then check if still canWithdraw() and call unbound again
I still dont think it is necessary to limit the queue length, as it is per account, that account owner can only attack itself.
for example in our use case, we can simply limit users to withdraw at least 10% of their funds and at least 1 fuse, so they cant create a huge queue withdrawing small amounts
//loop through queue and check if we have anything to withdraw | ||
for (uint256 i = 0; i < oldArrayLength; i ++) | ||
{ | ||
if (i > MAX_WITHDRAW_CHUNK_SIZE) |
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.
why test this inside the loop?
|
||
//shift queue and pop back | ||
for (i = 0; i < oldArrayLength - index; i++) { | ||
uintArrayStorage[keccak256(abi.encodePacked("unboundingQueueBlock", _staker))][i] = uintArrayStorage[keccak256(abi.encodePacked("unboundingQueueBlock", _staker))][i + index]; |
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.
have you considered the gas costs for all these keccak256 calls? is there a less gas-intensive way to handle this?
|
||
//check if we have anything | ||
require(toReturn != 0); | ||
index = i - 1; |
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.
isn't i
scoped to the for loop?
current lockin = 5days