diff --git a/contracts/factories/CreditFactory.sol b/contracts/factories/CreditFactory.sol index fd93c5f..2ebbe98 100644 --- a/contracts/factories/CreditFactory.sol +++ b/contracts/factories/CreditFactory.sol @@ -331,7 +331,7 @@ contract CreditFactory is AbstractFactory, ICreditFactory { botList_ = ICreditFacadeV3(prevCreditFacade).botList(); } - address weth = _tryGetAddress(AP_WETH_TOKEN, NO_VERSION_CONTROL); + address weth = _getAddress(AP_WETH_TOKEN, NO_VERSION_CONTROL); bytes memory constructorParams = abi.encode(addressProvider, creditManager, lossPolicy, botList_, weth, params.degenNFT, params.expirable); diff --git a/contracts/instance/AddressProvider.sol b/contracts/instance/AddressProvider.sol index ba44950..03480ad 100644 --- a/contracts/instance/AddressProvider.sol +++ b/contracts/instance/AddressProvider.sol @@ -5,132 +5,164 @@ pragma solidity ^0.8.23; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {LibString} from "@solady/utils/LibString.sol"; - import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol"; -import {AddressNotFoundException} from "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol"; -import {IAddressProvider, ContractValue} from "../interfaces/IAddressProvider.sol"; +import {IAddressProvider} from "../interfaces/IAddressProvider.sol"; +import {AddressProviderEntry} from "../interfaces/Types.sol"; import {AP_ADDRESS_PROVIDER, NO_VERSION_CONTROL} from "../libraries/ContractLiterals.sol"; import {ImmutableOwnableTrait} from "../traits/ImmutableOwnableTrait.sol"; -struct ContractKey { - string key; - uint256 version; -} - -/// @title Address provider V3 +/// @title Address provider /// @notice Stores addresses of important contracts contract AddressProvider is ImmutableOwnableTrait, IAddressProvider { - using EnumerableSet for EnumerableSet.AddressSet; - // using LibString for string; - using LibString for bytes32; + using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.UintSet; + + /// @dev Internal struct with version info for a given key + struct VersionInfo { + uint256 latest; + mapping(uint256 majorVersion => uint256) latestByMajor; + mapping(uint256 minorVersion => uint256) latestByMinor; + EnumerableSet.UintSet versionsSet; + } /// @notice Contract version uint256 public constant override version = 3_10; + + /// @notice Contract type bytes32 public constant override contractType = AP_ADDRESS_PROVIDER; - /// @notice Mapping from (contract key, version) to contract addresses - mapping(string => mapping(uint256 => address)) public override addresses; + /// @dev Set of saved entry keys + EnumerableSet.Bytes32Set internal _keysSet; - mapping(string => uint256) public latestVersions; - mapping(string => mapping(uint256 => uint256)) public latestMinorVersions; - mapping(string => mapping(uint256 => uint256)) public latestPatchVersions; + /// @dev Mapping from `key` to version info + mapping(bytes32 key => VersionInfo) internal _versionInfo; - ContractKey[] internal contractKeys; + /// @dev Mapping from `(key, ver)` pair to saved address + mapping(bytes32 key => mapping(uint256 ver => address)) internal _addresses; - error VersionNotFoundException(); + /// @notice Constructor + /// @param owner_ Contract owner + constructor(address owner_) ImmutableOwnableTrait(owner_) {} - constructor(address _owner) ImmutableOwnableTrait(_owner) { - // The first event is emitted for the address provider itself to aid in contract discovery - emit SetAddress(AP_ADDRESS_PROVIDER.fromSmallString(), version, address(this)); - } + // ------- // + // GETTERS // + // ------- // - /// @notice Returns the address of a contract with a given key and version - function getAddressOrRevert(string memory key, uint256 _version) - public - view - virtual - override - returns (address result) - { - result = addresses[key][_version]; - if (result == address(0)) revert AddressNotFoundException(); + /// @notice Returns the address by given `key` and `ver` + function getAddress(bytes32 key, uint256 ver) external view override returns (address) { + return _addresses[key][ver]; } - /// @notice Returns the address of a contract with a given key and version - function getAddressOrRevert(bytes32 key, uint256 _version) public view virtual override returns (address result) { - return getAddressOrRevert(key.fromSmallString(), _version); + /// @notice Returns the address by given `key` and `ver`, reverts if not found + function getAddressOrRevert(bytes32 key, uint256 ver) external view override returns (address result) { + result = _addresses[key][ver]; + if (result == address(0)) revert AddressNotFoundException(key, ver); } - function getLatestVersion(string memory key) external view override returns (uint256) { - uint256 latestVersion = latestVersions[key]; - if (latestVersion == 0) revert VersionNotFoundException(); - return latestVersion; + /// @notice Returns all known keys + function getKeys() external view override returns (bytes32[] memory) { + return _keysSet.values(); } - function getLatestMinorVersion(string memory key, uint256 majorVersion) external view override returns (uint256) { - majorVersion -= majorVersion % 100; - uint256 latestMinorVersion = latestMinorVersions[key][majorVersion]; - if (latestMinorVersion == 0) revert VersionNotFoundException(); - return latestMinorVersion; + /// @notice Returns all known versions for given `key` + function getVersions(bytes32 key) external view override returns (uint256[] memory) { + return _versionInfo[key].versionsSet.values(); } - function getLatestPatchVersion(string memory key, uint256 minorVersion) external view override returns (uint256) { - minorVersion -= minorVersion % 10; - uint256 latestPatchVersion = latestPatchVersions[key][minorVersion]; - if (latestPatchVersion == 0) revert VersionNotFoundException(); - return latestPatchVersion; + /// @notice Returns all saved entries + function getAllEntries() external view override returns (AddressProviderEntry[] memory entries) { + uint256 numKeys = _keysSet.length(); + + uint256 numEntries; + for (uint256 i; i < numKeys; ++i) { + numEntries += _versionInfo[_keysSet.at(i)].versionsSet.length(); + } + + entries = new AddressProviderEntry[](numEntries); + uint256 idx; + for (uint256 i; i < numKeys; ++i) { + bytes32 key = _keysSet.at(i); + VersionInfo storage info = _versionInfo[key]; + uint256 numVersions = info.versionsSet.length(); + for (uint256 j; j < numVersions; ++j) { + uint256 ver = info.versionsSet.at(j); + entries[idx++] = AddressProviderEntry(key, ver, _addresses[key][ver]); + } + } } - /// @notice Sets the address for the passed contract key - /// @param key Contract key - /// @param value Contract address - /// @param saveVersion Whether to save contract's version - function setAddress(string memory key, address value, bool saveVersion) external override onlyOwner { - _setAddress(key, value, saveVersion ? IVersion(value).version() : NO_VERSION_CONTROL); + /// @notice Returns the latest version for given `key` (excluding `NO_VERSION_CONTROL`) + /// @dev Reverts if `key` has no versions except `NO_VERSION_CONTROL` + function getLatestVersion(bytes32 key) external view override returns (uint256 ver) { + ver = _versionInfo[key].latest; + if (ver == 0) revert VersionNotFoundException(key); } - function setAddress(bytes32 key, address value, bool saveVersion) external override onlyOwner { - _setAddress(key.fromSmallString(), value, saveVersion ? IVersion(value).version() : NO_VERSION_CONTROL); + /// @notice Returns the latest minor version for given `majorVersion` + /// @dev Reverts if `majorVersion` is less than `100` + /// @dev Reverts if `key` has no entries with matching `majorVersion` + function getLatestMinorVersion(bytes32 key, uint256 majorVersion) external view override returns (uint256 ver) { + _validateVersion(key, majorVersion); + ver = _versionInfo[key].latestByMajor[_getMajorVersion(majorVersion)]; + if (ver == 0) revert VersionNotFoundException(key); } - /// @notice Sets the address for the passed contract key - /// @param addr Contract address - /// @param saveVersion Whether to save contract's version - function setAddress(address addr, bool saveVersion) external override onlyOwner { - _setAddress( - IVersion(addr).contractType().fromSmallString(), - addr, - saveVersion ? IVersion(addr).version() : NO_VERSION_CONTROL - ); + /// @notice Returns the latest patch version for given `minorVersion` + /// @dev Reverts if `minorVersion` is less than `100` + /// @dev Reverts if `key` has no entries with matching `minorVersion` + function getLatestPatchVersion(bytes32 key, uint256 minorVersion) external view override returns (uint256 ver) { + _validateVersion(key, minorVersion); + ver = _versionInfo[key].latestByMinor[_getMinorVersion(minorVersion)]; + if (ver == 0) revert VersionNotFoundException(key); } - /// @dev Implementation of `setAddress` - function _setAddress(string memory key, address value, uint256 _version) internal virtual { - addresses[key][_version] = value; - uint256 latestVersion = latestVersions[key]; - - // FIXME: we might update a non-latest minor version with a patch and this will be incorrectly skipped - if (_version > latestVersion) { - latestVersions[key] = _version; - uint256 majorVersion = _version / 100 * 100; - uint256 minorVersion = _version / 10 * 10; - latestMinorVersions[key][majorVersion] = _version; - latestPatchVersions[key][minorVersion] = _version; + // ------------- // + // CONFIGURATION // + // ------------- // + + /// @notice Sets the address for given `key` to `value`, optionally saving contract's version + /// @dev Reverts if caller is not the owner + /// @dev Reverts if `value` is zero address + /// @dev If `saveVersion` is true, reverts if version is less than 100 + function setAddress(bytes32 key, address value, bool saveVersion) external override onlyOwner { + if (value == address(0)) revert ZeroAddressException(key); + uint256 ver = NO_VERSION_CONTROL; + if (saveVersion) { + ver = IVersion(value).version(); + _validateVersion(key, ver); } - contractKeys.push(ContractKey(key, _version)); - emit SetAddress(key, _version, value); + if (_addresses[key][ver] == value) return; + _keysSet.add(key); + VersionInfo storage info = _versionInfo[key]; + info.versionsSet.add(ver); + _addresses[key][ver] = value; + emit SetAddress(key, ver, value); + + if (ver == NO_VERSION_CONTROL) return; + if (ver > info.latest) info.latest = ver; + uint256 majorVersion = _getMajorVersion(ver); + if (ver > info.latestByMajor[majorVersion]) info.latestByMajor[majorVersion] = ver; + uint256 minorVersion = _getMinorVersion(ver); + if (ver > info.latestByMinor[minorVersion]) info.latestByMinor[minorVersion] = ver; } - function getAllSavedContracts() external view returns (ContractValue[] memory) { - ContractValue[] memory result = new ContractValue[](contractKeys.length); - for (uint256 i = 0; i < contractKeys.length; i++) { - result[i] = ContractValue( - contractKeys[i].key, addresses[contractKeys[i].key][contractKeys[i].version], contractKeys[i].version - ); - } - return result; + // --------- // + // INTERNALS // + // --------- // + + /// @dev Returns the major version of a given version + function _getMajorVersion(uint256 ver) internal pure returns (uint256) { + return ver - ver % 100; + } + + /// @dev Returns the minor version of a given version + function _getMinorVersion(uint256 ver) internal pure returns (uint256) { + return ver - ver % 10; + } + + function _validateVersion(bytes32 key, uint256 ver) internal view { + if (ver < 100) revert InvalidVersionException(key, ver); } } diff --git a/contracts/instance/InstanceManager.sol b/contracts/instance/InstanceManager.sol index 859384d..a3b5e73 100644 --- a/contracts/instance/InstanceManager.sol +++ b/contracts/instance/InstanceManager.sol @@ -3,65 +3,85 @@ // (c) Gearbox Foundation, 2024. pragma solidity ^0.8.23; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {LibString} from "@solady/utils/LibString.sol"; + import {BytecodeRepository} from "../global/BytecodeRepository.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol"; +import {ProxyCall} from "../helpers/ProxyCall.sol"; +import {IInstanceManager} from "../interfaces/IInstanceManager.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, AP_CROSS_CHAIN_GOVERNANCE_PROXY, - AP_TREASURY_PROXY, AP_GEAR_STAKING, AP_GEAR_TOKEN, + AP_INSTANCE_MANAGER, + AP_INSTANCE_MANAGER_PROXY, + AP_TREASURY, + AP_TREASURY_PROXY, AP_WETH_TOKEN, - AP_MARKET_CONFIGURATOR_FACTORY + NO_VERSION_CONTROL } from "../libraries/ContractLiterals.sol"; -import {IAddressProvider} from "../interfaces/IAddressProvider.sol"; -import {IInstanceManager} from "../interfaces/IInstanceManager.sol"; -import {ProxyCall} from "../helpers/ProxyCall.sol"; -import {LibString} from "@solady/utils/LibString.sol"; + import {AddressProvider} from "./AddressProvider.sol"; +/// @title Instance manager contract InstanceManager is Ownable, IInstanceManager { using LibString for string; + using LibString for bytes32; - /// @notice Meta info about contract type & version + /// @notice Contract version uint256 public constant override version = 3_10; + + /// @notice Contract type bytes32 public constant override contractType = AP_INSTANCE_MANAGER; - address public immutable addressProvider; - address public immutable bytecodeRepository; + /// @notice Address provider + address public immutable override addressProvider; + + /// @notice Bytecode repository + address public immutable override bytecodeRepository; + + /// @notice Instance manager proxy + address public immutable override instanceManagerProxy; - address public instanceManagerProxy; - address public treasuryProxy; - address public crossChainGovernanceProxy; + /// @notice Treasury proxy + address public immutable override treasuryProxy; - bool public isActivated; + /// @notice Cross-chain governance proxy + address public immutable override crossChainGovernanceProxy; - error InvalidKeyException(string key); + /// @notice Whether the instance is activated + bool public override isActivated; + /// @notice Pending governance + address public override pendingGovernance; + + /// @dev Reverts if caller is not cross-chain governance modifier onlyCrossChainGovernance() { - require( - msg.sender - == IAddressProvider(addressProvider).getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL), - "Only cross chain governance can call this function" - ); + if (msg.sender != _getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL)) { + revert CallerIsNotCrossChainGovernanceException(msg.sender); + } _; } + /// @dev Reverts if caller is not pending governance + modifier onlyPendingGovernance() { + if (msg.sender != pendingGovernance) revert CallerIsNotPendingGovernanceException(msg.sender); + _; + } + + /// @dev Reverts if caller is not the DAO treasury modifier onlyTreasury() { - require( - msg.sender == IAddressProvider(addressProvider).getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL), - "Only financial multisig can call this function" - ); + if (msg.sender != _getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL)) { + revert CallerIsNotTreasuryException(msg.sender); + } _; } - constructor(address _owner) { + /// @notice Constructor + /// @param owner_ Contract owner (upon contract creation, must be cross-chain governance) + constructor(address owner_) { instanceManagerProxy = address(new ProxyCall()); treasuryProxy = address(new ProxyCall()); crossChainGovernanceProxy = address(new ProxyCall()); @@ -70,103 +90,144 @@ contract InstanceManager is Ownable, IInstanceManager { addressProvider = address(new AddressProvider(address(this))); _setAddress(AP_BYTECODE_REPOSITORY, address(bytecodeRepository), false); - _setAddress(AP_CROSS_CHAIN_GOVERNANCE, _owner, false); + _setAddress(AP_CROSS_CHAIN_GOVERNANCE, owner_, false); _setAddress(AP_INSTANCE_MANAGER_PROXY, instanceManagerProxy, false); _setAddress(AP_TREASURY_PROXY, treasuryProxy, false); _setAddress(AP_CROSS_CHAIN_GOVERNANCE_PROXY, crossChainGovernanceProxy, false); _setAddress(AP_INSTANCE_MANAGER, address(this), false); - _transferOwnership(_owner); + _transferOwnership(owner_); } - function activate(address _instanceOwner, address _treasury, address _weth, address _gear) external onlyOwner { - if (!isActivated) { - _transferOwnership(_instanceOwner); + /// @notice Returns the instance owner + function owner() public view override(Ownable, IInstanceManager) returns (address) { + return super.owner(); + } - _setAddress(AP_TREASURY, _treasury, false); - _setAddress(AP_WETH_TOKEN, _weth, false); - _setAddress(AP_GEAR_TOKEN, _gear, false); - isActivated = true; - } + // ------------- // + // CONFIGURATION // + // ------------- // + + /// @notice Activates the instance, setting the instance owner and saving the treasury, WETH and GEAR addresses + /// @dev Can only be called once by the cross-chain governance + function activate(address instanceOwner, address treasury, address weth, address gear) + external + override + onlyOwner + { + if (isActivated) return; + _transferOwnership(instanceOwner); + + _setAddress(AP_TREASURY, treasury, false); + _setAddress(AP_WETH_TOKEN, weth, false); + _setAddress(AP_GEAR_TOKEN, gear, false); + isActivated = true; } - function deploySystemContract(bytes32 _contractType, uint256 _version, bool _saveVersion) + /// @notice Deploys a system contract and saves its address in the address provider + /// @dev System contracts must accept address provider as the only constructor argument + /// @dev Reverts if caller is not the cross-chain governance + function deploySystemContract(bytes32 contractType_, uint256 version_, bool saveVersion) external + override onlyCrossChainGovernance { - address newSystemContract; - if ( - _contractType == AP_GEAR_STAKING && _version == 3_10 - && (block.chainid == 1 || block.chainid == 10 || block.chainid == 42161) - ) { - if (block.chainid == 1) { - newSystemContract = 0x2fcbD02d5B1D52FC78d4c02890D7f4f47a459c33; - } else if (block.chainid == 10) { - newSystemContract = 0x8D2622f1CA3B42b637e2ff6753E6b69D3ab9Adfd; - } else if (block.chainid == 42161) { - newSystemContract = 0xf3599BEfe8E79169Afd5f0b7eb0A1aA322F193D9; - } - } else { - newSystemContract = _deploySystemContract(_contractType, _version); - } + address newSystemContract = contractType_ == AP_GEAR_STAKING && version_ == 3_10 && _isLegacyChain() + ? _getLegacyGearStakingAddress() + : _deploySystemContract(contractType_, version_); - _setAddress(_contractType, newSystemContract, _saveVersion); + if (newSystemContract != address(0)) _setAddress(contractType_, newSystemContract, saveVersion); } - function _deploySystemContract(bytes32 _contractType, uint256 _version) internal returns (address) { - try ProxyCall(crossChainGovernanceProxy).proxyCall( - address(bytecodeRepository), - abi.encodeCall(BytecodeRepository.deploy, (_contractType, _version, abi.encode(addressProvider), 0)) - ) returns (bytes memory result) { - return abi.decode(result, (address)); - } catch { - return address(0); - } + /// @notice Allows cross-chain governance to set a global address in the address provider + /// @dev `key` must start with the "GLOBAL::" prefix + function setGlobalAddress(bytes32 key, address addr, bool saveVersion) external override onlyCrossChainGovernance { + _setAddressWithPrefix(key, "GLOBAL::", addr, saveVersion); } - function setLegacyAddress(string memory key, address addr, bool saveVersion) external onlyCrossChainGovernance { - if ((block.chainid == 1 || block.chainid == 10 || block.chainid == 42161) && !isActivated) { - IAddressProvider(addressProvider).setAddress(key, addr, saveVersion); - } + /// @notice Allows instance owner to set a local address in the address provider + /// @dev `key` must start with the "LOCAL::" prefix + function setLocalAddress(bytes32 key, address addr, bool saveVersion) external override onlyOwner { + _setAddressWithPrefix(key, "LOCAL::", addr, saveVersion); } - function setGlobalAddress(string memory key, address addr, bool saveVersion) external onlyCrossChainGovernance { - _setAddressWithPrefix(key, "GLOBAL_", addr, saveVersion); + /// @notice Allows cross-chain governance to configure global contracts such as bytecode repository, GEAR staking, etc. + function configureGlobal(address target, bytes calldata data) external override onlyCrossChainGovernance { + ProxyCall(crossChainGovernanceProxy).proxyCall(target, data); } - function setLocalAddress(string memory key, address addr, bool saveVersion) external onlyOwner { - _setAddressWithPrefix(key, "LOCAL_", addr, saveVersion); + /// @notice Allows instance owner to configure local contracts such as price feed store, bot list, etc. + function configureLocal(address target, bytes calldata data) external override onlyOwner { + ProxyCall(instanceManagerProxy).proxyCall(target, data); } - function _setAddressWithPrefix(string memory key, string memory prefix, address addr, bool saveVersion) internal { - if (!key.startsWith(prefix)) { - revert InvalidKeyException(key); - } - IAddressProvider(addressProvider).setAddress(key, addr, saveVersion); + /// @notice Allows DAO treasury to configure financial contracts such as fee splitters + function configureTreasury(address target, bytes calldata data) external override onlyTreasury { + ProxyCall(treasuryProxy).proxyCall(target, data); } - function configureGlobal(address target, bytes calldata data) external onlyCrossChainGovernance { - _configureGlobal(target, data); + /// @notice Sets `newCrossChainGovernance` as the pending cross-chain governance + /// @dev Can only be called by the current cross-chain governance + function setPendingGovernance(address newGovernance) external override onlyCrossChainGovernance { + pendingGovernance = newGovernance; + emit SetPendingGovernance(newGovernance); } - function _configureGlobal(address target, bytes memory data) internal { - ProxyCall(crossChainGovernanceProxy).proxyCall(target, data); + /// @notice Accepts the cross-chain governance role + /// @dev Can only be called by the pending governance + function acceptGovernance() external override onlyPendingGovernance { + _setAddress(AP_CROSS_CHAIN_GOVERNANCE, msg.sender, false); + pendingGovernance = address(0); + emit AcceptGovernance(msg.sender); } - function configureLocal(address target, bytes calldata data) external onlyOwner { - ProxyCall(instanceManagerProxy).proxyCall(target, data); - } + // --------- // + // INTERNALS // + // --------- // - function configureTreasury(address target, bytes calldata data) external onlyTreasury { - ProxyCall(treasuryProxy).proxyCall(target, data); + /// @dev Internal wrapper around address provider's `getAddressOrRevert` to reduce code size + function _getAddressOrRevert(bytes32 key, uint256 ver) internal view returns (address) { + return AddressProvider(addressProvider).getAddressOrRevert(key, ver); } + /// @dev Internal wrapper around address provider's `setAddress` to reduce code size function _setAddress(bytes32 key, address value, bool saveVersion) internal { - IAddressProvider(addressProvider).setAddress(key, value, saveVersion); + AddressProvider(addressProvider).setAddress(key, value, saveVersion); } - function owner() public view override(Ownable, IInstanceManager) returns (address) { - return super.owner(); + /// @dev Sets address in the address provider, ensuring that `key` starts with `prefix` + function _setAddressWithPrefix(bytes32 key, string memory prefix, address addr, bool saveVersion) internal { + if (!key.fromSmallString().startsWith(prefix)) revert InvalidKeyException(key); + _setAddress(key, addr, saveVersion); + } + + /// @dev Deploys a system contract and returns its address + function _deploySystemContract(bytes32 _contractType, uint256 _version) internal returns (address) { + try ProxyCall(crossChainGovernanceProxy).proxyCall( + address(bytecodeRepository), + abi.encodeCall(BytecodeRepository.deploy, (_contractType, _version, abi.encode(addressProvider), 0)) + ) returns (bytes memory result) { + return abi.decode(result, (address)); + } catch { + return address(0); + } + } + + /// @dev Whether there is a legacy instance on this chain + function _isLegacyChain() internal view returns (bool) { + return block.chainid == 1 || block.chainid == 10 || block.chainid == 42161; + } + + /// @dev Returns the address of the legacy GEAR staking contract on this chain + function _getLegacyGearStakingAddress() internal view returns (address) { + if (block.chainid == 1) { + return 0x2fcbD02d5B1D52FC78d4c02890D7f4f47a459c33; + } else if (block.chainid == 10) { + return 0x8D2622f1CA3B42b637e2ff6753E6b69D3ab9Adfd; + } else if (block.chainid == 42161) { + return 0xf3599BEfe8E79169Afd5f0b7eb0A1aA322F193D9; + } + return address(0); } } diff --git a/contracts/instance/MarketConfiguratorFactory.sol b/contracts/instance/MarketConfiguratorFactory.sol index c7d75dc..3e93507 100644 --- a/contracts/instance/MarketConfiguratorFactory.sol +++ b/contracts/instance/MarketConfiguratorFactory.sol @@ -3,7 +3,6 @@ // (c) Gearbox Foundation, 2024. pragma solidity ^0.8.23; -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IContractsRegister} from "../interfaces/IContractsRegister.sol"; @@ -14,17 +13,13 @@ import { AP_CROSS_CHAIN_GOVERNANCE, AP_MARKET_CONFIGURATOR, AP_MARKET_CONFIGURATOR_FACTORY, - AP_MARKET_CONFIGURATOR_LEGACY, NO_VERSION_CONTROL } from "../libraries/ContractLiterals.sol"; -import {MarketConfiguratorLegacy} from "../market/legacy/MarketConfiguratorLegacy.sol"; -import {MarketConfigurator} from "../market/MarketConfigurator.sol"; - import {DeployerTrait} from "../traits/DeployerTrait.sol"; +/// @title Market configurator factory contract MarketConfiguratorFactory is DeployerTrait, IMarketConfiguratorFactory { - using Address for address; using EnumerableSet for EnumerableSet.AddressSet; /// @notice Contract version @@ -47,46 +42,60 @@ contract MarketConfiguratorFactory is DeployerTrait, IMarketConfiguratorFactory _; } - /// @dev Reverts if caller is not one of market configurators - modifier onlyMarketConfigurators() { - if (!_registeredMarketConfiguratorsSet.contains(msg.sender)) { - revert CallerIsNotMarketConfiguratorException(msg.sender); - } - _; - } - + /// @dev Reverts if `msg.sender` is not the admin of `marketConfigurator` modifier onlyMarketConfiguratorAdmin(address marketConfigurator) { - if (!_registeredMarketConfiguratorsSet.contains(marketConfigurator)) { - revert AddressIsNotMarketConfiguratorException(marketConfigurator); - } - if (msg.sender != MarketConfigurator(marketConfigurator).admin()) { + if (msg.sender != IMarketConfigurator(marketConfigurator).admin()) { revert CallerIsNotMarketConfiguratorAdminException(msg.sender); } _; } + /// @notice Constructor + /// @param addressProvider_ Address provider contract address constructor(address addressProvider_) DeployerTrait(addressProvider_) {} + // ------- // + // GETTERS // + // ------- // + + /// @notice Returns whether `account` is a registered market configurator function isMarketConfigurator(address account) external view override returns (bool) { return _registeredMarketConfiguratorsSet.contains(account); } + /// @notice Returns all registered market configurators function getMarketConfigurators() external view override returns (address[] memory) { return _registeredMarketConfiguratorsSet.values(); } - function getMarketConfigurator(uint256 index) external view returns (address) { + /// @notice Returns the market configurator at `index` + function getMarketConfigurator(uint256 index) external view override returns (address) { return _registeredMarketConfiguratorsSet.at(index); } - function getNumMarketConfigurators() external view returns (uint256) { + /// @notice Returns the number of registered market configurators + function getNumMarketConfigurators() external view override returns (uint256) { return _registeredMarketConfiguratorsSet.length(); } + /// @notice Returns all shutdown market configurators function getShutdownMarketConfigurators() external view override returns (address[] memory) { return _shutdownMarketConfiguratorsSet.values(); } + // ------------- // + // CONFIGURATION // + // ------------- // + + /// @notice Allows anyone to permissionlessly deploy a new market configurator + /// @param emergencyAdmin Address to set as emergency admin + /// @param adminFeeTreasury Address to set as the second admin of the fee splitter, with the first one being the + /// DAO treasury. If `address(0)`, the splitter is not deployed and all fees will be sent to the treasury. + /// @param curatorName Risk curator name + /// @param deployGovernor If true, a governor contract is deployed and set as market configurator's admin. + /// `msg.sender` is set as its owner, queue and execution admin, while `emergencyAdmin` is set as veto admin. + /// Otherwise, `msg.sender` is set as the admin of the market configurator. + /// @return marketConfigurator Address of the newly deployed market configurator function createMarketConfigurator( address emergencyAdmin, address adminFeeTreasury, @@ -106,6 +115,10 @@ contract MarketConfiguratorFactory is DeployerTrait, IMarketConfiguratorFactory emit CreateMarketConfigurator(marketConfigurator, curatorName); } + /// @notice Allows the admin of `marketConfigurator` to shut it down + /// @dev Reverts if caller is not the admin of `marketConfigurator` + /// @dev Reverts if `marketConfigurator` is not registered or already shutdown + /// @dev Reverts if `marketConfigurator` has non-shutdown pools function shutdownMarketConfigurator(address marketConfigurator) external override @@ -114,22 +127,26 @@ contract MarketConfiguratorFactory is DeployerTrait, IMarketConfiguratorFactory if (!_shutdownMarketConfiguratorsSet.add(marketConfigurator)) { revert MarketConfiguratorIsAlreadyShutdownException(marketConfigurator); } - address contractsRegister = MarketConfigurator(marketConfigurator).contractsRegister(); + if (!_registeredMarketConfiguratorsSet.remove(marketConfigurator)) { + revert MarketConfiguratorIsNotRegisteredException(marketConfigurator); + } + address contractsRegister = IMarketConfigurator(marketConfigurator).contractsRegister(); if (IContractsRegister(contractsRegister).getPools().length != 0) { - revert CantShutdownMarketConfiguratorException(); + revert CantShutdownMarketConfiguratorException(marketConfigurator); } - _registeredMarketConfiguratorsSet.remove(marketConfigurator); emit ShutdownMarketConfigurator(marketConfigurator); } + /// @notice Allows cross-chain governance to register an externally deployed legacy market configurator + /// @dev Reverts if caller is not cross-chain governance + /// @dev Reverts if `marketConfigurator` is already registered or shutdown function addMarketConfigurator(address marketConfigurator) external override onlyCrossChainGovernance { - if (_registeredMarketConfiguratorsSet.contains(marketConfigurator)) { + if (!_registeredMarketConfiguratorsSet.add(marketConfigurator)) { revert MarketConfiguratorIsAlreadyAddedException(marketConfigurator); } if (_shutdownMarketConfiguratorsSet.contains(marketConfigurator)) { revert MarketConfiguratorIsAlreadyShutdownException(marketConfigurator); } - _registeredMarketConfiguratorsSet.add(marketConfigurator); emit CreateMarketConfigurator(marketConfigurator, IMarketConfigurator(marketConfigurator).curatorName()); } } diff --git a/contracts/instance/PriceFeedStore.sol b/contracts/instance/PriceFeedStore.sol index 8329043..c9d352e 100644 --- a/contracts/instance/PriceFeedStore.sol +++ b/contracts/instance/PriceFeedStore.sol @@ -166,7 +166,6 @@ contract PriceFeedStore is bool isExternal = _validatePriceFeedTree(priceFeed); _priceFeedInfo[priceFeed] = PriceFeedInfo({ - author: msg.sender, stalenessPeriod: stalenessPeriod, priceFeedType: isExternal ? bytes32("PRICE_FEED::EXTERNAL") : IPriceFeed(priceFeed).contractType(), version: isExternal ? 0 : IPriceFeed(priceFeed).version(), @@ -176,6 +175,25 @@ contract PriceFeedStore is emit AddPriceFeed(priceFeed, stalenessPeriod, name); } + /// @notice Forbids `priceFeed` for all tokens and removes it from the store + /// @dev Reverts if caller is not owner + /// @dev Reverts if `priceFeed` is not known + function removePriceFeed(address priceFeed) external override onlyOwner { + if (!_knownPriceFeeds.remove(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed); + delete _priceFeedInfo[priceFeed]; + + uint256 numTokens = _knownTokens.length(); + for (uint256 i; i < numTokens; ++i) { + address token = _knownTokens.at(i); + if (_allowedPriceFeeds[token].remove(priceFeed)) { + _allowanceTimestamps[token][priceFeed] = 0; + emit ForbidPriceFeed(token, priceFeed); + } + } + + emit RemovePriceFeed(priceFeed); + } + /// @notice Sets `priceFeed`'s staleness period to `stalenessPeriod` /// @dev Reverts if caller is not owner /// @dev Reverts if `priceFeed` is not known diff --git a/contracts/interfaces/IAddressProvider.sol b/contracts/interfaces/IAddressProvider.sol index 518df13..006d0e2 100644 --- a/contracts/interfaces/IAddressProvider.sol +++ b/contracts/interfaces/IAddressProvider.sol @@ -7,36 +7,41 @@ import {IAddressProvider as IAddressProviderBase} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IAddressProvider.sol"; import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol"; import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol"; - -struct ContractValue { - string key; - address value; - uint256 version; -} +import {AddressProviderEntry} from "./Types.sol"; /// @title Address provider interface interface IAddressProvider is IAddressProviderBase, IVersion, IImmutableOwnableTrait { - event SetAddress(string indexed key, uint256 indexed version, address indexed value); - - function addresses(string memory key, uint256 _version) external view returns (address); - - function getAddressOrRevert(string memory key, uint256 _version) external view returns (address); - - function getAllSavedContracts() external view returns (ContractValue[] memory); - - function getLatestVersion(string memory key) external view returns (uint256); - - function getLatestMinorVersion(string memory key, uint256 majorVersion) external view returns (uint256); - - function getLatestPatchVersion(string memory key, uint256 minorVersion) external view returns (uint256); + // ------ // + // EVENTS // + // ------ // + + event SetAddress(bytes32 indexed key, uint256 indexed ver, address indexed value); + + // ------ // + // ERRORS // + // ------ // + + error AddressNotFoundException(bytes32 key, uint256 ver); + error InvalidVersionException(bytes32 key, uint256 ver); + error VersionNotFoundException(bytes32 key); + error ZeroAddressException(bytes32 key); + + // ------- // + // GETTERS // + // ------- // + + function getAddress(bytes32 key, uint256 ver) external view returns (address); + function getAddressOrRevert(bytes32 key, uint256 ver) external view override returns (address); + function getKeys() external view returns (bytes32[] memory); + function getVersions(bytes32 key) external view returns (uint256[] memory); + function getAllEntries() external view returns (AddressProviderEntry[] memory); + function getLatestVersion(bytes32 key) external view returns (uint256); + function getLatestMinorVersion(bytes32 key, uint256 majorVersion) external view returns (uint256); + function getLatestPatchVersion(bytes32 key, uint256 minorVersion) external view returns (uint256); // ------------- // // CONFIGURATION // // ------------- // - function setAddress(string memory key, address addr, bool saveVersion) external; - function setAddress(bytes32 key, address value, bool saveVersion) external; - - function setAddress(address addr, bool saveVersion) external; } diff --git a/contracts/interfaces/IInstanceManager.sol b/contracts/interfaces/IInstanceManager.sol index e279f25..63db909 100644 --- a/contracts/interfaces/IInstanceManager.sol +++ b/contracts/interfaces/IInstanceManager.sol @@ -5,7 +5,28 @@ pragma solidity ^0.8.23; import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol"; +/// @title Instance manager interface interface IInstanceManager is IVersion { + // ------ // + // EVENTS // + // ------ // + + event SetPendingGovernance(address indexed newGovernance); + event AcceptGovernance(address indexed newGovernance); + + // ------ // + // ERRORS // + // ------ // + + error CallerIsNotCrossChainGovernanceException(address caller); + error CallerIsNotPendingGovernanceException(address caller); + error CallerIsNotTreasuryException(address caller); + error InvalidKeyException(bytes32 key); + + // ------- // + // GETTERS // + // ------- // + function addressProvider() external view returns (address); function bytecodeRepository() external view returns (address); function instanceManagerProxy() external view returns (address); @@ -13,13 +34,19 @@ interface IInstanceManager is IVersion { function crossChainGovernanceProxy() external view returns (address); function isActivated() external view returns (bool); function owner() external view returns (address); + function pendingGovernance() external view returns (address); + + // ------------- // + // CONFIGURATION // + // ------------- // function activate(address instanceOwner, address treasury, address weth, address gear) external; function deploySystemContract(bytes32 contractType, uint256 version, bool saveVersion) external; - function setGlobalAddress(string memory key, address addr, bool saveVersion) external; - function setLocalAddress(string memory key, address addr, bool saveVersion) external; - function setLegacyAddress(string memory key, address addr, bool saveVersion) external; + function setGlobalAddress(bytes32 key, address addr, bool saveVersion) external; + function setLocalAddress(bytes32 key, address addr, bool saveVersion) external; function configureGlobal(address target, bytes calldata data) external; function configureLocal(address target, bytes calldata data) external; function configureTreasury(address target, bytes calldata data) external; + function setPendingGovernance(address newGovernance) external; + function acceptGovernance() external; } diff --git a/contracts/interfaces/IMarketConfiguratorFactory.sol b/contracts/interfaces/IMarketConfiguratorFactory.sol index e59438a..342e390 100644 --- a/contracts/interfaces/IMarketConfiguratorFactory.sol +++ b/contracts/interfaces/IMarketConfiguratorFactory.sol @@ -6,6 +6,7 @@ pragma solidity ^0.8.23; import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol"; import {IDeployerTrait} from "./base/IDeployerTrait.sol"; +/// @title Market configurator factory interface interface IMarketConfiguratorFactory is IVersion, IDeployerTrait { // ------ // // EVENTS // @@ -18,28 +19,33 @@ interface IMarketConfiguratorFactory is IVersion, IDeployerTrait { // ERRORS // // ------ // - error AddressIsNotMarketConfiguratorException(address addr); error CallerIsNotCrossChainGovernanceException(address caller); - error CallerIsNotMarketConfiguratorException(address caller); error CallerIsNotMarketConfiguratorAdminException(address caller); - error CantShutdownMarketConfiguratorException(); + error CantShutdownMarketConfiguratorException(address marketConfigurator); error MarketConfiguratorIsAlreadyAddedException(address marketConfigurator); error MarketConfiguratorIsAlreadyShutdownException(address marketConfigruator); + error MarketConfiguratorIsNotRegisteredException(address marketConfigurator); - function isMarketConfigurator(address account) external view returns (bool); + // ------- // + // GETTERS // + // ------- // + function isMarketConfigurator(address account) external view returns (bool); function getMarketConfigurators() external view returns (address[] memory); - + function getMarketConfigurator(uint256 index) external view returns (address); + function getNumMarketConfigurators() external view returns (uint256); function getShutdownMarketConfigurators() external view returns (address[] memory); + // ------------- // + // CONFIGURATION // + // ------------- // + function createMarketConfigurator( address emergencyAdmin, address adminFeeTreasury, string calldata curatorName, bool deployGovernor ) external returns (address marketConfigurator); - function shutdownMarketConfigurator(address marketConfigurator) external; - function addMarketConfigurator(address marketConfigurator) external; } diff --git a/contracts/interfaces/IPriceFeedStore.sol b/contracts/interfaces/IPriceFeedStore.sol index 5f0ab07..f6d4804 100644 --- a/contracts/interfaces/IPriceFeedStore.sol +++ b/contracts/interfaces/IPriceFeedStore.sol @@ -46,6 +46,9 @@ interface IPriceFeedStore is IPriceFeedStoreBase, IVersion, IDeployerTrait, IImm /// @notice Emitted when a new price feed is added to the store event AddPriceFeed(address indexed priceFeed, uint32 stalenessPeriod, string name); + /// @notice Emitted when a price feed is removed from the store + event RemovePriceFeed(address indexed priceFeed); + /// @notice Emitted when the staleness period is set for a price feed event SetStalenessPeriod(address indexed priceFeed, uint32 stalenessPeriod); @@ -79,6 +82,7 @@ interface IPriceFeedStore is IPriceFeedStoreBase, IVersion, IDeployerTrait, IImm // ------------- // function addPriceFeed(address priceFeed, uint32 stalenessPeriod, string calldata name) external; + function removePriceFeed(address priceFeed) external; function setStalenessPeriod(address priceFeed, uint32 stalenessPeriod) external; function allowPriceFeed(address token, address priceFeed) external; function forbidPriceFeed(address token, address priceFeed) external; diff --git a/contracts/interfaces/Types.sol b/contracts/interfaces/Types.sol index 7230434..83fb7f2 100644 --- a/contracts/interfaces/Types.sol +++ b/contracts/interfaces/Types.sol @@ -3,6 +3,12 @@ // (c) Gearbox Foundation, 2024. pragma solidity ^0.8.23; +struct AddressProviderEntry { + bytes32 key; + uint256 ver; + address value; +} + struct Call { address target; bytes callData; @@ -46,7 +52,6 @@ struct MarketFactories { } struct PriceFeedInfo { - address author; string name; uint32 stalenessPeriod; bytes32 priceFeedType; @@ -58,14 +63,6 @@ struct ConnectedPriceFeed { address[] priceFeeds; } -// The `BytecodeInfoMeta` struct holds metadata about a bytecode in BytecodeRepository -// -// - `author`: A person who first upload smart-contract to BCR -// - `contractType`: A bytes32 identifier representing the type of the contract. -// - `version`: A uint256 indicating the version of the contract. -// - `sources`: An array of `Source` structs, each containing a comment and a link related to the contract's source. -// - `auditors`: An array of addresses representing the auditors who have reviewed the contract. -// - `reports`: An array of `SecurityReport` structs, each containing information about security audits conducted on the contract. struct Bytecode { bytes32 contractType; uint256 version; diff --git a/contracts/libraries/ContractLiterals.sol b/contracts/libraries/ContractLiterals.sol index 23dfd7e..df0dcb1 100644 --- a/contracts/libraries/ContractLiterals.sol +++ b/contracts/libraries/ContractLiterals.sol @@ -5,80 +5,63 @@ pragma solidity ^0.8.23; uint256 constant NO_VERSION_CONTROL = 0; +// Contract types and prefixes +bytes32 constant AP_ACCOUNT_FACTORY_DEFAULT = "ACCOUNT_FACTORY::DEFAULT"; bytes32 constant AP_ACL = "ACL"; -bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER"; -bytes32 constant AP_GOVERNOR = "GOVERNOR"; -bytes32 constant AP_TREASURY_SPLITTER = "TREASURY_SPLITTER"; - bytes32 constant AP_ADDRESS_PROVIDER = "ADDRESS_PROVIDER"; +bytes32 constant AP_BOT_LIST = "BOT_LIST"; +bytes32 constant AP_BYTECODE_REPOSITORY = "BYTECODE_REPOSITORY"; +bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER"; +bytes32 constant AP_CREDIT_CONFIGURATOR = "CREDIT_CONFIGURATOR"; +bytes32 constant AP_CREDIT_FACADE = "CREDIT_FACADE"; +bytes32 constant AP_CREDIT_FACTORY = "CREDIT_FACTORY"; +bytes32 constant AP_CREDIT_MANAGER = "CREDIT_MANAGER"; bytes32 constant AP_CROSS_CHAIN_GOVERNANCE = "CROSS_CHAIN_GOVERNANCE"; +bytes32 constant AP_CROSS_CHAIN_GOVERNANCE_PROXY = "CROSS_CHAIN_GOVERNANCE_PROXY"; bytes32 constant AP_CROSS_CHAIN_MULTISIG = "CROSS_CHAIN_MULTISIG"; +bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING"; +bytes32 constant AP_GEAR_TOKEN = "GEAR_TOKEN"; +bytes32 constant AP_GOVERNOR = "GOVERNOR"; bytes32 constant AP_INSTANCE_MANAGER = "INSTANCE_MANAGER"; - -// PROXIES bytes32 constant AP_INSTANCE_MANAGER_PROXY = "INSTANCE_MANAGER_PROXY"; -bytes32 constant AP_TREASURY_PROXY = "TREASURY_PROXY"; -bytes32 constant AP_CROSS_CHAIN_GOVERNANCE_PROXY = "CROSS_CHAIN_GOVERNANCE_PROXY"; - -bytes32 constant AP_POOL = "POOL"; -bytes32 constant AP_POOL_QUOTA_KEEPER = "POOL_QUOTA_KEEPER"; -bytes32 constant AP_INTEREST_RATE_MODEL_LINEAR = "IRM::LINEAR"; bytes32 constant AP_INTEREST_RATE_MODEL_DEFAULT = "IRM::DEFAULT"; -bytes32 constant AP_RATE_KEEPER_TUMBLER = "RATE_KEEPER::TUMBLER"; -bytes32 constant AP_RATE_KEEPER_GAUGE = "RATE_KEEPER::GAUGE"; +bytes32 constant AP_INTEREST_RATE_MODEL_FACTORY = "INTEREST_RATE_MODEL_FACTORY"; +bytes32 constant AP_INTEREST_RATE_MODEL_LINEAR = "IRM::LINEAR"; +bytes32 constant AP_LOSS_POLICY_ALIASED = "LOSS_POLICY::ALIASED"; bytes32 constant AP_LOSS_POLICY_DEFAULT = "LOSS_POLICY::DEFAULT"; -bytes32 constant AP_ACCOUNT_FACTORY_DEFAULT = "ACCOUNT_FACTORY::DEFAULT"; -bytes32 constant AP_ZERO_PRICE_FEED = "PRICE_FEED::ZERO"; - -bytes32 constant AP_CREDIT_MANAGER = "CREDIT_MANAGER"; -bytes32 constant AP_CREDIT_FACADE = "CREDIT_FACADE"; -bytes32 constant AP_CREDIT_CONFIGURATOR = "CREDIT_CONFIGURATOR"; - -bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE"; - -bytes32 constant AP_TREASURY = "TREASURY"; -bytes32 constant AP_GEAR_TOKEN = "GEAR_TOKEN"; -bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN"; -bytes32 constant AP_ROUTER = "ROUTER"; -bytes32 constant AP_BOT_LIST = "BOT_LIST"; -bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING"; -bytes32 constant AP_ZAPPER_REGISTER = "ZAPPER_REGISTER"; - -bytes32 constant AP_INFLATION_ATTACK_BLOCKER = "INFLATION_ATTACK_BLOCKER"; -bytes32 constant AP_DEGEN_DISTRIBUTOR = "DEGEN_DISTRIBUTOR"; -bytes32 constant AP_MULTI_PAUSE = "MULTI_PAUSE"; - -bytes32 constant AP_BYTECODE_REPOSITORY = "BYTECODE_REPOSITORY"; -bytes32 constant AP_PRICE_FEED_STORE = "PRICE_FEED_STORE"; - -bytes32 constant AP_DEGEN_NFT = "DEGEN_NFT"; +bytes32 constant AP_LOSS_POLICY_FACTORY = "LOSS_POLICY_FACTORY"; bytes32 constant AP_MARKET_CONFIGURATOR = "MARKET_CONFIGURATOR"; +bytes32 constant AP_MARKET_CONFIGURATOR_FACTORY = "MARKET_CONFIGURATOR_FACTORY"; bytes32 constant AP_MARKET_CONFIGURATOR_LEGACY = "MARKET_CONFIGURATOR::LEGACY"; - +bytes32 constant AP_POOL = "POOL"; bytes32 constant AP_POOL_FACTORY = "POOL_FACTORY"; -bytes32 constant AP_CREDIT_FACTORY = "CREDIT_FACTORY"; -bytes32 constant AP_INTEREST_RATE_MODEL_FACTORY = "INTEREST_RATE_MODEL_FACTORY"; +bytes32 constant AP_POOL_QUOTA_KEEPER = "POOL_QUOTA_KEEPER"; +bytes32 constant AP_PRICE_FEED_STORE = "PRICE_FEED_STORE"; +bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE"; bytes32 constant AP_PRICE_ORACLE_FACTORY = "PRICE_ORACLE_FACTORY"; bytes32 constant AP_RATE_KEEPER_FACTORY = "RATE_KEEPER_FACTORY"; -bytes32 constant AP_LOSS_POLICY_FACTORY = "LOSS_POLICY_FACTORY"; -bytes32 constant AP_MARKET_CONFIGURATOR_FACTORY = "MARKET_CONFIGURATOR_FACTORY"; +bytes32 constant AP_RATE_KEEPER_GAUGE = "RATE_KEEPER::GAUGE"; +bytes32 constant AP_RATE_KEEPER_TUMBLER = "RATE_KEEPER::TUMBLER"; +bytes32 constant AP_TREASURY = "TREASURY"; +bytes32 constant AP_TREASURY_PROXY = "TREASURY_PROXY"; +bytes32 constant AP_TREASURY_SPLITTER = "TREASURY_SPLITTER"; +bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN"; +bytes32 constant AP_ZERO_PRICE_FEED = "PRICE_FEED::ZERO"; +// Common domains bytes32 constant DOMAIN_ACCOUNT_FACTORY = "ACCOUNT_FACTORY"; +bytes32 constant DOMAIN_ADAPTER = "ADAPTER"; bytes32 constant DOMAIN_BOT = "BOT"; -bytes32 constant DOMAIN_POOL = "POOL"; bytes32 constant DOMAIN_CREDIT_MANAGER = "CREDIT_MANAGER"; -bytes32 constant DOMAIN_ADAPTER = "ADAPTER"; bytes32 constant DOMAIN_DEGEN_NFT = "DEGEN_NFT"; +bytes32 constant DOMAIN_IRM = "IRM"; bytes32 constant DOMAIN_LOSS_POLICY = "LOSS_POLICY"; -bytes32 constant DOMAIN_RATE_KEEPER = "RATE_KEEPER"; +bytes32 constant DOMAIN_POOL = "POOL"; bytes32 constant DOMAIN_PRICE_FEED = "PRICE_FEED"; -bytes32 constant DOMAIN_IRM = "IRM"; +bytes32 constant DOMAIN_RATE_KEEPER = "RATE_KEEPER"; bytes32 constant DOMAIN_ZAPPER = "ZAPPER"; -// ----- // -// ROLES // -// ----- // - +// Roles bytes32 constant ROLE_EMERGENCY_LIQUIDATOR = "EMERGENCY_LIQUIDATOR"; bytes32 constant ROLE_PAUSABLE_ADMIN = "PAUSABLE_ADMIN"; bytes32 constant ROLE_UNPAUSABLE_ADMIN = "UNPAUSABLE_ADMIN"; diff --git a/contracts/market/MarketConfigurator.sol b/contracts/market/MarketConfigurator.sol index 8646a91..ecdb97a 100644 --- a/contracts/market/MarketConfigurator.sol +++ b/contracts/market/MarketConfigurator.sol @@ -786,9 +786,7 @@ contract MarketConfigurator is DeployerTrait, IMarketConfigurator { } function _getLatestPatch(bytes32 key, uint256 minorVersion) internal view returns (address) { - return _getAddressOrRevert( - key, IAddressProvider(addressProvider).getLatestPatchVersion(key.fromSmallString(), minorVersion) - ); + return _getAddressOrRevert(key, IAddressProvider(addressProvider).getLatestPatchVersion(key, minorVersion)); } function _getLatestPatch(address factory) internal view returns (address) { diff --git a/contracts/market/legacy/MarketConfiguratorLegacy.sol b/contracts/market/legacy/MarketConfiguratorLegacy.sol index 258edb9..465baaf 100644 --- a/contracts/market/legacy/MarketConfiguratorLegacy.sol +++ b/contracts/market/legacy/MarketConfiguratorLegacy.sol @@ -22,7 +22,6 @@ import {Call, MarketFactories} from "../../interfaces/Types.sol"; import { AP_MARKET_CONFIGURATOR_LEGACY, AP_CROSS_CHAIN_GOVERNANCE_PROXY, - DOMAIN_BOT, DOMAIN_ZAPPER, NO_VERSION_CONTROL, ROLE_EMERGENCY_LIQUIDATOR, @@ -58,6 +57,11 @@ interface IZapperRegisterLegacy { function zappers(address pool) external view returns (address[] memory); } +struct PeripheryContract { + bytes32 domain; + address addr; +} + struct LegacyParams { address acl; address contractsRegister; @@ -66,7 +70,7 @@ struct LegacyParams { address[] pausableAdmins; address[] unpausableAdmins; address[] emergencyLiquidators; - address[] bots; + PeripheryContract[] peripheryContracts; } contract MarketConfiguratorLegacy is MarketConfigurator { @@ -187,10 +191,11 @@ contract MarketConfiguratorLegacy is MarketConfigurator { } } - uint256 numBots = legacyParams_.bots.length; - for (uint256 i; i < numBots; ++i) { - _peripheryContracts[DOMAIN_BOT].add(legacyParams_.bots[i]); - emit AddPeripheryContract(DOMAIN_BOT, legacyParams_.bots[i]); + uint256 numPeripheryContracts = legacyParams_.peripheryContracts.length; + for (uint256 i; i < numPeripheryContracts; ++i) { + PeripheryContract memory pc = legacyParams_.peripheryContracts[i]; + _peripheryContracts[pc.domain].add(pc.addr); + emit AddPeripheryContract(pc.domain, pc.addr); } } @@ -269,6 +274,12 @@ contract MarketConfiguratorLegacy is MarketConfigurator { gearStakingLegacy.functionCall(data); } + function removeLegacyPeripheryContract(bytes32 domain, address peripheryContract) external onlyAdmin { + if (_peripheryContracts[domain].remove(peripheryContract)) { + emit RemovePeripheryContract(domain, peripheryContract); + } + } + // --------- // // INTERNALS // // --------- // diff --git a/contracts/test/configuration/MarketConfigurator.unit.t.sol b/contracts/test/configuration/MarketConfigurator.unit.t.sol index 757b006..7bcfad4 100644 --- a/contracts/test/configuration/MarketConfigurator.unit.t.sol +++ b/contracts/test/configuration/MarketConfigurator.unit.t.sol @@ -471,7 +471,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(newFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("POOL_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(newFactory, true); + IAddressProvider(addressProvider).setAddress("POOL_FACTORY", newFactory, true); // Expect factory authorization changes vm.expectEmit(true, true, true, true); @@ -507,7 +507,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(patchFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("POOL_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(patchFactory, true); + IAddressProvider(addressProvider).setAddress("POOL_FACTORY", patchFactory, true); vm.prank(admin); marketConfigurator.upgradePoolFactory(address(pool)); @@ -531,7 +531,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(newFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("CREDIT_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(newFactory, true); + IAddressProvider(addressProvider).setAddress("CREDIT_FACTORY", newFactory, true); // Expect factory authorization changes vm.expectEmit(true, true, true, true); @@ -567,7 +567,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(patchFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("CREDIT_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(patchFactory, true); + IAddressProvider(addressProvider).setAddress("CREDIT_FACTORY", patchFactory, true); vm.prank(admin); marketConfigurator.upgradeCreditFactory(address(creditManager)); @@ -591,7 +591,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(newFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("PRICE_ORACLE_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(newFactory, true); + IAddressProvider(addressProvider).setAddress("PRICE_ORACLE_FACTORY", newFactory, true); // Expect factory authorization changes vm.expectEmit(true, true, true, true); @@ -624,7 +624,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { ); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(patchFactory, true); + IAddressProvider(addressProvider).setAddress("PRICE_ORACLE_FACTORY", patchFactory, true); vm.prank(admin); marketConfigurator.upgradePriceOracleFactory(address(pool)); @@ -653,7 +653,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { ); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(newFactory, true); + IAddressProvider(addressProvider).setAddress("INTEREST_RATE_MODEL_FACTORY", newFactory, true); // Expect factory authorization changes vm.expectEmit(true, true, true, true); @@ -686,7 +686,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { ); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(patchFactory, true); + IAddressProvider(addressProvider).setAddress("INTEREST_RATE_MODEL_FACTORY", patchFactory, true); vm.prank(admin); marketConfigurator.upgradeInterestRateModelFactory(address(pool)); @@ -714,7 +714,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(newFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("RATE_KEEPER_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(newFactory, true); + IAddressProvider(addressProvider).setAddress("RATE_KEEPER_FACTORY", newFactory, true); // Expect factory authorization changes vm.expectEmit(true, true, true, true); @@ -745,7 +745,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(patchFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("RATE_KEEPER_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(patchFactory, true); + IAddressProvider(addressProvider).setAddress("RATE_KEEPER_FACTORY", patchFactory, true); vm.prank(admin); marketConfigurator.upgradeRateKeeperFactory(address(pool)); @@ -771,7 +771,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(newFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("LOSS_POLICY_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(newFactory, true); + IAddressProvider(addressProvider).setAddress("LOSS_POLICY_FACTORY", newFactory, true); // Expect factory authorization changes vm.expectEmit(true, true, true, true); @@ -802,7 +802,7 @@ contract MarketConfiguratorUnitTest is ConfigurationTestHelper { vm.mockCall(patchFactory, abi.encodeWithSignature("contractType()"), abi.encode(bytes32("LOSS_POLICY_FACTORY"))); vm.prank(Ownable(addressProvider).owner()); - IAddressProvider(addressProvider).setAddress(patchFactory, true); + IAddressProvider(addressProvider).setAddress("LOSS_POLICY_FACTORY", patchFactory, true); vm.prank(admin); marketConfigurator.upgradeLossPolicyFactory(address(pool)); diff --git a/contracts/test/global/AddressProvider.unit.t.sol b/contracts/test/global/AddressProvider.unit.t.sol new file mode 100644 index 0000000..5ea1c08 --- /dev/null +++ b/contracts/test/global/AddressProvider.unit.t.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import {Test} from "forge-std/Test.sol"; +import {Vm} from "forge-std/Vm.sol"; + +import {AddressProvider} from "../../instance/AddressProvider.sol"; +import {IAddressProvider} from "../../interfaces/IAddressProvider.sol"; +import {AddressProviderEntry} from "../../interfaces/Types.sol"; +import {IImmutableOwnableTrait} from "../../interfaces/base/IImmutableOwnableTrait.sol"; +import {AP_ADDRESS_PROVIDER, NO_VERSION_CONTROL} from "../../libraries/ContractLiterals.sol"; + +contract AddressProviderTest is Test { + AddressProvider public provider; + address public owner; + + function setUp() public { + owner = makeAddr("owner"); + provider = new AddressProvider(owner); + } + + /// @notice Test constructor sets up initial state correctly + function test_U_AP_01_constructor_sets_initial_state() public view { + assertEq(provider.owner(), owner); + assertEq(provider.getKeys().length, 0); + } + + /// @notice Test address setting functionality + function test_U_AP_02_setAddress_works() public { + bytes32 key = "TEST"; + address value = makeAddr("test"); + + // Test it reverts if not owner + address notOwner = makeAddr("notOwner"); + vm.prank(notOwner); + vm.expectRevert(abi.encodeWithSelector(IImmutableOwnableTrait.CallerIsNotOwnerException.selector, notOwner)); + provider.setAddress(key, value, false); + + // Test it reverts if zero address + vm.prank(owner); + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.ZeroAddressException.selector, key)); + provider.setAddress(key, address(0), false); + + // Test successful address setting without version + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit IAddressProvider.SetAddress(key, NO_VERSION_CONTROL, value); + provider.setAddress(key, value, false); + + assertEq(provider.getAddress(key, NO_VERSION_CONTROL), value); + assertEq(provider.getAddressOrRevert(key, NO_VERSION_CONTROL), value); + + // Test successful address setting with version + address versionedValue = makeAddr("versioned"); + uint256 version = 310; + vm.mockCall(versionedValue, abi.encodeWithSignature("version()"), abi.encode(version)); + + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit IAddressProvider.SetAddress(key, version, versionedValue); + provider.setAddress(key, versionedValue, true); + + assertEq(provider.getAddress(key, version), versionedValue); + assertEq(provider.getAddressOrRevert(key, version), versionedValue); + + // Test setting same address twice doesn't emit event + vm.recordLogs(); + vm.prank(owner); + provider.setAddress(key, versionedValue, true); + + // Verify no SetAddress event was emitted + Vm.Log[] memory logs = vm.getRecordedLogs(); + assertEq(logs.length, 0); + + // Test it reverts if version is less than 100 + address invalidVersionValue = makeAddr("invalidVersion"); + vm.mockCall(invalidVersionValue, abi.encodeWithSignature("version()"), abi.encode(99)); + vm.prank(owner); + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.InvalidVersionException.selector, key, 99)); + provider.setAddress(key, invalidVersionValue, true); + } + + /// @notice Test version tracking functionality + function test_U_AP_03_version_tracking_works() public { + bytes32 key = "TEST"; + uint256[] memory versions = new uint256[](6); + versions[0] = 310; // 3.1.0 + versions[1] = 311; // 3.1.1 + versions[2] = 320; // 3.2.0 + versions[3] = 321; // 3.2.1 + versions[4] = 400; // 4.0.0 + versions[5] = 401; // 4.0.1 + + // Add addresses with different versions + for (uint256 i = 0; i < versions.length; i++) { + address value = makeAddr(string(abi.encode(versions[i]))); + vm.mockCall(value, abi.encodeWithSignature("version()"), abi.encode(versions[i])); + vm.prank(owner); + provider.setAddress(key, value, true); + } + + // Test latest version tracking + assertEq(provider.getLatestVersion(key), 401); + + // Test latest minor version tracking + assertEq(provider.getLatestMinorVersion(key, 300), 321); // Latest 3.x.x is 3.2.1 + assertEq(provider.getLatestMinorVersion(key, 400), 401); // Latest 4.x.x is 4.0.1 + + // Test latest patch version tracking + assertEq(provider.getLatestPatchVersion(key, 310), 311); // Latest 3.1.x is 3.1.1 + assertEq(provider.getLatestPatchVersion(key, 320), 321); // Latest 3.2.x is 3.2.1 + assertEq(provider.getLatestPatchVersion(key, 400), 401); // Latest 4.0.x is 4.0.1 + + // Test version not found cases + bytes32 unknownKey = "UNKNOWN"; + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.VersionNotFoundException.selector, unknownKey)); + provider.getLatestVersion(unknownKey); + + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.VersionNotFoundException.selector, key)); + provider.getLatestMinorVersion(key, 500); // No 5.x.x versions + + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.VersionNotFoundException.selector, key)); + provider.getLatestPatchVersion(key, 330); // No 3.3.x versions + + // Test invalid version cases + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.InvalidVersionException.selector, key, 99)); + provider.getLatestMinorVersion(key, 99); // Version < 100 + + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.InvalidVersionException.selector, key, 99)); + provider.getLatestPatchVersion(key, 99); // Version < 100 + } + + /// @notice Test getters functionality + function test_U_AP_04_getters_work() public { + bytes32 key = "TEST"; + uint256 version = 310; + address value = makeAddr("test"); + vm.mockCall(value, abi.encodeWithSignature("version()"), abi.encode(version)); + address noVersionValue = makeAddr("noVersion"); + + // Test getAddress returns zero for non-existent entry + assertEq(provider.getAddress(key, version), address(0)); + + // Test getAddressOrRevert reverts for non-existent entry + vm.expectRevert(abi.encodeWithSelector(IAddressProvider.AddressNotFoundException.selector, key, version)); + provider.getAddressOrRevert(key, version); + + // Add some entries + vm.startPrank(owner); + provider.setAddress(key, value, true); + provider.setAddress(key, noVersionValue, false); + vm.stopPrank(); + + // Test getKeys + bytes32[] memory keys = provider.getKeys(); + assertEq(keys.length, 1); + assertEq(keys[0], key); + + // Test getVersions + uint256[] memory versions = provider.getVersions(key); + assertEq(versions.length, 2); // 310 + NO_VERSION_CONTROL + assertTrue( + (versions[0] == version && versions[1] == NO_VERSION_CONTROL) + || (versions[0] == NO_VERSION_CONTROL && versions[1] == version) + ); + + // Test getAllEntries + AddressProviderEntry[] memory entries = provider.getAllEntries(); + assertEq(entries.length, 2); // 310 + NO_VERSION_CONTROL + + // Find and verify versioned entry + bool foundVersioned = false; + bool foundNoVersion = false; + for (uint256 i = 0; i < entries.length; i++) { + if (entries[i].ver == version) { + assertEq(entries[i].key, key); + assertEq(entries[i].value, value); + foundVersioned = true; + } else if (entries[i].ver == NO_VERSION_CONTROL) { + assertEq(entries[i].key, key); + assertEq(entries[i].value, noVersionValue); + foundNoVersion = true; + } + } + assertTrue(foundVersioned, "Versioned entry not found"); + assertTrue(foundNoVersion, "Non-versioned entry not found"); + } +} diff --git a/contracts/test/global/InstanceManager.unit.t.sol b/contracts/test/global/InstanceManager.unit.t.sol index aec7aca..b5f327b 100644 --- a/contracts/test/global/InstanceManager.unit.t.sol +++ b/contracts/test/global/InstanceManager.unit.t.sol @@ -2,20 +2,23 @@ pragma solidity ^0.8.23; import {Test} from "forge-std/Test.sol"; -import {InstanceManager} from "../../instance/InstanceManager.sol"; -import {IAddressProvider} from "../../interfaces/IAddressProvider.sol"; + import {BytecodeRepository} from "../../global/BytecodeRepository.sol"; import {ProxyCall} from "../../helpers/ProxyCall.sol"; +import {InstanceManager} from "../../instance/InstanceManager.sol"; +import {IAddressProvider} from "../../interfaces/IAddressProvider.sol"; +import {IInstanceManager} from "../../interfaces/IInstanceManager.sol"; import { - AP_INSTANCE_MANAGER, - AP_CROSS_CHAIN_GOVERNANCE, - AP_TREASURY, - AP_BYTECODE_REPOSITORY, AP_ADDRESS_PROVIDER, - AP_INSTANCE_MANAGER_PROXY, + AP_BYTECODE_REPOSITORY, + AP_CROSS_CHAIN_GOVERNANCE, AP_CROSS_CHAIN_GOVERNANCE_PROXY, - AP_TREASURY_PROXY, + AP_GEAR_STAKING, AP_GEAR_TOKEN, + AP_INSTANCE_MANAGER, + AP_INSTANCE_MANAGER_PROXY, + AP_TREASURY, + AP_TREASURY_PROXY, AP_WETH_TOKEN, NO_VERSION_CONTROL } from "../../libraries/ContractLiterals.sol"; @@ -23,122 +26,330 @@ import { contract InstanceManagerTest is Test { InstanceManager public manager; address public owner; - address public treasury; address public crossChainGovernance; + address public treasury; address public weth; address public gear; IAddressProvider public addressProvider; function setUp() public { owner = makeAddr("owner"); + crossChainGovernance = makeAddr("crossChainGovernance"); treasury = makeAddr("treasury"); weth = makeAddr("weth"); gear = makeAddr("gear"); - manager = new InstanceManager(owner); + // Deploy with cross-chain governance as initial owner + manager = new InstanceManager(crossChainGovernance); addressProvider = IAddressProvider(manager.addressProvider()); + + // Activate instance and transfer ownership to instance owner + vm.prank(crossChainGovernance); + manager.activate(owner, treasury, weth, gear); } /// @notice Test constructor sets up initial state correctly - function test_IM_01_constructor_sets_initial_state() public view { + function test_U_IM_01_constructor_sets_initial_state() public { + // Create new non-activated instance + InstanceManager newManager = new InstanceManager(crossChainGovernance); + IAddressProvider newAddressProvider = IAddressProvider(newManager.addressProvider()); + // Verify proxies were created - assertTrue(manager.instanceManagerProxy() != address(0)); - assertTrue(manager.treasuryProxy() != address(0)); - assertTrue(manager.crossChainGovernanceProxy() != address(0)); + assertTrue(newManager.instanceManagerProxy() != address(0)); + assertTrue(newManager.treasuryProxy() != address(0)); + assertTrue(newManager.crossChainGovernanceProxy() != address(0)); // Verify initial addresses were set assertEq( - addressProvider.getAddressOrRevert(AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL), manager.bytecodeRepository() + newAddressProvider.getAddressOrRevert(AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL), + newManager.bytecodeRepository() ); - assertEq(addressProvider.getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL), owner); assertEq( - addressProvider.getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL), - manager.instanceManagerProxy() + newAddressProvider.getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL), crossChainGovernance ); - assertEq(addressProvider.getAddressOrRevert(AP_TREASURY_PROXY, NO_VERSION_CONTROL), manager.treasuryProxy()); assertEq( - addressProvider.getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE_PROXY, NO_VERSION_CONTROL), - manager.crossChainGovernanceProxy() + newAddressProvider.getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL), + newManager.instanceManagerProxy() ); + assertEq( + newAddressProvider.getAddressOrRevert(AP_TREASURY_PROXY, NO_VERSION_CONTROL), newManager.treasuryProxy() + ); + assertEq( + newAddressProvider.getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE_PROXY, NO_VERSION_CONTROL), + newManager.crossChainGovernanceProxy() + ); + assertEq(newAddressProvider.getAddressOrRevert(AP_INSTANCE_MANAGER, NO_VERSION_CONTROL), address(newManager)); // Verify ownership - assertEq(manager.owner(), owner); - } + assertEq(newManager.owner(), crossChainGovernance); - /// @notice Test activation sets up remaining addresses - function test_IM_02_activate_sets_remaining_addresses() public { - vm.prank(owner); - manager.activate(owner, treasury, weth, gear); - - assertTrue(manager.isActivated()); - assertEq(addressProvider.getAddressOrRevert(AP_INSTANCE_MANAGER, NO_VERSION_CONTROL), address(manager)); - assertEq(addressProvider.getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL), treasury); - assertEq(addressProvider.getAddressOrRevert(AP_WETH_TOKEN, NO_VERSION_CONTROL), weth); - assertEq(addressProvider.getAddressOrRevert(AP_GEAR_TOKEN, NO_VERSION_CONTROL), gear); + // Verify not activated + assertFalse(newManager.isActivated()); } - /// @notice Test only owner can activate - function test_IM_03_activate_only_owner() public { - vm.prank(makeAddr("notOwner")); + /// @notice Test activation functionality + function test_U_IM_02_activate_works() public { + // Create new non-activated instance + InstanceManager newManager = new InstanceManager(crossChainGovernance); + IAddressProvider newAddressProvider = IAddressProvider(newManager.addressProvider()); + + // Test it reverts if not governance + vm.prank(makeAddr("notGovernance")); vm.expectRevert("Ownable: caller is not the owner"); - manager.activate(owner, treasury, weth, gear); - } + newManager.activate(owner, treasury, weth, gear); - /// @notice Test can't activate twice - function test_IM_04_cant_activate_twice() public { - vm.startPrank(owner); + // Test successful activation + vm.prank(crossChainGovernance); + newManager.activate(owner, treasury, weth, gear); - manager.activate(owner, treasury, weth, gear); + assertTrue(newManager.isActivated()); + assertEq(newManager.owner(), owner); + assertEq(newAddressProvider.getAddressOrRevert(AP_INSTANCE_MANAGER, NO_VERSION_CONTROL), address(newManager)); + assertEq(newAddressProvider.getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL), treasury); + assertEq(newAddressProvider.getAddressOrRevert(AP_WETH_TOKEN, NO_VERSION_CONTROL), weth); + assertEq(newAddressProvider.getAddressOrRevert(AP_GEAR_TOKEN, NO_VERSION_CONTROL), gear); - // Second activation should not change state + // Test it can't be activated twice address newTreasury = makeAddr("newTreasury"); - manager.activate(owner, newTreasury, weth, gear); - - assertEq(addressProvider.getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL), treasury); - vm.stopPrank(); + vm.prank(owner); + newManager.activate(owner, newTreasury, weth, gear); + assertEq(newAddressProvider.getAddressOrRevert(AP_TREASURY, NO_VERSION_CONTROL), treasury); } - /// @notice Test setting global address requires correct prefix - function test_IM_05_setGlobalAddress_requires_prefix() public { - vm.mockCall( - addressProvider.getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL), - abi.encodeWithSignature("getAddress(bytes32)"), - abi.encode(msg.sender) + /// @notice Test address setting functionality + function test_U_IM_03_setAddress_functions_work() public { + bytes32 globalKey = "GLOBAL::TEST"; + bytes32 localKey = "LOCAL::TEST"; + address testAddr = makeAddr("testAddr"); + + // Test setGlobalAddress + // Test it reverts if not governance + address notGovernance = makeAddr("notGovernance"); + vm.prank(notGovernance); + vm.expectRevert( + abi.encodeWithSelector(IInstanceManager.CallerIsNotCrossChainGovernanceException.selector, notGovernance) ); + manager.setGlobalAddress(globalKey, testAddr, false); + + // Test it reverts if key doesn't have correct prefix + vm.prank(crossChainGovernance); + vm.expectRevert(abi.encodeWithSelector(IInstanceManager.InvalidKeyException.selector, bytes32("INVALID"))); + manager.setGlobalAddress("INVALID", testAddr, false); + + // Test successful global address setting + vm.prank(crossChainGovernance); + manager.setGlobalAddress(globalKey, testAddr, false); + assertEq(addressProvider.getAddressOrRevert(globalKey, NO_VERSION_CONTROL), testAddr); + + // Test setLocalAddress + // Test it reverts if not governance + vm.prank(makeAddr("notOwner")); + vm.expectRevert("Ownable: caller is not the owner"); + manager.setLocalAddress(localKey, testAddr, false); + // Test it reverts if key doesn't have correct prefix vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(InstanceManager.InvalidKeyException.selector, "INVALID")); - manager.setGlobalAddress("INVALID", address(0), false); + vm.expectRevert(abi.encodeWithSelector(IInstanceManager.InvalidKeyException.selector, bytes32("INVALID"))); + manager.setLocalAddress("INVALID", testAddr, false); + + // Test successful local address setting + vm.prank(owner); + manager.setLocalAddress(localKey, testAddr, false); + assertEq(addressProvider.getAddressOrRevert(localKey, NO_VERSION_CONTROL), testAddr); } - /// @notice Test setting local address requires correct prefix - function test_IM_06_setLocalAddress_requires_prefix() public { + /// @notice Test configuration functionality + function test_U_IM_04_configure_functions_work() public { + address target = makeAddr("target"); + bytes memory data = "test"; + vm.mockCall(target, data, ""); + + // Test configureGlobal + // Test it reverts if not governance + address notGovernance = makeAddr("notGovernance"); + vm.expectRevert( + abi.encodeWithSelector(IInstanceManager.CallerIsNotCrossChainGovernanceException.selector, notGovernance) + ); + vm.prank(notGovernance); + manager.configureGlobal(target, data); + + // Test successful global configuration + vm.expectCall(manager.crossChainGovernanceProxy(), abi.encodeCall(ProxyCall.proxyCall, (target, data))); + vm.prank(crossChainGovernance); + manager.configureGlobal(target, data); + + // Test configureLocal + // Test it reverts if not governance + vm.prank(makeAddr("notOwner")); + vm.expectRevert("Ownable: caller is not the owner"); + manager.configureLocal(target, data); + + // Test successful local configuration + vm.expectCall(manager.instanceManagerProxy(), abi.encodeCall(ProxyCall.proxyCall, (target, data))); vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(InstanceManager.InvalidKeyException.selector, "INVALID")); - manager.setLocalAddress("INVALID", address(0), false); + manager.configureLocal(target, data); + + // Test configureTreasury + // Test it reverts if not treasury + address notTreasury = makeAddr("notTreasury"); + vm.expectRevert(abi.encodeWithSelector(IInstanceManager.CallerIsNotTreasuryException.selector, notTreasury)); + vm.prank(notTreasury); + manager.configureTreasury(target, data); + + // Test successful treasury configuration + vm.expectCall(manager.treasuryProxy(), abi.encodeCall(ProxyCall.proxyCall, (target, data))); + vm.prank(treasury); + manager.configureTreasury(target, data); } - /// @notice Test only cross chain governance can configure global - function test_IM_07_configureGlobal_access_control() public { + /// @notice Test governance transfer works + function test_U_IM_05_governance_transfer_works() public { + address newGovernance = makeAddr("newGovernance"); + + // Test only current governance can set pending vm.prank(makeAddr("notGovernance")); - vm.expectRevert("Only cross chain governance can call this function"); - manager.configureGlobal(address(0), ""); + vm.expectRevert( + abi.encodeWithSelector( + IInstanceManager.CallerIsNotCrossChainGovernanceException.selector, makeAddr("notGovernance") + ) + ); + manager.setPendingGovernance(newGovernance); + + // Test setting pending governance + vm.prank(crossChainGovernance); + vm.expectEmit(true, false, false, true); + emit IInstanceManager.SetPendingGovernance(newGovernance); + manager.setPendingGovernance(newGovernance); + assertEq(manager.pendingGovernance(), newGovernance); + + // Test only pending governance can accept + vm.prank(makeAddr("notPending")); + vm.expectRevert( + abi.encodeWithSelector( + IInstanceManager.CallerIsNotPendingGovernanceException.selector, makeAddr("notPending") + ) + ); + manager.acceptGovernance(); + + // Test accepting governance + vm.prank(newGovernance); + vm.expectEmit(true, false, false, true); + emit IInstanceManager.AcceptGovernance(newGovernance); + manager.acceptGovernance(); + + assertEq(manager.pendingGovernance(), address(0)); + assertEq(addressProvider.getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL), newGovernance); } - /// @notice Test only owner can configure local - function test_IM_08_configureLocal_access_control() public { - vm.prank(makeAddr("notOwner")); - vm.expectRevert("Ownable: caller is not the owner"); - manager.configureLocal(address(0), ""); + /// @notice Test system contract deployment + function test_U_IM_06_deploy_system_contract() public { + bytes32 contractType = "TYPE::TEST"; + uint256 version = 3_10; + address newContract = makeAddr("newContract"); + + // Test it reverts if not governance + vm.prank(makeAddr("notGovernance")); + vm.expectRevert( + abi.encodeWithSelector( + IInstanceManager.CallerIsNotCrossChainGovernanceException.selector, makeAddr("notGovernance") + ) + ); + manager.deploySystemContract(contractType, version, false); + + // Mock successful deployment + vm.mockCall( + address(manager.bytecodeRepository()), + abi.encodeCall( + BytecodeRepository.deploy, (contractType, version, abi.encode(manager.addressProvider()), bytes32(0)) + ), + abi.encode(newContract) + ); + + // Test successful deployment + vm.prank(crossChainGovernance); + manager.deploySystemContract(contractType, version, false); + assertEq(addressProvider.getAddressOrRevert(contractType, NO_VERSION_CONTROL), newContract); + + // Test deployment with version saving + address newVersionedContract = makeAddr("newVersionedContract"); + vm.mockCall(newVersionedContract, abi.encodeWithSignature("version()"), abi.encode(version)); + vm.mockCall( + address(manager.bytecodeRepository()), + abi.encodeCall( + BytecodeRepository.deploy, (contractType, version, abi.encode(manager.addressProvider()), bytes32(0)) + ), + abi.encode(newVersionedContract) + ); + + vm.prank(crossChainGovernance); + manager.deploySystemContract(contractType, version, true); + assertEq(addressProvider.getAddressOrRevert(contractType, version), newVersionedContract); + + // Test deployment failure + vm.mockCallRevert( + address(manager.bytecodeRepository()), + abi.encodeCall( + BytecodeRepository.deploy, (contractType, version, abi.encode(manager.addressProvider()), bytes32(0)) + ), + "DEPLOYMENT_FAILED" + ); + + vm.prank(crossChainGovernance); + manager.deploySystemContract(contractType, version, false); + // Should not update address if deployment failed + assertEq(addressProvider.getAddressOrRevert(contractType, NO_VERSION_CONTROL), newContract); } - /// @notice Test only treasury can configure treasury - function test_IM_09_configureTreasury_access_control() public { - vm.prank(owner); - manager.activate(owner, treasury, weth, gear); + /// @notice Test legacy GEAR staking deployment + function test_U_IM_07_deploy_legacy_gear_staking() public { + // Test on legacy chains + uint256[] memory legacyChains = new uint256[](3); + legacyChains[0] = 1; // Mainnet + legacyChains[1] = 10; // Optimism + legacyChains[2] = 42161; // Arbitrum + + address[] memory expectedAddresses = new address[](3); + expectedAddresses[0] = 0x2fcbD02d5B1D52FC78d4c02890D7f4f47a459c33; + expectedAddresses[1] = 0x8D2622f1CA3B42b637e2ff6753E6b69D3ab9Adfd; + expectedAddresses[2] = 0xf3599BEfe8E79169Afd5f0b7eb0A1aA322F193D9; + + for (uint256 i = 0; i < legacyChains.length; i++) { + vm.chainId(legacyChains[i]); + + // Test legacy address is used for version 3.10 + vm.prank(crossChainGovernance); + manager.deploySystemContract(AP_GEAR_STAKING, 3_10, false); + assertEq(addressProvider.getAddressOrRevert(AP_GEAR_STAKING, NO_VERSION_CONTROL), expectedAddresses[i]); + + // Test normal deployment for other versions + address newStaking = makeAddr("newStaking"); + vm.mockCall( + address(manager.bytecodeRepository()), + abi.encodeCall( + BytecodeRepository.deploy, + (AP_GEAR_STAKING, 3_11, abi.encode(manager.addressProvider()), bytes32(0)) + ), + abi.encode(newStaking) + ); + + vm.prank(crossChainGovernance); + manager.deploySystemContract(AP_GEAR_STAKING, 3_11, false); + assertEq(addressProvider.getAddressOrRevert(AP_GEAR_STAKING, NO_VERSION_CONTROL), newStaking); + } + + // Test on non-legacy chain + vm.chainId(137); // Polygon + address nonLegacyStaking = makeAddr("nonLegacyStaking"); + vm.mockCall( + address(manager.bytecodeRepository()), + abi.encodeCall( + BytecodeRepository.deploy, (AP_GEAR_STAKING, 3_10, abi.encode(manager.addressProvider()), bytes32(0)) + ), + abi.encode(nonLegacyStaking) + ); - vm.prank(makeAddr("notTreasury")); - vm.expectRevert("Only financial multisig can call this function"); - manager.configureTreasury(address(0), ""); + vm.prank(crossChainGovernance); + manager.deploySystemContract(AP_GEAR_STAKING, 3_10, false); + assertEq(addressProvider.getAddressOrRevert(AP_GEAR_STAKING, NO_VERSION_CONTROL), nonLegacyStaking); } } diff --git a/contracts/test/global/MarketConfiguratorFactory.unit.t.sol b/contracts/test/global/MarketConfiguratorFactory.unit.t.sol new file mode 100644 index 0000000..6e24389 --- /dev/null +++ b/contracts/test/global/MarketConfiguratorFactory.unit.t.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import {Test} from "forge-std/Test.sol"; + +import {MarketConfiguratorFactory} from "../../instance/MarketConfiguratorFactory.sol"; +import {IAddressProvider} from "../../interfaces/IAddressProvider.sol"; +import {IMarketConfiguratorFactory} from "../../interfaces/IMarketConfiguratorFactory.sol"; +import {IBytecodeRepository} from "../../interfaces/IBytecodeRepository.sol"; +import {Call} from "../../interfaces/Types.sol"; +import {IImmutableOwnableTrait} from "../../interfaces/base/IImmutableOwnableTrait.sol"; + +import { + AP_BYTECODE_REPOSITORY, + AP_CROSS_CHAIN_GOVERNANCE, + AP_MARKET_CONFIGURATOR, + NO_VERSION_CONTROL +} from "../../libraries/ContractLiterals.sol"; + +import {MarketConfiguratorFactoryHarness} from "./MarketConfiguratorFactoryHarness.sol"; + +contract MarketConfiguratorFactoryTest is Test { + MarketConfiguratorFactoryHarness public factory; + address public governance; + address public configurator; + IAddressProvider public addressProvider; + IBytecodeRepository public bytecodeRepository; + + function setUp() public { + governance = makeAddr("governance"); + configurator = makeAddr("configurator"); + addressProvider = IAddressProvider(makeAddr("addressProvider")); + bytecodeRepository = IBytecodeRepository(makeAddr("bytecodeRepository")); + + vm.mockCall(configurator, abi.encodeWithSignature("curatorName()"), abi.encode("Test Curator")); + + vm.mockCall( + address(addressProvider), + abi.encodeWithSignature( + "getAddressOrRevert(bytes32,uint256)", AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL + ), + abi.encode(governance) + ); + + vm.mockCall( + address(addressProvider), + abi.encodeWithSignature("getAddressOrRevert(bytes32,uint256)", AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL), + abi.encode(address(bytecodeRepository)) + ); + + factory = new MarketConfiguratorFactoryHarness(address(addressProvider)); + } + + function test_U_MCF_01_create_market_configurator_works() public { + address emergencyAdmin = makeAddr("emergencyAdmin"); + address adminFeeTreasury = makeAddr("treasury"); + string memory curatorName = "Test Curator"; + + // Mock deployment + vm.mockCall( + address(bytecodeRepository), + abi.encodeCall(IBytecodeRepository.getLatestPatchVersion, (AP_MARKET_CONFIGURATOR, 3_10)), + abi.encode(3_11) + ); + vm.mockCall( + address(bytecodeRepository), + abi.encodeCall( + IBytecodeRepository.deploy, + ( + AP_MARKET_CONFIGURATOR, + 3_11, + abi.encode( + address(addressProvider), address(this), emergencyAdmin, adminFeeTreasury, curatorName, false + ), + bytes32(bytes20(address(this))) + ) + ), + abi.encode(configurator) + ); + + // Expect CreateMarketConfigurator event + vm.expectEmit(true, true, true, true); + emit IMarketConfiguratorFactory.CreateMarketConfigurator(configurator, curatorName); + + factory.createMarketConfigurator(emergencyAdmin, adminFeeTreasury, curatorName, false); + + // Verify configurator was registered + assertTrue(factory.isMarketConfigurator(configurator)); + assertEq(factory.getMarketConfigurators()[0], configurator); + assertEq(factory.getNumMarketConfigurators(), 1); + } + + function test_U_MCF_02_shutdown_market_configurator_works() public { + address contractsRegister = makeAddr("contractsRegister"); + vm.mockCall(configurator, abi.encodeWithSignature("admin()"), abi.encode(address(this))); + + // Test it reverts if caller is not admin + address notAdmin = makeAddr("notAdmin"); + vm.prank(notAdmin); + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.CallerIsNotMarketConfiguratorAdminException.selector, notAdmin + ) + ); + factory.shutdownMarketConfigurator(configurator); + + // Test it reverts if already shutdown + vm.mockCall(configurator, abi.encodeWithSignature("admin()"), abi.encode(address(this))); + factory.exposed_addShutdownConfigurator(configurator); + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.MarketConfiguratorIsAlreadyShutdownException.selector, configurator + ) + ); + factory.shutdownMarketConfigurator(configurator); + factory.exposed_removeShutdownConfigurator(configurator); + + // Test it reverts if configurator is not registered + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.MarketConfiguratorIsNotRegisteredException.selector, configurator + ) + ); + factory.shutdownMarketConfigurator(configurator); + + // Test it reverts if configurator has pools + factory.exposed_addRegisteredConfigurator(configurator); + vm.mockCall(configurator, abi.encodeWithSignature("contractsRegister()"), abi.encode(contractsRegister)); + address[] memory pools = new address[](1); + pools[0] = makeAddr("pool"); + vm.mockCall(contractsRegister, abi.encodeWithSignature("getPools()"), abi.encode(pools)); + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.CantShutdownMarketConfiguratorException.selector, configurator + ) + ); + factory.shutdownMarketConfigurator(configurator); + + // Test it is shutdown successfully + vm.mockCall(contractsRegister, abi.encodeWithSignature("getPools()"), abi.encode(new address[](0))); + vm.expectEmit(true, false, false, false); + emit IMarketConfiguratorFactory.ShutdownMarketConfigurator(configurator); + factory.shutdownMarketConfigurator(configurator); + + // Verify final state + assertFalse(factory.isMarketConfigurator(configurator)); + assertEq(factory.getNumMarketConfigurators(), 0); + assertEq(factory.getShutdownMarketConfigurators()[0], configurator); + } + + function test_U_MCF_03_add_market_configurator_works() public { + // Test it reverts if not governance + address notGovernance = makeAddr("notGovernance"); + vm.prank(notGovernance); + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.CallerIsNotCrossChainGovernanceException.selector, notGovernance + ) + ); + factory.addMarketConfigurator(configurator); + + // Test successful addition + vm.prank(governance); + vm.expectEmit(true, true, true, true); + emit IMarketConfiguratorFactory.CreateMarketConfigurator(configurator, "Test Curator"); + factory.addMarketConfigurator(configurator); + assertTrue(factory.isMarketConfigurator(configurator)); + assertEq(factory.getMarketConfigurators()[0], configurator); + assertEq(factory.getNumMarketConfigurators(), 1); + + // Test it reverts on duplicate addition + vm.prank(governance); + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.MarketConfiguratorIsAlreadyAddedException.selector, configurator + ) + ); + factory.addMarketConfigurator(configurator); + + // Test it reverts if configurator is shutdown + address shutdownConfigurator = makeAddr("shutdownConfigurator"); + vm.mockCall(shutdownConfigurator, abi.encodeWithSignature("curatorName()"), abi.encode("Shutdown Curator")); + + factory.exposed_addShutdownConfigurator(shutdownConfigurator); + + vm.prank(governance); + vm.expectRevert( + abi.encodeWithSelector( + IMarketConfiguratorFactory.MarketConfiguratorIsAlreadyShutdownException.selector, shutdownConfigurator + ) + ); + factory.addMarketConfigurator(shutdownConfigurator); + } +} diff --git a/contracts/test/global/MarketConfiguratorFactoryHarness.sol b/contracts/test/global/MarketConfiguratorFactoryHarness.sol new file mode 100644 index 0000000..556e9c2 --- /dev/null +++ b/contracts/test/global/MarketConfiguratorFactoryHarness.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 +// Gearbox Protocol. Generalized leverage for DeFi protocols +// (c) Gearbox Foundation, 2024. +pragma solidity ^0.8.23; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {MarketConfiguratorFactory} from "../../instance/MarketConfiguratorFactory.sol"; + +/// @title Market configurator factory harness +/// @notice Exposes internal state manipulation functions for testing +contract MarketConfiguratorFactoryHarness is MarketConfiguratorFactory { + using EnumerableSet for EnumerableSet.AddressSet; + + constructor(address addressProvider_) MarketConfiguratorFactory(addressProvider_) {} + + function exposed_addRegisteredConfigurator(address configurator) external { + _registeredMarketConfiguratorsSet.add(configurator); + } + + function exposed_removeRegisteredConfigurator(address configurator) external { + _registeredMarketConfiguratorsSet.remove(configurator); + } + + function exposed_addShutdownConfigurator(address configurator) external { + _shutdownMarketConfiguratorsSet.add(configurator); + } + + function exposed_removeShutdownConfigurator(address configurator) external { + _shutdownMarketConfiguratorsSet.remove(configurator); + } +} diff --git a/contracts/test/global/PriceFeedStore.unit.t.sol b/contracts/test/global/PriceFeedStore.unit.t.sol index edfd0c8..9e5f38f 100644 --- a/contracts/test/global/PriceFeedStore.unit.t.sol +++ b/contracts/test/global/PriceFeedStore.unit.t.sol @@ -8,6 +8,7 @@ import {PriceFeedStore} from "../../instance/PriceFeedStore.sol"; import {IBytecodeRepository} from "../../interfaces/IBytecodeRepository.sol"; import {IPriceFeedStore, PriceUpdate} from "../../interfaces/IPriceFeedStore.sol"; import {IAddressProvider} from "../../interfaces/IAddressProvider.sol"; +import {IImmutableOwnableTrait} from "../../interfaces/base/IImmutableOwnableTrait.sol"; import { MockPriceFeed, MockSingleUnderlyingPriceFeed, @@ -95,7 +96,6 @@ contract PriceFeedStoreTest is Test { PriceFeedInfo memory priceFeedInfo = store.priceFeedInfo(address(priceFeed)); // Verify all parameters were set correctly - assertEq(priceFeedInfo.author, owner); assertEq(priceFeedInfo.priceFeedType, "PRICE_FEED::MOCK"); assertEq(priceFeedInfo.stalenessPeriod, stalenessPeriod); assertEq(priceFeedInfo.version, 1); @@ -467,7 +467,6 @@ contract PriceFeedStoreTest is Test { assertEq(info.version, 0); assertEq(info.stalenessPeriod, 3600); assertEq(info.name, "External Feed"); - assertEq(info.author, owner); vm.stopPrank(); } @@ -604,4 +603,57 @@ contract PriceFeedStoreTest is Test { vm.stopPrank(); } + + function test_PFS_25_removePriceFeed_works() public { + vm.startPrank(owner); + + // Test it reverts on unknown feed + vm.expectRevert( + abi.encodeWithSelector(IPriceFeedStore.PriceFeedIsNotKnownException.selector, address(priceFeed)) + ); + store.removePriceFeed(address(priceFeed)); + + // Add price feed and allow it for some tokens + store.addPriceFeed(address(priceFeed), 3600, "ETH/USD"); + store.allowPriceFeed(token, address(priceFeed)); + address token2 = makeAddr("token2"); + store.allowPriceFeed(token2, address(priceFeed)); + vm.stopPrank(); + + // Test it reverts if not owner + address notOwner = makeAddr("notOwner"); + vm.prank(notOwner); + vm.expectRevert(abi.encodeWithSelector(IImmutableOwnableTrait.CallerIsNotOwnerException.selector, notOwner)); + store.removePriceFeed(address(priceFeed)); + + // Test it successfully removes feed + vm.prank(owner); + + // Expect ForbidPriceFeed events for each token + vm.expectEmit(true, true, false, false); + emit IPriceFeedStore.ForbidPriceFeed(token, address(priceFeed)); + vm.expectEmit(true, true, false, false); + emit IPriceFeedStore.ForbidPriceFeed(token2, address(priceFeed)); + + // Expect RemovePriceFeed event + vm.expectEmit(true, false, false, false); + emit IPriceFeedStore.RemovePriceFeed(address(priceFeed)); + + store.removePriceFeed(address(priceFeed)); + + // Verify feed was removed + assertFalse(store.isKnownPriceFeed(address(priceFeed)), "Feed should not be known"); + assertFalse(store.isAllowedPriceFeed(token, address(priceFeed)), "Feed should not be allowed for token1"); + assertFalse(store.isAllowedPriceFeed(token2, address(priceFeed)), "Feed should not be allowed for token2"); + + vm.expectRevert( + abi.encodeWithSelector(IPriceFeedStore.PriceFeedIsNotAllowedException.selector, token, address(priceFeed)) + ); + store.getAllowanceTimestamp(token, address(priceFeed)); + + vm.expectRevert( + abi.encodeWithSelector(IPriceFeedStore.PriceFeedIsNotAllowedException.selector, token2, address(priceFeed)) + ); + store.getAllowanceTimestamp(token2, address(priceFeed)); + } } diff --git a/contracts/test/helpers/GlobalSetup.sol b/contracts/test/helpers/GlobalSetup.sol index aa9ad60..fa74b1f 100644 --- a/contracts/test/helpers/GlobalSetup.sol +++ b/contracts/test/helpers/GlobalSetup.sol @@ -36,6 +36,7 @@ import { AP_INTEREST_RATE_MODEL_LINEAR, AP_RATE_KEEPER_TUMBLER, AP_RATE_KEEPER_GAUGE, + AP_LOSS_POLICY_ALIASED, AP_LOSS_POLICY_DEFAULT, AP_CREDIT_MANAGER, AP_CREDIT_FACADE, @@ -59,6 +60,7 @@ import {TreasurySplitter} from "../../market/TreasurySplitter.sol"; // Core contracts import {BotListV3} from "@gearbox-protocol/core-v3/contracts/core/BotListV3.sol"; +import {AliasedLossPolicyV3} from "@gearbox-protocol/core-v3/contracts/core/AliasedLossPolicyV3.sol"; import {GearStakingV3} from "@gearbox-protocol/core-v3/contracts/core/GearStakingV3.sol"; import {PoolV3} from "@gearbox-protocol/core-v3/contracts/pool/PoolV3.sol"; import {PoolQuotaKeeperV3} from "@gearbox-protocol/core-v3/contracts/pool/PoolQuotaKeeperV3.sol"; @@ -202,7 +204,7 @@ contract GlobalSetup is Test, InstanceManagerHelper { } function _exportJson() internal { - // Store address manager state as JSON + // Store address manager state as JSON string memory json = vm.serializeAddress("addresses", "instanceManager", address(instanceManager)); json = vm.serializeAddress("addresses", "bytecodeRepository", address(bytecodeRepository)); json = vm.serializeAddress("addresses", "multisig", address(multisig)); @@ -344,6 +346,14 @@ contract GlobalSetup is Test, InstanceManagerHelper { }) ); + contractsToUpload.push( + UploadableContract({ + initCode: type(AliasedLossPolicyV3).creationCode, + contractType: AP_LOSS_POLICY_ALIASED, + version: 3_10 + }) + ); + contractsToUpload.push( UploadableContract({ initCode: type(MarketConfigurator).creationCode, diff --git a/contracts/traits/DeployerTrait.sol b/contracts/traits/DeployerTrait.sol index 12576b2..b1b4802 100644 --- a/contracts/traits/DeployerTrait.sol +++ b/contracts/traits/DeployerTrait.sol @@ -26,16 +26,12 @@ abstract contract DeployerTrait is IDeployerTrait { bytecodeRepository = _getAddressOrRevert(AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL); } - function _getAddressOrRevert(bytes32 key, uint256 version) internal view returns (address) { - return IAddressProvider(addressProvider).getAddressOrRevert(key, version); + function _getAddress(bytes32 key, uint256 version) internal view returns (address) { + return IAddressProvider(addressProvider).getAddress(key, version); } - function _tryGetAddress(bytes32 key, uint256 version) internal view returns (address) { - try IAddressProvider(addressProvider).getAddressOrRevert(key, version) returns (address result) { - return result; - } catch { - return address(0); - } + function _getAddressOrRevert(bytes32 key, uint256 version) internal view returns (address) { + return IAddressProvider(addressProvider).getAddressOrRevert(key, version); } function _getContractType(bytes32 domain, bytes32 postfix) internal pure returns (bytes32) { diff --git a/generated.ts b/generated.ts index 57d866b..3bde75b 100644 --- a/generated.ts +++ b/generated.ts @@ -5,19 +5,9 @@ export const addressProviderAbi = [ { type: 'constructor', - inputs: [{ name: '_owner', internalType: 'address', type: 'address' }], + inputs: [{ name: 'owner_', internalType: 'address', type: 'address' }], stateMutability: 'nonpayable', }, - { - type: 'function', - inputs: [ - { name: '', internalType: 'string', type: 'string' }, - { name: '', internalType: 'uint256', type: 'uint256' }, - ], - name: 'addresses', - outputs: [{ name: '', internalType: 'address', type: 'address' }], - stateMutability: 'view', - }, { type: 'function', inputs: [], @@ -28,18 +18,18 @@ export const addressProviderAbi = [ { type: 'function', inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, - { name: '_version', internalType: 'uint256', type: 'uint256' }, + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, + { name: 'ver', internalType: 'uint256', type: 'uint256' }, ], - name: 'getAddressOrRevert', - outputs: [{ name: 'result', internalType: 'address', type: 'address' }], + name: 'getAddress', + outputs: [{ name: '', internalType: 'address', type: 'address' }], stateMutability: 'view', }, { type: 'function', inputs: [ { name: 'key', internalType: 'bytes32', type: 'bytes32' }, - { name: '_version', internalType: 'uint256', type: 'uint256' }, + { name: 'ver', internalType: 'uint256', type: 'uint256' }, ], name: 'getAddressOrRevert', outputs: [{ name: 'result', internalType: 'address', type: 'address' }], @@ -48,73 +38,60 @@ export const addressProviderAbi = [ { type: 'function', inputs: [], - name: 'getAllSavedContracts', + name: 'getAllEntries', outputs: [ { - name: '', - internalType: 'struct ContractValue[]', + name: 'entries', + internalType: 'struct AddressProviderEntry[]', type: 'tuple[]', components: [ - { name: 'key', internalType: 'string', type: 'string' }, + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, + { name: 'ver', internalType: 'uint256', type: 'uint256' }, { name: 'value', internalType: 'address', type: 'address' }, - { name: 'version', internalType: 'uint256', type: 'uint256' }, ], }, ], stateMutability: 'view', }, + { + type: 'function', + inputs: [], + name: 'getKeys', + outputs: [{ name: '', internalType: 'bytes32[]', type: 'bytes32[]' }], + stateMutability: 'view', + }, { type: 'function', inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, { name: 'majorVersion', internalType: 'uint256', type: 'uint256' }, ], name: 'getLatestMinorVersion', - outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + outputs: [{ name: 'ver', internalType: 'uint256', type: 'uint256' }], stateMutability: 'view', }, { type: 'function', inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, { name: 'minorVersion', internalType: 'uint256', type: 'uint256' }, ], name: 'getLatestPatchVersion', - outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + outputs: [{ name: 'ver', internalType: 'uint256', type: 'uint256' }], stateMutability: 'view', }, { type: 'function', - inputs: [{ name: 'key', internalType: 'string', type: 'string' }], + inputs: [{ name: 'key', internalType: 'bytes32', type: 'bytes32' }], name: 'getLatestVersion', - outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + outputs: [{ name: 'ver', internalType: 'uint256', type: 'uint256' }], stateMutability: 'view', }, { type: 'function', - inputs: [ - { name: '', internalType: 'string', type: 'string' }, - { name: '', internalType: 'uint256', type: 'uint256' }, - ], - name: 'latestMinorVersions', - outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - inputs: [ - { name: '', internalType: 'string', type: 'string' }, - { name: '', internalType: 'uint256', type: 'uint256' }, - ], - name: 'latestPatchVersions', - outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - inputs: [{ name: '', internalType: 'string', type: 'string' }], - name: 'latestVersions', - outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + inputs: [{ name: 'key', internalType: 'bytes32', type: 'bytes32' }], + name: 'getVersions', + outputs: [{ name: '', internalType: 'uint256[]', type: 'uint256[]' }], stateMutability: 'view', }, { @@ -124,27 +101,6 @@ export const addressProviderAbi = [ outputs: [{ name: '', internalType: 'address', type: 'address' }], stateMutability: 'view', }, - { - type: 'function', - inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, - { name: 'value', internalType: 'address', type: 'address' }, - { name: 'saveVersion', internalType: 'bool', type: 'bool' }, - ], - name: 'setAddress', - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - inputs: [ - { name: 'addr', internalType: 'address', type: 'address' }, - { name: 'saveVersion', internalType: 'bool', type: 'bool' }, - ], - name: 'setAddress', - outputs: [], - stateMutability: 'nonpayable', - }, { type: 'function', inputs: [ @@ -167,13 +123,8 @@ export const addressProviderAbi = [ type: 'event', anonymous: false, inputs: [ - { name: 'key', internalType: 'string', type: 'string', indexed: true }, - { - name: 'version', - internalType: 'uint256', - type: 'uint256', - indexed: true, - }, + { name: 'key', internalType: 'bytes32', type: 'bytes32', indexed: true }, + { name: 'ver', internalType: 'uint256', type: 'uint256', indexed: true }, { name: 'value', internalType: 'address', @@ -183,13 +134,37 @@ export const addressProviderAbi = [ ], name: 'SetAddress', }, - { type: 'error', inputs: [], name: 'AddressNotFoundException' }, + { + type: 'error', + inputs: [ + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, + { name: 'ver', internalType: 'uint256', type: 'uint256' }, + ], + name: 'AddressNotFoundException', + }, { type: 'error', inputs: [{ name: 'caller', internalType: 'address', type: 'address' }], name: 'CallerIsNotOwnerException', }, - { type: 'error', inputs: [], name: 'VersionNotFoundException' }, + { + type: 'error', + inputs: [ + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, + { name: 'ver', internalType: 'uint256', type: 'uint256' }, + ], + name: 'InvalidVersionException', + }, + { + type: 'error', + inputs: [{ name: 'key', internalType: 'bytes32', type: 'bytes32' }], + name: 'VersionNotFoundException', + }, + { + type: 'error', + inputs: [{ name: 'key', internalType: 'bytes32', type: 'bytes32' }], + name: 'ZeroAddressException', + }, ] ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -316,6 +291,269 @@ export const defaultLossPolicyAbi = [ { type: 'error', inputs: [], name: 'ZeroAddressException' }, ] +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IAliasedLossPolicyV3 +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const iAliasedLossPolicyV3Abi = [ + { + type: 'function', + inputs: [], + name: 'accessMode', + outputs: [ + { name: '', internalType: 'enum ILossPolicy.AccessMode', type: 'uint8' }, + ], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'acl', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'checksEnabled', + outputs: [{ name: '', internalType: 'bool', type: 'bool' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'contractType', + outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [{ name: 'token', internalType: 'address', type: 'address' }], + name: 'getAliasPriceFeedParams', + outputs: [ + { + name: '', + internalType: 'struct PriceFeedParams', + type: 'tuple', + components: [ + { name: 'priceFeed', internalType: 'address', type: 'address' }, + { name: 'stalenessPeriod', internalType: 'uint32', type: 'uint32' }, + { name: 'skipCheck', internalType: 'bool', type: 'bool' }, + { name: 'tokenDecimals', internalType: 'uint8', type: 'uint8' }, + ], + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [ + { name: 'creditAccount', internalType: 'address', type: 'address' }, + ], + name: 'getRequiredAliasPriceFeeds', + outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'getTokensWithAlias', + outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [ + { name: 'creditAccount', internalType: 'address', type: 'address' }, + { name: 'caller', internalType: 'address', type: 'address' }, + { + name: 'params', + internalType: 'struct ILossPolicy.Params', + type: 'tuple', + components: [ + { name: 'totalDebtUSD', internalType: 'uint256', type: 'uint256' }, + { name: 'twvUSD', internalType: 'uint256', type: 'uint256' }, + { name: 'extraData', internalType: 'bytes', type: 'bytes' }, + ], + }, + ], + name: 'isLiquidatableWithLoss', + outputs: [{ name: '', internalType: 'bool', type: 'bool' }], + stateMutability: 'nonpayable', + }, + { + type: 'function', + inputs: [], + name: 'pool', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'priceFeedStore', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'serialize', + outputs: [{ name: 'serializedData', internalType: 'bytes', type: 'bytes' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [ + { + name: 'mode', + internalType: 'enum ILossPolicy.AccessMode', + type: 'uint8', + }, + ], + name: 'setAccessMode', + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + inputs: [ + { name: 'token', internalType: 'address', type: 'address' }, + { name: 'priceFeed', internalType: 'address', type: 'address' }, + ], + name: 'setAliasPriceFeed', + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + inputs: [{ name: 'enabled', internalType: 'bool', type: 'bool' }], + name: 'setChecksEnabled', + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + inputs: [], + name: 'underlying', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'version', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'mode', + internalType: 'enum ILossPolicy.AccessMode', + type: 'uint8', + indexed: false, + }, + ], + name: 'SetAccessMode', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'token', + internalType: 'address', + type: 'address', + indexed: true, + }, + { + name: 'priceFeed', + internalType: 'address', + type: 'address', + indexed: true, + }, + { + name: 'stalenessPeriod', + internalType: 'uint32', + type: 'uint32', + indexed: false, + }, + { name: 'skipCheck', internalType: 'bool', type: 'bool', indexed: false }, + ], + name: 'SetAliasPriceFeed', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { name: 'enabled', internalType: 'bool', type: 'bool', indexed: false }, + ], + name: 'SetChecksEnabled', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'token', + internalType: 'address', + type: 'address', + indexed: true, + }, + ], + name: 'UnsetAliasPriceFeed', + }, +] + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IAliasedLossPolicyV3Events +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +export const iAliasedLossPolicyV3EventsAbi = [ + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'token', + internalType: 'address', + type: 'address', + indexed: true, + }, + { + name: 'priceFeed', + internalType: 'address', + type: 'address', + indexed: true, + }, + { + name: 'stalenessPeriod', + internalType: 'uint32', + type: 'uint32', + indexed: false, + }, + { name: 'skipCheck', internalType: 'bool', type: 'bool', indexed: false }, + ], + name: 'SetAliasPriceFeed', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'token', + internalType: 'address', + type: 'address', + indexed: true, + }, + ], + name: 'UnsetAliasPriceFeed', + }, +] + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // IBytecodeRepository ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1220,6 +1458,13 @@ export const iCrossChainMultisigAbi = [ outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }], stateMutability: 'view', }, + { + type: 'function', + inputs: [], + name: 'disableRecoveryMode', + outputs: [], + stateMutability: 'nonpayable', + }, { type: 'function', inputs: [], @@ -1231,8 +1476,29 @@ export const iCrossChainMultisigAbi = [ type: 'function', inputs: [ { - name: 'proposal', - internalType: 'struct SignedProposal', + name: 'message', + internalType: 'struct SignedRecoveryModeMessage', + type: 'tuple', + components: [ + { + name: 'startingBatchHash', + internalType: 'bytes32', + type: 'bytes32', + }, + { name: 'signatures', internalType: 'bytes[]', type: 'bytes[]' }, + ], + }, + ], + name: 'enableRecoveryMode', + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + inputs: [ + { + name: 'batch', + internalType: 'struct SignedBatch', type: 'tuple', components: [ { name: 'name', internalType: 'string', type: 'string' }, @@ -1251,34 +1517,18 @@ export const iCrossChainMultisigAbi = [ ], }, ], - name: 'executeProposal', + name: 'executeBatch', outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - inputs: [], - name: 'getCurrentProposalHashes', - outputs: [{ name: '', internalType: 'bytes32[]', type: 'bytes32[]' }], - stateMutability: 'view', - }, - { - type: 'function', - inputs: [], - name: 'getExecutedProposalHashes', - outputs: [{ name: '', internalType: 'bytes32[]', type: 'bytes32[]' }], - stateMutability: 'view', - }, - { - type: 'function', - inputs: [ - { name: 'proposalHash', internalType: 'bytes32', type: 'bytes32' }, - ], - name: 'getProposal', + inputs: [{ name: 'batchHash', internalType: 'bytes32', type: 'bytes32' }], + name: 'getBatch', outputs: [ { name: '', - internalType: 'struct SignedProposal', + internalType: 'struct SignedBatch', type: 'tuple', components: [ { name: 'name', internalType: 'string', type: 'string' }, @@ -1299,6 +1549,20 @@ export const iCrossChainMultisigAbi = [ ], stateMutability: 'view', }, + { + type: 'function', + inputs: [], + name: 'getCurrentBatchHashes', + outputs: [{ name: '', internalType: 'bytes32[]', type: 'bytes32[]' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'getExecutedBatchHashes', + outputs: [{ name: '', internalType: 'bytes32[]', type: 'bytes32[]' }], + stateMutability: 'view', + }, { type: 'function', inputs: [], @@ -1322,7 +1586,7 @@ export const iCrossChainMultisigAbi = [ }, { name: 'prevHash', internalType: 'bytes32', type: 'bytes32' }, ], - name: 'hashProposal', + name: 'hashBatch', outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }], stateMutability: 'view', }, @@ -1336,7 +1600,7 @@ export const iCrossChainMultisigAbi = [ { type: 'function', inputs: [], - name: 'lastProposalHash', + name: 'lastBatchHash', outputs: [{ name: '', internalType: 'bytes32', type: 'bytes32' }], stateMutability: 'view', }, @@ -1357,10 +1621,10 @@ export const iCrossChainMultisigAbi = [ { type: 'function', inputs: [ - { name: 'proposalHash', internalType: 'bytes32', type: 'bytes32' }, + { name: 'batchHash', internalType: 'bytes32', type: 'bytes32' }, { name: 'signature', internalType: 'bytes', type: 'bytes' }, ], - name: 'signProposal', + name: 'signBatch', outputs: [], stateMutability: 'nonpayable', }, @@ -1380,7 +1644,7 @@ export const iCrossChainMultisigAbi = [ }, { name: 'prevHash', internalType: 'bytes32', type: 'bytes32' }, ], - name: 'submitProposal', + name: 'submitBatch', outputs: [], stateMutability: 'nonpayable', }, @@ -1404,18 +1668,32 @@ export const iCrossChainMultisigAbi = [ ], name: 'AddSigner', }, + { type: 'event', anonymous: false, inputs: [], name: 'DisableRecoveryMode' }, { type: 'event', anonymous: false, inputs: [ { - name: 'proposalHash', + name: 'startingBatchHash', internalType: 'bytes32', type: 'bytes32', indexed: true, }, ], - name: 'ExecuteProposal', + name: 'EnableRecoveryMode', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'batchHash', + internalType: 'bytes32', + type: 'bytes32', + indexed: true, + }, + ], + name: 'ExecuteBatch', }, { type: 'event', @@ -1448,7 +1726,7 @@ export const iCrossChainMultisigAbi = [ anonymous: false, inputs: [ { - name: 'proposalHash', + name: 'batchHash', internalType: 'bytes32', type: 'bytes32', indexed: true, @@ -1460,22 +1738,23 @@ export const iCrossChainMultisigAbi = [ indexed: true, }, ], - name: 'SignProposal', + name: 'SignBatch', }, { type: 'event', anonymous: false, inputs: [ { - name: 'proposalHash', + name: 'batchHash', internalType: 'bytes32', type: 'bytes32', indexed: true, }, ], - name: 'SubmitProposal', + name: 'SubmitBatch', }, { type: 'error', inputs: [], name: 'AlreadySignedException' }, + { type: 'error', inputs: [], name: 'BatchDoesNotExistException' }, { type: 'error', inputs: [], name: 'CantBeExecutedOnCurrentChainException' }, { type: 'error', @@ -1488,11 +1767,11 @@ export const iCrossChainMultisigAbi = [ name: 'InvalidConfirmationThresholdValueException', }, { type: 'error', inputs: [], name: 'InvalidPrevHashException' }, + { type: 'error', inputs: [], name: 'InvalidRecoveryModeMessageException' }, { type: 'error', inputs: [], name: 'InvalidconfirmationThresholdException' }, { type: 'error', inputs: [], name: 'NoCallsInProposalException' }, { type: 'error', inputs: [], name: 'NotEnoughSignaturesException' }, { type: 'error', inputs: [], name: 'OnlySelfException' }, - { type: 'error', inputs: [], name: 'ProposalDoesNotExistException' }, { type: 'error', inputs: [], name: 'SignerAlreadyExistsException' }, { type: 'error', inputs: [], name: 'SignerDoesNotExistException' }, ] @@ -1891,6 +2170,13 @@ export const iGaugeV3EventsAbi = [ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// export const iInstanceManagerAbi = [ + { + type: 'function', + inputs: [], + name: 'acceptGovernance', + outputs: [], + stateMutability: 'nonpayable', + }, { type: 'function', inputs: [ @@ -1993,10 +2279,17 @@ export const iInstanceManagerAbi = [ outputs: [{ name: '', internalType: 'address', type: 'address' }], stateMutability: 'view', }, + { + type: 'function', + inputs: [], + name: 'pendingGovernance', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, { type: 'function', inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, { name: 'addr', internalType: 'address', type: 'address' }, { name: 'saveVersion', internalType: 'bool', type: 'bool' }, ], @@ -2007,22 +2300,20 @@ export const iInstanceManagerAbi = [ { type: 'function', inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, + { name: 'key', internalType: 'bytes32', type: 'bytes32' }, { name: 'addr', internalType: 'address', type: 'address' }, { name: 'saveVersion', internalType: 'bool', type: 'bool' }, ], - name: 'setLegacyAddress', + name: 'setLocalAddress', outputs: [], stateMutability: 'nonpayable', }, { type: 'function', inputs: [ - { name: 'key', internalType: 'string', type: 'string' }, - { name: 'addr', internalType: 'address', type: 'address' }, - { name: 'saveVersion', internalType: 'bool', type: 'bool' }, + { name: 'newGovernance', internalType: 'address', type: 'address' }, ], - name: 'setLocalAddress', + name: 'setPendingGovernance', outputs: [], stateMutability: 'nonpayable', }, @@ -2040,6 +2331,52 @@ export const iInstanceManagerAbi = [ outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], stateMutability: 'view', }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'newGovernance', + internalType: 'address', + type: 'address', + indexed: true, + }, + ], + name: 'AcceptGovernance', + }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'newGovernance', + internalType: 'address', + type: 'address', + indexed: true, + }, + ], + name: 'SetPendingGovernance', + }, + { + type: 'error', + inputs: [{ name: 'caller', internalType: 'address', type: 'address' }], + name: 'CallerIsNotCrossChainGovernanceException', + }, + { + type: 'error', + inputs: [{ name: 'caller', internalType: 'address', type: 'address' }], + name: 'CallerIsNotPendingGovernanceException', + }, + { + type: 'error', + inputs: [{ name: 'caller', internalType: 'address', type: 'address' }], + name: 'CallerIsNotTreasuryException', + }, + { + type: 'error', + inputs: [{ name: 'key', internalType: 'bytes32', type: 'bytes32' }], + name: 'InvalidKeyException', + }, ] ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3271,6 +3608,13 @@ export const iMarketConfiguratorFactoryAbi = [ ], stateMutability: 'nonpayable', }, + { + type: 'function', + inputs: [{ name: 'index', internalType: 'uint256', type: 'uint256' }], + name: 'getMarketConfigurator', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, { type: 'function', inputs: [], @@ -3278,6 +3622,13 @@ export const iMarketConfiguratorFactoryAbi = [ outputs: [{ name: '', internalType: 'address[]', type: 'address[]' }], stateMutability: 'view', }, + { + type: 'function', + inputs: [], + name: 'getNumMarketConfigurators', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + stateMutability: 'view', + }, { type: 'function', inputs: [], @@ -3335,11 +3686,6 @@ export const iMarketConfiguratorFactoryAbi = [ ], name: 'ShutdownMarketConfigurator', }, - { - type: 'error', - inputs: [{ name: 'addr', internalType: 'address', type: 'address' }], - name: 'AddressIsNotMarketConfiguratorException', - }, { type: 'error', inputs: [{ name: 'caller', internalType: 'address', type: 'address' }], @@ -3352,12 +3698,9 @@ export const iMarketConfiguratorFactoryAbi = [ }, { type: 'error', - inputs: [{ name: 'caller', internalType: 'address', type: 'address' }], - name: 'CallerIsNotMarketConfiguratorException', - }, - { - type: 'error', - inputs: [], + inputs: [ + { name: 'marketConfigurator', internalType: 'address', type: 'address' }, + ], name: 'CantShutdownMarketConfiguratorException', }, { @@ -3374,6 +3717,13 @@ export const iMarketConfiguratorFactoryAbi = [ ], name: 'MarketConfiguratorIsAlreadyShutdownException', }, + { + type: 'error', + inputs: [ + { name: 'marketConfigurator', internalType: 'address', type: 'address' }, + ], + name: 'MarketConfiguratorIsNotRegisteredException', + }, ] ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3814,7 +4164,6 @@ export const priceFeedStoreAbi = [ internalType: 'struct PriceFeedInfo', type: 'tuple', components: [ - { name: 'author', internalType: 'address', type: 'address' }, { name: 'name', internalType: 'string', type: 'string' }, { name: 'stalenessPeriod', internalType: 'uint32', type: 'uint32' }, { name: 'priceFeedType', internalType: 'bytes32', type: 'bytes32' }, @@ -3824,6 +4173,13 @@ export const priceFeedStoreAbi = [ ], stateMutability: 'view', }, + { + type: 'function', + inputs: [{ name: 'priceFeed', internalType: 'address', type: 'address' }], + name: 'removePriceFeed', + outputs: [], + stateMutability: 'nonpayable', + }, { type: 'function', inputs: [ @@ -3936,6 +4292,19 @@ export const priceFeedStoreAbi = [ ], name: 'ForbidPriceFeed', }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'priceFeed', + internalType: 'address', + type: 'address', + indexed: true, + }, + ], + name: 'RemovePriceFeed', + }, { type: 'event', anonymous: false,