xanoutas/solidity-security-findings huggingface dataset:
Improper Approval Mechanism of Clearing House
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/ClearingHouse.sol#L148-L151
https://github.com/code-423n4/2023-01-astaria/blob/main/src/ClearingHouse.sol#L160-L165
https://github.com/code-423n4/2023-01-astaria/blob/main/src/LienToken.sol#L637-L641
https://github.com/code-423n4/2023-01-astaria/blob/main/src/CollateralToken.sol#L566-L577


# Vulnerability details

## Impact

The `ClearingHouse` implementation performs a `@solmate`-based `safeApprove` instruction ([1]) with the remaining `balanceOf(address(this))` but contains code handling any remainder of funds that may remain in the contract [2]. Investigation of the `payDebtViaClearingHouse` function will indicate that the function may not consume the maximum approval that was set to the `TRANSFER_PROXY` [3]. 

As a result, any consequent invocation of `_execute` via `safeTransferFrom` from OpenSea with a `paymentToken` (i.e. `identifier`) such as USDT would fail. Given that the `ClearingHouse` of a particular `collateralId` is created only once, this vulnerability will impact consequent listings and cause them to fatally fail for a token that has been used in the past and is part of the non-compliant EIP-20 tokens, with USDT being the prime and most popular example.

The code in USDT that causes this complication is as follows:

```sol
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
```

## Proof of Concept

The vulnerability is clearly defined above, however, for testing purposes, the `ClearingHouse::safeTransferFrom` function of a particular clearing house instance can be invoked twice with the same arguments. The second invocation will fail provided that the first invocation provided a token balance that exceeds the number of funds necessary for the debt payment which should be denoted in USDT.

On an important note, if the code used `safeApprove` from `@openzeppelin` this issue would affect any token, however, it is limited to non-standard tokens due to the usage of the `@solmate` implementation of `safeApprove`.

## Tools Used

Manual review of the codebase. Historically, findings pertaining to incorrect approval mechanisms that do not support USDT have been marked as "medium" in severity in the past in the following cases:

- Rubicon: https://code4rena.com/reports/2022-05-rubicon/#m-08-usdt-is-not-supported-because-of-approval-mechanism
- Duality Focus: https://code4rena.com/reports/2022-04-dualityfocus/#m-03-not-calling-approve0-before-setting-a-new-approval-causes-the-call-to-revert-when-used-with-tether-usdt

## Recommended Mitigation Steps

We advise approvals to be properly handled by evaluating whether a non-zero approval already exists and in such an instance nullifying it before re-setting it to a non-zero value. Example below:

```sol
// Optimizing lookup
address transferProxy = address(ASTARIA_ROUTER.TRANSFER_PROXY());

// If existing approval is non-zero -> set it to zero
if (ERC20(paymentToken).allowance(address(this), transferPr
Refinancing does not change stack.point.end correctly
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/AstariaXYZ/astaria-core/blob/fix/refinanceupdates/src/VaultImplementation.sol#L317-L365
https://github.com/AstariaXYZ/astaria-core/blob/fix/refinanceupdates/src/LienToken.sol#L196-L205


# Vulnerability details

Note: This is a buyoutLien report which is based on the new PR.

## Impact
Wrong stack.point.end is recalculated after refinancing. A longer stack.point.end will delay the auction from getting started

## Proof of Concept
According to the document (See https://docs.astaria.xyz/docs/protocol-mechanics/refinance)

```
An improvement in terms is considered if either of these conditions is met:

The loan interest rate decrease by more than 0.05%.
The loan duration increases by more than 5 days.
```

We can conclude that refinancing can only work either if the duration is increased or the rate is decreased. Let's have a scenario focusing on the duration.

A loan is applied from vault1 today which has terms of 10 days, so the stack start today (Let's call it A), and the stack ended on the 10th day.

Then, it is refinanced on the 9th day with a new term of 25 days.

After refinancing, the new stack should have the same start Date which is A, and it should end on the new end date 25th day after A. Right now, the new stack starts on the refinancing day and ends on 25 days later than the refinancing day. 

I would say this is wrong because if the refinance duration is less than 10 days, InvalidRefinance error will be thrown. 

See the following code in LienToken.sol

```
   if (
      !isValidRefinance({
        newLien: params.encumber.lien,
        position: params.position,
        stack: params.encumber.stack,
        buyoutFee: buyoutFee
      })
    ) {
      revert InvalidRefinance();
    }
```

Thinking of the duration cannot be less than 10 days

So with common sense, a loan starts from the 1st to the 10th day, after refinancing with a 25-day duration term, It should start from 1st to 25 days and not start from refinancing date and end 25 days after the refinancing date.

Added a test unit

```
 function test1() public {
    TestNFT nft = new TestNFT(3);
    vm.label(address(nft), "nft");
    address tokenContract = address(nft);
    uint256 tokenId = uint256(1);
    address publicVault = _createPublicVault({
      strategist: strategistOne,
      delegate: strategistTwo,
      epochLength: 10 days
    });

    address publicVault2 = _createPublicVault({
      strategist: strategistOne,
      delegate: strategistTwo,
      epochLength: 25 days
    });

    _lendToVault(
      Lender({addr: address(1), amountToLend: 50 ether}),
      publicVault
    );

    _lendToVault(
      Lender({addr: address(1), amountToLend: 50 ether}),
      publicVault2
    );

    uint256 vaultTokenBalance = IERC20(publicVault).balanceOf(address(1));
    ILienToken.Stack[] memory stack;
    (, stack) = _commitToLien({
      vault: publicVault,
      strategist: strategistOne,
      strategistPK: strategistOnePK,
      tokenCont
`Withdraw` function of Astaria Router does not reset approvals to 0 when shares burnt is less than maxShares
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626RouterBase.sol#L48
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/AstariaRouter.sol#L168


# Vulnerability details

## Impact
`Withdraw` function in [Line 48 of AstariaRouter.sol](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/AstariaRouter.sol#L168) allows LP's to place withdraw requests for vault tokens. This function calls the `withdraw` function in [Line 48 of its parent contract, ERC4626RouterBase.sol](https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626RouterBase.sol#L48). 

Note that this function is supposed to give approval for `maxSharesOut` (I've already submitted that issue) & calls the `withdraw` on PublicVault. PublicVault can burn shares <= maxSharesOut -> since approval was given to `vault` for `maxSharesOut` and actual amount of burned shares is less than `maxSharesOut` -> earlier approval given for `maxSharesOut` needs to be reset back to 0.

Not doing so will lead to over approval to vault that can be exploited later. Since this could directly lead to loss of LP tokens, I've marked it as HIGH risk

## Proof of Concept
- Bob has given approval to vault to spend max of 100 shares (`maxSharesOut`) to withdraw 200 WETH (say 1 share = 2 WETH)
- Vault burns 98 shares to withdraw 200 WETH 
- Bob's approval for 2 extra shares stays as is & can be exploited by the vault owner at a later date

## Tools Used
Manual

## Recommended Mitigation Steps

After vault runs `withdraw` function and returns `shares` -> and if `shares` < `maxSharesOut`, reset approval to 0. 

```
  function withdraw(
    IERC4626 vault,
    address to,
    uint256 amount,
    uint256 maxSharesOut
  ) public payable virtual override returns (uint256 sharesOut) {
    ERC20(address(vault)).safeApprove(address(vault), amount);
    if ((sharesOut = vault.withdraw(amount, to, msg.sender)) > maxSharesOut) {
      revert MaxSharesError();
    }
   // *********Recommend following********
  if(sharesOut < maxSharesOut){
    ERC20(address(vault)).safeApprove(address(vault), 0);
  }
}
```
`Withdraw` function in Astaria Router takes approval for wrong amount leading to possibility of over approvals
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626RouterBase.sol#L48
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/AstariaRouter.sol#L168
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L131
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L163


# Vulnerability details

## Impact
`withdraw()` function in [Line 168 of AstariaRouter.sol](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/AstariaRouter.sol#L168) allows `vault` token owners to withdraw underlying asset from vault. Note that implementation calls the `withdraw()` function of its parent contract [ERC4626RouterBase.sol, refer line 48](https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626RouterBase.sol#L48). 

Note that approval is taken for `amount` instead of `maxSharesOut` - amount here refers to the actual underlying asset that was deposited in the vault (eg. WETH) and not the shares held by owner. 

This function then calls `withdraw` function in [Line 131 of PublicVault.sol](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L131). This function reduces allowance of shares that are supposed to be burnt  - since the approval was given for `amount` of underlying asset & not `maxShares`, this could lead to either over allowance/under allowance situation. Under allowance leads to denial-of-service, over allowance leads to open approvals that were never supposed to be open. 

Since this can potentially lead to loss of vault tokens of LPs, I've marked it as `HIGH` risk

## Proof of Concept
- Bob is a LP who owns 100 shares in vault whose present vaule is 1000 ETH (let's assume each vault share is worth 10 ETH)
- Bob calls the `withdraw` function of Astaria Router
- Bob wants to withdraw 100 WETH by burning a maximum of 10 vault shares 
- Function takes approval for 100 shares instead of 10
- Even after withdrawal, Bob still unknowingly has given approval to vault to spend 90 more shares representing 900 ETH


## Tools Used
Manual

## Recommended Mitigation Steps

Change line 48 in `ERCRouterBase.sol` from

```
ERC20(address(vault)).safeApprove(address(vault), amount);
```

to

```
ERC20(address(vault)).safeApprove(address(vault), maxSharesOut);
```
Incorrect Approval
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/ClearingHouse.sol#L148


# Vulnerability details

## Impact
Some tokens like USDT have a build-in front running protection and require that approvals are set to zero before being set to the desired value, `payment - liquidatorPayment`.


## Recommended Mitigation Steps
Set safeApprove to 0 first then to `payment - liquidatorPayment`
LienToken.sol: Lien ids collision could prevent users from taking multiple loan according to same strategy 
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L448
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L900-L909
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/interfaces/ILienToken.sol#L52-L67


# Vulnerability details

## Impact
LienToken derives the lienId to mint using the formula:
`lienId = uint256(keccak256(abi.encode(lien)));`

Which makes it impossible for a borrower to take multiple loans from a vault and belonging to a same strategy. This could be problematic if the user wants to take a second loan under the same conditions but does not have the immediate liquidity to repay the first one.


## Proof of Concept
Links provided

## Tools Used
Manual review

## Recommended Mitigation Steps
Please consider adding a nonce for the lien added in the hash
`Deposit()` function for PublicVault wrongly compares `shares` with `minDepositAmount` preventing legitimate users from depositing assets into vault
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626-Cloned.sol#L27


# Vulnerability details

## Impact
When a user calls the `deposit` function on Public Vault, `deposit` function in [Line 27 of ERC4626-Cloned.sol](https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626-Cloned.sol#L27) gets called. Protocol only allows a deposit amount > minimum Deposit. However when making this check, code wrongly compares `shares` with `minimumDeposit` instead of comparing `assets` with `minimumDeposit`.

`shares` is a reflection of the ownership in the vault. Shares don't need to have a 1-to-1 conversion into assets. Higher the vault earnings, higher the share price -> lower the amount of shares that can be exchanged for a given unit of asset.

A wrong comparison can deny a legitimate depositor from depositing in the pool. Since the values are relatively small, I've marked this as MEDIUM risk

## Proof of Concept
Alice deposits 100 gwei (min deposit) to get 100 gwei of shares
Vault earnings double in 1 day (so 100 gwei shares represent 200 gwei in 1 day)
Bob deposits 100 gwei -> this is equal to 50 gwei of shares -> this fails the check since 50 gwei (shares) < 100 gwei (minimum deposit). 

Although Bob was a legitimate depositor, he got DOS'ed by the vault


## Tools Used
Manual

## Recommended Mitigation Steps

Replace following line in Line 27

```
require(shares > minDepositAmount(), "VALUE_TOO_SMALL");
```

with

```
require(assets > minDepositAmount(), "VALUE_TOO_SMALL");

```
[M-03] `fileBatch` function may fail due to exceed gas limit
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/AstariaRouter.sol#L263


# Vulnerability details

## Impact

The function `fileBatch` in `AstariaRouter` may fail due to unbounded loop over files array can get very large due to the user input being not validated properly.
And the loop in `fileBatch` did not have a mechanism to stop, it’s only based on the files length, and may take all the gas limit. If the gas limit is reached, this transaction will fail or revert.

## Proof of Concept

```solidity
263: function fileBatch(File[] calldata files) external requiresAuth {
```

## Tools Used

Manual audit

## Recommended Mitigation Steps

Perform files length check and revert if necessary.
Making The Function Payable Might Lead To User Losing Their Funds
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/AstariaRouter.sol#L203-L215


# Vulnerability details

## Impact
Making The Function (pullTokens) Payable Might Lead To User Losing Their Funds.


## Proof of Concept

The function here https://github.com/code-423n4/2023-01-astaria/blob/main/src/AstariaRouter.sol#L203-L215 has been made payable , 
but this function does not consume/use any ether that is sent by the user  , instead it uses `token` . 

If a user mistakenly sends ether to this function , that ether is lost forever.

You may argue that this is for gas optimization BUT that is usually the case for privileged functions i.e. function with permissions only for
owner, delegate etc. , as this is a public function it contains risk as described.


## Tools Used

Manual analysis

## Recommended Mitigation Steps

Remove the payable keyword.
[M-02] Non-Conforming ERC20 Tokens Not Recoverable
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/LienToken.sol#L374


# Vulnerability details

## Impact

There is a function `recoverERC20` to rescue any ERC20 tokens that were accidentally sent to the contract. However, there are tokens that do not return a value on success, which will cause the call to revert, even when the transfer would have been successful. This means that those tokens will be stuck forever and not be recoverable.

## Proof of Concept

Someone accidentally transfers USDT, one of the most commonly used ERC20 tokens, to the contract. Because USDT’s transfer does not return a boolean, it will not be possible to recover those tokens and they will be stuck forever.

## Tools Used

Manual audit

## Recommended Mitigation Steps

Use OpenZeppelin’s `safeTransfer` instead.
[M-01] Deprecated `safeApprove`
Audit: 2023-01-astaria-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/main/src/ClearingHouse.sol#L148


# Vulnerability details

## Impact

The function `safeApprove` function has been deprecated by OpenZeppelin team.

## Proof of Concept

```solidity
148: ERC20(paymentToken).safeApprove(
```

## Tools Used

Manual audit

## Recommended Mitigation Steps

Use `safeIncreaseAllowance` and `safeDecreaseAllowance` instead.
Non-reserveRoles can still reserve positonNFTs even after public reserves are blocked, due to inconsistent access controls
Audit: 2024-02-wise-lending-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L90-L97
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L99-L109


# Vulnerability details

## Bug Description
In order to understand this bug, we need to have overall context of positionNFT contract. Here we go:

```
    mapping(address => bool) public reserveRole;
    bool public reservePublicBlocked;
```

These two states are crucial to understand this bug. `reserveRole` stores addresses of users who have the authority to reserve nfts even after public reserving is blocked. While as `reservePublicBlocked` tracks whether public reserving is blocked or not.

Apart from these two states, `onlyReserveRole()` modifier checks if the public reserves are blocked, when they are it further validates whether the caller is assigned reserving responsibilities
```
    modifier onlyReserveRole() {
        if (reservePublicBlocked == true) {
            if (reserveRole[msg.sender] == false) {
                revert NotPermitted();
            }
        }
        _;
    }
```
`reservePositionForUser()` implements this modifier and functions as expected without introducing any bugs.
```
    function reservePositionForUser(
        address _user
    )
        onlyReserveRole
        external
        returns (uint256)
    {
        return _reservePositionForUser(
            _user
        );
    }
```

But unfortunately there is another function that allows anyone to reserve nfts regardless of reserveRole

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L90-L97

```
    function reservePosition()
        external
        returns (uint256)
    {
        return _reservePositionForUser(
            msg.sender
        );
    }
```

The user can then mint the reserved nft using either `approveMint(address,uint256)`, `mintPositionForUser(address)` or even with `mintPosition()`

## Impact
The PositionNfts Contract is supposed to allow non reserveRoles to reserve nfts only when 
`reservePublicBlocked` is false, but due to the lack of proper access controls, anyone can reserve nfts even after `reservePublicBlocked` is set to true.

For the further impact, user can then mint the reserved nft and withdraw it for ETH in Wiselending contract
## Proof of Concept
`Please note that, due to depreciation of _exists function in openzeppelin contracts, PositionNFT.sol causes compilation issues, so in order to run this POC comment, these lines out:`
- https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L161-L163
- https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L348-L351

```
import "forge-std/Test.sol";
import "contracts/PositionNFTs.sol";


contract
Potential risk that a pool may become inactive after the initial check
Audit: 2024-02-wise-lending-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/FeeManager/FeeManager.sol#L730-L814


# Vulnerability details

## Impact
If a pool becomes inactive after the initial check, the function may proceed with the payback operation, but the subsequent operations involving that pool may fail or behave unexpectedly. For example, if the receiving token pool becomes inactive, the function may not be able to correctly calculate or transfer the receiving token amount, potentially leading to loss of funds or other unintended consequences.
The severity of this issue can be considered High. The potential loss of funds, inconsistent state, and stuck funds can have significant financial implications and undermine the reliability and trustworthiness of the contract. Additionally, the inability to handle the scenario where a pool becomes inactive during the execution of the function can lead to unexpected behavior and potential vulnerabilities.

## Proof of Concept
The `paybackBadDebtForToken` function allows users to pay back the bad debt of a position by providing a payback token and receiving a corresponding amount of another token as a reward. However, there is a potential vulnerability related to the assumption that the pools for both the payback token and the receiving token are active.
The function checks if the pools for the `_paybackToken` and `_receivingToken` are active by verifying if their total deposit shares are non-zero:

       if (WISE_LENDING.getTotalDepositShares(_receivingToken) == 0) {
           revert PoolNotActive();
        }

       if (WISE_LENDING.getTotalDepositShares(_paybackToken) == 0) {
           revert PoolNotActive();
       }
However, this check is performed only once at the beginning of the function. If either of the pools becomes inactive after this initial check, the function may proceed with the payback operation, leading to inconsistencies or unexpected behavior.

Proof of Concept and Vulnerability Details: Suppose the `_receivingToken` pool is active when the function is called, but it becomes inactive after the initial check. The function will proceed with the payback operation, and when it tries to calculate the receiving amount using the `getReceivingToken` function, it may encounter issues or unexpected behavior due to the pool being inactive.
Similarly, if the `_paybackToken` pool becomes inactive after the initial check, the subsequent operations involving that pool, such as transferring the payback amount, may fail or lead to inconsistent state.


## Tools Used
Manual
## Recommended Mitigation Steps 
Perform the pool activity check not only at the beginning of the function but also immediately before any operations involving those pools. This way, you can ensure that the pools are still active before performing critical operations.

       function paybackBadDebtForToken(
           uint256 _nftId,
           address _paybackToken,