# 大表哥: audit scope

## In scope

| File | Contract | Runtime | Role |
|---|---|---|---|
| `src/BigCousinVault.sol` | `BigCousinVaultUpgradeable` | 14,979 B | Flap tax vault. Receives QQQB trading tax, splits 30/70 |
| `src/BigCousinVault.sol` | `BigCousinVaultBeaconFactory` | 8,010 B | Deploys BeaconProxy vaults via Flap's VaultPortal |
| `src/FlappyCuzTreasury.sol` | `FlappyCuzTreasury` | 9,450 B | Holds the game's QQQB, pays signed run receipts |

## Out of scope

- `src/flap/**`, canonical Flap interfaces and bases, included unmodified.
- `src/FreeCoinBeacon.sol`, `src/FlapDeployed.sol`, `src/libraries/**`, upstream
  reference material carried with the template.
- The 大表哥 token itself. It is a standard Flap 7777 tax token created by
  Flap's own VaultPortal; this project does not author token code.
- The off-chain score-verification service. Its behaviour is described below
  because it is part of the trust story, but its code is not in this package.

## Money flow

```
trader buys/sells 大表哥        2% buy / 2% sell, taken in QQQB
        |
        v
Flap TaxProcessor              transfer-first, then wake
        |
        v
BigCousinVaultUpgradeable      receive() recognises the QQQB delta
        |                      processRevenue() splits, permissionless
        |
        +--30%--> FlappyCuzTreasury  --signed receipt--> player wallet
        |
        +--70%--> projectTreasury
```

## Trust model, stated plainly

**The vault** holds QQQB only between a dispatch and the next `processRevenue()`
call. It can only ever move that QQQB to two addresses fixed at construction.
The 30% game share is a constant, not a parameter. The only privileged actor is
Flap's canonical Guardian, which can rescue balances during an incident and
upgrade the beacon.

**The treasury** is the riskier contract and should get the most attention.
Flappy Cuz is a browser game, so the client cannot be trusted with a score.
Runs are re-simulated server-side from a deterministic replay proof (seed plus
the frame indices on which the player flapped) and only then does the service
sign a receipt. The contract does not verify gameplay; it verifies a signature
and enforces these ceilings:

- each receipt is single-use, keyed by `runId`
- each receipt carries a deadline
- each receipt is bound to the claiming address, so it cannot be resold
- `maxClaimPerRun` and `maxDailyPerWallet` are enforced on-chain
- the owner can pause and rotate the signer

**The owner can also withdraw everything.** `sweep(token, to, amount)` is
`onlyOwner` and works on the payout token, for the full balance, in one
transaction, whether or not the contract is paused. This is a deliberate
emergency exit, so a compromised signer, a failed launch, or a wind-down cannot
strand the game treasury. We are not presenting the ceilings above as a
constraint on the owner; they bound the signer only. The owner key is intended
to be cold or a multisig, and is never the same key as `scoreSigner`.

**What bounds a leaked signer.** `maxDailyTotalBps`, and nothing else.

The per-wallet ceilings never could: `claimedOnDay` is keyed by `msg.sender`,
and an attacker holding the signer key controls unlimited wallets, so they sign
a fresh receipt for a fresh wallet and repeat. We measured that rather than
assuming it: before the global ceiling existed, 250 wallets drained 375 of 400
QQQB in one simulated day for roughly $75 of gas. Deployment also sets both
per-wallet ceilings to the contract maximum by policy, because capping what an
honest player earns is not the goal.

`maxDailyTotalBps` caps everything this contract pays out in a UTC day, across
all wallets, as basis points of the day's opening balance. A sybil cannot route
around it. It is set to 2,500 (25%), which caps the DAMAGE of a stolen key
rather than what anyone can earn: realistic daily payout is a low single-digit
percentage of the treasury, so honest play never reaches it, while a compromised
signer is held to a quarter of the balance per day until the key is rotated. The
contract refuses any value above 5,000 bps, and refuses zero, which would halt
payouts and look like a rug.

It is measured against the balance at the START of the day, reconstructed as the
current balance plus what has already gone out today, so the ceiling does not
shrink underneath honest players as the day proceeds.

The remaining mitigations still matter and are unchanged: how fast a compromise
is noticed, `pause()`, and the owner's `sweep`.

## Internal review before submission

An independent adversarial review was run over both contracts, the Flap bases
they use, the tests and the config before this package was sent. It is our own
review, not a third-party audit, and is offered as evidence of diligence rather
than as a substitute for yours.

It found no exploitable issue in the claim and signature path, the vault
split and accounting path, the beacon and initializer path, or the config
guards. Specifically checked and clean: EIP-712 domain separation against
cross-chain and cross-contract replay, signature malleability and the
`ecrecover` zero-address gap, receipt binding to `msg.sender`, `runUsed` set
before transfer so a failed claim does not burn a runId, conservation and
rounding in the 30/70 split, `accountedQuote` never exceeding the real balance,
reentrancy on every outflow, SafeERC20 behaviour against non-standard tokens,
`_disableInitializers` on the implementation, and the one-way upgrade lock.

Three findings, all fixed in this package:

1. The treasury's NatSpec claimed a leaked signer could take "at most the daily
   ceiling per wallet". Corrected, and then the underlying weakness was closed
   rather than merely documented: `maxDailyTotalBps` was added afterwards, so
   the exposure the finding described no longer exists. See above.
2. `vaultUISchema()` told users there was "no discretionary withdrawal path",
   while Flap's Guardian can rescue the entire balance. The schema now names
   the Guardian rescue as the one withdrawal path.
3. A mainnet-fork assertion read `assertEq(a + b, a + b)`, which can never fail.
   It now asserts that the split pays out exactly what was recognised, and that
   version was confirmed to fail when conservation is deliberately broken.

## Known, deliberate design decisions

1. **No swap.** Fees arrive in QQQB and payouts are in QQQB. Introducing a
   router would add price-manipulation surface for no benefit.
2. **30% is hardcoded.** The public site promises it; a setter would make that
   promise revocable without a trace.
3. **Immutable treasuries.** Changing a destination requires a new factory,
   which is a visible, deliberate act.
4. **The vault is permissionlessly drainable to its own two destinations.**
   Anyone may call `processRevenue()`. This is intentional: it means payouts do
   not depend on the project running a keeper.
5. **The treasury owner is trusted with the full balance.** See `sweep` above.
   The alternative, a timelock or a cap on withdrawals, would mean an incident
   could lock funds in a contract whose signer is already compromised. We chose
   a fast exit and a cold owner key over a slow exit and a hot one.
