xanoutas/solidity-security-findings huggingface dataset:
Risk of reentrancy `onERC721Received` function to manipulate collateral token configs shares
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L454-L473
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L1223-L1241


# Vulnerability details

## Issue Description

The `onERC721Received` function is invoked whenever the vault contract receives a Uniswap V3 position ERC721 token. This can happen either when an owner creates a new position or when a transformation occurs.

For this issue, we'll focus on the second case, specifically when a position is going through a transformation, which creates a new position token. In such a case, we have `tokenId != oldTokenId`, and the else block is run, as shown below:

```solidity
function onERC721Received(address, address from, uint256 tokenId, bytes calldata data)
    external
    override
    returns (bytes4)
{
    ...

    if {
        ...
    } else {
        uint256 oldTokenId = transformedTokenId;

        // if in transform mode - and a new position is sent - current position is replaced and returned
        if (tokenId != oldTokenId) {
            address owner = tokenOwner[oldTokenId];

            // set transformed token to new one
            transformedTokenId = tokenId;

            // copy debt to new token
            loans[tokenId] = Loan(loans[oldTokenId].debtShares);

            _addTokenToOwner(owner, tokenId);
            emit Add(tokenId, owner, oldTokenId);

            // clears data of old loan
            _cleanupLoan(oldTokenId, debtExchangeRateX96, lendExchangeRateX96, owner);

            //@audit can reenter with onERC721Received and call repay or borrow to call _updateAndCheckCollateral twice and manipulate collateral token configs

            // sets data of new loan
            _updateAndCheckCollateral(
                tokenId, debtExchangeRateX96, lendExchangeRateX96, 0, loans[tokenId].debtShares
            );
        }
    }

    return IERC721Receiver.onERC721Received.selector;
}
```

We should note that the `_cleanupLoan` function does return the old position token to the owner:

```solidity
function _cleanupLoan(
    uint256 tokenId,
    uint256 debtExchangeRateX96,
    uint256 lendExchangeRateX96,
    address owner
) internal {
    _removeTokenFromOwner(owner, tokenId);
    _updateAndCheckCollateral(
        tokenId,
        debtExchangeRateX96,
        lendExchangeRateX96,
        loans[tokenId].debtShares,
        0
    );
    delete loans[tokenId];
    nonfungiblePositionManager.safeTransferFrom(
        address(this),
        owner,
        tokenId
    );
    emit Remove(tokenId, owner);
}
```

The issue that can occur is that the `_cleanupLoan` is invoked before the `_updateAndCheckCollateral` call. So, a malicious owner can use the `onERC721Received` callback when receiving the old token to call the `borrow` function, which makes changes to `loans[tokenId].debtShares` and calls `_updateAndCheckCollateral`. When the call resumes, the `V3Vault.onERC721Received` function will call `_upda
Unscrupulous users can block being liquidated
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L1083


# Vulnerability details

## Impact
Can be blocked from being liquidated

## Proof of Concept
With the [liquidate](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L685-L757) method. we can see that the logic of liquidation is:
1.Check if the tokenId is in an unhealthy state
2.Calculate the cost of liquidation
3.If there is a bad debt, let the lender share it
4.from the liquidator to pull the liquidation needs of the fee
5.Send the reward to the liquidator
6.Return the liquidated tokenId to the owner

The problem occurs when the tokenId is returned to the owner. the return code is implemented in the [_cleanupLoan](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L1077-L1085) method with the following code:

```solidity
    function _cleanupLoan(uint256 tokenId, uint256 debtExchangeRateX96, uint256 lendExchangeRateX96, address owner)
        internal
    {
        _removeTokenFromOwner(owner, tokenId);
        _updateAndCheckCollateral(tokenId, debtExchangeRateX96, lendExchangeRateX96, loans[tokenId].debtShares, 0);
        delete loans[tokenId];
        nonfungiblePositionManager.safeTransferFrom(address(this), owner, tokenId); // @audit-issue If the owner is a contract and does not support onERC721Received, it will revert, resulting in a failure of the liquidation.
        emit Remove(tokenId, owner);
    }
```
And the owner can be set by the recipient parameter in the [create](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L400-L402) method. Then without any checking, set the recipient to owner in onERC721Received method. the implementation is as follows:
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L429-L477
```solidity
    function onERC721Received(address, address from, uint256 tokenId, bytes calldata data)
        external
        override
        returns (bytes4)
    {
...
        if (transformedTokenId == 0) {
            address owner = from;
            if (data.length > 0) {
                owner = abi.decode(data, (address)); // @audit-issue Get the specified parameter as owner
            }
            loans[tokenId] = Loan(0);

            _addTokenToOwner(owner, tokenId); // @audit-issue Set as owner
            emit Add(tokenId, owner, 0);
        } else {
...
        }

        return IERC721Receiver.onERC721Received.selector;
    }
```
_addTokenToOwner method:
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L1297-L1301
```solidity
    function _addTokenToOwner(address to, uint256 tokenId) internal {
        ownedTokensIndex[tokenId] = ownedTokens[to].length;
        ownedTokens[t
AutoCompound Holding Can Be Drained By Reentrancy Attack
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/transformers/AutoCompound.sol#L123


# Vulnerability details

## Impact

Autocompound transformer is to be called by the Vault/Operator for collection of fees, swap,to increase the position size. 

However a Malicious users can call the `execute` `funtion` with a position he holds (not in Vault) by calling the `execute` `function` If `params.amountIn` is greater than zero, `params.amountIn` will be use as the liquidity amount to add.


Malicious user can drain any tokens held by the AutoCompound contract that is supposed to be held for other user.


```
if (params.amountIn > 0 || state.amount0 > 0 || state.amount1 > 0) {
            uint256 amountIn = params.amountIn;
```


## Proof of Concept

https://github.com/code-423n4/2024-03-revert-lend/commit/ad1f05f3201f23bbd15e407ace1ca91bbaa1e9e8#r139820203

## Tools Used
Manual Review
## Recommended Mitigation Steps

The check effect pattern the execution of the auto compounding can be fixed
by handling a stronger user validation on 

```
nonfungiblePositionManager.positions(params.tokenId);
``` 
is to handle the check Between the storage read of the pending withdrawal balance 

```
AutoCompound execute() while updating the `state.amount0` and `state.amount1` 
```




## Assessed type

Reentrancy
During transformation the caller can re-enter the Vault through the onERC721Received function
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L497
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L450-L472


# Vulnerability details

## Impact
Calling `V3Vault.transform()` with some transformers (`AutoRange.sol`) opens a re-entrancy opportunity when the old position is replaced and returned to its owner inside `V3Vault.onERC721Received()`. If the owner is a contract it can change the `tokenId` or distort debt calculations by changing `debtExchangeRateX96` or change the state in a way not expected inside `transform()` 

## Proof of Concept
When calling `V3Vault.transform()` this is what happens:

```
function transform(
        uint256 tokenId,
        address transformer,
        bytes calldata data
    ) external override returns (uint256 newTokenId) {
        
        // Get debtEchangeRate
        (uint256 newDebtExchangeRateX96, ) = _updateGlobalInterest();
       
        .....

     
        //@audit-ok - Can re-enter from AutoRange.sol 
        (bool success, ) = transformer.call(data);  //  <--------- call Transformer
        if (!success) {
            revert TransformFailed();
        }

        .....

        // Check that debt is healthy based on debtEchangeRate

        uint256 debt = _convertToAssets(
            loans[tokenId].debtShares,
            newDebtExchangeRateX96,  // <------ No longer accurate, after re-entrancy
            Math.Rounding.Up
        );
        _requireLoanIsHealthy(tokenId, debt);

        transformedTokenId = 0;

        return tokenId;
    }
```
The 3 important steps here are:
- `debtExchangeRateX96` is updated and cached, to be used for calculating debt value after the transformation and confirm it is healthy
- transformer is called  
- finally the debt is re-calculated after the transformation to make sure it remains healthy

Some transformers (like `AutoRange.sol`) move current `tokenId` assets to a new position with a new `tokenId`. The new token is send back to `V3Vault`, where  the `onERC721Received()` function handles replacing the old tokeId with the new and sending the old token to its owner:

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L450-L472

The problem here is that the old NFT is transferred in the same transaction ([through `_cleanupLoan()`](https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L1083))

If the recipient is a contract, this will call it's own `onERC721Received()` method which can be used to re-enter `V3Vault`.

Some possible actions the malicious contract can take are:
- call `create()` to open a position with some arbitrary `tokenId`. This would transfer the NFT to `V3Vault` and call again `onERC721Received()`. Since the vault will be in a transformation mode (the exploiter re-entered before transf
Attacker can mint shares using arbitrary token, because the permitTransferFrom doesn't check the assets
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L893-L897


# Vulnerability details

## Impact
Attacker could use any arbitrary token to mint shares. Thus, it will disrupt the overall system and shares/asset calculation.

## Proof of Concept
***I have implemented the POC in the Vault.t.sol contract. I have add the arb.token additionally that address(WHALE) stored
~~~~~SetUP(one additional line)~~~~~~
///////Only one additional line in your SetUp
//@note shitty token for test
    IERC20 constant CEL = IERC20(0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d);

function test_permit_mint() external{ 

        uint256 amount = 1000000;
        uint256 shitCoinBalance = CEL.balanceOf(address(WHALE_ACCOUNT));

        uint256 lendLimit = vault.globalLendLimit();
        lendLimit += shitCoinBalance;
        console.log("LendLimit is", lendLimit);
        

        uint256 privateKey = 123;
        address addr = vm.addr(privateKey);

        // give coins
        vm.deal(addr, 1 ether);
        vm.prank(WHALE_ACCOUNT);
        CEL.transfer(addr, shitCoinBalance);
        // USDC.transfer(addr, amount * 2);
        

        vm.prank(addr);
        CEL.approve(PERMIT2, type(uint256).max);
        //USDC.approve(PERMIT2, type(uint256).max);
       

        //@note if don't proceed, try to do with some low-validity tokens
        ISignatureTransfer.PermitTransferFrom memory tf = ISignatureTransfer.PermitTransferFrom(
             ISignatureTransfer.TokenPermissions(address(CEL), 50), 1, block.timestamp
        );

        bytes memory signature = _getPermitTransferFromSignature(tf, privateKey, address(vault));
        bytes memory permitData = abi.encode(tf, signature);

        vm.prank(addr);
        vault.mint(50, address(addr), permitData);      
    }

## Tools Used
Manual Review. Foundry 

## Recommended Mitigation Steps
Add the require check that check the assets that is transferred from. It could be done via TokenPermission Struct

if(TokenPermission.token != USDC/DAI/e.t.c){
revert INVALID ASSET;
}


## Assessed type

Invalid Validation
User can trigger a DoS upon someone trying to liquidate them
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L680-L757


# Vulnerability details

## Impact
Similar to many lending protocols, the Revert Lend protocol has a feature allowing one user to liquidate another. The purpose of this liquidation mechanism is to settle outstanding loans and maintain protocol stability. However, a user eligible for liquidation can exploit this process by executing a DoS attack. This involves front-running the `liquidate()` intended for their liquidation, by calling any transformer contract. When a user's position is being transformed it can't be liquidated and the user can do this indefinitely, which would destabilize the protocol or cause financial harm to lenders and other users of the protocol.

## Tools Used
Manual review


## Assessed type

DoS
Reentrancy vulnerabilities (theft of ethers)
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/src/transformers/AutoCompound.sol#L101-L193
https://github.com/code-423n4/2024-03-revert-lend/src/transformers/AutoCompound.sol#L108-L112
https://github.com/code-423n4/2024-03-revert-lend/src/transformers/AutoCompound.sol#L142-L146
https://github.com/code-423n4/2024-03-revert-lend/src/transformers/AutoCompound.sol#L161
https://github.com/code-423n4/2024-03-revert-lend/src/transformers/AutoCompound.sol#L163-L167
https://github.com/code-423n4/2024-03-revert-lend/src/transformers/AutoCompound.sol#L175-L176


# Vulnerability details

## Impact
Possible Reentrancy vulnerabilities (theft of ethers) could cause damage to funds/ assets.

## Proof of Concept
```solidity
File: src/transformers/AutoCompound.sol

/// @audit ******************* Issue Detail *******************
Reentrancy (eth) in AutoCompound.execute(AutoCompound.ExecuteParams) (L#101-193):
	External calls:
	- (state.amount0,state.amount1) = nonfungiblePositionManager.collect(INonfungiblePositionManager.CollectParams(params.tokenId,address(this),type()(uint128).max,type()(uint128).max)) (L#108-112)
	- (state.amountInDelta,state.amountOutDelta) = _poolSwap(Swapper.PoolSwapParams(pool,IERC20(state.token0),IERC20(state.token1),state.fee,params.swap0To1,amountIn,0)) (L#142-146)
	- _checkApprovals(state.token0,state.token1) (L#161)
	- (None,state.compounded0,state.compounded1) = nonfungiblePositionManager.increaseLiquidity(INonfungiblePositionManager.IncreaseLiquidityParams(params.tokenId,state.maxAddAmount0,state.maxAddAmount1,0,0,block.timestamp)) (L#163-167)

	External calls sending eth:
	- _checkApprovals(state.token0,state.token1) (L#161)
		- (success,returndata) = target.call{value: value}(data) (L#135)

	State variables written after the call(s):
	- _setBalance(params.tokenId,state.token0,state.amount0 - state.compounded0 - state.amount0Fees) (L#175)
	- _setBalance(params.tokenId, state.token1, state.amount1 - state.compounded1 - state.amount1Fees) (L#176)

/// @audit ************** Possible Issue Line(s) **************
	L#108-112, L#142-146, L#161, L#163-167, L#175-176,

/// @audit ****************** Affected Code *******************
 101:     function execute(ExecuteParams calldata params) external nonReentrant {
 102:         if (!operators[msg.sender] && !vaults[msg.sender]) {
 103:             revert Unauthorized();
 104:         }
 105:         ExecuteState memory state;
 106: 
 107:         // collect fees - if the position doesn't have operator set or is called from vault - it won't work
 108:         (state.amount0, state.amount1) = nonfungiblePositionManager.collect(
 109:             INonfungiblePositionManager.CollectParams(
 110:                 params.tokenId, address(this), type(uint128).max, type(uint128).max
 111:             )
 112:         );
 113: 
 114:         // get position info
 115:         (,, state.token0, state.token1, state.fee, state.tickLower, state.tickUpper,,,,,) =
 116:             nonfungi
Malicious borrower can DOS the liquidation
Audit: 2024-03-revert-lend-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/457230945a49878eefdc1001796b10638c1e7584/src/V3Vault.sol#L744
https://github.com/code-423n4/2024-03-revert-lend/blob/457230945a49878eefdc1001796b10638c1e7584/src/V3Vault.sol#L1083


# Vulnerability details

## Detailed Explanation
To explain this fully, let's discuss this in 3 parts, 1st part will be about creating, then 2nd is borrowing and the last part will be liquidation process.

1st Part: Create
In the beginning, borrower need to transfer first the Uniswap V3 NFT position into the protocol. This will serve as the collateral when the borrower decided to make a loan against it. Please remember in this function that the recipient of NFT position (needed in liquidation part) is included in user parameter input.
````
File: V3Vault.sol
459:     function create(uint256 tokenId, address recipient) external override {
460:         nonfungiblePositionManager.safeTransferFrom(msg.sender, address(this), tokenId, abi.encode(recipient));
461:     }
````
Once the protocol receives the NFT position, function onERC721received will be executed. One of the parameter here is bytes calldata data, this is the data of recipient address from function create. In line 503 and 507 below, the recipient address will be assigned as tokenID owner.
````
File: V3Vault.sol
488:     function onERC721Received(address, address from, uint256 tokenId, bytes calldata data)
489:         external
490:         override
491:         returns (bytes4)
492:     {
493:         // only Uniswap v3 NFTs allowed - sent from other contract
494:         if (msg.sender != address(nonfungiblePositionManager) || from == address(this)) {
495:             revert WrongContract();
496:         }
497: 
498:         (uint256 debtExchangeRateX96, uint256 lendExchangeRateX96) = _updateGlobalInterest();
499: 
500:         if (transformedTokenId == 0) {
501:             address owner = from;
502:             if (data.length > 0) {
503:                 owner = abi.decode(data, (address));
504:             }
505:             loans[tokenId] = Loan(0);
506: 
507:             _addTokenToOwner(owner, tokenId);
508:             emit Add(tokenId, owner, 0);
````
2nd Part: Borrow
In this part, borrow function has a requirement to check if the token ID owner is the same with msg.sender (line 620-21) so it can proceed to borrow successfully. If the borrower want to pass this checking, he should assigned the recipient in the create function the msg.sender itself.
````
File: V3Vault.sol
609:     function borrow(uint256 tokenId, uint256 assets) external override {
610:         bool isTransformMode =
611:             transformedTokenId > 0 && transformedTokenId == tokenId && transformerAllowList[msg.sender];
612: 
613:         (uint256 newDebtExchangeRateX96, uint256 newLendExchangeRateX96) = _updateGlobalInterest();
614: 
615:         _resetDailyDebtIncreaseLimit(newLendExchangeRateX96, false);
616: 
617:         Loan storage loan = loans[tokenId];
Dangerous use of deadline parameter in AutoCompound.sol
Audit: 2024-03-revert-lend-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/AutoCompound.sol#L163


# Vulnerability details

## Impact
Actions in the Uniswap NonfungiblePositionManager contract are protected by a deadline parameter to limit the execution of pending transactions. Functions that modify the liquidity of the pool check this parameter against the current block timestamp in order to discard expired actions. 

User provided deadline arguments are utilized in transformers AutoRange.sol and LeverageTransformer.sol while interacting with the Uniswap NFT Position Manager. However, the execute() function in AutoCompound.sol uses block.timestamp as the deadline argument, which defeats the purpose of using a deadline. Using block.timestamp as the deadline is effectively a no-operation that has no effect nor protection. Since block.timestamp will take the timestamp value when the transaction gets mined, the check will end up comparing block.timestamp against itself.

Failure to provide a proper deadline value enables pending transactions to be maliciously executed at a later point in detriment of the submitter.

[AutoCompound.sol #L161-165](https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/AutoCompound.sol#L163) :
```javascript
(, state.compounded0, state.compounded1) = nonfungiblePositionManager.increaseLiquidity(
                    INonfungiblePositionManager.IncreaseLiquidityParams(
                        params.tokenId, state.maxAddAmount0, state.maxAddAmount1, 0, 0, block.timestamp
                    )
                );
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add a deadline parameter to the ExecuteParams struct and forward it to the corresponding underlying call to the Uniswap NonfungiblePositionManager contract.


## Assessed type

Uniswap
Hardcoded slippage value
Audit: 2024-03-revert-lend-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/AutoCompound.sol#L165
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/V3Utils.sol#L159-L160
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/V3Utils.sol#L206-L207
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/transformers/AutoRange.sol#L206-L207


# Vulnerability details

## Description
In AutoCompound.sol exactly in the execute() function the project is hardcoding the value of the slippage when calling nonfungiblePositionManager.increaseLiquidity(INonfungiblePositionManager.IncreaseLiquidityParams(params.tokenId, state.maxAddAmount0, state.maxAddAmount1, 0, 0, block.timestamp)); 
                

## Proof of Concept
The IncreaseLiquidityParams struct is the one that holds all the details of this call. 
   struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }
You can see that amount0Min and amount1Min are responsible for checking the slippage value however the project is hardcoding it to 0 right now. This means that  users are accepting 0 tokens in return in high volatility market conditions.
The project is also using the same method in other contracts such as V3Utils.sol and AutoRange.sol

## Tools Used
Manual Analysis

## Recommended Mitigation Steps
I recommend the project to allow setting the value in the function parameter as its the most efficient and simple way.





## Assessed type

Uniswap