code-423n4 / 2023-12-revolutionprotocol-findings

3 stars 2 forks source link

Users can frontrun auction settlement buying governance tokens #666

Closed c4-bot-2 closed 10 months ago

c4-bot-2 commented 10 months ago

Lines of code

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

Vulnerability details

Governance tokens use VRGDA to adjust the price of a token to adhere to a specific issuance schedule. This means that for a time t the price will be higher the more tokens have been sold until then.

When the deadline of an auction is reached and settled, governance tokens are minted to the creators of the art piece and the creatorsAddress address. Given that the auction deadline is known beforehand, there are incentives to buy governance tokens just before the auction is settled to receive more tokens. This will result in the creators receiving fewer tokens than expected.

Impact

Creators receive fewer governance tokens than expected.

Proof of Concept

Add the following code snippet to AuctionSettling.t.sol:

    (...)

import "forge-std/console2.sol";
    (...)

    function testAuditSettlingWithoutUserBuy() public {
        settlingAuctionWithWinningBidAndCreatorPayout(false);
    }

    function testAuditSettlingWithUserBuy() public {
        settlingAuctionWithWinningBidAndCreatorPayout(true);
    }

    function settlingAuctionWithWinningBidAndCreatorPayout(bool userBuy) public {
        uint256 bidAmount = 1 ether;
        address bidder = makeAddr("bidder");
        address creator = makeAddr("creator");
        vm.deal(bidder, bidAmount);

        uint256 verbId = createArtPiece(
            "Art Piece",
            "A new art piece",
            ICultureIndex.MediaType.IMAGE,
            "ipfs://image",
            "",
            "",
            creator,
            10_000
        );
        auction.unpause();

        // Bidder creates bid
        vm.startPrank(bidder);
        auction.createBid{ value: bidAmount }(verbId, bidder);
        vm.stopPrank();

        // Fast forward time to end the auction
        vm.warp(block.timestamp + auction.duration() + 1);

        if (userBuy) {
            // User buys governance tokens just before the auction is settled
            uint256 buyAmount = 10_000 ether;
            address user = makeAddr("user");
            vm.deal(user, buyAmount);

            address[] memory addresses = new address[](1);
            addresses[0] = user;
            uint[] memory bps = new uint[](1);
            bps[0] = 10_000;
            IERC20TokenEmitter.ProtocolRewardAddresses memory rewardAddrs = IERC20TokenEmitter.ProtocolRewardAddresses({
                builder: user,
                purchaseReferral: user,
                deployer: user
            });

            vm.startPrank(user);
            erc20TokenEmitter.buyToken{ value: buyAmount }(addresses, bps, rewardAddrs);
            vm.stopPrank();
        }

        // Settle auction
        auction.settleCurrentAndCreateNewAuction();

        console2.log("Creator governance tokens:  %s", erc20Token.balanceOf(creator));
    }

    (...)

Console output:

forge test --mt testAuditSettling -vv

Running 2 tests for test/auction/AuctionSettling.t.sol:AuctionHouseSettleTest
[PASS] testAuditSettlingWithUserBuy() (gas: 1652977)
Logs:
  Creator governance tokens:  50589718752928000

[PASS] testAuditSettlingWithoutUserBuy() (gas: 1483261)
Logs:
  Creator governance tokens:  108332847179522000

As we can see, when the user buys governance tokens just before the auction is settled, the tokens received by the creator are almost half of the tokens received when the user does not buy governance tokens.

Tools Used

Manual inspection.

Recommended Mitigation Steps

A possible solution would be reserving a percentage of the governance tokens for the buys done through the AuctionHouse contract.

Assessed type

Other

c4-pre-sort commented 10 months ago

raymondfam marked the issue as insufficient quality report

c4-pre-sort commented 10 months ago

raymondfam marked the issue as duplicate of #404

c4-judge commented 9 months ago

MarioPoneder marked the issue as unsatisfactory: Invalid

c4-judge commented 9 months ago

MarioPoneder changed the severity to QA (Quality Assurance)

c4-judge commented 9 months ago

MarioPoneder marked the issue as grade-c