//
Protocol may lose fee due to wrong calculation of referral fee
y0ng0p3 profile imagey0ng0p3
Medium

Finding description and impact

When purchasing listed tokens with a referral, users must pay fees. In case there a referrer, the referral fees is are calculated and deduct from the fees the buyer pays to the protocol in the SecondSwap_Marketplace::_handleTransfers function.

File: contracts/SecondSwap_Marketplace.sol#L451-L493 function _handleTransfers( Listing storage listing, uint256 _amount, uint256 discountedPrice, uint256 bfee, uint256 sfee, address _referral ) private returns (uint256 buyerFeeTotal, uint256 sellerFeeTotal, uint256 referralFeeCost) { uint256 baseAmount = (_amount * discountedPrice) / uint256( 10 ** ( IERC20Extended( address( IVestingManager(IMarketplaceSetting(marketplaceSetting).vestingManager()) .getVestingTokenAddress(listing.vestingPlan) ) ).decimals() ) ); // 3.1. Rounding issue leads to total drain of vesting entries require(baseAmount > 0, "SS_Marketplace: Amount too little"); // 3.1. Rounding issue leads to total drain of vesting entries buyerFeeTotal = (baseAmount * bfee) / BASE; sellerFeeTotal = (baseAmount * sfee) / BASE; IERC20(listing.currency).safeTransferFrom(msg.sender, address(this), (baseAmount + buyerFeeTotal)); // 3.6. DOS caused by the use of transfer and transferFrom functions referralFeeCost = 0; if (_referral != address(0) && listing.whitelist == address(0)) { referralFeeCost = buyerFeeTotal - (baseAmount * bfee * IMarketplaceSetting(marketplaceSetting).referralFee()) / (BASE * BASE); } IERC20(listing.currency).safeTransfer(listing.seller, (baseAmount - sellerFeeTotal)); // 3.6. DOS caused by the use of transfer and transferFrom functions uint256 feeCollectorTotal = (buyerFeeTotal + sellerFeeTotal); IERC20(listing.currency).safeTransfer( IMarketplaceSetting(marketplaceSetting).feeCollector(), feeCollectorTotal ); // 3.6. DOS caused by the use of transfer and transferFrom functions }

referralFeeCost is the part of fee that goes to the referral.

File: contracts/SecondSwap_MarketplaceSetting.sol#L36-L39 /** * @notice Percentage of fees allocated to referrals (in basis points) */ uint256 public referralFee;

The issue here is that the referralFeeCost are wrongly calculated based on what referralFee represents. Which is the percentage of fees allocated to referrals. Below is how referralFeeCost should be calculated.

referralFeeCost = (buyerFeeTotal * IMarketplaceSetting(marketplaceSetting).referralFee()) / BASE = (((baseAmount * bfee) / BASE) * IMarketplaceSetting(marketplaceSetting).referralFee()) / BASE = (baseAmount * bfee * IMarketplaceSetting(marketplaceSetting).referralFee()) / (BASE * BASE)

Instead, the current calculation substract the right value from buyerFeeTotal.
The impact here is twofold :

  • The more referralFee is closed to 0%, the more referrers will get and the protocol will lose money.
  • The more referralFee is closed to 100%, the less referrers will get than the should.

Proof of Concept

Recommended mitigation steps

File: contracts/SecondSwap_Marketplace.sol#L451-L493 function _handleTransfers( Listing storage listing, uint256 _amount, uint256 discountedPrice, uint256 bfee, uint256 sfee, address _referral ) private returns (uint256 buyerFeeTotal, uint256 sellerFeeTotal, uint256 referralFeeCost) { uint256 baseAmount = (_amount * discountedPrice) / uint256( 10 ** ( IERC20Extended( address( IVestingManager(IMarketplaceSetting(marketplaceSetting).vestingManager()) .getVestingTokenAddress(listing.vestingPlan) ) ).decimals() ) ); // 3.1. Rounding issue leads to total drain of vesting entries require(baseAmount > 0, "SS_Marketplace: Amount too little"); // 3.1. Rounding issue leads to total drain of vesting entries buyerFeeTotal = (baseAmount * bfee) / BASE; sellerFeeTotal = (baseAmount * sfee) / BASE; IERC20(listing.currency).safeTransferFrom(msg.sender, address(this), (baseAmount + buyerFeeTotal)); // 3.6. DOS caused by the use of transfer and transferFrom functions referralFeeCost = 0; if (_referral != address(0) && listing.whitelist == address(0)) { referralFeeCost = -- buyerFeeTotal - (baseAmount * bfee * IMarketplaceSetting(marketplaceSetting).referralFee()) / (BASE * BASE); } IERC20(listing.currency).safeTransfer(listing.seller, (baseAmount - sellerFeeTotal)); // 3.6. DOS caused by the use of transfer and transferFrom functions uint256 feeCollectorTotal = (buyerFeeTotal + sellerFeeTotal); IERC20(listing.currency).safeTransfer( IMarketplaceSetting(marketplaceSetting).feeCollector(), feeCollectorTotal ); // 3.6. DOS caused by the use of transfer and transferFrom functions }