sherlock-audit/2022-10-illuminate-judging

IllIllI - ERC777 transfer hooks can be used to bypass fees for markets that support Swivel

Opened this issue · 1 comments

IllIllI

medium

ERC777 transfer hooks can be used to bypass fees for markets that support Swivel

Summary

ERC777 transfer hooks can be used to bypass fees for markets that support Swivel

Vulnerability Detail

Most of the lend() functions calculate fees based on an amount that is directly transferred by the Lender contract. In the case of the Swivel version of lend(), it assumes that the Swivel orders provided are operating on the underlying, and only calculates fees based on those. After that, it allows the user to swap any excess underlying with swivelLendPremium(), and there are no checks that the 'premium' amount is a dust amount, and there are no fees charged on this amount.

If a user submits a Swivel order that adds one wei of Notional tokens (one of Swivel's supported tokens) to a Swivel position, which are ERC777 tokens, the user can use the pre-transfer hook to send a large amount of underlying to the Lender contract, so that when swivelLendPremium() is called, the large balance is swapped without fees. The one wei of Notional contributes zero to the fee, since the feenominator calculation is vulnerable to loss of precision.

A malicious user can automate this process by deploying a contract that does this automatically for novice users.

Impact

No protocol fees

Users can pay zero fees

Code Snippet

Fees are based on the order amounts:

// File: src/Lender.sol : Lender.lend()   #1

383                // Lent represents the total amount of underlying to be lent
384 @>             uint256 lent = swivelAmount(a);
385    
386                // Transfer underlying token from user to Illuminate
387                Safe.transferFrom(IERC20(u), msg.sender, address(this), lent);
388    
389                // Get the underlying balance prior to calling initiate
390                uint256 starting = IERC20(u).balanceOf(address(this));
391    
392                // Verify and collect the fee
393                {
394                    // Calculate fee for the total amount to be lent
395:@>                 uint256 fee = lent / feenominator;

https://github.com/sherlock-audit/2022-10-illuminate/blob/main/src/Lender.sol#L383-L395

No fees are charged on the premium:

// File: src/Lender.sol : Lender.lend()   #2

407                uint256 received;
408                {
409                    // Get the starting amount of principal tokens
410                    uint256 startingZcTokens = IERC20(
411                        IMarketPlace(marketPlace).token(u, m, p)
412                    ).balanceOf(address(this));
413    
414                    // Fill the given orders on Swivel
415                    ISwivel(swivelAddr).initiate(o, a, s);
416    
417 @>                 if (e) {
418 @>                     // Calculate the premium
419 @>                     uint256 premium = IERC20(u).balanceOf(address(this)) -
420 @>                         starting;
421 @> 
422 @>                     // Swap the premium for Illuminate principal tokens
423 @>                     swivelLendPremium(u, m, y, premium, premiumSlippage);
424 @>                 }
425    
426                    // Compute how many principal tokens were received
427                    received =
428                        IERC20(IMarketPlace(marketPlace).token(u, m, p)).balanceOf(
429                            address(this)
430                        ) -
431                        startingZcTokens;
432                }
433    
434:               // Mint Illuminate principal tokens to the user

https://github.com/sherlock-audit/2022-10-illuminate/blob/main/src/Lender.sol#L407-L434

Notional tokens are proxies of ERC777 tokens

Tool used

Manual Review

Recommendation

Charge a fee based on the total underlying after the Swivel orders are executed