xanoutas/solidity-security-findings huggingface dataset:
The creator does not receive additional ether - it accumulates on the contract
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/ERC20TokenEmitter.sol#L195-L197


# Vulnerability details

## Impact
In some cases, the ERC20TokenEmitter contract may accumulate residual ether that was not sent to the creator. The contract does not have a function to pick up the remaining ether.

When a user wants to buy tokens, he sends ether to the ERC20TokenEmitter.sol.buyToken() function and his eth are distributed to several addresses(creator, treasury). With some values of parameters CreatorRateBps  and EntropyRateBps, a situation may arise that not all the ether is sent and remains on the contract, but the contract does not have a function so that the owner can receive it
## Proof of Concept
Create new file in packages/revolution/test/token-emitter/
 ```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { Test } from "forge-std/Test.sol";
import { ERC20TokenEmitter } from "../../src/ERC20TokenEmitter.sol";
import { IERC20TokenEmitter } from "../../src/interfaces/IERC20TokenEmitter.sol";
import { RevolutionBuilderTest } from "../RevolutionBuilder.t.sol";

contract ERC20TokenEmitterTest is RevolutionBuilderTest {
      // 1,000 tokens per day is the target emission
    uint256 tokensPerTimeUnit = 1_000;
    uint256 expectedVolume = tokensPerTimeUnit * 1e18;
    string public tokenNamePrefix = "Vrb";
    function setUp() public override {
        super.setUp();
        super.setMockParams();
        super.setERC721TokenParams("Mock", "MOCK", "https://example.com/token/", tokenNamePrefix);
        int256 oneFullTokenTargetPrice = 1 ether;
        int256 priceDecayPercent = 1e18 / 10;
        super.setERC20TokenEmitterParams(
            oneFullTokenTargetPrice,
            priceDecayPercent,
            int256(1e18 * tokensPerTimeUnit),
            creatorsAddress
        );
        super.deployMock();
        vm.deal(address(0), 100000 ether);
    }
   function testPOCBuyToken() public {
        vm.startPrank(erc20TokenEmitter.owner());
        erc20TokenEmitter.setCreatorRateBps(3000);
        erc20TokenEmitter.setEntropyRateBps(1000);
        vm.stopPrank();
        vm.startPrank(address(0));
        address[] memory recipients = new address[](1);
        recipients[0] = address(1);
        uint256[] memory bps = new uint256[](1);
        bps[0] = 10_000;
        vm.deal(address(0), 100000 ether);
        erc20TokenEmitter.buyToken{ value: 1000000000000 }(
            recipients,
            bps,
            IERC20TokenEmitter.ProtocolRewardAddresses({
                builder: address(0),
                purchaseReferral: address(0),
                deployer: address(0)
            })
        );
        assertGt(address(erc20TokenEmitter).balance,0);
    }
}
```
## Tools Used
Manual review
## Recommended Mitigation Steps
Send remaining balance to creator's address in the end of buyToken() function.


## Assessed type

ETH-Transfer
Two items having same number of votes above the quorum can lead to invariant violation and unfairness towards either of the item
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/MaxHeap.sol#L156


# Vulnerability details

## Impact
When an item is dropped or extracted from the maxHeap tree, it is directly done so from the item at the root of the tree i.e. index 0. Although this is expected, if one of the child itemIds have number of votes equal to that of the root node, this would be a problem.

Here are the following impacts of this problem:
1. According to an invariant in the README, VerbsToken should always mint the top voted art piece in the CultureIndex. In case of this, both items have same number of votes yet the item at the root is extracted. This breaks the invariant since both the art pieces have tied yet one of them is chosen for auctioning.
2. Extraction of the root item would mean that the other item that was not auctioned will have to wait for another 24 hours to be auctioned. Additionally if the item is overtaken by another element, it would be further unfair for the item that was supposed to be auctioned previously.
3. The individuals impacted are the creators of the art piece and the community voters for the piece since it will create a loss of significance of the item, which would mean monetary loss for the creators (compared to if it would've been auctioned).

## Proof of Concept
Here's the whole process:

1. Let's assume this is the current state of the maxHeap tree.
 - The values have been made small for ease of demonstration of the issue
 - The itemIds being used are 1,2,3,4,5 with values being 50,50,15,10,20 respectively.
 - The current indexes of the items are 0,1,2,3,4 (since size starts from 0)
 - The current size of the tree is 5.
 - Over here, note that **itemId 1 is the parent of itemId 2 and both have the same values 50**.

[Link to image](https://drive.google.com/file/d/1NVIMnZ9kLw_1zp3S2CEfAG14yis4DyvP/view?usp=sharing)

![Initial state](https://gist.github.com/assets/109625274/47828a72-c2ff-4bf7-b395-2278f706bc5f)

2. Now let's say extractMax() is called. The state of the tree will now be as follows:
 - The red node represents the deleted value i.e. itemId 5 at index 4 does not exist anymore.
 - The green nodes represent the maxHeapify() operation which occured during extractMax(). ItemId 5 was first set to the root followed by which it was swapped with itemId 2 to maintain the max heap property.
 - The important point to notice here is that the root was popped and now the itemId 2 is in the root. This means it will have to wait for another 24 hours and survive the vote competition from it's child elements.

[Link to image](https://drive.google.com/file/d/1zLS5Ums1ZcnbMk_lpdZypUE3bKCXg1Ca/view?usp=sharing)

![State after extractMax](https://gist.github.com/assets/109625274/d55d8e7e-022b-4242-8991-76d2dee7756d)

3. Here is what the tree will look like if itemId 3, which had a value of 30 has overtaken itemId 2 during those 24 hours.

[Link to image
Loss of Funds for Users Due to Token Purchase after Maximum Supply
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/ERC20TokenEmitter.sol#L209-L215


# Vulnerability details

## Summary

When the token's total supply reaches its maximum, users lose funds when attempting to buy tokens, as the transaction completes without minting new tokens.

&nbsp;

## Vulnerability Details

To engage in the voting system, users must acquire tokens directly from the protocol, a process managed by `ERC20TokenEmitter::buyToken`. The success of the `buyToken()` function relies on whether the token has reached its maximum supply, as observed [here](https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/base/erc20/ERC20VotesUpgradeable.sol#L53-L63). If the total supply equals the maximum supply, the mint transaction fails. However, the `buyToken()` function does not catch this error and proceeds to complete the transaction without actually minting any new tokens for the buyer.

&nbsp;

## Impact

1.  Loss of funds for users.
2.  Compromise of the protocol's integrity.
3.  Poor user experience that leaves buyers frustrated.

&nbsp;

## Tools Used

Manual review

&nbsp;

## Recommended Mitigation Steps

Add a `require` statement at the beginning of the `buyToken()` function to ensure that the total supply is less than the maximum supply before the transaction can proceed. And catch any errors arising from `_ment()` calls.


## Assessed type

Context
Exclusion of New Users Due to Token Max Supply
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/base/erc20/ERC20VotesUpgradeable.sol#L53-L63


# Vulnerability details

## Summary

New users are unable to participate in the protocol once the governance token's maximum supply is reached.

&nbsp;

## Vulnerability Details

To engage in the voting system, users need to acquire tokens directly from the protocol, a process managed by `ERC20TokenEmitter::buyToken`. This function relies on whether the token has reached its maximum supply, as evidenced [here](https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/base/erc20/ERC20VotesUpgradeable.sol#L53-L63). If the total supply reaches the maximum supply, the mint transaction fails. Since the token is non-transferrable, it cannot be traded in the open market, limiting participation in CultureIndex voting to existing holders and effectively closing the protocol to new users.

&nbsp;

## Impact

This limitation impedes the system's ability to acquire new users (voters), potentially jeopardizing the vitality of the protocol.

&nbsp;

## Tools Used

Manual review

&nbsp;

## Recommended Mitigation Steps

A crucial design decision needs to be made within the team regarding whether to eliminate the cap on token supply or make the token transferrable. This decision will determine the future accessibility of the protocol for new users.



## Assessed type

Context
Incomplete Creator Rewards in Auction Settlement
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/AuctionHouse.sol#L398-L411


# Vulnerability details

## Summary

During the settlement of auctions in the `AuctionHouse`, the proceeds meant for creators are not accurately distributed, leading to potential loss of funds for the creators.

&nbsp;

## Vulnerability Details

In the process of settling auctions (`AuctionHouse::_settleAuction`), the proceeds from the auction (`_auction.amount`) are intended to be shared between the auction owner and the creators (`creatorsShare`). However, due to Solidity rounding error properties, residual tokens may be left after sending funds to the creators. This remaining balance is then used to buy tokens for the creators through `erc20TokenEmitter.buyToken()` which is then taxed to reward the protocol.

This process can result in an incomplete distribution of funds to creators, leading to potential financial loss for them. Additionally, the tax mechanism may further impact the overall amount reaching the creators.

```solidity
// Relevant excerpt from _settleAuction() in AuctionHouse.sol
uint256 creatorsShare = _auction.amount - auctioneerPayment;
// ... (other logic)
if (creatorsShare > ethPaidToCreators) {
    creatorTokensEmitted = erc20TokenEmitter.buyToken{ value: creatorsShare - ethPaidToCreators }(
        vrgdaReceivers,
        vrgdaSplits,
        IERC20TokenEmitter.ProtocolRewardAddresses({
            builder: address(0),
            purchaseReferral: address(0),
            deployer: deployer
        })
    );
}
```

## Impact

This issue deprives the piece creators of their rightful rewards, potentially resulting in a loss for them. It also introduces an opportunity for market competition by offering a more transparent and fairer deal than the protocol.

&nbsp;

## Tools Used

Manual review

&nbsp;

## Recommended Mitigation Steps

Implement a function in `ERC20TokenEmitter::buyToken()` that accounts for the creators' share of the tax when called from `AuctionHouse::_settleAuction()`. This can be achieved by adding a validation statement to check if the caller of `ERC20TokenEmitter::buyToken()` is the `AuctionHouse` and adjusting the logic accordingly.



## Assessed type

Decimal
Premature Nonce Incrementation in `CultureIndex::_verifyVoteSignature()`
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/CultureIndex.sol#L419-L444


# Vulnerability details

## Summary
`CultureIndex::_verifyVoteSignature` function improperly increments the `nonces` mapping before validating the signature, potentially leading to nonce misuse and weakened security against replay attacks.


## Vulnerability Details

 in the function `_verifyVoteSignature`, the nonce associated with a user's address (`from`) is incremented before the actual verification of the signature. This behavior is contrary to the standard practice in signature verification, where a nonce should only be incremented following a successful signature validation.
This function is responsible for verifying EIP-712 compliant signatures for transactions. The core issue lies in the handling of the `nonces` mapping, which is used to keep track of each account's nonce to prevent replay attacks.

In the existing implementation, the contract increments the nonce associated with the sender's address (`from`) immediately before verifying the signature's validity.


## Impact

1. Legitimate users may face transaction failures due to nonce mismatches between their records and the contract state, causing inconvenience and potential loss of trust.
    
2.  The security mechanism intended to prevent replay attacks is weakened, potentially allowing attackers to replay transactions.
    
3.   Malicious actors could exploit this flaw to increment nonces through invalid signatures, leading to a denial of service for legitimate users by exhausting their nonce space.

## Proof of Concept
Consider two characters, Alice and Bob. Alice is a legitimate user, and Bob is an attacker.

1.  **Scenario**: Alice sends a signature to vote. Due to network issues, the transaction is delayed.
2.  **Attack**: In the meantime, Bob, aware of Alice's pending transaction, sends multiple invalid signatures from Alice's address.
3.  **Outcome**: The contract increments Alice's nonce for each invalid attempt by Bob. When Alice's legitimate transaction arrives, the nonce does not match, leading to a failed transaction, causing a denial of service for Alice.
## Tools Used
Manual Review



## Recommended Mitigation Steps

modify the `_verifyVoteSignature` function to increment the nonce only after successful verification of the signature.
```solidity
function _verifyVoteSignature(
    address from,
    uint256[] calldata pieceIds,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) internal returns (bool success) {
    ...
    address recoveredAddress = ecrecover(digest, v, r, s);
    require(recoveredAddress == from, "INVALID_SIGNATURE");

    nonces[from]++; // Increment nonce after successful verification
    ...
}
```


## Assessed type

Other
Out-of-Bounds Access vulnerability in `MaxHeap::maxHeapify()`
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/MaxHeap.sol#L94-L113


# Vulnerability details

## Summary
In the `Maxheap::maxHeapify()` function, there is an incorrect boundary condition that allows for out-of-bounds array access.


## Vulnerability Details

`Maxheap::maxHeapify()` function is crucial for maintaining the heap property in the `MaxHeap` data structure. 
`Maxheap::maxHeapify()` function's boundary check, `if (pos >= (size / 2) && pos <= size) return;` , improperly allows for the `pos` variable to equal the `size` of the heap.
This condition is flawed because arrays in Solidity are zero-indexed, meaning the valid indices range from 0 to `size-1`. Allowing `pos` to be equal to `size` leads to an attempt to access an element at `heap[size]`, which is an index outside the bounds of the array, and can cause unpredictable behavior including transaction failure, heap corruption, or other unintended contract behaviors.


## Impact

It can lead to transaction reversion or unintended contract behavior, potentially disrupting contract operability and data integrity.

## Proof of Concept
Add this test function to the `2023-12-revolutionprotocol/packages/revolution/test/max-heap/MaxHeap.t.sol`
```solidity
/// @dev Test for out-of-bounds access vulnerability in maxHeapify
function testMaxHeapifyOutOfBounds() public {
    // Arrange: Insert enough elements to create a sizeable heap
    for(uint i = 0; i < 10; i++) {
        maxHeapTester.insert(i, i);
    }

    // Act and Assert: Try to call maxHeapify with `pos` equal to `size`
    try maxHeapTester.maxHeapifyTest(maxHeapTester.size()) {
        // If this line is reached, the test should fail as it indicates no revert occurred
        fail("maxHeapify did not revert on out-of-bounds access");
    } catch Error(string memory reason) {
        // Check the revert reason to ensure it's due to out-of-bounds access
        assertEq(reason, "Heap is empty", "Revert should be due to out-of-bounds access");
    }
}

```

After running the test, It will show this logs:
```
[FAIL. Reason: assertion failed] testMaxHeapifyOutOfBounds() (gas: 796172) 
Logs: 
Error: maxHeapify did not revert on out-of-bounds access
```

The test failure message `Error: maxHeapify did not revert on out-of-bounds access` indicates that the `maxHeapify` function did not revert when it was called with a `pos` equal to the size of the heap. This behavior aligns with the identified vulnerability - the function should have reverted due to out-of-bounds access, but it didn't, confirming the presence of the vulnerability.
## Tools Used
Manual Review, Foundry



## Recommended Mitigation Steps
Amend the boundary check in `maxHeapify` to ensure `pos` never equals `size`.
```solidity
function maxHeapify(uint256 pos) internal {
    // ... (other code)
    if (pos >= (size / 2) && pos < size) return;
    // ... (remaining function logic)
}
``
`CulturalIndex.createPiece` function: inaccurate `quorumVotes` assigned to the created art pieces
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/CultureIndex.sol#L226-L235
https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/AuctionHouse.sol#L357-L359


# Vulnerability details

## Impact

- Revolution protocol is meant to enable users from adding their art pieces to be voted on and auctioned if the piece passes `quorumVotes` assigned to it when it was created.

- The voting quorum for each piece varies based on the totalSupply of the ERC20 voting token and the totalSupply of the verbs token at the time when the piece is created:

  ```javascript
  newPiece.totalVotesSupply = _calculateVoteWeight(
    erc20VotingToken.totalSupply(),
    erc721VotingToken.totalSupply()
  );
  newPiece.totalERC20Supply = erc20VotingToken.totalSupply();
  //some code...
  newPiece.quorumVotes = (quorumVotesBPS * newPiece.totalVotesSupply) / 10_000;
  ```

- The verbs token (`erc721VotingToken`) is minted by the `AuctionHouse` only when an auction is **created** , and when the auction time ended with a winning bidder; then this bidder will receive this verbs token, and if there's no bidders on the auction; the minted verbs token for that auction is **burnt**:

  ```javascript
  //If no one has bid, burn the Verb
  if (_auction.bidder == address(0)) verbs.burn(_auction.verbId);
  ```

- So as can be noticed: at the time the art piece is added; the `quorumVotes` assigned to that piece will take into account the verbs token that is currently under auction, while this token might be burned if it has no bidders.

- And by knowing that only one auction can exist at a time; the assigned `quorumVotes` of the art piece can represent a higher threshold than the actual one (if the auction verbs is burned) by a value of `(erc721VotingToken.totalSupply() * erc721VotingTokenWeight * 1e18) * (quorumVotesBPS / 10_000)`.

## Proof of Concept

[CultureIndex.createPiece function/L226-L235](https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/CultureIndex.sol#L226-L235)

```javascript
newPiece.totalVotesSupply = _calculateVoteWeight(
  erc20VotingToken.totalSupply(),
  erc721VotingToken.totalSupply()
);
newPiece.totalERC20Supply = erc20VotingToken.totalSupply();
newPiece.metadata = metadata;
newPiece.sponsor = msg.sender;
newPiece.creationBlock = block.number;
newPiece.quorumVotes = (quorumVotesBPS * newPiece.totalVotesSupply) / 10_000;
```

[AuctionHouse.\_settleAuction function/ L357-L359](https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/AuctionHouse.sol#L357-L359)

```javascript
//If no one has bid, burn the Verb
if (_auction.bidder == address(0)) verbs.burn(_auction.verbId);
```

## Tools Used

Manual Review.

## Recommended Mitigation Steps

Check if there's a r
VRGDAC.sol#yToX: Calculation contains big rounding error.
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/libs/VRGDAC.sol#L67


# Vulnerability details

## Impact
By the big rounding error, it calculates the amount of tokens wrongly.

## Proof of Concepts
`VRGDAC.sol#yToX` function is as follows.
```solidity
File: VRGDAC.sol
54:     function yToX(int256 timeSinceStart, int256 sold, int256 amount) public view virtual returns (int256) {
55:         int256 soldDifference = wadMul(perTimeUnit, timeSinceStart) - sold;
56:         unchecked {
57:             return
58:                 wadMul(
59:                     perTimeUnit,
60:                     wadDiv(
61:                         wadLn(
62:                             wadDiv(
63:                                 wadMul(
64:                                     targetPrice,
65:                                     wadMul(
66:                                         perTimeUnit,
67:                                         wadExp(wadMul(soldDifference, wadDiv(decayConstant, perTimeUnit)))
68:                                     )
69:                                 ),
70:                                 wadMul(
71:                                     targetPrice,
72:                                     wadMul(
73:                                         perTimeUnit,
74:                                         wadPow(1e18 - priceDecayPercent, wadDiv(soldDifference, perTimeUnit))
75:                                     )
76:                                 ) - wadMul(amount, decayConstant)
77:                             )
78:                         ),
79:                         decayConstant
80:                     )
81:                 );
82:         }
83:     }
```
The equation on `L67` means `e ^ ( soldDifference * decayConstant / perTimeUnit )` but it is calculated by `e ^ (soldDifference * (decayConstant / perTimeUnit) )`.
It leads to big rounding error. In fact, the rounding error is expanded by `soldDifference` and `e ^ (...)`.
This error causes wrong calculation of the amount of tokens in `ERC20TokenEmitter.sol#buyToken` function. (`totalTokensForCreators, totalTokensForBuyers`)

## Tool used
Manual Review

## Recommended Mitigation Steps
`VRGDAC.sol#yToX` function has to be rewritten as follows.
```solidity
File: VRGDAC.sol
54:     function yToX(int256 timeSinceStart, int256 sold, int256 amount) public view virtual returns (int256) {
55:         int256 soldDifference = wadMul(perTimeUnit, timeSinceStart) - sold;
56:         unchecked {
57:             return
58:                 wadMul(
59:                     perTimeUnit,
60:                     wadDiv(
61:                         wadLn(
62:                             wadDiv(
63:                                 wadMul(
64:                                     targetPrice,
65:                                     wadMul(
66:                                         perTimeUnit,
67:   ---                                      wadExp(wadMul(sol