Summary
There is no check of the expiration
value when creating a new campaign through the SablierV2MerkleLockupFactory
, making the campaign useless.
Vulnerability Details
A user can either intentionally or by accident set the expiration
of a campaign/airdrop in the past. There are no checks in place to prevent that for happening.
Impact
The campaign is essentially rendered useless. No user can claim rewards, since there is a campaign expiration check when claiming:
SablierV2MerkleLockup.sol:
function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal {
if (hasExpired()) {
revert Errors.SablierV2MerkleLockup_CampaignExpired({
blockTimestamp: block.timestamp,
expiration: EXPIRATION
});
}
.
.
}
function hasExpired() public view override returns (bool) {
return EXPIRATION > 0 && EXPIRATION <= block.timestamp;
}
Proof of Code
Place the following code in 2024-05-Sablier/v2-periphery/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol
test file.
function test_ExpirationSetInThePast() external {
MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams();
uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT();
uint256 recipientCount = defaults.RECIPIENT_COUNT();
MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages();
baseParams.expiration = uint40(block.timestamp) - 1;
assert(baseParams.expiration < block.timestamp);
merkleLockupFactory.createMerkleLT(
baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount
);
}
Tools Used
Manual review
Recommendations
In SablierV2MerkleLockupFactory
file include the following checks, they will make the above test fail:
function createMerkleLL(
MerkleLockup.ConstructorParams memory baseParams,
ISablierV2LockupLinear lockupLinear,
LockupLinear.Durations memory streamDurations,
uint256 aggregateAmount,
uint256 recipientCount
)
external
returns (ISablierV2MerkleLL merkleLL)
{
++ if (baseParams.expiration <= block.timestamp) {
++ revert();
++ }
.
.
}
function createMerkleLT(
MerkleLockup.ConstructorParams memory baseParams,
ISablierV2LockupTranched lockupTranched,
MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages,
uint256 aggregateAmount,
uint256 recipientCount
)
external
returns (ISablierV2MerkleLT merkleLT)
{
++ if (baseParams.expiration <= block.timestamp) {
++ revert();
++ }
.
.
}