In March 2026 alone, DeFi protocols have hemorrhaged over $55 million to exploits — from Solv Protocol's $2.7M ERC-3525 reentrancy to Aave's $50M slippage catastrophe and Curve's $239K oracle manipulation. Yet most protocol teams still have no documented incident response plan. When the exploit alert fires at 3 AM, they're improvising under maximum stress with millions on the line.
This article provides the concrete, step-by-step playbook that every DeFi protocol team should have printed, laminated, and taped to their monitors before the inevitable happens.
Phase 0: The Golden Five Minutes (T+0 to T+5)
The first five minutes determine whether you lose $500K or $50M. Every second of deliberation is money leaving the protocol.
Immediate Actions Checklist
□ CONFIRM the exploit (not a false alarm from monitoring)
□ ACTIVATE the war room (dedicated Telegram/Discord channel)
□ PAUSE the protocol (if pause mechanism exists)
□ NOTIFY core team members (phone calls, not messages)
□ DO NOT post publicly yet
The Pause Decision Tree
Is the exploit actively draining funds RIGHT NOW?
├── YES → Pause immediately. Ask questions later.
│ Call pauseAll() / emergencyShutdown()
│ If multisig required: get signers NOW (phone calls)
│
└── NO (exploit occurred in past / conditions not yet met)
├── Can it be replicated? → Pause preventively
└── One-time condition? → Assess before pausing
Critical mistake teams make: Waiting for consensus before pausing. The Euler Finance team's 13-minute response time in 2023 was considered fast — but $197M had already left. If you have a pause mechanism, use it first and debate later.
Code: Emergency Pause Pattern (Solidity)
// The pause guardian should be a single EOA for speed,
// NOT a multisig for emergency pause
contract ProtocolCore is Pausable {
address public pauseGuardian;
modifier onlyPauseAuthority() {
require(
msg.sender == pauseGuardian ||
msg.sender == owner(),
"Not authorized to pause"
);
_;
}
function emergencyPause() external onlyPauseAuthority {
_pause();
emit EmergencyPaused(msg.sender, block.timestamp);
// Emit detailed event for post-mortem
}
// Unpause requires multisig (higher security for resumption)
function unpause() external onlyOwner {
_unpause();
}
}
For Solana Programs: Emergency Halt
// Anchor pattern for emergency pause
#[account]
pub struct ProtocolState {
pub authority: Pubkey,
pub pause_guardian: Pubkey,
pub is_paused: bool,
pub paused_at: i64,
pub pause_reason: [u8; 64],
}
pub fn emergency_pause(ctx: Context<EmergencyPause>, reason: String) -> Result<()> {
let state = &mut ctx.accounts.protocol_state;
require!(
ctx.accounts.signer.key() == state.pause_guardian
|| ctx.accounts.signer.key() == state.authority,
ErrorCode::Unauthorized
);
state.is_paused = true;
state.paused_at = Clock::get()?.unix_timestamp;
// Store reason for post-mortem
let reason_bytes = reason.as_bytes();
let len = reason_bytes.len().min(64);
state.pause_reason[..len].copy_from_slice(&reason_bytes[..len]);
msg!("EMERGENCY PAUSE: {}", reason);
Ok(())
}
// Every instruction should check this
pub fn check_not_paused(state: &ProtocolState) -> Result<()> {
require!(!state.is_paused, ErrorCode::ProtocolPaused);
Ok(())
}
Phase 1: Triage and Containment (T+5 to T+30)
You've paused. Now figure out what happened and stop the bleeding from secondary vectors.
The Triage Framework
Answer these questions in order:
| Question | Why It Matters |
|---|---|
| What function was exploited? | Scope the damage surface |
| Is the attacker's address a contract or EOA? | Contract = automated, expect more txs |
| How much has been taken? | Sets the severity level |
| Are funds still in the attacker's wallet? | Recovery opportunity window |
| Are other protocols affected? (composability) | Coordinate disclosure |
| Is the exploit replicable on other chains? (if multichain) | Pause everywhere |
Transaction Forensics Checklist
# EVM: Trace the exploit transaction
cast run <TX_HASH> --trace # Foundry
# Or use Tenderly/Phalcon for visual trace
# Identify the attacker
cast call <ATTACKER_CONTRACT> "owner()(address)" --rpc-url $RPC
# Check if attacker is still active
cast balance <ATTACKER_ADDRESS> --rpc-url $RPC
# For Solana: Use Helius or Solana Explorer
# Check transaction details
solana confirm -v <TX_SIGNATURE> --url mainnet-beta
Contact List (Have This Ready BEFORE an Incident)
## Internal
- [ ] CTO / Lead Dev: [phone number]
- [ ] Smart Contract Lead: [phone number]
- [ ] DevOps / Infrastructure: [phone number]
- [ ] Communications Lead: [phone number]
- [ ] Legal Counsel: [phone number]
## External (Security Partners)
- [ ] Audit firm (primary): [contact]
- [ ] Audit firm (secondary): [contact]
- [ ] SEAL 911 (Emergency response): https://seal911.org
- [ ] Chainalysis / TRM Labs (tracing): [contact]
- [ ] Bridge operators (if cross-chain): [contacts]
## Chain-Specific
- [ ] Block builder contacts (for MEV protection)
- [ ] Validator contacts (for Solana: leader schedule awareness)
- [ ] CEX security teams (for freezing funds)
SEAL 911 is the single most important contact for DeFi teams. It's a volunteer group of top security researchers who respond to active exploits in real-time. Join their Telegram and have the link bookmarked.
Phase 2: Analysis and Recovery Planning (T+30 to T+4h)
Root Cause Analysis Template
Every post-mortem should answer:
## Root Cause Analysis
### The Bug
- **Vulnerability type:** [reentrancy / oracle manipulation / access control / logic error]
- **Affected contract(s):** [addresses]
- **Affected function(s):** [function signatures]
- **Introduced in:** [commit hash / deployment date]
- **Audit coverage:** [was this code audited? by whom? was this flagged?]
### The Exploit
- **Attacker address(es):** [list]
- **Attack transaction(s):** [list with links]
- **Attack contract:** [address, if applicable]
- **Funds stolen:** [amount in USD at time of exploit]
- **Exploit flow:**
1. [Step-by-step description]
2. ...
### Why It Wasn't Caught
- [ ] Not in audit scope
- [ ] Auditor missed it
- [ ] Introduced after audit
- [ ] Known risk, accepted
- [ ] Monitoring didn't detect it
- [ ] Detected but response too slow
The Recovery Decision Matrix
Funds still in attacker wallet?
├── YES
│ ├── Attacker is a known entity → Legal action + negotiate
│ ├── Attacker wallet is a contract → Check for rescue vectors
│ │ └── Can you front-run a withdrawal? → White-hat rescue
│ └── Funds moving to CEX → Contact exchange security teams
│ (Binance, Coinbase, Kraken all have security@ emails)
│
└── NO (funds already moved)
├── Through Tornado Cash / mixer → Recovery unlikely
├── Through bridge → Contact destination chain teams
└── To CEX → Freeze request (legal + direct contact)
The Bounty Offer
Many exploits end with negotiated returns. The standard approach:
## On-Chain Message Template (send as tx input data)
To the person/team who exploited [Protocol Name]:
We have identified you as the person responsible for the exploit
of [Protocol] on [date]. We are offering a [10-15]% white-hat
bounty of [$ amount] if you return the remaining funds to
[treasury address] within 48 hours.
If funds are returned, we will:
- Pay the bounty immediately
- Not pursue legal action
- Credit you as a white-hat if desired
If funds are NOT returned within 48 hours, we will:
- Engage law enforcement
- Work with blockchain analytics firms to trace and identify you
- Pursue all available legal remedies
Contact: [secure email] or [Telegram handle]
Treasury address: [address]
Phase 3: Communication (Parallel to Phase 1-2)
The Communication Timeline
| Time | Action | Channel |
|---|---|---|
| T+0 to T+30m | Say nothing publicly | Internal only |
| T+30m | Brief acknowledgment | Twitter/X |
| T+2h | Detailed situation update | Twitter/X + Discord |
| T+24h | Full incident report | Blog + all channels |
| T+7d | Post-mortem with fix plan | Blog + governance |
| T+30d | Compensation plan (if applicable) | Governance proposal |
Template: Initial Acknowledgment (T+30m)
We are aware of an incident affecting [Protocol Name] and are
actively investigating. The protocol has been paused as a
precautionary measure. User funds are our top priority.
We will provide a detailed update within 2 hours.
Do NOT interact with any contracts claiming to offer refunds.
What NOT to Do
- Don't blame users. Even if a user did something foolish (like Aave's $50M slippage trader), your protocol should have guardrails.
- Don't speculate on the attacker's identity publicly. Legal liability.
- Don't promise compensation before you know the full scope.
- Don't delete Discord messages asking about the exploit. Transparency builds trust.
- Don't rush to unpause. A second exploit after resumption destroys all remaining credibility.
Phase 4: Fix, Verify, and Resume (T+24h to T+7d)
The Resume Checklist
□ Root cause fully understood and documented
□ Fix implemented and tested
□ Fix reviewed by DIFFERENT auditor than original (fresh eyes)
□ Formal verification of critical invariants (if applicable)
□ Monitoring upgraded to detect similar patterns
□ Circuit breakers added (rate limits, max withdrawal per block)
□ Governance approval obtained (if applicable)
□ War game: can the fix be bypassed?
□ Staged rollout plan (testnet → limited mainnet → full)
□ Communication plan for resumption
Post-Fix Monitoring Upgrades
After every incident, your monitoring should gain new detectors:
# Example: Alert on anomalous withdrawal patterns
class ExploitDetector:
def __init__(self, protocol_address, alert_callback):
self.protocol = protocol_address
self.alert = alert_callback
self.baseline_withdrawal = self.calculate_baseline()
def check_transaction(self, tx):
withdrawal_amount = self.parse_withdrawal(tx)
# Alert if single withdrawal > 10x average
if withdrawal_amount > self.baseline_withdrawal * 10:
self.alert(
severity="CRITICAL",
message=f"Anomalous withdrawal: {withdrawal_amount}",
tx_hash=tx.hash,
action="CONSIDER IMMEDIATE PAUSE"
)
# Alert if same address withdraws > 3x in 10 blocks
recent_count = self.count_recent_withdrawals(
tx.sender, blocks=10
)
if recent_count >= 3:
self.alert(
severity="HIGH",
message=f"Rapid withdrawal pattern: {tx.sender}",
tx_hash=tx.hash,
action="INVESTIGATE IMMEDIATELY"
)
Pre-Incident Preparation: The Security Readiness Score
Rate your protocol (1 point each):
□ Pause mechanism exists and is tested monthly
□ Pause guardian is a single signer (not multisig) for speed
□ Unpause requires multisig or timelock
□ SEAL 911 contact established
□ On-call rotation with phone numbers (not just Telegram)
□ Transaction monitoring with automated alerts (Forta/OpenZeppelin Defender/custom)
□ Circuit breakers on critical functions (max amount per tx/block)
□ Incident response playbook documented and distributed
□ War game / tabletop exercise conducted quarterly
□ Bug bounty program active (Immunefi/HackerOne)
□ Multiple audit firms used across protocol lifetime
□ Insurance coverage (Nexus Mutual / InsurAce)
Score: ___/12
- 10-12: Well prepared
- 7-9: Decent but gaps exist
- 4-6: Significant risk
- 0-3: It's not if, it's when
Real-World Lessons from March 2026
Solv Protocol ($2.7M) — The Double-Mint
What happened: An ERC-3525 Semi-Fungible Token deposit path allowed minting shares twice for a single deposit when a full SFT was provided.
IR lesson: The exploit affected ~10 users. Solv's team committed to full compensation within 24 hours — the gold standard for user trust recovery.
What they did right:
- Immediate pause after detection
- Clear, honest communication
- Compensation commitment before full post-mortem
Curve sDOLA ($239K) — The Donation Attack
What happened: The sDOLA token's donate() function allowed direct price manipulation of the ERC4626 vault share price, which Llamalend used as an oracle.
IR lesson: Composability risk is underestimated. Your protocol is only as secure as the tokens and oracles it integrates with.
Preventive measure: Price deviation checks and TWAP-based oracle fallbacks should be mandatory for any lending market accepting yield-bearing tokens.
Aave Slippage ($50M) — Not a Hack, But a Design Gap
What happened: A whale tried to swap $50M USDT through Aave's interface, got sandwiched by MEV bots, and lost nearly everything to slippage.
IR lesson: Even if "the user ignored warnings," your protocol's UX is your last line of defense. Hard-cap swap sizes. Add confirmation friction proportional to risk. Make it impossible for users to catastrophically self-harm.
Conclusion: Your Protocol WILL Be Tested
The question isn't whether your protocol will face a security incident — it's whether you'll respond in a way that preserves user trust and minimizes damage. The protocols that survive exploits and come back stronger are the ones that had a plan before the alert fired.
Print this playbook. Adapt it to your protocol. Run a tabletop exercise this week. Because the next $50M exploit isn't a matter of if — it's a matter of when.
This article is part of the DeFi Security series by DreamWork Security. Follow for weekly deep-dives into smart contract vulnerabilities, audit techniques, and security best practices.
Tags: #security #defi #blockchain #bestpractices
Top comments (0)