31566 - [SC - Medium] Checkpoints wont update block number in point b...
Submitted on May 21st 2024 at 12:27:19 UTC by @copperscrewer for Boost | Alchemix
Report ID: #31566
Report type: Smart Contract
Report severity: Medium
Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/VotingEscrow.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
Voting Escrow Checkpoints won't update block number in point because of a Rounding issue, currently the block time in Ethereum is around ~ 12 seconds, There is an attempt to calculate block slope by the difference of the block number multiplied by a MULTIPLIER and then divided by the difference in block.timestamp.
There is an issue because of the precision of the division.
Vulnerability Details
In _checkpoint of VotingEscrow there is a line
blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - lastPoint.ts);
Here blockSlope will always be zero as for each block.number the difference in block.timestamp is around ~ 12 seconds. The MULTIPLIER : 2 is too low to cover that and as a result it becomes zero as the numerator is lesser than the denominator
lastPoint.blk = initialLastPoint.blk + (blockSlope * (_time - initialLastPoint.ts)) / MULTIPLIER;
as a result will be stagnant and never be updated.
Impact Details
The Point objects used in _checkpoint never have their block value updated. This will affect any off-calculation/computation that relies on the block number for points to make a transaction
Remediation
Increase MULTIPLIER to a much higher value ~ 10000
Proof of Concept
Run forge test --match-test testRounding --fork-url https://eth-mainnet.alchemyapi.io/v2/[API_KETY] -vvv
after pasting this function in VotingEscrow.t.sol
function testRounding() external
{
VotingEscrowTest.Point memory lastPoint;
(int256 a, int256 b, uint256 c, uint256 d) = veALCX.pointHistory(veALCX.epoch());
lastPoint = Point(a,b,c,d);
hevm.warp(block.timestamp + 12);
hevm.roll(block.number + 1);
console.log("block.timestamp : ", block.timestamp, "\tBlock.number : ", block.number);
uint256 blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - lastPoint.ts);
console.log("blockSlope is ", blockSlope);
veALCX.checkpoint();
hevm.warp(block.timestamp + 12);
hevm.roll(block.number + 1);
( a, b, c, d) = veALCX.pointHistory(veALCX.epoch());
lastPoint = Point(a,b,c,d);
console.log("block.timestamp : ", block.timestamp, "\tBlock.number : ", block.number);
blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - lastPoint.ts);
console.log("blockSlope is ", blockSlope);
veALCX.checkpoint();
hevm.warp(block.timestamp + 12*10);
hevm.roll(block.number + 1*10);
( a, b, c, d) = veALCX.pointHistory(veALCX.epoch());
lastPoint = Point(a,b,c,d);
console.log("block.timestamp : ", block.timestamp, "\tBlock.number : ", block.number);
blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - lastPoint.ts);
console.log("blockSlope is ", blockSlope);
veALCX.checkpoint();
}
Last updated
Was this helpful?