Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: medium
Valid

Funds sent to SablierV2MerkleLL can be lost in the event of a chain reorg

Summary

If a chain reorg happens, "sender" can lose the funds he sent to SablierV2MerkleLL or SablierV2MerkleLT, which he wanted to stream to a list of recipients.
This is because the factory does not transfer the funds from the sender in the same transaction that the SablierV2MerkleLT contract gets created

Vulnerability Details

Apart from just allowing a "sender" to create a stream to a "recipient", Sablier also allows a sender to create a list of recipients, and each of the recepients can then create the streams. This is possible via SablierV2MerkleLockupFactory. Here is how it works:

  • Sender calls SablierV2MerkleLockupFactory#createMerkleLL or SablierV2MerkleLockupFactory#createMerkleLT to create the type of lockup they want, providing a Merkle root containing the addresses of recipients they want to stream to.

  • Sender can now send the tokens they want to stream to the SablierV2MerkleLL or SablierV2MerkleLT contract that got created

  • Each of the recipients in that merkle root can now call SablierV2MerkleLL#claim to start the stream

    • This would call the specified type of lockup contract(that is, SablierV2LockupLinear or SablierV2LockupTranched) to create the stream.

Here is the SablierV2MerkleLockupFactory#createMerkleLL function:

function createMerkleLL(
MerkleLockup.ConstructorParams memory baseParams,
ISablierV2LockupLinear lockupLinear,
LockupLinear.Durations memory streamDurations,
uint256 aggregateAmount,
uint256 recipientCount
) external returns (ISablierV2MerkleLL merkleLL) {
// Deploy the MerkleLockup contract with CREATE.
merkleLL = new SablierV2MerkleLL(baseParams, lockupLinear, streamDurations);//@audit-info funds are not transferred from the sender
// Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain.
emit CreateMerkleLL(merkleLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientCount);
}

This approach, which requires that the sender sends the tokens after creating the SablierV2MerkleLL contract, is vulnerable to blockchain reorgs, which would lead loss of funds for the sender.

For example,

  • At block=1, sender calls SablierV2MerkleLockupFactory#createMerkleLL, which returns the address of the SablierV2MerkleLL created.

  • At block=2, sender sends tokens to SablierV2MerkleLL address that was returned

  • Chain reorg happens, block 1 gets dropped

  • Funds that were sent to that SablierV2MerkleLL address are lost.

Note that factory uses create opcode, so returned address is not deterministic, and sender cannot recreate the lockup contract on that same address

Impact

It is intended that SablierV2MerkleLL(LT) will be used for events like airdrops.
In case of a reorg, as explained in this report, the organization planning the airdrop will lose their tokens

Tools Used

Manual Review

Recommendations

Consider implementing one of these:

  • The lockup factory should transfer the tokens from the sender address to the SablierV2MerkleLL address immediately it gets created. So in the event of a reorg, sender would get his funds back(RECOMMENDED)

OR

  • SablierV2MerkleLL should be created deterministically, so in the case of reorg, sender can redeploy the lockup contract to that same address.

Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

CREATE is vulnerable to ChainReorgs

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.