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 4 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
2 changes: 1 addition & 1 deletion contracts/common/Vesting/Vesting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ abstract contract Vesting is IVesting, Governed, APRCalculatorConnector {
/**
* @inheritdoc IVesting
*/
function setPenaltyDecreasePerWeek(uint256 newRate) external onlyRole(DEFAULT_ADMIN_ROLE) {
function setPenaltyDecreasePerWeek(uint256 newRate) external onlyGovernance {
R-Santev marked this conversation as resolved.
Show resolved Hide resolved
if (newRate < 10 || newRate > 150) revert PenaltyRateOutOfRange();
penaltyDecreasePerWeek = newRate;
}
Expand Down
16 changes: 16 additions & 0 deletions docs/common/Vesting/Vesting.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,20 @@ error PenaltyRateOutOfRange()



### Unauthorized

```solidity
error Unauthorized(string only)
```





#### Parameters

| Name | Type | Description |
|---|---|---|
| only | string | undefined |


181 changes: 174 additions & 7 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
R-Santev marked this conversation as resolved.
Show resolved Hide resolved
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 Down Expand Up @@ -1715,15 +1800,97 @@ 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 liquidDebtBeforeCut = await hydraDelegation.liquidityDebts(vestManager.address);
expect(liquidDebtBeforeNewPosition).to.be.gt(liquidDebtBeforeCut);

// 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.gt(liquidDebtBeforeCut);
expect(liquidDebtAfter).to.be.lt(0);
});
});

describe("penaltyDecreasePerWeek()", 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();

await expect(hydraDelegation.connect(delegatedValidator).setPenaltyDecreasePerWeek(100)).to.be.revertedWith(
ERRORS.accessControl(delegatedValidator.address.toLocaleLowerCase(), admin)
);
await expect(hydraDelegation.connect(delegatedValidator).setPenaltyDecreasePerWeek(100))
.to.be.revertedWithCustomError(hydraDelegation, "Unauthorized")
.withArgs(ERRORS.unauthorized.governanceArg);
});

it("should revert setting penalty decrease per week if amount of of range", async function () {
Expand Down
28 changes: 13 additions & 15 deletions test/HydraStaking/VestedStaking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,33 +521,31 @@ 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();

await expect(hydraDelegation.connect(delegatedValidator).setPenaltyDecreasePerWeek(100)).to.be.revertedWith(
ERRORS.accessControl(delegatedValidator.address.toLocaleLowerCase(), admin)
);
await expect(hydraStaking.connect(delegatedValidator).setPenaltyDecreasePerWeek(100))
.to.be.revertedWithCustomError(hydraStaking, "Unauthorized")
.withArgs(ERRORS.unauthorized.governanceArg);
});

it("should revert setting penalty decrease per week if amount of of range", async function () {
R-Santev marked this conversation as resolved.
Show resolved Hide resolved
const { hydraDelegation } = await loadFixture(this.fixtures.vestedDelegationFixture);
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