diff --git a/config/tokens.ts b/config/tokens.ts index 3b621022..eff67aae 100644 --- a/config/tokens.ts +++ b/config/tokens.ts @@ -36,6 +36,7 @@ type BaseTokenConfig = { oracleType?: string; dataStreamFeedId?: string; dataStreamFeedDecimals?: number; + dataStreamInverted?: boolean; dataStreamSpreadReductionFactor?: BigNumberish; priceFeed?: OraclePriceFeed; }; diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index 53845a67..2702eb0d 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -552,6 +552,7 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { allowedBaseKeys[Keys.BUYBACK_MAX_PRICE_IMPACT_FACTOR] = true; allowedBaseKeys[Keys.BUYBACK_MAX_PRICE_AGE] = true; + allowedBaseKeys[Keys.DATA_STREAM_INVERTED] = true; allowedBaseKeys[Keys.DATA_STREAM_SPREAD_REDUCTION_FACTOR] = true; } diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index 8a14a980..6ffd3197 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -332,6 +332,8 @@ library Keys { bytes32 public constant PRICE_FEED_HEARTBEAT_DURATION = keccak256(abi.encode("PRICE_FEED_HEARTBEAT_DURATION")); // @dev key for data stream feed id bytes32 public constant DATA_STREAM_ID = keccak256(abi.encode("DATA_STREAM_ID")); + // @dev key for data stream inverted flag (e.g. USD/JPY for JPY) + bytes32 public constant DATA_STREAM_INVERTED = keccak256(abi.encode("DATA_STREAM_INVERTED")); // @dev key for data stream feed multiplier bytes32 public constant DATA_STREAM_MULTIPLIER = keccak256(abi.encode("DATA_STREAM_MULTIPLIER")); bytes32 public constant DATA_STREAM_SPREAD_REDUCTION_FACTOR = keccak256(abi.encode("DATA_STREAM_SPREAD_REDUCTION_FACTOR")); @@ -1855,6 +1857,16 @@ library Keys { )); } + // @dev key for data stream inverted flag (e.g. USD/JPY for JPY) + // @param token the token to get the key for + // @return key for data stream inverted flag + function dataStreamInvertedKey(address token) internal pure returns (bytes32) { + return keccak256(abi.encode( + DATA_STREAM_INVERTED, + token + )); + } + // @dev key for data stream feed multiplier // @param token the token to get the key for // @return key for data stream feed multiplier diff --git a/contracts/oracle/ChainlinkDataStreamProvider.sol b/contracts/oracle/ChainlinkDataStreamProvider.sol index d74a7ff5..8d564431 100644 --- a/contracts/oracle/ChainlinkDataStreamProvider.sol +++ b/contracts/oracle/ChainlinkDataStreamProvider.sol @@ -77,6 +77,13 @@ contract ChainlinkDataStreamProvider is IOracleProvider { uint256 adjustedBidPrice = Precision.mulDiv(uint256(uint192(report.bid)), precision, Precision.FLOAT_PRECISION); uint256 adjustedAskPrice = Precision.mulDiv(uint256(uint192(report.ask)), precision, Precision.FLOAT_PRECISION); + bool inverted = dataStore.getBool(Keys.dataStreamInvertedKey(token)); + if (inverted) { + uint256 temp = adjustedBidPrice; + adjustedBidPrice = Precision.mulDiv(Precision.FLOAT_PRECISION, Precision.FLOAT_PRECISION, adjustedAskPrice); + adjustedAskPrice = Precision.mulDiv(Precision.FLOAT_PRECISION, Precision.FLOAT_PRECISION, temp); + } + uint256 spreadReductionFactor = _getDataStreamSpreadReductionFactor(token); if (spreadReductionFactor != 0) { // small optimization for full reduction diff --git a/deploy/configureDataStreamFeeds.ts b/deploy/configureDataStreamFeeds.ts index 3328a336..f99bc209 100644 --- a/deploy/configureDataStreamFeeds.ts +++ b/deploy/configureDataStreamFeeds.ts @@ -2,7 +2,7 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { TokenConfig } from "../config/tokens"; import * as keys from "../utils/keys"; -import { setBytes32IfDifferent, setUintIfDifferent } from "../utils/dataStore"; +import { setBoolIfDifferent, setBytes32IfDifferent, setUintIfDifferent } from "../utils/dataStore"; import { expandDecimals } from "../utils/math"; const func = async ({ gmx }: HardhatRuntimeEnvironment) => { @@ -32,6 +32,14 @@ const func = async ({ gmx }: HardhatRuntimeEnvironment) => { `data stream feed id for ${tokenSymbol} ${token.address}` ); + if (token.dataStreamInverted) { + await setBoolIfDifferent( + keys.dataStreamInvertedKey(token.address), + token.dataStreamInverted, + `data stream feed inverted flag for ${tokenSymbol} ${token.address}` + ); + } + const dataStreamMultiplier = expandDecimals(1, 60 - token.decimals - token.dataStreamFeedDecimals); await setUintIfDifferent( keys.dataStreamMultiplierKey(token.address), diff --git a/utils/keys.ts b/utils/keys.ts index 4757fd20..05832474 100644 --- a/utils/keys.ts +++ b/utils/keys.ts @@ -118,6 +118,7 @@ export const PRICE_FEED = hashString("PRICE_FEED"); export const PRICE_FEED_MULTIPLIER = hashString("PRICE_FEED_MULTIPLIER"); export const PRICE_FEED_HEARTBEAT_DURATION = hashString("PRICE_FEED_HEARTBEAT_DURATION"); export const DATA_STREAM_ID = hashString("DATA_STREAM_ID"); +export const DATA_STREAM_INVERTED = hashString("DATA_STREAM_INVERTED"); export const DATA_STREAM_MULTIPLIER = hashString("DATA_STREAM_MULTIPLIER"); export const DATA_STREAM_SPREAD_REDUCTION_FACTOR = hashString("DATA_STREAM_SPREAD_REDUCTION_FACTOR"); export const STABLE_PRICE = hashString("STABLE_PRICE"); @@ -402,6 +403,10 @@ export function dataStreamIdKey(token: string) { return hashData(["bytes32", "address"], [DATA_STREAM_ID, token]); } +export function dataStreamInvertedKey(token: string) { + return hashData(["bytes32", "address"], [DATA_STREAM_INVERTED, token]); +} + export function dataStreamMultiplierKey(token: string) { return hashData(["bytes32", "address"], [DATA_STREAM_MULTIPLIER, token]); }