Post-Mortem

Summary

On October 19th the Mira protocol experienced a security incident where two bugs in Mira's codebase caused the application to inadvertently overpay some traders at the expense of others. In total traders walked away with ~$68k of assets that belonged to LPs in the sDai/USDC pool. The issue was quickly mitigated, and all users were refunded in full within six days of the incident. Below is our analysis of the incident to improve the security in the Fuel ecosystem and to ensure transparency around the operation of the protocol.

Timeline

Oct 19 (9:00–15:00 UTC+0): The Mira team identifies a sharp drop in TVL overnight and investigates alongside Ottersec and other collaborators. Data queries confirm multiple large withdrawals, including one whale removing approximately $1.2M. A surge in trading activity is also observed, and further analysis reveals that the sDAI/USDC pool has fewer assets remaining than what was withdrawn, confirming that a bug must exist in the code.

Oct 19 (15:00–16:00 UTC+0): Mitchell from Immunefi is brought in to lead the war room operations to organize our efforts. A pause hooksmart contract is immediately deployed to pause swaps and deposits to ensure all funds are safe while a more long-term fix is implemented. The analysis begins to determine the issue's root cause, the full impact, and possible longer-term solutions.

Oct 19 (16:00–18:00 UTC+0): Two bugs are identified as the root cause:

  1. An invariant calculation bug in the main singleton contract for pools.

  2. A decimal mismatch bug in one of the scripts.

Work starts on deploying a hook to enforce proper invariant checks and rewriting the scripts to resolve the decimal mismatch issue.

Oct 20: A new hook smart contract is deployed to verify and enforce invariant checks for all trades, ensuring mathematical guarantees without requiring user action. Fully securing contracts and mitigating the vulnerability.

Oct 21-23: The swap script is updated to handle token decimal mismatches, resolving the specific issue that drained the sDAI/USDC pool. Follow-up reviews of the full code base are conducted by Mira and Ottersec to confirm the security and identify any follow-on impacts.

Oct 23 (15:30 UTC+0): Swaps and deposits are unpaused, and normal operation resumes.

Oct 25: All affected users are refunded in full.

Root cause analysis

After investigating the contracts with the help of Ottersec, it became clear that there were two bugs that caused traders to withdraw more funds than intended.

Invariant calculation bug

Mira uses a singleton contract model where all liquidity pools share the same smart contract (similar to Uniswap V4), as opposed to more traditional AMM architectures where each pool has its own smart contract.

This singleton architecture led to a bug being introduced in the codebase. Instead of reading the balance of tokens for a specific pool, the contract reads the total balance of the given asset in the singleton contract. This incorrect value was used for the invariant check in the swap function.

This error allowed users to withdraw more tokens than they were entitled to during a swap, bypassing the intended mathematical guarantees (invariants) of the DEX. The issue affected all pools in the protocol, making them vulnerable to potential exploitation.

Decimal mismatch bug

The swap script, which calculates output token amounts during swaps, had a bug in the decimal calculation for swapping stable-pair assets. For example, USDC uses 6 decimals, while sDai on Fuel uses 9 decimals. The script misinterpreted these differences, leading it to request more assets than were actually required for the swap. This mismatch only affected the sDAI/USDC pool, since it was the only pool that both used the stable-swap invariant and had assets with different amounts of decimals. This bug leads users to request excessive tokens, inadvertently taking tokens that belo.

Combined effect

The Invariant Calculation Bug caused the system to operate on incorrect assumptions about pool balances, creating a vulnerability across all pools. However, this issue might have gone unnoticed for much longer if not for the Decimal Mismatch Bug.

The mismatch caused traders of a specific pool (sDAI/USDC) to request excessive tokens, which unintentionally drained that pool. While the rest of the pools remained unaffected because the front-end logic adhered to intended token request amounts, the decimal bug effectively surfaced the deeper invariant issue.

This early detection ensured the invariant bug was identified and fixed before it could be maliciously exploited by a blackhat.

Impact

The resulting impact of the bugs was that ~$68k in assets inadvertently was given to traders, which belonged to a handful of liquidity providers. As well as downtime of the core functionality for about 3 days while the fixes were being implemented.

Resolution

To address the vulnerabilities and ensure the safety of user funds, we took the following actions:

Immediate Mitigation: Pausing Functionality

As soon as the issues were identified, we deployed a pause hook for the swap and deposit functionality across the protocol. This swift action ensured LPs didn’t lose any more funds while we worked on implementing permanent fixes.

While the Mira contract itself does not have a pause functionality, we were able to pause the contract using the “hook” functionality (described below).

Fixing the Invariant Bug via Contract Hook

Mira was designed to prioritize decentralization, with an immutable, non-upgradable smart contract (similar to Uniswap). However, a “hook” feature was included to enable future feature additions without allowing the protocol to become custodial. This feature ended up proving critical in resolving the issue.

A new hook smart contract was created to verify every trade in the protocol. This hook implemented logic to correctly check the invariant after each swap. This ensured that all swaps adhered to proper mathematical guarantees going forward. Leveraging the hook allowed us to address the issue without redeploying the core contract, averting the need for any action on the part of users.

Fixing the Decimal Mismatch Bug via Script Update

The swap script, embedded in the front end, was updated to handle token decimal mismatches correctly. Because scripts are not deployed on-chain, we were able to quickly push this fix via the front end, resolving the specific issue that caused traders to be overpaid in the sDai/USDC pool.

User Refunds

To uphold trust and fairness, we fully refunded all affected users within 6 days of the incident. This ensured that no user suffered any financial loss as a result of the bugs, reinforcing our commitment to user security and satisfaction.

The biggest LPs were contacted and paid directly, while there was a long tail of smaller LPs which likely never noticed the issue and were refunded by receiving LP tokens directly to their wallets.

Acknowledgments

We want to give out a special thanks to the following individuals for their crucial contributions during this incident:

  • Mitchell Amador, CEO of Immunefi (leading on-chain audit and bug bounty platform), for providing expert guidance and support leading the war room operations and helping us focus our efforts during a stressful time.

  • Robert Chen, CEO of OtterSec, and James Wang, Security Researcher at OtterSec, for their thorough analysis, timely assistance, and support in identifying and addressing the root cause of the bugs.

We want to thank the community and our users for their patience during this time. Security incidents are an inherent risk when building on innovative technology, but with skilled teams and a committed community, issues can be identified and resolved before they lead to exploitation.

By sharing findings and encouraging open dialogue, we help the entire ecosystem learn and strengthen its defenses.

For any questions about this incident or if you are in need of support, reach out to hi@mira.ly and we’ll get back to you shortly.

Subscribe to MIRA
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.