Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: high
Invalid

Protocol Revenue Loss Through Unregistered Token Streams

Discussion

The protocol fee is set by the protocol admin per token, this means any token not set to receive protocol fees wont pay any fees, Users can stream tokens that have not been set to pay fees as stream tokens which means they wont pay any fees.

function setProtocolFee(IERC20 token, UD60x18 newProtocolFee) external override onlyAdmin {
// Check: the new protocol fee is not greater than the maximum allowed.
if (newProtocolFee > MAX_FEE) {
revert Errors.SablierFlowBase_ProtocolFeeTooHigh(newProtocolFee, MAX_FEE);
}
UD60x18 oldProtocolFee = protocolFee[token];
// Effects: set the new protocol fee.
protocolFee[token] = newProtocolFee;
// Log the change of the protocol fee.
emit ISablierFlowBase.SetProtocolFee({
admin: msg.sender,
token: token,
oldProtocolFee: oldProtocolFee,
newProtocolFee: newProtocolFee
});
// Refresh the NFT metadata for all streams.
emit BatchMetadataUpdate({ _fromTokenId: 1, _toTokenId: nextStreamId - 1 });
}

As we can see the protocolFee is charged per token protocolFee[token] = newProtocolFee;. If the token being streamed is not added to the protocolFee array no fees will be paid.

User can exploit this by streaming tokens that have not been added to the protocoFee array to bypass the protocolFee Which in turn will lead to reduced revenue for the protocol.

POC

In this proof of concept we set the protocolFee to a different token from the token being streamed.

-The protocol fee is set per token, which means tokens that haven't been assigned a fee won't incur any charges.

-Users can exploit this by choosing to stream tokens that haven't been assigned a protocol fee, effectively bypassing the fee system.

-This could lead to reduced revenue for the protocol, as users might preferentially use tokens without assigned fees.

-The proof of concept demonstrates that by setting the protocol fee for one token (testToken) but streaming a different token (USDC), the protocol fee amount is zero.

function test_Bypass_ProtocolFee() public {
//creating a stream with usdc while the testToken is set to have protocolFee.
streamId = sablierFlow.create(
eve,
bob,
UD21x18.wrap(1e18),
usdc,
true
);
//set Protocol fee with testToken.
vm.prank(address(this));
sablierFlow.setProtocolFee(testToken, PROTOCOL_FEE);
uint128 streamBalanceBefore = sablierFlow.getStream(1).balance;
console2.log(streamBalanceBefore);
vm.startPrank(eve);
sablierFlow.deposit(1, 200e6, eve, bob);
vm.stopPrank();
vm.warp(101 seconds);
vm.startPrank(eve);
sablierFlow.withdraw(1, bob, 100e6);
sablierFlow.getStream(1);
vm.stopPrank();
}

As we can observe here the protocolFeeAmount is zero which shows the protocol does not receive fees for usdc tokens.

protocolFeeAmount: 0

├─ [38576] SablierFlow::withdraw(3, 0x0000000000000000000000000000000000002222, 100000000 [1e8])
│ ├─ [25151] ERC20Mock::transfer(0x0000000000000000000000000000000000002222, 100000000 [1e8])
│ │ ├─ emit Transfer(from: SablierFlow: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: 0x0000000000000000000000000000000000002222, value: 100000000 [1e8])
│ │ └─ ← [Return] true
│ ├─ emit WithdrawFromFlowStream(streamId: 3, to: 0x0000000000000000000000000000000000002222, token: ERC20Mock: [0xF62849F9A0B5Bf2913b396098F7c7019b51A820a], caller: 0x0000000000000000000000000000000000004444, withdrawAmount: 100000000 [1e8], protocolFeeAmount: 0)
│ ├─ emit MetadataUpdate(_tokenId: 3)

Users can use this to avoid paying protocol fees essentially using the protocol streaming service for free.

Recommendation

Ensure all tokens used are charged for fees. Or allow only whitelisted tokens

Updates

Lead Judging Commences

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

sancybars Submitter
9 months ago
sancybars Submitter
9 months ago
inallhonesty Lead Judge
9 months ago
inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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