Platform: Code4rena
Start Date: 26/09/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 113
Period: 5 days
Judge: 0xean
Total Solo HM: 6
Id: 166
League: ETH
Rank: 1/113
Findings: 4
Award: $11,445.66
π Selected for report: 2
π Solo Findings: 2
4882.9086 USDC - $4,882.91
Detailed description of the impact of this finding.
uint32 _liquidityCooldown = liquidityCooldown; if (_liquidityCooldown > 0) { require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown); }
The code above from AlgebraPool.sol#_recalculatePosition causes a revert if the liquidity has been added or removed with in the specified cooldown period. _recalculatePosition is called whenever a position is minted (added to) or burned (removed from). Mint allows a user to add liquidity to a different user, which allows an adversary to DOS a specific user from depositing or withdrawing their liquidity. The adversary would front run the mint or burn transaction with a malicious mint for a small amount. This would initiate the cooldown period causing the user's deposit or withdraw to fail.
Concentrated liquidity positions are particularly vulnerable to impermanent loss. An adversary could take advantage of this to block the user causing greater impermanent loss to a user that is trying to withdraw a position. This would mean that this DOS vector timed correctly could lead to loss of funds for the user being DOS'd.
I don't see any reason why mint needs to allow specifying a different recipient. It should be remove to resolve this DOS vector
#0 - 0xean
2022-10-02T20:54:22Z
Attack path looks valid. If we assume the cooldown period to be something near half a day (a day being the max), then 2 calls per day could lock a users liquidity for relatively cheaply.
#1 - sameepsi
2022-10-04T06:13:24Z
duplicate of #70
#2 - 0xean
2022-10-04T13:36:33Z
will use #70 as canonical issue, closing this as dupe.
π Selected for report: 0x52
3255.2724 USDC - $3,255.27
Flashloan users forced to pay higher fees than expected
The first swap of the block sets the fee that will apply to all other actions during that block, including the fee that will be applied to flashloans. If a swap occurs in the same block before the flashloan, the fee taken from the flashloan will be different than expected potentially much different. This could be used by an adversary to force a flashloan user to pay higher fees. They would frontrun the flashloan call with a swap that would push the fees higher. This is dependent on the current state of the swap beforehand but liquidity providers would have a strong incentive to monitor the pool to maximize their profits.
Manual Review
Using the swap fee as the flashloan fee seems counterintuitive to me. It doesn't cause any impermanent loss to the liquidity providers, which is what the dynamic fee is designed to offset. There are two possible solutions to this. The first solution would be to allow the flashloan user to specify a max fee they will pay. The other solution, which I believe makes more sense, is to just use a flat fee on flashloans. This provides more consistent outcomes for flashloan users, making the product more attractive.
#0 - IliaAzhel
2022-10-04T17:28:12Z
Indeed, the flashloan fee may depend on previous transactions in the block. However, fee can not only increase, but also decrease.
π Selected for report: 0x52
3255.2724 USDC - $3,255.27
Exact output functionality does not exist for fee on transfer tokens
require((amountRequired = int256(balanceToken0().sub(balance0Before))) > 0, 'IIA');
Exact output swaps are signaled by setting amountRequired to a negative number. When calling AlgebraPool#swapSupportingFeeOnInputTokens amount required is set to the difference is token balances before and after which will always be positive. Since amountRequired can't be negative, the exact output functionality is impossible to use for those tokens.
Manual Review
Fee on transfer tokens are messy and there is no standard implementation to query the fee from the token contract. Ultimately support for these tokens should be added through either user inputs (i.e. allowing high slippage) or a specialized router
π Selected for report: 0xNazgul
Also found by: 0x1f8b, 0x52, 0xDecorativePineapple, 0xSmartContract, 0xmatt, Aeros, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, DimitarDimitrov, IllIllI, JC, Jeiwan, Lambda, Matin, Migue, Mukund, Ocean_Sky, Olivierdem, RaymondFam, RockingMiles, Rolezn, Ruhum, Satyam_Sharma, Shinchan, Tomo, Trabajo_de_mates, V_B, Waze, __141345__, a12jmx, ajtra, asutorufos, aysha, brgltd, bulej93, carrotsmuggler, catchup, cccz, chrisdior4, cryptonue, cryptphi, d3e4, defsec, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, kaden, karanctf, ladboy233, lukris02, mahdikarimi, martin, mics, natzuu, oyc_109, p_crypt0, pedr02b2, rbserver, reassor, rotcivegaf, rvierdiiev, sikorico, slowmoses, sorrynotsorry, tnevler, trustindistrust
52.213 USDC - $52.21
User is forced to pay token transfer fees twice, once when taking payment and once when returning extra
Fee on transfer tokens take a fee each time the token is transferred. AlgebraPool#swapSupportingFeeOnInputTokens implements two transfers, one for the main payment and one for the refund. This causes the users to have to pay token transfer fees twice.
Support for fee on transfer tokens should be added through either user inputs (i.e. allowing high slippage) or a specialized router
#0 - vladyan18
2022-10-04T10:59:39Z
We cannot determine how many tokens with fee on transfer a pool will receive until it has received them. On the other hand, the return of tokens can only occur under special circumstances, such as a set limitSqrtPrice
.
#1 - 0xean
2022-10-04T15:27:30Z
Going to downgrade to QA, hard problem to solve given slippage, etc. I think this is more of a consequence of FOTs design