Skip to content
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

reflectEraUpdate for cancelUnbond #403

Merged
merged 2 commits into from
Apr 23, 2024
Merged
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
6 changes: 6 additions & 0 deletions contracts/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ contract Staking is IStaking, Initializable, OwnableUpgradeable {
'G008'
);
require(_amount > 0, 'S003');

reflectEraUpdate(_source, _runner);

if (this.isEmptyDelegation(_source, _runner)) {
stakingIndexerNos[_source][_runner] = stakingIndexerLengths[_source];
stakingIndexers[_source][stakingIndexerLengths[_source]] = _runner;
Expand Down Expand Up @@ -338,6 +341,9 @@ contract Staking is IStaking, Initializable, OwnableUpgradeable {
msg.sender == address(this),
'G008'
);

reflectEraUpdate(_source, _runner);

require(delegation[_source][_runner].valueAfter >= _amount && _amount > 0, 'S005');

delegation[_source][_runner].valueAfter -= _amount;
Expand Down
6 changes: 0 additions & 6 deletions contracts/StakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ contract StakingManager is IStakingManager, Initializable, OwnableUpgradeable {
*/
function stake(address _runner, uint256 _amount) external override {
Staking staking = Staking(settings.getContractAddress(SQContracts.Staking));
staking.reflectEraUpdate(_runner, _runner);
if (staking.isEmptyDelegation(_runner, _runner)) {
require(msg.sender == settings.getContractAddress(SQContracts.IndexerRegistry), 'G001');
staking.addRunner(_runner);
Expand All @@ -59,7 +58,6 @@ contract StakingManager is IStakingManager, Initializable, OwnableUpgradeable {
function delegate(address _runner, uint256 _amount) external {
require(msg.sender != _runner, 'G004');
Staking staking = Staking(settings.getContractAddress(SQContracts.Staking));
staking.reflectEraUpdate(msg.sender, _runner);
// delegation limit should not exceed
staking.checkDelegateLimitation(_runner, _amount);
staking.delegateToIndexer(msg.sender, _runner, _amount);
Expand All @@ -72,7 +70,6 @@ contract StakingManager is IStakingManager, Initializable, OwnableUpgradeable {
*/
function unstake(address _runner, uint256 _amount) external {
Staking staking = Staking(settings.getContractAddress(SQContracts.Staking));
staking.reflectEraUpdate(_runner, _runner);
if (msg.sender == settings.getContractAddress(SQContracts.IndexerRegistry)) {
staking.removeRunner(_runner);
} else {
Expand All @@ -97,7 +94,6 @@ contract StakingManager is IStakingManager, Initializable, OwnableUpgradeable {
// check if called by an indexer
require(_runner != msg.sender, 'G004');
Staking staking = Staking(settings.getContractAddress(SQContracts.Staking));
staking.reflectEraUpdate(msg.sender, _runner);
staking.startUnbond(msg.sender, _runner, _amount, UnbondType.Undelegation);
}

Expand All @@ -112,9 +108,7 @@ contract StakingManager is IStakingManager, Initializable, OwnableUpgradeable {
// delegation limit should not exceed
staking.checkDelegateLimitation(_toRunner, _amount);

staking.reflectEraUpdate(_source, _fromRunner);
staking.removeDelegation(_source, _fromRunner, _amount);
staking.reflectEraUpdate(_source, _toRunner);
staking.addDelegation(_source, _toRunner, _amount);
}

Expand Down
2 changes: 1 addition & 1 deletion test/Maintenance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Maintenance Mode Test', () => {
await token.connect(wallet_2).increaseAllowance(staking.address, etherParse('10000'));
await token.increaseAllowance(staking.address, etherParse('10000'));
await token.increaseAllowance(purchaseOfferMarket.address, etherParse('10000'));
await indexerRegistry.registerIndexer(etherParse('1000'), METADATA_HASH, 0);
await indexerRegistry.registerIndexer(etherParse('1001'), METADATA_HASH, 0);
await indexerRegistry.connect(wallet_2).registerIndexer(etherParse('1000'), METADATA_HASH, 0);

await planManager.createPlanTemplate(time.duration.days(3).toString(), 1000, 100, token.address, METADATA_HASH);
Expand Down
7 changes: 5 additions & 2 deletions test/RewardsDistributer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,12 +584,13 @@ describe('RewardsDistributor Contract', () => {
await stakingManager.connect(delegator).undelegate(runner.address, etherParse('1'));
// 3. start new era -> delegator -> `applyStakeChange`
await startNewEra(eraManager);
await rewardsHelper.indexerCatchup(runner.address);
await rewardsHelper.connect(runner).indexerCatchup(runner.address);
// 4. check totalStakingAmount equal 0, era reward equal 0
await checkValues(etherParse('2.697302697'), etherParse('9.002697302697'), 0, 0);
// 5. indexer register successfully
await stakingManager.connect(runner).widthdraw();
await token.connect(runner).increaseAllowance(staking.address, etherParse('1000'));
await staking.reflectEraUpdate(runner.address, runner.address);
await indexerRegistry
.connect(runner)
.registerIndexer(etherParse('1000'), METADATA_HASH, 100, { gasLimit: '2000000' });
Expand All @@ -604,6 +605,7 @@ describe('RewardsDistributor Contract', () => {
await stakingManager.connect(runner).widthdraw();
await stakingManager.connect(delegator).widthdraw();
// 3. indexer reregister -> add previous delegator and a new delegator
await staking.reflectEraUpdate(runner.address, runner.address);
await token.connect(runner).increaseAllowance(staking.address, etherParse('1000'));
await indexerRegistry
.connect(runner)
Expand Down Expand Up @@ -633,7 +635,7 @@ describe('RewardsDistributor Contract', () => {
await checkValues(etherParse('4.996702697'), etherParse('9.001697302697'), etherParse('1002'), 0);
});

it('reward distribution should work after indexer reregister few more ears later', async () => {
it.only('reward distribution should work after indexer reregister few more ears later', async () => {
// 1. delegator undelegate all the tokens
await stakingManager.connect(delegator).undelegate(runner.address, etherParse('1'));
// 2. start new era -> indexer `collectAndDistributeRewards` -> `applyStakeChange | delegator -> `applyStakeChange`
Expand All @@ -645,6 +647,7 @@ describe('RewardsDistributor Contract', () => {
await startNewEra(eraManager);
await startNewEra(eraManager);
// 3. indexer reregister -> add previous delegator and a new delegator
await staking.reflectEraUpdate(runner.address, runner.address);
await token.connect(runner).increaseAllowance(staking.address, etherParse('1000'));
await indexerRegistry
.connect(runner)
Expand Down
94 changes: 63 additions & 31 deletions test/Staking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,19 @@ import {
StakingManager,
RewardsHelper,
} from '../src';
import { etherParse, lastestBlockTime, registerRunner, revertMsg, startNewEra, timeTravel } from './helper';
import {
addInstantRewards,
etherParse,
lastestBlockTime,
registerRunner,
revertMsg,
startNewEra,
timeTravel,
} from './helper';
import { deployContracts } from './setup';

describe('Staking Contract', () => {
let runner, runner2, delegator, delegator2;
let root, runner, runner2, delegator, delegator2;
let token: ERC20;
let staking: Staking;
let stakingManager: StakingManager;
Expand All @@ -30,7 +38,7 @@ describe('Staking Contract', () => {
let rewardsStaking: RewardsStaking;
let rewardsHelper: RewardsHelper;

const amount = etherParse('2002');
const selfStake = etherParse('2002');

const checkDelegation = async (_delegator: string, indexerAddress: string, valueAfter: BigNumber, era: number) => {
const stakingAmount = await staking.delegation(_delegator, indexerAddress);
Expand All @@ -53,21 +61,21 @@ describe('Staking Contract', () => {
};

const configWallet = async () => {
await registerRunner(token, indexerRegistry, staking, runner, runner, etherParse('2002'));
await registerRunner(token, indexerRegistry, staking, runner, runner2, etherParse('2002'));
await token.connect(runner).transfer(delegator.address, amount);
await token.connect(runner).transfer(delegator2.address, amount);
await token.connect(delegator).increaseAllowance(staking.address, amount);
await registerRunner(token, indexerRegistry, staking, root, runner, etherParse('2002'));
await registerRunner(token, indexerRegistry, staking, root, runner2, etherParse('2002'), 1e5);
await token.connect(root).transfer(runner.address, selfStake);
await token.connect(root).transfer(delegator.address, selfStake);
await token.connect(root).transfer(delegator2.address, selfStake);
await token.connect(delegator).increaseAllowance(staking.address, selfStake);
};

const deployer = () => deployContracts(runner, runner2);
const deployer = () => deployContracts(root, runner2);
before(async () => {
[runner, runner2, delegator] = await ethers.getSigners();
[root, runner, runner2, delegator, delegator2] = await ethers.getSigners();
});

beforeEach(async () => {
const deployment = await waffle.loadFixture(deployer);
[runner, runner2, delegator, delegator2] = await ethers.getSigners();
token = deployment.token;
staking = deployment.staking;
stakingManager = deployment.stakingManager;
Expand Down Expand Up @@ -118,10 +126,10 @@ describe('Staking Contract', () => {

// first stake from indexer should be effective immediately
const stakingAmount = await staking.delegation(runner.address, runner.address);
expect(stakingAmount.valueAt).to.equal(amount);
expect(stakingAmount.valueAfter).to.equal(amount);
await checkStakingAmount(runner.address, amount, 2);
expect(await token.balanceOf(staking.address)).to.equal(amount.mul(2));
expect(stakingAmount.valueAt).to.equal(selfStake);
expect(stakingAmount.valueAfter).to.equal(selfStake);
await checkStakingAmount(runner.address, selfStake, 2);
expect(await token.balanceOf(staking.address)).to.equal(selfStake.mul(2));
});

it('staking by indexer should work', async () => {
Expand All @@ -130,7 +138,7 @@ describe('Staking Contract', () => {
await stakingManager.connect(runner).stake(runner.address, moreStakingAmount);
// check staking changes
expect(await stakingManager.getAfterDelegationAmount(runner.address, runner.address)).to.equal(
amount.add(moreStakingAmount)
selfStake.add(moreStakingAmount)
);
});

Expand All @@ -139,7 +147,7 @@ describe('Staking Contract', () => {
await stakingManager.connect(runner).unstake(runner.address, unstakeAmount);
// check staking changes
expect(await stakingManager.getAfterDelegationAmount(runner.address, runner.address)).to.equal(
amount.sub(unstakeAmount)
selfStake.sub(unstakeAmount)
);
});

Expand Down Expand Up @@ -190,9 +198,9 @@ describe('Staking Contract', () => {

it('redelegate with invalid params should fail', async () => {
// self delegation
await expect(
stakingManager.redelegate(runner.address, runner2.address, etherParse('1'))
).to.be.revertedWith('G004');
await expect(stakingManager.redelegate(root.address, runner2.address, etherParse('1'))).to.be.revertedWith(
'G004'
);
// out of amount
await stakingManager.connect(delegator).delegate(runner.address, etherParse('1'));
await expect(
Expand All @@ -208,7 +216,7 @@ describe('Staking Contract', () => {
await startNewEra(eraManager);
expect(await staking.stakingIndexerLengths(delegator.address)).to.equal(1);
await checkDelegation(delegator.address, runner.address, etherParse('1'), 3);
await checkStakingAmount(runner.address, amount.add(etherParse('1')), 3);
await checkStakingAmount(runner.address, selfStake.add(etherParse('1')), 3);

expect(await token.balanceOf(delegator.address)).to.equal(delegatorBalance.sub(etherParse('1')));
expect(await token.balanceOf(staking.address)).to.equal(contractBalance.add(etherParse('1')));
Expand All @@ -222,9 +230,9 @@ describe('Staking Contract', () => {
await startNewEra(eraManager);
expect(await staking.stakingIndexerLengths(delegator.address)).to.equal(2);
await checkDelegation(delegator.address, from_indexer, etherParse('0'), 3);
await checkStakingAmount(from_indexer, amount, 3);
await checkStakingAmount(from_indexer, selfStake, 3);
await checkDelegation(delegator.address, to_indexer, etherParse('1'), 3);
await checkStakingAmount(to_indexer, amount.add(etherParse('1')), 3);
await checkStakingAmount(to_indexer, selfStake.add(etherParse('1')), 3);
});

it('delegate by indexer should fail', async () => {
Expand All @@ -236,7 +244,7 @@ describe('Staking Contract', () => {
it('delegation excess max limitation should fail', async () => {
const indexerLeverageLimit = await staking.indexerLeverageLimit();
const indexerStakingAmount = await stakingManager.getAfterDelegationAmount(runner.address, runner.address);
await token.connect(runner).transfer(delegator.address, indexerStakingAmount.mul(indexerLeverageLimit));
await token.connect(root).transfer(delegator.address, indexerStakingAmount.mul(indexerLeverageLimit));

await expect(
stakingManager
Expand Down Expand Up @@ -268,8 +276,8 @@ describe('Staking Contract', () => {

// check changes of staking storage
await startNewEra(eraManager);
await checkDelegation(runner.address, runner.address, amount.sub(etherParse('0.5')), 3);
await checkStakingAmount(runner.address, amount.add(etherParse('1.5')), 3);
await checkDelegation(runner.address, runner.address, selfStake.sub(etherParse('0.5')), 3);
await checkStakingAmount(runner.address, selfStake.add(etherParse('1.5')), 3);
await checkUnbondingAmount(runner.address, 0, startTime, etherParse('0.5'));

// check changes of unbonding storage
Expand All @@ -279,7 +287,7 @@ describe('Staking Contract', () => {
});

it('request unregister to unstaking all by indexer registry should work', async () => {
await indexerRegistry.unregisterIndexer({ gasLimit: '1000000' });
await indexerRegistry.connect(runner).unregisterIndexer({ gasLimit: '1000000' });

// check changes of indexer storage
await startNewEra(eraManager);
Expand All @@ -299,7 +307,7 @@ describe('Staking Contract', () => {
// check changes of staking storage
await startNewEra(eraManager);
await checkDelegation(delegator.address, runner.address, etherParse('1'), 3);
await checkStakingAmount(runner.address, amount.add(etherParse('1')), 3);
await checkStakingAmount(runner.address, selfStake.add(etherParse('1')), 3);
await checkUnbondingAmount(delegator.address, 0, startTime, etherParse('1'));

// check changes of unbonding storage
Expand All @@ -315,7 +323,7 @@ describe('Staking Contract', () => {
// check changes of staking storage
await startNewEra(eraManager);
await checkDelegation(delegator.address, runner.address, etherParse('0.5'), 3);
await checkStakingAmount(runner.address, amount.add(etherParse('0.5')), 3);
await checkStakingAmount(runner.address, selfStake.add(etherParse('0.5')), 3);

// check all unbondingAmounts
const unbondingAmounts = await stakingManager.getUnbondingAmounts(delegator.address);
Expand All @@ -339,7 +347,7 @@ describe('Staking Contract', () => {
await rewardsHelper.batchCollectAndDistributeRewards(runner.address, 10);
await rewardsHelper.batchApplyStakeChange(runner.address, [delegator.address, delegator2.address]);
// deregister from network
await expect(indexerRegistry.unregisterIndexer({ gasLimit: '1000000' }))
await expect(indexerRegistry.connect(runner).unregisterIndexer({ gasLimit: '1000000' }))
.to.be.emit(indexerRegistry, 'UnregisterIndexer')
.withArgs(runner.address);

Expand All @@ -352,7 +360,9 @@ describe('Staking Contract', () => {

it('request undelegate with invlaid params should fail', async () => {
// indexer unstake out of balance
await expect(stakingManager.undelegate(runner.address, amount)).to.be.revertedWith('G004');
await expect(stakingManager.connect(runner).undelegate(runner.address, selfStake)).to.be.revertedWith(
'G004'
);
// amount should be positive
await expect(
stakingManager.connect(delegator).undelegate(runner.address, etherParse('0'))
Expand Down Expand Up @@ -398,6 +408,28 @@ describe('Staking Contract', () => {
await expect(stakingManager.connect(delegator).widthdraw()).to.be.revertedWith('S009');
});

it('cancel commission unbond should work', async () => {
// commission rate: 10%
// reward 1000 SQT
// commission = 100 SQT
const era = await eraManager.eraNumber();
let stake = await staking.delegation(runner2.address, runner2.address);
expect(stake.era).to.eq(era);
expect(stake.valueAt).to.eq(selfStake);
expect(stake.valueAfter).to.eq(selfStake);
// receive rewards
await addInstantRewards(token, rewardsDistributor, root, runner2.address, era, etherParse('1000'));
await startNewEra(eraManager);
await rewardsDistributor.collectAndDistributeRewards(runner2.address);
// cancel commission
expect(await staking.unbondingLength(runner2.address)).to.eq(1);
await stakingManager.connect(runner2).cancelUnbonding(0);
stake = await staking.delegation(runner2.address, runner2.address);
expect(stake.era).to.eq(era.add(1));
expect(stake.valueAt).to.eq(selfStake);
expect(stake.valueAfter).to.eq(selfStake.add(etherParse('100')));
});

it('cancelUnbonding resize should work', async () => {
// use unbonding length to 6, max undelegate is 5
await staking.setMaxUnbondingRequest(6);
Expand Down
16 changes: 16 additions & 0 deletions test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
ProjectRegistry,
RewardsBooster,
StateChannel,
SQToken,
RewardsDistributor,
} from '../src';
import { METADATA_HASH } from './constants';
import { expect } from 'chai';
Expand Down Expand Up @@ -295,3 +297,17 @@ export const revertMsg = {
insufficientBalance: 'ERC20: transfer amount exceeds balance',
insufficientAllowance: 'ERC20: insufficient allowance',
};

export async function addInstantRewards(
sqtoken: ERC20,
rewardsDistributor: RewardsDistributor,
signer: SignerWithAddress,
runner: string,
era: BigNumberish,
amount: BigNumberish
) {
await wrapTxs(async () => {
await sqtoken.connect(signer).approve(rewardsDistributor.address, amount);
await rewardsDistributor.connect(signer).addInstantRewards(runner, signer.address, amount, era);
});
}
Loading