Skip to content

Commit

Permalink
add migration delegation tests
Browse files Browse the repository at this point in the history
  • Loading branch information
devintegral2 committed Jun 12, 2020
1 parent fd8a69a commit 06557c6
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 6 deletions.
59 changes: 59 additions & 0 deletions contracts/test/LegacyStaker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pragma solidity ^0.5.0;

import "./UnitTestStakers.sol";


contract LegacyStaker is UnitTestStakers {
constructor (uint256 firstEpoch) public UnitTestStakers(firstEpoch) {
}

function createLegacyDelegation(uint256 to) external payable {
address delegator = msg.sender;
_checkActiveStaker(to);
require(msg.value >= minDelegation(), "insufficient amount");
require(delegations[delegator].amount == 0, "delegation already exists");
require(stakerIDs[delegator] == 0, "already staking");
require(maxDelegatedLimit(stakers[to].stakeAmount) >= stakers[to].delegatedMe.add(msg.value), "staker's limit is exceeded");

Delegation memory newDelegation;
newDelegation.createdEpoch = currentEpoch();
newDelegation.createdTime = block.timestamp;
newDelegation.amount = msg.value;
newDelegation.toStakerID = to;
newDelegation.paidUntilEpoch = currentSealedEpoch;
delegations[delegator] = newDelegation;

stakers[to].delegatedMe = stakers[to].delegatedMe.add(msg.value);
delegationsNum++;
delegationsTotalAmount = delegationsTotalAmount.add(msg.value);

emit CreatedDelegation(delegator, to, msg.value);
}

function prepareToWithdrawLegacyDelegation() external {
address delegator = msg.sender;
require(delegations[delegator].amount != 0, "delegation doesn't exist");
require(delegations[delegator].deactivatedTime == 0, "delegation is deactivated");
require(delegations[delegator].paidUntilEpoch == currentSealedEpoch, "not all rewards claimed"); // for rewards burning

uint256 stakerID = delegations[delegator].toStakerID;
_mayBurnRewardsOnDeactivation(true, stakerID, delegator, delegations[delegator].amount, delegations[delegator].amount);

delegations[delegator].deactivatedEpoch = currentEpoch();
delegations[delegator].deactivatedTime = block.timestamp;
uint256 delegationAmount = delegations[delegator].amount;

if (stakers[stakerID].stakeAmount != 0) {
// if staker haven't withdrawn
stakers[stakerID].delegatedMe = stakers[stakerID].delegatedMe.sub(delegationAmount);
}

emit DeactivatedDelegation(delegator, stakerID);
}
}

contract Factory {
function createLegacyStaker(uint256 firstEpoch) external {
new LegacyStaker(firstEpoch);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity ^0.5.0;

import "./Staker.sol";
import "../sfc/Staker.sol";

contract TestStakers is Stakers {
function stakeLockPeriodTime() public pure returns (uint256) {
Expand Down Expand Up @@ -93,9 +93,13 @@ contract UnitTestStakers is Stakers {
}

function discardDelegationRewards(uint256 stakerID) public {
_checkAndUpgradeDelegateStorage(msg.sender);
require(delegations_v2[msg.sender][stakerID].amount != 0, "delegation doesn't exist");
delegations_v2[msg.sender][stakerID].paidUntilEpoch = currentSealedEpoch;
if (delegations[msg.sender].amount != 0) {
delegations[msg.sender].paidUntilEpoch = currentSealedEpoch;
} else if (delegations_v2[msg.sender][stakerID].amount != 0) {
delegations_v2[msg.sender][stakerID].paidUntilEpoch = currentSealedEpoch;
} else {
revert("delegation doesn't exist");
}
}

function rewardsAllowed() public view returns (bool) {
Expand Down
141 changes: 141 additions & 0 deletions test/MigrationState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
const {
BN,
ether,
expectRevert,
time,
balance,
constants,
} = require('openzeppelin-test-helpers');
const {expect} = require('chai');

const { ZERO_ADDRESS } = constants;

const assertEqual = (a, b) => assert.isTrue(Object.is(a, b), `Expected ${a.toString()} to equal ${b.toString()}`);

const LegacyStaker = artifacts.require('LegacyStaker');
const Factory = artifacts.require('Factory');

const getLegacyDeposition = async (depositor) => this.stakers.delegations.call(depositor);
const getDeposition = async (depositor, to) => this.stakers.delegations_v2.call(depositor, to);
const getStaker = async (stakerID) => this.stakers.stakers.call(stakerID);
const createLegacyDelegation = async(depositor, stakerID, amount, epoch) => {
await this.stakers.createLegacyDelegation(stakerID, { from: depositor, value: amount});
const now = await time.latest();
const deposition = await getLegacyDeposition(depositor);

expect(deposition.amount).to.be.bignumber.equal(amount);
expect(deposition.createdEpoch).to.be.bignumber.equal(epoch);
expect(now.sub(deposition.createdTime)).to.be.bignumber.lt(new BN('3'));
expect(deposition.toStakerID).to.be.bignumber.equal(stakerID);
}

contract('Migration tests', async ([firstStaker, secondStaker, firstDepositor, secondDepositor, thirdDepositor, fourthDepositor, fifthDepositor]) => {
describe ('migration delegate tests', async () => {
before(async () => {
const factory = await Factory.new();
const response = await factory.createLegacyStaker(0);
console.log('\tdeploying gas used:', response.receipt.gasUsed.toString(10));
});

beforeEach(async () => {
this.firstEpoch = 0;
this.stakers = await LegacyStaker.new(this.firstEpoch);
this.validatorComission = new BN('150000'); // 0.15
});

it('auto migrate legacy deposition to new model', async () => {
// create 5 legacy delegation
await this.stakers._createStake({ from: firstStaker, value: ether('2.0')});
const firstStakerID = await this.stakers.getStakerID(firstStaker);
const currentEpoch = (await this.stakers.currentSealedEpoch()).add(new BN ('1'));
const delegationAmount = ether('2.0');
await createLegacyDelegation(firstDepositor, firstStakerID, delegationAmount, currentEpoch);
await createLegacyDelegation(secondDepositor, firstStakerID, delegationAmount, currentEpoch);
await createLegacyDelegation(thirdDepositor, firstStakerID, delegationAmount, currentEpoch);
await createLegacyDelegation(fourthDepositor, firstStakerID, delegationAmount, currentEpoch);
await createLegacyDelegation(fifthDepositor, firstStakerID, delegationAmount, currentEpoch);
// check stake with legacy delegations
const firstStakerEntity = await getStaker(firstStakerID);
expect(firstStakerEntity.delegatedMe).to.be.bignumber.equal(delegationAmount.mul(new BN('5')));
expect(await this.stakers.delegationsTotalAmount.call()).to.be.bignumber.equal(delegationAmount.mul(new BN('5')));
expect(await this.stakers.delegationsNum.call()).to.be.bignumber.equal(new BN('5'));
// check legacy delegations
expect((await getLegacyDeposition(firstDepositor)).amount).to.be.bignumber.equal(delegationAmount);
expect((await getLegacyDeposition(secondDepositor)).amount).to.be.bignumber.equal(delegationAmount);
expect((await getLegacyDeposition(thirdDepositor)).amount).to.be.bignumber.equal(delegationAmount);
expect((await getLegacyDeposition(fourthDepositor)).amount).to.be.bignumber.equal(delegationAmount);
expect((await getLegacyDeposition(fifthDepositor)).amount).to.be.bignumber.equal(delegationAmount);
// migrate first delegation (increaseDelegation)
await this.stakers.increaseDelegation(firstStakerID, { from: firstDepositor, value: delegationAmount});
// migrate second delegation (claimDelegationRewards)
await this.stakers._makeEpochSnapshots(5);
await this.stakers.claimDelegationRewards(currentEpoch, firstStakerID, { from: secondDepositor });
// migrate third delegation (prepareToWithdrawDelegation)
await this.stakers.discardDelegationRewards(firstStakerID, { from: thirdDepositor });
await this.stakers.prepareToWithdrawDelegation(firstStakerID, { from: thirdDepositor });
// migrate fourth delegation (prepareToWithdrawDelegationPartial)
const wrID = new BN('0');
await this.stakers.discardDelegationRewards(firstStakerID, { from: fourthDepositor });
await this.stakers.prepareToWithdrawDelegationPartial(wrID, firstStakerID, delegationAmount.div(new BN('2')), { from: fourthDepositor });
// migrate fifth delegation (withdrawDelegation)
await this.stakers.discardDelegationRewards(firstStakerID, { from: fifthDepositor });
await this.stakers.prepareToWithdrawLegacyDelegation({ from: fifthDepositor });
time.increase(86400 * 7);
await this.stakers._makeEpochSnapshots(5);
await this.stakers._makeEpochSnapshots(5);
await this.stakers._makeEpochSnapshots(5);
await this.stakers.withdrawDelegation(firstStakerID, { from: fifthDepositor });
// check removed legacy delegations
expect((await getLegacyDeposition(firstDepositor)).amount).to.be.bignumber.equal(new BN('0'));
expect((await getLegacyDeposition(secondDepositor)).amount).to.be.bignumber.equal(new BN('0'));
expect((await getLegacyDeposition(thirdDepositor)).amount).to.be.bignumber.equal(new BN('0'));
expect((await getLegacyDeposition(fourthDepositor)).amount).to.be.bignumber.equal(new BN('0'));
expect((await getLegacyDeposition(fifthDepositor)).amount).to.be.bignumber.equal(new BN('0'));
// check delegation in new model
expect((await getDeposition(firstDepositor, firstStakerID)).toStakerID).to.be.bignumber.equal(firstStakerID);
expect((await getDeposition(secondDepositor, firstStakerID)).toStakerID).to.be.bignumber.equal(firstStakerID);
expect((await getDeposition(thirdDepositor, firstStakerID)).toStakerID).to.be.bignumber.equal(firstStakerID);
expect((await getDeposition(fourthDepositor, firstStakerID)).toStakerID).to.be.bignumber.equal(firstStakerID);
// expect((await getDeposition(fifthDepositor, firstStakerID)).toStakerID).to.be.bignumber.equal(firstStakerID); // can't check because withdrawDelegation removes delegation entity
});

it('manually migrate legacy deposition to new model', async () => {
// create legacy delegation
await this.stakers._createStake({ from: firstStaker, value: ether('2.0')});
const firstStakerID = await this.stakers.getStakerID(firstStaker);
const currentEpoch = (await this.stakers.currentSealedEpoch()).add(new BN ('1'));
const delegationAmount = ether('2.0');
await createLegacyDelegation(firstDepositor, firstStakerID, delegationAmount, currentEpoch);
// check legacy delegations
expect((await getLegacyDeposition(firstDepositor)).amount).to.be.bignumber.equal(delegationAmount);
// migrate delegation (_syncDelegator)
await this.stakers._syncDelegator(firstDepositor, firstStakerID);
// check removed legacy delegations
expect((await getLegacyDeposition(firstDepositor)).amount).to.be.bignumber.equal(new BN('0'));
// check delegation in new model
expect((await getDeposition(firstDepositor, firstStakerID)).toStakerID).to.be.bignumber.equal(firstStakerID);
});

it('can\'t call calcDelegationRewards while delegation is in the legacy model', async () => {
// create legacy delegation
await this.stakers._createStake({ from: firstStaker, value: ether('2.0')});
const firstStakerID = await this.stakers.getStakerID(firstStaker);
const currentEpoch = (await this.stakers.currentSealedEpoch()).add(new BN ('1'));
const delegationAmount = ether('2.0');
await createLegacyDelegation(firstDepositor, firstStakerID, delegationAmount, currentEpoch);
// check legacy delegations
expect((await getLegacyDeposition(firstDepositor)).amount).to.be.bignumber.equal(delegationAmount);
// can't call calcDelegationRewards while delegation is in the legacy model
await this.stakers._makeEpochSnapshots(5);
await this.stakers._makeEpochSnapshots(5);
await expectRevert(this.stakers.calcDelegationRewards(firstDepositor, firstStakerID, new BN('0'), currentEpoch), "old version delegation, please update");
// migrate delegation (_syncDelegator)
await this.stakers._syncDelegator(firstDepositor, firstStakerID);
// call calcDelegationRewards
const rewards = await this.stakers.calcDelegationRewards(firstDepositor, firstStakerID, new BN('0'), currentEpoch);
expect(rewards[0]).to.be.bignumber.equal(new BN('595000000212500000'));
expect(rewards[1]).to.be.bignumber.equal(new BN('1'));
expect(rewards[2]).to.be.bignumber.equal(new BN('1'));
})
});
});
4 changes: 2 additions & 2 deletions test/Stakers.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,9 @@ contract('Staker test', async ([firstStaker, secondStaker, thirdStaker, firstDep

it('checking prepareToWithdrawDelegation function', async () => {
const getStaker = async (stakerID) => this.stakers.stakers.call(stakerID);
const getDeposition = async (depositor, to) => this.stakers.delegations_v2.call(depositor, to);
const getDeposition = async (depositor, to) => this.stakers.delegations_v2.call(depositor, to);

await this.stakers._createStake({from: firstStaker, value: ether('1.0')});
await this.stakers._createStake({from: firstStaker, value: ether('1.0')});
let firstStakerID = await this.stakers.getStakerID(firstStaker);
await expectRevert(this.stakers.prepareToWithdrawDelegation(firstStakerID, {from: firstDepositor}), 'delegation doesn\'t exist');
const firstDepositorEntityBefore = await getDeposition(firstDepositor, firstStakerID);
Expand Down

0 comments on commit 06557c6

Please sign in to comment.