Skip to content

Commit

Permalink
Feat: locking l2 upgrade (DON'T MERGE) (#542)
Browse files Browse the repository at this point in the history
### Description

Updates Locking Contracts to handle upcoming block.time change

Mainly changes the roundTimestamp() to have a forked implementation that
uses different values to calculate week no for the blocks before and
after l2 transition. Week numbers that return from roundTimestamp before
and after should be continues and consistent.

### Other changes

No

### Tested

Unit and fork tests

### Related issues

- Fixes #538

---------

Co-authored-by: philbow61 <[email protected]>
  • Loading branch information
baroooo and philbow61 authored Dec 6, 2024
1 parent 20fc515 commit 218ed8c
Show file tree
Hide file tree
Showing 9 changed files with 824 additions and 27 deletions.
12 changes: 6 additions & 6 deletions contracts/governance/locking/Locking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ contract Locking is ILocking, LockingBase, LockingRelock, LockingVotes {
counter++;

uint32 currentBlock = getBlockNumber();
uint32 time = roundTimestamp(currentBlock);
uint32 time = getWeekNumber(currentBlock);
addLines(account, _delegate, amount, slopePeriod, cliff, time, currentBlock);
accounts[account].amount = accounts[account].amount + (amount);

Expand Down Expand Up @@ -92,7 +92,7 @@ contract Locking is ILocking, LockingBase, LockingRelock, LockingVotes {
function getAvailableForWithdraw(address account) public view returns (uint96) {
uint96 value = accounts[account].amount;
uint32 currentBlock = getBlockNumber();
uint32 time = roundTimestamp(currentBlock);
uint32 time = getWeekNumber(currentBlock);
uint96 bias = accounts[account].locked.actualValue(time, currentBlock);
value = value - (bias);
return value;
Expand Down Expand Up @@ -124,7 +124,7 @@ contract Locking is ILocking, LockingBase, LockingRelock, LockingVotes {
* since the starting point week. The starting point is set during the contract initialization.
*/
function getWeek() external view returns (uint256) {
return roundTimestamp(getBlockNumber());
return getWeekNumber(getBlockNumber());
}

/**
Expand All @@ -139,7 +139,7 @@ contract Locking is ILocking, LockingBase, LockingRelock, LockingVotes {
address account = verifyLockOwner(id);
address _delegate = locks[id].delegate;
uint32 currentBlock = getBlockNumber();
uint32 time = roundTimestamp(currentBlock);
uint32 time = getWeekNumber(currentBlock);
accounts[_delegate].balance.update(time);
(uint96 bias, uint96 slope, uint32 cliff) = accounts[_delegate].balance.remove(id, time, currentBlock);
LibBrokenLine.Line memory line = LibBrokenLine.Line(time, bias, slope, cliff);
Expand All @@ -158,7 +158,7 @@ contract Locking is ILocking, LockingBase, LockingRelock, LockingVotes {
return 0;
}
uint32 currentBlock = getBlockNumber();
uint32 time = roundTimestamp(currentBlock);
uint32 time = getWeekNumber(currentBlock);
return totalSupplyLine.actualValue(time, currentBlock);
}

Expand All @@ -172,7 +172,7 @@ contract Locking is ILocking, LockingBase, LockingRelock, LockingVotes {
return 0;
}
uint32 currentBlock = getBlockNumber();
uint32 time = roundTimestamp(currentBlock);
uint32 time = getWeekNumber(currentBlock);
return accounts[account].balance.actualValue(time, currentBlock);
}

Expand Down
159 changes: 145 additions & 14 deletions contracts/governance/locking/LockingBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ import "./libs/LibBrokenLine.sol";
abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
using LibBrokenLine for LibBrokenLine.BrokenLine;
/**
* @dev Duration of a week in blocks on the CELO blockchain assuming 5 seconds per block
* @dev Duration of a week in blocks on the CELO blockchain before the L2 transition (5 seconds per block)
*/
uint32 public constant WEEK = 120_960;
/**
* @dev Duration of a week in blocks on the CELO blockchain after the L2 transition (1 seconds per block)
*/
uint32 public constant L2_WEEK = 604_800;
/**
* @dev Epoch shift for L1
*/
uint32 public constant L1_EPOCH_SHIFT = 89964;
/**
* @dev Maximum allowable cliff period for token locks in weeks
*/
Expand Down Expand Up @@ -84,6 +92,31 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
* @dev Total supply line of veMento
*/
LibBrokenLine.BrokenLine public totalSupplyLine;

// ***************
// New variables for L2 transition upgrade (3 slots)
// ***************
/**
* @dev L2 transition block number
*/
uint256 public l2TransitionBlock;
/**
* @dev L2 starting point week number
*/
int256 public l2StartingPointWeek;
/**
* @dev Shift amount used after L2 transition to move the start of the epoch to 00-00 UTC Wednesday (approx)
*/
uint32 public l2EpochShift;
/**
* @dev Address of the Mento Labs multisig
*/
address public mentoLabsMultisig;
/**
* @dev Flag to pause locking and governance
*/
bool public paused;

/**
* @dev Emitted when create Lock with parameters (account, delegate, amount, slopePeriod, cliff)
*/
Expand Down Expand Up @@ -125,6 +158,26 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
* @dev set newMinSlopePeriod
*/
event SetMinSlopePeriod(uint256 indexed newMinSlopePeriod);
/**
* @dev set new Mento Labs multisig address
*/
event SetMentoLabsMultisig(address indexed mentoLabsMultisig);
/**
* @dev set new L2 transition block number
*/
event SetL2TransitionBlock(uint256 indexed l2TransitionBlock);
/**
* @dev set new L2 shift amount
*/
event SetL2EpochShift(uint32 indexed l2EpochShift);
/**
* @dev set new L2 starting point week number
*/
event SetL2StartingPointWeek(int256 indexed l2StartingPointWeek);
/**
* @dev set new paused flag
*/
event SetPaused(bool indexed paused);

/**
* @dev Initializes the contract with token, starting point week, and minimum cliff and slope periods.
Expand All @@ -149,6 +202,11 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
minSlopePeriod = _minSlopePeriod;
}

modifier onlyMentoLabs() {
require(msg.sender == mentoLabsMultisig, "caller is not MentoLabs multisig");
_;
}

/**
* @notice Adds a new locking line for an account, initializing the lock with specified parameters.
* @param account Address for which tokens are being locked.
Expand Down Expand Up @@ -221,7 +279,7 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
* @param amount of tokens to lock
* @param slopePeriod period over which the tokens will unlock
* @param cliff initial period during which tokens remain locked and do not start unlocking
**/
*/
function getLock(
uint96 amount,
uint32 slopePeriod,
Expand Down Expand Up @@ -256,23 +314,46 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {

/**
* @notice Calculates the week number for a given blocknumber
* @param ts block number
* @dev It takes L2 transition into account to calculate the week number consistently
* @param blockNumber block number to calculate the week number for
* @return week number the block number belongs to
*/
function roundTimestamp(uint32 ts) public view returns (uint32) {
if (ts < getEpochShift()) {
function getWeekNumber(uint32 blockNumber) public view returns (uint32) {
require(!paused, "locking is paused");

if (blockNumber < _getEpochShift(blockNumber)) {
return 0;
}
uint32 shifted = ts - (getEpochShift());
return shifted / WEEK - uint32(startingPointWeek);
uint32 shifted = blockNumber - _getEpochShift(blockNumber);

if (_isPreL2Transition(blockNumber)) {
return shifted / WEEK - uint32(startingPointWeek);
} else {
return uint32(uint256(int256(uint256(shifted / L2_WEEK)) - l2StartingPointWeek));
}
}

/**
* @notice Returns the epoch shift based on L2 transition status
* @dev Epoch shift is the amount of blocks to move the epoch start to 00-00 UTC Wednesday (approx).
* @dev l2EpochShift will be moved into a constant once L2 transition is complete and stable.
* @param blockNumber block number to calculate the shift for
* @return shift amount in blocks (L1_EPOCH_SHIFT for L1, l2EpochShift for L2)
*/
function _getEpochShift(uint32 blockNumber) internal view virtual returns (uint32) {
if (_isPreL2Transition(blockNumber)) {
return L1_EPOCH_SHIFT;
}
return l2EpochShift;
}

/**
* @notice method returns the amount of blocks to shift locking epoch to.
* we move it to 00-00 UTC Wednesday (approx) by shifting 89964 blocks (CELO)
* @notice Determines if a block is before the L2 transition point
* @param blockNumber block number to check
* @return true if before L2 transition, false if after
*/
function getEpochShift() internal view virtual returns (uint32) {
return 89964;
function _isPreL2Transition(uint32 blockNumber) internal view returns (bool) {
return l2TransitionBlock == 0 || blockNumber < l2TransitionBlock;
}

/**
Expand Down Expand Up @@ -339,7 +420,7 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
* @param blockNumber block number until which to update lines
*/
function updateAccountLinesBlockNumber(address account, uint32 blockNumber) external onlyOwner {
uint32 time = roundTimestamp(blockNumber);
uint32 time = getWeekNumber(blockNumber);
updateAccountLines(account, time);
}

Expand All @@ -348,9 +429,59 @@ abstract contract LockingBase is OwnableUpgradeable, IVotesUpgradeable {
* @param blockNumber block number until which to update line
*/
function updateTotalSupplyLineBlockNumber(uint32 blockNumber) external onlyOwner {
uint32 time = roundTimestamp(blockNumber);
uint32 time = getWeekNumber(blockNumber);
updateTotalSupplyLine(time);
}

uint256[50] private __gap;
/**
* @notice Sets the Mento Labs multisig address
* @param mentoLabsMultisig_ address of the Mento Labs multisig
*/
function setMentoLabsMultisig(address mentoLabsMultisig_) external onlyOwner {
mentoLabsMultisig = mentoLabsMultisig_;
emit SetMentoLabsMultisig(mentoLabsMultisig_);
}

/**
* @notice Sets the L2 transition block number and pauses locking and governance
* @param l2TransitionBlock_ block number of the L2 transition
*/
function setL2TransitionBlock(uint256 l2TransitionBlock_) external onlyMentoLabs {
l2TransitionBlock = l2TransitionBlock_;
paused = true;

emit SetL2TransitionBlock(l2TransitionBlock_);
}

/**
* @notice Sets the L2 epoch shift amount
* @param l2EpochShift_ shift amount that will be used after L2 transition
*/
function setL2EpochShift(uint32 l2EpochShift_) external onlyMentoLabs {
l2EpochShift = l2EpochShift_;

emit SetL2EpochShift(l2EpochShift_);
}

/**
* @notice Sets the L2 starting point week number
* @param l2StartingPointWeek_ starting point week number that will be used after L2 transition
*/
function setL2StartingPointWeek(int256 l2StartingPointWeek_) external onlyMentoLabs {
l2StartingPointWeek = l2StartingPointWeek_;

emit SetL2StartingPointWeek(l2StartingPointWeek_);
}

/**
* @notice Sets the paused flag
* @param paused_ flag to pause locking and governance
*/
function setPaused(bool paused_) external onlyMentoLabs {
paused = paused_;

emit SetPaused(paused_);
}

uint256[47] private __gap;
}
2 changes: 1 addition & 1 deletion contracts/governance/locking/LockingRelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ abstract contract LockingRelock is LockingBase {

address account = verifyLockOwner(id);
uint32 currentBlock = getBlockNumber();
uint32 time = roundTimestamp(currentBlock);
uint32 time = getWeekNumber(currentBlock);
verification(account, id, newAmount, newSlopePeriod, newCliff, time);

address _delegate = locks[id].delegate;
Expand Down
6 changes: 3 additions & 3 deletions contracts/governance/locking/LockingVotes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract LockingVotes is LockingBase {
*/
function getVotes(address account) external view override returns (uint256) {
uint32 currentBlock = getBlockNumber();
uint32 currentWeek = roundTimestamp(currentBlock);
uint32 currentWeek = getWeekNumber(currentBlock);
return accounts[account].balance.actualValue(currentWeek, currentBlock);
}

Expand All @@ -24,7 +24,7 @@ contract LockingVotes is LockingBase {
* at the end of the last period
*/
function getPastVotes(address account, uint256 blockNumber) external view override returns (uint256) {
uint32 currentWeek = roundTimestamp(uint32(blockNumber));
uint32 currentWeek = getWeekNumber(uint32(blockNumber));
require(blockNumber < getBlockNumber() && currentWeek > 0, "block not yet mined");

return accounts[account].balance.actualValue(currentWeek, uint32(blockNumber));
Expand All @@ -35,7 +35,7 @@ contract LockingVotes is LockingBase {
* at the end of the last period
*/
function getPastTotalSupply(uint256 blockNumber) external view override returns (uint256) {
uint32 currentWeek = roundTimestamp(uint32(blockNumber));
uint32 currentWeek = getWeekNumber(uint32(blockNumber));
require(blockNumber < getBlockNumber() && currentWeek > 0, "block not yet mined");

return totalSupplyLine.actualValue(currentWeek, uint32(blockNumber));
Expand Down
3 changes: 3 additions & 0 deletions test/fork/ForkTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { BancorExchangeProviderForkTest } from "./BancorExchangeProviderForkTest
import { GoodDollarTradingLimitsForkTest } from "./GoodDollar/TradingLimitsForkTest.sol";
import { GoodDollarSwapForkTest } from "./GoodDollar/SwapForkTest.sol";
import { GoodDollarExpansionForkTest } from "./GoodDollar/ExpansionForkTest.sol";
import { LockingUpgradeForkTest } from "./upgrades/LockingUpgradeForkTest.sol";

contract Alfajores_ChainForkTest is ChainForkTest(ALFAJORES_ID, 1, uints(16)) {}

Expand Down Expand Up @@ -120,3 +121,5 @@ contract Celo_GoodDollarTradingLimitsForkTest is GoodDollarTradingLimitsForkTest
contract Celo_GoodDollarSwapForkTest is GoodDollarSwapForkTest(CELO_ID) {}

contract Celo_GoodDollarExpansionForkTest is GoodDollarExpansionForkTest(CELO_ID) {}

contract Celo_LockingUpgradeForkTest is LockingUpgradeForkTest(CELO_ID) {}
Loading

0 comments on commit 218ed8c

Please sign in to comment.