Assessment reports>Singularity>Medium findings>Front-runners of ,uniswapLiquidityProvision, can cause loss of fees
Category: Coding Mistakes

Front-runners of uniswapLiquidityProvision can cause loss of fees

Medium Severity
Medium Impact
Medium Likelihood

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.

Zellic © 2024Back to top ↑