Skip to content

Latest commit

 

History

History
42 lines (28 loc) · 2.79 KB

M-03.md

File metadata and controls

42 lines (28 loc) · 2.79 KB

Add premium doesn't collect fees

Summary

Fees are applied to premiums when a new position is opened, but the same mechanism is not enforced when margin is added to an existing position.

Impact

When a new position is created in the LAMM protocol, fees are collected in favor of the LP owner that provides the liquidity for the position. The fee is calculated by applying a configurable rate over the tokens "from" amounts:

https://github.com/code-423n4/2023-12-particle/blob/a3af40839b24aa13f5764d4f84933dbfa8bc8134/contracts/protocol/ParticlePositionManager.sol#L191-L201

191:         // pay for fee
192:         if (FEE_FACTOR > 0) {
193:             cache.feeAmount = ((params.marginFrom + cache.amountFromBorrowed) * FEE_FACTOR) / Base.BASIS_POINT;
194:             cache.treasuryAmount = (cache.feeAmount * _treasuryRate) / Base.BASIS_POINT;
195:             _treasury[cache.tokenFrom] += cache.treasuryAmount;
196:             if (params.zeroForOne) {
197:                 lps.addTokensOwed(params.tokenId, uint128(cache.feeAmount - cache.treasuryAmount), 0);
198:             } else {
199:                 lps.addTokensOwed(params.tokenId, 0, uint128(cache.feeAmount - cache.treasuryAmount));
200:             }
201:         }

The fee is applied to amountFromBorrowed, the "from" amount from the LP liquidity, and marginFrom, the user provided margin to cover the required amount for the swap and provide a premium in the position.

However, the same logic is not followed when the borrower adds more margin using addPremium(). In this case, the amounts are added in full to the position's premiums without incurring any fees.

This not only represents an inconsistent behavior (which may be a design decision), but allows the borrower to avoid paying part of the fees. The borrower can provide enough marginTo so that the swap can reach the required collateralTo amount, leaving the token "from" premium at zero. Then, in a second call, provide the needed premium to cover for eventual LP fees using addPremium(). This can be done with null liquidation risk and is greatly simplified by leveraging the multicall functionality. The borrower can bundle both operations in a single transaction, without even deploying a contract.

Proof of Concept

A user can skip paying part of the fees by bundling two operations in a single transaction:

  1. A call to openPosition() with the minimal required amount of marginFrom so that amountReceived + amountToBorrowed + marginTo >= collateralTo.
  2. A call to addPremium() to provide the required premiums to cover for eventual fees.

Recommendation

Apply the same fee schedule to addPremium(). Depending on the value of zeroForOne, determine the "from" token, apply the FEE_FACTOR to it, and accrue the fee to the corresponding liquidity position.