DEV Community

ohmygod
ohmygod

Posted on

The Makina Finance Exploit: How a 280M USDC Flash Loan Turned Curve Pool Data Into a $4M ATM

On January 20, 2026, Makina Finance — a DeFi yield execution engine with $100M TVL — lost approximately $4 million in one of the most technically elegant oracle manipulation attacks of the year. The attacker borrowed 280 million USDC across two flash loans, temporarily distorted liquidity across multiple Curve Finance pools, and exploited Makina's blind trust in external pool data to inflate share prices by 31% within a single transaction.

What makes this exploit worth studying isn't just the amount stolen — it's the pattern. Makina is the latest in a long line of protocols that treated composable DeFi data as ground truth. And it won't be the last.


The Setup: What Makina Got Wrong

Makina Finance operates as an execution engine for on-chain yield strategies, managing user deposits through "Machine" vaults that interact with external DeFi protocols. At the heart of its accounting sits the Caliber contract, responsible for calculating Assets Under Management (AUM) and the critical sharePrice that determines how much each depositor's share is worth.

Here's where the vulnerability lived: Caliber computed positional AUM by calling external Curve Finance functions — specifically calc_withdraw_one_coin() and pool balance() readings — and used their outputs directly as multipliers in its calculations. No sanity checks. No time-weighted averaging. No flash loan resistance.

// Simplified illustration of the vulnerable pattern
function accountForPosition(uint256 positionId) external {
    // Reads external Curve pool data directly
    uint256 mimReward = curvePool.calc_withdraw_one_coin(lpAmount, coinIndex);
    uint256 poolBalance = curveDAI_USDC_USDT.balances(usdcIndex);

    // Uses raw external data as AUM multiplier — no validation!
    uint256 positionalAUM = balance * totalMIM3CrvReward / total3CrvLPSupply;

    // Propagates directly to sharePrice
    totalAUM += positionalAUM;
    sharePrice = totalAUM / totalShares;
}
Enter fullscreen mode Exit fullscreen mode

This is the DeFi equivalent of asking "how much is my house worth?" and accepting whatever number a stranger shouts from across the street.


The Attack: A Masterclass in Composability Abuse

The attacker executed the entire exploit in a single transaction. Here's the kill chain:

Step 1: Borrow Everything

Flash loans from two sources:

  • 160.59M USDC from Morpho
  • 119.41M USDC from Aave V2

Total borrowed: 280M USDC — at zero collateral, zero cost (minus gas).

Step 2: Position for Profit

  • Added 100M USDC to Makina's DUSD/USDC Curve pool → received LP tokens
  • Swapped 10M USDC → 9.215M DUSD to establish a position for later arbitrage

Step 3: Poison the Oracle

This is where it gets surgical:

  1. Added 170M USDC to Curve's DAI/USDC/USDT pool → received 3Crv LP tokens
  2. Added 30M in DAI/USDC/USDT liquidity to the MIM-3LP3CRV-f pool
  3. Partially withdrew to receive MIM tokens
  4. Exchanged LP tokens for additional MIM, deliberately skewing pool balances:
    • DAI: ~34.87M
    • USDC: ~35.99M
    • USDT: ~96.65M (massively inflated)

These distorted balances are what Makina's Caliber contract would read as "truth."

Step 4: Inflate and Extract

With pools poisoned, the attacker called accountForPosition() on Caliber. The inflated MIM-3Crv reward values (via calc_withdraw_one_coin()) boosted the positional AUM to ~19.36M, which updated Machine's totalAUM to ~67.88M.

Result: sharePrice jumped from 1.011771 → 1.331577 — a 31% inflation in one block.

The attacker then:

  • Swapped ~9.215M DUSD → ~12.785M USDC (profiting from the inflated rate)
  • Removed LP tokens for ~100.66M USDC
  • Repeated the cycle until the pool's USDC was drained

Step 5: Clean Exit

After repaying all 280M USDC in flash loans:

  • Swapped profit USDC → WETH on Uniswap V3
  • Unwrapped to ETH
  • Transferred 1,299 ETH (~$4M) to an external address

The MEV Plot Twist

An MEV bot front-ran part of the attack, capturing 276 ETH (~$870K) — sent directly to a block builder/validator. The attacker's net take: approximately 1,023 ETH (~$3.2M).

This is a recurring pattern: even exploit profits aren't safe from the MEV supply chain.


The Root Cause: Trusting Composable State

Let's be precise about what went wrong:

Factor Detail
Primary vulnerability Unvalidated external Curve pool data used in AUM calculations
Amplifier Flash loans providing unlimited temporary capital
Missing defense No TWAP/VWAP, no sanity bounds, no flash loan detection
Architectural flaw Upgradeable Caliber proxy with no rate limiting
Impact scope Isolated to DUSD/USDC pool; other vaults unaffected

The core lesson: in DeFi, spot data from composable protocols is adversarial input. Any value that can be manipulated within a single transaction should be treated with the same suspicion as user input in a web application.


The Defense Playbook: How to Not Be Makina

1. Never Trust Spot Pool Data for Accounting

// BAD: Reading spot balance
uint256 aum = curvePool.balances(0);

// BETTER: Use time-weighted oracle with bounds
uint256 twapPrice = oracle.getTimeWeightedPrice(token, 30 minutes);
require(twapPrice > minBound && twapPrice < maxBound, "Price out of range");
Enter fullscreen mode Exit fullscreen mode

If your accounting depends on external pool state, you're one flash loan away from bankruptcy.

2. Flash Loan Detection

modifier noFlashLoan() {
    require(block.number > lastInteractionBlock[msg.sender], "Same block");
    lastInteractionBlock[msg.sender] = block.number;
    _;
}
Enter fullscreen mode Exit fullscreen mode

Simple but effective: prevent the same address from depositing and withdrawing in a single block.

3. SharePrice Circuit Breakers

function _updateSharePrice(uint256 newPrice) internal {
    uint256 maxDelta = lastSharePrice * MAX_PRICE_CHANGE_BPS / 10000;
    require(
        newPrice <= lastSharePrice + maxDelta,
        "SharePrice change exceeds threshold"
    );
    lastSharePrice = newPrice;
}
Enter fullscreen mode Exit fullscreen mode

A 31% sharePrice jump in one transaction should trip every alarm. Cap per-block price movements.

4. Delayed AUM Calculations

Never allow AUM recalculation and withdrawal in the same transaction. Introduce a minimum delay (even 1 block) between accountForPosition() and any withdrawal that depends on the updated values.

5. Solana Parallel: CPI Return Validation

Solana programs face the same composability risk through Cross-Program Invocations (CPI). If your program reads oracle data from another program, validate it:

// Anchor: Validate oracle freshness and bounds
let oracle_data = OracleAccount::try_from(&ctx.accounts.price_oracle)?;
require!(
    Clock::get()?.unix_timestamp - oracle_data.last_update < MAX_STALENESS,
    ErrorCode::StaleOracle
);
require!(
    oracle_data.price > MIN_PRICE && oracle_data.price < MAX_PRICE,
    ErrorCode::PriceOutOfBounds
);
Enter fullscreen mode Exit fullscreen mode

The Bigger Picture: Oracle Manipulation in 2026

Makina joins a growing list of 2026 oracle-related exploits:

  • Venus Protocol (March 15): $3.7M lost via donation attack + TWAP manipulation on THE token
  • Aave CAPO incident (March 10): Oracle misconfiguration triggered $26M in unwarranted liquidations
  • Makina Finance (January 20): $4M via flash loan oracle manipulation on Curve pool data

The pattern is consistent: protocols that rely on spot composable data without defensive layers will eventually be exploited. The question is when, not if.

According to industry data, oracle manipulation and flash loan attacks have drained over $400 million from DeFi protocols in 2026 alone. And we're only in March.


Takeaways for Builders and Auditors

  1. Audit your oracle dependencies. Map every external data source your protocol reads. Each one is an attack surface.

  2. Assume flash loans exist. Any value that can change within a single transaction can be adversarially manipulated. Design accordingly.

  3. Implement circuit breakers. SharePrice, collateral ratios, and TVL should all have maximum per-block change limits.

  4. Test with adversarial composability in mind. Your fuzz tests should include flash loan scenarios that manipulate external protocols your code depends on.

  5. Consider the MEV dimension. Even if your protocol isn't directly attacked, MEV bots can amplify or redirect exploit proceeds — adding unpredictable loss surfaces.

The Makina exploit wasn't novel. It used well-known techniques: flash loans, oracle manipulation, composability abuse. But it worked because the protocol's designers didn't account for the adversarial nature of on-chain data. In DeFi, trust is a vulnerability. Verify everything.


This article is part of the DeFi Security Research series. Follow for weekly deep-dives into smart contract vulnerabilities, audit techniques, and defense patterns across EVM and Solana ecosystems.

Top comments (0)