# Morpho Markets

The `ChainlinkOEVMorphoWrapper` is used for liquidations in Morpho Blue isolated lending markets. Unlike the core market wrapper, this variant receives **underlying tokens** (not mTokens) as collateral and supports slippage protection via `maxRepayAmount`.

## Key Differences from Core Markets

| Aspect                | Core (ChainlinkOEVWrapper) | Morpho (ChainlinkOEVMorphoWrapper)        |
| --------------------- | -------------------------- | ----------------------------------------- |
| Market type           | Moonwell Core (mToken)     | Morpho Blue isolated                      |
| Collateral received   | mTokens                    | Underlying tokens                         |
| Market identification | mToken addresses           | `MarketParams` struct                     |
| Slippage protection   | Fixed repay amount         | `maxRepayAmount` parameter                |
| Excess return         | N/A                        | Unused loan tokens returned automatically |

## Function Signature

```solidity
function updatePriceEarlyAndLiquidate(
    MarketParams memory marketParams,
    address borrower,
    uint256 seizedAssets,
    uint256 maxRepayAmount
) external
```

| Parameter        | Type           | Description                                                |
| ---------------- | -------------- | ---------------------------------------------------------- |
| `marketParams`   | `MarketParams` | Morpho market identification struct                        |
| `borrower`       | `address`      | The underwater borrower to liquidate                       |
| `seizedAssets`   | `uint256`      | Amount of collateral to seize                              |
| `maxRepayAmount` | `uint256`      | Maximum loan tokens willing to repay (slippage protection) |

### MarketParams Struct

```solidity
struct MarketParams {
    address loanToken;       // Token being borrowed (e.g. USDC)
    address collateralToken; // Token used as collateral (e.g. xWELL)
    address oracle;          // Must use this OEV wrapper as BASE_FEED_1
    address irm;             // Interest rate model
    uint256 lltv;            // Liquidation loan-to-value ratio
}
```

## Liquidation Flow

```
┌─────────────────┐     ┌────────────────────────────┐     ┌─────────────────┐
│   Liquidator    │────▶│  ChainlinkOEVMorphoWrapper │────▶│   Morpho Blue   │
└─────────────────┘     └────────────────────────────┘     └─────────────────┘
        │                           │                              │
        │  1. Transfer max loan     │                              │
        │     tokens                │                              │
        │──────────────────────────▶│                              │
        │                           │  2. Update cachedRoundId     │
        │                           │                              │
        │                           │  3. Approve & call liquidate │
        │                           │─────────────────────────────▶│
        │                           │                              │
        │                           │  4. Receive collateral       │
        │                           │     tokens                   │
        │                           │◀─────────────────────────────│
        │                           │                              │
        │  5. Return excess loan    │                              │
        │     tokens                │                              │
        │◀──────────────────────────│                              │
        │                           │                              │
        │  6. Receive collateral    │  7. Send protocol fee        │
        │     (repay + bonus)       │     to feeRecipient          │
        │◀──────────────────────────│                              │
```

## Solidity Example

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}

interface IChainlinkOEVMorphoWrapper {
    function updatePriceEarlyAndLiquidate(
        MarketParams memory marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 maxRepayAmount
    ) external;
}

contract MorphoOEVLiquidator {
    IChainlinkOEVMorphoWrapper public oevWrapper;

    constructor(address _oevWrapper) {
        oevWrapper = IChainlinkOEVMorphoWrapper(_oevWrapper);
    }

    function liquidate(
        MarketParams calldata marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 maxRepayAmount
    ) external {
        address loanToken = marketParams.loanToken;

        // 1. Transfer max loan tokens from caller
        IERC20(loanToken).transferFrom(msg.sender, address(this), maxRepayAmount);

        // 2. Approve OEV wrapper
        IERC20(loanToken).approve(address(oevWrapper), maxRepayAmount);

        // 3. Execute liquidation - excess tokens returned automatically
        oevWrapper.updatePriceEarlyAndLiquidate(
            marketParams,
            borrower,
            seizedAssets,
            maxRepayAmount
        );

        // 4. Collateral tokens (not mTokens) are now in this contract
        uint256 collateralBalance = IERC20(marketParams.collateralToken).balanceOf(
            address(this)
        );
        IERC20(marketParams.collateralToken).transfer(msg.sender, collateralBalance);

        // 5. Return any remaining loan tokens
        uint256 loanBalance = IERC20(loanToken).balanceOf(address(this));
        if (loanBalance > 0) {
            IERC20(loanToken).transfer(msg.sender, loanBalance);
        }
    }
}
```

{% hint style="warning" %}
The market's Morpho oracle must have this OEV wrapper as `BASE_FEED_1`. The contract verifies this requirement and will revert if the oracle is not configured correctly.
{% endhint %}

## Revert Conditions

| Condition                  | Error Message                                                             |
| -------------------------- | ------------------------------------------------------------------------- |
| `borrower == address(0)`   | `"ChainlinkOEVMorphoWrapper: borrower cannot be zero address"`            |
| `seizedAssets == 0`        | `"ChainlinkOEVMorphoWrapper: seized assets cannot be zero"`               |
| `maxRepayAmount == 0`      | `"ChainlinkOEVMorphoWrapper: max repay amount cannot be zero"`            |
| Wrong oracle `BASE_FEED_1` | `"ChainlinkOEVMorphoWrapper: oracle must be the same as the base feed 1"` |
| Chainlink price ≤ 0        | `"Chainlink price cannot be lower or equal to 0"`                         |
| Incomplete round           | `"Round is in incompleted state"`                                         |
| Stale Chainlink data       | `"Stale price"`                                                           |
| Actual repay > max         | `"ChainlinkOEVMorphoWrapper: repaid amount exceeds maximum"`              |
| Invalid loan token price   | `"ChainlinkOEVMorphoWrapper: invalid loan token price"`                   |

## Events

| Event                            | Parameters                                                                                                         |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `PriceUpdatedEarlyAndLiquidated` | `address indexed borrower, uint256 seizedAssets, uint256 repaidAssets, uint256 protocolFee, uint256 liquidatorFee` |
| `FeeRecipientChanged`            | `address oldFeeRecipient, address newFeeRecipient`                                                                 |
| `LiquidatorFeeBpsChanged`        | `uint16 oldLiquidatorFeeBps, uint16 newLiquidatorFeeBps`                                                           |
| `MaxRoundDelayChanged`           | `uint256 oldMaxRoundDelay, uint256 newMaxRoundDelay`                                                               |
| `MaxDecrementsChanged`           | `uint256 oldMaxDecrements, uint256 newMaxDecrements`                                                               |

{% hint style="info" %}
Note: The Morpho wrapper's `PriceUpdatedEarlyAndLiquidated` event has **different parameters** than the core wrapper - it uses `seizedAssets` and `repaidAssets` instead of `repayAmount`, `mTokenCollateral`, and `mTokenLoan`.
{% endhint %}

## Deployed Contracts

### Base - ChainlinkOEVMorphoWrapper

| Asset    | Address                                                                                                                 |
| -------- | ----------------------------------------------------------------------------------------------------------------------- |
| WELL/USD | [`0xAEeE6335f50e1f8aF924DF0742b1879C9761F5f5`](https://basescan.org/address/0xAEeE6335f50e1f8aF924DF0742b1879C9761F5f5) |

#### WELL/USDC Morpho Market Parameters (Base)

```solidity
MarketParams memory params = MarketParams({
    loanToken: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913,       // USDC
    collateralToken: 0xA88594D404727625A9437C3f886C7643872296AE, // xWELL
    oracle: 0x71FBaD6c2200C8A5B89380f9B6bb8a35d411c852,          // Morpho Chainlink Oracle
    irm: 0x46415998764C29aB2a25CbeA6254146D50D22687,             // Adaptive Curve IRM
    lltv: 625000000000000000                                     // 62.5%
});
```
