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

feat: add pricefeed base for redstone adapter #114

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions packages/contracts/contracts/BOB/IRedstoneAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

/**
* @title Interface of RedStone adapter
* @author The Redstone Oracles team
*/
interface IRedstoneAdapter {

/**
* @notice Returns the latest properly reported value of the data feed
* @return value The latest value of the given data feed
*/
function getValueForDataFeed(bytes32) external view returns (uint256);

/**
* @notice Returns details for the given round and data feed
* @return dataFeedValue
* @return roundDataTimestamp
* @return roundBlockTimestamp
*/
function getRoundDataFromAdapter(bytes32, uint256) external view returns (uint256 dataFeedValue, uint128 roundDataTimestamp, uint128 roundBlockTimestamp);

/**
* @notice Returns the latest properly reported round id
* @return value The latest value of the round id
*/
function getLatestRoundId() external view returns (uint256);

/**
* @notice Returns block timestamp of the latest successful update
* @return blockTimestamp The block timestamp of the latest successful update
*/
function getBlockTimestampFromLatestUpdate() external view returns (uint256 blockTimestamp);
}
90 changes: 90 additions & 0 deletions packages/contracts/contracts/BOB/MockRedstoneAdapterBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./IRedstoneAdapter.sol";

contract MockRedstoneAdapterBase is IRedstoneAdapter {
uint256 private latestPrice;
uint256 private previousPrice;
uint256 private latestRoundId;
uint256 private blockTimestampFromLatestUpdate;
uint128 private blockTimestampFromPreviousUpdate;

bool latestRevert;
bool previousRevert;

// --- Setter Functions ---

function setPrice(uint256 _price) external {
latestPrice = _price;
}

function setPrevPrice(uint256 _price) external {
previousPrice = _price;
}

function setUpdateTime(uint256 _blockTimestampFromLatestUpdate) external {
blockTimestampFromLatestUpdate = _blockTimestampFromLatestUpdate;
}

function setPrevTime(uint128 _blockTimestampFromPreviousUpdate) external {
blockTimestampFromPreviousUpdate = _blockTimestampFromPreviousUpdate;
}

function setLatestRoundId(uint256 _latestRoundId) external {
latestRoundId = _latestRoundId;
}

function setLatestRevert() external {
latestRevert = !latestRevert;
}

function setPrevRevert() external {
previousRevert = !previousRevert;
}

// --- Getters that adhere to the RedstoneAdapter interface ---

/**
* @notice Returns the latest properly reported value of the data feed
* @return value The latest value of the given data feed
*/
function getValueForDataFeed(bytes32) public view returns (uint256) {
if (latestRevert) {require( 1== 0, "getRoundData reverted");}
return latestPrice;
}

/**
* @notice Returns details for the given round and data feed
* @return dataFeedValue
* @return roundDataTimestamp
* @return roundBlockTimestamp
*/
function getRoundDataFromAdapter(bytes32, uint256) public view returns (
uint256 dataFeedValue,
uint128 roundDataTimestamp,
uint128 roundBlockTimestamp
) {
if (previousRevert) {require( 1== 0, "getRoundData reverted");}
dataFeedValue = previousPrice;
roundDataTimestamp = blockTimestampFromPreviousUpdate;
roundBlockTimestamp = blockTimestampFromPreviousUpdate;
}

/**
* @notice Returns block timestamp of the latest successful update
* @return blockTimestamp The block timestamp of the latest successful update
*/
function getBlockTimestampFromLatestUpdate() public view returns (uint256) {
return blockTimestampFromLatestUpdate;
}

/**
* @notice Returns latest successful round number
* @return latestRoundId
*/
function getLatestRoundId() public view returns (uint256) {
return latestRoundId;
}
}
161 changes: 161 additions & 0 deletions packages/contracts/contracts/BOB/PriceFeedBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./IRedstoneAdapter.sol";
import "../Dependencies/CheckContract.sol";
import "../Dependencies/AggregatorV3Interface.sol";

/**
* @title Main logic of the price feed contract
* @author The Redstone Oracles team
* @dev Implementation of common functions for the PriceFeed contract
* that queries data from the specified PriceFeedAdapter
*
* It can be used by projects that have already implemented with Chainlink-like
* price feeds and would like to minimise changes in their existing codebase.
*
* If you are flexible, it's much better (and cheaper in terms of gas) to query
* the PriceFeedAdapter contract directly
*/
contract PriceFeedBase is AggregatorV3Interface, CheckContract {
uint256 internal constant UINT80_MAX = uint256(type(uint80).max);
uint256 internal constant INT256_MAX = uint256(type(int256).max);

IRedstoneAdapter public immutable redstoneAdapter;
bytes32 public immutable dataFeedId;

error UnsafeUintToIntConversion(uint256 value);
error UnsafeUint256ToUint80Conversion(uint256 value);

constructor (address _redstoneAdapterAddress, bytes32 _dataFeedId) {
checkContract(_redstoneAdapterAddress);

redstoneAdapter = IRedstoneAdapter(_redstoneAdapterAddress);
dataFeedId = _dataFeedId;
}

/**
* @notice Returns the number of decimals for the price feed
* @dev By default, RedStone uses 8 decimals for data feeds
* @return decimals The number of decimals in the price feed values
*/
function decimals() public virtual pure override returns (uint8) {
return 8;
}

/**
* @notice Description of the Price Feed
* @return description
*/
function description() public view virtual override returns (string memory) {
return "Redstone Price Feed";
}

/**
* @notice Version of the Price Feed
* @dev Currently it has no specific motivation and was added
* only to be compatible with the Chainlink interface
* @return version
*/
function version() public virtual pure override returns (uint256) {
return 1;
}

/**
* @notice Returns details for the given round
* @param _requestedRoundId Requested round identifier
*/
function getRoundData(uint80 _requestedRoundId) public view virtual returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
) {
(
uint256 dataFeedValue,
uint128 roundDataTimestamp,
uint128 roundBlockTimestamp
) = redstoneAdapter.getRoundDataFromAdapter(dataFeedId, _requestedRoundId);

roundId = _requestedRoundId;

if (dataFeedValue > INT256_MAX) {
revert UnsafeUintToIntConversion(dataFeedValue);
}

answer = int256(dataFeedValue);
startedAt = roundDataTimestamp;
updatedAt = roundBlockTimestamp;

// We want to be compatible with Chainlink's interface
// And in our case the roundId is always equal to answeredInRound
answeredInRound = _requestedRoundId;
}

/**
* @notice Returns details of the latest successful update round
* @dev It uses few helpful functions to abstract logic of getting
* latest round id and value
* @return roundId The number of the latest round
* @return answer The latest reported value
* @return startedAt Block timestamp when the latest successful round started
* @return updatedAt Block timestamp of the latest successful round
* @return answeredInRound The number of the latest round
*/
function latestRoundData()
public
view
override
virtual
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
roundId = _latestRound();
answer = _latestAnswer();

uint256 blockTimestamp = redstoneAdapter.getBlockTimestampFromLatestUpdate();

// These values are equal after chainlink’s OCR update
startedAt = blockTimestamp;
updatedAt = blockTimestamp;

// We want to be compatible with Chainlink's interface
// And in our case the roundId is always equal to answeredInRound
answeredInRound = roundId;
}

/**
* @notice Chainlink function for getting the latest successfully reported value
* @return latestAnswer The latest successfully reported value
*/
function _latestAnswer() internal view returns (int256) {
uint256 uintAnswer = redstoneAdapter.getValueForDataFeed(dataFeedId);

if (uintAnswer > INT256_MAX) {
revert UnsafeUintToIntConversion(uintAnswer);
}

return int256(uintAnswer);
}

/**
* @notice Chainlink function for getting the number of latest round
* @return latestRound The number of the latest update round
*/
function _latestRound() internal view returns (uint80) {
uint256 roundIdUint256 = redstoneAdapter.getLatestRoundId();

if (roundIdUint256 > UINT80_MAX) {
revert UnsafeUint256ToUint80Conversion(roundIdUint256);
}

return uint80(roundIdUint256);
}
}
Loading
Loading