Open code423n4 opened 1 year ago
141345 marked the issue as duplicate of #78
141345 marked the issue as duplicate of #260
gzeon-c4 changed the severity to QA (Quality Assurance)
Not dupe of #260, I think the write up is correct and it seems fund can be stuck in certain edge case.
gzeon-c4 marked the issue as grade-b
Adding more context focusing on the most important parts.
decreaseLiquidity
Here's a foundry test that can be run on the forked arbitrum mainnet. When we try to remove 40000 of liquidity the function will return zeros. https://gist.github.com/imherefortech/9c8a84da66714458be610cfd6fc184e3
This will cause a revert in GoodEntry due to subsequent collect
call as described in the main report.
This can happen if the tick has outstanding debt as specified in the comments. Outstanding debt in the context of the protocol means that there are open positions for this liquidity tick. The issue will arise if open positions take the majority of the tick's liquidity.
Looking at the vault page we can see that there are often ticks with very little liquidity. Meaning that an adversary can easily open a position taking most of the liquidity
Deposits/Withdrawal from the vaults are not working and user funds are stuck. As far as I see, there is no way to get back the funds other than upgrading the contracts. The issue has been present for about a week on alpha and was fixed by the devs via an upgrade.
lol edited my previous comment, I was meant to say the writeup looks correct but it seems to be quite an edge case and therefore the risk is low but @Keref can you review?
Hi, sry overlooked this one I guess it didnt appear in the list? Yes it was confirmed, the auditor also contacted me in private and we solved the bug and updated the live contract during the audit, checking that it didn't try to collect (0, 0)
Cool, I will bump this back to Med as this clearly affected the availability of the protocol. However, I am not fully convinced that this will make the asset stuck permanently without an upgrade. For example, it seems to be possible to borrow everything from the lendingpool so that aBal is set to 0 and the withdraw will be skipped.
This previously downgraded issue has been upgraded by gzeon-c4
gzeon-c4 marked the issue as not a duplicate
gzeon-c4 marked the issue as primary issue
gzeon-c4 marked the issue as selected for report
Yes it might be correct that there are ways to unstuck the funds without an upgrade.
Lines of code
https://github.com/code-423n4/2023-08-goodentry/blob/main/contracts/TokenisableRange.sol#L306-L313
Vulnerability details
Impact
Note: this issue happened on the deployed version of GoodEntry and was discovered when using https://alpha.goodentry.io
Due to incorrect parameters and validation when working with UniV3 LP the vault may enter a state where rebalancing reverts. This means any deposits and withdrawals from the vault become unavailable.
Code walkthrough
When rebalancing a vault, the existing positions need to be removed from Uni. This is done in removeFromTick function.
Here, zeros are passed as
amount0Min
andamount1Min
arguments. The execution continues in TokenisableRange.withdraw function.decreaseLiquidity
is called to remove liquidity from Uni.Here there is an edge-case that for really small change in liquidity the returned values
removed0
andremoved1
can be 0s (will be explained at the end of a section).Then,
collect
is called andremoved0
,removed1
are passed as arguments.However,
collect
reverts when both of these values are zeros - https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositionManager.sol#L316As a result, any deposit/withdraw/rebalancing of the vault will revert when it will be attempting to remove existing liquidity.
When can decreaseLiquidity return 0s
This edge-case is possible to achieve as it happened in the currently deployed alpha version of the product. The sponsor confirmed that the code deployed is the same as presented for the audit.
The tick that caused the revert has less than a dollar of liquidity. Additionally, that tick has outstanding debt and so the
aBal
value was small enough to cause the issue. In the scenario that happened on-chain aBal is only 33446.Proof of Concept
The issue can be demonstrated on the contracts deployed on the arbitrum mainnet. This is the foundry test
The test can be executed by forking the arbitrum mainnet
The result is an error in UniV3
NonfungiblePosition.collect
methodTools Used
E2E testing, then code review
Recommended Mitigation Steps
It is unclear why this
collect
call is needed because the fees are already collected a few lines above inclaimFees
. I suggest removing the secondcollect
call altogether. If it's needed then perhaps only collect if one of removed0/removed1 is non-zero.Assessed type
Uniswap