Skip to content
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
8 changes: 4 additions & 4 deletions script/honey/HoneyAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ abstract contract HoneyAddressBook {
honeyImpl: 0x96b1a552A97dA5503343d0F9FF2766c616E62905,
honeyFactory: 0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401,
honeyFactoryReader: 0x285e147060CDc5ba902786d3A471224ee6cE0F91,
honeyFactoryImpl: 0x6331F0a4E0220a14Be27BD31aF091F0a1AC036A1,
honeyFactoryImpl: 0xB587F98c81c37432067060196aC1DdAEC106ED8D,
honeyFactoryReaderImpl: 0x91C54526A9f8D0391F64392f24C7E8ff94A5f4fB,
collateralVaultImpl: 0xAa4f2Bc7a06c89BEAB5125D82e25D4166b4a4681,
honeyFactoryPythWrapper: 0xF5686e448BE103beA465105bEb9d284a34ae7e95
Expand All @@ -54,7 +54,7 @@ abstract contract HoneyAddressBook {
honeyImpl: 0xD1886E0659Ed88812aeA75862Cc9891097c25542,
honeyFactory: 0xA4aFef880F5cE1f63c9fb48F661E27F8B4216401,
honeyFactoryReader: 0x285e147060CDc5ba902786d3A471224ee6cE0F91,
honeyFactoryImpl: 0xD38a1fD3E943a61066903889b8e0889EcAc6Dedd,
honeyFactoryImpl: 0xF44F412c1CC4e92aE657aF93740De86048A06b95,
honeyFactoryReaderImpl: 0x22ee76216B1b7E4f34CF1417da3E4773F7cbA8E6,
collateralVaultImpl: 0xE3689043e7F860FbC0c814839cd7dF5022223172,
honeyFactoryPythWrapper: 0xE5Ad9BA751714ec8cdd554b5a9f12BFcA13980cB
Expand All @@ -68,7 +68,7 @@ abstract contract HoneyAddressBook {
honeyImpl: 0x4D4Ff71C50D47a5A45682F42E1E96bdED8fE7516,
honeyFactory: 0x2AA7F988284fD04cE83b27d017B89731c67d8F67,
honeyFactoryReader: 0xf1CF3467C9508dfa6D1197F5359419856B3A3300,
honeyFactoryImpl: 0xd6E9728eefa526F9B6Cf3c9ba5CbC060F937cDb0,
honeyFactoryImpl: 0xFc5d7EDd77f0b2386f5F960D6868D9022E6603d6,
honeyFactoryReaderImpl: 0x52d5848Ab7A304369cd2879DAfd9Ed3349E8ebB1,
collateralVaultImpl: 0x5DeDB0F5587F83798245a53189c1A52437A52475,
honeyFactoryPythWrapper: 0xA55F4a5D0415bFAaaE8207F77Ab2dc406e7594A2
Expand All @@ -82,7 +82,7 @@ abstract contract HoneyAddressBook {
honeyImpl: 0x36a10E516452BD2A80Bd5F421B0bC69eB0dFBca9,
honeyFactory: 0xeBF958b3b453f76fBE491a7F3ED29e37a509F530,
honeyFactoryReader: 0x353F8910914a46b3b971d54A131184fB46B8d7f8,
honeyFactoryImpl: 0x6178832DAC009EDA2e1D97f158cd94545F49c289,
honeyFactoryImpl: 0x22F160db4f4a5C17E985430b25de9B57609D31F1,
honeyFactoryReaderImpl: 0x1eD95AF653E0a540fC61AD8d1C7D2C5271855fD5,
collateralVaultImpl: 0x149C89732A9e83FDf20CA4AB03A94C3b4eb21C46,
honeyFactoryPythWrapper: 0x0E870Ae0ecff7036A7d34F9c571794C4ce1C3d62
Expand Down
7 changes: 2 additions & 5 deletions script/honey/actions/AddCollateral.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,9 @@ contract AddCollateralVaultScript is BaseScript, AddressBook {

console2.log("Adding collateral %s", IERC20(collateral).symbol());

// NOTE: the price oracle must have freshly pushed data, otherwise
// the honey factory will consider the asset as depegged.
IPriceOracle priceOracle = IPriceOracle(honeyFactory.priceOracle());
IPriceOracle.Data memory data = priceOracle.getPriceUnsafe(collateral);
require(data.publishTime >= block.timestamp - honeyFactory.priceFeedMaxDelay(), "Price data too old");

// check the price is available
priceOracle.priceAvailable(collateral);
ERC4626 vault = honeyFactory.createVault(collateral);
console2.log("Collateral Vault deployed at:", address(vault));
// Set mint rate to 1:1
Expand Down
13 changes: 9 additions & 4 deletions script/oracles/OraclesAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct OraclesAddresses {
address peggedPriceOracle;
address rootPriceOracle;
address extPyth; // Third-party dependencies
address redStonePriceOracle;
}

abstract contract OraclesAddressBook {
Expand All @@ -30,7 +31,8 @@ abstract contract OraclesAddressBook {
pythPriceOracle: 0x5CA67e134c52B1d11E038A5a4eD8Ddcdb1238943,
peggedPriceOracle: 0xE72FA7893ec375D82a0ff3078920C39D87F8FC2D,
rootPriceOracle: 0xe641aacDf2055F0D20c9ABc8FeF9dFBc5A68600B,
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43,
redStonePriceOracle: 0x28f8171A2561898Cfc9C355a6e20F1B40FeA19CC
});
}

Expand All @@ -39,7 +41,8 @@ abstract contract OraclesAddressBook {
pythPriceOracle: 0x5CA67e134c52B1d11E038A5a4eD8Ddcdb1238943,
peggedPriceOracle: 0xE72FA7893ec375D82a0ff3078920C39D87F8FC2D,
rootPriceOracle: 0xe641aacDf2055F0D20c9ABc8FeF9dFBc5A68600B,
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43,
redStonePriceOracle: 0x678d63074D8dd2eC4C2d7269879e43eaD4BE1338
});
}

Expand All @@ -48,7 +51,8 @@ abstract contract OraclesAddressBook {
pythPriceOracle: 0x8d6864Da39C9Cd4454709Ee55fD383fa2e72200A,
peggedPriceOracle: 0x969FA953E1554237357571a2Cf04dF717d8c9ca5,
rootPriceOracle: 0x99F8Ff2f48001FaE858C809b61599013F6f5a3d1,
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43,
redStonePriceOracle: 0x5DCC24061c3ccB9A9aA6f16A143bC34bAC2B0C04
});
}

Expand All @@ -57,7 +61,8 @@ abstract contract OraclesAddressBook {
pythPriceOracle: 0x4D7c72253b91B083f44ac0415f2D776861258025,
peggedPriceOracle: 0x0811E041eF374591A01cb49E5030D24b4911287e,
rootPriceOracle: 0x1EE6c86aeC1307Ef7D1c5144e1A2830BF5bD81Ae,
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43
extPyth: 0x2880aB155794e7179c9eE2e38200202908C17B43,
redStonePriceOracle: 0x392282F6241bAce724a6bD493676a2c634EaA3DB
});
}
}
2 changes: 2 additions & 0 deletions script/oracles/OraclesPredictAddresses.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { BasePredictScript, console2 } from "../base/BasePredict.s.sol";
import { PythPriceOracle } from "src/extras/PythPriceOracle.sol";
import { PeggedPriceOracle } from "src/extras/PeggedPriceOracle.sol";
import { RootPriceOracle } from "src/extras/RootPriceOracle.sol";
import { RedStonePriceOracle } from "src/extras/RedStonePriceOracle.sol";

contract OraclesPredictAddressesScript is BasePredictScript {
function run() public view {
console2.log("Price oracles contracts will be deployed at: ");
_predictProxyAddress("PythPriceOracle", type(PythPriceOracle).creationCode);
_predictAddress("PeggedPriceOracle", type(PeggedPriceOracle).creationCode);
_predictAddress("RootPriceOracle", type(RootPriceOracle).creationCode);
_predictProxyAddress("RedStonePriceOracle", type(RedStonePriceOracle).creationCode);
}
}
36 changes: 36 additions & 0 deletions script/oracles/deployment/5_DeployRedStonePriceOracle.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { console2 } from "forge-std/Script.sol";
import { BaseScript } from "../../base/Base.s.sol";
import { RBAC } from "../../base/RBAC.sol";
import { RedStonePriceOracleDeployer } from "src/extras/RedStonePriceOracleDeployer.sol";
import { RedStonePriceOracle } from "src/extras/RedStonePriceOracle.sol";
import { AddressBook } from "../../base/AddressBook.sol";

contract DeployRedStonePriceOracleScript is RBAC, BaseScript, AddressBook {
function run() public pure {
console2.log("Please run specific function.");
}

function deployRedStonePriceOracle(address redstonePriceFeedAdapter, address governance) public broadcast {
RedStonePriceOracleDeployer oracleDeployer = new RedStonePriceOracleDeployer(
governance, redstonePriceFeedAdapter, _saltsForProxy(type(RedStonePriceOracle).creationCode)
);

RedStonePriceOracle redStonePriceOracle = RedStonePriceOracle(oracleDeployer.oracle());
_checkDeploymentAddress(
"RedStonePriceOracle", address(redStonePriceOracle), _oraclesAddresses.redStonePriceOracle
);

RBAC.RoleDescription memory adminRole = RBAC.RoleDescription({
contractName: "RedStonePriceOracle",
contractAddr: _oraclesAddresses.redStonePriceOracle,
name: "DEFAULT_ADMIN_ROLE",
role: redStonePriceOracle.DEFAULT_ADMIN_ROLE()
});

RBAC.AccountDescription memory deployer = RBAC.AccountDescription({ name: "deployer", addr: governance });
_requireRole(adminRole, deployer);
}
}
2 changes: 1 addition & 1 deletion script/pol/POLAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ abstract contract POLAddressBook {
bgtStaker: 0x44F07Ce5AfeCbCC406e6beFD40cc2998eEb8c7C6,
bgtStakerImpl: 0xDD7FA46a1a735DBD7E7eD4B1928176D28002e205,
feeCollector: 0x7Bb8DdaC7FbE3FFC0f4B3c73C4F158B06CF82650,
feeCollectorImpl: 0x0fE7B2A78f8c239569ec22cdbdb472694afc289c,
feeCollectorImpl: 0xAc74e54BAAe195Dd88fDC1aCdd967925B3eB220f,
bgtIncentiveDistributor: 0x77DA09bC82652f9A14d1b170a001e759640298e6,
bgtIncentiveDistributorImpl: 0x5c5BfeFddB6f6A51F1D24A6a99F3BeA53eE59F29,
incentivesCollector: 0x1984Baf659607Cc5f206c55BB3B00eb3E180190B,
Expand Down
2 changes: 1 addition & 1 deletion src/extras/IPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.26;

/// @title Provide asset prices in USD with WAD precision
/// @dev Generic interface that wraps the Pyth oracle
/// @dev Generic interface that wraps the Pyth oracle and redstone price oracle
interface IPriceOracle {
/// @dev TBD whether to also return a confidence interval.
struct Data {
Expand Down
143 changes: 143 additions & 0 deletions src/extras/RedStonePriceOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { IPriceOracle } from "./IPriceOracle.sol";
import { Utils } from "../libraries/Utils.sol";

/// @title RedStone price feed adapter interface
interface IRedStonePriceFeedAdapter {
/// @dev Get the last update details.
/// @param dataFeedId The data feed ID.
/// @return lastDataTimestamp The timestamp when the data was last updated.
/// @return lastBlockTimestamp The timestamp when the block was last updated.
/// @return lastValue The last value.
function getLastUpdateDetails(bytes32 dataFeedId)
external
view
returns (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue);

/// @dev Get the last update details without any sanity checks.
/// @param dataFeedId The data feed ID.
/// @return lastDataTimestamp The timestamp when the data was last updated.
/// @return lastBlockTimestamp The timestamp when the block was last updated.
/// @return lastValue The last value.
function getLastUpdateDetailsUnsafe(bytes32 dataFeedId)
external
view
returns (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue);
}

/// @title RedStone price oracle
/// @dev Provides price data from RedStone feeds in WAD precision.
contract RedStonePriceOracle is IPriceOracle, AccessControlUpgradeable, UUPSUpgradeable {
using Utils for bytes4;

/// @notice The RedStone price feed adapter.
IRedStonePriceFeedAdapter public redstonePriceFeedAdapter;

/// @notice The RedStone data feed IDs mapping
mapping(address asset => bytes32 dataFeedId) public dataFeedIds;

/// @notice Emitted when a data feed ID is changed.
event DataFeedIdChanged(address indexed asset, bytes32 indexed dataFeedId);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(address governance_, address redstonePriceFeedAdapter_) external initializer {
__AccessControl_init();
__UUPSUpgradeable_init();
if (governance_ == address(0)) ZeroAddress.selector.revertWith();
if (redstonePriceFeedAdapter_ == address(0)) ZeroAddress.selector.revertWith();
redstonePriceFeedAdapter = IRedStonePriceFeedAdapter(redstonePriceFeedAdapter_);
_grantRole(DEFAULT_ADMIN_ROLE, governance_);
}

function _authorizeUpgrade(address newImplementation) internal virtual override {
// Silent warning
newImplementation;
_checkRole(DEFAULT_ADMIN_ROLE);
}

/// @notice Set the data feed ID for a given asset.
/// @param asset The asset.
/// @param dataFeedId The RedStone data feed ID.
function setDataFeedId(address asset, bytes32 dataFeedId) external {
_checkRole(DEFAULT_ADMIN_ROLE);
if (asset == address(0) || dataFeedId == bytes32(0)) ZeroAddress.selector.revertWith();
dataFeedIds[asset] = dataFeedId;
emit DataFeedIdChanged(asset, dataFeedId);
}

/// @dev Get the latest round data and convert it to the WAD precision.
function _wrapData(uint256 lastValue, uint256 lastBlockTimestamp)
internal
pure
returns (IPriceOracle.Data memory)
{
// redstone feed returns value in 8 decimals, so we need to convert it to 18 decimals.
return IPriceOracle.Data({ price: Utils.changeDecimals(lastValue, 8, 18), publishTime: lastBlockTimestamp });
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IPriceOracle FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @inheritdoc IPriceOracle
/// @dev getLastUpdateDetails has an inbuilt sanity check for 30 hours stale data along with positive value check.
function getPrice(address asset) public view onlyAssetWithDataFeedIdSet(asset) returns (Data memory data) {
(, uint256 lastBlockTimestamp, uint256 lastValue) =
redstonePriceFeedAdapter.getLastUpdateDetails(dataFeedIds[asset]);
return _wrapData(lastValue, lastBlockTimestamp);
}

/// @inheritdoc IPriceOracle
function getPriceUnsafe(address asset) public view onlyAssetWithDataFeedIdSet(asset) returns (Data memory data) {
(, uint256 lastBlockTimestamp, uint256 lastValue) =
redstonePriceFeedAdapter.getLastUpdateDetailsUnsafe(dataFeedIds[asset]);
return _wrapData(lastValue, lastBlockTimestamp);
}

/// @inheritdoc IPriceOracle
/// @dev For any number of age less than heartbeat interval, this will mostly revert as redstone feeds are
/// generally updated at heartbeat interval.
function getPriceNoOlderThan(
address asset,
uint256 age
)
external
view
onlyAssetWithDataFeedIdSet(asset)
returns (Data memory data)
{
data = getPriceUnsafe(asset);
// Throws panic revert if age is greater than block.timestamp
// Revert with UnavailableData if the price is older than the age.
if (data.publishTime < block.timestamp - age) {
UnavailableData.selector.revertWith(asset);
}
return data;
}

/// @inheritdoc IPriceOracle
function priceAvailable(address asset) external view returns (bool) {
if (dataFeedIds[asset] == bytes32(0)) {
return false;
}
(, uint256 lastBlockTimestamp,) = redstonePriceFeedAdapter.getLastUpdateDetailsUnsafe(dataFeedIds[asset]);
return lastBlockTimestamp != 0;
}

/// @dev Modifier to check if the data feed ID is set for a given asset.
/// @param asset The asset.
modifier onlyAssetWithDataFeedIdSet(address asset) {
if (dataFeedIds[asset] == bytes32(0)) {
UnavailableData.selector.revertWith(asset);
}
_;
}
}
22 changes: 22 additions & 0 deletions src/extras/RedStonePriceOracleDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { Create2Deployer } from "../base/Create2Deployer.sol";
import { Salt } from "../base/Salt.sol";
import { RedStonePriceOracle } from "./RedStonePriceOracle.sol";

contract RedStonePriceOracleDeployer is Create2Deployer {
/// @notice The RedStonePriceOracle contract.
// solhint-disable-next-line immutable-vars-naming
RedStonePriceOracle public immutable oracle;

constructor(address governance, address redstonePriceFeedAdapter, Salt memory oracleSalt) {
// deploy the RedStonePriceOracle implementation
address oracleImpl = deployWithCreate2(oracleSalt.implementation, type(RedStonePriceOracle).creationCode);
// deploy the RedStonePriceOracle proxy
oracle = RedStonePriceOracle(deployProxyWithCreate2(oracleImpl, oracleSalt.proxy));

// initialize the contracts
oracle.initialize(governance, redstonePriceFeedAdapter);
}
}
Loading
Loading