Flashbots Protect integration

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Flashbots Protect integration
Simple
~1 business day
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of Oracle Manipulation Protection System

Oracle manipulation is one of the most profitable attack vectors in DeFi. Essence: an attacker artificially changes the price in a data source (oracle) that the protocol uses, profits from the incorrect price, then the price returns to normal. Unlike classic code exploits, you don't necessarily need to hack the smart contract—just temporarily influence the data that the contract trusts.

Mango Markets (October 2022): $114M stolen through manipulation of MNGO tokens in Mango oracle. CREAM Finance: $130M through flash loan + price manipulation of yUSD. These aren't edge cases—they're systemic risk with incorrect price feed architecture.

Types of Oracles and Attack Vectors

Spot price from AMM (worst option)

Using getReserves() from a Uniswap V2 pair as a price source—direct path to flash loan attack.

// CRITICALLY VULNERABLE
function getPrice(address token) public view returns (uint256) {
    (uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(pair).getReserves();
    // Spot price = reserve1 / reserve0
    // Flash loan can change reserves 100x in one transaction
    return uint256(reserve1) * 1e18 / uint256(reserve0);
}

Attack takes one transaction: flash loan → swap distorts reserves → call vulnerable protocol → repay flash loan. No traces, no risk to attacker.

TWAP (Time-Weighted Average Price)

TWAP is the standard defense against flash loan attacks. Average price over a period cannot be significantly changed by a single transaction.

// Uniswap V3 TWAP via OracleLibrary
import "@uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol";

contract TWAPOracle {
    address public immutable pool;        // Uniswap V3 pool
    uint32 public constant TWAP_PERIOD = 30 minutes;
    
    function getTWAP() public view returns (uint256 price) {
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = TWAP_PERIOD;
        secondsAgos[1] = 0;
        
        (int56[] memory tickCumulatives, ) = IUniswapV3Pool(pool).observe(secondsAgos);
        
        int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
        int24 arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(TWAP_PERIOD)));
        
        // Convert tick to price
        uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);
        price = FullMath.mulDiv(
            uint256(sqrtRatioX96) * uint256(sqrtRatioX96),
            1e18,
            2 ** 192
        );
    }
}

TWAP Limitations: 30-minute TWAP can be shifted with gradual manipulation over 30 minutes. For tokens with low liquidity—attack cost decreases. TWAP isn't suitable as the sole defense for volatile assets or low-liquidity pools.

Chainlink Price Feeds

Chainlink is a decentralized oracle network. Price is aggregated from dozens of independent node operators, updated when deviation exceeds threshold (usually 0.5% or 1%) or by heartbeat (every 24 hours).

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract ChainlinkOracleConsumer {
    AggregatorV3Interface public immutable priceFeed;
    
    // Maximum time since last update
    uint256 public constant STALENESS_THRESHOLD = 3600; // 1 hour for most feeds
    
    constructor(address _priceFeed) {
        priceFeed = AggregatorV3Interface(_priceFeed);
    }
    
    function getPrice() public view returns (uint256) {
        (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        ) = priceFeed.latestRoundData();
        
        // Check staleness — price not outdated?
        require(
            block.timestamp - updatedAt <= STALENESS_THRESHOLD,
            "Oracle: stale price"
        );
        
        // Check round completeness
        require(answeredInRound >= roundId, "Oracle: incomplete round");
        
        // Check for negative price (shouldn't happen, but check)
        require(answer > 0, "Oracle: invalid price");
        
        // Normalize to 18 decimals
        uint8 decimals = priceFeed.decimals();
        uint256 normalizedPrice = uint256(answer);
        if (decimals < 18) {
            normalizedPrice = normalizedPrice * 10 ** (18 - decimals);
        } else if (decimals > 18) {
            normalizedPrice = normalizedPrice / 10 ** (decimals - 18);
        }
        
        return normalizedPrice;
    }
}

Common Mistakes with Chainlink:

  • Not checking updatedAt—accepting stale price during Chainlink downtime
  • Not checking answeredInRound >= roundId—accepting price from incomplete round
  • Hardcoding STALENESS_THRESHOLD without considering specific feed (heartbeat for ETH/USD = 1 hour, for exotic assets = 24 hours)

Multi-oracle System: Median Aggregation

Best defense—multiple independent sources with median aggregation. Manipulation of one source doesn't affect the median of three.

contract MultiOracleAggregator {
    struct OracleConfig {
        address oracle;
        uint256 stalenessThreshold;
        uint256 weight;     // weight for weighted average if needed
        bool active;
    }
    
    OracleConfig[] public oracles;
    
    // Maximum allowed deviation between oracles (basis points)
    uint256 public constant MAX_DEVIATION = 500;  // 5%
    
    function getPrice() external view returns (uint256 price, bool isValid) {
        uint256[] memory prices = new uint256[](oracles.length);
        uint256 validCount = 0;
        
        for (uint256 i = 0; i < oracles.length; i++) {
            if (!oracles[i].active) continue;
            
            try IOracle(oracles[i].oracle).getPrice() returns (uint256 p, bool valid) {
                if (valid && p > 0) {
                    prices[validCount++] = p;
                }
            } catch {
                // Oracle unavailable—continue with others
            }
        }
        
        // Need minimum 2 valid sources
        require(validCount >= 2, "Insufficient oracle responses");
        
        // Sort and take median
        uint256 median = _getMedian(prices, validCount);
        
        // Check all prices within MAX_DEVIATION of median
        bool allWithinDeviation = true;
        for (uint256 i = 0; i < validCount; i++) {
            uint256 deviation = _absDiff(prices[i], median) * 10000 / median;
            if (deviation > MAX_DEVIATION) {
                allWithinDeviation = false;
                break;
            }
        }
        
        return (median, allWithinDeviation);
    }
    
    function _getMedian(uint256[] memory arr, uint256 length) internal pure returns (uint256) {
        // Insertion sort for small arrays (usually 3-5 oracles)
        for (uint256 i = 1; i < length; i++) {
            uint256 key = arr[i];
            int256 j = int256(i) - 1;
            while (j >= 0 && arr[uint256(j)] > key) {
                arr[uint256(j + 1)] = arr[uint256(j)];
                j--;
            }
            arr[uint256(j + 1)] = key;
        }
        
        if (length % 2 == 1) {
            return arr[length / 2];
        } else {
            return (arr[length / 2 - 1] + arr[length / 2]) / 2;
        }
    }
}

Circuit Breaker on Abnormal Prices

When price deviates sharply—automatic protocol pause:

contract PriceCircuitBreaker {
    uint256 public lastValidPrice;
    uint256 public lastUpdateTime;
    
    // Maximum price change per update
    uint256 public constant MAX_PRICE_CHANGE_BPS = 1000;  // 10% per update
    
    // Maximum change per hour
    uint256 public constant MAX_HOURLY_CHANGE_BPS = 2000;  // 20% per hour
    
    bool public circuitBreakerTripped;
    
    struct PriceHistory {
        uint256 price;
        uint256 timestamp;
    }
    
    PriceHistory[10] public priceHistory;  // Ring buffer of last 10 prices
    uint256 public historyIndex;
    
    function updatePrice(uint256 newPrice) external onlyOracle {
        require(!circuitBreakerTripped, "Circuit breaker active");
        
        if (lastValidPrice > 0) {
            uint256 change = _absDiff(newPrice, lastValidPrice) * 10000 / lastValidPrice;
            
            // Sharp change per update
            if (change > MAX_PRICE_CHANGE_BPS) {
                circuitBreakerTripped = true;
                emit CircuitBreakerTripped(lastValidPrice, newPrice, change);
                return;
            }
            
            // Change per hour
            uint256 hourlyChange = _calculateHourlyChange(newPrice);
            if (hourlyChange > MAX_HOURLY_CHANGE_BPS) {
                circuitBreakerTripped = true;
                emit CircuitBreakerTripped(lastValidPrice, newPrice, hourlyChange);
                return;
            }
        }
        
        // Save to history
        priceHistory[historyIndex % 10] = PriceHistory(newPrice, block.timestamp);
        historyIndex++;
        
        lastValidPrice = newPrice;
        lastUpdateTime = block.timestamp;
        
        emit PriceUpdated(newPrice, block.timestamp);
    }
    
    function resetCircuitBreaker() external onlyGovernance {
        // Reset only through governance after analyzing cause
        circuitBreakerTripped = false;
        emit CircuitBreakerReset(msg.sender);
    }
}

Flash Loan Vulnerability: Detection and Protection

Read-only Reentrancy

Specific attack for Balancer/Curve-style pools: attacker uses flash loan callback to call another protocol while balances are already changed but state protection (reentrancy guard) still active in original pool.

// Balancer V2 re-entrancy lock check
interface IVault {
    function getReserves() external view returns (uint256, uint256);
    // Vault locked → getReserves() returns incorrect data during callback
}

contract BalancerOracleConsumer {
    IVault public immutable balancerVault;
    
    modifier ensureNotLocked() {
        // Check Balancer vault not locked (not in flash loan)
        // Try calling operationKind—if vault locked, reverts
        (, bytes memory data) = address(balancerVault).staticcall(
            abi.encodeWithSignature("getAuthorizer()")
        );
        require(data.length > 0, "Balancer vault locked");
        _;
    }
    
    function getBalancerPrice() external view ensureNotLocked returns (uint256) {
        // Safe call only when vault not in flash loan
        (uint256 reserve0, uint256 reserve1) = balancerVault.getReserves();
        return reserve1 * 1e18 / reserve0;
    }
}

Flash Loan Detection at Transaction Level

contract FlashLoanAwareProtocol {
    // Mapping from block.number → true if flash loan occurred in this block
    // through known flash loan providers
    mapping(uint256 => bool) private _flashLoanInBlock;
    
    // Callback from flash loan providers (AAVE, Uniswap, Balancer)
    // We get notified if flash loan is used in our protocol
    function markFlashLoanBlock() external onlyFlashLoanProvider {
        _flashLoanInBlock[block.number] = true;
    }
    
    modifier noFlashLoan() {
        require(!_flashLoanInBlock[block.number], "Flash loan detected in block");
        _;
    }
    
    // Price-sensitive operations only when no flash loan in block
    function borrow(address asset, uint256 amount) external noFlashLoan {
        uint256 price = oracle.getPrice(asset);
        // ...
    }
}

Pyth Network and Pull Oracle Model

Chainlink uses push model: data updates automatically on deviation or heartbeat. Pyth uses pull model: user updates price before transaction by providing signed price attestation from network.

import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";

contract PythOracleConsumer {
    IPyth public immutable pyth;
    bytes32 public immutable priceId;
    
    // Maximum price lifetime (60 seconds for most assets)
    uint256 public constant PRICE_MAX_AGE = 60;
    
    // updatePriceFeeds called in same transaction as operation
    function borrowWithPythPrice(
        uint256 borrowAmount,
        bytes[] calldata priceUpdateData  // data from Pyth
    ) external payable {
        // Update price (user pays fee ~$0.001)
        uint256 updateFee = pyth.getUpdateFee(priceUpdateData);
        pyth.updatePriceFeeds{value: updateFee}(priceUpdateData);
        
        // Get updated price
        PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, PRICE_MAX_AGE);
        
        require(price.price > 0, "Invalid price");
        require(price.conf < uint64(price.price) / 10, "Price confidence too low");
        
        uint256 normalizedPrice = _normalizePrice(price.price, price.expo);
        
        _executeBorrow(msg.sender, borrowAmount, normalizedPrice);
    }
    
    function _normalizePrice(int64 price, int32 expo) internal pure returns (uint256) {
        if (expo >= 0) {
            return uint256(uint64(price)) * 10 ** uint32(expo);
        } else {
            return uint256(uint64(price)) / 10 ** uint32(-expo);
        }
    }
}

Confidence interval in Pyth—important parameter: conf shows price uncertainty. If conf / price > 10%—data unreliable, operation should be rejected.

Oracle Anomaly Monitoring

interface PriceAnomaly {
  asset: string;
  currentPrice: bigint;
  historicalMedian: bigint;
  deviationPct: number;
  timestamp: number;
  oracleSource: string;
}

class OracleMonitor {
  private priceHistory: Map<string, bigint[]> = new Map();
  
  async detectAnomalies(
    assets: string[],
    oracleAddresses: string[]
  ): Promise<PriceAnomaly[]> {
    const anomalies: PriceAnomaly[] = [];
    
    for (let i = 0; i < assets.length; i++) {
      const currentPrice = await this.getCurrentPrice(oracleAddresses[i]);
      const history = this.priceHistory.get(assets[i]) ?? [];
      
      if (history.length >= 10) {
        // Median of last 10 prices
        const sorted = [...history].sort((a, b) => (a > b ? 1 : -1));
        const median = sorted[Math.floor(sorted.length / 2)];
        
        const deviation = Math.abs(
          Number((currentPrice - median) * 10000n / median)
        ) / 100;
        
        if (deviation > 15) {  // 15% deviation from median
          anomalies.push({
            asset: assets[i],
            currentPrice,
            historicalMedian: median,
            deviationPct: deviation,
            timestamp: Date.now(),
            oracleSource: oracleAddresses[i]
          });
          
          // Send alert immediately
          await this.sendAlert(anomalies[anomalies.length - 1]);
        }
      }
      
      // Update history
      history.push(currentPrice);
      if (history.length > 100) history.shift();
      this.priceHistory.set(assets[i], history);
    }
    
    return anomalies;
  }
}

Recommendations for Choosing Oracle Strategy

Scenario Recommended Solution
Major assets (ETH, BTC, stablecoins) Chainlink + TWAP as fallback
Long-tail DeFi tokens Uniswap V3 TWAP 30min + Circuit breaker
Cross-chain applications Pyth (fast update) + Chainlink verification
High-frequency protocols Pyth pull model (current price in each tx)
Low-liquidity assets Multi-oracle with high deviation threshold

Development Phases

Phase Content Timeline
Audit current oracle Analyze vulnerabilities of existing integration 1–2 weeks
Design multi-oracle Choose sources, weights, aggregation logic 1–2 weeks
Smart contracts Aggregator, circuit breaker, anomaly detection 3–4 weeks
Integration Chainlink, Pyth, TWAP connection 2–3 weeks
Monitoring system Off-chain alerting, dashboard 2–3 weeks
Testing Fork tests with attack simulation, fuzz 2–3 weeks
Audit 2–3 weeks