019be3d6-842f-702c-a179-72c42be44b63
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| 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 |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3"
}
},
"implementation": "0x71ea0eb2605bd63fe69012a60c75bdbd22e8b3d3",
"onchain_method": "eip1967"
}
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.
{"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"]}]}
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
{
"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
}
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
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewtrace_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 | — |
viewevidence_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 | — |
viewevidence_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 | — |
viewevidence_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."
}
|