From b7355c6ebc9ad8214fe2fc8302531f5dcbeafab5 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Fri, 27 Oct 2023 17:01:25 +0200 Subject: [PATCH] fix(oracle): use 512 bit multiplication --- src/ChainlinkOracle.sol | 8 +-- src/libraries/FullMath.sol | 107 +++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 src/libraries/FullMath.sol diff --git a/src/ChainlinkOracle.sol b/src/ChainlinkOracle.sol index 7ca4f5f..fc8e2b1 100644 --- a/src/ChainlinkOracle.sol +++ b/src/ChainlinkOracle.sol @@ -6,12 +6,14 @@ import {IOracle} from "../lib/morpho-blue/src/interfaces/IOracle.sol"; import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol"; import {IERC4626, VaultLib} from "./libraries/VaultLib.sol"; import {ErrorsLib} from "./libraries/ErrorsLib.sol"; +import {FullMath} from "./libraries/FullMath.sol"; /// @title ChainlinkOracle /// @author Morpho Labs /// @custom:contact security@morpho.org /// @notice Morpho Blue oracle using Chainlink-compliant feeds. contract ChainlinkOracle is IOracle { + using FullMath for uint256; using VaultLib for IERC4626; using ChainlinkDataFeedLib for AggregatorV3Interface; @@ -94,8 +96,8 @@ contract ChainlinkOracle is IOracle { /// @inheritdoc IOracle function price() external view returns (uint256) { - return ( - VAULT.getAssets(VAULT_CONVERSION_SAMPLE) * BASE_FEED_1.getPrice() * BASE_FEED_2.getPrice() * SCALE_FACTOR - ) / (QUOTE_FEED_1.getPrice() * QUOTE_FEED_2.getPrice()); + return (VAULT.getAssets(VAULT_CONVERSION_SAMPLE) * SCALE_FACTOR).mulDiv( + BASE_FEED_1.getPrice() * BASE_FEED_2.getPrice(), QUOTE_FEED_1.getPrice() * QUOTE_FEED_2.getPrice() + ); } } diff --git a/src/libraries/FullMath.sol b/src/libraries/FullMath.sol new file mode 100644 index 0000000..4a7366a --- /dev/null +++ b/src/libraries/FullMath.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title Contains 512-bit math functions +/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of +/// precision +/// @dev From https://github.com/Uniswap/v3-core/blob/0.8/contracts/libraries/FullMath.sol. +library FullMath { + /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or + /// denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv + function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + // 512-bit multiply [prod1 prod0] = a * b + // Compute the product mod 2**256 and mod 2**256 - 1 + // then use the Chinese Remainder Theorem to reconstruct + // the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2**256 + prod0 + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(a, b, not(0)) + prod0 := mul(a, b) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division + if (prod1 == 0) { + require(denominator > 0); + assembly { + result := div(prod0, denominator) + } + return result; + } + + // Make sure the result is less than 2**256. + // Also prevents denominator == 0 + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0] + // Compute remainder using mulmod + uint256 remainder; + assembly { + remainder := mulmod(a, b, denominator) + } + // Subtract 256 bit number from 512 bit number + assembly { + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator + // Compute largest power of two divisor of denominator. + // Always >= 1. + uint256 twos = (0 - denominator) & denominator; + // Divide denominator by power of two + assembly { + denominator := div(denominator, twos) + } + + // Divide [prod1 prod0] by the factors of two + assembly { + prod0 := div(prod0, twos) + } + // Shift in bits from prod1 into prod0. For this we need + // to flip `twos` such that it is 2**256 / twos. + // If twos is zero, then it becomes one + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + prod0 |= prod1 * twos; + + // Invert denominator mod 2**256 + // Now that denominator is an odd number, it has an inverse + // modulo 2**256 such that denominator * inv = 1 mod 2**256. + // Compute the inverse by starting with a seed that is correct + // correct for four bits. That is, denominator * inv = 1 mod 2**4 + uint256 inv = (3 * denominator) ^ 2; + // Now use Newton-Raphson iteration to improve the precision. + // Thanks to Hensel's lifting lemma, this also works in modular + // arithmetic, doubling the correct bits in each step. + inv *= 2 - denominator * inv; // inverse mod 2**8 + inv *= 2 - denominator * inv; // inverse mod 2**16 + inv *= 2 - denominator * inv; // inverse mod 2**32 + inv *= 2 - denominator * inv; // inverse mod 2**64 + inv *= 2 - denominator * inv; // inverse mod 2**128 + inv *= 2 - denominator * inv; // inverse mod 2**256 + + // Because the division is now exact we can divide by multiplying + // with the modular inverse of denominator. This will give us the + // correct result modulo 2**256. Since the precoditions guarantee + // that the outcome is less than 2**256, this is the final result. + // We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inv; + return result; + } + } +}