DEV Community

ohmygod
ohmygod

Posted on

The 2026 DeFi Pre-Launch Security Checklist: 7 Attack Surfaces Your Audit Probably Missed

The OWASP Smart Contract Top 10 reshuffled in 2026 — reentrancy dropped from #2 to #8, proxy vulnerabilities entered the chart, and business logic flaws climbed. But the real story isn't what moved on the list. It's the gaps between the categories where protocols are still getting drained.

After reviewing dozens of post-mortems from Q1 2026 alone (SIR.trading's $355K transient storage exploit, the GMX V1 reentrancy, multiple account abstraction griefing incidents), I've compiled a pre-launch security checklist that goes beyond "did you run Slither and hire an auditor."

This is the checklist I wish every protocol team would tape to their wall before deploying to mainnet.


1. Transient Storage (EIP-1153): The 100-Gas Reentrancy You Didn't Plan For

The assumption that died: For nearly a decade, developers relied on the 2,300 gas stipend from transfer() and send() being insufficient for state-modifying SSTORE (5,000+ gas). EIP-1153 changed everything — TSTORE costs just 100 gas, well within the stipend.

What to check:

// ❌ DANGEROUS: Old-school "safe" ETH transfer
payable(recipient).transfer(amount);
// Recipient can now TSTORE in their fallback — 100 gas is enough

// ✅ SAFER: Use reentrancy guards even on "safe" transfers
function withdraw() external nonReentrant {
    uint256 amount = balances[msg.sender];
    balances[msg.sender] = 0; // CEI: Effects before Interactions
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
}
Enter fullscreen mode Exit fullscreen mode

Checklist items:

  • [ ] Audit every transfer() and send() call — they're no longer reentrancy-safe
  • [ ] Verify that transient storage slots are explicitly cleared after use within a transaction
  • [ ] Test that your reentrancy guards work with TSTORE-based callbacks
  • [ ] If using transient storage for locks, ensure they handle DELEGATECALL contexts correctly

Real-world failure: SIR.trading lost $355K in March 2025 because a forgotten transient storage slot persisted across internal calls within the same transaction. The attacker re-entered through a callback and found a "lock" that was already set from a previous call frame — but pointing to stale state.


2. Read-Only Reentrancy: Your View Functions Are Lying

Traditional reentrancy guards protect state-modifying functions. But what about view functions that dependent protocols call for price data?

The attack pattern:

  1. Attacker calls removeLiquidity() on Protocol A
  2. During the external ETH transfer, attacker's contract calls Protocol B
  3. Protocol B reads getVirtualPrice() from Protocol A
  4. The price is wrong because Protocol A's state is mid-update
  5. Protocol B makes decisions based on stale/manipulated data

What to check:

// ❌ DANGEROUS: Unguarded view function used as price oracle
function getVirtualPrice() external view returns (uint256) {
    return totalAssets * PRECISION / totalSupply;
    // During removeLiquidity, totalAssets is updated but
    // totalSupply hasn't been burned yet — price is inflated
}

// ✅ SAFER: Guard view functions with the same lock
modifier nonReentrantView() {
    require(!_locked, "ReentrancyGuard: reentrant view call");
    _;
}

function getVirtualPrice() external view nonReentrantView returns (uint256) {
    return totalAssets * PRECISION / totalSupply;
}
Enter fullscreen mode Exit fullscreen mode

Checklist items:

  • [ ] Identify every view/pure function that external protocols might use as an oracle
  • [ ] Apply reentrancy guards to price-returning view functions
  • [ ] Test with a mock attacker contract that re-enters view functions mid-state-change
  • [ ] Document which functions return consistent values during all execution states
  • [ ] If integrating with Curve/Balancer pools, verify their get_virtual_price / getRate calls are safe

3. Account Abstraction (ERC-4337) Composability Traps

If your protocol interacts with smart contract wallets — and in 2026, it almost certainly does — you have a new attack surface.

What to check:

// ❌ DANGEROUS: Assuming msg.sender is an EOA
require(msg.sender == tx.origin, "No contracts allowed");
// This breaks all smart wallet users AND doesn't prevent all attacks

// ❌ DANGEROUS: Assuming one user per transaction
// Smart wallets batch multiple UserOperations in one tx
// Transient storage from UserOp #1 can leak into UserOp #2

// ✅ BETTER: Use per-operation nonces and explicit context isolation
function execute(bytes calldata data, uint256 nonce) external {
    require(nonces[msg.sender] == nonce, "Invalid nonce");
    nonces[msg.sender]++;
    // ... execution logic
}
Enter fullscreen mode Exit fullscreen mode

Checklist items:

  • [ ] Remove all tx.origin checks — they break account abstraction and don't prevent attacks
  • [ ] If using transient storage, ensure state isolation between batched operations
  • [ ] Test your protocol with ERC-4337 bundled transactions (multiple UserOps in one tx)
  • [ ] Verify signature validation handles ERC-1271 (smart contract signatures) correctly
  • [ ] Check for cross-account replay: signatures must be bound to specific address + chain ID + nonce

4. Oracle Manipulation in the Age of Composability

Oracle attacks didn't disappear — they got more sophisticated. The latest generation combines flash loans with cross-protocol state inconsistencies.

The modern attack chain:

  1. Flash loan massive liquidity
  2. Manipulate a "secondary" price source (a pool that feeds a pool that feeds your protocol)
  3. Your oracle reads the manipulated price through 2-3 layers of indirection
  4. Profit

Checklist items:

  • [ ] Map your complete oracle dependency graph — not just direct sources, but what your sources depend on
  • [ ] Use TWAP with sufficient lookback periods (30 minutes minimum for volatile pairs)
  • [ ] Implement circuit breakers: if price moves >X% in one block, pause and switch to fallback
  • [ ] Test with flash loan simulations that manipulate secondary and tertiary price sources
  • [ ] Never use spot prices from AMM pools as primary oracle — combine with Chainlink/Pyth/RedStone
// ❌ DANGEROUS: Spot price from a single pool
uint256 price = poolReserve0 * PRECISION / poolReserve1;

// ✅ SAFER: TWAP with deviation check against trusted oracle
uint256 twapPrice = oracle.consult(token, TWAP_PERIOD);
uint256 chainlinkPrice = chainlinkFeed.latestAnswer();
uint256 deviation = twapPrice > chainlinkPrice
    ? (twapPrice - chainlinkPrice) * 10000 / chainlinkPrice
    : (chainlinkPrice - twapPrice) * 10000 / chainlinkPrice;
require(deviation < MAX_DEVIATION_BPS, "Price deviation too high");
Enter fullscreen mode Exit fullscreen mode

5. Proxy and Upgradeability: The New #1 Attack Surface

Proxy vulnerabilities surged in the OWASP 2026 rankings for good reason — they combine smart contract bugs with operational security failures.

Checklist items:

  • [ ] Storage collision testing: verify your proxy's storage layout doesn't overlap with implementation slots
  • [ ] Initialization: initialize() must be called in the same transaction as deployment (or use constructors in implementation to disable initializers)
  • [ ] Verify _authorizeUpgrade() has proper access control (not just onlyOwner — consider timelock + multisig)
  • [ ] Test upgrade path: deploy V1, upgrade to V2, verify all V1 state is preserved correctly
  • [ ] If using UUPS, ensure _authorizeUpgrade exists in every implementation — missing it bricks the proxy
  • [ ] Add upgrade event monitoring to your incident response toolkit
// ❌ DANGEROUS: Forgetting to protect initialization
contract MyProtocolV2 is UUPSUpgradeable {
    function initialize() public initializer {
        // If someone calls this before the team...
        __Ownable_init(msg.sender); // Attacker is now owner
    }
}

// ✅ SAFER: Disable initializers in constructor
contract MyProtocolV2 is UUPSUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Cross-Chain Message Verification

If your protocol receives messages from bridges (Axelar, LayerZero, Wormhole, Hyperlane), the CrossCurve exploit from January 2026 should be your required reading — a missing gateway check let attackers spoof bridge messages and drain $3M.

Checklist items:

  • [ ] Verify the source of cross-chain messages at the contract level (not just in off-chain infra)
  • [ ] Validate the sender address on the source chain — don't just trust the bridge's relayer
  • [ ] Implement message replay protection with unique nonces per source chain
  • [ ] Rate-limit cross-chain operations (maximum value per time window per source)
  • [ ] Test with spoofed messages that bypass the bridge but call your receiver directly
// ❌ DANGEROUS: Trusting any call to the receiver function
function receiveMessage(bytes calldata payload) external {
    // Anyone can call this!
    _processPayload(payload);
}

// ✅ SAFER: Verify the gateway AND source
function receiveMessage(
    bytes32 commandId,
    string calldata sourceChain,
    string calldata sourceAddress,
    bytes calldata payload
) external {
    require(msg.sender == address(gateway), "Not gateway");
    require(
        keccak256(bytes(sourceAddress)) == trustedSenders[sourceChain],
        "Untrusted sender"
    );
    require(!processedCommands[commandId], "Already processed");
    processedCommands[commandId] = true;
    _processPayload(payload);
}
Enter fullscreen mode Exit fullscreen mode

7. Key Management and Operational Security

The Resolv hack reminded us: sometimes the vulnerability isn't in the code — it's in the AWS key that controls the minting function. $25M gone because of a compromised cloud credential.

Checklist items:

  • [ ] No single key should have unilateral power over critical functions (mint, pause, upgrade, withdraw)
  • [ ] Privileged operations require timelock (24-48h minimum) + multisig (3/5 or higher)
  • [ ] Monitor admin key activity with real-time alerts (Forta, OpenZeppelin Defender, custom bots)
  • [ ] Rotate keys on a schedule and after any team member departure
  • [ ] If using HSMs or MPC wallets, verify the threshold configuration
  • [ ] Document your incident response plan: who has keys, who approves, what's the escalation path

The Meta-Checklist: Process Over Checklists

The seven areas above will catch specific bugs. But the protocols that don't get hacked share a common trait: they have a security process, not just a security checklist.

  1. Audit ≠ Security. An audit is a snapshot. Security is continuous. Use monitoring (Forta, Hypernative), bug bounties (Immunefi), and internal red-teaming.

  2. Test the integration, not just the contract. Most 2026 exploits happen at the seams between protocols. Fork mainnet, simulate real composability scenarios, and fuzz the interactions.

  3. Assume your keys will be compromised. Design your system so that a single compromised key can't drain everything. Timelocks and multisigs aren't optional — they're your last line of defense.

  4. Keep your dependencies updated. The Agave v3.0.14 incident showed that 82% of Solana validators ignored a critical patch for days. Don't be the protocol running an unpatched dependency.

  5. Make security boring. The best security is the kind nobody notices because it's woven into every PR review, every deployment, and every architectural decision.


Conclusion

The DeFi security landscape in 2026 is more nuanced than ever. The classic vulnerabilities haven't disappeared — they've evolved. Reentrancy wears a new disguise (transient storage, read-only variants). Oracle manipulation works through multiple layers of indirection. And the attack surface expanded dramatically with account abstraction and cross-chain messaging.

Use this checklist as a starting point, not a destination. The protocols that survive are the ones that treat security as a continuous process, not a box to check before launch.


What would you add to this checklist? Drop your suggestions in the comments — I'll incorporate the best ones into a follow-up piece.

Top comments (0)