Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: low
Invalid

ERC20s with pausable, upgradable or blocklist features may disrupt functionality

Description The Sablier README acknowledges the issues with rebased tokens and fee-on-transfer tokens. There are however, popular tokens, such as USDC or USDT, that have functionality which could severely disrupt the functionality of Sablier. Functionality such as pausable, blocklists and upgradability stem from the token issuer's centralised control.

Impact Senders may be unable to create streams and recipients may be unable to withdraw tokens they are owed.

Proof of Concepts

Code

Place the following code in a Foundry Forge test file.

pragma solidity >=0.8.22;
import { Test } from "../../lib/forge-std/src/Test.sol";
import { PausableMockERC20, IERC20 } from "./PausableMockERC20.sol";
import { SablierV2LockupLinear } from "../../src/SablierV2LockupLinear.sol";
import { SablierV2NFTDescriptor } from "../../src/SablierV2NFTDescriptor.sol";
import { ISablierV2NFTDescriptor } from "../../src/interfaces/ISablierV2NFTDescriptor.sol";
import { Lockup, LockupLinear, Broker } from "../../src/types/DataTypes.sol";
import { wrap } from "@prb/math/src/ud60x18/Casting.sol";
contract BlacklistTest is Test, Events {
PausableMockERC20 token;
SablierV2LockupLinear sablier;
SablierV2NFTDescriptor nftDescriptor;
address sender = makeAddr("sender");
address recipient = makeAddr("recipient");
address tokenController = makeAddr("tokenController");
address sablierAdmin = makeAddr("sablierAdmin");
uint256 ONE_THOUSAND_TOKENS = 1000 * 1e18;
LockupLinear.CreateWithTimestamps params;
function setUp() public {
nftDescriptor = new SablierV2NFTDescriptor();
sablier = new SablierV2LockupLinear(sablierAdmin, ISablierV2NFTDescriptor(nftDescriptor));
vm.startPrank(tokenController);
token = new PausableMockERC20();
token.mint(ONE_THOUSAND_TOKENS);
token.transfer(sender, ONE_THOUSAND_TOKENS);
vm.stopPrank();
// Define the parameters for createWithTimestamps
params = LockupLinear.CreateWithTimestamps({
sender: sender,
recipient: recipient,
totalAmount: uint128(ONE_THOUSAND_TOKENS),
asset: IERC20(token),
cancelable: true,
transferable: false,
timestamps: LockupLinear.Timestamps({
start: uint40(block.timestamp), // Stream starts now
cliff: uint40(block.timestamp + 60), // Cliff period ends in 60 seconds
end: uint40(block.timestamp + 3600) // Stream ends in 1 hour
}),
broker: Broker({ account: address(0), fee: wrap(0) })
});
}
function test_blacklist() public {
// stream recipient is blacklisted
vm.prank(tokenController);
token.addToBlacklist(recipient);
// create stream
vm.startPrank(sender);
token.approve(address(sablier), ONE_THOUSAND_TOKENS);
sablier.createWithTimestamps(params);
vm.stopPrank();
// Set block timestamp to the end time of the stream
vm.warp(params.timestamps.end);
// expect withdraw to revert because recipient is blacklisted
vm.expectRevert();
sablier.withdraw(1, recipient, uint128(ONE_THOUSAND_TOKENS));
}
function test_pausableToken() public {
// create stream
vm.startPrank(sender);
token.approve(address(sablier), ONE_THOUSAND_TOKENS);
sablier.createWithTimestamps(params);
vm.stopPrank();
// token is paused
vm.prank(tokenController);
token.pause();
// Set block timestamp to the end time of the stream
vm.warp(params.timestamps.end);
// expect withdraw to revert because token is paused
vm.expectRevert();
sablier.withdraw(1, recipient, uint128(ONE_THOUSAND_TOKENS));
}

Recommended Mitigation Consider adding a disclaimer for users to be aware of the functionality of tokens they are using for payment streams.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

Support

FAQs

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