DeFi Security Guide

Comprehensive security practices for DeFi protocols, DEXs, lending platforms, and yield aggregators.

Overview

DeFi protocols have lost billions to exploits. Understanding attack vectors and implementing proper defenses is critical.

Major DeFi Exploits

Ronin Bridge
Access Control
$625M
Wormhole
Signature Verification
$326M
Nomad Bridge
Initialization Bug
$190M
Mango Markets
Oracle Manipulation
$114M
Cream Finance
Flash Loan Attack
$130M

Flash Loan Protection

Flash loans enable attackers to borrow billions without collateral. Protect against price manipulation attacks.

Solidity
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.19;
3
4contract FlashLoanProtected {
5 mapping(address => uint256) public lastBlock;
6
7 // Prevent same-block manipulation
8 modifier noFlashLoan() {
9 require(
10 lastBlock[tx.origin] != block.number,
11 "Flash loan detected"
12 );
13 lastBlock[tx.origin] = block.number;
14 _;
15 }
16
17 // Use TWAP instead of spot prices
18 function getPrice() public view returns (uint256) {
19 // Use Uniswap V3 TWAP or Chainlink
20 return oracle.consult(token, 1e18, TWAP_PERIOD);
21 }
22
23 function swap(uint256 amount) external noFlashLoan {
24 uint256 price = getPrice(); // TWAP is flash-loan resistant
25 // ... swap logic
26 }
27}

TWAP Period

Use a TWAP period of at least 10-30 minutes to resist manipulation. Shorter periods are still vulnerable.

Oracle Security

Oracle manipulation is a leading cause of DeFi exploits. Never use single-source spot prices.

Solidity
1// Multi-oracle strategy with validation
2contract SecureOracleConsumer {
3 AggregatorV3Interface public chainlinkOracle;
4 IUniswapV3Pool public uniswapPool;
5
6 uint256 public constant MAX_DEVIATION = 5; // 5%
7 uint256 public constant STALENESS_THRESHOLD = 1 hours;
8
9 function getValidatedPrice() public view returns (uint256) {
10 // Get Chainlink price
11 uint256 chainlinkPrice = getChainlinkPrice();
12
13 // Get Uniswap TWAP
14 uint256 twapPrice = getTwapPrice();
15
16 // Validate prices are within acceptable deviation
17 uint256 deviation = calculateDeviation(chainlinkPrice, twapPrice);
18 require(deviation <= MAX_DEVIATION, "Price deviation too high");
19
20 // Use Chainlink as primary, TWAP as validation
21 return chainlinkPrice;
22 }
23
24 function getChainlinkPrice() internal view returns (uint256) {
25 (, int256 price,, uint256 updatedAt,) =
26 chainlinkOracle.latestRoundData();
27
28 require(price > 0, "Invalid price");
29 require(
30 block.timestamp - updatedAt < STALENESS_THRESHOLD,
31 "Stale price"
32 );
33
34 return uint256(price);
35 }
36}

MEV Protection

Miner Extractable Value (MEV) attacks include front-running, sandwich attacks, and transaction reordering.

Solidity
1// Commit-reveal pattern for MEV protection
2contract MEVProtected {
3 mapping(bytes32 => bool) public commitments;
4 mapping(bytes32 => uint256) public commitBlock;
5
6 uint256 public constant REVEAL_DELAY = 1; // blocks
7
8 // Phase 1: Commit hash of action
9 function commit(bytes32 commitment) external {
10 require(!commitments[commitment], "Already committed");
11 commitments[commitment] = true;
12 commitBlock[commitment] = block.number;
13 }
14
15 // Phase 2: Reveal and execute
16 function reveal(
17 uint256 amount,
18 uint256 minOutput,
19 bytes32 secret
20 ) external {
21 bytes32 commitment = keccak256(
22 abi.encodePacked(msg.sender, amount, minOutput, secret)
23 );
24
25 require(commitments[commitment], "No commitment");
26 require(
27 block.number > commitBlock[commitment] + REVEAL_DELAY,
28 "Too early"
29 );
30
31 delete commitments[commitment];
32
33 // Execute swap with slippage protection
34 _swap(amount, minOutput);
35 }
36}

Liquidity Pool Security

AMM and liquidity pool implementations require careful attention to invariants and edge cases.

Solidity
1// Secure AMM invariant checks
2contract SecureAMM {
3 // Check invariant before AND after operations
4 modifier maintainInvariant() {
5 uint256 kBefore = reserve0 * reserve1;
6 _;
7 uint256 kAfter = reserve0 * reserve1;
8 require(kAfter >= kBefore, "Invariant violated");
9 }
10
11 // Prevent first depositor attacks
12 function initializePool(
13 uint256 amount0,
14 uint256 amount1
15 ) external {
16 require(totalSupply == 0, "Already initialized");
17
18 // Mint minimum liquidity to dead address
19 uint256 liquidity = sqrt(amount0 * amount1);
20 require(liquidity > MINIMUM_LIQUIDITY, "Insufficient liquidity");
21
22 _mint(address(0xdead), MINIMUM_LIQUIDITY);
23 _mint(msg.sender, liquidity - MINIMUM_LIQUIDITY);
24 }
25}

Governance Security

Governance attacks can drain treasuries. Implement proper timelocks and vote validation.

Solidity
1// Secure governance with timelocks
2contract SecureGovernance {
3 uint256 public constant TIMELOCK_DELAY = 2 days;
4 uint256 public constant VOTING_PERIOD = 3 days;
5 uint256 public constant QUORUM = 4; // 4% of supply
6
7 // Prevent flash loan voting attacks
8 function castVote(uint256 proposalId) external {
9 // Use voting power from PREVIOUS block
10 uint256 votingPower = token.getPriorVotes(
11 msg.sender,
12 proposals[proposalId].startBlock - 1
13 );
14
15 require(votingPower > 0, "No voting power");
16 // ... voting logic
17 }
18
19 // All proposals go through timelock
20 function execute(uint256 proposalId) external {
21 Proposal storage proposal = proposals[proposalId];
22
23 require(proposal.eta != 0, "Not queued");
24 require(block.timestamp >= proposal.eta, "Timelock active");
25 require(
26 block.timestamp <= proposal.eta + GRACE_PERIOD,
27 "Proposal expired"
28 );
29
30 proposal.executed = true;
31 _executeTransaction(proposal);
32 }
33}

Use Bloodhound for DeFi

Run bloodhound scan --mode apex for comprehensive DeFi security analysis including flash loan, oracle, and MEV vulnerability detection.