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: 3/113
Findings: 2
Award: $4,936.22
π Selected for report: 1
π Solo Findings: 0
4882.9086 USDC - $4,882.91
In the AlgebraPool contract, when the user provides liquidity via the mint function, the lastLiquidityAddTimestamp is updated to the current time.
(_position.liquidity, _position.lastLiquidityAddTimestamp) = ( liquidityNext, liquidityNext > 0 ? (liquidityDelta > 0 ? _blockTimestamp() : lastLiquidityAddTimestamp) : 0 );
Later when the user removes the liquidity via burn function, the transaction will revert if the current time is less than lastLiquidityAddTimestam + _liquidityCooldown.
if (liquidityDelta < 0) { uint32 _liquidityCooldown = liquidityCooldown; if (_liquidityCooldown > 0) { require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown); } }
liquidityCooldown is max 1 day. However, in the mint function, users can provide liquidity on behalf of other users, which also means that malicious users can keep other users on liquidity cooldown forever by providing a little bit of liquidity on behalf of other users, thus preventing other users from removing liquidity.
function mint( address sender, address recipient, // @audit: users can provide liquidity on behalf of other users int24 bottomTick, int24 topTick, uint128 liquidityDesired, bytes calldata data ) ... (, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(recipient, bottomTick, topTick, int256(liquidityActual).toInt128());
https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L226-L231 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L513-L523
None
Consider allowing users to provide liquidity only for themselves, or setting liquidityCooldown to 0
#0 - trust1995
2022-10-01T21:49:56Z
Seems like a valid freeze of funds.
#1 - sameepsi
2022-10-04T06:05:01Z
This is a valid issue but the severity should be medium. This can be easily mitigated by simply setting up cool down period to 0.
#2 - 0xean
2022-10-04T13:37:35Z
https://github.com/code-423n4/2022-09-quickswap-findings/issues/83#issuecomment-1264731071 see comment here.
Issue is valid and leads to locking of funds, High severity is warranted. Turning cool do to 0 would work, but has other consequences for JIT liquidity.
π 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
53.3143 USDC - $53.31
Some tokens (e.g. LEND) revert when transfering a zero value amount.
https://github.com/d-xo/weird-erc20#revert-on-zero-value-transfers
AlgebraPool.flash() might revert when fees0 = 0 or fees1 = 0 and asset is a revert on zero value transfers token.
And during the fees0/fees1 calculation, it's possible fees0/fees1 = 0 when communityFeeToken0/communityFeeToken1 == 0 or paid0/paid1 is small.
uint256 paid0 = balanceToken0(); require(balance0Before.add(fee0) <= paid0, 'F0'); paid0 -= balance0Before; if (paid0 > 0) { uint8 _communityFeeToken0 = globalState.communityFeeToken0; uint256 fees0; if (_communityFeeToken0 > 0) { fees0 = (paid0 * _communityFeeToken0) / Constants.COMMUNITY_FEE_DENOMINATOR; TransferHelper.safeTransfer(token0, vault, fees0); } totalFeeGrowth0Token += FullMath.mulDiv(paid0 - fees0, Constants.Q128, _liquidity); } uint256 paid1 = balanceToken1(); require(balance1Before.add(fee1) <= paid1, 'F1'); paid1 -= balance1Before; if (paid1 > 0) { uint8 _communityFeeToken1 = globalState.communityFeeToken1; uint256 fees1; if (_communityFeeToken1 > 0) { fees1 = (paid1 * _communityFeeToken1) / Constants.COMMUNITY_FEE_DENOMINATOR; TransferHelper.safeTransfer(token1, vault, fees1); } totalFeeGrowth1Token += FullMath.mulDiv(paid1 - fees1, Constants.Q128, _liquidity); }
None
We should check if fees0 != 0 and fees1 != 0 before transfer.
#0 - sameepsi
2022-10-04T06:06:22Z
The protocol is built to support standard ERC-20 tokens. We can't simply support for all types of non-standard tokens
#1 - 0xean
2022-10-04T20:14:57Z
see comment - https://github.com/code-423n4/2022-09-quickswap-findings/issues/45#issuecomment-1267526059
downgrading to QA