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 VestingWalletWithCliff #4870

Merged

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Feb 2, 2024

... an extension of VestingWallet with an added cliff in the release schedule

Fixes #4701
Fixes LIB-1190

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

Copy link

changeset-bot bot commented Feb 2, 2024

🦋 Changeset detected

Latest commit: 188d86e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Amxx Amxx added this to the 5.1 milestone Feb 7, 2024
@Amxx
Copy link
Collaborator Author

Amxx commented Feb 8, 2024

Following discussion, moving to a model where the extension is an abstract contract that needs to be inherited/compiled.

.changeset/wise-bobcats-speak.md Outdated Show resolved Hide resolved
contracts/finance/VestingWalletWithCliff.sol Outdated Show resolved Hide resolved
uint256 totalAllocation,
uint64 timestamp
) internal view virtual override returns (uint256) {
return timestamp < cliff() ? 0 : super._vestingSchedule(totalAllocation, timestamp);
Copy link
Member

Choose a reason for hiding this comment

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

This is not always calling super, and it's part of the discussion we've had before in regards to always call super.

In this case it harmless, so it doesn't make sense to call super every time since the ternary operator is extending the original branching in _vestingWallet, making it equivalent to:

function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
    if (timestamp < cliff() || timestamp < start()) {
        return 0;
    } else if (timestamp >= end()) {
        return totalAllocation;
    } else {
        return (totalAllocation * (timestamp - start())) / duration();
    }
}

I think it doesn't make sense to call super every time here, but if _vestingWallet is overridden by an user and it includes a check or an state update, it might be an issue.

I don't have a strong opinion. But I'm leaning towards not calling super. What do you think?

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 think not caller super at all is dangerous. Lets discuss that next week.

contracts/finance/VestingWalletWithCliff.sol Outdated Show resolved Hide resolved
* @dev Sets the sender as the initial owner, the beneficiary as the pending owner, the start timestamp, the
* vesting duration and the duration of the cliff of the vesting wallet.
*/
constructor(uint64 cliffSeconds) {
Copy link
Member

Choose a reason for hiding this comment

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

I see durationSeconds is used in VestingWallet, but there it's straightforward because the parameter is the actual duration. In the case of cliffSeconds, it's added over the start, which may seem confusing.

We can:

  • Rename cliffSeconds to cliffDurationSeconds
  • Rename the cliff() function to cliffTimestamp

Leaning towards number 1:

Suggested change
constructor(uint64 cliffSeconds) {
constructor(uint64 cliffDurationSeconds) {

contracts/finance/VestingWalletWithCliff.sol Outdated Show resolved Hide resolved
test/finance/VestingWalletWithCliff.test.js Outdated Show resolved Hide resolved
ernestognw
ernestognw previously approved these changes Feb 13, 2024
Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

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

I've been thinking a lot about the _vestingSchedule not calling super, and even tried to think about implementing the cliff in terms of the _vestingSchedule, but also realized that allows an attacker to extend the cliff undefinitely if they have enough resources.

So far I think this is the best approach, and also, if the "cliff" also means ignoring the scheduling function at all, then it makes sense to return 0 directly.

I added a note about this to _vestingWallet, since in the end, this is a threat if they ever have to dissambiguate the inheritance graph, so ideally they should take a look at this and decide whether or not they're okay with the cliff implementation.

@Amxx Amxx merged commit ae1bafc into OpenZeppelin:master Feb 13, 2024
16 checks passed
@Amxx Amxx deleted the feature/finance/vesting-wallet-with-cliff branch February 13, 2024 09:01
@urataps
Copy link

urataps commented May 17, 2024

Why vesting wallet cliff is not available in the v5.0.2 version? Can't import it in my contracts.

@Amxx
Copy link
Collaborator Author

Amxx commented May 17, 2024

@urataps This PR was not released yet. It will be in 5.1

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.

"Vesting Cliff" feature for VestingWallet
3 participants