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

Cleanup expired schedules #41

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Conversation

itirabasso
Copy link
Collaborator

No description provided.

@itirabasso itirabasso changed the base branch from main to aludelV3Lib December 16, 2022 18:18
@itirabasso itirabasso force-pushed the aludelV3Lib branch 3 times, most recently from a2caeba to 4cbb0df Compare January 16, 2023 19:24
@itirabasso itirabasso force-pushed the cleanup_expired_schedules branch from 9911b02 to bc48be8 Compare January 16, 2023 20:00
@itirabasso itirabasso force-pushed the aludelV3Lib branch 2 times, most recently from d3bdf4d to bb00a8f Compare January 17, 2023 11:26
@itirabasso itirabasso force-pushed the cleanup_expired_schedules branch from bc48be8 to bfb9db2 Compare January 17, 2023 12:09
@itirabasso itirabasso marked this pull request as ready for review January 17, 2023 15:23
@itirabasso itirabasso force-pushed the aludelV3Lib branch 2 times, most recently from 2d4240c to e8532c0 Compare January 25, 2023 11:51
@itirabasso itirabasso force-pushed the cleanup_expired_schedules branch from bfb9db2 to 6e92dd2 Compare January 31, 2023 11:17
Copy link
Collaborator

@plaintextpaco plaintextpaco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks good but it's incomplete.

we should include tests like:

  • a scenario which causes a locked contract with the previous implementation, but which can be recoverable with this one
  • assert internal state of the rewardschedule array
  • perhaps unitarily test the function

also, IMO the cleanup function should be callable directly, with a bound on the amount of iterations, so really bad situations can be recovered from (since adding a rewardSchedule is O(1))


// check if the period is already expired
if (block.timestamp - schedule.start >= schedule.duration) {
// length decreces
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.learncpp.com/cpp-tutorial/comments/

these comments state that which is obvious from looking at the code, they're better suited to explain why the code does what it does

function cleanupExpiredRewardSchedules(
IAludelV3.AludelData storage aludel
) internal {
// check : measure how much gas this actually saves
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like something to implement in this PR 🙃

@itirabasso itirabasso force-pushed the cleanup_expired_schedules branch from 6e92dd2 to 47c33df Compare February 8, 2023 13:57
@itirabasso itirabasso changed the base branch from aludelV3Lib to main February 8, 2023 14:02
@itirabasso itirabasso force-pushed the cleanup_expired_schedules branch 3 times, most recently from 25fa14f to 4da2d5e Compare February 14, 2023 18:38
This should reduce the computational cost of `calculateLockedShares`
because if we remove expired schedules, which are fully unlocked at that
stage, we need to iterate less elements during the calculation.

remove length optimization
if we avoid accessing the array's length this is how the gas-snapshots
changes

```
test_many_users_multiple_stakes() (gas: -211 (-0.013%))
test_funding_shares() (gas: -592 (-0.094%))
test_single_unstake(uint8,uint40,uint256,uint256) (gas: 701603 (6.979%))
Overall gas change: 700800 (2.337%)
```
* test_fund_without_approval
* test_fund_remove_expired_schedules
expired reward schedules are removed after the program is funded again.
@itirabasso itirabasso force-pushed the cleanup_expired_schedules branch from 4da2d5e to 0d0eb3e Compare February 24, 2023 16:59
Copy link
Collaborator

@plaintextpaco plaintextpaco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issues blocking merging are:

  • modularization of tests
  • lack of tests regarding schedule removal, which left bugs slip through

@@ -28,9 +28,9 @@ import {Utils} from "./Utils.sol";
import {UserFactory} from "./UserFactory.sol";

import "forge-std/console2.sol";
import "forge-std/StdUtils.sol";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import individual names used or put the package into a name to avoid polluting the namespace

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand this concern for contracts that are deployed but I don't see the point of enforcing this in tests.
I would prefer to have the whole package available instead of having to specify each of the names I want to use.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do import "forge-std/StdUtils" as STD and access every exported thing as STD.thing

this'd not pollute the global namespace, and would make explicit where every usage comes from. In that way, if I want to find the docs for a particular function when reviewing the code or taking a look at it some time later, I can easily figure out where to look, whereas if the name is in the global namespace I'd have to do a fair bit more guessing

src/contracts/aludel/AludelV3Lib.sol Show resolved Hide resolved
src/test/AludelV3.t.sol Show resolved Hide resolved

IAludelV3.AludelData memory data = aludel.getAludelData();
// before fund there should be no schedules
assertEq(data.rewardSchedules.length, 0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like an assert that you added when playing around with the codebase to see what would work, and not one conclusive to test_fund_remove_expired_schedules

vm.warp(block.timestamp + 1);
aludel.fund(reward, duration);
// so there should be two schedules
assertEq(aludel.getAludelData().rewardSchedules.length, 2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the body of the test doesn't fully relate to its description. it states that fund should remove expired schedules yet none is removed.

}
}

contract Given_A_New_Aludel_Is_Being_Funded is Test {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This contract has a lot of code duplication with AludelV3Test

Please create a 3rd contract with the setup common to both and make them extend the setUp method and add whatever extra state variables are necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this, but I was hesitant to add it right away.
Would you agree to have a few more iterations of these test contracts before creating the common contract for all tests?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not saying we should have a common contract for all tests, that feels like premature optimization given the current state of affairs, I was talking about just this testfile.

If you look at both contracts side by side, you can see the repetition easily:
image

I don't like the idea of merging so much repeated code, but I wouldn't mind it in a draft PR

src/test/AludelV3.t.sol Show resolved Hide resolved
src/test/AludelV3.t.sol Show resolved Hide resolved
{
reward = bound(reward, 1, MAX_REWARD);
duration = bound(duration, 1, 1000 days);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why all the whitespace?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't mind it.
if neither forge fmt nor lint-sol cries about it, I'll just go with it.

Copy link
Collaborator

@plaintextpaco plaintextpaco Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for me the whitespace in source code contributes to how I understand it, I expect empty lines to have a similar function to the paragraph separation in natural languages, and when I see two emtpy lines together I expect some big context switch, such as in sections in a book. (or, say, two different contracts, or public vs internal functions of the same contract)

Having the whitespace mean nothing at all (such as having 1-2 emtpy lines between code that accomplishes the same thing) adds a bit of cognitive noise to processing code for me.

this, for example, is how this function makes most sense to me:

        reward = bound(reward, 1, MAX_REWARD);
        duration = bound(duration, 1, 1000 days);
        Utils.fundMockToken(admin.addr(), rewardToken, reward);
        rewardToken.approve(address(aludel), reward);

        aludel.fund(reward, duration);
        assertEq(aludel.getAludelData().rewardSharesOutstanding, reward * BASE_SHARES_PER_WEI);

... or it could have no empty lines at all. This whitespace pattern is what I usually prefer for asserting reverts, now that I think about it.

The whitespace of this particular function is not a hill I die on, but I expect it to mean something

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants