TW
Tripwire
Find contracts. Test them. Review real vulns.

Analysis Run

019be3d6-842f-702c-a179-72c42be44b63

failed
Chain ID
1
Address
Block Number
Created
Thu, Jan 22, 2026 3:54 AM
Review Summary

What survived the funnel

Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?

Confirmed
0
Validated
0
Source
Verified
Review Backing
Present
Best current signal
Untrusted DELEGATECALL target reachable
crit 0 high 0 proxy context implementation known
Review Checklist
1. Read the findings table first. Ignore raw jobs until the result looks real.
2. Confirm attacker control, not just the existence of a sink.
3. Confirm money or privilege impact.
4. Keep it if the behavior is real. Kill it if this is normal proxy, admin, or upgrade plumbing.

Processing Jobs

Status & attempts
ID Type Status Attempts Heartbeat
41009 artifact.fetch complete 1 / 3 2 months ago
41010 proxy.resolve complete 1 / 3 2 months ago
41011 tool.cast_disasm complete 1 / 3 2 months ago
41012 analysis.bundle complete 1 / 3 2 months ago
41013 capability.graph complete 1 / 3 2 months ago
41014 detector.run complete 1 / 3 2 months ago
41015 validation.fork complete 1 / 3 2 months ago
41403 report.consolidate complete 1 / 6 2 months ago
41404 tool.slither failed 2 / 2 2 months ago

Artifact

Runtime bytecode: available
Creation TX:
Deployer:
Proxy: yes confirmed
Verified Source: yes
Proxy evidence
{
    "method": "etherscan+onchain",
    "status": "confirmed",
    "etherscan_hint": {
        "source": "etherscan",
        "etherscan": {
            "proxy_flag": true,
            "implementation": "0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3"
        }
    },
    "implementation": "0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3",
    "onchain_method": "eip1967"
}

Slither

tool.slither
Status
failed slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-xqbioggl) 'forge config --json' running 'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-xqbioggl) Traceback (most recent call last): File "/var/www/tripwire/.venv/bin/slither", line 10, in <module> sys.exit(main()) ~~~~^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main main_impl(all_detector_classes=detectors, all_printer_classes=printers) ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl ) = process_all(filename, args, detector_classes, printer_classes) ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all compilations = compile_all(target, **vars(args)) File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all compilations.append(CryticCompile(target, **kwargs)) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__ self._compile(**kwargs) ~~~~~~~~~~~~~^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile self._platform.compile(self, **kwargs) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile hardhat_like_parsing( ~~~~~~~~~~~~~~~~~~~~^ crytic_compile, str(self._target), build_directory, str(self._project_root) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing raise InvalidCompilation(txt) crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command? /tmp/slither-xqbioggl/out/build-info is not a directory.
Attempts
2 / 2
Findings
0
Solc
View stderr
Traceback (most recent call last):
  File "/var/www/tripwire/worker/worker.py", line 198, in main
    payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
  File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
    return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
    stdout_text, stderr_text, result_json = _run_slither(
                                            ~~~~~~~~~~~~^
        workspace,
        ^^^^^^^^^^
    ...<4 lines>...
        target_path=target_path,
        ^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
    raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-xqbioggl)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-xqbioggl)
Traceback (most recent call last):
  File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
    main_impl(all_detector_classes=detectors, all_printer_classes=printers)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
    ) = process_all(filename, args, detector_classes, printer_classes)
        ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
    compilations = compile_all(target, **vars(args))
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
    compilations.append(CryticCompile(target, **kwargs))
                        ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
    self._compile(**kwargs)
    ~~~~~~~~~~~~~^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
    self._platform.compile(self, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
    hardhat_like_parsing(
    ~~~~~~~~~~~~~~~~~~~~^
        crytic_compile, str(self._target), build_directory, str(self._project_root)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
    raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-xqbioggl/out/build-info is not a directory.

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
2
No findings?
no
Summary
Found oracle price validation gaps impacting MF_ONE pricing and a rapid-withdrawal refund accounting bug that can permanently reduce user limits.
View stdout
{"no_findings":false,"summary":"Found oracle price validation gaps impacting MF_ONE pricing and a rapid-withdrawal refund accounting bug that can permanently reduce user limits.","findings":[{"severity":"medium","category":"oracle","title":"MF_ONE pricing relies on unbounded/stale oracle values, enabling share mispricing","exploit_scenario":"If the Midas oracle can be manipulated or returns stale values, an attacker can deposit MF_ONE at an inflated price to mint excess shares, or force exchange price updates off a bad price. Those shares can later be redeemed for more USDC than deposited, draining value from other LPs.","recommendation":"Use an oracle interface that exposes updatedAt/roundId and enforce staleness checks and sane bounds; consider TWAP or a secondary oracle to cap deviations before using the price for deposits or TVL calculations.","evidence":"`IMidasOracle.lastAnswer()` is used directly to value MF_ONE for TVL and deposits, with no freshness/bounds checks.","locations":["contracts/main/Vault.sol:43","contracts/main/Vault.sol:96","contracts/main/strategies/morpho/StrategyMorphoBlue.sol:380"]},{"severity":"low","category":"other","title":"Rapid withdrawal refunds do not restore user limits","exploit_scenario":"When the operator refunds a rapid withdrawal, the refund path deletes the user’s request before passing the amount back to the vault, so `refundInstantLimit` receives zero. Separately, `userTotalRedeem` is never decremented on refunds/cancellations. Users who are refunded can be permanently blocked from future rapid redemptions despite having their shares returned.","recommendation":"Cache the requested share amount before deleting it and pass that value to `refundInstantLimit`; also decrement `userTotalRedeem` when a rapid withdrawal is canceled/refunded so limits reflect actual redeemed amounts.","evidence":"`refundWithdraw` deletes `_withdrawalRequestInstant` before calling `refundInstantLimit`, and `userTotalRedeem` only ever increases.","locations":["contracts/main/RedeemOperator.sol:297","contracts/main/RedeemOperator.sol:303","contracts/main/RedeemOperator.sol:305","contracts/main/RedeemOperator.sol:307","contracts/main/Vault.sol:63","contracts/main/Vault.sol:77"]}]}
View stderr
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-z8pvr768
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019be470-9992-75f1-943f-083b902006f5
--------
user
Context: The user requested the proxy at 0xc9ecc74110850168fc7373c2586e5a525b02b02f.
Audit the implementation at 0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3.
Findings should refer to the implementation unless explicitly about proxy mechanics.

You are a security auditor. Audit this Solidity codebase for exploitable vulnerabilities.
Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation, oracle manipulation,
accounting/invariant breaks, external call patterns, unchecked return values, and dangerous delegatecall.

Scope rules:
- You MUST fully read `AUDIT_BUNDLE.txt` before concluding. It contains the complete contents of all first-party Solidity sources in this workspace.
- Treat `lib/` and `@openzeppelin/` as third-party dependencies; skip them unless there is a concrete misuse angle in first-party code.
- If there are only dependency files and no first-party sources, set no_findings=true and explain in summary.

Output MUST be valid JSON matching the provided schema.
- If you find nothing meaningful: set no_findings=true and findings=[].
- Otherwise: set no_findings=false and include findings with severity/category/title and file/line locations when possible.

Output constraints (IMPORTANT):
- severity MUST be exactly one of: info, low, medium, high, critical (lowercase).
- category MUST be exactly one of: access_control, reentrancy, math, oracle, upgradeability, auth, other.
Keep summary short and concrete.
mcp startup: no servers
exec
/bin/bash -lc ls in /tmp/codex-z8pvr768 succeeded in 7ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
contracts
@morpho-org
@openzeppelin
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-z8pvr768 succeeded in 7ms:
This file concatenates the full contents of first-party Solidity sources for auditing.
Do not treat dependency code under lib/ or @openzeppelin/ as first-party.

================================================================================
FILE: @morpho-org/morpho-blue/src/interfaces/IIrm.sol
================================================================================
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {MarketParams, Market} from "./IMorpho.sol";

/// @title IIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);


thinking
**Planning implementation address identification**

Planning to parse the audit bundle to locate main contract and identify the implementation address, focusing on proxy patterns by searching for "contract" keywords to pinpoint relevant files.
exec
/bin/bash -lc 'rg -n "contract " AUDIT_BUNDLE.txt' in /tmp/codex-z8pvr768 succeeded in 7ms:
293:    /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
354:    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
380:    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
1368: * @dev This contract is used to manage KMS addresses for batch operations.
1370:contract Manager is Ownable {
1374:     * @dev Initializes the contract by setting the admin and adding the initial operator to the whitelist.
1375:     * @param _admin The address of the contract owner, it will be a multisignature address.
1452:contract RedeemOperator is IRedeemOperator, Constants, Ownable {
1460:    // Address of the vault contract (immutable)
1463:    // Address of the core token contract (immutable)
1498:     * @dev Initializes the contract with the vault, operator, fee receiver, and gas parameters.
1800: * @dev This contract is the logical implementation of the vault,
1805:contract Vault is VaultYieldBasic, Constants {
2012:abstract contract Constants {
2112: * contract ERC1967 {
2131: * contract Lock {
2450: * @dev This contract is the actual address of the strategy pool.
2453:contract StrategyMorphoBlue is IStrategy, OwnableUpgradeable, Constants {
2586:     * @dev Transfers funds from the vault contract to this contract.
2689:     * @dev Get the amount of assets in all lending protocols involved in this contract for the strategy pool.

thinking
**Investigating contract implementation candidates**

I'm exploring the audit bundle to identify which contract is deployed at address 0x71ea..., focusing on Vault, StrategyMorphoBlue, Manager, and RedeemOperator, and planning to use line numbers to examine their code sections.
exec
/bin/bash -lc "sed -n '1320,2200p' AUDIT_BUNDLE.txt" in /tmp/codex-z8pvr768 succeeded in 6ms:
    uint128 amount;
}

/// @title IMorphoFlashLoanCallback
/// @notice Interface that users willing to use `flashLoan`'s callback must implement.
interface IMorphoFlashLoanCallback {
    /// @notice Callback called when a flash loan occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of assets that was flash loaned.
    /// @param data Arbitrary data passed to the `flashLoan` function.
    function onMorphoFlashLoan(uint256 assets, bytes calldata data) external;
}

/// @title IPublicAllocator
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for PublicAllocator to have access to all the functions with the appropriate function
/// signatures.
interface IPublicAllocator {
    /// @notice Reallocates from a list of markets to one market.
exec
/bin/bash -lc "sed -n '2200,3400p' AUDIT_BUNDLE.txt" in /tmp/codex-z8pvr768 succeeded in 7ms:
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {


thinking
**Identifying audit target as Vault implementation**

The implementation at 0x71ea... likely corresponds to the Vault contract behind a proxy at 0xc9ec.... The focus will be auditing Vault's implementation code for common vulnerabilities and suspicious patterns.
thinking
**Detailing multi-contract vulnerability findings**

Key issues include a logic bug in RedeemOperator refundWithdraw causing instant limit refund to fail; potential oracle manipulation in Vault's MF_ONE pricing lacking staleness checks; unsafe external calls without reentrancy guards in Vault's strategy interactions; and share accounting inconsistencies involving userTotalSupply and redeems. These indicate medium-severity risks around accounting, oracle trust, and external call patterns.
exec
/bin/bash -lc 'rg -n "safeProtocolRatio" -n AUDIT_BUNDLE.txt' in /tmp/codex-z8pvr768 succeeded in 13ms:
2485:        uint256 safeProtocolRatio;
2537:        (uint256 _safeProtocolRatio, address _admin, address _rebalancer) =
2541:        if (_safeProtocolRatio > MORPHO_LLTV) revert Errors.InvalidSafeProtocolRatio();
2546:        s.safeProtocolRatio = _safeProtocolRatio;
2575:        emit UpdateSafeProtocolRatio(getStorage().safeProtocolRatio, _newRatio);
2576:        getStorage().safeProtocolRatio = _newRatio;
2668:        isOK_ = getStorage().safeProtocolRatio > collateralRatio_;


thinking
**Confirming flashLoan access constraints**

Verified that only the trusted rebalancer can trigger flashLoan interactions via callFlashLoan, preventing unauthorized calls to onMorphoFlashLoan despite no explicit caller checks there. Also confirmed reentrancy is guarded by ReentrancyGuardTransient on key Vault functions.
thinking
**Identifying upgrade and allowance risks**

Found that the owner can set instant fees beyond 100% causing deposit DoS, and the proxy pattern allows initializing the implementation contract, risking ownership hijack of logic contracts. Also noted potential allowance accumulation risks in vault-to-strategy token transfers if strategies are malicious or compromised.
exec
/bin/bash -lc "sed -n '2550,3100p' AUDIT_BUNDLE.txt" in /tmp/codex-z8pvr768 succeeded in 10ms:
        s.redeemRequestId = 0;

        __Ownable_init(_admin);
    }

    function vault() external view returns (address) {
        return getStorage().vault;
    }

    function rebalancer() external view returns (address) {
        return getStorage().rebalancer;
    }

    /**
     * @dev Add a new address to the position adjustment whitelist.
     * @param _newRebalancer The new address to be added.
     */
    function updateRebalancer(address _newRebalancer) external onlyOwner {
        if (_newRebalancer == address(0)) revert Errors.InvalidRebalancer();
        emit UpdateRebalancer(getStorage().rebalancer, _newRebalancer);


thinking
**Analyzing potential rounding and access control issues**

Noted possible rounding leftover in gas token distribution that stays in RedeemOperator for owner sweep, likely intended. Confirmed access control suffices in reallocateLiquidity despite using IPublicAllocator. Checked withdrawal and deposit flows for zero shares or unexpected ETH transfer, finding protections or fallbacks in place.
thinking
**Identifying oracle manipulation and accounting bugs**

Found critical risk of oracle price manipulation enabling minting of excessive shares due to stale or unchecked prices in Vault and StrategyMorphoBlue. Also uncovered a user rapid redeem refund bug causing permanent rapid redeem limit blocking by failing to restore limits after refundWithdraw. Several bookkeeping mismatches and upgrade/access patterns examined but deemed low risk or intended.
exec
/bin/bash -lc 'rg -n "interface IStrategy" -n AUDIT_BUNDLE.txt' in /tmp/codex-z8pvr768 succeeded in 7ms:
992:interface IStrategy {
exec
/bin/bash -lc "sed -n '960,1030p' AUDIT_BUNDLE.txt" in /tmp/codex-z8pvr768 succeeded in 13ms:
pragma solidity ^0.8.25;

interface IRedeemOperator {
    // Events for logging actions
    event RegisterWithdrawal(address indexed user, uint256 shares, bool isRapid);
    event ConfirmWithdrawal(address[] users, uint256[] amounts, bool isRapid);
    event UpdateOperator(address oldOperator, address newOperator);
    event UpdateFeeReceiver(address oldFeeReceiver, address newFeeReceiver);
    event Sweep(address token);
    event UpdateExchangePriceRate(uint256 oldRate, uint256 newRate);

    function registerWithdrawal(address _user, uint256 _shares, bool _isRapid, uint256 _epoch) external;

    function pendingWithdrawersCount(bool _isRapid, uint256 _epoch) external view returns (uint256);

    function pendingWithdrawers(uint256 _limit, uint256 _offset, bool _isRapid, uint256 _epoch) external view returns (address[] memory result_);

    function allPendingWithdrawers(bool _isRapid, uint256 _epoch) external view returns (address[] memory);

    function confirmWithdrawal(address[] calldata _Users, uint256 _totalGasTokenAmount, bool _isRapid, uint256 _epoch, uint256 _fairExchangePrice) external;


thinking
**Analyzing reentrancy and state risks**

I'm noting that getNetAssets isn't marked view and might enable reentrancy via malicious strategies, but since only owner or rebalancer can call, the risk is limited. Focusing on oracle manipulation and refund instant limit issues seems higher priority, while access control and depositPaused checks don't indicate problems.
thinking
**Identifying duplicate user issue causing revert in withdrawal**

Duplicates in the withdrawal users array can cause underflow and revert during distribution due to repeated removals and zeroed withdrawal amounts, leading to possible DoS by a trusted operator. Meanwhile, key findings remain oracle manipulation from unvalidated prices and refund accounting bugs, with plans to confirm exact source code line numbers.
exec
/bin/bash -lc "nl -ba contracts/main/Vault.sol | sed -n '1,200p'" in /tmp/codex-z8pvr768 succeeded in 7ms:
     1	// SPDX-License-Identifier: BUSL-1.1
     2	pragma solidity ^0.8.25;
     3	
     4	import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
     5	import "./libraries/Errors.sol";
     6	import "./common/Constants.sol";
     7	import "./vault/VaultYieldBasic.sol";
     8	import "../interfaces/midas/IDepositVault.sol";
     9	import "../interfaces/midas/IMidasOracle.sol";
    10	/**
    11	 * @title Vault contract
    12	 * @author Naturelab
    13	 * @dev This contract is the logical implementation of the vault,
    14	 * and its main purpose is to provide users with a gateway for depositing
    15	 * and withdrawing funds and to manage user shares.
    16	 */
    17	
    18	contract Vault is VaultYieldBasic, Constants {
    19	    using SafeERC20 for IERC20;
    20	

thinking
**Reviewing refund withdraw bug lines**
exec
/bin/bash -lc "nl -ba contracts/main/RedeemOperator.sol | sed -n '200,360p'" in /tmp/codex-z8pvr768 succeeded in 17ms:
   200	            count_ = _pendingWithdrawersInstant[_epoch].length();
   201	        } else {
   202	            count_ = _pendingWithdrawers[_epoch].length();
   203	        }
   204	
   205	        if (_offset >= count_ || _limit == 0) return result_;
   206	
   207	        count_ -= _offset;
   208	        if (count_ > _limit) count_ = _limit;
   209	
   210	        result_ = new address[](count_);
   211	        for (uint256 i = 0; i < count_; ++i) {
   212	            if (_isRapid) {
   213	                result_[i] = _pendingWithdrawersInstant[_epoch].at(_offset + i);
   214	            } else {
   215	                result_[i] = _pendingWithdrawers[_epoch].at(_offset + i);
   216	            }
   217	        }
   218	        return result_;
   219	    }

thinking
**Confirming line numbers for oracle manipulation**
exec
/bin/bash -lc "nl -ba contracts/main/strategies/morpho/StrategyMorphoBlue.sol | sed -n '300,520p'" in /tmp/codex-z8pvr768 succeeded in 6ms:
   300	
   301	    function borrow(address _asset, uint256 _amount) external onlyRebalancer {
   302	        _borrow(_asset, _amount);
   303	        emit Borrow(_asset, _amount);
   304	    }
   305	
   306	    function repay(address _asset, uint256 _amount) external onlyRebalancer {
   307	        _repay(_asset, _amount);
   308	        emit Repay(_asset, _amount);
   309	    }
   310	
   311	    function supplyAssetsUser() public view returns (uint256 totalSupplyAssets) {
   312	        totalSupplyAssets = IMorpho(MORPHO_POOL).expectedSupplyAssets(getMarketParams(), address(this));
   313	    }
   314	
   315	    function collateralAssetsUser() public view returns (uint256 totalCollateralAssets) {
   316	        bytes32[] memory slots = new bytes32[](1);
   317	        slots[0] =
   318	            MorphoStorageLib.positionBorrowSharesAndCollateralSlot(MarketParamsLib.id(getMarketParams()), address(this));
   319	        bytes32[] memory values = IMorpho(MORPHO_POOL).extSloads(slots);


thinking
**Identifying Vault and Oracle Risks**

Vault's optionalDeposit may allow deposits exceeding MF_ONE balance due to unchecked _assets, risking transferFrom failure. The Midas Oracle used by StrategyMorphoBlue lacks sanitation, posing a similar risk. VaultYieldBasic's exchange price update trusts an unverified rebalancer, enabling potential share manipulation. Also, VaultYieldBasic's initialize method on the implementation contract could allow takeover if not properly protected, raising upgradeability concerns.
thinking
**Highlighting Oracle and Redemption Accounting Issues**

The Vault and Strategy rely on a Midas Oracle without staleness checks, allowing possible price manipulation or outdated data to inflate share minting and misprice redemptions, posing a medium-risk oracle manipulation. Additionally, the redemption logic doesn’t decrement `userTotalRedeem` after refund or cancellation, causing permanent reduction in users’ ability to perform rapid redeems, representing a low-severity accounting bug affecting user experience.
exec
/bin/bash -lc 'rg -n "userTotalRedeem" -n contracts/main' in /tmp/codex-z8pvr768 succeeded in 26ms:
contracts/main/Vault.sol:33:    mapping(address => uint256) internal userTotalRedeem;
contracts/main/Vault.sol:70:        if (userTotalRedeem[msg.sender] + _shares > userTotalSupply[msg.sender]) {
contracts/main/Vault.sol:77:        userTotalRedeem[msg.sender] += _shares;


thinking
**Identifying minor logic bug and fee underflow risk**

Cancel doesn’t restore user redeem capacity, creating a low-severity liveness issue that could confuse or disrupt user balances but isn’t exploitable externally. Also, a fee can exceed 100% causing deposit underflow and revert, a governance risk but not an external vulnerability.
thinking
**Crafting detailed findings with oracle and refund bugs**

I analyzed multiple potential vulnerabilities and settled on two main findings: a medium-severity oracle price validation gap risking share mispricing and a low-severity rapid withdrawal refund accounting bug causing permanent user limit reductions. Both relate to key contract files with clear exploit and recommendation details to include in the final JSON report.
codex
{"no_findings":false,"summary":"Found oracle price validation gaps impacting MF_ONE pricing and a rapid-withdrawal refund accounting bug that can permanently reduce user limits.","findings":[{"severity":"medium","category":"oracle","title":"MF_ONE pricing relies on unbounded/stale oracle values, enabling share mispricing","exploit_scenario":"If the Midas oracle can be manipulated or returns stale values, an attacker can deposit MF_ONE at an inflated price to mint excess shares, or force exchange price updates off a bad price. Those shares can later be redeemed for more USDC than deposited, draining value from other LPs.","recommendation":"Use an oracle interface that exposes updatedAt/roundId and enforce staleness checks and sane bounds; consider TWAP or a secondary oracle to cap deviations before using the price for deposits or TVL calculations.","evidence":"`IMidasOracle.lastAnswer()` is used directly to value MF_ONE for TVL and deposits, with no freshness/bounds checks.","locations":["contracts/main/Vault.sol:43","contracts/main/Vault.sol:96","contracts/main/strategies/morpho/StrategyMorphoBlue.sol:380"]},{"severity":"low","category":"other","title":"Rapid withdrawal refunds do not restore user limits","exploit_scenario":"When the operator refunds a rapid withdrawal, the refund path deletes the user’s request before passing the amount back to the vault, so `refundInstantLimit` receives zero. Separately, `userTotalRedeem` is never decremented on refunds/cancellations. Users who are refunded can be permanently blocked from future rapid redemptions despite having their shares returned.","recommendation":"Cache the requested share amount before deleting it and pass that value to `refundInstantLimit`; also decrement `userTotalRedeem` when a rapid withdrawal is canceled/refunded so limits reflect actual redeemed amounts.","evidence":"`refundWithdraw` deletes `_withdrawalRequestInstant` before calling `refundInstantLimit`, and `userTotalRedeem` only ever increases.","locations":["contracts/main/RedeemOperator.sol:297","contracts/main/RedeemOperator.sol:303","contracts/main/RedeemOperator.sol:305","contracts/main/RedeemOperator.sol:307","contracts/main/Vault.sol:63","contracts/main/Vault.sol:77"]}]}
tokens used
69,295
Metadata
{
    "codex": {
        "ran_at": "2026-01-22T06:47:18.235800+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 66,
        "import_count": 87,
        "first_party_contracts_count": 28
    },
    "summary": "Found oracle price validation gaps impacting MF_ONE pricing and a rapid-withdrawal refund accounting bug that can permanently reduce user limits.",
    "raw_final": {
        "summary": "Found oracle price validation gaps impacting MF_ONE pricing and a rapid-withdrawal refund accounting bug that can permanently reduce user limits.",
        "findings": [
            {
                "title": "MF_ONE pricing relies on unbounded/stale oracle values, enabling share mispricing",
                "category": "oracle",
                "evidence": "`IMidasOracle.lastAnswer()` is used directly to value MF_ONE for TVL and deposits, with no freshness/bounds checks.",
                "severity": "medium",
                "locations": [
                    "contracts/main/Vault.sol:43",
                    "contracts/main/Vault.sol:96",
                    "contracts/main/strategies/morpho/StrategyMorphoBlue.sol:380"
                ],
                "recommendation": "Use an oracle interface that exposes updatedAt/roundId and enforce staleness checks and sane bounds; consider TWAP or a secondary oracle to cap deviations before using the price for deposits or TVL calculations.",
                "exploit_scenario": "If the Midas oracle can be manipulated or returns stale values, an attacker can deposit MF_ONE at an inflated price to mint excess shares, or force exchange price updates off a bad price. Those shares can later be redeemed for more USDC than deposited, draining value from other LPs."
            },
            {
                "title": "Rapid withdrawal refunds do not restore user limits",
                "category": "other",
                "evidence": "`refundWithdraw` deletes `_withdrawalRequestInstant` before calling `refundInstantLimit`, and `userTotalRedeem` only ever increases.",
                "severity": "low",
                "locations": [
                    "contracts/main/RedeemOperator.sol:297",
                    "contracts/main/RedeemOperator.sol:303",
                    "contracts/main/RedeemOperator.sol:305",
                    "contracts/main/RedeemOperator.sol:307",
                    "contracts/main/Vault.sol:63",
                    "contracts/main/Vault.sol:77"
                ],
                "recommendation": "Cache the requested share amount before deleting it and pass that value to `refundInstantLimit`; also decrement `userTotalRedeem` when a rapid withdrawal is canceled/refunded so limits reflect actual redeemed amounts.",
                "exploit_scenario": "When the operator refunds a rapid withdrawal, the refund path deletes the user\u2019s request before passing the amount back to the vault, so `refundInstantLimit` receives zero. Separately, `userTotalRedeem` is never decremented on refunds/cancellations. Users who are refunded can be permanently blocked from future rapid redemptions despite having their shares returned."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019be3d6-842f-702c-a179-72c42be44b63",
        "proxy_address": "0xc9ecc74110850168fc7373c2586e5a525b02b02f",
        "implementation_run_id": "019be3d8-dab2-7125-9844-e6c896367631",
        "implementation_address": "0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
2
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
2
Total opcodes
712
Flags
delegatecall_present
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: PUSH2 0x000c
00000008: PUSH2 0x000e
0000000b: JUMP
0000000c: JUMPDEST
0000000d: STOP
0000000e: JUMPDEST
0000000f: PUSH32 0x000000000000000000000000b7b274f034c135153febf7339d6e7772a0c18cfc
00000030: PUSH1 0x01
00000032: PUSH1 0x01
00000034: PUSH1 0xa0
00000036: SHL
00000037: SUB
00000038: AND
00000039: CALLER
0000003a: SUB
0000003b: PUSH2 0x007a
0000003e: JUMPI
0000003f: PUSH0
00000040: CALLDATALOAD
00000041: PUSH1 0x01
00000043: PUSH1 0x01
00000045: PUSH1 0xe0
00000047: SHL
00000048: SUB
00000049: NOT
0000004a: AND
0000004b: PUSH4 0x278f7943
00000050: PUSH1 0xe1
00000052: SHL
00000053: EQ
00000054: PUSH2 0x0070
00000057: JUMPI
00000058: PUSH1 0x40
0000005a: MLOAD
0000005b: PUSH4 0x34ad5dbb
00000060: PUSH1 0xe2
00000062: SHL
00000063: DUP2
00000064: MSTORE
00000065: PUSH1 0x04
00000067: ADD
00000068: PUSH1 0x40
0000006a: MLOAD
0000006b: DUP1
0000006c: SWAP2
0000006d: SUB
0000006e: SWAP1
0000006f: REVERT
00000070: JUMPDEST
00000071: PUSH2 0x0078
00000074: PUSH2 0x0082
00000077: JUMP
00000078: JUMPDEST
00000079: JUMP
0000007a: JUMPDEST
0000007b: PUSH2 0x0078
0000007e: PUSH2 0x00b0
00000081: JUMP
00000082: JUMPDEST
00000083: PUSH0
00000084: DUP1
00000085: PUSH2 0x0091
00000088: CALLDATASIZE
00000089: PUSH1 0x04
0000008b: DUP2
0000008c: DUP5
0000008d: PUSH2 0x0303
00000090: JUMP
00000091: JUMPDEST
00000092: DUP2
00000093: ADD
00000094: SWAP1
00000095: PUSH2 0x009e
00000098: SWAP2
00000099: SWAP1
0000009a: PUSH2 0x033e
0000009d: JUMP
0000009e: JUMPDEST
0000009f: SWAP2
000000a0: POP
000000a1: SWAP2
000000a2: POP
000000a3: PUSH2 0x00ac
000000a6: DUP3
000000a7: DUP3
000000a8: PUSH2 0x00c0
000000ab: JUMP
000000ac: JUMPDEST
000000ad: POP
000000ae: POP
000000af: JUMP
000000b0: JUMPDEST
000000b1: PUSH2 0x0078
000000b4: PUSH2 0x00bb
000000b7: PUSH2 0x011a
000000ba: JUMP
000000bb: JUMPDEST
000000bc: PUSH2 0x0151
000000bf: JUMP
000000c0: JUMPDEST
000000c1: PUSH2 0x00c9
000000c4: DUP3
000000c5: PUSH2 0x016f
000000c8: JUMP
000000c9: JUMPDEST
000000ca: PUSH1 0x40
000000cc: MLOAD
000000cd: PUSH1 0x01
000000cf: PUSH1 0x01
000000d1: PUSH1 0xa0
000000d3: SHL
000000d4: SUB
000000d5: DUP4
000000d6: AND
000000d7: SWAP1
000000d8: PUSH32 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b
000000f9: SWAP1
000000fa: PUSH0
000000fb: SWAP1
000000fc: LOG2
000000fd: DUP1
000000fe: MLOAD
000000ff: ISZERO
00000100: PUSH2 0x0112
00000103: JUMPI
00000104: PUSH2 0x010d
00000107: DUP3
00000108: DUP3
00000109: PUSH2 0x01ea
0000010c: JUMP
0000010d: JUMPDEST
0000010e: POP
0000010f: POP
00000110: POP
00000111: JUMP
00000112: JUMPDEST
00000113: PUSH2 0x00ac
00000116: PUSH2 0x025c
00000119: JUMP
0000011a: JUMPDEST
0000011b: PUSH0
0000011c: PUSH2 0x014c
0000011f: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
00000140: SLOAD
00000141: PUSH1 0x01
00000143: PUSH1 0x01
00000145: PUSH1 0xa0
00000147: SHL
00000148: SUB
00000149: AND
0000014a: SWAP1
0000014b: JUMP
0000014c: JUMPDEST
0000014d: SWAP1
0000014e: POP
0000014f: SWAP1
00000150: JUMP
00000151: JUMPDEST
00000152: CALLDATASIZE
00000153: PUSH0
00000154: DUP1
00000155: CALLDATACOPY
00000156: PUSH0
00000157: DUP1
00000158: CALLDATASIZE
00000159: PUSH0
0000015a: DUP5
0000015b: GAS
0000015c: DELEGATECALL
0000015d: RETURNDATASIZE
0000015e: PUSH0
0000015f: DUP1
00000160: RETURNDATACOPY
00000161: DUP1
00000162: DUP1
00000163: ISZERO
00000164: PUSH2 0x016b
00000167: JUMPI
00000168: RETURNDATASIZE
00000169: PUSH0
0000016a: RETURN
0000016b: JUMPDEST
0000016c: RETURNDATASIZE
0000016d: PUSH0
0000016e: REVERT
0000016f: JUMPDEST
00000170: DUP1
00000171: PUSH1 0x01
00000173: PUSH1 0x01
00000175: PUSH1 0xa0
00000177: SHL
00000178: SUB
00000179: AND
0000017a: EXTCODESIZE
0000017b: PUSH0
0000017c: SUB
0000017d: PUSH2 0x01a9
00000180: JUMPI
00000181: PUSH1 0x40
00000183: MLOAD
00000184: PUSH4 0x4c9c8ce3
00000189: PUSH1 0xe0
0000018b: SHL
0000018c: DUP2
0000018d: MSTORE
0000018e: PUSH1 0x01
00000190: PUSH1 0x01
00000192: PUSH1 0xa0
00000194: SHL
00000195: SUB
00000196: DUP3
00000197: AND
00000198: PUSH1 0x04
0000019a: DUP3
0000019b: ADD
0000019c: MSTORE
0000019d: PUSH1 0x24
0000019f: ADD
000001a0: JUMPDEST
000001a1: PUSH1 0x40
000001a3: MLOAD
000001a4: DUP1
000001a5: SWAP2
000001a6: SUB
000001a7: SWAP1
000001a8: REVERT
000001a9: JUMPDEST
000001aa: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
000001cb: DUP1
000001cc: SLOAD
000001cd: PUSH1 0x01
000001cf: PUSH1 0x01
000001d1: PUSH1 0xa0
000001d3: SHL
000001d4: SUB
000001d5: NOT
000001d6: AND
000001d7: PUSH1 0x01
000001d9: PUSH1 0x01
000001db: PUSH1 0xa0
000001dd: SHL
000001de: SUB
000001df: SWAP3
000001e0: SWAP1
000001e1: SWAP3
000001e2: AND
000001e3: SWAP2
000001e4: SWAP1
000001e5: SWAP2
000001e6: OR
000001e7: SWAP1
000001e8: SSTORE
000001e9: JUMP
000001ea: JUMPDEST
000001eb: PUSH1 0x60
000001ed: PUSH0
000001ee: DUP1
000001ef: DUP5
000001f0: PUSH1 0x01
000001f2: PUSH1 0x01
000001f4: PUSH1 0xa0
000001f6: SHL
000001f7: SUB
000001f8: AND
000001f9: DUP5
000001fa: PUSH1 0x40
000001fc: MLOAD
000001fd: PUSH2 0x0206
00000200: SWAP2
00000201: SWAP1
00000202: PUSH2 0x0407
00000205: JUMP
00000206: JUMPDEST
00000207: PUSH0
00000208: PUSH1 0x40
0000020a: MLOAD
0000020b: DUP1
0000020c: DUP4
0000020d: SUB
0000020e: DUP2
0000020f: DUP6
00000210: GAS
00000211: DELEGATECALL
00000212: SWAP2
00000213: POP
00000214: POP
00000215: RETURNDATASIZE
00000216: DUP1
00000217: PUSH0
00000218: DUP2
00000219: EQ
0000021a: PUSH2 0x023e
0000021d: JUMPI
0000021e: PUSH1 0x40
00000220: MLOAD
00000221: SWAP2
00000222: POP
00000223: PUSH1 0x1f
00000225: NOT
00000226: PUSH1 0x3f
00000228: RETURNDATASIZE
00000229: ADD
0000022a: AND
0000022b: DUP3
0000022c: ADD
0000022d: PUSH1 0x40
0000022f: MSTORE
00000230: RETURNDATASIZE
00000231: DUP3
00000232: MSTORE
00000233: RETURNDATASIZE
00000234: PUSH0
00000235: PUSH1 0x20
00000237: DUP5
00000238: ADD
00000239: RETURNDATACOPY
0000023a: PUSH2 0x0243
0000023d: JUMP
0000023e: JUMPDEST
0000023f: PUSH1 0x60
00000241: SWAP2
00000242: POP
00000243: JUMPDEST
00000244: POP
00000245: SWAP2
00000246: POP
00000247: SWAP2
00000248: POP
00000249: PUSH2 0x0253
0000024c: DUP6
0000024d: DUP4
0000024e: DUP4
0000024f: PUSH2 0x027b
00000252: JUMP
00000253: JUMPDEST
00000254: SWAP6
00000255: SWAP5
00000256: POP
00000257: POP
00000258: POP
00000259: POP
0000025a: POP
0000025b: JUMP
0000025c: JUMPDEST
0000025d: CALLVALUE
0000025e: ISZERO
0000025f: PUSH2 0x0078
00000262: JUMPI
00000263: PUSH1 0x40
00000265: MLOAD
00000266: PUSH4 0xb398979f
0000026b: PUSH1 0xe0
0000026d: SHL
0000026e: DUP2
0000026f: MSTORE
00000270: PUSH1 0x04
00000272: ADD
00000273: PUSH1 0x40
00000275: MLOAD
00000276: DUP1
00000277: SWAP2
00000278: SUB
00000279: SWAP1
0000027a: REVERT
0000027b: JUMPDEST
0000027c: PUSH1 0x60
0000027e: DUP3
0000027f: PUSH2 0x0290
00000282: JUMPI
00000283: PUSH2 0x028b
00000286: DUP3
00000287: PUSH2 0x02da
0000028a: JUMP
0000028b: JUMPDEST
0000028c: PUSH2 0x02d3
0000028f: JUMP
00000290: JUMPDEST
00000291: DUP2
00000292: MLOAD
00000293: ISZERO
00000294: DUP1
00000295: ISZERO
00000296: PUSH2 0x02a7
00000299: JUMPI
0000029a: POP
0000029b: PUSH1 0x01
0000029d: PUSH1 0x01
0000029f: PUSH1 0xa0
000002a1: SHL
000002a2: SUB
000002a3: DUP5
000002a4: AND
000002a5: EXTCODESIZE
000002a6: ISZERO
000002a7: JUMPDEST
000002a8: ISZERO
000002a9: PUSH2 0x02d0
000002ac: JUMPI
000002ad: PUSH1 0x40
000002af: MLOAD
000002b0: PUSH4 0x9996b315
000002b5: PUSH1 0xe0
000002b7: SHL
000002b8: DUP2
000002b9: MSTORE
000002ba: PUSH1 0x01
000002bc: PUSH1 0x01
000002be: PUSH1 0xa0
000002c0: SHL
000002c1: SUB
000002c2: DUP6
000002c3: AND
000002c4: PUSH1 0x04
000002c6: DUP3
000002c7: ADD
000002c8: MSTORE
000002c9: PUSH1 0x24
000002cb: ADD
000002cc: PUSH2 0x01a0
000002cf: JUMP
000002d0: JUMPDEST
000002d1: POP
000002d2: DUP1
000002d3: JUMPDEST
000002d4: SWAP4
000002d5: SWAP3
000002d6: POP
000002d7: POP
000002d8: POP
000002d9: JUMP
000002da: JUMPDEST
000002db: DUP1
000002dc: MLOAD
000002dd: ISZERO
000002de: PUSH2 0x02ea
000002e1: JUMPI
000002e2: DUP1
000002e3: MLOAD
000002e4: DUP1
000002e5: DUP3
000002e6: PUSH1 0x20
000002e8: ADD
000002e9: REVERT
000002ea: JUMPDEST
000002eb: PUSH1 0x40
000002ed: MLOAD
000002ee: PUSH4 0xd6bda275
000002f3: PUSH1 0xe0
000002f5: SHL
000002f6: DUP2
000002f7: MSTORE
000002f8: PUSH1 0x04
000002fa: ADD
000002fb: PUSH1 0x40
000002fd: MLOAD
000002fe: DUP1
000002ff: SWAP2
00000300: SUB
00000301: SWAP1
00000302: REVERT
00000303: JUMPDEST
00000304: PUSH0
00000305: DUP1
00000306: DUP6
00000307: DUP6
00000308: GT
00000309: ISZERO
0000030a: PUSH2 0x0311
0000030d: JUMPI
0000030e: PUSH0
0000030f: DUP1
00000310: REVERT
00000311: JUMPDEST
00000312: DUP4
00000313: DUP7
00000314: GT
00000315: ISZERO
00000316: PUSH2 0x031d
00000319: JUMPI
0000031a: PUSH0
0000031b: DUP1
0000031c: REVERT
0000031d: JUMPDEST
0000031e: POP
0000031f: POP
00000320: DUP3
00000321: ADD
00000322: SWAP4
00000323: SWAP2
00000324: SWAP1
00000325: SWAP3
00000326: SUB
00000327: SWAP2
00000328: POP
00000329: JUMP
0000032a: JUMPDEST
0000032b: PUSH4 0x4e487b71
00000330: PUSH1 0xe0
00000332: SHL
00000333: PUSH0
00000334: MSTORE
00000335: PUSH1 0x41
00000337: PUSH1 0x04
00000339: MSTORE
0000033a: PUSH1 0x24
0000033c: PUSH0
0000033d: REVERT
0000033e: JUMPDEST
0000033f: PUSH0
00000340: DUP1
00000341: PUSH1 0x40
00000343: DUP4
00000344: DUP6
00000345: SUB
00000346: SLT
00000347: ISZERO
00000348: PUSH2 0x034f
0000034b: JUMPI
0000034c: PUSH0
0000034d: DUP1
0000034e: REVERT
0000034f: JUMPDEST
00000350: DUP3
00000351: CALLDATALOAD
00000352: PUSH1 0x01
00000354: PUSH1 0x01
00000356: PUSH1 0xa0
00000358: SHL
00000359: SUB
0000035a: DUP2
0000035b: AND
0000035c: DUP2
0000035d: EQ
0000035e: PUSH2 0x0365
00000361: JUMPI
00000362: PUSH0
00000363: DUP1
00000364: REVERT
00000365: JUMPDEST
00000366: SWAP2
00000367: POP
00000368: PUSH1 0x20
0000036a: DUP4
0000036b: ADD
0000036c: CALLDATALOAD
0000036d: PUSH8 0xffffffffffffffff
00000376: DUP1
00000377: DUP3
00000378: GT
00000379: ISZERO
0000037a: PUSH2 0x0381
0000037d: JUMPI
0000037e: PUSH0
0000037f: DUP1
00000380: REVERT
00000381: JUMPDEST
00000382: DUP2
00000383: DUP6
00000384: ADD
00000385: SWAP2
00000386: POP
00000387: DUP6
00000388: PUSH1 0x1f
0000038a: DUP4
0000038b: ADD
0000038c: SLT
0000038d: PUSH2 0x0394
00000390: JUMPI
00000391: PUSH0
00000392: DUP1
00000393: REVERT
00000394: JUMPDEST
00000395: DUP2
00000396: CALLDATALOAD
00000397: DUP2
00000398: DUP2
00000399: GT
0000039a: ISZERO
0000039b: PUSH2 0x03a6
0000039e: JUMPI
0000039f: PUSH2 0x03a6
000003a2: PUSH2 0x032a
000003a5: JUMP
000003a6: JUMPDEST
000003a7: PUSH1 0x40
000003a9: MLOAD
000003aa: PUSH1 0x1f
000003ac: DUP3
000003ad: ADD
000003ae: PUSH1 0x1f
000003b0: NOT
000003b1: SWAP1
000003b2: DUP2
000003b3: AND
000003b4: PUSH1 0x3f
000003b6: ADD
000003b7: AND
000003b8: DUP2
000003b9: ADD
000003ba: SWAP1
000003bb: DUP4
000003bc: DUP3
000003bd: GT
000003be: DUP2
000003bf: DUP4
000003c0: LT
000003c1: OR
000003c2: ISZERO
000003c3: PUSH2 0x03ce
000003c6: JUMPI
000003c7: PUSH2 0x03ce
000003ca: PUSH2 0x032a
000003cd: JUMP
000003ce: JUMPDEST
000003cf: DUP2
000003d0: PUSH1 0x40
000003d2: MSTORE
000003d3: DUP3
000003d4: DUP2
000003d5: MSTORE
000003d6: DUP9
000003d7: PUSH1 0x20
000003d9: DUP5
000003da: DUP8
000003db: ADD
000003dc: ADD
000003dd: GT
000003de: ISZERO
000003df: PUSH2 0x03e6
000003e2: JUMPI
000003e3: PUSH0
000003e4: DUP1
000003e5: REVERT
000003e6: JUMPDEST
000003e7: DUP3
000003e8: PUSH1 0x20
000003ea: DUP7
000003eb: ADD
000003ec: PUSH1 0x20
000003ee: DUP4
000003ef: ADD
000003f0: CALLDATACOPY
000003f1: PUSH0
000003f2: PUSH1 0x20
000003f4: DUP5
000003f5: DUP4
000003f6: ADD
000003f7: ADD
000003f8: MSTORE
000003f9: DUP1
000003fa: SWAP6
000003fb: POP
000003fc: POP
000003fd: POP
000003fe: POP
000003ff: POP
00000400: POP
00000401: SWAP3
00000402: POP
00000403: SWAP3
00000404: SWAP1
00000405: POP
00000406: JUMP
00000407: JUMPDEST
00000408: PUSH0
00000409: DUP3
0000040a: MLOAD
0000040b: DUP1
0000040c: PUSH1 0x20
0000040e: DUP6
0000040f: ADD
00000410: DUP5
00000411: MCOPY
00000412: PUSH0
00000413: SWAP3
00000414: ADD
00000415: SWAP2
00000416: DUP3
00000417: MSTORE
00000418: POP
00000419: SWAP2
0000041a: SWAP1
0000041b: POP
0000041c: JUMP
0000041d: INVALID
0000041e: LOG2
0000041f: PUSH5 0x6970667358
00000425: UNKNOWN(0x22)
00000426: SLT
00000427: KECCAK256
00000428: UNKNOWN(0xEF)
00000429: DUP9
0000042a: DUP5
0000042b: PUSH19 0x210ab43c548aa9b0ac2bf227378edd2c212fae
0000043f: UNKNOWN(0xD7)
00000440: SWAP7
00000441: LOG4
00000442: UNKNOWN(0xB3)
00000443: UNKNOWN(0xB1)
00000444: UNKNOWN(0xAE)
00000445: UNKNOWN(0xE4)
00000446: DUP7
00000447: UNKNOWN(0xCB)
00000448: PUSH5 0x736f6c6343
0000044e: STOP
0000044f: ADDMOD
00000450: NOT
00000451: STOP
00000452: CALLER

Findings

validated = confirmed for call sinks; sink observed for others
Severity Category Tool Title Confidence Validated Confirmed Details
critical upgradeability detector Untrusted DELEGATECALL target reachable 70 no no
view
trace_id: 0x5e5b6ebddb7479b8119996f899c16450014a0e37f016bd82a5cf1db2e5640d68
call_targets: 0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3
target_varies: no
classification: constant_target
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "sink_reached",
    "attempts": 1,
    "trace_id": "0x5e5b6ebddb7479b8119996f899c16450014a0e37f016bd82a5cf1db2e5640d68",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": [
        "0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3"
    ],
    "matched_probe": null,
    "target_varies": false,
    "classification": "constant_target"
}
witness_json
{
    "notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
    "constraints": [],
    "entrypoints": [
        {
            "calldata": "0x278f7943",
            "selector": "0x278f7943",
            "calldata_variants": [
                "0x278f7943",
                "0x278f79430000000000000000000000000000000000000000000000000000000000000000"
            ]
        }
    ]
}
evidence_json
{
    "examples": [
        {
            "pc": 348,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        },
        {
            "pc": 529,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 2,
    "untrusted_delegatecall_count": 2
}
medium upgradeability cast DELEGATECALL present 80 no
view
evidence_json
{
    "notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
    "delegatecall_count": 2
}
medium oracle codex MF_ONE pricing relies on unbounded/stale oracle values, enabling share mispricing 75 no
view
evidence_json
{
    "evidence": "`IMidasOracle.lastAnswer()` is used directly to value MF_ONE for TVL and deposits, with no freshness/bounds checks.",
    "locations": [
        "contracts/main/Vault.sol:43",
        "contracts/main/Vault.sol:96",
        "contracts/main/strategies/morpho/StrategyMorphoBlue.sol:380"
    ],
    "recommendation": "Use an oracle interface that exposes updatedAt/roundId and enforce staleness checks and sane bounds; consider TWAP or a secondary oracle to cap deviations before using the price for deposits or TVL calculations.",
    "exploit_scenario": "If the Midas oracle can be manipulated or returns stale values, an attacker can deposit MF_ONE at an inflated price to mint excess shares, or force exchange price updates off a bad price. Those shares can later be redeemed for more USDC than deposited, draining value from other LPs."
}
low other codex Rapid withdrawal refunds do not restore user limits 65 no
view
evidence_json
{
    "evidence": "`refundWithdraw` deletes `_withdrawalRequestInstant` before calling `refundInstantLimit`, and `userTotalRedeem` only ever increases.",
    "locations": [
        "contracts/main/RedeemOperator.sol:297",
        "contracts/main/RedeemOperator.sol:303",
        "contracts/main/RedeemOperator.sol:305",
        "contracts/main/RedeemOperator.sol:307",
        "contracts/main/Vault.sol:63",
        "contracts/main/Vault.sol:77"
    ],
    "recommendation": "Cache the requested share amount before deleting it and pass that value to `refundInstantLimit`; also decrement `userTotalRedeem` when a rapid withdrawal is canceled/refunded so limits reflect actual redeemed amounts.",
    "exploit_scenario": "When the operator refunds a rapid withdrawal, the refund path deletes the user\u2019s request before passing the amount back to the vault, so `refundInstantLimit` receives zero. Separately, `userTotalRedeem` is never decremented on refunds/cancellations. Users who are refunded can be permanently blocked from future rapid redemptions despite having their shares returned."
}