Summary
The createMerkleLT function lacks checks to ensure that tranchesWithPercentages.duration is not zero and that tranchesWithPercentages.length is greater than 0.
If tranches.length is zero or tranchesWithPercentages.duration is zero, it becomes impossible to create the stream, as verified by the following checks:
checkCreateLockupTranched
_checkTranches
This can lead to the creation of a non-functional SablierV2MerkleLT contract.
Vulnerability Details
https://github.com/Cyfrin/2024-05-Sablier/blob/main/v2-periphery/src/SablierV2MerkleLockupFactory.sol#L61
function createMerkleLT(
MerkleLockup.ConstructorParams memory baseParams,
ISablierV2LockupTranched lockupTranched,
MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages,
uint256 aggregateAmount,
uint256 recipientCount
)
external
returns (ISablierV2MerkleLT merkleLT)
{
uint64 totalPercentage;
uint256 totalDuration;
for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) {
uint64 percentage = tranchesWithPercentages[i].unlockPercentage.unwrap();
totalPercentage = totalPercentage + percentage;
unchecked {
totalDuration += tranchesWithPercentages[i].duration;
}
}
..
}
Impact
This can lead to the creation of a non-functional SablierV2MerkleLT contract.
Tools Used
Manual
Recommendations
It's recommended to add the validation for the duration when creating the MerkleLT contract.
function createMerkleLT(
MerkleLockup.ConstructorParams memory baseParams,
ISablierV2LockupTranched lockupTranched,
MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages,
uint256 aggregateAmount,
uint256 recipientCount
)
external
returns (ISablierV2MerkleLT merkleLT)
{
// Calculate the sum of percentages and durations across all tranches.
uint64 totalPercentage;
uint256 totalDuration;
+ require(tranchesWithPercentages.length > 0);
for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) {
uint64 percentage = tranchesWithPercentages[i].unlockPercentage.unwrap();
totalPercentage = totalPercentage + percentage;
+ require(tranchesWithPercentages[i].duration > 0);
unchecked {
// Safe to use `unchecked` because its only used in the event.
totalDuration += tranchesWithPercentages[i].duration;
}
}
...
}