> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.steakhouse.financial/docs/api/mcp` to find what you need.

# MetaOracle (Deviation Timelock)

The `MetaOracleDeviationTimelock` is a safety wrapper that selects between a primary and backup oracle. It switches to the backup when prices diverge beyond a threshold for a sustained challenge period, and switches back to the primary after prices reconverge for a sustained healing period. The contract implements Morpho’s `IOracle` interface, so downstream systems can treat it like any other oracle.

### Deployments

Use the network-specific factory addresses to deploy new MetaOracleDeviationTimelock instances.

| Chain    | MetaOracle factory                                                                                                                  |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Ethereum | [`0x44d049eEd4AD33807859C45bBd3a8eb47917a9F4`](https://etherscan.io/address/0x44d049eEd4AD33807859C45bBd3a8eb47917a9F4)             |
| Base     | [`0x507f940234005f7c49dfE2F27E361C3b66Fb31CF`](https://basescan.org/address/0x507f940234005f7c49dfE2F27E361C3b66Fb31CF)             |
| Arbitrum | [`0x4f3F56A045a2D33ceEf1D1fD5F4C776b8bFb2168`](https://arbiscan.io/address/0x4f3F56A045a2D33ceEf1D1fD5F4C776b8bFb2168)              |
| Katana   | [`0x53A30343c8A51d685d17c1c40AAe8e76D2B79b68`](https://katanascan.com/address/0x53A30343c8A51d685d17c1c40AAe8e76D2B79b68)           |
| Polygon  | [`0xd058Fc46edd745B6c883Ef3F775669039235753d`](https://polygonscan.com/address/0xd058Fc46edd745B6c883Ef3F775669039235753d)          |
| Monad    | [`0x1C1FD6dc5D84C16cD152aC2E91F80327FE3aEd9F`](https://monadvision.com/address/0x1C1FD6dc5D84C16cD152aC2E91F80327FE3aEd9F)          |
| Unichain | [`0xf1BaEC963675A367E3ADe06A531B6407D78ab2A3`](https://uniscan.xyz/address/0xf1BaEC963675A367E3ADe06A531B6407D78ab2A3)              |
| HyperEVM | [`0x9fAE9968e4e68bEE5ddcb48bb68Fb27CC57ff384`](https://explorer.hyperliquid.xyz/address/0x9fAE9968e4e68bEE5ddcb48bb68Fb27CC57ff384) |

Explorer links reflect current public explorers; swap in preferred explorers if needed.

### Audit and source

* MetaOracleDeviationTimelock source: [MetaOracleDeviationTimelock.sol](https://github.com/Steakhouse-Financial/steakhouse-oracles/blob/36385342274f71999b66c79c4475234a39ba9a01/src/MetaOracleDeviationTimelock.sol)
* Factory source: [MetaOracleDeviationTimelockFactory.sol](https://github.com/Steakhouse-Financial/steakhouse-oracles/blob/36385342274f71999b66c79c4475234a39ba9a01/src/MetaOracleDeviationTimelockFactory.sol)
* Audit report (Cantina): [MetaOracleDeviationTimelock audit report](https://github.com/Steakhouse-Financial/steakhouse-oracles/blob/5ce0e80adb69bc06f5b390032150e7ea41d30c02/audits/2025-06-23-metaoracledeviationtimelock-cantina-managed-review.pdf)

### Core behavior

* The contract starts with the primary oracle selected after initialization.
* `price()` reflects whichever oracle is active (primary or backup).
* Deviation is computed as `abs(primary - backup) * 1e18 / average(primary, backup)`.
* A deviation can only trigger a switch after a timelocked challenge; reconvergence can only trigger a switch back after a timelocked healing.

### Flow diagram

**Primary → backup (challenge)**

```mermaid
%%{init: {"theme":"base","themeVariables":{"background":"#fffdf1","primaryColor":"#ece7e1","primaryTextColor":"#1a1e20","primaryBorderColor":"#dedcd2","lineColor":"#515a5e","mainBkg":"#ece7e1","nodeBorder":"#dedcd2","clusterBkg":"#fffdf1","titleColor":"#1a1e20","edgeLabelBackground":"#fffdf1","arrowheadColor":"#086552","fontFamily":"Söhne, ui-sans-serif, system-ui, sans-serif","fontSize":"13px"},"flowchart":{"nodeSpacing":20,"rankSpacing":30}}}%%
flowchart LR
    Primary["Primary active"] --> Deviate{"Deviation > threshold?"}
    Deviate -- "No" --> Primary
    Deviate -- "Yes" --> Challenge["challenge() starts timelock"]
    Challenge --> ChallengeEnd{"After challengeTimelockDuration"}
    ChallengeEnd -- "Deviation cleared" --> Primary
    ChallengeEnd -- "Still deviant" --> Backup["acceptChallenge() -> backup"]



```

**Backup → primary (healing)**

```mermaid
%%{init: {"theme":"base","themeVariables":{"background":"#fffdf1","primaryColor":"#ece7e1","primaryTextColor":"#1a1e20","primaryBorderColor":"#dedcd2","lineColor":"#515a5e","mainBkg":"#ece7e1","nodeBorder":"#dedcd2","clusterBkg":"#fffdf1","titleColor":"#1a1e20","edgeLabelBackground":"#fffdf1","arrowheadColor":"#086552","fontFamily":"Söhne, ui-sans-serif, system-ui, sans-serif","fontSize":"13px"},"flowchart":{"nodeSpacing":20,"rankSpacing":30}}}%%
flowchart LR
    Backup["Backup active"] --> Reconverge{"Prices reconverged?"}
    Reconverge -- "No" --> Backup
    Reconverge -- "Yes" --> Heal["heal() starts timelock"]
    Heal --> HealEnd{"After healingTimelockDuration"}
    HealEnd -- "Deviation returns" --> Backup
    HealEnd -- "Still converged" --> Primary["acceptHealing() -> primary"]
```

### Parameters

* `primaryOracle`: preferred oracle used in normal conditions.
* `backupOracle`: fallback oracle used during deviation periods.
* `deviationThreshold`: maximum allowed relative deviation, scaled by `1e18` (e.g., `0.01e18` for 1%).
* `challengeTimelockDuration`: seconds a deviation must persist before switching to backup.
* `healingTimelockDuration`: seconds prices must remain reconverged before switching back to primary.

Initialization rejects zero addresses, equal oracle addresses, non-positive thresholds, and any initial deviation above `deviationThreshold`.

### Challenge flow (primary → backup)

1. Anyone calls `challenge()` when the primary is active and prices are deviant.
2. The contract starts a challenge timelock (`challengeExpiresAt`).
3. If prices reconverge before expiry, anyone can call `revokeChallenge()`.
4. After expiry, anyone can call `acceptChallenge()` while deviation still holds to switch to the backup.

### Healing flow (backup → primary)

1. Anyone calls `heal()` when the backup is active and prices have reconverged.
2. The contract starts a healing timelock (`healingExpiresAt`).
3. If prices deviate again before expiry, anyone can call `revokeHealing()`.
4. After expiry, anyone can call `acceptHealing()` while prices are still converged to switch back to the primary.

### Example: XAUT/USDT market

**Setup**

* Primary oracle: XAUT priced from the XAU (gold) reference feed, USDT fixed at `1`.
* Backup oracle: XAUT/USDT market TWAP (both legs from market pricing).

**Flow**

1. As long as the gold reference and the XAUT market price are close, the MetaOracle uses the primary oracle.
2. If XAUT trades away from the gold reference (or USDT drifts), the deviation exceeds the threshold and `challenge()` starts the timelock.
3. If the deviation persists through the challenge timelock, `acceptChallenge()` switches pricing to the market TWAP backup.
4. Once the market price reconverges with the gold reference for the healing timelock, `acceptHealing()` switches back to the primary.

```mermaid
%%{init: {"theme":"base","themeVariables":{"background":"#fffdf1","primaryColor":"#ece7e1","primaryTextColor":"#1a1e20","primaryBorderColor":"#dedcd2","secondaryColor":"#ece7e1","tertiaryColor":"#fffdf1","lineColor":"#515a5e","textColor":"#1a1e20","mainBkg":"#ece7e1","nodeBorder":"#dedcd2","clusterBkg":"#fffdf1","titleColor":"#1a1e20","edgeLabelBackground":"#fffdf1","arrowheadColor":"#086552","fontFamily":"Söhne, ui-sans-serif, system-ui, sans-serif"}}}%%
sequenceDiagram
    participant User as Any caller
    participant Meta as MetaOracle
    participant Primary as "Primary (XAU ref, USDT=1)"
    participant Backup as "Backup (XAUT/USDT TWAP)"

    Note over Meta: Starts on primary
    Meta->>Primary: price()
    User->>Meta: challenge() when deviation > threshold
    Note over Meta: Wait challengeTimelockDuration
    User->>Meta: acceptChallenge() if still deviant
    Note over Meta: Switches to backup
    Meta->>Backup: price()
    User->>Meta: heal() when prices reconverge
    Note over Meta: Wait healingTimelockDuration
    User->>Meta: acceptHealing() if still converged
    Note over Meta: Switches back to primary
    Meta->>Primary: price()

```

### Factory

MetaOracleDeviationTimelock instances are deployed by `MetaOracleDeviationTimelockFactory` using EIP-1167 clones. Each deployment emits `MetaOracleDeployed` with the proxy address, implementation, oracle pair, threshold, and timelock durations.
