diff --git a/contracts/0.4.24/Lido.sol b/contracts/0.4.24/Lido.sol index 77a9337c9..c2e0d69f8 100644 --- a/contracts/0.4.24/Lido.sol +++ b/contracts/0.4.24/Lido.sol @@ -586,7 +586,7 @@ contract Lido is Versioned, StETHPermit, AragonApp { * @dev can be called only by accounting */ function mintShares(address _recipient, uint256 _amountOfShares) public { - _auth(getLidoLocator().accounting()); + require(msg.sender == getLidoLocator().accounting() || msg.sender == getLidoLocator().vaultHub(), "APP_AUTH_FAILED"); _whenNotStopped(); _mintShares(_recipient, _amountOfShares); @@ -639,7 +639,7 @@ contract Lido is Versioned, StETHPermit, AragonApp { */ function burnExternalShares(uint256 _amountOfShares) external { require(_amountOfShares != 0, "BURN_ZERO_AMOUNT_OF_SHARES"); - _auth(getLidoLocator().accounting()); + _auth(getLidoLocator().vaultHub()); _whenNotStopped(); uint256 externalShares = EXTERNAL_SHARES_POSITION.getStorageUint256(); @@ -663,7 +663,7 @@ contract Lido is Versioned, StETHPermit, AragonApp { */ function rebalanceExternalEtherToInternal() external payable { require(msg.value != 0, "ZERO_VALUE"); - _auth(getLidoLocator().accounting()); + _auth(getLidoLocator().vaultHub()); _whenNotStopped(); uint256 shares = getSharesByPooledEth(msg.value); diff --git a/contracts/0.8.25/Accounting.sol b/contracts/0.8.25/Accounting.sol index 321a4d1d1..0da4dec23 100644 --- a/contracts/0.8.25/Accounting.sol +++ b/contracts/0.8.25/Accounting.sol @@ -20,9 +20,7 @@ import {ReportValues} from "contracts/common/interfaces/ReportValues.sol"; /// @notice contract is responsible for handling accounting oracle reports /// calculating all the state changes that is required to apply the report /// and distributing calculated values to relevant parts of the protocol -/// @dev accounting is inherited from VaultHub contract to reduce gas costs and -/// simplify the auth flows, but they are mostly independent -contract Accounting is VaultHub { +contract Accounting { struct Contracts { address accountingOracleAddress; IOracleReportSanityChecker oracleReportSanityChecker; @@ -30,6 +28,7 @@ contract Accounting is VaultHub { IWithdrawalQueue withdrawalQueue; IPostTokenRebaseReceiver postTokenRebaseReceiver; IStakingRouter stakingRouter; + VaultHub vaultHub; } struct PreReportState { @@ -83,6 +82,8 @@ contract Accounting is VaultHub { uint256 precisionPoints; } + error NotAuthorized(string operation, address addr); + /// @notice deposit size in wei (for pre-maxEB accounting) uint256 private constant DEPOSIT_SIZE = 32 ether; @@ -93,24 +94,14 @@ contract Accounting is VaultHub { /// @param _lidoLocator Lido Locator contract /// @param _lido Lido contract - /// @param _connectedVaultsLimit Maximum number of active vaults that can be connected to the hub - /// @param _relativeShareLimitBP Maximum share limit for a single vault relative to Lido TVL in basis points constructor( ILidoLocator _lidoLocator, - ILido _lido, - uint256 _connectedVaultsLimit, - uint256 _relativeShareLimitBP - ) VaultHub(_lido, _connectedVaultsLimit, _relativeShareLimitBP) { + ILido _lido + ) { LIDO_LOCATOR = _lidoLocator; LIDO = _lido; } - function initialize(address _admin) external initializer { - if (_admin == address(0)) revert ZeroArgument("_admin"); - - __VaultHub_init(_admin); - } - /// @notice calculates all the state changes that is required to apply the report /// @param _report report values /// @param _withdrawalShareRate maximum share rate used for withdrawal finalization @@ -232,7 +223,7 @@ contract Accounting is VaultHub { // Calculate the amount of ether locked in the vaults to back external balance of stETH // and the amount of shares to mint as fees to the treasury for each vaults (update.vaultsLockedEther, update.vaultsTreasuryFeeShares, update.totalVaultsTreasuryFeeShares) = - _calculateVaultsRebase( + _contracts.vaultHub.calculateVaultsRebase( update.postTotalShares, update.postTotalPooledEther, _pre.totalShares, @@ -341,7 +332,8 @@ contract Accounting is VaultHub { _update.etherToFinalizeWQ ); - _updateVaults( + // TODO: Remove this once decide on vaults reporting + _contracts.vaultHub.updateVaults( _report.vaultValues, _report.inOutDeltas, _update.vaultsLockedEther, @@ -349,7 +341,7 @@ contract Accounting is VaultHub { ); if (_update.totalVaultsTreasuryFeeShares > 0) { - STETH.mintExternalShares(LIDO_LOCATOR.treasury(), _update.totalVaultsTreasuryFeeShares); + LIDO.mintExternalShares(LIDO_LOCATOR.treasury(), _update.totalVaultsTreasuryFeeShares); } _notifyRebaseObserver(_contracts.postTokenRebaseReceiver, _report, _pre, _update); @@ -469,7 +461,8 @@ contract Accounting is VaultHub { address burner, address withdrawalQueue, address postTokenRebaseReceiver, - address stakingRouter + address stakingRouter, + address vaultHub ) = LIDO_LOCATOR.oracleReportComponents(); return @@ -479,7 +472,8 @@ contract Accounting is VaultHub { IBurner(burner), IWithdrawalQueue(withdrawalQueue), IPostTokenRebaseReceiver(postTokenRebaseReceiver), - IStakingRouter(stakingRouter) + IStakingRouter(stakingRouter), + VaultHub(vaultHub) ); } diff --git a/contracts/0.8.25/vaults/VaultHub.sol b/contracts/0.8.25/vaults/VaultHub.sol index fd2df762e..e9034b81e 100644 --- a/contracts/0.8.25/vaults/VaultHub.sol +++ b/contracts/0.8.25/vaults/VaultHub.sol @@ -18,7 +18,7 @@ import {Math256} from "contracts/common/lib/Math256.sol"; /// It also allows to force rebalance of the vaults /// Also, it passes the report from the accounting oracle to the vaults and charges fees /// @author folkyatina -abstract contract VaultHub is PausableUntilWithRoles { +contract VaultHub is PausableUntilWithRoles { /// @custom:storage-location erc7201:VaultHub struct VaultHubStorage { /// @notice vault sockets with vaults connected to the hub @@ -74,22 +74,30 @@ abstract contract VaultHub is PausableUntilWithRoles { /// @notice Lido stETH contract IStETH public immutable STETH; + address public immutable accounting; /// @param _stETH Lido stETH contract /// @param _connectedVaultsLimit Maximum number of vaults that can be connected simultaneously /// @param _relativeShareLimitBP Maximum share limit relative to TVL in basis points - constructor(IStETH _stETH, uint256 _connectedVaultsLimit, uint256 _relativeShareLimitBP) { + constructor(IStETH _stETH, address _accounting, uint256 _connectedVaultsLimit, uint256 _relativeShareLimitBP) { if (_connectedVaultsLimit == 0) revert ZeroArgument("_connectedVaultsLimit"); if (_relativeShareLimitBP == 0) revert ZeroArgument("_relativeShareLimitBP"); if (_relativeShareLimitBP > TOTAL_BASIS_POINTS) revert RelativeShareLimitBPTooHigh(_relativeShareLimitBP, TOTAL_BASIS_POINTS); STETH = _stETH; + accounting = _accounting; CONNECTED_VAULTS_LIMIT = _connectedVaultsLimit; RELATIVE_SHARE_LIMIT_BP = _relativeShareLimitBP; _disableInitializers(); } + function initialize(address _admin) external initializer { + if (_admin == address(0)) revert ZeroArgument("_admin"); + + __VaultHub_init(_admin); + } + /// @param _admin admin address to manage the roles function __VaultHub_init(address _admin) internal onlyInitializing { __AccessControlEnumerable_init(); @@ -394,13 +402,13 @@ abstract contract VaultHub is PausableUntilWithRoles { emit VaultDisconnected(_vault); } - function _calculateVaultsRebase( + function calculateVaultsRebase( uint256 _postTotalShares, uint256 _postTotalPooledEther, uint256 _preTotalShares, uint256 _preTotalPooledEther, uint256 _sharesToMintAsFees - ) internal view returns (uint256[] memory lockedEther, uint256[] memory treasuryFeeShares, uint256 totalTreasuryFeeShares) { + ) public view returns (uint256[] memory lockedEther, uint256[] memory treasuryFeeShares, uint256 totalTreasuryFeeShares) { /// HERE WILL BE ACCOUNTING DRAGON // \||/ @@ -476,12 +484,13 @@ abstract contract VaultHub is PausableUntilWithRoles { treasuryFeeShares = (treasuryFee * _preTotalShares) / _preTotalPooledEther; } - function _updateVaults( + function updateVaults( uint256[] memory _valuations, int256[] memory _inOutDeltas, uint256[] memory _locked, uint256[] memory _treasureFeeShares - ) internal { + ) external { + if (msg.sender != accounting) revert NotAuthorized("updateVaults", msg.sender); VaultHubStorage storage $ = _getVaultHubStorage(); for (uint256 i = 0; i < _valuations.length; i++) { diff --git a/contracts/0.8.9/LidoLocator.sol b/contracts/0.8.9/LidoLocator.sol index 982d7c491..8bf1bfa64 100644 --- a/contracts/0.8.9/LidoLocator.sol +++ b/contracts/0.8.9/LidoLocator.sol @@ -30,6 +30,7 @@ contract LidoLocator is ILidoLocator { address oracleDaemonConfig; address accounting; address wstETH; + address vaultHub; } error ZeroAddress(); @@ -50,6 +51,7 @@ contract LidoLocator is ILidoLocator { address public immutable oracleDaemonConfig; address public immutable accounting; address public immutable wstETH; + address public immutable vaultHub; /** * @notice declare service locations @@ -73,6 +75,7 @@ contract LidoLocator is ILidoLocator { oracleDaemonConfig = _assertNonZero(_config.oracleDaemonConfig); accounting = _assertNonZero(_config.accounting); wstETH = _assertNonZero(_config.wstETH); + vaultHub = _assertNonZero(_config.vaultHub); } function coreComponents() external view returns( @@ -99,6 +102,7 @@ contract LidoLocator is ILidoLocator { address, address, address, + address, address ) { return ( @@ -107,7 +111,8 @@ contract LidoLocator is ILidoLocator { burner, withdrawalQueue, postTokenRebaseReceiver, - stakingRouter + stakingRouter, + vaultHub ); } diff --git a/contracts/common/interfaces/ILidoLocator.sol b/contracts/common/interfaces/ILidoLocator.sol index 5e5028bb4..8116d7fe9 100644 --- a/contracts/common/interfaces/ILidoLocator.sol +++ b/contracts/common/interfaces/ILidoLocator.sol @@ -22,7 +22,7 @@ interface ILidoLocator { function oracleDaemonConfig() external view returns(address); function accounting() external view returns (address); function wstETH() external view returns (address); - + function vaultHub() external view returns (address); /// @notice Returns core Lido protocol component addresses in a single call /// @dev This function provides a gas-efficient way to fetch multiple component addresses in a single call function coreComponents() external view returns( @@ -42,6 +42,7 @@ interface ILidoLocator { address burner, address withdrawalQueue, address postTokenRebaseReceiver, - address stakingRouter + address stakingRouter, + address vaultHub ); } diff --git a/lib/deploy.ts b/lib/deploy.ts index e753f0091..8b308d604 100644 --- a/lib/deploy.ts +++ b/lib/deploy.ts @@ -248,6 +248,7 @@ async function getLocatorConfig(locatorAddress: string) { "oracleDaemonConfig", "accounting", "wstETH", + "vaultHub", ] as (keyof LidoLocator.ConfigStruct)[]; const configPromises = addresses.map((name) => locator[name]()); diff --git a/lib/protocol/discover.ts b/lib/protocol/discover.ts index 3032020f5..63234c329 100644 --- a/lib/protocol/discover.ts +++ b/lib/protocol/discover.ts @@ -158,10 +158,11 @@ const getWstEthContract = async ( /** * Load all required vaults contracts. */ -const getVaultsContracts = async (config: ProtocolNetworkConfig) => { +const getVaultsContracts = async (config: ProtocolNetworkConfig, locator: LoadedContract) => { return (await batch({ stakingVaultFactory: loadContract("VaultFactory", config.get("stakingVaultFactory")), stakingVaultBeacon: loadContract("UpgradeableBeacon", config.get("stakingVaultBeacon")), + vaultHub: loadContract("VaultHub", config.get("vaultHub") || (await locator.vaultHub())), })) as VaultsContracts; }; @@ -177,7 +178,7 @@ export async function discover() { ...(await getStakingModules(foundationContracts.stakingRouter, networkConfig)), ...(await getHashConsensusContract(foundationContracts.accountingOracle, networkConfig)), ...(await getWstEthContract(foundationContracts.withdrawalQueue, networkConfig)), - ...(await getVaultsContracts(networkConfig)), + ...(await getVaultsContracts(networkConfig, locator)), } as ProtocolContracts; log.debug("Contracts discovered", { @@ -204,6 +205,7 @@ export async function discover() { // Vaults "Staking Vault Factory": contracts.stakingVaultFactory.address, "Staking Vault Beacon": contracts.stakingVaultBeacon.address, + "Vault Hub": contracts.vaultHub.address, }); const signers = { diff --git a/lib/protocol/types.ts b/lib/protocol/types.ts index bbb168f12..9fbd533a3 100644 --- a/lib/protocol/types.ts +++ b/lib/protocol/types.ts @@ -21,6 +21,7 @@ import { UpgradeableBeacon, ValidatorsExitBusOracle, VaultFactory, + VaultHub, WithdrawalQueueERC721, WithdrawalVault, WstETH, @@ -58,6 +59,7 @@ export type ProtocolNetworkItems = { // vaults stakingVaultFactory: string; stakingVaultBeacon: string; + vaultHub: string; }; export interface ContractTypes { @@ -82,6 +84,7 @@ export interface ContractTypes { WstETH: WstETH; VaultFactory: VaultFactory; UpgradeableBeacon: UpgradeableBeacon; + VaultHub: VaultHub; } export type ContractName = keyof ContractTypes; @@ -133,6 +136,7 @@ export type WstETHContracts = { export type VaultsContracts = { stakingVaultFactory: LoadedContract; stakingVaultBeacon: LoadedContract; + vaultHub: LoadedContract; }; export type ProtocolContracts = { locator: LoadedContract } & CoreContracts & diff --git a/lib/state-file.ts b/lib/state-file.ts index 474910b08..53057802e 100644 --- a/lib/state-file.ts +++ b/lib/state-file.ts @@ -87,6 +87,7 @@ export enum Sk { scratchDeployGasUsed = "scratchDeployGasUsed", minFirstAllocationStrategy = "minFirstAllocationStrategy", accounting = "accounting", + vaultHub = "vaultHub", tokenRebaseNotifier = "tokenRebaseNotifier", // Vaults stakingVaultImpl = "stakingVaultImpl", diff --git a/scripts/defaults/testnet-defaults.json b/scripts/defaults/testnet-defaults.json index 082fd8ce1..06032d496 100644 --- a/scripts/defaults/testnet-defaults.json +++ b/scripts/defaults/testnet-defaults.json @@ -77,7 +77,7 @@ "epochsPerFrame": 12 } }, - "accounting": { + "vaultHub": { "deployParameters": { "connectedVaultsLimit": 500, "relativeShareLimitBP": 1000 diff --git a/scripts/scratch/steps/0090-deploy-non-aragon-contracts.ts b/scripts/scratch/steps/0090-deploy-non-aragon-contracts.ts index fe2450ffb..dde72ba34 100644 --- a/scripts/scratch/steps/0090-deploy-non-aragon-contracts.ts +++ b/scripts/scratch/steps/0090-deploy-non-aragon-contracts.ts @@ -24,7 +24,7 @@ export async function main() { const treasuryAddress = state[Sk.appAgent].proxy.address; const chainSpec = state[Sk.chainSpec]; const depositSecurityModuleParams = state[Sk.depositSecurityModule].deployParameters; - const accountingParams = state[Sk.accounting].deployParameters; + const vaultHubParams = state[Sk.vaultHub].deployParameters; const burnerParams = state[Sk.burner].deployParameters; const hashConsensusForAccountingParams = state[Sk.hashConsensusForAccountingOracle].deployParameters; const hashConsensusForExitBusParams = state[Sk.hashConsensusForValidatorsExitBusOracle].deployParameters; @@ -142,8 +142,13 @@ export async function main() { const accounting = await deployBehindOssifiableProxy(Sk.accounting, "Accounting", proxyContractsOwner, deployer, [ locator.address, lidoAddress, - accountingParams.connectedVaultsLimit, - accountingParams.relativeShareLimitBP, + ]); + + const vaultHub = await deployBehindOssifiableProxy(Sk.vaultHub, "VaultHub", proxyContractsOwner, deployer, [ + lidoAddress, + accounting.address, + vaultHubParams.connectedVaultsLimit, + vaultHubParams.relativeShareLimitBP, ]); // Deploy AccountingOracle @@ -213,6 +218,7 @@ export async function main() { oracleDaemonConfig.address, accounting.address, wstETH.address, + vaultHub.address, ]; await updateProxyImplementation(Sk.lidoLocator, "LidoLocator", locator.address, proxyContractsOwner, [locatorConfig]); } diff --git a/scripts/scratch/steps/0120-initialize-non-aragon-contracts.ts b/scripts/scratch/steps/0120-initialize-non-aragon-contracts.ts index 5caf33576..b2834c0df 100644 --- a/scripts/scratch/steps/0120-initialize-non-aragon-contracts.ts +++ b/scripts/scratch/steps/0120-initialize-non-aragon-contracts.ts @@ -28,7 +28,7 @@ export async function main() { const eip712StETHAddress = state[Sk.eip712StETH].address; const withdrawalVaultAddress = state[Sk.withdrawalVault].proxy.address; const oracleDaemonConfigAddress = state[Sk.oracleDaemonConfig].address; - const accountingAddress = state[Sk.accounting].proxy.address; + const vaultHubAddress = state[Sk.vaultHub].proxy.address; // Set admin addresses (using deployer for testnet) const testnetAdmin = deployer; @@ -37,7 +37,7 @@ export async function main() { const stakingRouterAdmin = testnetAdmin; const withdrawalQueueAdmin = testnetAdmin; const withdrawalVaultAdmin = testnetAdmin; - const accountingAdmin = testnetAdmin; + const vaultHubAdmin = testnetAdmin; // Initialize NodeOperatorsRegistry @@ -147,7 +147,7 @@ export async function main() { await makeTx(oracleDaemonConfig, "renounceRole", [CONFIG_MANAGER_ROLE, testnetAdmin], { from: testnetAdmin }); - // Initialize Accounting - const accounting = await loadContract("Accounting", accountingAddress); - await makeTx(accounting, "initialize", [accountingAdmin], { from: deployer }); + // Initialize VaultHub + const vaultHub = await loadContract("VaultHub", vaultHubAddress); + await makeTx(vaultHub, "initialize", [vaultHubAdmin], { from: deployer }); } diff --git a/scripts/scratch/steps/0130-grant-roles.ts b/scripts/scratch/steps/0130-grant-roles.ts index a68bb8999..4d1115709 100644 --- a/scripts/scratch/steps/0130-grant-roles.ts +++ b/scripts/scratch/steps/0130-grant-roles.ts @@ -1,10 +1,10 @@ import { ethers } from "hardhat"; import { - Accounting, Burner, StakingRouter, ValidatorsExitBusOracle, + VaultHub, WithdrawalQueueERC721, WithdrawalVault, } from "typechain-types"; @@ -31,7 +31,7 @@ export async function main() { const accountingAddress = state[Sk.accounting].proxy.address; const validatorsExitBusOracleAddress = state[Sk.validatorsExitBusOracle].proxy.address; const depositSecurityModuleAddress = state[Sk.depositSecurityModule].address; - + const vaultHubAddress = state[Sk.vaultHub].proxy.address; // StakingRouter const stakingRouter = await loadContract("StakingRouter", stakingRouterAddress); await makeTx( @@ -109,12 +109,12 @@ export async function main() { from: deployer, }); - // Accounting - const accounting = await loadContract("Accounting", accountingAddress); - await makeTx(accounting, "grantRole", [await accounting.VAULT_MASTER_ROLE(), agentAddress], { + // VaultHub + const vaultHub = await loadContract("VaultHub", vaultHubAddress); + await makeTx(vaultHub, "grantRole", [await vaultHub.VAULT_MASTER_ROLE(), agentAddress], { from: deployer, }); - await makeTx(accounting, "grantRole", [await accounting.VAULT_REGISTRY_ROLE(), deployer], { + await makeTx(vaultHub, "grantRole", [await vaultHub.VAULT_REGISTRY_ROLE(), deployer], { from: deployer, }); } diff --git a/scripts/scratch/steps/0145-deploy-vaults.ts b/scripts/scratch/steps/0145-deploy-vaults.ts index 9cdf4fbad..84c18a5fb 100644 --- a/scripts/scratch/steps/0145-deploy-vaults.ts +++ b/scripts/scratch/steps/0145-deploy-vaults.ts @@ -1,7 +1,7 @@ import { keccak256 } from "ethers"; import { ethers } from "hardhat"; -import { Accounting } from "typechain-types"; +import { VaultHub } from "typechain-types"; import { loadContract, makeTx } from "lib"; import { deployWithoutProxy } from "lib/deploy"; @@ -11,7 +11,7 @@ export async function main() { const deployer = (await ethers.provider.getSigner()).address; const state = readNetworkState({ deployer }); - const accountingAddress = state[Sk.accounting].proxy.address; + const vaultHubAddress = state[Sk.vaultHub].proxy.address; const locatorAddress = state[Sk.lidoLocator].proxy.address; const depositContract = state.chainSpec.depositContract; @@ -19,7 +19,7 @@ export async function main() { // Deploy StakingVault implementation contract const imp = await deployWithoutProxy(Sk.stakingVaultImpl, "StakingVault", deployer, [ - accountingAddress, + vaultHubAddress, depositContract, ]); const impAddress = await imp.getAddress(); @@ -50,17 +50,17 @@ export async function main() { console.log("Factory address", await factory.getAddress()); // Add VaultFactory and Vault implementation to the Accounting contract - const accounting = await loadContract("Accounting", accountingAddress); + const vaultHub = await loadContract("VaultHub", vaultHubAddress); // Grant roles for the Accounting contract - const vaultMasterRole = await accounting.VAULT_MASTER_ROLE(); - const vaultRegistryRole = await accounting.VAULT_REGISTRY_ROLE(); + const vaultMasterRole = await vaultHub.VAULT_MASTER_ROLE(); + const vaultRegistryRole = await vaultHub.VAULT_REGISTRY_ROLE(); - await makeTx(accounting, "grantRole", [vaultMasterRole, deployer], { from: deployer }); - await makeTx(accounting, "grantRole", [vaultRegistryRole, deployer], { from: deployer }); + await makeTx(vaultHub, "grantRole", [vaultMasterRole, deployer], { from: deployer }); + await makeTx(vaultHub, "grantRole", [vaultRegistryRole, deployer], { from: deployer }); - await makeTx(accounting, "addVaultProxyCodehash", [vaultBeaconProxyCodeHash], { from: deployer }); + await makeTx(vaultHub, "addVaultProxyCodehash", [vaultBeaconProxyCodeHash], { from: deployer }); - await makeTx(accounting, "renounceRole", [vaultMasterRole, deployer], { from: deployer }); - await makeTx(accounting, "renounceRole", [vaultRegistryRole, deployer], { from: deployer }); + await makeTx(vaultHub, "renounceRole", [vaultMasterRole, deployer], { from: deployer }); + await makeTx(vaultHub, "renounceRole", [vaultRegistryRole, deployer], { from: deployer }); } diff --git a/scripts/scratch/steps/0150-transfer-roles.ts b/scripts/scratch/steps/0150-transfer-roles.ts index 39e2e8759..0b7a05df0 100644 --- a/scripts/scratch/steps/0150-transfer-roles.ts +++ b/scripts/scratch/steps/0150-transfer-roles.ts @@ -23,7 +23,7 @@ export async function main() { { name: "WithdrawalQueueERC721", address: state.withdrawalQueueERC721.proxy.address }, { name: "OracleDaemonConfig", address: state.oracleDaemonConfig.address }, { name: "OracleReportSanityChecker", address: state.oracleReportSanityChecker.address }, - { name: "Accounting", address: state.accounting.proxy.address }, + { name: "VaultHub", address: state.vaultHub.proxy.address }, ]; for (const contract of ozAdminTransfers) { diff --git a/test/0.4.24/lido/lido.externalShares.test.ts b/test/0.4.24/lido/lido.externalShares.test.ts index 735e4bdd5..58a347c86 100644 --- a/test/0.4.24/lido/lido.externalShares.test.ts +++ b/test/0.4.24/lido/lido.externalShares.test.ts @@ -17,7 +17,7 @@ describe("Lido.sol:externalShares", () => { let deployer: HardhatEthersSigner; let user: HardhatEthersSigner; let whale: HardhatEthersSigner; - let accountingSigner: HardhatEthersSigner; + let vaultHubSigner: HardhatEthersSigner; let lido: Lido; let acl: ACL; @@ -43,7 +43,7 @@ describe("Lido.sol:externalShares", () => { const locatorAddress = await lido.getLidoLocator(); locator = await ethers.getContractAt("LidoLocator", locatorAddress, deployer); - accountingSigner = await impersonate(await locator.accounting(), ether("1")); + vaultHubSigner = await impersonate(await locator.vaultHub(), ether("1")); // Add some ether to the protocol await lido.connect(whale).submit(ZeroAddress, { value: ether("1000") }); @@ -105,7 +105,7 @@ describe("Lido.sol:externalShares", () => { // Add some external ether to protocol const amountToMint = (await lido.getMaxMintableExternalShares()) - 1n; - await lido.connect(accountingSigner).mintExternalShares(whale, amountToMint); + await lido.connect(vaultHubSigner).mintExternalShares(whale, amountToMint); expect(await lido.getExternalShares()).to.equal(amountToMint); }); @@ -130,7 +130,7 @@ describe("Lido.sol:externalShares", () => { it("Returns zero after minting max available amount", async () => { const amountToMint = await lido.getMaxMintableExternalShares(); - await lido.connect(accountingSigner).mintExternalShares(whale, amountToMint); + await lido.connect(vaultHubSigner).mintExternalShares(whale, amountToMint); expect(await lido.getMaxMintableExternalShares()).to.equal(0n); }); @@ -180,7 +180,7 @@ describe("Lido.sol:externalShares", () => { await lido.setMaxExternalRatioBP(maxExternalRatioBP); const maxAvailable = await lido.getMaxMintableExternalShares(); - await expect(lido.connect(accountingSigner).mintExternalShares(whale, maxAvailable + 1n)).to.be.revertedWith( + await expect(lido.connect(vaultHubSigner).mintExternalShares(whale, maxAvailable + 1n)).to.be.revertedWith( "EXTERNAL_BALANCE_LIMIT_EXCEEDED", ); }); @@ -189,7 +189,7 @@ describe("Lido.sol:externalShares", () => { await lido.stop(); await lido.setMaxExternalRatioBP(maxExternalRatioBP); - await expect(lido.connect(accountingSigner).mintExternalShares(whale, 1n)).to.be.revertedWith( + await expect(lido.connect(vaultHubSigner).mintExternalShares(whale, 1n)).to.be.revertedWith( "CONTRACT_IS_STOPPED", ); }); @@ -202,7 +202,7 @@ describe("Lido.sol:externalShares", () => { const sharesToMint = 1n; const etherToMint = await lido.getPooledEthByShares(sharesToMint); - await expect(lido.connect(accountingSigner).mintExternalShares(whale, sharesToMint)) + await expect(lido.connect(vaultHubSigner).mintExternalShares(whale, sharesToMint)) .to.emit(lido, "Transfer") .withArgs(ZeroAddress, whale, etherToMint) .to.emit(lido, "TransferShares") @@ -227,22 +227,22 @@ describe("Lido.sol:externalShares", () => { }); it("if external balance is too small", async () => { - await expect(lido.connect(accountingSigner).burnExternalShares(1n)).to.be.revertedWith("EXT_SHARES_TOO_SMALL"); + await expect(lido.connect(vaultHubSigner).burnExternalShares(1n)).to.be.revertedWith("EXT_SHARES_TOO_SMALL"); }); it("if protocol is stopped", async () => { await lido.stop(); - await expect(lido.connect(accountingSigner).burnExternalShares(1n)).to.be.revertedWith("CONTRACT_IS_STOPPED"); + await expect(lido.connect(vaultHubSigner).burnExternalShares(1n)).to.be.revertedWith("CONTRACT_IS_STOPPED"); }); it("if trying to burn more than minted", async () => { await lido.setMaxExternalRatioBP(maxExternalRatioBP); const amount = 100n; - await lido.connect(accountingSigner).mintExternalShares(whale, amount); + await lido.connect(vaultHubSigner).mintExternalShares(whale, amount); - await expect(lido.connect(accountingSigner).burnExternalShares(amount + 1n)).to.be.revertedWith( + await expect(lido.connect(vaultHubSigner).burnExternalShares(amount + 1n)).to.be.revertedWith( "EXT_SHARES_TOO_SMALL", ); }); @@ -253,18 +253,18 @@ describe("Lido.sol:externalShares", () => { await lido.setMaxExternalRatioBP(maxExternalRatioBP); const amountToMint = await lido.getMaxMintableExternalShares(); - await lido.connect(accountingSigner).mintExternalShares(accountingSigner.address, amountToMint); + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner.address, amountToMint); // Now burn them const stethAmount = await lido.getPooledEthByShares(amountToMint); - await expect(lido.connect(accountingSigner).burnExternalShares(amountToMint)) + await expect(lido.connect(vaultHubSigner).burnExternalShares(amountToMint)) .to.emit(lido, "Transfer") - .withArgs(accountingSigner.address, ZeroAddress, stethAmount) + .withArgs(vaultHubSigner.address, ZeroAddress, stethAmount) .to.emit(lido, "TransferShares") - .withArgs(accountingSigner.address, ZeroAddress, amountToMint) + .withArgs(vaultHubSigner.address, ZeroAddress, amountToMint) .to.emit(lido, "ExternalSharesBurned") - .withArgs(accountingSigner.address, amountToMint, stethAmount); + .withArgs(vaultHubSigner.address, amountToMint, stethAmount); // Verify external balance was reduced const externalEther = await lido.getExternalEther(); @@ -275,15 +275,15 @@ describe("Lido.sol:externalShares", () => { await lido.setMaxExternalRatioBP(maxExternalRatioBP); // Multiple mints - await lido.connect(accountingSigner).mintExternalShares(accountingSigner.address, 100n); - await lido.connect(accountingSigner).mintExternalShares(accountingSigner.address, 200n); + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner.address, 100n); + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner.address, 200n); // Burn partial amount - await lido.connect(accountingSigner).burnExternalShares(150n); + await lido.connect(vaultHubSigner).burnExternalShares(150n); expect(await lido.getExternalShares()).to.equal(150n); // Burn remaining - await lido.connect(accountingSigner).burnExternalShares(150n); + await lido.connect(vaultHubSigner).burnExternalShares(150n); expect(await lido.getExternalShares()).to.equal(0n); }); }); @@ -302,7 +302,7 @@ describe("Lido.sol:externalShares", () => { it("Reverts if amount of ether is greater than minted shares", async () => { await expect( lido - .connect(accountingSigner) + .connect(vaultHubSigner) .rebalanceExternalEtherToInternal({ value: await lido.getPooledEthBySharesRoundUp(1n) }), ).to.be.revertedWith("EXT_SHARES_TOO_SMALL"); }); @@ -311,13 +311,13 @@ describe("Lido.sol:externalShares", () => { await lido.setMaxExternalRatioBP(maxExternalRatioBP); const amountToMint = await lido.getMaxMintableExternalShares(); - await lido.connect(accountingSigner).mintExternalShares(accountingSigner.address, amountToMint); + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner.address, amountToMint); const bufferedEtherBefore = await lido.getBufferedEther(); const etherToRebalance = await lido.getPooledEthBySharesRoundUp(1n); - await lido.connect(accountingSigner).rebalanceExternalEtherToInternal({ + await lido.connect(vaultHubSigner).rebalanceExternalEtherToInternal({ value: etherToRebalance, }); @@ -332,15 +332,15 @@ describe("Lido.sol:externalShares", () => { }); it("Can mint and burn without precision loss", async () => { - await lido.connect(accountingSigner).mintExternalShares(accountingSigner, 1n); // 1 wei - await lido.connect(accountingSigner).mintExternalShares(accountingSigner, 1n); // 2 wei - await lido.connect(accountingSigner).mintExternalShares(accountingSigner, 1n); // 3 wei - await lido.connect(accountingSigner).mintExternalShares(accountingSigner, 1n); // 4 wei + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner, 1n); // 1 wei + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner, 1n); // 2 wei + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner, 1n); // 3 wei + await lido.connect(vaultHubSigner).mintExternalShares(vaultHubSigner, 1n); // 4 wei - await expect(lido.connect(accountingSigner).burnExternalShares(4n)).not.to.be.reverted; // 4 * 1.5 = 6 wei + await expect(lido.connect(vaultHubSigner).burnExternalShares(4n)).not.to.be.reverted; // 4 * 1.5 = 6 wei expect(await lido.getExternalEther()).to.equal(0n); expect(await lido.getExternalShares()).to.equal(0n); - expect(await lido.sharesOf(accountingSigner)).to.equal(0n); + expect(await lido.sharesOf(vaultHubSigner)).to.equal(0n); }); }); diff --git a/test/0.8.25/vaults/vaultFactory.test.ts b/test/0.8.25/vaults/vaultFactory.test.ts index 800817bc8..1d7ca6b40 100644 --- a/test/0.8.25/vaults/vaultFactory.test.ts +++ b/test/0.8.25/vaults/vaultFactory.test.ts @@ -5,7 +5,6 @@ import { ethers } from "hardhat"; import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { - Accounting, BeaconProxy, Delegation, DepositContract__MockForBeaconChainDepositor, @@ -16,6 +15,7 @@ import { StETH__HarnessForVaultHub, UpgradeableBeacon, VaultFactory, + VaultHub, WETH9__MockForVault, WstETH__HarnessForVault, } from "typechain-types"; @@ -38,8 +38,8 @@ describe("VaultFactory.sol", () => { let depositContract: DepositContract__MockForBeaconChainDepositor; let proxy: OssifiableProxy; let beacon: UpgradeableBeacon; - let accountingImpl: Accounting; - let accounting: Accounting; + let vaultHubImpl: VaultHub; + let vaultHub: VaultHub; let implOld: StakingVault; let implNew: StakingVault__HarnessForTestUpgrade; let delegation: Delegation; @@ -76,20 +76,14 @@ describe("VaultFactory.sol", () => { depositContract = await ethers.deployContract("DepositContract__MockForBeaconChainDepositor", deployer); // Accounting - accountingImpl = await ethers.deployContract("Accounting", [ - locator, - steth, - VAULTS_CONNECTED_VAULTS_LIMIT, - VAULTS_RELATIVE_SHARE_LIMIT_BP, - ]); - - proxy = await ethers.deployContract("OssifiableProxy", [accountingImpl, admin, new Uint8Array()], admin); - accounting = await ethers.getContractAt("Accounting", proxy, deployer); - await accounting.initialize(admin); + vaultHubImpl = await ethers.deployContract("VaultHub", [steth, ZeroAddress, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP]); + proxy = await ethers.deployContract("OssifiableProxy", [vaultHubImpl, admin, new Uint8Array()], admin); + vaultHub = await ethers.getContractAt("VaultHub", proxy, deployer); + await vaultHub.initialize(admin); //vault implementation - implOld = await ethers.deployContract("StakingVault", [accounting, depositContract], { from: deployer }); - implNew = await ethers.deployContract("StakingVault__HarnessForTestUpgrade", [accounting, depositContract], { + implOld = await ethers.deployContract("StakingVault", [vaultHub, depositContract], { from: deployer }); + implNew = await ethers.deployContract("StakingVault__HarnessForTestUpgrade", [vaultHub, depositContract], { from: deployer, }); @@ -103,9 +97,9 @@ describe("VaultFactory.sol", () => { vaultFactory = await ethers.deployContract("VaultFactory", [beacon, delegation], { from: deployer }); //add VAULT_MASTER_ROLE role to allow admin to connect the Vaults to the vault Hub - await accounting.connect(admin).grantRole(await accounting.VAULT_MASTER_ROLE(), admin); + await vaultHub.connect(admin).grantRole(await vaultHub.VAULT_MASTER_ROLE(), admin); //add VAULT_REGISTRY_ROLE role to allow admin to add factory and vault implementation to the hub - await accounting.connect(admin).grantRole(await accounting.VAULT_REGISTRY_ROLE(), admin); + await vaultHub.connect(admin).grantRole(await vaultHub.VAULT_REGISTRY_ROLE(), admin); //the initialize() function cannot be called on a contract await expect(implOld.initialize(stranger, operator, "0x")).to.revertedWithCustomError( @@ -202,7 +196,7 @@ describe("VaultFactory.sol", () => { context("connect", () => { it("connect ", async () => { - const vaultsBefore = await accounting.vaultsCount(); + const vaultsBefore = await vaultHub.vaultsCount(); expect(vaultsBefore).to.eq(0); const config1 = { @@ -236,7 +230,7 @@ describe("VaultFactory.sol", () => { //attempting to add a vault without adding a proxy bytecode to the allowed list await expect( - accounting + vaultHub .connect(admin) .connectVault( await vault1.getAddress(), @@ -245,15 +239,15 @@ describe("VaultFactory.sol", () => { config1.rebalanceThresholdBP, config1.treasuryFeeBP, ), - ).to.revertedWithCustomError(accounting, "VaultProxyNotAllowed"); + ).to.revertedWithCustomError(vaultHub, "VaultProxyNotAllowed"); const vaultProxyCodeHash = keccak256(vaultBeaconProxyCode); //add proxy code hash to whitelist - await accounting.connect(admin).addVaultProxyCodehash(vaultProxyCodeHash); + await vaultHub.connect(admin).addVaultProxyCodehash(vaultProxyCodeHash); //connect vault 1 to VaultHub - await accounting + await vaultHub .connect(admin) .connectVault( await vault1.getAddress(), @@ -263,7 +257,7 @@ describe("VaultFactory.sol", () => { config1.treasuryFeeBP, ); - const vaultsAfter = await accounting.vaultsCount(); + const vaultsAfter = await vaultHub.vaultsCount(); expect(vaultsAfter).to.eq(1); const version1Before = await vault1.version(); @@ -283,7 +277,7 @@ describe("VaultFactory.sol", () => { //we upgrade implementation - we do not check implementation, just proxy bytecode await expect( - accounting + vaultHub .connect(admin) .connectVault( await vault2.getAddress(), @@ -292,7 +286,7 @@ describe("VaultFactory.sol", () => { config2.rebalanceThresholdBP, config2.treasuryFeeBP, ), - ).to.not.revertedWithCustomError(accounting, "VaultProxyNotAllowed"); + ).to.not.revertedWithCustomError(vaultHub, "VaultProxyNotAllowed"); const vault1WithNewImpl = await ethers.getContractAt("StakingVault__HarnessForTestUpgrade", vault1, deployer); const vault2WithNewImpl = await ethers.getContractAt("StakingVault__HarnessForTestUpgrade", vault2, deployer); diff --git a/test/0.8.25/vaults/accounting.test.ts b/test/0.8.25/vaults/vaultHub.test.ts similarity index 66% rename from test/0.8.25/vaults/accounting.test.ts rename to test/0.8.25/vaults/vaultHub.test.ts index 2b44169ff..06a0a0c51 100644 --- a/test/0.8.25/vaults/accounting.test.ts +++ b/test/0.8.25/vaults/vaultHub.test.ts @@ -4,44 +4,41 @@ import { ethers } from "hardhat"; import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; -import { Accounting, LidoLocator, OssifiableProxy, StETH__Harness } from "typechain-types"; +import { OssifiableProxy, StETH__Harness, VaultHub } from "typechain-types"; import { ether } from "lib"; -import { deployLidoLocator } from "test/deploy"; import { Snapshot, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP } from "test/suite"; -describe("Accounting.sol", () => { +describe("VaultHub.sol", () => { let admin: HardhatEthersSigner; let user: HardhatEthersSigner; let holder: HardhatEthersSigner; let stranger: HardhatEthersSigner; let proxy: OssifiableProxy; - let vaultHubImpl: Accounting; - let accounting: Accounting; + let vaultHubImpl: VaultHub; let steth: StETH__Harness; - let locator: LidoLocator; + let vaultHub: VaultHub; let originalState: string; before(async () => { [admin, user, holder, stranger] = await ethers.getSigners(); - locator = await deployLidoLocator(); steth = await ethers.deployContract("StETH__Harness", [holder], { value: ether("10.0") }); // VaultHub - vaultHubImpl = await ethers.deployContract("Accounting", [ - locator, + vaultHubImpl = await ethers.deployContract("VaultHub", [ steth, + ZeroAddress, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP, ]); proxy = await ethers.deployContract("OssifiableProxy", [vaultHubImpl, admin, new Uint8Array()], admin); - accounting = await ethers.getContractAt("Accounting", proxy, user); + vaultHub = await ethers.getContractAt("VaultHub", proxy, user); }); beforeEach(async () => (originalState = await Snapshot.take())); @@ -56,16 +53,16 @@ describe("Accounting.sol", () => { ); }); it("reverts on `_admin` address is zero", async () => { - await expect(accounting.initialize(ZeroAddress)) - .to.be.revertedWithCustomError(vaultHubImpl, "ZeroArgument") + await expect(vaultHub.initialize(ZeroAddress)) + .to.be.revertedWithCustomError(vaultHub, "ZeroArgument") .withArgs("_admin"); }); it("initialization happy path", async () => { - const tx = await accounting.initialize(admin); + const tx = await vaultHub.initialize(admin); - expect(await accounting.vaultsCount()).to.eq(0); + expect(await vaultHub.vaultsCount()).to.eq(0); - await expect(tx).to.be.emit(accounting, "Initialized").withArgs(1); + await expect(tx).to.be.emit(vaultHub, "Initialized").withArgs(1); }); }); }); diff --git a/test/0.8.25/vaults/vaulthub/contracts/VaultHub__Harness.sol b/test/0.8.25/vaults/vaulthub/contracts/VaultHub__Harness.sol index 67e5af5ba..62a6b59ce 100644 --- a/test/0.8.25/vaults/vaulthub/contracts/VaultHub__Harness.sol +++ b/test/0.8.25/vaults/vaulthub/contracts/VaultHub__Harness.sol @@ -3,18 +3,15 @@ pragma solidity ^0.8.0; -import {Accounting} from "contracts/0.8.25/Accounting.sol"; +import {VaultHub} from "contracts/0.8.25/vaults/VaultHub.sol"; +import {ILido as IStETH} from "contracts/0.8.25/interfaces/ILido.sol"; -import {ILido} from "contracts/0.8.25/interfaces/ILido.sol"; -import {ILidoLocator} from "contracts/common/interfaces/ILidoLocator.sol"; - -contract VaultHub__Harness is Accounting { +contract VaultHub__Harness is VaultHub { constructor( - address _locator, address _steth, uint256 _connectedVaultsLimit, uint256 _relativeShareLimitBP - ) Accounting(ILidoLocator(_locator), ILido(_steth), _connectedVaultsLimit, _relativeShareLimitBP) {} + ) VaultHub(IStETH(_steth), address(0), _connectedVaultsLimit, _relativeShareLimitBP) {} function mock__calculateVaultsRebase( uint256 _postTotalShares, @@ -28,7 +25,7 @@ contract VaultHub__Harness is Accounting { returns (uint256[] memory lockedEther, uint256[] memory treasuryFeeShares, uint256 totalTreasuryFeeShares) { return - _calculateVaultsRebase( + calculateVaultsRebase( _postTotalShares, _postTotalPooledEther, _preTotalShares, diff --git a/test/0.8.25/vaults/vaulthub/vaulthub.forceExit.test.ts b/test/0.8.25/vaults/vaulthub/vaulthub.forceExit.test.ts index 70031f158..226b3c01f 100644 --- a/test/0.8.25/vaults/vaulthub/vaulthub.forceExit.test.ts +++ b/test/0.8.25/vaults/vaulthub/vaulthub.forceExit.test.ts @@ -16,7 +16,6 @@ import { impersonate } from "lib"; import { findEvents } from "lib/event"; import { ether } from "lib/units"; -import { deployLidoLocator } from "test/deploy"; import { Snapshot, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP } from "test/suite"; const SAMPLE_PUBKEY = "0x" + "01".repeat(48); @@ -51,12 +50,10 @@ describe("VaultHub.sol:forceExit", () => { before(async () => { [deployer, user, stranger, feeRecipient] = await ethers.getSigners(); - const locator = await deployLidoLocator(); steth = await ethers.deployContract("StETH__HarnessForVaultHub", [user], { value: ether("10000.0") }); depositContract = await ethers.deployContract("DepositContract__MockForVaultHub"); const vaultHubImpl = await ethers.deployContract("VaultHub__Harness", [ - locator, steth, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP, @@ -64,14 +61,14 @@ describe("VaultHub.sol:forceExit", () => { const proxy = await ethers.deployContract("OssifiableProxy", [vaultHubImpl, deployer, new Uint8Array()]); - const accounting = await ethers.getContractAt("VaultHub__Harness", proxy); - await accounting.initialize(deployer); + const vaultHubAdmin = await ethers.getContractAt("VaultHub__Harness", proxy); + await vaultHubAdmin.initialize(deployer); vaultHub = await ethers.getContractAt("VaultHub__Harness", proxy, user); vaultHubAddress = await vaultHub.getAddress(); - await accounting.grantRole(await vaultHub.VAULT_MASTER_ROLE(), user); - await accounting.grantRole(await vaultHub.VAULT_REGISTRY_ROLE(), user); + await vaultHubAdmin.grantRole(await vaultHub.VAULT_MASTER_ROLE(), user); + await vaultHubAdmin.grantRole(await vaultHub.VAULT_REGISTRY_ROLE(), user); const stakingVaultImpl = await ethers.deployContract("StakingVault__MockForVaultHub", [ await vaultHub.getAddress(), diff --git a/test/0.8.25/vaults/vaulthub/vaulthub.hub.test.ts b/test/0.8.25/vaults/vaulthub/vaulthub.hub.test.ts index a4a49e2f8..415b15417 100644 --- a/test/0.8.25/vaults/vaulthub/vaulthub.hub.test.ts +++ b/test/0.8.25/vaults/vaulthub/vaulthub.hub.test.ts @@ -100,25 +100,25 @@ describe("VaultHub.sol:hub", () => { depositContract = await ethers.deployContract("DepositContract__MockForVaultHub"); - const vaultHubImpl = await ethers.deployContract("Accounting", [ - locator, + const vaultHubImpl = await ethers.deployContract("VaultHub", [ lido, + ZeroAddress, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP, ]); const proxy = await ethers.deployContract("OssifiableProxy", [vaultHubImpl, deployer, new Uint8Array()]); - const accounting = await ethers.getContractAt("Accounting", proxy); - await accounting.initialize(deployer); + const vaultHubAdmin = await ethers.getContractAt("VaultHub", proxy); + await vaultHubAdmin.initialize(deployer); - vaultHub = await ethers.getContractAt("Accounting", proxy, user); - await accounting.grantRole(await vaultHub.PAUSE_ROLE(), user); - await accounting.grantRole(await vaultHub.RESUME_ROLE(), user); - await accounting.grantRole(await vaultHub.VAULT_MASTER_ROLE(), user); - await accounting.grantRole(await vaultHub.VAULT_REGISTRY_ROLE(), user); + vaultHub = await ethers.getContractAt("VaultHub", proxy, user); + await vaultHubAdmin.grantRole(await vaultHub.PAUSE_ROLE(), user); + await vaultHubAdmin.grantRole(await vaultHub.RESUME_ROLE(), user); + await vaultHubAdmin.grantRole(await vaultHub.VAULT_MASTER_ROLE(), user); + await vaultHubAdmin.grantRole(await vaultHub.VAULT_REGISTRY_ROLE(), user); - await updateLidoLocatorImplementation(await locator.getAddress(), { accounting }); + await updateLidoLocatorImplementation(await locator.getAddress(), { vaultHub }); const stakingVaultImpl = await ethers.deployContract("StakingVault__MockForVaultHub", [ await vaultHub.getAddress(), @@ -446,7 +446,7 @@ describe("VaultHub.sol:hub", () => { await vault.report(1n, ether("1"), ether("1")); // Below minimal required valuation expect(await vaultHub.isVaultHealthy(vaultAddress)).to.equal(false); - await lido.connect(user).transferShares(await locator.accounting(), 1n); + await lido.connect(user).transferShares(await locator.vaultHub(), 1n); await vaultHub.connect(user).burnShares(vaultAddress, 1n); expect(await vaultHub.isVaultHealthy(vaultAddress)).to.equal(true); // Should be healthy with no shares diff --git a/test/0.8.25/vaults/vaulthub/vaulthub.pausable.test.ts b/test/0.8.25/vaults/vaulthub/vaulthub.pausable.test.ts index 37615b30b..ee870e0f5 100644 --- a/test/0.8.25/vaults/vaulthub/vaulthub.pausable.test.ts +++ b/test/0.8.25/vaults/vaulthub/vaulthub.pausable.test.ts @@ -1,4 +1,5 @@ import { expect } from "chai"; +import { ZeroAddress } from "ethers"; import { ethers } from "hardhat"; import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; @@ -8,7 +9,6 @@ import { StETH__HarnessForVaultHub, VaultHub } from "typechain-types"; import { ether, MAX_UINT256 } from "lib"; -import { deployLidoLocator } from "test/deploy"; import { Snapshot, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP } from "test/suite"; describe("VaultHub.sol:pausableUntil", () => { @@ -16,6 +16,7 @@ describe("VaultHub.sol:pausableUntil", () => { let user: HardhatEthersSigner; let stranger: HardhatEthersSigner; + let vaultHubAdmin: VaultHub; let vaultHub: VaultHub; let steth: StETH__HarnessForVaultHub; @@ -24,24 +25,17 @@ describe("VaultHub.sol:pausableUntil", () => { before(async () => { [deployer, user, stranger] = await ethers.getSigners(); - const locator = await deployLidoLocator(); steth = await ethers.deployContract("StETH__HarnessForVaultHub", [user], { value: ether("1.0") }); - const vaultHubImpl = await ethers.deployContract("Accounting", [ - locator, - steth, - VAULTS_CONNECTED_VAULTS_LIMIT, - VAULTS_RELATIVE_SHARE_LIMIT_BP, - ]); - + const vaultHubImpl = await ethers.deployContract("VaultHub", [steth, ZeroAddress, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP]); const proxy = await ethers.deployContract("OssifiableProxy", [vaultHubImpl, deployer, new Uint8Array()]); - const accounting = await ethers.getContractAt("Accounting", proxy); - await accounting.initialize(deployer); + vaultHubAdmin = await ethers.getContractAt("VaultHub", proxy); + await vaultHubAdmin.initialize(deployer); - vaultHub = await ethers.getContractAt("Accounting", proxy, user); - await accounting.grantRole(await vaultHub.PAUSE_ROLE(), user); - await accounting.grantRole(await vaultHub.RESUME_ROLE(), user); + vaultHub = await ethers.getContractAt("VaultHub", proxy, user); + await vaultHubAdmin.grantRole(await vaultHub.PAUSE_ROLE(), user); + await vaultHubAdmin.grantRole(await vaultHub.RESUME_ROLE(), user); }); beforeEach(async () => (originalState = await Snapshot.take())); diff --git a/test/0.8.9/accounting.handleOracleReport.test.ts b/test/0.8.9/accounting.handleOracleReport.test.ts index 7d4680eb8..3f02b9688 100644 --- a/test/0.8.9/accounting.handleOracleReport.test.ts +++ b/test/0.8.9/accounting.handleOracleReport.test.ts @@ -64,11 +64,7 @@ describe("Accounting.sol:report", () => { deployer, ); - const accountingImpl = await ethers.deployContract( - "Accounting", - [locator, lido, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP], - deployer, - ); + const accountingImpl = await ethers.deployContract("Accounting", [locator, lido], deployer); const accountingProxy = await ethers.deployContract( "OssifiableProxy", [accountingImpl, deployer, new Uint8Array()], @@ -76,7 +72,20 @@ describe("Accounting.sol:report", () => { ); accounting = await ethers.getContractAt("Accounting", accountingProxy, deployer); await updateLidoLocatorImplementation(await locator.getAddress(), { accounting }); - await accounting.initialize(deployer); + + const vaultHubImpl = await ethers.deployContract( + "VaultHub", + [lido, accounting, VAULTS_CONNECTED_VAULTS_LIMIT, VAULTS_RELATIVE_SHARE_LIMIT_BP], + deployer, + ); + const vaultHubProxy = await ethers.deployContract( + "OssifiableProxy", + [vaultHubImpl, deployer, new Uint8Array()], + deployer, + ); + const vaultHub = await ethers.getContractAt("VaultHub", vaultHubProxy, deployer); + await updateLidoLocatorImplementation(await locator.getAddress(), { vaultHub }); + await vaultHub.initialize(deployer); const accountingOracleSigner = await impersonate(await locator.accountingOracle(), ether("100.0")); accounting = accounting.connect(accountingOracleSigner); diff --git a/test/0.8.9/contracts/LidoLocator__MockForSanityChecker.sol b/test/0.8.9/contracts/LidoLocator__MockForSanityChecker.sol index c38818a9c..5bf49672a 100644 --- a/test/0.8.9/contracts/LidoLocator__MockForSanityChecker.sol +++ b/test/0.8.9/contracts/LidoLocator__MockForSanityChecker.sol @@ -24,6 +24,7 @@ contract LidoLocator__MockForSanityChecker is ILidoLocator { address oracleDaemonConfig; address accounting; address wstETH; + address vaultHub; } address public immutable lido; @@ -42,7 +43,7 @@ contract LidoLocator__MockForSanityChecker is ILidoLocator { address public immutable oracleDaemonConfig; address public immutable accounting; address public immutable wstETH; - + address public immutable vaultHub; constructor(ContractAddresses memory addresses) { lido = addresses.lido; depositSecurityModule = addresses.depositSecurityModule; @@ -60,20 +61,26 @@ contract LidoLocator__MockForSanityChecker is ILidoLocator { oracleDaemonConfig = addresses.oracleDaemonConfig; accounting = addresses.accounting; wstETH = addresses.wstETH; + vaultHub = addresses.vaultHub; } function coreComponents() external view returns (address, address, address, address, address, address) { return (elRewardsVault, oracleReportSanityChecker, stakingRouter, treasury, withdrawalQueue, withdrawalVault); } - function oracleReportComponents() external view returns (address, address, address, address, address, address) { + function oracleReportComponents() + external + view + returns (address, address, address, address, address, address, address) + { return ( accountingOracle, oracleReportSanityChecker, burner, withdrawalQueue, postTokenRebaseReceiver, - stakingRouter + stakingRouter, + vaultHub ); } } diff --git a/test/0.8.9/contracts/LidoLocator__MockMutable.sol b/test/0.8.9/contracts/LidoLocator__MockMutable.sol index e102d2a4d..a3a31c1c4 100644 --- a/test/0.8.9/contracts/LidoLocator__MockMutable.sol +++ b/test/0.8.9/contracts/LidoLocator__MockMutable.sol @@ -23,6 +23,7 @@ contract LidoLocator__MockMutable is ILidoLocator { address oracleDaemonConfig; address accounting; address wstETH; + address vaultHub; } error ZeroAddress(); @@ -43,7 +44,7 @@ contract LidoLocator__MockMutable is ILidoLocator { address public immutable oracleDaemonConfig; address public immutable accounting; address public immutable wstETH; - + address public immutable vaultHub; /** * @notice declare service locations * @dev accepts a struct to avoid the "stack-too-deep" error @@ -66,20 +67,26 @@ contract LidoLocator__MockMutable is ILidoLocator { oracleDaemonConfig = _assertNonZero(_config.oracleDaemonConfig); accounting = _assertNonZero(_config.accounting); wstETH = _assertNonZero(_config.wstETH); + vaultHub = _assertNonZero(_config.vaultHub); } function coreComponents() external view returns (address, address, address, address, address, address) { return (elRewardsVault, oracleReportSanityChecker, stakingRouter, treasury, withdrawalQueue, withdrawalVault); } - function oracleReportComponents() external view returns (address, address, address, address, address, address) { + function oracleReportComponents() + external + view + returns (address, address, address, address, address, address, address) + { return ( accountingOracle, oracleReportSanityChecker, burner, withdrawalQueue, postTokenRebaseReceiver, - stakingRouter + stakingRouter, + vaultHub ); } diff --git a/test/0.8.9/lidoLocator.test.ts b/test/0.8.9/lidoLocator.test.ts index 85f782432..3300358d8 100644 --- a/test/0.8.9/lidoLocator.test.ts +++ b/test/0.8.9/lidoLocator.test.ts @@ -22,6 +22,7 @@ const services = [ "oracleDaemonConfig", "accounting", "wstETH", + "vaultHub", ] as const; type ArrayToUnion = A[number]; @@ -92,6 +93,7 @@ describe("LidoLocator.sol", () => { withdrawalQueue, postTokenRebaseReceiver, stakingRouter, + vaultHub, } = config; expect(await locator.oracleReportComponents()).to.deep.equal([ @@ -101,6 +103,7 @@ describe("LidoLocator.sol", () => { withdrawalQueue, postTokenRebaseReceiver, stakingRouter, + vaultHub, ]); }); }); diff --git a/test/0.8.9/sanityChecker/oracleReportSanityChecker.negative-rebase.test.ts b/test/0.8.9/sanityChecker/oracleReportSanityChecker.negative-rebase.test.ts index 977c25343..340180a2b 100644 --- a/test/0.8.9/sanityChecker/oracleReportSanityChecker.negative-rebase.test.ts +++ b/test/0.8.9/sanityChecker/oracleReportSanityChecker.negative-rebase.test.ts @@ -87,6 +87,7 @@ describe("OracleReportSanityChecker.sol:negative-rebase", () => { oracleDaemonConfig: deployer.address, accounting: await accounting.getAddress(), wstETH: deployer.address, + vaultHub: deployer.address, }, ]); diff --git a/test/deploy/locator.ts b/test/deploy/locator.ts index e41e54111..cc7d650b9 100644 --- a/test/deploy/locator.ts +++ b/test/deploy/locator.ts @@ -28,8 +28,9 @@ async function deployDummyLocator(config?: Partial, de validatorsExitBusOracle: certainAddress("dummy-locator:validatorsExitBusOracle"), withdrawalQueue: certainAddress("dummy-locator:withdrawalQueue"), withdrawalVault: certainAddress("dummy-locator:withdrawalVault"), - accounting: certainAddress("dummy-locator:withdrawalVault"), + accounting: certainAddress("dummy-locator:accounting"), wstETH: certainAddress("dummy-locator:wstETH"), + vaultHub: certainAddress("dummy-locator:vaultHub"), ...config, }); @@ -106,6 +107,7 @@ async function getLocatorConfig(locatorAddress: string) { "oracleDaemonConfig", "accounting", "wstETH", + "vaultHub", ] as Partial[]; const configPromises = addresses.map((name) => locator[name]()); diff --git a/test/integration/vaults/happy-path.integration.ts b/test/integration/vaults/happy-path.integration.ts index fa144058e..4bcb17052 100644 --- a/test/integration/vaults/happy-path.integration.ts +++ b/test/integration/vaults/happy-path.integration.ts @@ -142,7 +142,6 @@ describe("Scenario: Staking Vaults Happy Path", () => { const _stakingVault = await ethers.getContractAt("StakingVault", implAddress); const _delegation = await ethers.getContractAt("Delegation", delegationAddress); - expect(await _stakingVault.vaultHub()).to.equal(ctx.contracts.accounting.address); expect(await _stakingVault.DEPOSIT_CONTRACT()).to.equal(depositContract); expect(await _delegation.STETH()).to.equal(ctx.contracts.lido.address); @@ -206,7 +205,7 @@ describe("Scenario: Staking Vaults Happy Path", () => { }); it("Should allow Lido to recognize vaults and connect them to accounting", async () => { - const { lido, accounting } = ctx.contracts; + const { lido, vaultHub } = ctx.contracts; expect(await stakingVault.locked()).to.equal(0); // no ETH locked yet @@ -218,11 +217,11 @@ describe("Scenario: Staking Vaults Happy Path", () => { const agentSigner = await ctx.getSigner("agent"); - await accounting + await vaultHub .connect(agentSigner) .connectVault(stakingVault, shareLimit, reserveRatio, rebalanceThreshold, treasuryFeeBP); - expect(await accounting.vaultsCount()).to.equal(1n); + expect(await vaultHub.vaultsCount()).to.equal(1n); expect(await stakingVault.locked()).to.equal(VAULT_CONNECTION_DEPOSIT); }); @@ -268,7 +267,7 @@ describe("Scenario: Staking Vaults Happy Path", () => { }); it("Should allow Token Master to mint max stETH", async () => { - const { accounting, lido } = ctx.contracts; + const { vaultHub, lido } = ctx.contracts; // Calculate the max stETH that can be minted on the vault 101 with the given LTV stakingVaultMaxMintingShares = await lido.getSharesByPooledEth( @@ -284,7 +283,7 @@ describe("Scenario: Staking Vaults Happy Path", () => { // Validate minting with the cap const mintOverLimitTx = delegation.connect(curator).mintShares(curator, stakingVaultMaxMintingShares + 1n); await expect(mintOverLimitTx) - .to.be.revertedWithCustomError(accounting, "InsufficientValuationToMint") + .to.be.revertedWithCustomError(vaultHub, "InsufficientValuationToMint") .withArgs(stakingVault, stakingVault.valuation()); const mintTx = await delegation.connect(curator).mintShares(curator, stakingVaultMaxMintingShares); @@ -434,9 +433,9 @@ describe("Scenario: Staking Vaults Happy Path", () => { }); it("Should allow Manager to rebalance the vault to reduce the debt", async () => { - const { accounting, lido } = ctx.contracts; + const { vaultHub, lido } = ctx.contracts; - const socket = await accounting["vaultSocket(address)"](stakingVaultAddress); + const socket = await vaultHub["vaultSocket(address)"](stakingVaultAddress); const sharesMinted = await lido.getPooledEthByShares(socket.sharesMinted); await delegation.connect(curator).rebalanceVault(sharesMinted, { value: sharesMinted });