Skip to content
This repository was archived by the owner on Mar 1, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions contracts/root/predicates/ERC20Predicate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import {SafeMath} from "openzeppelin-solidity/contracts/math/SafeMath.sol";

import {IErcPredicate} from "./IPredicate.sol";
import {Registry} from "../../common/Registry.sol";
import {
WithdrawManagerHeader
} from "../withdrawManager/WithdrawManagerStorage.sol";
import {WithdrawManagerHeader} from "../withdrawManager/WithdrawManagerStorage.sol";

contract ERC20Predicate is IErcPredicate {
using RLPReader for bytes;
Expand Down
4 changes: 1 addition & 3 deletions contracts/root/predicates/IPredicate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {RLPEncode} from "../../common/lib/RLPEncode.sol";

import {IWithdrawManager} from "../withdrawManager/IWithdrawManager.sol";
import {IDepositManager} from "../depositManager/IDepositManager.sol";
import {
ExitsDataStructure
} from "../withdrawManager/WithdrawManagerStorage.sol";
import {ExitsDataStructure} from "../withdrawManager/WithdrawManagerStorage.sol";
import {ChainIdMixin} from "../../common/mixin/ChainIdMixin.sol";

interface IPredicate {
Expand Down
7 changes: 2 additions & 5 deletions contracts/staking/slashing/SlashingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,10 @@ contract SlashingManager is ISlashingManager, Ownable {
break;
} else if (stakeManager.isValidator(validatorId) && signer > lastAdd) {
lastAdd = signer;
uint256 amount;
uint256 delegatedAmount;
(amount,,,,,,,,,,,delegatedAmount,) = stakeManager.validators(validatorId);
(, uint256 totalAmount) = stakeManager.signerState(signer);

// add delegation power
amount = amount.add(delegatedAmount);
_stakePower = _stakePower.add(amount);
_stakePower = _stakePower.add(totalAmount);
}
}
return (_stakePower >= stakeManager.currentValidatorSetTotalStake().mul(2).div(3).add(1));
Expand Down
147 changes: 85 additions & 62 deletions contracts/staking/stakeManager/StakeManager.sol

Large diffs are not rendered by default.

34 changes: 15 additions & 19 deletions contracts/staking/stakeManager/StakeManagerExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ contract StakeManagerExtension is StakeManagerStorage, Initializable, StakeManag
bytes calldata _signerPubkey
) external {
uint256 currentValidatorAmount = validators[validatorId].amount;
address signer = validators[validatorId].signer;

// re-use variable, dirty. It's deactivationEpoch
(, uint256 senderValidatorId) = _readStatus(signer);
require(
validators[validatorId].deactivationEpoch == 0 && currentValidatorAmount != 0,
senderValidatorId == 0 && currentValidatorAmount != 0,
"Invalid validator for an auction"
);
uint256 senderValidatorId = signerToValidator[msg.sender];

senderValidatorId = signerToValidator[msg.sender];
// make sure that signer wasn't used already
require(
NFTContract.balanceOf(msg.sender) == 0 && // existing validators can't bid
Expand All @@ -53,8 +57,7 @@ contract StakeManagerExtension is StakeManagerStorage, Initializable, StakeManag
"Invalid auction period"
);

uint256 perceivedStake = currentValidatorAmount;
perceivedStake = perceivedStake.add(validators[validatorId].delegatedAmount);
uint256 perceivedStake = signerState[signer].totalAmount;

Auction storage auction = validatorAuction[validatorId];
uint256 currentAuctionAmount = auction.amount;
Expand Down Expand Up @@ -98,14 +101,14 @@ contract StakeManagerExtension is StakeManagerStorage, Initializable, StakeManag
);
require(auction.user != address(0x0), "Invalid auction");

address signer = validators[validatorId].signer;
uint256 validatorAmount = validators[validatorId].amount;
uint256 perceivedStake = validatorAmount;
uint256 perceivedStake = signerState[signer].totalAmount;
uint256 auctionAmount = auction.amount;

perceivedStake = perceivedStake.add(validators[validatorId].delegatedAmount);

(, uint256 deactivationEpoch) = _readStatus(signer);
// validator is last auctioner
if (perceivedStake >= auctionAmount && validators[validatorId].deactivationEpoch == 0) {
if (perceivedStake >= auctionAmount && deactivationEpoch == 0) {
require(token.transfer(auctionUser, auctionAmount), "Bid return failed");
//cleanup auction data
auction.startEpoch = _currentEpoch;
Expand All @@ -127,17 +130,10 @@ contract StakeManagerExtension is StakeManagerStorage, Initializable, StakeManag

function migrateValidatorsData(uint256 validatorIdFrom, uint256 validatorIdTo) external {
for (uint256 i = validatorIdFrom; i < validatorIdTo; ++i) {
ValidatorShare contractAddress = ValidatorShare(validators[i].contractAddress);
if (contractAddress != ValidatorShare(0)) {
// move validator rewards out from ValidatorShare contract
validators[i].reward = contractAddress.validatorRewards_deprecated().add(INITIALIZED_AMOUNT);
validators[i].delegatedAmount = contractAddress.activeAmount();
validators[i].commissionRate = contractAddress.commissionRate_deprecated();
} else {
validators[i].reward = validators[i].reward.add(INITIALIZED_AMOUNT);
}

validators[i].delegatorsReward = INITIALIZED_AMOUNT;
address signer = validators[i].signer;

signerState[signer].totalAmount = validators[i].amount.add(validators[i].delegatedAmount_deprecated).sub(INITIALIZED_AMOUNT);
_writeStatus(signer, Status(uint256(validators[i].status_deprecated)), validators[i].deactivationEpoch_deprecated);
}
}

Expand Down
8 changes: 4 additions & 4 deletions contracts/staking/stakeManager/StakeManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {StakingNFT} from "./StakingNFT.sol";
import {ValidatorShareFactory} from "../validatorShare/ValidatorShareFactory.sol";

contract StakeManagerStorage is GovernanceLockable, RootChainable {
enum Status {Inactive, Active, Locked, Unstaked}
enum Status_deprecated {Inactive, Active, Locked, Unstaked}

struct Auction {
uint256 amount;
Expand All @@ -34,15 +34,15 @@ contract StakeManagerStorage is GovernanceLockable, RootChainable {
uint256 amount;
uint256 reward;
uint256 activationEpoch;
uint256 deactivationEpoch;
uint256 deactivationEpoch_deprecated;
uint256 jailTime;
address signer;
address contractAddress;
Status status;
Status_deprecated status_deprecated;
uint256 commissionRate;
uint256 lastCommissionUpdate;
uint256 delegatorsReward;
uint256 delegatedAmount;
uint256 delegatedAmount_deprecated;
uint256 initialRewardPerStake;
}

Expand Down
18 changes: 18 additions & 0 deletions contracts/staking/stakeManager/StakeManagerStorageExtension.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
pragma solidity 0.5.17;

contract StakeManagerStorageExtension {
enum Status {Inactive, Active, Locked, Unstaked}

struct Signer {
uint256 status;
uint256 totalAmount;
}

address public eventsHub;
uint256 public rewardPerStake;
address public extensionCode;
Expand All @@ -14,4 +21,15 @@ contract StakeManagerStorageExtension {
uint256 public maxRewardedCheckpoints;
// increase / decrease value for faster or slower checkpoints, 0 - 100%
uint256 public checkpointRewardDelta;

mapping(address => Signer) public signerState;

function _readStatus(address signer) internal view returns(Status status, uint256 deactivationEpoch) {
uint256 combinedStatus = signerState[signer].status;
return (Status(combinedStatus >> 240), uint256(uint240(combinedStatus)));
}

function _writeStatus(address signer, Status status, uint256 deactivationEpoch) internal {
signerState[signer].status = (uint256(status) << 240) | deactivationEpoch;
}
}
34 changes: 20 additions & 14 deletions contracts/staking/validatorShare/ValidatorShare.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I
*/

function buyVoucher(uint256 _amount, uint256 _minSharesToMint) public returns(uint256 amountToDeposit) {
_withdrawAndTransferReward(msg.sender);
_withdrawAndTransferReward(msg.sender, msg.sender);

amountToDeposit = _buyShares(_amount, _minSharesToMint, msg.sender);
require(stakeManager.delegationDeposit(validatorId, amountToDeposit, msg.sender), "deposit failed");
Expand Down Expand Up @@ -159,12 +159,17 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I
}

function withdrawRewards() public {
uint256 rewards = _withdrawAndTransferReward(msg.sender);
uint256 rewards = _withdrawAndTransferReward(msg.sender, msg.sender);
require(rewards >= minAmount, "Too small rewards amount");
}

function withdrawRewardsTo(address to) public {
uint256 rewards = _withdrawAndTransferReward(msg.sender, to);
require(rewards >= minAmount, "Too small rewards amount");
}

function migrateOut(address user, uint256 amount) external onlyOwner {
_withdrawAndTransferReward(user);
_withdrawAndTransferReward(user, user);
(uint256 totalStaked, uint256 rate) = getTotalStake(user);
require(totalStaked >= amount, "Migrating too much");

Expand All @@ -181,7 +186,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I
}

function migrateIn(address user, uint256 amount) external onlyOwner {
_withdrawAndTransferReward(user);
_withdrawAndTransferReward(user, user);
_buyShares(amount, 0, user);
}

Expand All @@ -194,16 +199,17 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I

function slash(
uint256 validatorStake,
uint256 delegatedAmount,
uint256 totalAmount,
uint256 totalAmountToSlash
) external onlyOwner returns (uint256) {
uint256 _withdrawPool = withdrawPool;
uint256 delegationAmount = delegatedAmount.add(_withdrawPool);
uint256 delegationAmount = totalAmount.sub(validatorStake).add(_withdrawPool);
if (delegationAmount == 0) {
return 0;
}

// total amount to be slashed from delegation pool (active + inactive)
uint256 _amountToSlash = delegationAmount.mul(totalAmountToSlash).div(validatorStake.add(delegationAmount));
uint256 _amountToSlash = delegationAmount.mul(totalAmountToSlash).div(totalAmount);
uint256 _amountToSlashWithdrawalPool = _withdrawPool.mul(_amountToSlash).div(delegationAmount);

// slash inactive pool
Expand Down Expand Up @@ -281,7 +287,7 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I
uint256 shares = claimAmount.mul(precision).div(rate);
require(shares <= maximumSharesToBurn, "too much slippage");

_withdrawAndTransferReward(msg.sender);
_withdrawAndTransferReward(msg.sender, msg.sender);

_burn(msg.sender, shares);
stakeManager.updateValidatorState(validatorId, -int256(claimAmount));
Expand Down Expand Up @@ -358,11 +364,11 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I
return liquidRewards;
}

function _withdrawAndTransferReward(address user) private returns (uint256) {
uint256 liquidRewards = _withdrawReward(user);
function _withdrawAndTransferReward(address from, address to) private returns (uint256) {
uint256 liquidRewards = _withdrawReward(from);
if (liquidRewards != 0) {
require(stakeManager.transferFunds(validatorId, liquidRewards, user), "Insufficent rewards");
stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidRewards);
require(stakeManager.transferFunds(validatorId, liquidRewards, to), "Insufficent rewards");
stakingLogger.logDelegatorClaimRewards(validatorId, from, liquidRewards);
}
return liquidRewards;
}
Expand Down Expand Up @@ -401,9 +407,9 @@ contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, I
uint256 value
) internal {
// get rewards for recipient
_withdrawAndTransferReward(to);
_withdrawAndTransferReward(to, to);
// convert rewards to shares
_withdrawAndTransferReward(from);
_withdrawAndTransferReward(from, from);
// move shares to recipient
super._transfer(from, to, value);
}
Expand Down
11 changes: 8 additions & 3 deletions test/units/staking/SlashingManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,10 @@ contract('Slashing:validator', async function(accounts) {
})

it('validator must be unstaked within current epoch', async function() {
const validatorData = await stakeManager.validators(slashedValidatorId)
assertBigNumberEquality(validatorData.deactivationEpoch, await stakeManager.currentEpoch())
const state = await stakeManager.signerState(validatorAddr)
// mask out deactivation epoch
const deactivationEpoch = state.status.and(web3.utils.toBN('1766847064778384329583297500742918515827483896875618958121606201292619775'))
assertBigNumberEquality(deactivationEpoch, await stakeManager.currentEpoch())
})

it('validator must not be jailed', async function() {
Expand All @@ -174,6 +176,7 @@ contract('Slashing:validator', async function(accounts) {
})
})
})

contract('Slashing:delegation', async function(accounts) {
let stakeToken
let stakeManager
Expand Down Expand Up @@ -229,10 +232,12 @@ contract('Slashing:delegation', async function(accounts) {
logs[0].event.should.equal('Slashed')
assertBigNumberEquality(logs[0].args.amount, web3.utils.toWei('200'))
const validator1 = await stakeManager.validators(1)

validator = await stakeManager.validators(2)
const state = await stakeManager.signerState(wallets[1].getAddressString())

assertBigNumberEquality(validator1.amount, web3.utils.toWei('900'))
assertBigNumberEquality(validator.delegatedAmount, web3.utils.toWei('230'))
assertBigNumberEquality(state.totalAmount.sub(validator.amount), web3.utils.toWei('230'))
})
})
})
2 changes: 1 addition & 1 deletion test/units/staking/ValidatorShare.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ contract('ValidatorShare', async function() {
this.stakeToken = await TestToken.new('MATIC', 'MATIC')

await this.stakeManager.setStakingToken(this.stakeToken.address)

await this.stakeToken.mint(this.stakeManager.address, toWei('10000000'))

this.validatorId = '8'
Expand Down Expand Up @@ -647,6 +646,7 @@ contract('ValidatorShare', async function() {

await buyVoucher(this.validatorContract, this.stakeAmount, this.user)
})

before('slash', async function() {
await slash.call(this, [{ validator: this.validatorId, amount: this.stakeAmount }], [this.validatorUser], this.validatorUser)
})
Expand Down
24 changes: 10 additions & 14 deletions test/units/staking/stakeManager/StakeManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1547,13 +1547,6 @@ contract('StakeManager', async function(accounts) {
from: validatorUser
}), 'fee too small')
})

it('when fee overflows', async function() {
const overflowFee = new BN(2).pow(new BN(256))
await expectRevert.unspecified(this.stakeManager.topUpForFee(validatorUser, overflowFee, {
from: validatorUser
}))
})
})
})

Expand Down Expand Up @@ -2104,6 +2097,7 @@ contract('StakeManager', async function(accounts) {
prepareToTest()
testConfirmAuctionBidForNewValidator()
})

describe('when 1000 dynasties has passed', function() {
prepareToTest()
before(async function() {
Expand All @@ -2113,6 +2107,7 @@ contract('StakeManager', async function(accounts) {

testConfirmAuctionBidForNewValidator()
})

describe('when validator has more stake then last bid', function() {
prepareToTest()
before(async function() {
Expand Down Expand Up @@ -2378,7 +2373,7 @@ contract('StakeManager', async function(accounts) {
await this.stakeManager.unstakeClaim(aliceId, { from: initialStakers[1].getChecksumAddressString() })
})

it('Should migrate', async function() {
it('should migrate', async function() {
await this.stakeManager.migrateDelegation(aliceId, bobId, migrationAmount, { from: delegator })
})
})
Expand All @@ -2401,7 +2396,7 @@ contract('StakeManager', async function(accounts) {
})

describe('Chad delegates to Alice', async function() {
it('Should delegate', async function() {
it('should delegate', async function() {
this.receipt = await buyVoucher(aliceContract, delegationAmount, delegator)
})

Expand Down Expand Up @@ -2431,7 +2426,8 @@ contract('StakeManager', async function(accounts) {

it('Active amount must be updated', async function() {
const validator = await this.stakeManager.validators(aliceId)
assertBigNumberEquality(validator.delegatedAmount, delegationAmountBN)
const state = await this.stakeManager.signerState(initialStakers[1].getChecksumAddressString())
assertBigNumberEquality(state.totalAmount.sub(validator.amount), delegationAmountBN)
})
})

Expand Down Expand Up @@ -2489,13 +2485,13 @@ contract('StakeManager', async function(accounts) {
})

it('Alice active amount must be updated', async function() {
const validator = await this.stakeManager.validators(aliceId)
assertBigNumberEquality(validator.delegatedAmount, delegationAmountBN.sub(migrationAmountBN))
const delegatedAmount = await this.stakeManager.delegatedAmount(aliceId)
assertBigNumberEquality(delegatedAmount, delegationAmountBN.sub(migrationAmountBN))
})

it('Bob active amount must be updated', async function() {
const validator = await this.stakeManager.validators(bobId)
assertBigNumberEquality(validator.delegatedAmount, migrationAmount)
const delegatedAmount = await this.stakeManager.delegatedAmount(bobId)
assertBigNumberEquality(delegatedAmount, migrationAmount)
})
})
})
Expand Down