xanoutas/solidity-security-findings huggingface dataset:
For a public vault, first liquidity provider, who has 10 ether asset tokens, can block other users from becoming liquidity providers
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC4626-Cloned.sol#L38-L52
https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC4626-Cloned.sol#L129-L133
https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC20-Cloned.sol#L172-L183


# Vulnerability details

## Impact
For a public vault, calling the `AstariaRouter.mint` or `PublicVault.mint` function can eventually call the following `ERC4626Cloned.mint` function. Because the `ERC4626Cloned.previewMint` function returns `10e18` when the public vault's `totalSupply()` returns 0, the `ERC4626Cloned.mint` function would transfer `10e18` asset tokens from the first liquidity provider to the public vault and mint the specified number of shares in return. Hence, as the first liquidity provider of the public vault, a user who has 10 ether asset tokens can, for example, call the `AstariaRouter.mint` function with the `shares` and `maxAmountIn` inputs both being `type(uint256).max` to mint `type(uint256).max` shares while only lending 10 ether asset tokens. Afterwards, all other users' `AstariaRouter.mint`, `PublicVault.mint`, `AstariaRouter.deposit`, and `PublicVault.deposit` function calls will all revert since the public vault's total supply is already `type(uint256).max`, and the `ERC20Cloned._mint` function's execution of `s._totalSupply += amount` will overflow. As a result, for this public vault, the first liquidity provider is able to block other users from becoming liquidity providers and becomes the only one who can earn yield.

https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC4626-Cloned.sol#L38-L52
```solidity
  function mint(
    uint256 shares,
    address receiver
  ) public virtual returns (uint256 assets) {
    assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.
    require(assets > minDepositAmount(), "VALUE_TOO_SMALL");
    // Need to transfer before minting or ERC777s could reenter.
    ERC20(asset()).safeTransferFrom(msg.sender, address(this), assets);

    _mint(receiver, shares);

    emit Deposit(msg.sender, receiver, assets, shares);

    afterDeposit(assets, shares);
  }
```

https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC4626-Cloned.sol#L129-L133
```solidity
  function previewMint(uint256 shares) public view virtual returns (uint256) {
    uint256 supply = totalSupply(); // Saves an extra SLOAD if totalSupply is non-zero.

    return supply == 0 ? 10e18 : shares.mulDivUp(totalAssets(), supply);
  }
```

https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC20-Cloned.sol#L172-L183
```solidity
  function _mint(address to, uint256 amount) internal virtual {
    ERC20Data storage s = _loadERC20Slot();
    s._totalSupply += amount;

    // Cannot overflow because the sum of all user
    // balances can't exceed the max uint256 value.
    unchecked {
      s.balanceOf[to] += amount;
    }

    emit Transfer(address(0), to, amount);
  }
```

## Proof of Concept
Please add the followi
Strategist can fail to withdraw asset token from a private vault
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC4626RouterBase.sol#L41-L52
https://github.com/code-423n4/2023-01-astaria/blob/main/src/Vault.sol#L70-L73


# Vulnerability details

## Impact
Calling the `AstariaRouter.withdraw` function calls the following `ERC4626RouterBase.withdraw` function; however, calling `ERC4626RouterBase.withdraw` function for a private vault reverts because the `Vault` contract does not have an `approve` function. Directly calling the `Vault.withdraw` function for a private vault can also revert since the `Vault` contract does not have a way to set the allowance for itself to transfer the asset token, which can cause many ERC20 tokens' `transferFrom` function calls to revert when deducting the transfer amount from the allowance. Hence, after depositing some of the asset token in a private vault, the strategist can fail to withdraw this asset token from this private vault and lose this deposit.

https://github.com/AstariaXYZ/astaria-gpl/blob/main/src/ERC4626RouterBase.sol#L41-L52
```solidity
  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();
    }
  }
```

https://github.com/code-423n4/2023-01-astaria/blob/main/src/Vault.sol#L70-L73
```solidity
  function withdraw(uint256 amount) external {
    require(msg.sender == owner());
    ERC20(asset()).safeTransferFrom(address(this), msg.sender, amount);
  }
```

## Proof of Concept
Please add the following test in `src\test\AstariaTest.t.sol`. This test will pass to demonstrate the described scenario.

```solidity
  function testPrivateVaultStrategistIsUnableToWithdraw() public {
    uint256 amountToLend = 50 ether;
    vm.deal(strategistOne, amountToLend);

    address privateVault = _createPrivateVault({
      strategist: strategistOne,
      delegate: strategistTwo
    });

    vm.startPrank(strategistOne);

    WETH9.deposit{value: amountToLend}();
    WETH9.approve(privateVault, amountToLend);

    // strategistOne deposits 50 ether WETH to privateVault
    Vault(privateVault).deposit(amountToLend, strategistOne);

    // calling router's withdraw function for withdrawing assets from privateVault reverts
    vm.expectRevert(bytes("APPROVE_FAILED"));
    ASTARIA_ROUTER.withdraw(
      IERC4626(privateVault),
      strategistOne,
      amountToLend,
      type(uint256).max
    );

    // directly withdrawing various asset amounts from privateVault also fails
    vm.expectRevert(bytes("TRANSFER_FROM_FAILED"));
    Vault(privateVault).withdraw(amountToLend);

    vm.expectRevert(bytes("TRANSFER_FROM_FAILED"));
    Vault(privateVault).withdraw(1);

    vm.stopPrank();
  }
```

## Tools Used
VSCode

## Recommended Mitigation Steps
https://github.com/code-423n4/2023-0
Funds may be locked in `WithdrawProxy` due to wrong `withdrawRatio` decimals
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/WithdrawProxy.sol#L273


# Vulnerability details

## Impact
Liquidation auction proceedings may be locked in `WithdrawProxy` indefinitely if the asset of the vault has less than 18 decimals.
## Proof of Concept
The [claim](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/WithdrawProxy.sol#L240) function of `WithdrawProxy` is used to distribute auction proceedings after collateral liquidation. If an auction ends in another epoch, a `Withdraw` proxy [is deployed](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L663-L671) and is [set as the payee](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L326-L328). `WithdrawProxy` is also used by liquidity providers to receive liquidity they have removed from a vault: `WithdrawProxy` [serves as an intermediary contract](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L173-L189) when liquidity provider redeem their shares.

To separate accounting of liquidation proceedings and withdrawn liquidity, `WithdrawProxy` uses the `withdrawRatio` variable, which is [set when processing the current epoch](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L312-L319): withdraw ratio tells how much tokens to leave in `WithdrawProxy` during claiming, for liquidity providers to redeem them from the contract ([WithdrawProxy.sol#L271-L282](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/WithdrawProxy.sol#L271-L282)):
```solidity
transferAmount = uint256(s.withdrawRatio).mulDivDown(
  balance,
  10**ERC20(asset()).decimals()
);

unchecked {
  balance -= transferAmount;
}

if (balance > 0) {
  ERC20(asset()).safeTransfer(VAULT(), balance);
}
```

In the above snippet, in the `transferAmount` calculation, the current contract's balance is multiplied by the withdraw ratio. However, the denominator in the `mulDivDown` is wrong: it should always be `10e18` because withdraw ratio always has 18 decimals since it's calculated as a ratio of total supplies of shares of a vault and a `WithdrawProxy` ([PublicVault.sol#L312-L319](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L312-L319)):
```solidity
uint256 proxySupply = currentWithdrawProxy.totalSupply();

s.liquidationWithdrawRatio = proxySupply
  .mulDivDown(1e18, totalSupply())
  .safeCastTo88();

currentWithdrawProxy.setWithdrawRatio(s.liquidationWithdrawRatio);
```

As a result, in a vault that uses an asset with less than 18 decimals (e.g. USDC), claiming assets from `WithdrawProxy` wil positive `withdrawRatio` will always fail because the [transferAmount](https://gi
Borrowers' debt may be bigger than the maximal potential debt, potentially leading to bad debt
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L491


# Vulnerability details

## Impact
Vaults may accumulate bad debt and liquidity providers may lose rewards due to over-borrowed positions.
## Proof of Concept
One of the loan terms a strategist may set is [maxPotentialDebt](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/interfaces/ILienToken.sol#L56), which is the maximal debt a lien can have. The number includes the loan principal and the total amount of interest accrued over the period of a loan ([LienToken.sol#L715-L725](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L715-L725)). When a new lien is created, there's a check that ensures that the total potential debt of a stack is not bigger than `maxPotentialDebt` ([LienToken.sol#L491](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L491)). However, this check is only applied when there's at least one existing lien in the stack: when the lien created is the first lien of a borrower, the stack will be empty and the check will be skipped.

This allows borrowers to take loans that have more potential debt than `maxPotentialDebt`, which may make the potential debt of a loan bigger than `liquidationInitialAsk`. `maxPotentialDebt` and `liquidationInitialAsk` are two crucial parameters that control bad debt of vaults: if `maxPotentialDebt` is bigger than `liquidationInitialAsk`, then the potential debt of a position may be bigger than the starting auction price of the collateral ([liquidationInitialAsk sets the starting auction price during liquidation](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/AstariaRouter.sol#L644)). In the best case (if a collateral is sold right at `liquidationInitialAsk`), liquidity providers will lose rewards in the amount of `maxPotentialDebt - liquidationInitialAsk`. In the worst case (if `maxPotentialDebt - liquidationInitialAsk` is bigger than the interest generate by the loan), liquidity providers will lose all rewards for providing funds to the borrower and the liquidation will create a bad debt for the vault.

### Exploit Scenario
1. An NFT costs 50 ETH at the market. A strategist creates a vault and signs the following loan terms for the NFT:
  1. max amount: 50 ETH;
  1. max potential debt: 50 ETH;
  1. liquidation initial ask: 51 ETH (the amount is higher to leave room for price fluctuations).
In the worst case scenario, the strategist expects that a loan backed by the NFT will generate no more than 50 ETH of debt, and that the NFT may be liquidated at 51-50 ETH, which will allow to cover the entire debt.
1. The holder of the NFT takes a maximal loan of 50 ETH. However, due to the skipped maximal potential debt check, the borrower's potential debt is 52 ETH.
1. T
Vault may be drained after a liquidated NFT was claimed by the liquidator
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/ClearingHouse.sol#L220-L231


# Vulnerability details

## Impact
The owner of a collateral NFT that was liquidated and then claimed by the liquidator (after the auction had no bids) may drain the vault the loan was taken from.
## Proof of Concept
There's an extreme situation when a liquidated and auctioned collateral NFT had no bids and the auction has expired. In this situation, the liquidator may claim the NFT by calling [CollateralToken.liquidatorNFTClaim](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L109). The function:
1. calls [ClearingHouse.settleLiquidatorNFTClaim](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/ClearingHouse.sol#L220) to burn the lien token associated with the loan and clean up the accounting without repaying the actual loan (the loan cannot be repaid since there were no bids);
1. [releases the collateral NFT to the liquidator](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L352).

However, the function doesn't settle the auction. As a result:
1. the `CollateralToken` is not burned ([CollateralToken.sol#L538](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L538));
1. the link between the collateral ID and the underlying token is not removed ([CollateralToken.sol#L537](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L537));
1. the link between the collateral ID and the auction is also not removed ([CollateralToken.sol#L544](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L544)).

This allows the owner of the liquidated collateral NFT to create a new lien and take the maximal loan without providing any collateral.

### Exploit Scenario
1. Alice deposits an NFT token as a collateral and takes a loan.
1. Alice's loan expires and her NFT collateral gets liquidated by Bob.
1. The collateral NFT wasn't sold off auction as there were no bids.
1. Bob claims the collateral NFT and receives it.
1. Alice takes another loan from the vault without providing any collateral.

The following PoC demonstrates the above scenario:
```solidity
// src/test/AstariaTest.t.sol
function testAuctionEndNoBidsMismanagement_AUDIT() public {
  address bob = address(2);
  TestNFT nft = new TestNFT(6);
  uint256 tokenId = uint256(5);
  address tokenContract = address(nft);

  // Creating a public vault and providing some liquidity.
  address publicVault = _createPublicVault({
    strategist: strategistOne,
    delegate: strategistTwo,
    epochLength: 14 days
  });

  _lendToVault(Lender({addr: bob, amountToLend: 150 ether}), publicVault);
  (, ILienToken.Sta
Inexistent Access Control of Clearing House
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The `safeTransferFrom` function of the `ClearingHouse` does not apply any access control to its caller, permitting it to be arbitrarily invoked. GIven that this function is meant to be invoked as part of an OpenSea workflow, it is possible to hijack the said workflow and permit anyone to settle the auction before it is meant to be.

## Proof of Concept

Any invocation of `safeTransferFrom` with valid arguments will succeed currently if invoked by any `address`.

## Tools Used

Manual review of the codebase.

## Recommended Mitigation Steps

We advise the `safeTransferFrom` function to apply proper limitations as to who invokes it as presently, the security of an OpenSea exchange workflow is compromised. Examples of proper management include validating that the caller is the OpenSea special-purpose contract that performs NFT transfers or limiting it to a user-defined address / address list if multiple exchange types similar to OpenSea are desired to be supported.
Buying out corrupts the slope of a vault, reducing rewards of LPs
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L189
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L627-L628


# Vulnerability details

## Impact
After a buyout, the slope of a vault won't be increased. As a result, liquidity providers will lose reward for providing liquidity to borrowers and the borrower will not pay interest for the lien that was bought out.
## Proof of Concept
Buyout is an important refinancing mechanism that allows borrowers to apply new terms (e.g. changed loan rate and/our duration) to their loans. [The implementation of the mechanism](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/VaultImplementation.sol#L313) allows borrower to repay the owed amount for a lien, burn the lien, and create a new lien. When burning and creating liens it's important to update [the slope of a vault](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L627-L628): is the total interest accrued by vaults. However, during a buyout the slope of the vault where a new lien is created is not increased:
1. after a new lien is created, the slope of the vault is not increased ([LienToken.sol#L127](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L127));
1. however, the slope of the vault is decreased after the old lien is burned ([LienToken.sol#L189](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L189), [PublicVault.sol#L627-L628](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L627-L628))

Since, during a buyout, a lien with a different interest rate may be created (due to changed loan terms), the slope of the vault must be updated correctly:
1. the slope of the previous lien must be reduced from the total slope of the vault;
1. the slope of the new lien must be added to the total slope of the vault.

If the slope of the new lien is not added to the total slope of the vault, then the lien doesn't generate interest, which means the borrower doesn't need to pay interest for taking the loan and liquidity providers won't be rewarded for providing funds to the borrower.

The following PoC demonstrates that the slope of a vault is 0 after the only existing lien was bought out:
```solidity
// src/test/AstariaTest.t.sol
function testBuyoutLienWrongSlope_AUDIT() public {
  TestNFT nft = new TestNFT(1);
  address tokenContract = address(nft);
  uint256 tokenId = uint256(0);

  uint256 initialBalance = WETH9.balanceOf(address(this));

  // create a PublicVault with a 14-day epoch
  address publicVault = _createPublicVault({
    strategist: strategistOne,
    delegate: strategistTwo,
    epochLength: 14 days
  });
  vm.label(publicV
Buying out corrupts the counter of liens and impairs epochs processing
Audit: 2023-01-astaria-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L189
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L633


# Vulnerability details

## Impact
After a buyout, the lien counter of the epoch of the new lien won't be increased. As a result, the epoch may be processed when there's at least one unpaid lien, leaving the vault with a bad debt that cannot be repaid because, after an epoch was processed, it belongs to an older epoch.
## Proof of Concept
Buyout is an important refinancing mechanism that allows borrowers to apply new terms (e.g. changed loan rate and/our duration) to their loans. [The implementation of the mechanism](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/VaultImplementation.sol#L313) allows borrower to repay the owed amount for a lien, burn the lien, and create a new lien. When burning and creating liens it's important to update [lien counters for epochs](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L538-L560): an epoch cannot be processed [if the count of liens in the epoch is greater than 0](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L303-L305). However, during a buyout the lien count is not increased for the new lien:
1. after a new lien is created, the counter is not increased in the vault ([LienToken.sol#L127](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L127));
1. however, a respective counter is decreased after the old lien is burned ([LienToken.sol#L189](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/LienToken.sol#L189), [PublicVault.sol#L633](https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L633))

Since the net number of liens before a buyout is 0 (one is created and one is burnt), the counters must be updated correctly: the counter for the epoch of the burned lien must be decreased by 1 and the counter of the epoch of the created lien must be increased by 1.

To demonstrate the bug we need to add a getter to `PublicVault` because epoch data is not public in the current implementation:
```solidity
function getEpochData(uint256 epoch) public returns (IPublicVault.EpochData memory epochData) {
    VaultData storage s = _loadStorageSlot();
    epochData = s.epochData[uint64(epoch)];
}
```

After than, we can clearly see that the counter of the epoch of the new lien is not increased after a buyout:
```solidity
// src/test/AstariaTest.t.sol
function testBuyoutLien_AUDIT() public {
  TestNFT nft = new TestNFT(1);
  address tokenContract = address(nft);
  uint256 tokenId = uint256(0);

  uint256 initialBalance = WETH9.balanceOf(addre