Front-runners of uniswapLiquidityProvision can cause loss of fees
Description
With the uniswapLiquidityProvision function, users can use two assets they own on the darkpool in order to provide liquidity on a Uniswap pool. The advantage to a user for doing this is earning (parts of) the fees that users of Uniswap are paying to trade in the position's range. Uniswap pools can have different fees, from 0.01% to 1%.
The uniswapLiquidityProvision function takes a proof as well as the following struct UniswapLiquidityProvisionArgs as arguments:
struct UniswapLiquidityProvisionArgs {
// data of the note that will be used for liquidity provision as first token
UniswapNoteData noteData1;
// data of the note that will be used for liquidity provision as second token
UniswapNoteData noteData2;
// address of the relayer
address payable relayer;
// gas fees of the relayer
uint256[2] relayerGasFees;
// merkle root of the merkle tree that the commitment of the note is included
bytes32 merkleRoot;
// note footer of the note that will be created after mint (both tokens)
bytes32[2] changeNoteFooters;
// tick min of the liquidity provision
int24 tickMin;
// tick max of the liquidity provision
int24 tickMax;
// note footer of the NFT position note that will be created after liquidity provision
bytes32 positionNoteFooter;
// pool fee of the liquidity provision (Uniswap)
uint24 poolFee;
}
struct UniswapNoteData {
address assetAddress;
uint256 amount;
bytes32 nullifier;
}The proof checks that notes with the nullifiers noteData1.nullifier and noteData2.nullifier are present in the Merkle tree with root hash merkleRoot, with assets noteData1.assetAddress and noteData2.assetAddress and amounts noteData1.amount and noteData2.amount belonging to the same user. It is also checked that the three note footers are well-formed and belong to the same user and that the user signed the two input notes and nullifiers, the three output footers, and tickMin and tickMax.
However, relayer, relayerGasFee, and poolFee are not used in the proof, and in particular, are not checked to be signed by the user owning the input note.
As the uniswapLiquidityProvision function can be called by anyone, an attacker getting knowledge of the arguments to a call to uniswapLiquidityProvision (for example by observing a transaction including it in the mempool or by acting as a relayer for the call) can then front-run the call with their own transaction, where they can choose the above arguments freely.
The impact of this regarding manipulation of relayer and relayerGasFee is discussed in a separate finding; see Finding ref↗.
An attacker can submit the transaction with poolFee set to a lower value than in the original transaction. The user will then possibly receive a lower amount of fees than they expected (e.g., if the liquidity is not provided to a pool with 0.01% fees instead of 1%). Furthermore, a front-runner is incentivized to carry out this attack if they themselves wish to swap their asset in a direction and at a price that is made possible by this position. By submitting the uniswapLiquidityProvision call with a lower fee, they can within the same transaction swap against that position at a possibly much lower fee than otherwise.
Impact
Attackers front-running calls to uniswapLiquidityProvision can lower the Uniswap fee level for which liquidity will be provided, benefitting the attacker by being able to trade at lower fees and causing loss of potential fee profits to the original user.
Recommendations
Ensure that the user owning the input notes approves of relayer, relayerGasFee, and poolFee by including those values as public circuit variables in the uniswap_lp circuit and including them in the signature signed by the user's private key.
Remediation
This issue has been acknowledged by Singularity. In commit , relayer was added as public circuit variables in the uniswap_lp circuit and included in the signature signed by the user's private key. In commit , poolFee was analogously added.
Singularity informed us that initially, only Singularity will run relayers, and that gas fees will be handled before third parties can run relayers.