Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RToken Oracle #849

Merged
merged 12 commits into from
Jun 29, 2023
19 changes: 19 additions & 0 deletions contracts/interfaces/IRTokenOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

// RToken Oracle Interface
interface IRTokenOracle {
struct CachedOracleData {
akshatmittal marked this conversation as resolved.
Show resolved Hide resolved
uint192 cachedPrice; // {UoA/tok}
uint256 cachedAtTime; // {s}
uint48 cachedAtNonce; // {basketNonce}
uint48 cachedTradesOpen;
uint256 cachedTradesNonce; // {tradeNonce}
}

// @returns rTokenPrice {D18} {UoA/rTok} The price of the RToken, in UoA
function latestPrice() external returns (uint192 rTokenPrice, uint256 updatedAt);

// Force recalculate the price of the RToken
function forceUpdatePrice() external;
}
9 changes: 2 additions & 7 deletions contracts/interfaces/ITrading.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,8 @@ interface ITrading is IComponent, IRewardableComponent {
/// @return The number of ongoing trades open
function tradesOpen() external view returns (uint48);

/// Light wrapper around FixLib.mulDiv to support try-catch
function mulDiv(
uint192 x,
uint192 y,
uint192 z,
RoundingMode rounding
) external pure returns (uint192);
/// @return The number of total trades ever opened
function tradesNonce() external view returns (uint256);
}

interface TestITrading is ITrading {
Expand Down
67 changes: 67 additions & 0 deletions contracts/libraries/Fixed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,73 @@ library FixLib {
return uint192(shiftDelta / FIX_ONE); // {D18} = {D36} / {D18}
}
}

/// Divide two fixes, rounding up to FIX_MAX and down to 0
/// @param a Numerator
/// @param b Denominator
function safeDiv(
uint192 a,
uint192 b,
RoundingMode rounding
) internal pure returns (uint192) {
if (a == 0) return 0;
if (b == 0) return FIX_MAX;

uint256 raw = _divrnd(FIX_ONE_256 * a, uint256(b), rounding);
if (raw >= FIX_MAX) return FIX_MAX;
return uint192(raw); // don't need _safeWrap
}

/// Multiplies two fixes and divide by a third
/// @param a First to multiply
/// @param b Second to multiply
/// @param c Denominator
function safeMulDiv(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other reviewers, this is the function that needs your eyes

Copy link
Contributor

@jankjr jankjr Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see safeMulDivonly diviates from mulDiv in the overflow case if (hi >= c) return FIX_MAX; - as is the intention.

I don't see any unintended behaviour introduced. LGTM

uint192 a,
uint192 b,
uint192 c,
RoundingMode rounding
) internal pure returns (uint192 result) {
if (a == 0 || b == 0) return 0;
if (a == FIX_MAX || b == FIX_MAX || c == 0) return FIX_MAX;

uint256 result_256;
unchecked {
(uint256 hi, uint256 lo) = fullMul(a, b);
if (hi >= c) return FIX_MAX;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line and L595 are the only 2 that feel a bit odd to me, but we already follow a similar pattern in safeMul, so i think this is correct to return FIX_MAX instead of erroring.

uint256 mm = mulmod(a, b, c);
if (mm > lo) hi -= 1;
lo -= mm;
uint256 pow2 = c & (0 - c);

uint256 c_256 = uint256(c);
// Warning: Should not access c below this line

c_256 /= pow2;
lo /= pow2;
lo += hi * ((0 - pow2) / pow2 + 1);
uint256 r = 1;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
r *= 2 - c_256 * r;
result_256 = lo * r;

// Apply rounding
if (rounding == CEIL) {
if (mm > 0) result_256 += 1;
} else if (rounding == ROUND) {
if (mm > ((c_256 - 1) / 2)) result_256 += 1;
}
}

if (result_256 >= FIX_MAX) return FIX_MAX;
return uint192(result_256);
}
}

// ================ a couple pure-uint helpers================
Expand Down
Loading