SablierV2MerkleLT is used to create system specific airstreams
, which close to an airdrop, but using sablier lockup streams to unlock the tokens in time. Currently it is possible to for users to create linear and tranche streams.
When a user create such airstream
, he interacts with SablierV2MerkleLockupFactory and he has to provide array of tranches
with corresponding duration and % of the total amount, which is being unlocked for that "tranche". Also user provides necessary creational params for such SablierV2MerkleLockup
to populate the following state vars of the airstream:
We can see that creator may specify whether the streams, which are claimed could be more adjusted to be more or less "decentralized" by specifying address of admin
, expiration
, etc. As we are in web3 world, we believe that more decentralization = better
. So we may assume a scenario of an airstream
with no admin (address(0)) and no expiration
.
Creator should then send the correponding erc20 tokens to SablierV2MerkleLT
, which are planned to be distributed in the airstrem
.
But on creation of such stream, there are no enough validation checks, which may lead to funder loosing his tokens and claimers being unable to claim.
When lockupTranched is created, there are a couple of checks made. One such is whether provided tranches are less than a limit.
But when airstream is created there is no check whether provided array of tranches is less than the limit of the corresponding lockupTranched
. Also such is missing in the factory function. This may lead to creator initiating such contract with more thances than the limit and funding it. A revert would be the result when user try to claim his shares. If creator has set no admin, all funds are lost.
EXAMPLE
Imagine the following scenario:
Bob is a alternative employer and wants to incentives newcomers by creating a SablierV2MerkleLT
for them.
For him it is easier to deploy SablierV2MerkleLT
and then each employee have to claim his stream, whenever they start/want.
Because Bob wants to be fair and transparrent, he deploy it with expiration = 0
(No expiration time) and admin = address(0)
so he cannot withdraw the assets.
He fund the contract with 52 (weeks) * salaryPerWeekPerPerson * personCount = lets say $100 000 USDC.
He has called SablierV2MerkleLockupFactory::createMerkleLT
with tranchesWithPercentages
array which is with 52 elements inside (weeks).
The problem arrises when first user calls claim
and try to create a stream with tranches
array with length of 52
The tx will always revert inside LOCKUP_TRANCHED.createWithDurations()
=> _create
=> Helpers.checkCreateLockupTranched(createAmounts.deposit, params.tranches, MAX_TRANCHE_COUNT, params.startTime);
, because MAX_TRANCHE_COUNT = 30
root revert here
Those $100K are lost, because there is no way for Bob to change tranchesWithPercentages
array for SablierV2MerkleLT
. Neither for somehow change MAX_TRANCHE_COUNT
for the corresponding stream. Also Bob cannot withdraw the funds, because he set no admin and clawback is uncallable
DoS -> Loss of funds
Manual Review
When SablierV2MerkleLL
is created, always check if provided tranchesWithPercentages
array matches corresponding MAX_TRANCHE_COUNT
limitation:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.