Skip to content

Commit

Permalink
Merge pull request #18 from Gearbox-protocol/instance-manager
Browse files Browse the repository at this point in the history
feat: instance manager and global contracts
  • Loading branch information
lekhovitsky authored Jan 7, 2025
2 parents 9d53869 + 7f8eaf6 commit 8541f1b
Show file tree
Hide file tree
Showing 23 changed files with 2,162 additions and 295 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,5 @@ dist
# Forge output
out
cache
broadcast
broadcast
.DS_Store
4 changes: 4 additions & 0 deletions contracts/global/AddressProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ contract AddressProvider is Ownable2Step, IAddressProvider {
_setAddress(key, value, saveVersion ? IVersion(value).version() : NO_VERSION_CONTROL);
}

function setAddress(bytes32 key, address value, bool saveVersion) external override onlyOwner {
_setAddress(key.fromSmallString(), value, saveVersion ? IVersion(value).version() : NO_VERSION_CONTROL);
}

/// @notice Sets the address for the passed contract key
/// @param addr Contract address
/// @param saveVersion Whether to save contract's version
Expand Down
630 changes: 386 additions & 244 deletions contracts/global/BytecodeRepository.sol

Large diffs are not rendered by default.

120 changes: 120 additions & 0 deletions contracts/global/InstanceManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {BytecodeRepository} from "./BytecodeRepository.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {
AP_INSTANCE_MANAGER,
AP_CROSS_CHAIN_GOVERNANCE,
AP_TREASURY,
NO_VERSION_CONTROL,
AP_BYTECODE_REPOSITORY,
AP_ADDRESS_PROVIDER,
AP_INSTANCE_MANAGER_PROXY,
AP_CROSS_CHAIN_GOVERNANCE_PROXY,
AP_TREASURY_PROXY
} from "../libraries/ContractLiterals.sol";
import {IAddressProvider} from "../interfaces/IAddressProvider.sol";
import {ProxyCall} from "../helpers/ProxyCall.sol";
import {LibString} from "@solady/utils/LibString.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {AddressProvider} from "./AddressProvider.sol";

contract InstanceManager is Ownable {
using LibString for string;

address public immutable addressProvider;
address public immutable bytecodeRepository;

address public instanceManagerProxy;
address public treasuryProxy;
address public crossChainGovernanceProxy;

address public marketConfiguratorFactory;
address public priceFeedStore;

bool public isActivated;

error InvalidKeyException(string key);

modifier onlyCrossChainGovernance() {
require(
msg.sender
== IAddressProvider(addressProvider).getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL),
"Only financial multisig can call this function"
);
_;
}

modifier onlyTreasury() {
require(
msg.sender == IAddressProvider(addressProvider).getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL),
"Only financial multisig can call this function"
);
_;
}

constructor(address _owner) {
bytecodeRepository = address(new BytecodeRepository(address(this)));
addressProvider = address(new AddressProvider());

IAddressProvider(addressProvider).setAddress(AP_BYTECODE_REPOSITORY, address(bytecodeRepository), true);
IAddressProvider(addressProvider).setAddress(AP_INSTANCE_MANAGER, address(this), true);
IAddressProvider(addressProvider).setAddress(AP_CROSS_CHAIN_GOVERNANCE, _owner, false);

instanceManagerProxy = address(new ProxyCall());
treasuryProxy = address(new ProxyCall());
crossChainGovernanceProxy = address(new ProxyCall());

IAddressProvider(addressProvider).setAddress(AP_INSTANCE_MANAGER_PROXY, instanceManagerProxy, false);
IAddressProvider(addressProvider).setAddress(AP_TREASURY_PROXY, treasuryProxy, false);
IAddressProvider(addressProvider).setAddress(AP_CROSS_CHAIN_GOVERNANCE_PROXY, crossChainGovernanceProxy, false);

_transferOwnership(_owner);
}

function activate(address _instanceOwner, address _treasury) external onlyOwner {
if (!isActivated) {
_verifyCoreContractsDeploy();
_transferOwnership(_instanceOwner);

IAddressProvider(addressProvider).setAddress(AP_TREASURY, _treasury, false);
isActivated = true;
}
}

function deploySystemContract(bytes32 _contractName, uint256 _version) external onlyCrossChainGovernance {
// deploy contract
// set address in address provider
address newSystemContract =
BytecodeRepository(bytecodeRepository).deploy(_contractName, _version, abi.encode(addressProvider), 0);
IAddressProvider(addressProvider).setAddress(_contractName, newSystemContract, true);
}

function setAddress(string memory key, address addr, bool saveVersion) external onlyCrossChainGovernance {
IAddressProvider(addressProvider).setAddress(key, addr, saveVersion);
}

function setLocalAddress(string memory key, address addr, bool saveVersion) external onlyOwner {
if (!key.startsWith("LOCAL_")) {
revert InvalidKeyException(key);
}
IAddressProvider(addressProvider).setAddress(key, addr, saveVersion);
}

function _verifyCoreContractsDeploy() internal view {
// verify that all core contracts are deployed
}

function configureGlobal(address target, bytes calldata data) external onlyCrossChainGovernance {
ProxyCall(crossChainGovernanceProxy).proxyCall(target, data);
}

function configureLocal(address target, bytes calldata data) external onlyOwner {
ProxyCall(instanceManagerProxy).proxyCall(target, data);
}

function configureTreasury(address target, bytes calldata data) external onlyTreasury {
ProxyCall(treasuryProxy).proxyCall(target, data);
}
}
144 changes: 143 additions & 1 deletion contracts/global/PriceFeedStore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,148 @@
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
import {PriceFeedValidationTrait} from "@gearbox-protocol/core-v3/contracts/traits/PriceFeedValidationTrait.sol";
import {IPriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";

import {IPriceFeedStore} from "../interfaces/IPriceFeedStore.sol";
import {AP_PRICE_FEED_STORE, AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL} from "../libraries/ContractLiterals.sol";
import {IAddressProvider} from "../interfaces/IAddressProvider.sol";
import {PriceFeedInfo} from "../interfaces/Types.sol";

contract PriceFeedStore is Ownable, SanityCheckTrait, PriceFeedValidationTrait, IPriceFeedStore {
using EnumerableSet for EnumerableSet.AddressSet;

//
// CONSTANTS
//

/// @notice Meta info about contract type & version
uint256 public constant override version = 3_10;
bytes32 public constant override contractType = AP_PRICE_FEED_STORE;

//
// VARIABLES
//

/// @dev Set of all known price feeds
EnumerableSet.AddressSet internal _knownPriceFeeds;

/// @dev Set of all known price feeds
EnumerableSet.AddressSet internal _knownTokens;

/// @dev Mapping from token address to its set of allowed price feeds
mapping(address => EnumerableSet.AddressSet) internal _allowedPriceFeeds;

/// @notice Mapping from price feed address to its data
mapping(address => PriceFeedInfo) public priceFeedInfo;

constructor(address _addressProvider) {
address instanceManager =
IAddressProvider(_addressProvider).getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL);
_transferOwnership(instanceManager);
}

/// @notice Returns the list of price feeds available for a token
function getPriceFeeds(address token) external view returns (address[] memory) {
return _allowedPriceFeeds[token].values();
}

/// @notice Returns whether a price feed is allowed to be used for a token
function isAllowedPriceFeed(address token, address priceFeed) external view returns (bool) {
return _allowedPriceFeeds[token].contains(priceFeed);
}

/// @notice Returns the staleness period for a price feed
function getStalenessPeriod(address priceFeed) external view returns (uint32) {
return priceFeedInfo[priceFeed].stalenessPeriod;
}

function getKnownTokens() external view returns (address[] memory) {
return _knownTokens.values();
}

/**
* @notice Adds a new price feed
* @param priceFeed The address of the new price feed
* @param stalenessPeriod Staleness period of the new price feed
* @dev Reverts if the price feed's result is stale based on the staleness period
*/
function addPriceFeed(address priceFeed, uint32 stalenessPeriod) external onlyOwner nonZeroAddress(priceFeed) {
if (_knownPriceFeeds.contains(priceFeed)) revert PriceFeedAlreadyAddedException(priceFeed);

_validatePriceFeed(priceFeed, stalenessPeriod);

bytes32 priceFeedType;
uint256 priceFeedVersion;

try IPriceFeed(priceFeed).contractType() returns (bytes32 _cType) {
priceFeedType = _cType;
priceFeedVersion = IPriceFeed(priceFeed).version();
} catch {
priceFeedType = "PF_EXTERNAL_ORACLE";
priceFeedVersion = 0;
}

_knownPriceFeeds.add(priceFeed);
priceFeedInfo[priceFeed].author = msg.sender;
priceFeedInfo[priceFeed].priceFeedType = priceFeedType;
priceFeedInfo[priceFeed].stalenessPeriod = stalenessPeriod;
priceFeedInfo[priceFeed].version = priceFeedVersion;

emit AddPriceFeed(priceFeed, stalenessPeriod);
}

/**
* @notice Sets the staleness period for an existing price feed
* @param priceFeed The address of the price feed
* @param stalenessPeriod New staleness period for the price feed
* @dev Reverts if the price feed is not added to the global list
*/
function setStalenessPeriod(address priceFeed, uint32 stalenessPeriod)
external
onlyOwner
nonZeroAddress(priceFeed)
{
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedNotKnownException(priceFeed);
uint32 oldStalenessPeriod = priceFeedInfo[priceFeed].stalenessPeriod;

if (stalenessPeriod != oldStalenessPeriod) {
_validatePriceFeed(priceFeed, stalenessPeriod);
priceFeedInfo[priceFeed].stalenessPeriod = stalenessPeriod;
emit SetStalenessPeriod(priceFeed, stalenessPeriod);
}
}

/**
* @notice Allows a price feed for use with a particular token
* @param token Address of the token
* @param priceFeed Address of the price feed
* @dev Reverts if the price feed is not added to the global list
*/
function allowPriceFeed(address token, address priceFeed) external onlyOwner nonZeroAddress(token) {
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedNotKnownException(priceFeed);

_allowedPriceFeeds[token].add(priceFeed);

emit AllowPriceFeed(token, priceFeed);
}

/**
* @notice Forbids a price feed for use with a particular token
* @param token Address of the token
* @param priceFeed Address of the price feed
* @dev Reverts if the price feed is not added to the global list or the per-token list
*/
function forbidPriceFeed(address token, address priceFeed) external onlyOwner nonZeroAddress(token) {
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedNotKnownException(priceFeed);
if (!_allowedPriceFeeds[token].contains(priceFeed)) revert PriceFeedIsNotAllowedException(token, priceFeed);

_allowedPriceFeeds[token].remove(priceFeed);

abstract contract PriceFeedStore is IPriceFeedStore {}
emit ForbidPriceFeed(token, priceFeed);
}
}
Loading

0 comments on commit 8541f1b

Please sign in to comment.