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