Skip to content

Commit

Permalink
fixes #3 (#30)
Browse files Browse the repository at this point in the history
* use fixed point math lib

* add test for precision loss
  • Loading branch information
zhongeric authored Jul 8, 2024
1 parent 4ce5b58 commit bf1f79b
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/FeeOnTransferDetector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma abicoder v2;

import "solmate/tokens/ERC20.sol";
import "solmate/utils/SafeTransferLib.sol";
import "solmate/utils/FixedPointMathLib.sol";
import "v2-core/interfaces/IUniswapV2Pair.sol";
import "./lib/UniswapV2Library.sol";

Expand All @@ -17,6 +18,7 @@ struct TokenFees {
/// @notice Detects the buy and sell fee for a fee-on-transfer token
contract FeeOnTransferDetector {
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;

error SameToken();
error PairLookupFailed();
Expand Down Expand Up @@ -129,7 +131,7 @@ contract FeeOnTransferDetector {
pure
returns (uint256 buyFeeBps)
{
buyFeeBps = (amountRequestedToBorrow - amountBorrowed) * BPS / amountRequestedToBorrow;
buyFeeBps = (amountRequestedToBorrow - amountBorrowed).mulDivUp(BPS, amountRequestedToBorrow);
}

/// @notice helper function to calculate the sell fee in bps
Expand All @@ -142,7 +144,7 @@ contract FeeOnTransferDetector {
try this.callTransfer(tokenBorrowed, address(pair), amountBorrowed) {
uint256 amountSold = tokenBorrowed.balanceOf(address(pair)) - pairBalanceBeforeSell;
uint256 sellFee = amountBorrowed - amountSold;
sellFeeBps = sellFee * BPS / amountBorrowed;
sellFeeBps = sellFee.mulDivUp(BPS, amountBorrowed);
} catch (bytes memory) {
sellFeeBps = buyFeeBps;
}
Expand Down
17 changes: 17 additions & 0 deletions test/FeeOnTransferDetector.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ contract FeeOnTransferDetectorTest is Test {
assertEq(fees.externalTransferFailed, false);
}

function testBasicFotTokenNoPrecisionLoss() public {
MockFotToken fotToken = new MockFotToken(200, 500);
MockToken otherToken = new MockToken();
address pair = factory.deployPair(address(fotToken), address(otherToken));
fotToken.setPair(pair);
fotToken.mint(pair, 100 ether);
otherToken.mint(pair, 100 ether);
IUniswapV2Pair(pair).sync();

// previously used to fail due to precision loss from integer division
TokenFees memory fees = detector.validate(address(fotToken), address(otherToken), 9999);
assertEq(fees.buyFeeBps, 200);
assertEq(fees.sellFeeBps, 500);
assertEq(fees.feeTakenOnTransfer, false);
assertEq(fees.externalTransferFailed, false);
}

function testBasicFotTokenWithExternalFees() public {
MockFotTokenWithExternalFees fotToken = new MockFotTokenWithExternalFees(500);
MockToken otherToken = new MockToken();
Expand Down

0 comments on commit bf1f79b

Please sign in to comment.