// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {IAmishOracle} from "../interfaces/IAmishOracle.sol"; /// @title OracleLib /// @notice Shared reader for `IAmishOracle` adapters: fetches the collateral price, rejects a zero /// price and a feed older than `maxStaleness`, and returns the adapter's fixed price decimals /// alongside it. Used at loan creation (opening-health check) and again on the principal chain /// when health is recomputed for margin call / liquidation, so the read lives in one place. library OracleLib { error OracleLib_InvalidPrice(); error OracleLib_StalePrice(); /// @notice Read and validate the price of `collateralToken` in `principalToken` from `oracle`. /// @param maxStaleness Maximum age (seconds) of the feed's last update relative to `block.timestamp`. /// @return price Whole-collateral price in whole-principal units at `10 ** priceDecimals` scale. /// @return priceDecimals The adapter's fixed price decimals. function readPrice(address oracle, address collateralToken, address principalToken, uint32 maxStaleness) internal view returns (uint256 price, uint8 priceDecimals) { uint256 updatedAt; (price, updatedAt) = IAmishOracle(oracle).getPrice(collateralToken, principalToken); if (price == 0) revert OracleLib_InvalidPrice(); if (block.timestamp > updatedAt + maxStaleness) revert OracleLib_StalePrice(); priceDecimals = IAmishOracle(oracle).decimals(); } /// @notice Best-effort variant of {readPrice}: reports failure instead of reverting when the /// adapter reverts, the price is zero, or the feed is stale. For callers that only /// opportunistically refresh state off the price (e.g. clearing a cured margin call /// during a repayment) and must never let a dead feed block their primary action. /// @return ok False if no valid, fresh price could be read; `price`/`priceDecimals` are zero then. function tryReadPrice(address oracle, address collateralToken, address principalToken, uint32 maxStaleness) internal view returns (bool ok, uint256 price, uint8 priceDecimals) { try IAmishOracle(oracle).getPrice(collateralToken, principalToken) returns (uint256 p, uint256 updatedAt) { if (p == 0 || block.timestamp > updatedAt + maxStaleness) return (false, 0, 0); price = p; } catch { return (false, 0, 0); } try IAmishOracle(oracle).decimals() returns (uint8 d) { return (true, price, d); } catch { return (false, 0, 0); } } }