xanoutas/solidity-security-findings huggingface dataset:
Well collateralized loans can be locked forever
Audit: 2024-04-lavarage-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L12


# Vulnerability details

## Impact
Borrowed funds might never be returned, and collateral can be stuck inside the position, potentially forever.

This happens when the borrower doesn't repay the loan and if it remains collateralized to the extent that it can never be liquidated, as there aren't any time limits for the loan.

## Proof of Concept
Borrowers can [borrow](https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L12) well collaterilazed loans that can't be liquidated anytime soon as they have an infinite duration. Lenders cannot extract the collateral (supposing a healthy LTV), no matter how much time passes.

Borrowers might have no incentive to do so, but this scenario can easily happen anyway: for example, borrowers might lose access to their wallets or they might even die.

If this happens, funds are stuck inside the position until the LTV is high enough to liquidate the position (potentially never).

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider implementing a maximum duration for the loan: if this time passes and the borrower doesn't repay it, the lender should be able to liquidate it.


## Assessed type

Timing
Tokens can be stolen during liquidation
Audit: 2024-04-lavarage-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/liquidate.rs#L37-L41


# Vulnerability details

## Proof of Concept
When liquidation happens, then user should query oracle API and provide address of account that should be liquidated. I don't know the code of oracle, but this service then signs transaction and then anyone can execute it later. It's likely that Oracle checks position size of Position and signs it.

There are 2 checks inside `liquidate` function. First is that [LTV is more than 90%](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/liquidate.rs#L27) and another is that [wallet operator is same as provided operator](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/liquidate.rs#L27).

The problem is that there is not check that provided wallet is used as [trading pool's operator](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/state/pool.rs#L12). Thus any node wallet can be provided which allows attacker to steal funds as they [will be sent to the operator](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/liquidate.rs#L66-L70).

## Impact
Tokens can be stolen.

## Tools Used
VsCode
## Recommended Mitigation Steps
Check that provided wallet is same as inside trading pool of position.


## Assessed type

Error
Borrowers can steal lenders funds
Audit: 2024-04-lavarage-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L98-L103


# Vulnerability details

## Proof of Concept
When trader borrows sol from trading pool, then position account [is created](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/context/borrow.rs#L8-L9). Pls, note, that `random_account_as_id` field that [is provided by trader](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/context/borrow.rs#L23) is used to create PDA. This is needed, because same trader can have several position with same trading pool.

`borrow` function checks that [there will be `add_collateral` instruction](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L40) later in transaction and only after that it [borrows sol to the trader](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L77). This is because `add_collateral` should check that trader has provided tokens as collateral. Also  `borrow` function [stores `random_account_as_id`](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L87) to the position, so we know which random was used to derive position account.

`add_collateral` function checks that position account is now [funded with enough amount of tokens](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L98-L103) to cover borrowed funds. `add_collateral` function is provided with context, where [position account is provided](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/context/add_collateral.rs#L8-L10) with `random_account_as_id` as well.

The problem is that trader can provide another position, which [is already collateralized](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L98-L103). In that case trader will be able to steal all borrowed funds.

Pls, note that there is same problem inside `swapback.rs`, which allows to steal tokens from position without repaying sol on it.

## Impact
Funds can be stolen.

## Tools Used
VsCode
## Recommended Mitigation Steps
In the function that is responsible for the check of final instruction you should check that position account provided is same.


## Assessed type

Error
Lack of freeze authority check for collateral tokens on create trading pool
Audit: 2024-04-lavarage-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/lending.rs#L9-L20


# Vulnerability details


## Impact
SPL tokens are used as collateral in the protocol. On borrow, there is a transfer from the borrower into a PDA (position account). On repay, the other way around.  
However, SPL token could have a freeze authority. Therefore, any account is vulnerable to be frozen. This could be harmful for both borrowers and lenders. I beleive, The protocol should be resilient enough to not fall into such situations where the funds are locked and borrowing or repaying are DoSed.

## Proof of Concept

There is no check for freeze authority of the mint (i.e. token).

More info on freeze authority feature:
> The Mint may also contain a freeze_authority which can be used to issue FreezeAccount instructions that will render an Account unusable. Token instructions that include a frozen account will fail until the Account is thawed using the ThawAccount instruction. The SetAuthority instruction can be used to change a Mint's freeze_authority. If a Mint's freeze_authority is set to None then account freezing and thawing is permanently disabled and all currently frozen accounts will also stay frozen permanently.

[SPL Token#freezing-accounts](https://spl.solana.com/token#freezing-accounts)

## Tools Used
Manual analysis

## Recommended Mitigation Steps

Ensure the collateral token does not have an active freeze_authority. If the freeze_authority was set to None, then freezing feature can never work again.




## Assessed type

Access Control
The borrower can receive the opening fee, profit_share and interest_share
Audit: 2024-04-lavarage-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L12
https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L137


# Vulnerability details


## Impact
`feeReceipient` is not checked on `borrow` and `repay`. The borrower can pass any account, even their account so they receive the fee. (i.e. free borrowing). Also, `profit_share` and `interest_share` will be transffered to the borrower instead of the platform.

This applies on `borrow` for the openning fee and on `repay` for both `profit_share` and `interest_share`.

## Proof of Concept

- On borrow,  `fee_receipient` is unchecked
```rust
  #[account(mut)]
  pub fee_receipient: UncheckedAccount<'info>,
```
[borrow.rs#L26](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/context/borrow.rs#L26)

- On repay,  `fee_receipient` is unchecked too
```rust
  #[account(mut)]
  pub fee_receipient: UncheckedAccount<'info>,
```
[repay_sol.rs#L23](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/context/repay_sol.rs#L23)


- On `borrow` and `repay` functions, there is no validation against the passed `fee_receipient` account.

## Tools Used
Manual analysis

## Recommended Mitigation Steps

Add a config account on the protocol level, this config will have `fee_receipient` to be set by the admin. Then, on `borrow` and `repay`, check the passed account if it matches against the one from the config.




## Assessed type

Invalid Validation
Borrowers can avoid the payment of an interest share fee by setting themselves as a `fee_receipient`
Audit: 2024-04-lavarage-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L185
https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L192
https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/context/repay_sol.rs#L23


# Vulnerability details

## Impact
Borrowers can avoid the payment of the 20% interest share fee on their accumulated interest

## Proof of Concept
The current implementation of the `repay_sol` function sends a 20% interest share fee to the specified `fee_receipient` account that is passed in to it. However, as it can be seen, that account is a user specified one:

```rust
    let transfer_instruction3 = system_instruction::transfer(
    &ctx.accounts.trader.key(),
    &ctx.accounts.fee_receipient.key(),
    interest_share,
    );
    anchor_lang::solana_program::program::invoke(
        &transfer_instruction3,
        &[
            ctx.accounts.trader.to_account_info(),
            ctx.accounts.fee_receipient.to_account_info(),
        ],
    )?;
```

```rust
    /// CHECK: We just want the value of this account
    #[account(mut)]
    pub fee_receipient: UncheckedAccount<'info>,
```

What this means, is that the user can take advantage of that and avoid the payment of an interest share fee, by simply passing in a public key that is controlled by them for that value. It is unclear who exactly is supposed to be the on the receiving end for the fee payment, but what's important is that that they can easily be easily be prevented from receiving it.


## Tools Used
Manual review

## Recommended Mitigation Steps
Apply some restrictions on the `fee_receipient` public key value. For example, you can make it be a property of the `Pool` struct that is set on the creation of each new trading pool by its operator.





## Assessed type

Invalid Validation
Innocent borrower could incur losses caused by a malicious lender
Audit: 2024-04-lavarage-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L12


# Vulnerability details

## Impact
The protocol allows the lender to change the interest rate anytime. However, since the new interest rate is stored on trading pool level, the lender could front-run a borrowing transaction that's yet to be processed, updating the interest rate too high (up to 99). This is harmful to the borrower even if the borrower repays the SOL immediately. 
That's because the minimum elapsed days on repay is set to be one
```rust
  let days_elapsed = ((current_timestamp - timestamp) as u64 / 86400) + 1; // +1 to ensure interest is charged from day 0
```
[swapback.rs#L145](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L145)



## Proof of Concept
- Update interest rate on pool level
	[lending.rs#L38-L51](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/lending.rs#L38-L51)

- Minimum elapsed days is 1
	```rust
	  let days_elapsed = ((current_timestamp - timestamp) as u64 / 86400) + 1; // +1 to ensure interest is charged from day 0
	```
	[swapback.rs#L145](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L145)


## Tools Used
Manual analysis

## Recommended Mitigation Steps

Allow the borrower to pass maximum interest rate, this protects the borrower from any change of the interest rate that occur after they send their TX. 

Another suggestion: store the interest rate on position level instead.



## Assessed type

Other
Small loans will never be liquidated, generating bad debt for lenders
Audit: 2024-04-lavarage-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L12
https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/liquidate.rs#L19


# Vulnerability details

## Impact
There isn't a minimum position requirement for borrowers when they start a loan.

Malicious borrowers might abuse this to create a large amount of small loans that will be unprofitable to liquidate. This results in a total loss of funds for lenders as they will get only bad debt.

## Proof of Concept
1) A malicious borrower [starts](https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/swap.rs#L12) multiple loans with extremely low amounts of funds borrowed in each one, in a single transaction (by chaining `borrow - add_collateral - borrow - ...` with different positions on the same pool). They get the same amount of funds as they would have got with a single big loan AND they pay just for a single transaction.
2) When the LTV goes > 90% the lender should [liquidate](https://github.com/code-423n4/2024-04-lavarage/blob/9e8295b542fb71b2ba9b4693e25619585266d19e/libs/smart-contracts/programs/lavarage/src/processor/liquidate.rs#L19) each of these loans, but it will be unprofitable to do so as they NEED to execute and pay for multiple transactions.
3) The lender will never liquidate all the loans (as it's unprofitable) so they only get bad debt.

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider implementing a minimum amount to start a loan.


## Assessed type

Invalid Validation
Borrower can use own address as fee recipient to pay less interest to lender
Audit: 2024-04-lavarage-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L183-L187


# Vulnerability details

## Proof of Concept
When user repays sol, then he can provide [fee recipient](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/context/repay_sol.rs#L23). This is designed to be UI account that receives that fee from users who use it. But it also possible that users will do calls directly.

Let's check what receives fee recipient during repay. It receives [profit share](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L170-L174) which is extra amount earned by borrower. Also it receives [lender interest share](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L183-L187), which is [part of interest that were accrued](https://github.com/code-423n4/2024-04-lavarage/blob/main/libs/smart-contracts/programs/lavarage/src/processor/swapback.rs#L151).

There are 2 problems here:
- Lenders receive smaller interest rate than they provided in the pool
- Borrowers that don't use UI can have smaller interest rate by providing own address as fee recipient.

## Impact
Lenders do not receive full interests and borrowers can pay less.

## Tools Used
VsCode
## Recommended Mitigation Steps
I believe that fee recipient should not get interest share, only profit.


## Assessed type

Error
Upgraded Q -> 3 from #264 [1691857350267]
Audit: 2023-07-pooltogether-findings
Severity: HIGH

Judge has assessed an item in Issue #264 as 3 risk. The relevant finding follows:

 Let's see how it can be exploited. You can add this test to Withdraw.t.sol and run with forge test -vv --match-contract VaultWithdrawTest --match-test testWithdrawAllAssetsForHalfShares:

  function testWithdrawAllAssetsForHalfShares() external {
    vm.startPrank(alice);
    
    // Alice deposits type(uint96).max amount of assets
    uint256 _amount =  uint256(type(uint96).max);
    underlyingAsset.mint(alice, _amount);
    _deposit(underlyingAsset, vault, _amount, alice);
    console2.log("shares", vault.balanceOf(alice)); // shares 79228162514264337593543950335

    // Alice again deposits type(uint96).max amount of assets
    underlyingAsset.mint(alice, _amount);
    _deposit(underlyingAsset, vault, _amount, alice);
    console2.log("shares", vault.balanceOf(alice)); // shares 158456325028528675187087900670

    // Alice withdraws max possible amount of assets
    vault.withdraw(vault.maxWithdraw(alice), alice, alice);
    // Alice receives all assets
    console2.log("assets", underlyingAsset.balanceOf(alice)); // assets 158456325028528675187087900670
    // Alice still has a half of shares amount
    console2.log("shares", vault.balanceOf(alice)); // shares 79228162514264337593543950336
    
    // Due to rounding we should extract `1` from balance
    assertEq(vault.balanceOf(alice) - 1, type(uint96).max);
    assertEq(underlyingAsset.balanceOf(alice), _amount * 2);

    vm.stopPrank();
  }
Users can have balances more than type(uint96).max because of uint112 in the field balance of AccountDetails struct.
But burning from their balances will be only for amounts less than type(uint96).max because of bits cut off during type conversion in the _burn.
Unsecure and predictable random number generation in closeDraw.winningRandomNumber_()
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L348-#L387


# Vulnerability details

## Impact
Unsecure and predictable random number generation in closeDraw.winningRandomNumber_() can lead to external influence by malicious attackers. Leading to undermining of the fairness and security and unpredictability of the draw function. Both the timestamp and the block hash can be influenced by miners to some degree. 


## Proof of Concept

File: PrizePool.sol
Code Link: https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L348C3-L387C1
Code:
  function closeDraw(uint256 winningRandomNumber_) external onlyDrawManager returns (uint16) {
    // check winning random number
    if (winningRandomNumber_ == 0) {
      revert RandomNumberIsZero();
    }
    if (block.timestamp < _openDrawEndsAt()) {
      revert DrawNotFinished(_openDrawEndsAt(), uint64(block.timestamp));
    }


    uint8 _numTiers = numberOfTiers;
    uint8 _nextNumberOfTiers = _numTiers;


    if (lastClosedDrawId != 0) {
      _nextNumberOfTiers = _computeNextNumberOfTiers(_numTiers);
    }


    uint64 openDrawStartedAt_ = _openDrawStartedAt();


    _nextDraw(_nextNumberOfTiers, uint96(_contributionsForDraw(lastClosedDrawId + 1)));


    _winningRandomNumber = winningRandomNumber_;
    claimCount = 0;
    canaryClaimCount = 0;
    largestTierClaimed = 0;
    _lastClosedDrawStartedAt = openDrawStartedAt_;
    _lastClosedDrawAwardedAt = uint64(block.timestamp);


    emit DrawClosed(
      lastClosedDrawId,
      winningRandomNumber_,
      _numTiers,
      _nextNumberOfTiers,
      _reserve,
      prizeTokenPerShare,
      _lastClosedDrawStartedAt
    );


    return lastClosedDrawId;
  }



## Tools Used
Manual Review

## Recommended Mitigation Steps

Do not use block.timestamp as a source of randomness, instead use various multiple time sources and external consensus mechanisms. The current block timestamp must be strictly larger than the timestamp of the last block, but the only guarantee is that it will be somewhere between the timestamps of two consecutive blocks in the canonical chain.


## Assessed type

Timing
Sponsor function allows voiding some elses chance to win
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L988
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L480-L482


# Vulnerability details

## Impact
Anyone can delegate someone elses balance to the sponsorship address, increasing their own likelihood of winning, while voiding the victims chance.

## Proof of Concept

The issue is in the call-chain starting with `Vault.sponsor`:

```js
//Vault
function sponsor(uint256 _assets, address _receiver) external returns (uint256) {
    return _sponsor(_assets, _receiver);
}

function _sponsor(uint256 _assets, address _receiver) internal returns (uint256) {
uint256 _shares = deposit(_assets, _receiver);

if (
    _twabController.delegateOf(address(this), _receiver) != _twabController.SPONSORSHIP_ADDRESS()
) {
    _twabController.sponsor(_receiver);
}

//TwabController
function sponsor(address _from) external {
    _delegate(msg.sender, _from, SPONSORSHIP_ADDRESS);
}

function _delegate(address _vault, address _from, address _to) internal {
    address _currentDelegate = _delegateOf(_vault, _from);
    
    ...

    _transferDelegateBalance(
      _vault,
      _currentDelegate,
      _to,
      uint96(userObservations[_vault][_from].details.balance)
    );
```

Essentially anyone can call `Vault.sponsor` to deposit any amount of assets (even 0) to any receiver and the TwabController will then re-delegate the whole balance from the current delegate of the receiver to the `SPONSORSHIP_ADDRESS`. 

## Tools Used

Manual Review

## Recommended Mitigation Steps

Sponsoring should be restricted to `msg.sender` as receiver


## Assessed type

Invalid Validation