<!-- canonical: https://0xsimao.com/findings/winnables-raffles-unrestrictedly-affect-odds-themselves -->

# Admin can unrestrictedly affect the odds of a raffle by setting themselves up with role(1) in `WinnablesTicket`

Medium · Sherlock · Raffle · 16th August 2024

Finding M-3 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/129

---

### Summary

A core invariant defined in the contest README is that:

> Admins cannot affect the odds of a raffle

While the centralization risk of admin self-minting tickets is noted, the following assumption is noted:

> The existence of max ticket supply and max holdings however guarantees a minimum level of fairness

By setting themselves up with role(1) directly in the `WinnablesTicket` token contract, the admin can bypass all these assumptions (max ticket supply, max holdings), and affect the winning odds with no limit.

### Root Cause

First of all, let's look at `mint()` in `WinnablesTickets` contract:

```solidity
function mint(address to, uint256 id, uint256 amount) external onlyRole(1) {
```
https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicket.sol#L182-L199

It is clear that role(1) can mint unlimited tickets. Furthermore, the admin can also grant themselves the role, bypassing any restrictions in `buyTicket()`. We now investigate the impact (how the results are affected by the admin minting tickets to themselves)

When the raffle results are created, the winner is calculated using the `supply` from the ticket storage. 

```solidity
function _getWinnerByRequestId(uint256 requestId) internal view returns(address) {
    RequestStatus storage request = _chainlinkRequests[requestId];
    uint256 supply = IWinnablesTicket(TICKETS_CONTRACT).supplyOf(request.raffleId); // @audit supplyOf is taken from the ticket
    uint256 winningTicketNumber = request.randomWord % supply;
    return IWinnablesTicket(TICKETS_CONTRACT).ownerOf(request.raffleId, winningTicketNumber);
}
```

https://github.com/sherlock-audit/2024-08-winnables-raffles/blob/main/public-contracts/contracts/WinnablesTicketManager.sol#L472

By minting (virtually) unlimited tickets to themselves, the admin bypasses all restrictions imposed in ticket purchase, granting themselves victory odds far exceeding the restrictions imposed.

### Internal pre-conditions

_No response_

### External pre-conditions

_No response_

### Attack Path

1. The admin locks a prize, and starts a raffle as normal. People starts buying tickets to enter the raffle.
2. Admin grants themselves role(1) on the `WinnablesTickets` (not the ticket manager), and mints themselves (or any related party) almost unlimited tickets.
3. Admin bypasses all max ticket restrictions, and said party is virtually guaranteed to be the winner.

### Impact

Admin can unrestrictedly affect the odds of a raffle, breaking protocol invariant

### PoC

_No response_

### Mitigation

_No response_

---

Related findings:

- [FeeManager's admin cannot grant or revoke any role](https://0xsimao.com/findings/titles-publishing-protocol-fee-cannot-grant-revoke): TITLES Publishing Protocol
