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.