Skip to content

Commit

Permalink
feat: migration functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jordaniza committed Dec 13, 2024
1 parent ae21ce9 commit 7cf3d68
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 10 deletions.
81 changes: 81 additions & 0 deletions src/escrow/increasing/VotingEscrowIncreasing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,87 @@ contract VotingEscrow is

bool private _lockNFTSet;

/*//////////////////////////////////////////////////////////////
Added: V2
//////////////////////////////////////////////////////////////*/

/// @notice Emitted when the user migrates to the new destination contract
/// @param owner The owner of the veNFT at the time of the migration
/// @param oldTokenId TokenId burned in the old staking contract
/// @param newTokenId TokenId minted in the new staking contract
/// @param amount The locked amount migrated between contracts
event Migrated(
address indexed owner,
uint256 indexed oldTokenId,
uint256 indexed newTokenId,
uint256 amount
);

/// @notice Emitted when the migrator is added, activating the migration
event MigrationEnabled(address migrator);

error MigrationAlreadySet();
error MigrationNotActive();

/// @notice The destination staking contract can add this to allow another address to call
/// the migrate function on it
bytes32 public constant MIGRATOR_ROLE = keccak256("MIGRATOR");

/// @notice destination migration contract
address public migrator;

/// @notice The Escrow Admin can enable migrations by setting a destination migration contract.
/// @dev This function also approves all tokens in this contract to be transferred to the new migrator.
/// @param _migrator The address of the destination migration contract
function enableMigration(address _migrator) external auth(ESCROW_ADMIN_ROLE) {
if (migrator != address(0)) revert MigrationAlreadySet();
migrator = _migrator;
IERC20(token).approve(migrator, totalLocked);
emit MigrationEnabled(_migrator);
}

/// @notice Defined on the staking contract being exited from - burn the tokenId and mint a new one.
/// @dev Skips withdrawal queue logic and vote resets
/// @param _tokenId veNFT to migrate from
/// @return newTokenId veNFT created during the migrationg
function migrateFrom(uint256 _tokenId) external returns (uint256 newTokenId) {
// check the migration contract is set and the tokenid is active
if (migrator == address(0)) revert MigrationNotActive();
if (votingPower(_tokenId) == 0) revert CannotExit();

// the user should be approved
address owner = IERC721EMB(lockNFT).ownerOf(_tokenId);

LockedBalance memory oldLocked = _locked[_tokenId];
uint256 value = oldLocked.amount;

// burn the current veNFT and write a zero checkpoint.
_locked[_tokenId] = LockedBalance(0, 0);
totalLocked -= value;
_checkpointClear(_tokenId);
IERC721EMB(lockNFT).burn(_tokenId);

// createLockFor on the new contract for the owner of the veNFT
newTokenId = VotingEscrow(migrator).migrateTo(value, owner);

// emit the migrated event
emit Migrated(owner, _tokenId, newTokenId, value);

return newTokenId;
}

/// @notice Defined on the destination staking contract. Creates a new veNFT with the old params
/// @dev Skips validations like pause, allowing migration ahead of general release.
/// @param _value The amount of underlying token to be migrated.
/// @param _for The original owner of the lock
/// @return newTokenId the veNFT on the destination staking contract
function migrateTo(
uint256 _value,
address _for
) external nonReentrant auth(MIGRATOR_ROLE) returns (uint256 newTokenId) {
return _createLockFor(_value, _for);
}

/*//////////////////////////////////////////////////////////////
Initialization
//////////////////////////////////////////////////////////////*/
Expand Down
15 changes: 5 additions & 10 deletions src/libs/CurveConstantLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,14 @@ pragma solidity ^0.8.0;

/// @title CurveConstantLib
/// @notice Precomputed coefficients for escrow curve
/// This curve implementation is a quadratic curve of the form y = (1/7)t^2 + (2/7)t + 1
/// Which is a transformation of the quadratic curve y = (x^2 + 6)/7
/// That starts with 1 unit of voting in period 1, and max 6 in period 6.
/// To use this in zero indexed time, with a per-second rate of increase,
/// we transform this to the polynomial y = (1/7)t^2 + (2/7)t + 1
/// where t = timestamp / 2_weeks (2 weeks is one period)
/// Below are the shared coefficients for the linear and quadratic terms
/// That starts with 1 unit of voting in period 1, and max 6 in period 6, increasing linearly
library CurveConstantLib {
int256 internal constant SHARED_CONSTANT_COEFFICIENT = 1e18;
/// @dev 2 / (7 * 2_weeks) - expressed in fixed point
/// @dev rate of increase per second expressed in fixed point
int256 internal constant SHARED_LINEAR_COEFFICIENT = 236205593348;
/// @dev 1 / (7 * (2_weeks)^2) - expressed in fixed point
int256 internal constant SHARED_QUADRATIC_COEFFICIENT = 97637;

/// @dev linear curve with zero quadratic term
int256 internal constant SHARED_QUADRATIC_COEFFICIENT = 0;

/// @dev the maxiumum number of epochs the cure can keep increasing
uint256 internal constant MAX_EPOCHS = 5;
Expand Down

0 comments on commit 7cf3d68

Please sign in to comment.