Platform: Code4rena
Start Date: 30/09/2021
Pot Size: $100,000 SUSHI
Total HM: 24
Participants: 7
Period: 7 days
Judge: alcueca
Total Solo HM: 16
Id: 35
League: ETH
Rank: 5/7
Findings: 3
Award: $7,508.19
🌟 Selected for report: 5
🚀 Solo Findings: 2
🌟 Selected for report: tensors
377.7706 SUSHI - $4,722.13
tensors
This is a possible line of attack on the staking contract, in particular the claimReward() function:
A user with some spare capital mints a liquidity position with a very tight range (1-2 ticks wide) at the current price. Because the range is so small, his position.liquidity on his NFT is large (DyDxMath.sol).
The user then sets up a bot to frontrun any price changes that someone else tries to do, burning his position after claiming rewards. He then mints a new liquidity position at the new price after the other persons trades go through.
Rinse and repeat this process. If done correctly, no funds are at risk from the bot owner, he doesn't pay any fees for burning/minting either.
So what you have left is a sequence of positions with high position.liquidity and in the correct price range all the time, without taking on any risk. Thereby stealing incentive funds.
The lines below reward the bot owner with a large amount of the token: https://github.com/sushiswap/trident/blob/c405f3402a1ed336244053f8186742d2da5975e9/contracts/pool/concentrated/ConcentratedLiquidityPoolManager.sol#L90-L94
Recommendation: Lock the positions during a set time while they are staked.
#0 - sarangparikh22
2021-10-28T22:53:48Z
This seems very unlikely to happen and does not affect the pool, it's equivalent to just re balancing your position.
#1 - alcueca
2021-11-12T13:59:46Z
@sarangparikh22, Isn't the warden describing a Just In Time liquidity pattern?
#2 - sarangparikh22
2021-11-16T23:53:32Z
@alcueca yes exactly, even done right, the bot would still face huge IL. We don't intend to solve this.
🌟 Selected for report: tensors
113.3312 SUSHI - $1,416.64
tensors
I'm adding this as an issue because I didn't see it mentioned anywhere in the codebase, and I think its a fair point that relates to how the protocol gives out rewards to users. As I understand , the point of staking is to provide users with additional compensation for providing liquidity (and taking on risk) for the good of the protocol. If a large fraction of rewards go to users who don't provide a huge benefit to the protocol, that's a problem.
Consider two different pools: USDC-DAI and USDC-ETH. Suppose a user has $10K worth of tokens and decides to provide liquidity to each of these pools.
In the USDC-DAI pool the user can very safely provide the $10K with a 1% spread between upper and lower tick. The total amount of liquidity he provides is roughly $10K * (1/0.01) = $1 M dollars of liquidity per second. The impermanent loss here is going to be basically 0 in normal conditions. The liquidity will be in range all the time.
The same situation in the USDC-ETH pool on the other hand: Suppose a user has $10K worth of USDC+ETH, provides it with a 1% spread between upper and lower ticks at the current price => roughly $1 M dollars of liquidity per second, the same as before. However, now there is a good chance that price ranges by more than 1% meaning he loses all of his more valuable tokens for the cheaper ones due to impermanent loss. The liquidity will be out of range for a much longer percentage of the time.
However, if the incentives for each pool are the same, the staking protocol would value the liquidity per second of each LP situation equally. To make things "fair per unit of risk/liquidity" the incentive on the USDC-ETH should be something like 10x or 20x the incentive on the USDC-DAI pool. The pools with higher volatility should have a significantly higher incentive.
Recommendations: Make sure the developers are at least aware of something like this when choosing incentive amounts for different pools. Carefully choose incentive amounts for each pool.
#0 - sarangparikh22
2021-10-12T10:12:38Z
This is not a med-risk issue, or an issue at all, we will improve the docs, so that devs are aware on how to set the incentives.
#1 - alcueca
2021-11-12T13:55:25Z
Setting the incentives wrong will make the protocol leak value, which warrants a Severity 2. The issue was not disclosed, and therefore is valid.
16.9997 SUSHI - $212.50
tensors
It is possible that a typo results in an incentive having no owner, meaning it cannot be recalled by the reclaimIncentive function:
addIncentive() should have a standard check to make sure that an owner is declared so that funds aren't lost.
#0 - alcueca
2021-11-12T10:49:56Z
Choosing #76 as the main.
16.9997 SUSHI - $212.50
tensors
It is widely considered best practice to lock the pragma statements to a specific compiler. See, for example, https://swcregistry.io/docs/SWC-103
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
#0 - sarangparikh22
2021-10-11T13:05:02Z
The compiler version to use is fixed in hardhat settings.
#1 - alcueca
2021-11-12T10:53:17Z
Severity 1 is correct.
🌟 Selected for report: tensors
37.7771 SUSHI - $472.21
tensors
Consider using version 0.8.8 instead of using solidity 0.8.0. More recent versions of solidity have compiler optimizations, user defined types (this would be very useful in the concentratedLiquidityPools code), overriding interface functions, reading from immutables, among other things. This could help reading and writing safe and clean code.
#0 - sarangparikh22
2021-10-11T15:13:41Z
This is non critical issue.
🌟 Selected for report: tensors
37.7771 SUSHI - $472.21
tensors
The functions on L274, L439, L667 is not used. I guess, other Pool contracts in Trident use them, but since they just revert here, there is no point in including them. Unused code can hint at programming or architectural errors.
Recommended Mitigation Steps Use it or remove it.
#0 - sarangparikh22
2021-10-13T20:27:15Z
These functions are present to conform the IPool standard of the Trident, if we remove them, the compilation would fail.
#1 - alcueca
2021-11-12T14:02:57Z
You are not really conforming to the standard if necessary functions revert. While this is not an issue in the ConcentratedLiquidityPool itself, it points to an issue with your IPool standard, or with the applicability of the IPool standard to the ConcentratedLiquidityPool.
Platforms that expect contracts to conform with IPool now have to deal with the fact that some IPool contracts behave differently. Sustained.