-
Notifications
You must be signed in to change notification settings - Fork 0
Migration Vesting Vault #1
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
Merged
jalextowle
merged 7 commits into
main
from
jalextowle/migration/migration-vesting-vault
Feb 25, 2025
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
1ae60b1
Wrote a migration vesting vault
jalextowle e333e15
Use custom errors in the MigrationVestingVault
jalextowle 24b1036
Started work on the migration vault tests
jalextowle 0d3c5a1
Fixed remaining tests
jalextowle 9c90c82
Pull from the treasury instead of having to deposit HD
jalextowle e10bd36
Adds some more tests
jalextowle c5eb1f0
Addressed review feedback from @jrhea and @Sean392
jalextowle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| pragma solidity 0.8.24; | ||
|
|
||
| import { IERC20 } from "council/interfaces/IERC20.sol"; | ||
| import { IVotingVault } from "council/interfaces/IVotingVault.sol"; | ||
| import { History } from "council/libraries/History.sol"; | ||
| import { VestingVaultStorage } from "council/libraries/VestingVaultStorage.sol"; | ||
| import { Storage } from "council/libraries/Storage.sol"; | ||
| import { AbstractVestingVault } from "council/vaults/VestingVault.sol"; | ||
|
|
||
| /// @title MigrationVestingVault | ||
| /// @notice A migration vault that converts ELFI tokens to HD tokens. Migrated | ||
| /// tokens are granted with a linear vesting schedule. The grant is | ||
| /// created at a destination address provided by the migrator. This | ||
| /// contract inherits full voting power tracking from | ||
| /// `AbstractVestingVault`. | ||
| contract MigrationVestingVault is AbstractVestingVault { | ||
| using History for History.HistoricalBalances; | ||
|
|
||
| /// @dev Thrown when an existing grant is found. | ||
| error ExistingGrantFound(); | ||
|
|
||
| /// @dev Thrown when ELFI transfers fail. | ||
| error ElfiTransferFailed(); | ||
|
|
||
| /// @dev Thrown when there are insufficient HD tokens. | ||
| error InsufficientHDTokens(); | ||
|
|
||
| /// @dev The HD treasury that is funding this migration contract. | ||
| address public immutable hdTreasury; | ||
|
|
||
| /// @dev The ELFI token to migrate from. | ||
| IERC20 public immutable elfiToken; | ||
|
|
||
| /// @dev The conversion rate from ELFI to HD. | ||
| uint256 public immutable conversionMultiplier; | ||
|
|
||
| /// @dev The global start block at which all grants start vesting. | ||
| uint256 public immutable startBlock; | ||
|
|
||
| /// @dev The global expiration block at which all grants fully vest. | ||
| uint256 public immutable expiration; | ||
|
|
||
| /// @notice Constructs the migration vault. | ||
| /// @param _hdTreasury The HD treasury that is funding this migration | ||
| /// contract. | ||
| /// @param _hdToken The ERC20 token to be vested (HD token). | ||
| /// @param _elfiToken The ERC20 token to migrate from (ELFI token). | ||
| /// @param _stale The stale block lag used in voting power calculations. | ||
| /// @param _conversionMultiplier The conversion multiplier from ELFI to HD. | ||
| /// @param _startBlock The global start block for all grants. | ||
| /// @param _expiration The global expiration block for all grants. | ||
| constructor( | ||
| address _hdTreasury, | ||
| IERC20 _hdToken, | ||
| IERC20 _elfiToken, | ||
| uint256 _stale, | ||
| uint256 _conversionMultiplier, | ||
| uint256 _startBlock, | ||
| uint256 _expiration | ||
| ) AbstractVestingVault(_hdToken, _stale) { | ||
| hdTreasury = _hdTreasury; | ||
| elfiToken = _elfiToken; | ||
| conversionMultiplier = _conversionMultiplier; | ||
| startBlock = _startBlock; | ||
| expiration = _expiration; | ||
| } | ||
|
|
||
| /// @notice Migrates a specified amount of ELFI tokens into a vesting grant of HD tokens. | ||
| /// @dev The caller must have approved this contract for the ELFI token amount. | ||
| /// The destination address must not have an existing grant. | ||
| /// @param amount The number of tokens to migrate (in ELFI units). | ||
| /// @param destination The address at which the vesting grant will be created. | ||
| function migrate(uint256 amount, address destination) external { | ||
| // Ensure the destination does not already have an active grant. | ||
| VestingVaultStorage.Grant storage existingGrant = _grants()[destination]; | ||
| if (existingGrant.allocation != 0) { | ||
| revert ExistingGrantFound(); | ||
| } | ||
|
|
||
| // Transfer ELFI tokens from the caller to this contract. | ||
| if (!elfiToken.transferFrom(msg.sender, address(this), amount)) { | ||
| revert ElfiTransferFailed(); | ||
| } | ||
|
|
||
| // Calculate the HD token amount to be granted. | ||
| uint256 hdAmount = amount * conversionMultiplier; | ||
|
|
||
| // Pull the HD tokens from the source. | ||
| if (!token.transferFrom(hdTreasury, address(this), hdAmount)) { | ||
| revert InsufficientHDTokens(); | ||
| } | ||
|
|
||
| // Calculate the initial voting power using the current unvested multiplier. | ||
| Storage.Uint256 memory unvestedMultiplier = _unvestedMultiplier(); | ||
| uint128 initialVotingPower = uint128((hdAmount * uint128(unvestedMultiplier.data)) / 100); | ||
|
|
||
| // Create the grant at the destination address. | ||
| _grants()[destination] = VestingVaultStorage.Grant({ | ||
| allocation: uint128(hdAmount), | ||
| withdrawn: 0, | ||
| created: uint128(startBlock), | ||
| expiration: uint128(expiration), | ||
| cliff: uint128(startBlock), // vesting starts immediately | ||
| latestVotingPower: initialVotingPower, | ||
| delegatee: destination, | ||
| range: [uint256(0), uint256(0)] | ||
| }); | ||
|
|
||
| // Update the destination's voting power. | ||
| History.HistoricalBalances memory votingPower = History.load("votingPower"); | ||
| votingPower.push(destination, initialVotingPower); | ||
| emit VoteChange(destination, destination, int256(uint256(initialVotingPower))); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens to the extra tokens pulled from treasury if someone withdraws early?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm actually, they should just continue vesting. sorry, just thinking outloud
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, thinking out loud is good