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;
}