xanoutas/solidity-security-findings huggingface dataset:
Users can avoid paying `creatorFee` during minting
Audit: 2024-08-phi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L264-L304


# Vulnerability details

`PhiFactory` allows minting of NFT using [`claim()`](https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L264-L304) function, which calls the `merkleClaim()` and `signatureClaim()` functions.
However, the `claim()` function does not verify whether the user is sending ether to the contract, leading to potential misuse of protocol funds to cover the `creatorFee`.

```solidity
__ SNIP __
            MintArgs memory mintArgs = MintArgs({ tokenId: tokenId, quantity: quantity_, imageURI: imageURI_ });
            uint256 mintFee = getArtMintFee(artId, quantity_);
@>          this.merkleClaim{ value: mintFee }(proof, claimData, mintArgs, leafPart_);
__ SNIP __
            MintArgs memory mintArgs = MintArgs({ tokenId: tokenId, quantity: quantity_, imageURI: imageURI_ });
            uint256 mintFee = getArtMintFee(artId, quantity_);
@>          this.signatureClaim{ value: mintFee }(signature_, claimData, mintArgs);
__ SNIP __
```

This design flaw allows users to mint NFTs without paying the fee out-of-pocket, as the contract’s balance can be used instead. Attackers can exploit this by creating an art and subsequently calling `claim()` function to drain the contract’s balance.

## Impact

The primary risk is that the contract's funds could be drained or misused. Attackers could deplete the contract balance by exploiting this vulnerability, or use the contract’s funds to cover `creatorFee` instead of paying out of their own funds.

## Proof of Concept

The following code snippet is a test case to show this exploit scenario.

```solidity
    function test_claim_without_pay_fee() public {
        _createArt(ART_ID_URL_STRING);
        uint256 artId = 1;
        bytes32 advanced_data = bytes32("1");
        bytes memory signData =
            abi.encode(expiresIn, participant, referrer, verifier, artId, block.chainid, advanced_data);
        bytes32 msgHash = keccak256(signData);
        bytes32 digest = ECDSA.toEthSignedMessageHash(msgHash);
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(claimSignerPrivateKey, digest);
        if (v != 27) s = s | bytes32(uint256(1) << 255);
        bytes memory signature = abi.encodePacked(r, s);
        bytes memory data =
            abi.encode(1, participant, referrer, verifier, expiresIn, uint256(1), advanced_data, IMAGE_URL, signature);
        bytes memory dataCompressed = LibZip.cdCompress(data);
        uint256 totalMintFee = phiFactory.getArtMintFee(1, 1);
        // add balance to PhiFatory
        vm.deal(address(phiFactory), totalMintFee);

        vm.startPrank(participant, participant);
        // call claim() function without send ether
        phiFactory.claim(dataCompressed);

        // referrer payout
        address artAddress = phiFactory.getArtAddress(1);
        assertEq(IERC1155(artAddress).balanceOf(participant, 1), 1, "particpiant erc1155 balance");
`UUPSUpgradeable` is not initialized in `PhiNFT1155` contract
Audit: 2024-08-phi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-phi/blob/8c0985f7a10b231f916a51af5d506dd6b0c54120/src/art/PhiNFT1155.sol#L96-L125


# Vulnerability details

## Impact
The `PhiNFT1155` contract inherits from OpenZeppelin's `UUPSUpgradeable` but does not call `__UUPSUpgradeable_init()` in its `initialize` function. This omission can lead to the following issues:
-  **Incomplete Initialization**: The UUPS upgrade mechanism might not be properly initialized, which could result in the contract not functioning as intended with regard to upgrades.

-  **Upgrade Insecurity**: Without proper initialization, there may be a lack of safeguards against unauthorized upgrades, potentially allowing malicious actors to exploit the contract's upgradeability.


## Proof of Concept
Missing Initialization Call: The contract code lacks the call to `__UUPSUpgradeable_init()` in the `initialize` function.
```solidity
function initialize(
    uint256 credChainId_,
    uint256 credId_,
    string memory verificationType_,
    address protocolFeeDestination_
)
    external
    initializer
{
    __Ownable_init(msg.sender);
    __Pausable_init();
    __ReentrancyGuard_init();
    initializeRoyalties(protocolFeeDestination_);
    
    ///@audit Missing __UUPSUpgradeable_init() call
}
```
Without this initialization, the contract’s upgrade authorization mechanism may not be properly set up, which can lead to security issues during upgrades.


## Tools Used
Manual Review

## Recommended Mitigation Steps
Add Initialization Call: Update the `initialize` function to include the call to `__UUPSUpgradeable_init()` to ensure proper setup of the UUPS upgrade mechanism:
```diff
function initialize(
    uint256 credChainId_,
    uint256 credId_,
    string memory verificationType_,
    address protocolFeeDestination_
)
    external
    initializer
{
    __Ownable_init(msg.sender);
    __Pausable_init();
    __ReentrancyGuard_init();
    initializeRoyalties(protocolFeeDestination_);
+   __UUPSUpgradeable_init(); // Add this line
}
```



## Assessed type

Other
Incorrect Fee Handling Prevents Protocol from Updating Fees
Audit: 2024-08-phi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-phi/blob/8c0985f7a10b231f916a51af5d506dd6b0c54120/src/PhiFactory.sol#L422-L434


# Vulnerability details

## Impact
The primary impact of this bug is that the protocol cannot change the mint and art creation fees as intended. This limitation has the following consequences:

The protocol is unable to set the correct mint fee of 0.00005 ETH per NFT minted, as [documented in the specifications](https://docs.philand.xyz/explore-phi/phi-protocol/ecosystem/protocol-fees#mint-fee):

```
   Mint Fee
   This fee is applied when a Minter wants to mint a Cred NFT.
   Protocol will have a mint price/fee of 0.00005 ETH.
```

```js
    function setProtocolFee(uint256 protocolFee_) external onlyOwner {
        if (protocolFee_ > 10_000) revert ProtocolFeeTooHigh(); // @audit <== This check is incorrect
        mintProtocolFee = protocolFee_;
        emit ProtocolFeeSet(protocolFee_);
    }

    function setArtCreatFee(uint256 artCreateFee_) external onlyOwner {
        if (artCreateFee_ > 10_000) revert ArtCreatFeeTooHigh(); // @audit <== This check is incorrect
        artCreateFee = artCreateFee_;
        emit ArtCreatFeeSet(artCreateFee_);
    }
```

This discrepancy between the intended fee (0.00005 ETH) and the maximum settable fee (10,000 wei) renders the fee adjustment mechanism ineffective.

The protocol loses the ability to adjust its revenue model in response to market conditions or strategic decisions, as both the mint fee and art creation fee are effectively locked at their initial values.

Below lines demonstrate that the fees are being applied as direct values rather than percentages, contrary to the setter functions' assumptions.

From `src/PhiFactory.sol`:
```solidity
   function _processClaim(
        uint256 artId_,
        address minter_,
        address ref_,
        address verifier_,
        uint256 quantity_,
        bytes32 data_,
        string memory imageURI_,
        uint256 etherValue_
    )
        private
    {
        PhiArt storage art = arts[artId_];

        // Handle refund
        uint256 mintFee = getArtMintFee(artId_, quantity_);
        if ((etherValue_ - mintFee) > 0) {
            _msgSender().safeTransferETH(etherValue_ - mintFee);
        }
        protocolFeeDestination.safeTransferETH(mintProtocolFee * quantity_); // @audit
```

From `src/art/PhiNFT1155.sol`:
```js
   function createArtFromFactory(uint256 artId_) external payable onlyPhiFactory whenNotPaused returns (uint256) {
        _artIdToTokenId[artId_] = tokenIdCounter;
        _tokenIdToArtId[tokenIdCounter] = artId_;

        uint256 artFee = phiFactoryContract.artCreateFee();

        protocolFeeDestination.safeTransferETH(artFee); // @audit
```

It's important to note that while the immediate financial impact may be limited if the initial fees were set correctly, the long-term implications of this inflexibility could be significant for the protocol's adaptability and financial management.

## Proof o
Refund sent to contract instead of user in `PhiFactory::_processClaim`
Audit: 2024-08-phi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L283
https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L378-L380
https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L740


# Vulnerability details

## Impact
In the `_processClaim` function of PhiFactory, excess ETH is refunded to `_msgSender()`, which is the contract itself when called internally, instead of the original transaction sender.

Users who overpay when claiming art (i.e., send more ETH than required for the mint fee) will have their excess funds trapped in the contract instead of being refunded. 

This leads to a direct loss of funds for users and may discourage
participation in the protocol, but limited to the amount of overpayment per transaction.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

Here's the call stack for this issue:

1. External call to `this.merkleClaim(...)` (same issue stands for `this.signatureClaim(...)`), which changes the `msg.sender` ([PhiFactory.sol#L283](https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L283)):

```javascript
// PhiFactory.sol
function claim(bytes calldata encodeData_) external payable {
    bytes memory encodedata_ = LibZip.cdDecompress(encodeData_);
    (uint256 artId) = abi.decode(encodedata_, (uint256));
    PhiArt storage art = arts[artId];
    uint256 tokenId = IPhiNFT1155Ownable(art.artAddress).getTokenIdFromFactoryArtId(artId);

    if (art.verificationType.eq("MERKLE")) {
        // existing code...

@>      this.merkleClaim{ value: mintFee }(proof, claimData, mintArgs, leafPart_);
    } else if (art.verificationType.eq("SIGNATURE")) {
        // existing code...

@>      this.signatureClaim{ value: mintFee }(signature_, claimData, mintArgs);
    } else {
        revert InvalidVerificationType();
    }
}
```

2. Internal call to `_processClaim` ([PhiFactory.sol#L378-L380](https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L378-L380)):

```javascript
// PhiFactory.sol
function merkleClaim(
    bytes32[] calldata proof_,
    bytes calldata encodeData_,
    MintArgs calldata mintArgs_,
    bytes32 leafPart_
)
    external
    payable
    whenNotPaused
{
    // existing code...

@>  _processClaim(
        artId_, minter_, ref_, art.credCreator, mintArgs_.quantity, leafPart_, mintArgs_.imageURI, msg.value
    );
}
```

3. Handling of refunds in `_processClaim` takes current `_msgSender()` into account, which is the contract itself ([PhiFactory.sol#L740](https://github.com/code-423n4/2024-08-phi/blob/main/src/PhiFactory.sol#L740)):

```javascript
// PhiFactory.sol
function _processClaim(
    uint256 artId_,
    address minter_,
    address ref_,
    address verifier_,
    uint256 quantity_,
    bytes32 data_,
    string memory imageURI_,
    uint256 etherValue_
)
    private
{
    PhiArt storage art = arts[artId
DutchAuction and BatchAuction is suspected to reorg attack
Audit: 2024-07-reserve-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/Broker.sol#L246
https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/Broker.sol#L283


# Vulnerability details

## Impact
Impact: High - funds[::sellAmount] can be stolen Likelihood: Low - as it requires an event of block reorganizations. Overall: Medium

## Proof of Concept
As mentioned in the report's title, reorgs can occur in all EVM chains and most likely on L2's like Arbitrum or Polygon, and as stated in the protocol's README Reserve is compatible with L2,s and even with mainnet, here are some reference links for some previous reorgs that happened in the past:
[Ethereum](https://decrypt.co/101390/ethereum-beacon-chain-blockchain-reorg) 
[Polygon](https://polygonscan.com/block/36757444/f?hash=0xf9aefee3ea0e4fc5f67aac48cb6e25912158ce9dca9ec6c99259d937433d6df8) - 2 years ago, this is with 120 blocks depth which means 4 minutes of re-written tx's 
Optimistic rollups (Optimism/Arbitrum) are also suspect to reorgs since if someone finds a fraud the blocks will be reverted, even though the user receives a confirmation.
These are the biggest events of reorgs that happened, here is a link for forked blocks, which means excluded blocks as a result of "Block Reorganizations" on Polygon: https://polygonscan.com/blocks_forked?p=1, where can be observed that at least two-digit block reorgs happen every month.

The vulnerability here is that Trade Auction creators rely on address derivation in advance or when trying to deploy the same address on different chains, any funds sent to the deployed contracts can be stolen.

Imagine the following scenario:

Alice calls `opentrade` and deploys a new Auction contract and funds it with `sellAmount`.
Bob has an active bot that observes the blockchain and alerts in reorg.
Bob calls one of the forementioned create functions
Thus an Auction is created with an address to which Alice sends the tokens.
Finally Alice's tx is executed and an Auction is funded which Bob controls.


## Tools Used
Manual Review

## Recommended Mitigation Steps
Use `CREATE2` with `salt` that inlcudes `msg.sender`.

> Note: The same attack vectors also applies to deploying a contracts through proxies in Deployer.sol



## Assessed type

Library
Incorrect Exchange Rate Calculation in `_scaleUp()` Function Allows Inflated RToken Supply and Insufficient Collateral Backing
Audit: 2024-07-reserve-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/RToken.sol#L483-L497
https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/RToken.sol#L89-L110


# Vulnerability details

The `issue()` and `issueTo()` functions allow users to mint new RTokens by depositing the required collateral baskets. The contract tracks the number of collateral baskets needed (`basketsNeeded`) to fully collateralize the RToken supply and calculates the exchange rate as `basketsNeeded / totalSupply`.

There is an issue in the `_scaleUp()` function, which is called by `issueTo()` to mint new tokens and update the `basketsNeeded` value. The problem arises when minting tokens from a zero supply state. In this case, the function mints `amtBaskets` worth of RTokens instead of using the current exchange rate, allowing the effective exchange rate to decrease on subsequent mints.

## Impact
* Minting more RTokens than intended for the deposited collateral, leading to an inflated supply and insufficient collateral backing.
* Incorrect accounting of collateral and RTokens in the system, potentially causing solvency issues for the protocol.

## Proof of Concept
* The RToken contract is deployed with an initial zero supply and zero `basketsNeeded`.

* Alice calls `issueTo(alice, 1)`, minting 1 RToken to her address. The `_scaleUp()` function is called with `amtBaskets = 1`, `totalSupply = 0`, and `basketsNeeded = 0`. Since `totalSupply` is zero, the function mints `amtRToken = amtBaskets = 1` to Alice and updates `basketsNeeded` to 1.

* Bob calls `issueTo(bob, 1)`, minting 1 RToken to his address. The `_scaleUp()` function is called with `amtBaskets = 1`, `totalSupply = 1`, and `basketsNeeded = 1`. The function calculates `amtRToken` as follows:
```solidity
uint256 amtRToken = amtBaskets.muluDivu(totalSupply, basketsNeeded);
```
This results in `amtRToken = 1 * 1 / 1 = 1`, minting 1 RToken to Bob and updating `basketsNeeded` to 2.

The exchange rate has decreased from 1 basket per RToken (after Alice's mint) to 0.5 baskets per RToken (after Bob's mint).

## Tools Used
Manual review

## Recommended Mitigation Steps
The `_scaleUp()` function should establish a non-zero initial `basketsNeeded` value when minting from a zero supply state. 
```diff
function _scaleUp(
    address recipient,
    uint192 amtBaskets,
    uint256 totalSupply
) private {
-   uint256 amtRToken = totalSupply != 0
-       ? amtBaskets.muluDivu(totalSupply, basketsNeeded) // {rTok} = {BU} * {qRTok} * {qRTok}
-       : amtBaskets; // {rTok}
+   uint256 amtRToken;
+   if (totalSupply == 0) {
+       amtRToken = amtBaskets;
+       basketsNeeded = amtBaskets;
+   } else {
+       amtRToken = amtBaskets.muluDivu(totalSupply, basketsNeeded);
+       basketsNeeded += amtBaskets;
+   }
    emit BasketsNeededChanged(basketsNeeded, basketsNeeded + amtBaskets);
-   basketsNeeded += amtBaskets;

    // Mint
`BackingManager#grantRTokenAllowance()` & `AllowanceLib#safeApproveFallbackToMax()` would be non-functional for some supported tokens
Audit: 2024-07-reserve-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/825e5a98a8c94a7b4458b3b359a507edb9e662ba/contracts/p1/BackingManager.sol#L69-L75
https://github.com/code-423n4/2024-07-reserve/blob/825e5a98a8c94a7b4458b3b359a507edb9e662ba/contracts/p1/AssetRegistry.sol#L207


# Vulnerability details



## Proof of Concept

First note this from the readme https://github.com/code-423n4/2024-07-reserve/blob/825e5a98a8c94a7b4458b3b359a507edb9e662ba/README.md#L142-L145

```markdown
### ERC20 token behaviors in scope

| Question | Answer |
| -------- | ------ |

| [Revert on large approvals and/or transfers](https://github.com/d-xo/weird-erc20?tab=readme-ov-file#revert-on-large-approvals--transfers) | In scope |
```

Now take a look at https://github.com/code-423n4/2024-07-reserve/blob/825e5a98a8c94a7b4458b3b359a507edb9e662ba/contracts/libraries/Allowance.sol#L17-L46

```solidity
function safeApproveFallbackToMax(
  address tokenAddress,
  address spender,
  uint256 value
) internal {
  IERC20ApproveOnly token = IERC20ApproveOnly(tokenAddress);

  // 1. Set initial allowance to 0
  token.approve(spender, 0);
  // untestable:
  //    allowance should always be 0 if token behaves correctly
  require(token.allowance(address(this), spender) == 0, 'allowance not 0');

  if (value == 0) return;

  // 2. Try to set the provided allowance
  bool success; // bool success = false;
  try token.approve(spender, value) {
    success = token.allowance(address(this), spender) == value;
    // solhint-disable-next-line no-empty-blocks
  } catch {}

  // 3. Fall-back to setting a maximum allowance
  if (!success) {
    token.approve(spender, type(uint256).max);
    // untestable:
    //    allowance should always be max value if token behaves correctly
    require(token.allowance(address(this), spender) >= value, 'allowance missing');
  }
}

```

This is a helper function that's used accross the proocol and it's used to cover all the edge cases for the types of tokens the protocol supports, issue however is that the fallback logic would not work for some of the supported tokens, which is because it attempts approve `type(uint256).max)` on these tokens, but per the readMe, we can see that some of the tokens that are to be supported would revert on these approvals, which would make this attempt incompatible with them.

In the same light, take a look at https://github.com/code-423n4/2024-07-reserve/blob/825e5a98a8c94a7b4458b3b359a507edb9e662ba/contracts/p1/BackingManager.sol#L69-L75

```solidity
function grantRTokenAllowance(IERC20 erc20) external notFrozen {
  require(assetRegistry.isRegistered(erc20), 'erc20 unregistered');
  // == Interaction ==
  IERC20(address(erc20)).safeApprove(address(rToken), 0);
  IERC20(address(erc20)).safeApprove(address(rToken), type(uint256).max);
}

```

The above would also revert on these set of tokens.

## Impact

A functionality of the protocol and it's availbility would be impacted, cause as hinted this fallback attempt to approving
StRSRVotes owners can block token burning and transfers.
Audit: 2024-07-reserve-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/StRSRVotes.sol#L162
https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/StRSRVotes.sol#L166
https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/StRSRVotes.sol#L220


# Vulnerability details

## Impact
Impacts caused by allowing delgation of votes to zero address

1. Bad delegatees can block delegators from redelegating and from sending their NFTs. https://github.com/code-423n4/2023-12-revolutionprotocol-findings/issues/49
2. StRSRVotes owners can block token burning and transfer by delegating to zero address.

## Vulnerability Details 

delegate() and delegateBySig() allow the delegation to zero address, which result in owner's votes elimination in the checkpoint. I.e. the votes are subtracted from the owner, but aren't added anywhere. _moveVotingPower() invoked by _delegate() treats the corresponding call as a burning, erasing the votes.

The impact is that the further transfer and burning attempts for the ids of the owner will be reverted because _afterTokenTransfer() callback will try to reduce owner's votes, which are already zero, reverting the calls due to subtraction fail. The overall impact is governance token burning and transfer being disabled whenever the owner delegated to zero address. This can be done deliberately, i.e. any owner can disable burning and transfer of the owned ids at any moment, which can interfere with governance voting process.

## Proof of Concept
User facing functions delegate() and delegateBySig() call `_delegate` which allows for zero address delegation:
[StRSRVotes.sol#L220](https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/StRSRVotes.sol#L220)
```solidity
    function _delegate(address delegator, address delegatee) internal {
        address currentDelegate = delegates(delegator);
        uint256 delegatorBalance = balanceOf(delegator);
        _delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);

        _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
    }
```

In this case _moveVotingPower() will reduce the votes from the owner, not adding it to anywhere as src is the owner, while dst is zero address.
[StRSRVotes.sol#L235](https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/StRSRVotes.sol#L235)
```solidity
        if (src != dst && amount != 0) {
            if (src != address(0)) {
                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(
                    _checkpoints[era][src],
                    _subtract,
                    amount
                );
                emit DelegateVotesChanged(src, oldWeight, newWeight);
            }

            if (dst != address(0)) { // @audit here w