Security

Audits

LiquidLottery has undergone two independent security audits by Hashlockarrow-up-right.

Audit 1 — Initial Review

Findings summary:

Severity
Count
Status

High

1

Mitigated

Medium

3

Fixed

Low

3

Fixed / Acknowledged

Gas

1

Fixed

Key findings and resolutions:

  • [High] Centralized admin control — mitigated by adding 48-hour and 72-hour timelocks on all critical admin actions (router changes, upgrades, etc.), reducing the impact of a compromised admin key.

  • [Medium] VRF request ID collision — addressed by validating the round ID in fulfillRandomWords and tracking pendingRoundId.

  • [Medium] Gas DoS in settlement — resolved by implementing batch settlement (settleRoundBatch) so large rounds can be settled in chunks of configurable size.

  • [Medium] Missing VRF coordinator validation — addressed with contract-existence checks on address setters.

  • [Low] Precision loss in prize distribution — dust from integer division carries over to the next round.

  • [Low] Deterministic RNG derivation — accepted; the VRF seed itself is unpredictable, and the derivation is deterministic by design.

  • [Low] Reentrancy in prize claiming — the contract uses OpenZeppelin ReentrancyGuard and checks-effects-interactions where possible.

Full report: HASHLOCK_AUDIT.mdarrow-up-right

Audit 2 — Follow-Up Review

Findings summary:

Severity
Count
Status

Medium

2

Fixed / Acknowledged

Low

3

Fixed / Acknowledged

Gas

1

Fixed

Informational

1

Acknowledged

Key findings and resolutions:

  • [Medium] tx.origin in noContract modifier — acknowledged; the modifier exists to prevent contract-based griefing and is paired with msg.sender checks for access control.

  • [Medium] Gas DoS in single settlement — already addressed with settleRoundBatch in the first audit cycle.

  • [Low] Open settlement functions — settlement is permissionless by design (anyone can call it), with a 1 % fee-pool reward incentivizing timely settlement.

  • [Low] Precision loss — same as Audit 1, with dust rollover to next round.

  • [Low] Timestamp dependence — acknowledged; miner manipulation is bounded to ~15 seconds and does not materially affect fairness.

Full report: HASHLOCK_AUDIT_2.mdarrow-up-right


Winning numbers are generated using Chainlink VRF (Verifiable Random Function) v2.5 on the Base network.

How It Works

  1. The lottery contract on Hyperliquid sends a draw request via CCIP to LotteryVRFRequester on Base.

  2. LotteryVRFRequester calls the Chainlink VRF Coordinator requesting 1 random word.

  3. Chainlink's decentralized oracle network generates the random word with a cryptographic proof.

  4. The VRF Coordinator calls fulfillRandomWords() on LotteryVRFRequester.

  5. The random word is sent back to Hyperliquid via CCIP.

  6. The lottery contract deterministically derives all drawn numbers from the single random word.

Why This Matters

  • Tamper-proof — neither the contract owner, the miners, nor the VRF node operators can predict or influence the outcome.

  • Verifiable — anyone can verify the VRF proof on-chain and confirm the drawn numbers are correct.

  • Decentralized — Chainlink VRF is operated by a decentralized network of independent node operators.


Non-Custodial Design

The smart contract is fully non-custodial:

  • Prize pools are held in the contract, not in any external wallet.

  • No human can move prize funds — pools can only be distributed through the settlement logic.

  • Owner fees are accumulated separately and can only be withdrawn from the fee pool (withdrawFees).

  • Prizes never expire — players can claim at any time.

  • Pending payouts — if an ETH transfer fails, funds are escrowed in pendingPayouts and can be withdrawn by the player at any time.


Admin Safeguards

All sensitive admin operations are protected by timelocks:

Action
Delay
Mechanism

Change CCIP router

48 hours

proposeSetCCIPRouterexecuteSetCCIPRouter

Change VRF requester

48 hours

proposeSetVRFRequesterexecuteSetVRFRequester

Change source chain

48 hours

proposeSetSourceChainSelectorexecuteSetSourceChainSelector

Change upkeep interval

24 hours

proposeSetUpkeepIntervalexecuteSetUpkeepInterval

Upgrade contract

72 hours + 1 h window

proposeUpgradeupgradeToAndCall

Any pending action can be cancelled by the owner with cancelAdminAction(hash).

During the upgrade execution window, ticket purchases are blocked to prevent state corruption.


Emergency Mechanisms

Admin Emergency Cancel

If a draw is stuck (CCIP message lost, VRF failure), the owner can cancel after a 5-minute grace period:

Public Emergency Cancel

If the owner is unresponsive, anyone can cancel a stuck draw after 24 hours:

This ensures the lottery can never be permanently stuck, even without owner intervention.


Access Control

Role
Who
Capabilities

Owner (admin)

Deployer

Admin functions, fee withdrawal, emergency cancel

CCIP Router

Chainlink

Deliver cross-chain messages to ccipReceive

VRF Requester

LotteryVRFRequester on Base

Send draw results back via CCIP

Players

Any EOA

Buy tickets, claim prizes, trigger public draw, trigger settlement

The noContract modifier on buyTickets requires tx.origin == msg.sender, preventing contracts from purchasing tickets directly.


Additional Protections

  • ReentrancyGuard — all state-changing functions that transfer ETH are protected.

  • ERC-165 supportsInterface — both HyperLotteryV1 and LotteryVRFRequester correctly respond to CCIP interface queries.

  • CCIP sender validationccipReceive verifies the source chain selector and sender address.

  • Upgrade window purchase blockbuyTickets reverts during the 1-hour upgrade execution window.

  • Contract-existence checks — all address setters verify the target has deployed bytecode.

Last updated

Was this helpful?