<!-- canonical: https://0xsimao.com/findings/morpho-midnight-midnight-parameter-bundles-constructor -->

# `midnight` parameter in `MidnightBundles` could be set in the constructor

Low/Info · Blackthorn · Lending · 6th April, 2026

Finding L-3 of the Morpho Midnight security review.

- Protocol: https://morpho.org/
- Report: /reports/morpho-midnight
- Codebase: https://github.com/0xsimao/midnight/tree/e9085f8c5fe96df6e075847b95b0dd7cea86110d
- Source: https://github.com/morpho-org/midnight/blob/main/audits/2026-07-02-blackthorn.pdf

---

## Summary

Every external function in `MidnightBundles` accepts `midnight` as a caller supplied parameter, which is not needed and could be set in the constructor instead for better phishing resistance.

## Vulnerability Detail

The bundler uses the supplied `midnight` for three operations in the same call:

1. The auth gate: `IMidnight(midnight).isAuthorized(taker, msg.sender)`.
2. Approval: `_forceApproveMax(token, midnight)`.
3. External dispatch: `IMidnight(midnight).take(...)` / `repay` / `supplyCollateral` / `withdrawCollateral`.

A phishing frontend, malicious SDK, or compromised wallet induces the victim to call the bundler with `midnight = EvilMidnight`, and allows token theft bypassing the checks above.

## Impact

Theft of all tokens pulled in a single call, bounded by the user's signed permit amount or approval.

## Code Snippet

https://github.com/sherlock-audit/2026-04-morpho-midnight-apr-6th-2026/blob/main/morpho-org__midnight/src/periphery/MidnightBundles.sol#L306-L326

For example:
```solidity
function repayAndWithdrawCollateral(
    address midnight,
    Market memory market,
    uint256 assets,
    address onBehalf,
    TokenPermit memory loanTokenPermit,
    ...
) external {
    require(onBehalf == msg.sender || IMidnight(midnight).isAuthorized(onBehalf, msg.sender), Unauthorized());
    ...
    _pullToken(loanToken, msg.sender, assets, loanTokenPermit);
    _forceApproveMax(loanToken, midnight);

    IMidnight(midnight).repay(market, units, onBehalf, address(0), "");
    ...
}
```

The same pattern repeats at lines [38](https://github.com/sherlock-audit/2026-04-morpho-midnight-apr-6th-2026/blob/main/morpho-org__midnight/src/periphery/MidnightBundles.sol#L38), [106](https://github.com/sherlock-audit/2026-04-morpho-midnight-apr-6th-2026/blob/main/morpho-org__midnight/src/periphery/MidnightBundles.sol#L106), [170](https://github.com/sherlock-audit/2026-04-morpho-midnight-apr-6th-2026/blob/main/morpho-org__midnight/src/periphery/MidnightBundles.sol#L170), and [243](https://github.com/sherlock-audit/2026-04-morpho-midnight-apr-6th-2026/blob/main/morpho-org__midnight/src/periphery/MidnightBundles.sol#L243).

## Tool Used

Manual Review

## Recommendation

Hardcode `midnight` as an `immutable` set in the constructor and remove the parameter from every external function. This mirrors how `EcrecoverRatifier` already pins its `MIDNIGHT`:

```solidity
address public immutable MIDNIGHT;

constructor(address midnight) {
    MIDNIGHT = midnight;
}
```

---

Related findings:

- [Deadline parameter in SyrupUserActions::_swapViaBalancer() could be set to minimize MEV](https://0xsimao.com/findings/maple-finance-syrup-balancer-swap-deadline): Maple Syrup
- [_setDefaultRoyalty() in the constructor is overriding the BasicRoyalties constructor](https://0xsimao.com/findings/metazero-i-default-royalty-overriding-royalties): MetaZero Omnichain NFTs
- [StakingOperator does not set isUnlockWindowActive to true in the constructor if the flag passed is true](https://0xsimao.com/findings/singularity-staking-window-active-flag): Singularity
