<!-- canonical: https://0xsimao.com/findings/winnables-raffles-buying-tickets-winner-oog -->

# Users buying too many tickets will DoS them and the protocol if they are the winner due to OOG

Medium · Sherlock · Raffle · 16th August 2024

Finding M-5 of the Winnables Raffles competition.

- Protocol: https://audits.sherlock.xyz/contests/516
- Codebase: https://github.com/0xsimao/2024-08-winnables-raffles/tree/81b28633d0f450e33a8b32976e17122418f5d47e
- Source: https://github.com/sherlock-audit/2024-08-winnables-raffles-judging/issues/398

---

### Summary

`WinnablesTicket` stores `nft` ownership by setting the first minted nft id ownership to the user minting and all the next minted nfts remain as `0`. This means it always costs the same to mint, but the `ownerOf()` function becomes much more expensive, to the point where it may cause OOG errors. In this case, the user is able to buy tickets via [WinnablesTicketManager::buyTickets()](https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicketManager.sol#L182), the draw is made in [WinnablesTicketManager::drawWinner()](https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicketManager.sol#L310) and the chainlink request is fulfilled with the winner in [WinnablesTicketManager::fulfillRandomWords()](https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicketManager.sol#L350). However, in [WinnablesTicketManager::propagateWinner()](https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicketManager.sol#L334), it reverts due to OOG when calling [WinnablesTicket::ownerOf()](https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicket.sol#L97-L99).

### Root Cause

In `WinnablesTicket:97-99`, it may run out of gas if enough tickets were bought.

### Internal pre-conditions

1. Admin sets max holdings and max tickets to a number of at least 4000 or simillar.

### External pre-conditions

None.

### Attack Path

1. Users by tickets via WinnablesTicketManager::buyTickets()`
2. The draw is made in `WinnablesTicketManager::drawWinner()` 
3. The chainlink request is fulfilled with the winner in `WinnablesTicketManager::fulfillRandomWords()]
4. In `WinnablesTicketManager::propagateWinner()`, it reverts due to OOG when calling `WinnablesTicket::ownerOf()`.

### Impact

DoSed winner and protocol `ETH` from the raffle.

### PoC

The following test costs 9 million gas, more than the block limit on Avalanche.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {WinnablesPrizeManager, LinkTokenInterface} from "contracts/WinnablesPrizeManager.sol";
import {WinnablesTicket} from "contracts/WinnablesTicket.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract WinnablesPrizeManagerTest is Test {
    WinnablesPrizeManager public winnablesPrizeManager;
    WinnablesTicket public winnablesTicket;
    LinkTokenInterface public link;
    IRouterClient public router;
    address public owner;


    function setUp() public {
        vm.createSelectFork(vm.envString("ETHEREUM_RPC_URL"));
        link = LinkTokenInterface(0x514910771AF9Ca656af840dff83E8264EcF986CA);
        router = IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);
        owner = makeAddr("owner");

        vm.prank(owner);
        winnablesPrizeManager = new WinnablesPrizeManager(address(link), address(router));

        vm.prank(owner);
        winnablesTicket = new WinnablesTicket();
    }

    function test_POC_OwnerOfDos() public {
        vm.prank(owner);
        winnablesTicket.setRole(owner, 1, true);

        vm.prank(owner);
        winnablesTicket.mint(owner, 1, 4000);

        winnablesTicket.ownerOf(1, 3999);
    }
}

```

### Mitigation

Set a reasonable cap for max holdings.

---

Related findings:

- [Anyone will DoS setting a new rewards duration which harms the protocol/users as they will receive too much or too little rewards](https://0xsimao.com/findings/exactly-protocol-update-staking-contract-ii-rewards-duration-harms-little): Exactly Protocol Update - Staking Contract
- [Anyone can grief users, stopping them from fulfilling their withdrawals](https://0xsimao.com/findings/clip-finance-i-grief-stopping-fulfilling-withdrawals): Clip Finance Strategies
- [Connext delegates can perform important actions, make sure smart contract users implement them](https://0xsimao.com/findings/fuji-finance-perform-important-actions-sure): Fuji Finance
- [Malicious users can DOS the protocol by setting downsideProtected to a large value](https://0xsimao.com/findings/autonomint-dos-downside-protected-large): Autonomint
