Skip to content
HomeAboutServicesGuidesContact

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 PDF

In 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.

ClientLumenswap Labs (fictional)
Codebaselumenswap-core at commit 4be1f02
ScopeRouter, Vault, OracleAdapter, FeeSplitter, RewardStaker, LumenToken — 4,120 lines of Solidity 0.8.x
Out of scopeFront-end, third-party bridge, off-chain keeper bots
Review windowTen working days, two senior auditors
MethodsManual 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.

SeverityMeaning
CriticalDirect loss or freezing of user funds, exploitable by anyone
HighLoss under realistic conditions, or trust assumptions broken
MediumBounded loss, or requires unusual conditions to exploit
LowBest-practice gap with indirect or operational risk
InformationalCode 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.

IDTitleSeverityStatus
TA-01Reentrant withdrawal drains the vaultCriticalFixed, verified
TA-02Stale oracle price accepted as freshHighFixed, verified
TA-03Swaps lack slippage and deadline boundsHighFixed, verified
TA-04Unchecked return value on legacy tokensMediumFixed, verified
TA-05Fee math loses precision for small tradesMediumFixed, verified
TA-06Initialization can be front-runMediumFixed, verified
TA-07Unbounded reward loop can jam claimsMediumAccepted, mitigation planned
TA-08Privileged changes emit no eventsLowFixed, verified
TA-09..12Gas, custom errors, immutability, calldataInformationalNoted

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

Related reading