The contract maintains two separate data structures for tracking lock information:
When a user creates a lock via the lock() function, the contract correctly updates the _lockState variable:
However, the direct locks mapping is never updated. This becomes problematic when the getLockEndTime() function is called:
This function reads from the locks mapping rather than from _lockState, resulting in it always returning 0 (the default value) instead of the actual lock end time.
This inconsistency leads to incorrect data being returned from the getLockEndTime() function, which could mislead users or integrated protocols that rely on this function to determine when locks expire. However, since the getLockPosition().end works correctly and provides the same information, and the getLockEndTime() function does not appear to be used in critical protocol logic, the practical impact is minimal. This is a low severity issue that represents a correctness bug rather than a security vulnerability.
pragma solidity ^0.8.19;
import "contracts/core/tokens/RToken.sol";
import "contracts/core/tokens/RAACToken.sol";
import "contracts/core/tokens/veRAACToken.sol";
import "contracts/core/collectors/FeeCollector.sol";
import "contracts/core/governance/boost/BoostController.sol";
import "contracts/core/governance/gauges/BaseGauge.sol";
import "contracts/core/governance/gauges/GaugeController.sol";
import "contracts/core/governance/gauges/RAACGauge.sol";
import "contracts/core/governance/gauges/RWAGauge.sol";
import "contracts/core/governance/proposals/Governance.sol";
import "contracts/core/governance/proposals/TimelockController.sol";
import {Test, console} from "forge-std/Test.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MockERC20 is ERC20 {
constructor() ERC20("Mock", "MCK") {}
function mint(address to, uint256 amount) public {
_mint(to, amount);
}
}
contract MasterGovernanceTest is Test {
RToken public rToken;
RAACToken public raacToken;
veRAACToken public veRaacToken;
MockERC20 public mockCrvUSD;
address public protocolOwner = makeAddr("ProtocolOwner");
address public rTokenMinter = makeAddr("rTokenMinter");
address public rTokenBurner = makeAddr("rTokenBurner");
address public raacTokenMinter = makeAddr("RAACTokenMinter");
address public veRaacTokenMinter = makeAddr("veRaacTokenMinter");
address public Alice = makeAddr("Alice");
address public Bob = makeAddr("Bob");
function setUp() public {
vm.startPrank(protocolOwner);
mockCrvUSD = new MockERC20();
rToken = new RToken(
"RToken",
"RTKN",
protocolOwner,
address(mockCrvUSD)
);
rToken.setMinter(rTokenMinter);
rToken.setBurner(rTokenBurner);
raacToken = new RAACToken(
protocolOwner,
100,
50
);
raacToken.setMinter(raacTokenMinter);
veRaacToken = new veRAACToken(address(raacToken));
veRaacToken.setMinter(veRaacTokenMinter);
raacToken.manageWhitelist(address(veRaacToken), true);
vm.stopPrank();
}
function testFail_GetLockEndTimeNotWorking() public {
vm.startPrank(raacTokenMinter);
raacToken.mint(Alice, 1000e18);
vm.stopPrank();
vm.startPrank(Alice);
raacToken.approve(address(veRaacToken), type(uint256).max);
uint256 initialLockAmount = 500e18;
uint256 initialLockDuration = 365 days;
veRaacToken.lock(initialLockAmount, initialLockDuration);
IveRAACToken.LockPosition memory firstLockPosition = veRaacToken.getLockPosition(Alice);
console.log("First lock - End time:", firstLockPosition.end);
uint256 getLockTimeReturn = veRaacToken.getLockEndTime(Alice);
assertEq(getLockTimeReturn, firstLockPosition.end);
}
}
├─ [1530] veRAACToken::getLockPosition(Alice: [0xBf0b5A4099F0bf6c8bC4252eBeC548Bae95602Ea]) [staticcall]
│ └─ ← [Return] LockPosition({ amount: 500000000000000000000 [5e20], end: 31536001 [3.153e7], power: 125000000000000000000 [1.25e20] })
├─ [0] console::log("First lock - End time:", 31536001 [3.153e7]) [staticcall]
│ └─ ← [Stop]
├─ [2655] veRAACToken::getLockEndTime(Alice: [0xBf0b5A4099F0bf6c8bC4252eBeC548Bae95602Ea]) [staticcall]
│ └─ ← [Return] 0
├─ [0] VM::assertEq(0, 31536001 [3.153e7]) [staticcall]
│ └─ ← [Revert] assertion failed: 0 != 31536001
└─ ← [Revert] assertion failed: 0 != 31536001