Sample report
Technical audit report
A line-by-line security review, start to finish: twelve findings, the proof behind each one, and what happened after the team fixed them.
Download the original PDFIn short
- Twelve findings: one critical, two high, four medium, one low, four informational. Eleven fixed and re-verified, one accepted with a documented plan.
- The critical allowed complete draining of pooled funds and was reproduced on a fork before disclosure.
- Every finding carries a location, a proof of concept and a resolution. The accepted one is published at full length rather than dropped.
This is an illustrative deliverable, not a record of a real engagement. The subject, the Lumenswap Protocol, is a fictional decentralised exchange invented for the purpose. Names, figures and findings demonstrate format and depth; they are not real results.
Engagement summary
Every report opens by stating what it covered, because a finding only means something against a scope. The commit is named so you can check that what shipped is what was read.
| Client | Lumenswap Labs (fictional) |
| Codebase | lumenswap-core at commit 4be1f02 |
| Scope | Router, Vault, OracleAdapter, FeeSplitter, RewardStaker, LumenToken — 4,120 lines of Solidity 0.8.x |
| Out of scope | Front-end, third-party bridge, off-chain keeper bots |
| Review window | Ten working days, two senior auditors |
| Methods | Manual line-by-line review, AI-assisted cross-checks, fuzzing, invariant testing, fork-based exploit reproduction |
Executive summary
We found twelve issues: one critical, two high, four medium, one low and four informational. Every actionable finding was fixed by the team and re-verified by us against the original exploit, except one medium accepted with a documented plan.
The critical finding allowed complete draining of pooled funds and was reproduced on a fork before disclosure. After remediation, the codebase holds against every attack we constructed.
Severity definitions
Severity scales are not standardised across the industry, so a report that does not define its own is asking you to guess. Ours is impact first: what happens if someone exploits it.
| Severity | Meaning |
|---|---|
| Critical | Direct loss or freezing of user funds, exploitable by anyone |
| High | Loss under realistic conditions, or trust assumptions broken |
| Medium | Bounded loss, or requires unusual conditions to exploit |
| Low | Best-practice gap with indirect or operational risk |
| Informational | Code quality, gas and hardening notes; no direct risk |
Findings overview
Every finding carries a status. A report that lists issues without saying what became of them tells you only that someone looked.
| ID | Title | Severity | Status |
|---|---|---|---|
| TA-01 | Reentrant withdrawal drains the vault | Critical | Fixed, verified |
| TA-02 | Stale oracle price accepted as fresh | High | Fixed, verified |
| TA-03 | Swaps lack slippage and deadline bounds | High | Fixed, verified |
| TA-04 | Unchecked return value on legacy tokens | Medium | Fixed, verified |
| TA-05 | Fee math loses precision for small trades | Medium | Fixed, verified |
| TA-06 | Initialization can be front-run | Medium | Fixed, verified |
| TA-07 | Unbounded reward loop can jam claims | Medium | Accepted, mitigation planned |
| TA-08 | Privileged changes emit no events | Low | Fixed, verified |
| TA-09..12 | Gas, custom errors, immutability, calldata | Informational | Noted |
A finding in full: swaps lack slippage and deadline bounds
HIGH. Router.sol, swapExact(), lines 88-97.
swapExact() accepts a minimum-out of zero and no deadline. Pending transactions can be sandwiched: an attacker moves the price before the victim's swap executes and restores it after, pocketing the difference. Stuck transactions can also execute much later at a worse price.
In plain terms: an order without a price floor or expiry is a blank cheque. Bots that watch pending transactions can worsen your price on purpose and keep the difference.
Impact: every user swap is exposed to MEV sandwiching — a measurable, ongoing value leak rather than a single dramatic theft.
Proof of concept: a sandwich bot script on a fork extracted 2.4% from a sample swap with zero minimum-out.
Recommendation: require a non-zero minimum output and a deadline on every swap, and have the front-end set sane defaults.
Resolution: fixed and verified. The sample sandwich now reverts the victim transaction instead of filling at the worse price.
The finding that was not fixed
TA-07, medium. RewardStaker.sol, claimAll(), lines 112-126.
claimAll() iterates every historical reward epoch for the caller. A long-lived account eventually exceeds the block gas limit and can never claim through this path again.
In plain terms: the claim function re-reads a user's whole history every time. Stay staked long enough and your own history grows too heavy to carry, locking the claim button for you.
Impact: long-term stakers, the protocol's best users, can be locked out of rewards. Funds are not lost but recovery requires a migration.
Resolution: accepted for launch with a documented cap on epoch count and an accumulator refactor scheduled for the next release. We re-tested the cap.
We publish accepted findings rather than quietly dropping them. A report where everything was fixed is either a very short engagement or an incomplete account of one.
Verification statement
After the team's remediation commit, we re-executed every proof of concept, the full fuzzing corpus and the invariant suite. No fixed finding reproduces, and the accepted finding's cap holds under test.
The public attestation for this engagement references both the audited and the remediated commit hashes, so anyone checking it can tell which code was read and which code shipped.
Questions
Why is the subject fictional?
Because naming a real client requires their written consent, and a sample exists to show format rather than to advertise who hired us. The findings are patterns we see repeatedly; the protocol they sit in is invented.
Is a report with a critical finding a bad sign?
No. A mature codebase almost always has findings. What matters is whether each one was fixed and re-verified, and whether anything was accepted rather than resolved. A report with no findings at all is the one worth questioning.
What does the commit hash do?
It pins the report to exact code. If the hash in the report does not match what was deployed, the report describes something other than what is running, however good it looks.
The work behind this
Web3 and protocol audit
Line-by-line review of contracts, chain layer and upgrade paths, with proof-of-concept for every finding.
Formal verification
Mathematical proof that a critical property holds for every reachable state, not just the tested ones.
Deployment verification
We check that what reached production matches what was actually reviewed.
Related reading
Guide
How to prepare for a smart contract audit
Preparation is the part of the engagement you control, and the part that moves the price. What to have ready before you ask for a quote.
Guide
What audits a bridge protocol needs
A bridge is several systems that have to agree, and most of them are not the contracts. What to commission, what each engagement covers, and the order that makes each one cheaper than the last.
Guide
How to read a smart contract audit report
Severity scales, scope statements, commit hashes, and the sections most people skip. Explained against reports you can open in the next tab.