Skip to content

Commit

Permalink
Merge pull request #9 from 0xPolygon/fix/forceUnstake
Browse files Browse the repository at this point in the history
Unstake only active validators
  • Loading branch information
simonDos authored Aug 22, 2024
2 parents 39abd88 + 97cb2ef commit c795e76
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
branches: [main, dev, feat/*]

jobs:
build:
Expand Down
7 changes: 4 additions & 3 deletions contracts/staking/stakeManager/StakeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -419,14 +419,14 @@ contract StakeManager is
}

function unstake(uint256 validatorId) external onlyStaker(validatorId) {
_unstake(validatorId, false);
_unstakeValidator(validatorId, false);
}

function unstakePOL(uint256 validatorId) external onlyStaker(validatorId) {
_unstake(validatorId, true);
_unstakeValidator(validatorId, true);
}

function _unstake(uint256 validatorId, bool pol) internal {
function _unstakeValidator(uint256 validatorId, bool pol) internal {
require(validatorAuction[validatorId].amount == 0);

Status status = validators[validatorId].status;
Expand Down Expand Up @@ -1112,6 +1112,7 @@ contract StakeManager is
}

function _unstake(uint256 validatorId, uint256 exitEpoch, bool pol) internal {
require(validators[validatorId].deactivationEpoch == 0);
// TODO: if validators unstake and slashed to 0, he will be forced to unstake again
// must think how to handle it correctly
_updateRewards(validatorId);
Expand Down
52 changes: 52 additions & 0 deletions test/units/staking/stakeManager/StakeManager.Staking.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,58 @@ describe('unstake', function () {
})
})

describe('forceUnstake cannot only unstake active validators', function () {
const AliceWallet = wallets[1]
const others = [wallets[2], wallets[3]]

before(async function() {
await freshDeploy.call(this, true)
})
before(doStake(AliceWallet))
before(doStake(others[0]))
before(doStake(others[1]))
before('Alice is forceUnstaked', async function () {
this.validatorId = await this.stakeManager.getValidatorId(AliceWallet.getAddressString())
await this.governance.update(
this.stakeManager.address,
this.stakeManager.interface.encodeFunctionData('forceUnstake', [this.validatorId])
)
this.lastSyncedEpoch = await this.stakeManager.currentEpoch()
await checkPoint(others, this.rootChainOwner, this.stakeManager)
})
it('Alice is unstaked', async function () {
const { deactivationEpoch } = await this.stakeManager.validators(this.validatorId)
assertBigNumberEquality(deactivationEpoch, this.lastSyncedEpoch)
})
it('Alice cannot be forceUnstaked again', async function () {
await expectRevert(this.governance.update(
this.stakeManager.address,
this.stakeManager.interface.encodeFunctionData('forceUnstake', [this.validatorId])
), 'Update failed')
})
it('Alice cannot be forceUnstaked after claiming', async function () {
const endEpoch = this.lastSyncedEpoch.add(await this.stakeManager.WITHDRAWAL_DELAY())
// mock for i ... range(delay) checkPoint()
await this.governance.update(
this.stakeManager.address,
this.stakeManager.interface.encodeFunctionData('setCurrentEpoch', [endEpoch + 1])
)

await this.stakeManager
.connect(this.stakeManager.provider.getSigner(AliceWallet.getAddressString()))
.unstakeClaim(this.validatorId)
await checkPoint(others, this.rootChainOwner, this.stakeManager)

await expectRevert(
this.governance.update(
this.stakeManager.address,
this.stakeManager.interface.encodeFunctionData('forceUnstake', [this.validatorId])
),
'Update failed'
)
})
})

describe('when user unstakes right after stake', async function () {
const user = wallets[2].getChecksumAddressString()
const amounts = walletAmounts[wallets[2].getAddressString()]
Expand Down

0 comments on commit c795e76

Please sign in to comment.