Summary : The protocolFee variable in the _withdraw function of the SablierFlow contract shadows the protocolFee state variable in the SablierFlowBase contract and the protocolFee function in the ISablierFlowBase interface.
Impact : This issue may cause confusion and make the code harder to understand and maintain. It may also lead to unexpected behavior if the wrong protocolFee variable is accessed or modified.
Proof of Concept Code:
Here is a proof of concept code that demonstrates the variable shadowing issue:
SablierFlow.sol
pragma solidity ^0.8.0;
import "./SablierFlowBase.sol";
import "./ISablierFlowBase.sol";
contract SablierFlow is SablierFlowBase {
function \_withdraw(
uint256 streamId,
address to,
uint128 amount
)
internal
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount)
{
IERC20 token = \_streams\[streamId].token;
UD60x18 protocolFee = protocolFee\[token];
uint128 fee = protocolFee * amount;
}
}
SablierFlowBase.sol
pragma solidity ^0.8.0;
contract SablierFlowBase {
mapping(IERC20 token => UD60x18 fee) public override protocolFee;
}
ISablierFlowBase.sol
pragma solidity ^0.8.0;
interface ISablierFlowBase {
function protocolFee(IERC20 token) external view returns (UD60x18);
}
ExploitContract.sol
pragma solidity ^0.8.0;
import "./SablierFlow\.sol";
contract ExploitContract {
SablierFlow public sablierFlow;
constructor(address sablierFlowAddress) public {
sablierFlow = SablierFlow(sablierFlowAddress);
}
function exploit() public {
uint256 streamId = 1;
address to = address(this);
uint128 amount = 100;
sablierFlow.protocolFee(IERC20(token)) = 1000;
sablierFlow._withdraw(streamId, to, amount);
uint128 fee = sablierFlow.protocolFee(IERC20(token)) * amount;
require(fee == 1000 * amount, "Incorrect protocol fee");
}
}
Tools Used : VS code
Recommendations : To fix this issue, it is recommended to rename the protocolFee variable in the _withdraw function to a unique name that does not shadow any other variables.
**Example Fix: **
function \_withdraw(
uint256 streamId,
address to,
uint128 amount
)
internal
returns (uint128 withdrawnAmount, uint128 protocolFeeAmount)
{
UD60x18 withdrawalProtocolFee = protocolFee\[token];
uint128 fee = withdrawalProtocolFee * amount;
}