The protocol allows for any type of token to be used. In a situation where a FOT token is used, the amount actually been received in a stream deposit would be lesser than what the protocol accounts for, which would lead to the balance of that token in the protocol been overstated and also affecting the accounting in the protocol, as stream balance was used a reasonable amounts of time in the protocol to determine sensitive values in the protocol.
pragma solidity >=0.8.22;
import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol";
import { UD21x18 } from "@prb/math/src/UD21x18.sol";
import { Test, console } from "forge-std/src/Test.sol";
import { FlowNFTDescriptor } from "src/FlowNFTDescriptor.sol";
import { SablierFlow } from "src/SablierFlow.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockERC20WithFee is ERC20 {
uint private constant FEE_BPS = 100;
uint private constant BPS = 10000;
constructor(
string memory _name,
string memory _symbol,
uint _totalSupply
) ERC20(_name, _symbol) {
_mint(msg.sender, _totalSupply);
}
function getReceivedAmount(
address,
address,
uint _sentAmount
) public pure returns (uint receivedAmount, uint feeAmount) {
receivedAmount = (_sentAmount * (BPS - FEE_BPS)) / BPS;
feeAmount = _sentAmount - receivedAmount;
}
function transfer(
address to,
uint value
) public override returns(bool) {
(uint transferAmount, uint burnAmount) = getReceivedAmount(msg.sender, to, value);
_burn(msg.sender, burnAmount);
super._transfer(msg.sender ,to, transferAmount);
return true;
}
function transferFrom(address from, address to, uint256 value) public override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
(uint transferAmount, uint burnAmount) = getReceivedAmount(spender, to, value);
_burn(from, burnAmount);
super._transfer(from ,to, transferAmount);
return true;
}
}
contract POC is Test {
SablierFlow flow;
FlowNFTDescriptor nftDescriptor;
MockERC20WithFee mock;
address admin = makeAddr("admin");
address sender = makeAddr("sender");
address recipient = makeAddr("recipient");
function setUp() public {
vm.startPrank(admin);
nftDescriptor = new FlowNFTDescriptor();
flow = new SablierFlow(admin, nftDescriptor );
mock = new MockERC20WithFee("MockERC20WithFee", "MOCK", 100 ether);
mock.transfer(sender, 12 ether);
vm.stopPrank();
}
function test_poc() public {
vm.startPrank(sender);
UD21x18 ratePerSecond = UD21x18.wrap(0.001e18);
flow.create(sender, recipient, ratePerSecond, mock, true);
mock.approve(address(flow), 10 ether);
flow.deposit(1, 10 ether, sender, recipient);
assert(mock.balanceOf(address(flow)) < 10 ether);
}
}
Inaccurate accounting.
Use before and after balance to accurately reflect the true amount received/A token whitelist system