When people hear the words smart contract audit, they often imagine an auditor opening Solidity files and searching for vulnerabilities.
In reality, a professional smart contract audit is much more than that.
A serious audit involves understanding the protocol, identifying trust assumptions, modeling potential attackers, reviewing contract logic, analyzing financial invariants, testing edge cases, validating attack paths, communicating findings to developers, and verifying that vulnerabilities have actually been fixed.
A typical audit process looks like this:
Project Scope
↓
Codebase Preparation
↓
Architecture Understanding
↓
Threat Modeling
↓
Manual Code Review
↓
Automated Analysis
↓
Fuzzing / Invariant Testing
↓
Attack Path Validation
↓
Findings
↓
Developer Fixes
↓
Re-audit
↓
Final Report
The exact process varies between audit firms and protocols, but the fundamental objective is the same:
Identify realistic ways an attacker could violate the protocol’s security assumptions before the protocol goes live.
The first step is determining what is actually being audited.
This sounds simple, but scope definition is one of the most important parts of the process.
A protocol may contain:
contracts/
├── core/
├── vaults/
├── tokens/
├── oracle/
├── governance/
├── interfaces/
├── mocks/
└── test/
Not every file necessarily belongs in the audit.
The audit team and development team typically agree on:
A fixed commit is particularly important.
Otherwise, the code being reviewed can change while the audit is in progress.
A professional report should clearly identify the audited version.
For example:
Repository:
github.com/example/protocol
Commit:
a81f2c9
Solidity:
0.8.24
In Scope:
contracts/core/*
contracts/vault/*
contracts/oracle/*
This establishes exactly what the security assessment covers.
One of the biggest mistakes in smart contract auditing is immediately opening Solidity files and looking for common vulnerability patterns.
Before reviewing individual functions, an auditor should understand:
What is this protocol supposed to do?
For a lending protocol, for example, the auditor needs to understand:
User
↓
Deposit Collateral
↓
Borrow Asset
↓
Oracle Price
↓
Health Factor
↓
Liquidation
For a vault:
User
↓
Deposit
↓
Share Calculation
↓
Strategy
↓
Yield
↓
Withdraw
For a DEX:
Trader
↓
Swap
↓
Pool
↓
Price Calculation
↓
Token Transfer
Without understanding these flows, it is easy to miss vulnerabilities that span multiple contracts.
After understanding the protocol at a high level, auditors map the contract architecture.
For example:
┌─────────────┐
│ Governance │
└──────┬──────┘
│
▼
┌──────────┐
│ Router │
└────┬─────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
Vault A Vault B Vault C
│ │ │
└──────┬───┴──────────┘
▼
Oracle
│
▼
Price Source
The auditor wants to know:
This architecture map becomes the foundation for the rest of the audit.
Not every actor in a protocol has the same permissions.
A professional audit therefore identifies different trust boundaries.
Typical roles include:
User
Admin
Owner
Guardian
Keeper
Oracle
Relayer
Governance
Multisig
Timelock
External Protocol
For each role, the auditor asks:
What can this actor do?
For example:
function setOracle(
address newOracle
) external onlyOwner {
oracle = newOracle;
}
The question is not simply:
“Is
onlyOwnerpresent?”
The deeper question is:
“Is the owner supposed to be able to change the oracle without additional restrictions?”
This distinction is important.
A function can be technically protected and still represent a dangerous centralization or trust assumption.
Once the architecture is understood, the auditor starts thinking like an attacker.
A basic threat model asks:
Usually:
Unprivileged external user
But other attackers may matter:
Flash-loan attacker
Compromised keeper
Malicious token
Compromised oracle
Malicious governance participant
Compromised admin
MEV bot
The auditor then considers what resources an attacker can control.
For example:
Attacker controls:
- transaction ordering
- transaction timing
- input parameters
- token approvals
- temporary capital
- contract callbacks
And asks:
What protocol assumptions can these capabilities break?
A useful audit technique is to enumerate all externally reachable entry points.
For example:
deposit()
withdraw()
borrow()
repay()
liquidate()
swap()
claim()
stake()
unstake()
setOracle()
upgradeTo()
Each function becomes an entry point for analysis.
The auditor examines:
Who can call it?
↓
What parameters can they control?
↓
What state does it modify?
↓
What external contracts are called?
↓
What assets move?
↓
What assumptions are made?
This process is particularly useful for discovering attack paths that span several functions.
This is where a large part of professional auditing work happens.
The auditor reads the actual implementation and checks whether the code matches the intended protocol behavior.
Typical areas include:
onlyOwner
onlyRole
authorized
call()
delegatecall()
transfer()
callbacks
overflow
underflow
rounding
precision
division by zero
decimal conversion
balances
shares
totalAssets
totalSupply
debt
collateral
price
TWAP
spot price
staleness
decimals
fallbacks
proxy
beacon
UUPS
implementation
initializer
storage layout
ERC20
ERC4626
DEX
lending protocols
bridges
oracles
The goal is not merely to find code that “looks suspicious.”
The auditor must determine whether the behavior can actually violate a security property.
One of the most powerful parts of a smart contract audit is identifying invariants.
An invariant is a property that should remain true regardless of how users interact with the protocol.
For example:
Total user balances <= protocol assets
Or:
Total debt <= available collateral
Or:
Shares cannot be created without corresponding value
Or:
Only authorized actors can upgrade the implementation
Or:
A user's withdrawal cannot exceed their claim
Instead of asking:
“Is this function correct?”
the auditor asks:
“Can any sequence of transactions make this invariant false?”
This mindset often finds more interesting vulnerabilities than simple pattern matching.
Manual review is usually combined with automated tools.
Depending on the project, auditors may use tools such as:
Automated tools are useful because they can quickly identify suspicious patterns across a large codebase.
For example:
Potential reentrancy
Unused return value
Dangerous external call
Unprotected function
Shadowed variable
Incorrect initialization
However:
Automated tools are not a replacement for human reasoning.
They can identify patterns.
They generally cannot fully understand the economic intent of a protocol.
Fuzzing tests a contract with many unexpected inputs.
Instead of manually testing:
deposit(100)
deposit(1000)
deposit(10000)
a fuzzer may generate thousands or millions of combinations.
For example:
amount = 0
amount = 1
amount = type(uint256).max
amount = random value
The goal is to discover unexpected states.
Fuzzing becomes particularly powerful when combined with assertions.
For example:
assert(totalAssets >= totalLiabilities);
The fuzzer attempts to find a sequence of operations that violates the assertion.
Invariant testing takes this concept further.
Instead of checking individual examples, the auditor defines properties that should always remain true.
For example:
Invariant:
User cannot withdraw more value than they own.
Then the test framework generates sequences such as:
deposit()
deposit()
withdraw()
transfer()
deposit()
withdraw()
withdraw()
The auditor wants to know whether any sequence can break the invariant.
This is extremely useful for DeFi protocols because many vulnerabilities only appear after a particular sequence of state transitions.
Finding suspicious code is not enough.
A good auditor tries to construct an actual attack.
Suppose an auditor discovers:
Function A
↓
changes exchange rate
Function B
↓
calculates shares using exchange rate
The next question is:
Can an attacker call A immediately before B?
The complete attack may look like:
1. Attacker deposits
↓
2. Manipulates protocol state
↓
3. Exchange rate changes
↓
4. Calls share calculation
↓
5. Receives excessive shares
↓
6. Withdraws
↓
7. Attacker profits
This is much stronger evidence than simply saying:
“The exchange-rate calculation looks dangerous.”
For serious findings, auditors may create a Proof of Concept (PoC).
A PoC demonstrates that the vulnerability is exploitable.
For example:
Initial state:
Attacker balance = $10,000
Attack:
Manipulate price
↓
Deposit
↓
Receive excessive shares
↓
Withdraw
Final state:
Attacker balance = $25,000
A PoC helps both the auditor and developers understand:
It also reduces the possibility of reporting theoretical issues that cannot actually be exploited.
Once vulnerabilities are validated, they are categorized by severity.
A typical classification is:
Critical
High
Medium
Low
Informational
Severity usually considers:
Impact
+
Likelihood
+
Exploitability
+
Required privileges
+
Attack prerequisites
For example:
An unprivileged attacker can directly drain most or all protocol funds.
A realistic attack can cause substantial loss of funds or compromise a major protocol component.
The vulnerability has meaningful impact but requires specific conditions or has a more limited scope.
The issue has limited security impact or requires unusual circumstances.
The finding does not represent a direct vulnerability but may improve code quality or security posture.
A professional finding should be reproducible.
A typical finding contains:
Title
Severity
Affected Contracts
Affected Functions
Description
Root Cause
Attack Scenario
Impact
Recommendation
For example:
High — Oracle Price Can Be Manipulated
Affected:
Vault.sol
Root Cause:
The vault uses a manipulable spot price.
Attack:
1. Attacker obtains temporary capital.
2. Manipulates the underlying pool.
3. Changes the reported price.
4. Deposits or borrows using the manipulated valuation.
5. Removes the manipulation.
Impact:
The attacker can extract value from the vault.
Recommendation:
Use a manipulation-resistant oracle mechanism.
The finding should explain why the vulnerability exists, not merely point to a line of code.
An audit is not simply a document delivered at the end.
There is usually an interaction between auditors and developers.
Developers may respond:
"This behavior is intentional."
"This contract is trusted."
"This function is only called by governance."
"This condition cannot occur."
"We already fixed this issue."
The auditor then needs to verify these claims.
This is important because security depends on both:
Code
+
Protocol assumptions
A technically suspicious behavior may be acceptable if it is an explicitly documented trust assumption.
Conversely, a behavior developers believe is impossible may actually be reachable.
After findings are delivered, developers modify the code.
For example:
Audit
↓
Finding
↓
Developer Fix
↓
New Commit
The important point is that the audit does not end when the first report is delivered.
The fixes themselves need to be reviewed.
This stage is often underestimated.
Suppose an auditor reports:
High:
Incorrect share calculation
The developer changes:
shares = amount * totalSupply / totalAssets;
to:
shares = amount * totalSupply / totalAssetsAdjusted;
The question is:
Is the new formula actually safe?
The auditor needs to review the modification.
They should also check:
This is why re-auditing is an important part of the security process.
Security fixes should not break existing functionality.
Therefore, testing should be repeated after remediation.
For example:
Before Fix
↓
Exploit succeeds
After Fix
↓
Exploit fails
But that is not enough.
The auditor should also verify:
Normal deposit works
Normal withdrawal works
Liquidation works
Rewards work
Governance works
Upgrade works
A security patch should eliminate the vulnerability without creating a new protocol failure.
After the review and remediation process is complete, the audit team prepares the final report.
A typical report contains:
A high-level overview of the protocol and security assessment.
The contracts, repository, commit, and version reviewed.
The techniques used during the assessment.
For example:
Manual Review
Static Analysis
Fuzz Testing
Invariant Testing
Attack Simulation
A detailed description of vulnerabilities.
For example:
Critical: 0
High: 1
Medium: 2
Low: 3
Info: 5
Fixed
Acknowledged
Partially Fixed
Not Fixed
This gives users and stakeholders a clear picture of the security state at the time of the audit.
A smart contract audit does not mean:
“The protocol is now impossible to exploit.”
No audit can provide that guarantee.
A protocol can change after the audit.
For example:
Audited Code
↓
New Feature
↓
New Contract
↓
New Oracle
↓
New Upgrade
↓
New Risk
Therefore, significant code changes should trigger additional security review.
For production protocols, teams should also consider:
Security should be treated as a continuous process.
Putting everything together, a professional audit can be represented as:
┌────────────────────┐
│ Project Scope │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Codebase Review │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Architecture Map │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Threat Modeling │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Entry Point Review │
└─────────┬──────────┘
↓
┌────────────┴────────────┐
↓ ↓
Manual Review Automated Analysis
│ │
└────────────┬────────────┘
↓
┌────────────────────┐
│ Fuzzing / Invariant│
│ Testing │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Attack Simulation │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Findings & PoCs │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Developer Fixes │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Re-audit / Verify │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Final Report │
└─────────┬──────────┘
↓
┌────────────────────┐
│ Continuous Security│
└────────────────────┘
Not every audit provides the same level of security assurance.
A strong audit should answer several important questions.
Reading Solidity without understanding the business logic is not enough.
The most interesting vulnerabilities often require combining several functions.
A suspicious piece of code is not necessarily an exploitable vulnerability.
DeFi protocols are financial systems.
Price manipulation, rounding, liquidity, collateral, and accounting must be considered.
A protocol may depend on:
Oracle
DEX
ERC20
ERC4626
Bridge
Lending Protocol
Those dependencies become part of the threat model.
Finding a vulnerability is only part of the job.
The fix needs to be verified as well.
Modern security tooling is extremely powerful.
However, smart contract vulnerabilities increasingly involve logic and economics rather than simple coding mistakes.
Consider:
Oracle
↓
Price
↓
Collateral
↓
Borrow Limit
↓
Liquidation
Every individual component may appear correct.
The vulnerability may exist in the relationship between them.
This is why professional auditing combines:
Code Analysis
+
Protocol Understanding
+
Threat Modeling
+
Economic Reasoning
+
Automated Testing
+
Attack Simulation
The strongest audits are not simply looking for known vulnerability patterns.
They are trying to answer:
“What can an attacker actually do that the protocol designers did not intend?”
A smart contract audit is a structured security investigation.
It starts before the first vulnerability is found.
The auditor first needs to understand:
What does the protocol do?
Who can interact with it?
Who is trusted?
Where does the money move?
What assumptions does the system make?
What happens when those assumptions fail?
Only after understanding these questions does the detailed code review become meaningful.
A professional audit therefore goes far beyond checking for reentrancy or running a static analyzer.
It combines:
Architecture analysis → threat modeling → manual review → automated testing → invariant analysis → attack simulation → vulnerability reporting → remediation → re-audit.
The ultimate goal is not to produce a long list of issues.
The goal is to identify the realistic attack paths that could cause loss of funds, unauthorized control, protocol manipulation, or permanent disruption — before attackers discover them.
At PandaSecurity, our approach focuses on understanding the protocol from an attacker’s perspective and combining manual security research with automated analysis and practical exploit validation.
The best time to find a smart contract vulnerability is before it reaches mainnet.
UNCX's token vesting contract lost roughly $87.7K when an attacker exploited a share-accounting flaw in release(), topUpVesting(), and cancel(). We break down the root cause, the exact attack sequence, and what builders should take away from it — without the jargon.
Autheo’s THEO token lost 92% after attackers used a Hyperlane TrustedRelayerIsm misconfiguration to mint 19 million unbacked tokens on Base and extract roughly $1.76 million. Here’s how the attack worked and why single-key security models remain dangerous for cross-chain assets.
Uniswap v4 hooks explained: the PoolManager singleton, flash accounting, and what the Cork and Bunni exploits teach us about hook security. A PandaSecurity deep dive.
An attacker forced a Uniswap V4 vault into a zero-liquidity state, redeemed one minimal share unit, and withdrew roughly 14,838.47 DAI. Panda Security breaks down the transaction, vulnerable code, root cause, and practical fixes.