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

add testing #91

Merged
merged 5 commits into from
Jan 7, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ abstract contract VestedDelegation is IVestedDelegation, Vesting, Delegation, Ve
// update the old delegation position
DelegationPool storage oldDelegation = delegationPools[oldStaker];
uint256 amount = oldDelegation.balanceOf(msg.sender);
// we delete the previous position historical data to avoid any possible issues
// we delete the previous position historical data (for the new validator) to avoid any possible issues
delegationPools[newStaker].cleanDelegatorHistoricalData(msg.sender);

// undelegate (withdraw & emit event) the old amount from the old position
Expand Down
180 changes: 175 additions & 5 deletions test/HydraDelegation/VestedDelegation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ export function RunVestedDelegationTests(): void {
)
)
.to.be.revertedWithCustomError(hydraDelegation, "DelegateRequirement")
.withArgs("DelegPoolLib", ERRORS.DelegPoolLib.lateBalanceChange);
.withArgs("_verifyRewardsMatured", ERRORS.vesting.previousRPS);
});

it("should revert when get reward with early balance", async function () {
Expand Down Expand Up @@ -1480,7 +1480,36 @@ export function RunVestedDelegationTests(): void {
)
)
.to.be.revertedWithCustomError(hydraDelegation, "DelegateRequirement")
.withArgs("DelegPoolLib", ERRORS.DelegPoolLib.invalidParamsIndex);
.withArgs("_verifyRewardsMatured", ERRORS.vesting.previousRPS);
});

it("should revert when the position have future valid RPS and a person is trying to get an old reward", async function () {
const { systemHydraChain, hydraStaking, hydraDelegation, delegatedValidator, vestManager } = await loadFixture(
this.fixtures.vestedDelegationFixture
);

await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 5, this.epochSize, WEEK * 2);

// prepare params for call
const { epochNum, balanceChangeIndex } = await getClaimableRewardRPSData(
systemHydraChain,
hydraDelegation,
delegatedValidator.address,
vestManager.address
);

await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 1, this.epochSize);

await expect(
hydraDelegation.calculatePositionTotalReward(
delegatedValidator.address,
vestManager.address,
epochNum,
balanceChangeIndex
)
)
.to.be.revertedWithCustomError(hydraDelegation, "DelegateRequirement")
.withArgs("_verifyRewardsMatured", ERRORS.vesting.previousRPS);
});

it("should revert when calculate the total reward with earlier balance change index", async function () {
Expand Down Expand Up @@ -1570,6 +1599,62 @@ export function RunVestedDelegationTests(): void {
expect(positionClaimableReward).to.lt(positionTotalReward);
});

it("should not claim if position is matured but we pass old RPS data", async function () {
const { systemHydraChain, hydraStaking, hydraDelegation, delegatedValidator, vestManager, vestManagerOwner } =
await loadFixture(this.fixtures.weeklyVestedDelegationFixture);

// prepare params for call
let { epochNum, balanceChangeIndex } = await getClaimableRewardRPSData(
systemHydraChain,
hydraDelegation,
delegatedValidator.address,
vestManager.address
);

const positionClaimableReward = await hydraDelegation.calculatePositionClaimableReward(
delegatedValidator.address,
vestManager.address,
epochNum,
balanceChangeIndex
);

// prepare the total reward params
({ epochNum, balanceChangeIndex } = await getTotalRewardRPSData(
systemHydraChain,
hydraDelegation,
delegatedValidator.address,
vestManager.address
));

const positionTotalReward = await hydraDelegation.calculatePositionTotalReward(
delegatedValidator.address,
vestManager.address,
epochNum,
balanceChangeIndex
);

await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 1, this.epochSize);

expect(positionTotalReward).to.not.be.equal(positionClaimableReward);

const position = await hydraDelegation.vestedDelegationPositions(
delegatedValidator.address,
vestManager.address
);

// increase so the position is fully matured
await time.increaseTo(position.end.add(position.duration).add(DAY));
await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 1, this.epochSize);

await expect(
vestManager
.connect(vestManagerOwner)
.claimVestedPositionReward(delegatedValidator.address, epochNum, balanceChangeIndex)
)
.to.be.revertedWithCustomError(hydraDelegation, "DelegateRequirement")
.withArgs("_verifyRewardsMatured", ERRORS.vesting.previousRPS);
});

it("should successfully calculate the total reward (must be equal to claimable) for matured position, and claim", async function () {
const { systemHydraChain, hydraStaking, hydraDelegation, delegatedValidator, vestManager } = await loadFixture(
this.fixtures.vestedDelegationFixture
Expand All @@ -1582,7 +1667,7 @@ export function RunVestedDelegationTests(): void {
vestManager.address
);

// increase so the position is fully
// increase so the position is fully matured
await time.increaseTo(position.end.add(position.duration).add(DAY));

await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 3, this.epochSize);
Expand Down Expand Up @@ -1715,8 +1800,93 @@ export function RunVestedDelegationTests(): void {
});
});

describe("Liquid Debt", async function () {
it("should give negative liquid debt when vesting", async function () {
const { hydraDelegation, vestManager } = await loadFixture(this.fixtures.vestedDelegationFixture);

const liquidDebt = await hydraDelegation.liquidityDebts(vestManager.address);

expect(liquidDebt).to.be.lt(0);
});

it("Should clear all debt and take all Lydras on cutting the whole position", async function () {
const { systemHydraChain, hydraStaking, hydraDelegation, vestManager, delegatedValidator, vestManagerOwner } =
await loadFixture(this.fixtures.vestedDelegationFixture);

// commit epochs to distribute rewards
await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 5, this.epochSize, WEEK);

const liquidDebtBefore = await hydraDelegation.liquidityDebts(vestManager.address);

await vestManager.connect(vestManagerOwner).cutVestedDelegatePosition(delegatedValidator.address, 100);

const liquidDebtAfter = await hydraDelegation.liquidityDebts(vestManager.address);

expect(liquidDebtAfter).to.be.eq(liquidDebtBefore.add(100));
expect(liquidDebtAfter).to.be.gt(liquidDebtBefore);
expect(liquidDebtAfter).to.be.lt(0);
});

it("When cutting a position, the liquid debt should be changed properly (above), and we provide 0 Lydra", async function () {
const {
systemHydraChain,
hydraStaking,
hydraDelegation,
vestManager,
delegatedValidator,
vestManagerOwner,
liquidToken,
} = await loadFixture(this.fixtures.vestedDelegationFixture);

// commit epochs to distribute rewards
await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 5, this.epochSize, WEEK);

const liquidDebtBefore = await hydraDelegation.liquidityDebts(vestManager.address);

await liquidToken.connect(vestManagerOwner).approve(vestManager.address, this.minDelegation.mul(100));
// cut the entire position without providing any Lydra, because the liquid debt is more than the position for the manager
await vestManager
.connect(vestManagerOwner)
.cutVestedDelegatePosition(delegatedValidator.address, this.minDelegation.mul(2));

const liquidDebtAfter = await hydraDelegation.liquidityDebts(vestManager.address);

expect(await liquidToken.balanceOf(vestManagerOwner.address)).to.be.eq(0);
expect(liquidDebtAfter).to.be.gt(liquidDebtBefore);
expect(liquidDebtAfter).to.be.eq(0);
});

it("When opening a big position (more than the liquid debt), another position with same manager will handle liquid debt properly", async function () {
R-Santev marked this conversation as resolved.
Show resolved Hide resolved
R-Santev marked this conversation as resolved.
Show resolved Hide resolved
const { systemHydraChain, hydraStaking, hydraDelegation, vestManager, delegatedValidator, vestManagerOwner } =
await loadFixture(this.fixtures.vestedDelegationFixture);

const liquidDebtBeforeNewPosition = await hydraDelegation.liquidityDebts(vestManager.address);

await vestManager
.connect(vestManagerOwner)
.openVestedDelegatePosition(this.signers.validators[1].address, 52, { value: this.minDelegation.mul(100) });
// commit epochs to distribute rewards
await commitEpochs(systemHydraChain, hydraStaking, [delegatedValidator], 5, this.epochSize, WEEK);
const expectedLiquidTokensFromPosition = calcLiquidTokensToDistributeOnVesting(52, this.minDelegation.mul(100));
const liquidDebtForPosition = this.minDelegation.mul(100).sub(expectedLiquidTokensFromPosition);

const liquidDebtBeforeCut = await hydraDelegation.liquidityDebts(vestManager.address);
expect(liquidDebtBeforeNewPosition).to.be.eq(liquidDebtBeforeCut.add(liquidDebtForPosition));

// cut the entire position without providing any Lydra, because the liquid debt is more than the position for the manager
await vestManager
.connect(vestManagerOwner)
.cutVestedDelegatePosition(delegatedValidator.address, this.minDelegation.mul(2));

const liquidDebtAfter = await hydraDelegation.liquidityDebts(vestManager.address);

expect(liquidDebtAfter).to.be.eq(liquidDebtBeforeCut.add(this.minDelegation.mul(2)));
expect(liquidDebtAfter).to.be.lt(0);
});
});

describe("penaltyDecreasePerWeek()", async function () {
it("should revert setting penalty decrease per week if not governance", async function () {
it("should revert setting penalty decrease per week if not Governance", async function () {
const { hydraDelegation, delegatedValidator } = await loadFixture(this.fixtures.vestedDelegationFixture);

const admin = await hydraDelegation.DEFAULT_ADMIN_ROLE();
Expand All @@ -1726,7 +1896,7 @@ export function RunVestedDelegationTests(): void {
);
});

it("should revert setting penalty decrease per week if amount of of range", async function () {
it("should revert setting penalty decrease per week if amount out of range", async function () {
const { hydraDelegation } = await loadFixture(this.fixtures.vestedDelegationFixture);

await expect(
Expand Down
64 changes: 51 additions & 13 deletions test/HydraStaking/VestedStaking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,44 @@ export function RunVestedStakingTests(): void {
);
});

it("Should manage debt and collect tokens properly for vested staking positions", async function () {
const { systemHydraChain, hydraStaking } = await loadFixture(this.fixtures.stakedValidatorsStateFixture);

const validator = this.signers.validators[0];
const stakedAmount = await hydraStaking.stakeOf(validator.address);

const liquidDebtBeforeNewPosition = await hydraStaking.liquidityDebts(validator.address);
expect(liquidDebtBeforeNewPosition).to.be.equal(0);

await hydraStaking.connect(validator).stakeWithVesting(1, { value: this.minDelegation });
const expectedLiquidTokensFromPosition = calcLiquidTokensToDistributeOnVesting(
1,
stakedAmount.add(this.minDelegation)
);
const liquidDebtForPosition = stakedAmount.add(this.minDelegation).sub(expectedLiquidTokensFromPosition);
expect(await hydraStaking.liquidityDebts(validator.address)).to.be.equal(liquidDebtForPosition.mul(-1));

// commit epochs and increase time to mature the position
await commitEpochs(systemHydraChain, hydraStaking, [validator], 3, this.epochSize, WEEK);

await hydraStaking.connect(validator).stakeWithVesting(52, { value: this.minDelegation.mul(5) });
const expectedLiquidTokensFromNewPosition = calcLiquidTokensToDistributeOnVesting(
52,
this.minDelegation.mul(6).add(stakedAmount)
);
const liquidDebtForNewPosition = this.minDelegation
.mul(6)
.add(stakedAmount)
.sub(expectedLiquidTokensFromNewPosition);
expect(await hydraStaking.liquidityDebts(validator.address)).to.be.equal(liquidDebtForNewPosition.mul(-1));

await hydraStaking.connect(validator).unstake(this.minDelegation);

expect(await hydraStaking.liquidityDebts(validator.address)).to.be.equal(
liquidDebtForNewPosition.sub(this.minDelegation).mul(-1)
);
});

it("should get staker penalty and rewards must return 0 (burned), if closing from active position", async function () {
const { stakerHydraStaking, systemHydraChain, hydraStaking } = await loadFixture(
this.fixtures.newVestingValidatorFixture
Expand Down Expand Up @@ -521,33 +559,33 @@ export function RunVestedStakingTests(): void {
});

describe("penaltyDecreasePerWeek()", async function () {
it("should revert setting penalty decrease per week if not admin", async function () {
const { hydraDelegation, delegatedValidator } = await loadFixture(this.fixtures.vestedDelegationFixture);
it("should revert setting penalty decrease per week if not Governance", async function () {
const { hydraStaking, delegatedValidator } = await loadFixture(this.fixtures.vestedDelegationFixture);

const admin = await hydraDelegation.DEFAULT_ADMIN_ROLE();
const admin = await hydraStaking.DEFAULT_ADMIN_ROLE();

await expect(hydraDelegation.connect(delegatedValidator).setPenaltyDecreasePerWeek(100)).to.be.revertedWith(
await expect(hydraStaking.connect(delegatedValidator).setPenaltyDecreasePerWeek(100)).to.be.revertedWith(
ERRORS.accessControl(delegatedValidator.address.toLocaleLowerCase(), admin)
);
});

it("should revert setting penalty decrease per week if amount of of range", async function () {
const { hydraDelegation } = await loadFixture(this.fixtures.vestedDelegationFixture);
it("should revert setting penalty decrease per week if amount out of range", async function () {
const { hydraStaking } = await loadFixture(this.fixtures.vestedDelegationFixture);

await expect(
hydraDelegation.connect(this.signers.governance).setPenaltyDecreasePerWeek(9)
).to.be.revertedWithCustomError(hydraDelegation, "PenaltyRateOutOfRange");
hydraStaking.connect(this.signers.governance).setPenaltyDecreasePerWeek(9)
).to.be.revertedWithCustomError(hydraStaking, "PenaltyRateOutOfRange");

await expect(
hydraDelegation.connect(this.signers.governance).setPenaltyDecreasePerWeek(151)
).to.be.revertedWithCustomError(hydraDelegation, "PenaltyRateOutOfRange");
hydraStaking.connect(this.signers.governance).setPenaltyDecreasePerWeek(151)
).to.be.revertedWithCustomError(hydraStaking, "PenaltyRateOutOfRange");
});

it("should set penalty decrease per week", async function () {
const { hydraDelegation } = await loadFixture(this.fixtures.vestedDelegationFixture);
const { hydraStaking } = await loadFixture(this.fixtures.vestedDelegationFixture);

await hydraDelegation.connect(this.signers.governance).setPenaltyDecreasePerWeek(100);
expect(await hydraDelegation.penaltyDecreasePerWeek()).to.be.eq(100);
await hydraStaking.connect(this.signers.governance).setPenaltyDecreasePerWeek(100);
expect(await hydraStaking.penaltyDecreasePerWeek()).to.be.eq(100);
});
});
});
Expand Down
1 change: 1 addition & 0 deletions test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const ERRORS = {
vesting: {
invalidEpoch: "INVALID_EPOCH",
wrongRPS: "WRONG_RPS",
previousRPS: "PREVIOUS_RPS",
},
DelegPoolLib: {
invalidParamsIndex: "INVALID_PARAMS_INDEX",
Expand Down
Loading