From efdfb37a34f83f1f7e8fe85b92fae18aa913d2ad Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Fri, 9 Jan 2026 14:34:28 +0200 Subject: [PATCH 01/19] Add redemption assets to validators checker --- contracts/interfaces/IValidatorsChecker.sol | 11 +++--- contracts/validators/ValidatorsChecker.sol | 40 +++++++++++---------- test/EthValidatorsChecker.t.sol | 12 ++++--- test/gnosis/GnoValidatorsChecker.t.sol | 3 ++ 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/contracts/interfaces/IValidatorsChecker.sol b/contracts/interfaces/IValidatorsChecker.sol index 83b6e18d..e6255b06 100644 --- a/contracts/interfaces/IValidatorsChecker.sol +++ b/contracts/interfaces/IValidatorsChecker.sol @@ -59,13 +59,16 @@ interface IValidatorsChecker is IMulticall { * @notice Function for getting the exit queue missing assets * @param vault The address of the vault * @param withdrawingAssets The amount of assets currently being withdrawn from validators + * @param redemptionAssets The amount of assets to be redeemed * @param targetCumulativeTickets The target cumulative tickets * @return missingAssets The exit queue missing assets */ - function getExitQueueMissingAssets(address vault, uint256 withdrawingAssets, uint256 targetCumulativeTickets) - external - view - returns (uint256 missingAssets); + function getExitQueueMissingAssets( + address vault, + uint256 withdrawingAssets, + uint256 redemptionAssets, + uint256 targetCumulativeTickets + ) external view returns (uint256 missingAssets); /** * @notice Function for checking validators manager signature diff --git a/contracts/validators/ValidatorsChecker.sol b/contracts/validators/ValidatorsChecker.sol index 47a37c03..e7fc7cd9 100644 --- a/contracts/validators/ValidatorsChecker.sol +++ b/contracts/validators/ValidatorsChecker.sol @@ -72,12 +72,16 @@ abstract contract ValidatorsChecker is Multicall, IValidatorsChecker { } /// @inheritdoc IValidatorsChecker - function getExitQueueMissingAssets(address vault, uint256 withdrawingAssets, uint256 targetCumulativeTickets) - external - view - override - returns (uint256 missingAssets) - { + function getExitQueueMissingAssets( + address vault, + uint256 withdrawingAssets, + uint256 redemptionAssets, + uint256 targetCumulativeTickets + ) external view override returns (uint256 missingAssets) { + // start with redemption assets as missing assets + missingAssets = redemptionAssets; + + // fetch vault exit queue state ( uint128 queuedShares, uint128 unclaimedAssets, @@ -85,26 +89,23 @@ abstract contract ValidatorsChecker is Multicall, IValidatorsChecker { uint128 totalExitingAssets, uint256 totalTickets ) = IVaultState(vault).getExitQueueData(); - // check whether already covered - if (totalTickets >= targetCumulativeTickets) { - return 0; - } // calculate the amount of tickets that need to be covered - uint256 totalTicketsToCover = targetCumulativeTickets - totalTickets; + uint256 totalTicketsToCover; + if (totalTickets < targetCumulativeTickets) { + totalTicketsToCover = targetCumulativeTickets - totalTickets; + } // calculate missing assets from legacy exits - uint256 ticketsToCover; if (totalExitingTickets > 0) { - ticketsToCover = Math.min(totalTicketsToCover, totalExitingTickets); - missingAssets = Math.mulDiv(ticketsToCover, totalExitingAssets, totalExitingTickets); - totalTicketsToCover -= ticketsToCover; + uint256 legacyTicketsToCover = Math.min(totalTicketsToCover, totalExitingTickets); + missingAssets += Math.mulDiv(legacyTicketsToCover, totalExitingAssets, totalExitingTickets); + totalTicketsToCover -= legacyTicketsToCover; } // calculate missing assets from queued shares if (totalTicketsToCover > 0 && queuedShares > 0) { - ticketsToCover = Math.min(totalTicketsToCover, queuedShares); - missingAssets += IVaultState(vault).convertToAssets(ticketsToCover); + missingAssets += IVaultState(vault).convertToAssets(Math.min(totalTicketsToCover, queuedShares)); } // check whether there is enough available assets @@ -207,8 +208,9 @@ abstract contract ValidatorsChecker is Multicall, IValidatorsChecker { uint256 endIndex; for (uint256 i = 0; i < validatorsCount;) { endIndex += validatorLength; - leaves[params.proofIndexes[i]] = - keccak256(bytes.concat(keccak256(abi.encode(params.validators[startIndex:endIndex], currentIndex)))); + leaves[params.proofIndexes[i]] = keccak256( + bytes.concat(keccak256(abi.encode(params.validators[startIndex:endIndex], currentIndex))) + ); startIndex = endIndex; unchecked { diff --git a/test/EthValidatorsChecker.t.sol b/test/EthValidatorsChecker.t.sol index 1ed7f7af..1ddc1a50 100644 --- a/test/EthValidatorsChecker.t.sol +++ b/test/EthValidatorsChecker.t.sol @@ -482,6 +482,7 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { uint256 missingAssets = validatorsChecker.getExitQueueMissingAssets( emptyVault, 0, // No pending assets + 0, // No redemption assets targetCumulativeTickets ); @@ -507,13 +508,14 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { uint256 missingAssetsNoPending = validatorsChecker.getExitQueueMissingAssets( vault, 0, // No pending assets + 0, // No redemption assets cumulativeTickets ); // Test with some pending assets uint256 pendingAssets = 0.5 ether; uint256 missingAssetsWithPending = - validatorsChecker.getExitQueueMissingAssets(vault, pendingAssets, cumulativeTickets); + validatorsChecker.getExitQueueMissingAssets(vault, pendingAssets, 0, cumulativeTickets); // Pending assets should reduce missing assets assertGt(missingAssetsNoPending, missingAssetsWithPending, "Pending assets should reduce missing assets"); @@ -542,13 +544,14 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { uint256 initialMissingAssets = validatorsChecker.getExitQueueMissingAssets( vault, 0, // No pending assets + 0, cumulativeTickets ); // Add pending assets and check changes uint256 pendingAssets = 4 ether; uint256 updatedMissingAssets = - validatorsChecker.getExitQueueMissingAssets(vault, pendingAssets, cumulativeTickets); + validatorsChecker.getExitQueueMissingAssets(vault, pendingAssets, 0, cumulativeTickets); // Verify missing assets decrease with pending assets assertLt(updatedMissingAssets, initialMissingAssets, "Missing assets should decrease with pending assets"); @@ -566,12 +569,12 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { uint256 cumulativeTickets = validatorsChecker.getExitQueueCumulativeTickets(vault); // Get missing assets with no pending assets - uint256 missingAssetsBefore = validatorsChecker.getExitQueueMissingAssets(vault, 0, cumulativeTickets); + uint256 missingAssetsBefore = validatorsChecker.getExitQueueMissingAssets(vault, 0, 0, cumulativeTickets); // Test with large amount of pending assets (more than missing) uint256 excessPendingAssets = missingAssetsBefore * 2; uint256 missingAssetsAfter = - validatorsChecker.getExitQueueMissingAssets(vault, excessPendingAssets, cumulativeTickets); + validatorsChecker.getExitQueueMissingAssets(vault, excessPendingAssets, 0, cumulativeTickets); // No missing assets with excess pending assets assertEq(missingAssetsAfter, 0, "No missing assets with excess pending assets"); @@ -595,6 +598,7 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { uint256 missingAssets = validatorsChecker.getExitQueueMissingAssets( vault, 0, // No pending assets + 0, cumulativeTickets ); diff --git a/test/gnosis/GnoValidatorsChecker.t.sol b/test/gnosis/GnoValidatorsChecker.t.sol index 4301685e..eed8069d 100644 --- a/test/gnosis/GnoValidatorsChecker.t.sol +++ b/test/gnosis/GnoValidatorsChecker.t.sol @@ -73,6 +73,7 @@ contract GnoValidatorsCheckerTest is Test, GnoHelpers { uint256 missingAssets = validatorsChecker.getExitQueueMissingAssets( emptyVault, 0, // withdrawingAssets + 0, // No redemption assets cumulativeTickets // targetCumulativeTickets (same as current since queue is empty) ); @@ -95,6 +96,7 @@ contract GnoValidatorsCheckerTest is Test, GnoHelpers { uint256 initialMissingAssets = validatorsChecker.getExitQueueMissingAssets( prevVersionVault, 0, // withdrawingAssets + 0, // No redemption assets initialCumulativeTickets // targetCumulativeTickets ); @@ -108,6 +110,7 @@ contract GnoValidatorsCheckerTest is Test, GnoHelpers { uint256 updatedMissingAssets = validatorsChecker.getExitQueueMissingAssets( prevVersionVault, 0, // withdrawingAssets + 0, // No redemption assets initialCumulativeTickets // use same target as before for fair comparison ); From 1a68c9505f4d30898d2c40ceff8b1b921198725e Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Tue, 13 Jan 2026 14:51:58 +0200 Subject: [PATCH 02/19] Update meta vault interfaces, make meta factories permissionless --- contracts/interfaces/IEthMetaVault.sol | 53 +----------- contracts/interfaces/IEthMetaVaultFactory.sol | 6 +- contracts/interfaces/IEthPrivMetaVault.sol | 13 +++ contracts/interfaces/IGnoMetaVault.sol | 58 +------------ contracts/interfaces/IGnoMetaVaultFactory.sol | 6 +- contracts/interfaces/IGnoPrivMetaVault.sol | 13 +++ contracts/interfaces/IMetaVault.sol | 86 +++++++++++++++++++ contracts/interfaces/IOsTokenRedeemer.sol | 79 +++++++---------- contracts/interfaces/IVaultSubVaults.sol | 13 +++ contracts/libraries/Errors.sol | 1 - .../{custom => }/EthMetaVaultFactory.sol | 23 ++--- .../{custom => }/GnoMetaVaultFactory.sol | 23 ++--- contracts/vaults/modules/VaultOsToken.sol | 4 +- 13 files changed, 184 insertions(+), 194 deletions(-) create mode 100644 contracts/interfaces/IEthPrivMetaVault.sol create mode 100644 contracts/interfaces/IGnoPrivMetaVault.sol create mode 100644 contracts/interfaces/IMetaVault.sol rename contracts/vaults/ethereum/{custom => }/EthMetaVaultFactory.sol (60%) rename contracts/vaults/gnosis/{custom => }/GnoMetaVaultFactory.sol (67%) diff --git a/contracts/interfaces/IEthMetaVault.sol b/contracts/interfaces/IEthMetaVault.sol index a065f1af..ca07d179 100644 --- a/contracts/interfaces/IEthMetaVault.sol +++ b/contracts/interfaces/IEthMetaVault.sol @@ -3,63 +3,14 @@ pragma solidity ^0.8.22; import {IKeeperRewards} from "./IKeeperRewards.sol"; -import {IVaultAdmin} from "./IVaultAdmin.sol"; -import {IVaultVersion} from "./IVaultVersion.sol"; -import {IVaultFee} from "./IVaultFee.sol"; -import {IVaultState} from "./IVaultState.sol"; -import {IVaultEnterExit} from "./IVaultEnterExit.sol"; -import {IVaultOsToken} from "./IVaultOsToken.sol"; -import {IVaultSubVaults} from "./IVaultSubVaults.sol"; -import {IMulticall} from "./IMulticall.sol"; +import {IMetaVault} from "./IMetaVault.sol"; /** * @title IEthMetaVault * @author StakeWise * @notice Defines the interface for the EthMetaVault contract */ -interface IEthMetaVault is - IVaultAdmin, - IVaultVersion, - IVaultFee, - IVaultState, - IVaultEnterExit, - IVaultOsToken, - IVaultSubVaults, - IMulticall -{ - /** - * @dev Struct for deploying the EthMetaVault contract - * @param keeper The address of the Keeper contract - * @param vaultsRegistry The address of the VaultsRegistry contract - * @param osTokenVaultController The address of the OsTokenVaultController contract - * @param osTokenConfig The address of the OsTokenConfig contract - * @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract - * @param curatorsRegistry The address of the CuratorsRegistry contract - * @param exitingAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking - */ - struct EthMetaVaultConstructorArgs { - address keeper; - address vaultsRegistry; - address osTokenVaultController; - address osTokenConfig; - address osTokenVaultEscrow; - address curatorsRegistry; - uint64 exitingAssetsClaimDelay; - } - - /** - * @dev Struct for initializing the EthMetaVault contract - * @param subVaultsCurator The address of the initial sub-vaults curator - * @param capacity The Vault stops accepting deposits after exceeding the capacity - * @param feePercent The fee percent that is charged by the Vault - * @param metadataIpfsHash The IPFS hash of the Vault's metadata file - */ - struct EthMetaVaultInitParams { - address subVaultsCurator; - uint256 capacity; - uint16 feePercent; - string metadataIpfsHash; - } +interface IEthMetaVault is IMetaVault { /** * @notice Initializes or upgrades the EthMetaVault contract. Must transfer security deposit during the deployment. diff --git a/contracts/interfaces/IEthMetaVaultFactory.sol b/contracts/interfaces/IEthMetaVaultFactory.sol index 7c2b9f99..7757e608 100644 --- a/contracts/interfaces/IEthMetaVaultFactory.sol +++ b/contracts/interfaces/IEthMetaVaultFactory.sol @@ -10,12 +10,11 @@ pragma solidity ^0.8.22; interface IEthMetaVaultFactory { /** * @notice Event emitted on a MetaVault creation - * @param caller The address of the factory caller * @param admin The address of the Vault admin * @param vault The address of the created Vault * @param params The encoded parameters for initializing the Vault contract */ - event MetaVaultCreated(address indexed caller, address indexed admin, address indexed vault, bytes params); + event MetaVaultCreated(address indexed admin, address indexed vault, bytes params); /** * @notice The address of the Vault implementation contract used for proxy creation @@ -31,8 +30,7 @@ interface IEthMetaVaultFactory { /** * @notice Create Vault. Must transfer security deposit together with a call. - * @param admin The address of the Vault admin * @param params The encoded parameters for initializing the Vault contract */ - function createVault(address admin, bytes calldata params) external payable returns (address vault); + function createVault(bytes calldata params) external payable returns (address vault); } diff --git a/contracts/interfaces/IEthPrivMetaVault.sol b/contracts/interfaces/IEthPrivMetaVault.sol new file mode 100644 index 00000000..d45d3edc --- /dev/null +++ b/contracts/interfaces/IEthPrivMetaVault.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IVaultWhitelist} from "./IVaultWhitelist.sol"; +import {IEthMetaVault} from "./IEthMetaVault.sol"; + +/** + * @title IEthPrivMetaVault + * @author StakeWise + * @notice Defines the interface for the EthPrivMetaVault contract + */ +interface IEthPrivMetaVault is IEthMetaVault, IVaultWhitelist {} diff --git a/contracts/interfaces/IGnoMetaVault.sol b/contracts/interfaces/IGnoMetaVault.sol index 3bd02008..439b19a3 100644 --- a/contracts/interfaces/IGnoMetaVault.sol +++ b/contracts/interfaces/IGnoMetaVault.sol @@ -2,71 +2,19 @@ pragma solidity ^0.8.22; -import {IVaultAdmin} from "./IVaultAdmin.sol"; -import {IVaultVersion} from "./IVaultVersion.sol"; -import {IVaultFee} from "./IVaultFee.sol"; -import {IVaultState} from "./IVaultState.sol"; -import {IVaultEnterExit} from "./IVaultEnterExit.sol"; -import {IVaultOsToken} from "./IVaultOsToken.sol"; -import {IVaultSubVaults} from "./IVaultSubVaults.sol"; -import {IMulticall} from "./IMulticall.sol"; +import {IMetaVault} from "./IMetaVault.sol"; /** * @title IGnoMetaVault * @author StakeWise * @notice Defines the interface for the GnoMetaVault contract */ -interface IGnoMetaVault is - IVaultAdmin, - IVaultVersion, - IVaultFee, - IVaultState, - IVaultEnterExit, - IVaultOsToken, - IVaultSubVaults, - IMulticall -{ - /** - * @dev Struct for deploying the GnoMetaVault contract - * @param keeper The address of the Keeper contract - * @param vaultsRegistry The address of the VaultsRegistry contract - * @param osTokenVaultController The address of the OsTokenVaultController contract - * @param osTokenConfig The address of the OsTokenConfig contract - * @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract - * @param curatorsRegistry The address of the CuratorsRegistry contract - * @param gnoToken The address of the GNO token - * @param exitingAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking - */ - struct GnoMetaVaultConstructorArgs { - address keeper; - address vaultsRegistry; - address osTokenVaultController; - address osTokenConfig; - address osTokenVaultEscrow; - address curatorsRegistry; - address gnoToken; - uint64 exitingAssetsClaimDelay; - } - - /** - * @dev Struct for initializing the GnoMetaVault contract - * @param subVaultsCurator The address of the initial sub-vaults curator - * @param capacity The Vault stops accepting deposits after exceeding the capacity - * @param feePercent The fee percent that is charged by the Vault - * @param metadataIpfsHash The IPFS hash of the Vault's metadata file - */ - struct GnoMetaVaultInitParams { - address subVaultsCurator; - uint256 capacity; - uint16 feePercent; - string metadataIpfsHash; - } - +interface IGnoMetaVault is IMetaVault { /** * @notice Initializes or upgrades the GnoMetaVault contract. Must transfer security deposit during the deployment. * @param params The encoded parameters for initializing the GnoVault contract */ - function initialize(bytes calldata params) external payable; + function initialize(bytes calldata params) external; /** * @notice Deposit GNO to the Vault diff --git a/contracts/interfaces/IGnoMetaVaultFactory.sol b/contracts/interfaces/IGnoMetaVaultFactory.sol index 6996fcd9..d58f680a 100644 --- a/contracts/interfaces/IGnoMetaVaultFactory.sol +++ b/contracts/interfaces/IGnoMetaVaultFactory.sol @@ -10,12 +10,11 @@ pragma solidity ^0.8.22; interface IGnoMetaVaultFactory { /** * @notice Event emitted on a MetaVault creation - * @param caller The address of the factory caller * @param admin The address of the Vault admin * @param vault The address of the created Vault * @param params The encoded parameters for initializing the Vault contract */ - event MetaVaultCreated(address indexed caller, address indexed admin, address indexed vault, bytes params); + event MetaVaultCreated(address indexed admin, address indexed vault, bytes params); /** * @notice The address of the Vault implementation contract used for proxy creation @@ -31,8 +30,7 @@ interface IGnoMetaVaultFactory { /** * @notice Create Vault. Must transfer security deposit together with a call. - * @param admin The address of the Vault admin * @param params The encoded parameters for initializing the Vault contract */ - function createVault(address admin, bytes calldata params) external returns (address vault); + function createVault(bytes calldata params) external returns (address vault); } diff --git a/contracts/interfaces/IGnoPrivMetaVault.sol b/contracts/interfaces/IGnoPrivMetaVault.sol new file mode 100644 index 00000000..6ea8513e --- /dev/null +++ b/contracts/interfaces/IGnoPrivMetaVault.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IVaultWhitelist} from "./IVaultWhitelist.sol"; +import {IGnoMetaVault} from "./IGnoMetaVault.sol"; + +/** + * @title IGnoPrivMetaVault + * @author StakeWise + * @notice Defines the interface for the GnoPrivMetaVault contract + */ +interface IGnoPrivMetaVault is IGnoMetaVault, IVaultWhitelist {} diff --git a/contracts/interfaces/IMetaVault.sol b/contracts/interfaces/IMetaVault.sol new file mode 100644 index 00000000..49be4645 --- /dev/null +++ b/contracts/interfaces/IMetaVault.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IVaultAdmin} from "./IVaultAdmin.sol"; +import {IVaultVersion} from "./IVaultVersion.sol"; +import {IVaultFee} from "./IVaultFee.sol"; +import {IVaultState} from "./IVaultState.sol"; +import {IVaultEnterExit} from "./IVaultEnterExit.sol"; +import {IVaultOsToken} from "./IVaultOsToken.sol"; +import {IVaultSubVaults} from "./IVaultSubVaults.sol"; +import {IMulticall} from "./IMulticall.sol"; +import {ISubVaultsCurator} from "./ISubVaultsCurator.sol"; + +/** + * @title IMetaVault + * @author StakeWise + * @notice Defines the interface for the MetaVault contract + */ +interface IMetaVault is + IVaultAdmin, + IVaultVersion, + IVaultFee, + IVaultState, + IVaultEnterExit, + IVaultOsToken, + IVaultSubVaults, + IMulticall +{ + /** + * @notice Event emitted when assets are redeemed from sub-vaults + * @param assetsRedeemed The amount of assets redeemed to the meta vault + */ + event SubVaultsAssetsRedeemed(uint256 assetsRedeemed); + + /** + * @dev Struct for deploying the MetaVault contract + * @param keeper The address of the Keeper contract + * @param vaultsRegistry The address of the VaultsRegistry contract + * @param osTokenVaultController The address of the OsTokenVaultController contract + * @param osTokenConfig The address of the OsTokenConfig contract + * @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract + * @param curatorsRegistry The address of the CuratorsRegistry contract + * @param exitingAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking + */ + struct MetaVaultConstructorArgs { + address keeper; + address vaultsRegistry; + address osTokenVaultController; + address osTokenConfig; + address osTokenVaultEscrow; + address curatorsRegistry; + uint64 exitingAssetsClaimDelay; + } + + /** + * @dev Struct for initializing the MetaVault contract + * @param subVaultsCurator The address of the initial sub-vaults curator + * @param capacity The Vault stops accepting deposits after exceeding the capacity + * @param feePercent The fee percent that is charged by the Vault + * @param metadataIpfsHash The IPFS hash of the Vault's metadata file + */ + struct MetaVaultInitParams { + address subVaultsCurator; + uint256 capacity; + uint16 feePercent; + string metadataIpfsHash; + } + + /** + * @notice Calculates the required sub-vaults exit requests to fulfill the assets to redeem + * @param assetsToRedeem The amount of assets to redeem + * @return redeemRequests The array of sub-vaults exit requests + */ + function calculateSubVaultsRedemptions(uint256 assetsToRedeem) + external + view + returns (ISubVaultsCurator.ExitRequest[] memory redeemRequests); + + /** + * @notice Redeems assets from sub-vaults to the meta vault. Can only be called by the redeemer. + * @param assetsToRedeem The amount of assets to redeem to the meta vault + * @return totalRedeemedAssets The total amount of assets redeemed from sub-vaults + */ + function redeemSubVaultsAssets(uint256 assetsToRedeem) external returns (uint256 totalRedeemedAssets); +} diff --git a/contracts/interfaces/IOsTokenRedeemer.sol b/contracts/interfaces/IOsTokenRedeemer.sol index b4c36f68..1b8c35b1 100644 --- a/contracts/interfaces/IOsTokenRedeemer.sol +++ b/contracts/interfaces/IOsTokenRedeemer.sol @@ -45,28 +45,7 @@ interface IOsTokenRedeemer is IMulticall { * @param merkleRoot The Merkle root of the redeemable positions * @param ipfsHash The IPFS hash of the redeemable positions */ - event RedeemablePositionsProposed(bytes32 indexed merkleRoot, string ipfsHash); - - /** - * @notice Event emitted when the pending redeemable positions are accepted - * @param merkleRoot The Merkle root of the accepted redeemable positions - * @param ipfsHash The IPFS hash of the accepted redeemable positions - */ - event RedeemablePositionsAccepted(bytes32 indexed merkleRoot, string ipfsHash); - - /** - * @notice Event emitted when the new redeemable positions are denied - * @param merkleRoot The Merkle root of the denied redeemable positions - * @param ipfsHash The IPFS hash of the denied redeemable positions - */ - event RedeemablePositionsDenied(bytes32 indexed merkleRoot, string ipfsHash); - - /** - * @notice Event emitted when the redeemable positions are removed - * @param merkleRoot The Merkle root of the removed redeemable positions - * @param ipfsHash The IPFS hash of the removed redeemable positions - */ - event RedeemablePositionsRemoved(bytes32 indexed merkleRoot, string ipfsHash); + event RedeemablePositionsUpdated(bytes32 indexed merkleRoot, string ipfsHash); /** * @notice Event emitted on shares added to the exit queue @@ -174,8 +153,8 @@ interface IOsTokenRedeemer is IMulticall { function exitQueueTimestamp() external view returns (uint256); /** - * @notice The address that can propose redeemable OsToken positions - * @return The address of the positions manager + * @notice The address authorized to redeem OsToken positions + * @return The address of the redeemer */ function positionsManager() external view returns (address); @@ -196,6 +175,13 @@ interface IOsTokenRedeemer is IMulticall { view returns (uint256 queuedShares, uint256 unclaimedAssets, uint256 totalTickets); + /** + * @notice Calculates the missing assets in the exit queue for a target cumulative tickets. + * @param targetCumulativeTickets The target cumulative tickets in the exit queue + * @return missingAssets The number of missing assets in the exit queue + */ + function getExitQueueMissingAssets(uint256 targetCumulativeTickets) external view returns (uint256 missingAssets); + /** * @notice Checks if the exit queue can be processed * @return True if the exit queue can be processed, false otherwise @@ -209,13 +195,6 @@ interface IOsTokenRedeemer is IMulticall { */ function redeemablePositions() external view returns (bytes32 merkleRoot, string memory ipfsHash); - /** - * @notice The pending redeemable positions Merkle root and IPFS hash that is waiting to be accepted - * @return merkleRoot The Merkle root of the pending redeemable positions - * @return ipfsHash The IPFS hash of the pending redeemable positions - */ - function pendingRedeemablePositions() external view returns (bytes32 merkleRoot, string memory ipfsHash); - /** * @notice Gets the index of the exit queue for a given position ticket. * @param positionTicket The position ticket to search for @@ -244,25 +223,10 @@ interface IOsTokenRedeemer is IMulticall { function setPositionsManager(address positionsManager_) external; /** - * @notice Proposes new redeemable positions. Can only be called by the positions manager. - * @param newPositions The new redeemable positions to propose + * @notice Set new redeemable positions. Can only be called by the owner. + * @param newPositions The new redeemable positions */ - function proposeRedeemablePositions(RedeemablePositions calldata newPositions) external; - - /** - * @notice Accepts the pending redeemable positions. Can only be called by the owner. - */ - function acceptRedeemablePositions() external; - - /** - * @notice Denies the pending redeemable positions. Can only be called by the owner. - */ - function denyRedeemablePositions() external; - - /** - * @notice Removes the redeemable positions. Can only be called by the owner. - */ - function removeRedeemablePositions() external; + function setRedeemablePositions(RedeemablePositions calldata newPositions) external; /** * @notice Permit OsToken shares to be used for redemption. @@ -289,6 +253,23 @@ interface IOsTokenRedeemer is IMulticall { */ function claimExitedAssets(uint256 positionTicket, uint256 exitQueueIndex) external; + /** + * @notice Redeem OsToken shares from a specific sub-vault. Can only be called by the meta vault. + * @param subVault The address of the sub-vault + * @param osTokenShares The number of OsToken shares to redeem + */ + function redeemSubVaultOsToken(address subVault, uint256 osTokenShares) external; + + /** + * @notice Redeem assets from the sub-vaults to the meta vault. Can only be called by the positions manager. + * @param metaVault The address of the meta vault + * @param assetsToRedeem The number of assets to redeem + * @return totalRedeemedAssets The total number of redeemed assets + */ + function redeemSubVaultsAssets(address metaVault, uint256 assetsToRedeem) + external + returns (uint256 totalRedeemedAssets); + /** * @notice Redeem OsToken shares from the vault positions. * @param positions The array of OsToken positions to redeem diff --git a/contracts/interfaces/IVaultSubVaults.sol b/contracts/interfaces/IVaultSubVaults.sol index 032d09a3..a1213539 100644 --- a/contracts/interfaces/IVaultSubVaults.sol +++ b/contracts/interfaces/IVaultSubVaults.sol @@ -49,6 +49,13 @@ interface IVaultSubVaults { */ event SubVaultAdded(address indexed caller, address indexed vault); + /** + * @notice Emitted when the new meta sub-vault is proposed + * @param caller The address of the caller + * @param vault The address of the meta sub-vault + */ + event MetaSubVaultProposed(address indexed caller, address indexed vault); + /** * @notice Emitted when the sub-vault is ejecting * @param caller The address of the caller @@ -125,6 +132,12 @@ interface IVaultSubVaults { */ function addSubVault(address vault) external; + /** + * @notice Function to accept a meta sub-vault. Can only be called by the VaultsRegistry owner. + * @param metaSubVault The address of the meta sub-vault to accept + */ + function acceptMetaSubVault(address metaSubVault) external; + /** * @notice Function to remove a sub-vault. Can only be called by the admin. * All the sub-vault shares will be added to the exit queue. diff --git a/contracts/libraries/Errors.sol b/contracts/libraries/Errors.sol index 73e6ce13..07f61758 100644 --- a/contracts/libraries/Errors.sol +++ b/contracts/libraries/Errors.sol @@ -61,6 +61,5 @@ library Errors { error InvalidCurator(); error RewardsNonceIsHigher(); error InvalidRedeemablePositions(); - error RedeemablePositionsProposed(); error InvalidDelay(); } diff --git a/contracts/vaults/ethereum/custom/EthMetaVaultFactory.sol b/contracts/vaults/ethereum/EthMetaVaultFactory.sol similarity index 60% rename from contracts/vaults/ethereum/custom/EthMetaVaultFactory.sol rename to contracts/vaults/ethereum/EthMetaVaultFactory.sol index a7e19a1f..ee0da47e 100644 --- a/contracts/vaults/ethereum/custom/EthMetaVaultFactory.sol +++ b/contracts/vaults/ethereum/EthMetaVaultFactory.sol @@ -3,18 +3,17 @@ pragma solidity ^0.8.22; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol"; -import {IEthMetaVaultFactory} from "../../../interfaces/IEthMetaVaultFactory.sol"; -import {IEthMetaVault} from "../../../interfaces/IEthMetaVault.sol"; -import {IVaultsRegistry} from "../../../interfaces/IVaultsRegistry.sol"; -import {Errors} from "../../../libraries/Errors.sol"; +import {IEthMetaVaultFactory} from "../../interfaces/IEthMetaVaultFactory.sol"; +import {IEthMetaVault} from "../../interfaces/IEthMetaVault.sol"; +import {IVaultsRegistry} from "../../interfaces/IVaultsRegistry.sol"; +import {Errors} from "../../libraries/Errors.sol"; /** * @title EthMetaVaultFactory * @author StakeWise * @notice Factory for deploying Ethereum meta Vaults */ -contract EthMetaVaultFactory is Ownable2Step, IEthMetaVaultFactory { +contract EthMetaVaultFactory is IEthMetaVaultFactory { IVaultsRegistry internal immutable _vaultsRegistry; /// @inheritdoc IEthMetaVaultFactory @@ -25,30 +24,26 @@ contract EthMetaVaultFactory is Ownable2Step, IEthMetaVaultFactory { /** * @dev Constructor - * @param initialOwner The address of the contract owner * @param _implementation The implementation address of Vault * @param vaultsRegistry The address of the VaultsRegistry contract */ - constructor(address initialOwner, address _implementation, IVaultsRegistry vaultsRegistry) Ownable(initialOwner) { + constructor(address _implementation, IVaultsRegistry vaultsRegistry) { implementation = _implementation; _vaultsRegistry = vaultsRegistry; } /// @inheritdoc IEthMetaVaultFactory - function createVault(address admin, bytes calldata params) + function createVault(bytes calldata params) external payable override - onlyOwner returns (address vault) { - if (admin == address(0)) revert Errors.ZeroAddress(); - // create vault vault = address(new ERC1967Proxy(implementation, "")); // set admin so that it can be initialized in the Vault - vaultAdmin = admin; + vaultAdmin = msg.sender; // initialize Vault IEthMetaVault(vault).initialize{value: msg.value}(params); @@ -60,6 +55,6 @@ contract EthMetaVaultFactory is Ownable2Step, IEthMetaVaultFactory { _vaultsRegistry.addVault(vault); // emit event - emit MetaVaultCreated(msg.sender, admin, vault, params); + emit MetaVaultCreated(msg.sender, vault, params); } } diff --git a/contracts/vaults/gnosis/custom/GnoMetaVaultFactory.sol b/contracts/vaults/gnosis/GnoMetaVaultFactory.sol similarity index 67% rename from contracts/vaults/gnosis/custom/GnoMetaVaultFactory.sol rename to contracts/vaults/gnosis/GnoMetaVaultFactory.sol index b3ff1ba8..7e913fd2 100644 --- a/contracts/vaults/gnosis/custom/GnoMetaVaultFactory.sol +++ b/contracts/vaults/gnosis/GnoMetaVaultFactory.sol @@ -5,18 +5,17 @@ pragma solidity ^0.8.22; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol"; -import {IGnoMetaVaultFactory} from "../../../interfaces/IGnoMetaVaultFactory.sol"; -import {IGnoMetaVault} from "../../../interfaces/IGnoMetaVault.sol"; -import {IVaultsRegistry} from "../../../interfaces/IVaultsRegistry.sol"; -import {Errors} from "../../../libraries/Errors.sol"; +import {IGnoMetaVaultFactory} from "../../interfaces/IGnoMetaVaultFactory.sol"; +import {IGnoMetaVault} from "../../interfaces/IGnoMetaVault.sol"; +import {IVaultsRegistry} from "../../interfaces/IVaultsRegistry.sol"; +import {Errors} from "../../libraries/Errors.sol"; /** * @title GnoMetaVaultFactory * @author StakeWise * @notice Factory for deploying Gnosis meta Vaults */ -contract GnoMetaVaultFactory is Ownable2Step, IGnoMetaVaultFactory { +contract GnoMetaVaultFactory is IGnoMetaVaultFactory { uint256 private constant _securityDeposit = 1e9; IVaultsRegistry internal immutable _vaultsRegistry; @@ -31,13 +30,11 @@ contract GnoMetaVaultFactory is Ownable2Step, IGnoMetaVaultFactory { /** * @dev Constructor - * @param initialOwner The address of the contract owner * @param _implementation The implementation address of Vault * @param vaultsRegistry The address of the VaultsRegistry contract * @param gnoToken The address of the GNO token contract */ - constructor(address initialOwner, address _implementation, IVaultsRegistry vaultsRegistry, address gnoToken) - Ownable(initialOwner) + constructor(address _implementation, IVaultsRegistry vaultsRegistry, address gnoToken) { implementation = _implementation; _vaultsRegistry = vaultsRegistry; @@ -45,9 +42,7 @@ contract GnoMetaVaultFactory is Ownable2Step, IGnoMetaVaultFactory { } /// @inheritdoc IGnoMetaVaultFactory - function createVault(address admin, bytes calldata params) external override onlyOwner returns (address vault) { - if (admin == address(0)) revert Errors.ZeroAddress(); - + function createVault(bytes calldata params) external override returns (address vault) { // transfer GNO security deposit to the factory // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), _securityDeposit); @@ -59,7 +54,7 @@ contract GnoMetaVaultFactory is Ownable2Step, IGnoMetaVaultFactory { _gnoToken.approve(vault, _securityDeposit); // set admin so that it can be initialized in the Vault - vaultAdmin = admin; + vaultAdmin = msg.sender; // initialize Vault IGnoMetaVault(vault).initialize(params); @@ -71,6 +66,6 @@ contract GnoMetaVaultFactory is Ownable2Step, IGnoMetaVaultFactory { _vaultsRegistry.addVault(vault); // emit event - emit MetaVaultCreated(msg.sender, admin, vault, params); + emit MetaVaultCreated(msg.sender, vault, params); } } diff --git a/contracts/vaults/modules/VaultOsToken.sol b/contracts/vaults/modules/VaultOsToken.sol index 7c3e44e4..de2aeb4e 100644 --- a/contracts/vaults/modules/VaultOsToken.sol +++ b/contracts/vaults/modules/VaultOsToken.sol @@ -23,10 +23,10 @@ abstract contract VaultOsToken is VaultImmutables, VaultState, VaultEnterExit, I uint256 private constant _maxPercent = 1e18; /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - IOsTokenVaultController private immutable _osTokenVaultController; + IOsTokenVaultController internal immutable _osTokenVaultController; /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - IOsTokenConfig private immutable _osTokenConfig; + IOsTokenConfig internal immutable _osTokenConfig; /// @custom:oz-upgrades-unsafe-allow state-variable-immutable IOsTokenVaultEscrow private immutable _osTokenVaultEscrow; From 7b5948a916ecec29dc72cff8146ae272cd16cf30 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Tue, 13 Jan 2026 18:52:42 +0200 Subject: [PATCH 03/19] Fix tests --- test/EthMetaVault.t.sol | 26 +--- test/EthOsTokenRedeemer.t.sol | 196 ++++----------------------- test/VaultSubVaults.t.sol | 15 +- test/gnosis/GnoMetaVault.t.sol | 23 +--- test/gnosis/GnoOsTokenRedeemer.t.sol | 8 +- test/helpers/EthHelpers.sol | 15 +- test/helpers/GnoHelpers.sol | 18 +-- 7 files changed, 62 insertions(+), 239 deletions(-) diff --git a/test/EthMetaVault.t.sol b/test/EthMetaVault.t.sol index d722d3cd..1a398363 100644 --- a/test/EthMetaVault.t.sol +++ b/test/EthMetaVault.t.sol @@ -5,12 +5,13 @@ import {Test, stdStorage, StdStorage, console} from "forge-std/Test.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IEthMetaVault} from "../contracts/interfaces/IEthMetaVault.sol"; import {IEthVault} from "../contracts/interfaces/IEthVault.sol"; +import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; import {IVaultState} from "../contracts/interfaces/IVaultState.sol"; import {IVaultSubVaults} from "../contracts/interfaces/IVaultSubVaults.sol"; import {IVaultEnterExit} from "../contracts/interfaces/IVaultEnterExit.sol"; import {Errors} from "../contracts/libraries/Errors.sol"; -import {EthMetaVault} from "../contracts/vaults/ethereum/custom/EthMetaVault.sol"; -import {EthMetaVaultFactory} from "../contracts/vaults/ethereum/custom/EthMetaVaultFactory.sol"; +import {EthMetaVault} from "../contracts/vaults/ethereum/EthMetaVault.sol"; +import {EthMetaVaultFactory} from "../contracts/vaults/ethereum/EthMetaVaultFactory.sol"; import {BalancedCurator} from "../contracts/curators/BalancedCurator.sol"; import {CuratorsRegistry} from "../contracts/curators/CuratorsRegistry.sol"; import {EthHelpers} from "./helpers/EthHelpers.sol"; @@ -54,7 +55,7 @@ contract EthMetaVaultTest is Test, EthHelpers { // Deploy meta vault bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, // 10% @@ -86,23 +87,6 @@ contract EthMetaVaultTest is Test, EthHelpers { return _createVault(VaultType.EthVault, _admin, initParams, false); } - function test_deployWithZeroAdmin() public { - // Attempt to deploy a meta vault with zero admin - bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ - subVaultsCurator: curator, - capacity: 1000 ether, - feePercent: 1000, - metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" - }) - ); - - EthMetaVaultFactory factory = _getOrCreateMetaFactory(VaultType.EthMetaVault); - vm.deal(address(this), address(this).balance + _securityDeposit); - vm.expectRevert(Errors.ZeroAddress.selector); - factory.createVault{value: _securityDeposit}(address(0), initParams); - } - function test_deployment() public view { // Verify the vault was deployed correctly assertEq(metaVault.vaultId(), keccak256("EthMetaVault"), "Incorrect vault ID"); @@ -290,7 +274,7 @@ contract EthMetaVaultTest is Test, EthHelpers { // Test with empty sub vaults // Create a new meta vault without sub vaults bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, diff --git a/test/EthOsTokenRedeemer.t.sol b/test/EthOsTokenRedeemer.t.sol index dea49dcc..74aad449 100644 --- a/test/EthOsTokenRedeemer.t.sol +++ b/test/EthOsTokenRedeemer.t.sol @@ -56,7 +56,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { // Deploy OsTokenRedeemer osTokenRedeemer = - new EthOsTokenRedeemer(_osToken, address(contracts.osTokenVaultController), owner, EXIT_QUEUE_UPDATE_DELAY); + new EthOsTokenRedeemer(address(contracts.vaultsRegistry), _osToken, address(contracts.osTokenVaultController), owner, EXIT_QUEUE_UPDATE_DELAY); // Set up manager vm.prank(owner); @@ -116,12 +116,9 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { _depositToVault(address(vault2), DEPOSIT_AMOUNT, user3, user3); } - function _proposeAndAcceptPositions(IOsTokenRedeemer.RedeemablePositions memory positions) internal { - vm.prank(positionsManager); - osTokenRedeemer.proposeRedeemablePositions(positions); - + function _setRedeemablePositions(IOsTokenRedeemer.RedeemablePositions memory positions) internal { vm.prank(owner); - osTokenRedeemer.acceptRedeemablePositions(); + osTokenRedeemer.setRedeemablePositions(positions); } function _hashPair(bytes32 leaf1, bytes32 leaf2) internal pure returns (bytes32) { @@ -164,13 +161,14 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: merkleRoot, ipfsHash: TEST_IPFS_HASH}); - _proposeAndAcceptPositions(redeemablePositions); + _setRedeemablePositions(redeemablePositions); uint256 redeemedSharesBefore = osTokenRedeemer.redeemedShares(); uint256 redeemedAssetsBefore = osTokenRedeemer.redeemedAssets(); bytes32[] memory proof = new bytes32[](0); bool[] memory proofFlags = new bool[](0); + vm.prank(positionsManager); osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); uint256 assets = contracts.osTokenVaultController.convertToAssets(shares); @@ -197,7 +195,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { uint256 invalidExitQueueDelay = uint256(type(uint64).max) + 1; vm.expectRevert(Errors.InvalidDelay.selector); - new EthOsTokenRedeemer(_osToken, address(contracts.osTokenVaultController), owner, invalidExitQueueDelay); + new EthOsTokenRedeemer(address(contracts.vaultsRegistry),_osToken, address(contracts.osTokenVaultController), owner, invalidExitQueueDelay); } function test_setPositionsManager_notOwner() public { @@ -234,181 +232,35 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { assertEq(osTokenRedeemer.positionsManager(), newManager); } - function test_proposeRedeemablePositions_notPositionsManager() public { - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - - vm.prank(user1); - vm.expectRevert(Errors.AccessDenied.selector); - osTokenRedeemer.proposeRedeemablePositions(positions); - } - - function test_proposeRedeemablePositions_invalidPositions() public { - // Test with empty merkle root - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: EMPTY_ROOT, ipfsHash: TEST_IPFS_HASH}); - - vm.prank(positionsManager); - vm.expectRevert(Errors.InvalidRedeemablePositions.selector); - osTokenRedeemer.proposeRedeemablePositions(positions); - - // Test with empty IPFS hash - positions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: ""}); - - vm.prank(positionsManager); - vm.expectRevert(Errors.InvalidRedeemablePositions.selector); - osTokenRedeemer.proposeRedeemablePositions(positions); - } - - function test_proposeRedeemablePositions_hasPendingProposal() public { - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - - vm.prank(positionsManager); - osTokenRedeemer.proposeRedeemablePositions(positions); - - // Try to propose again - vm.prank(positionsManager); - vm.expectRevert(Errors.RedeemablePositionsProposed.selector); - osTokenRedeemer.proposeRedeemablePositions(positions); - } - - function test_proposeRedeemablePositions_sameValue() public { - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - - // First set a root - _proposeAndAcceptPositions(positions); - - // Try to propose the same root again - vm.prank(positionsManager); - vm.expectRevert(Errors.ValueNotChanged.selector); - osTokenRedeemer.proposeRedeemablePositions(positions); - } - - function test_proposeRedeemablePositions_success() public { - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - - vm.expectEmit(true, true, false, true); - emit IOsTokenRedeemer.RedeemablePositionsProposed(positions.merkleRoot, positions.ipfsHash); - - vm.prank(positionsManager); - _startSnapshotGas("EthOsTokenRedeemerTest_test_proposeRedeemablePositions_success"); - osTokenRedeemer.proposeRedeemablePositions(positions); - _stopSnapshotGas(); - - (bytes32 pendingRoot, string memory pendingIpfs) = osTokenRedeemer.pendingRedeemablePositions(); - assertEq(pendingRoot, positions.merkleRoot); - assertEq(pendingIpfs, positions.ipfsHash); - } - - function test_acceptRedeemablePositions_notOwner() public { + function test_setRedeemablePositions_notOwner() public { IOsTokenRedeemer.RedeemablePositions memory positions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); vm.prank(positionsManager); - osTokenRedeemer.proposeRedeemablePositions(positions); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, positionsManager)); + osTokenRedeemer.setRedeemablePositions(positions); vm.prank(user1); vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, user1)); - osTokenRedeemer.acceptRedeemablePositions(); - } - - function test_acceptRedeemablePositions_noPendingProposal() public { - vm.prank(owner); - vm.expectRevert(Errors.InvalidRedeemablePositions.selector); - osTokenRedeemer.acceptRedeemablePositions(); + osTokenRedeemer.setRedeemablePositions(positions); } - function test_acceptRedeemablePositions_success() public { + function test_setRedeemablePositions_success() public { IOsTokenRedeemer.RedeemablePositions memory positions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - vm.prank(positionsManager); - osTokenRedeemer.proposeRedeemablePositions(positions); - vm.expectEmit(true, true, false, true); - emit IOsTokenRedeemer.RedeemablePositionsAccepted(positions.merkleRoot, positions.ipfsHash); + emit IOsTokenRedeemer.RedeemablePositionsUpdated(positions.merkleRoot, positions.ipfsHash); vm.prank(owner); - _startSnapshotGas("EthOsTokenRedeemerTest_test_acceptRedeemablePositions_success"); - osTokenRedeemer.acceptRedeemablePositions(); + _startSnapshotGas("EthOsTokenRedeemerTest_test_setRedeemablePositions_success"); + osTokenRedeemer.setRedeemablePositions(positions); _stopSnapshotGas(); // Verify positions were accepted (bytes32 currentRoot, string memory currentIpfs) = osTokenRedeemer.redeemablePositions(); assertEq(currentRoot, positions.merkleRoot); assertEq(currentIpfs, positions.ipfsHash); - - // Verify pending positions were cleared - (bytes32 pendingRoot, string memory pendingIpfs) = osTokenRedeemer.pendingRedeemablePositions(); - assertEq(pendingRoot, EMPTY_ROOT); - assertEq(pendingIpfs, ""); - } - - function test_denyRedeemablePositions_notOwner() public { - vm.prank(user1); - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, user1)); - osTokenRedeemer.denyRedeemablePositions(); - } - - function test_denyRedeemablePositions_noPendingProposal() public { - // Should not revert even if no pending positions - vm.prank(owner); - osTokenRedeemer.denyRedeemablePositions(); - } - - function test_denyRedeemablePositions_success() public { - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - - vm.prank(positionsManager); - osTokenRedeemer.proposeRedeemablePositions(positions); - - vm.expectEmit(true, true, false, true); - emit IOsTokenRedeemer.RedeemablePositionsDenied(positions.merkleRoot, positions.ipfsHash); - - vm.prank(owner); - _startSnapshotGas("EthOsTokenRedeemerTest_test_denyRedeemablePositions_success"); - osTokenRedeemer.denyRedeemablePositions(); - _stopSnapshotGas(); - - // Verify pending positions were cleared - (bytes32 pendingRoot, string memory pendingIpfs) = osTokenRedeemer.pendingRedeemablePositions(); - assertEq(pendingRoot, EMPTY_ROOT); - assertEq(pendingIpfs, ""); - } - - function test_removeRedeemablePositions_notOwner() public { - vm.prank(user1); - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, user1)); - osTokenRedeemer.removeRedeemablePositions(); - } - - function test_removeRedeemablePositions_noPendingProposal() public { - vm.prank(owner); - osTokenRedeemer.removeRedeemablePositions(); - } - - function test_removeRedeemablePositions_success() public { - IOsTokenRedeemer.RedeemablePositions memory positions = - IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); - - _proposeAndAcceptPositions(positions); - - vm.expectEmit(true, true, false, true); - emit IOsTokenRedeemer.RedeemablePositionsRemoved(positions.merkleRoot, positions.ipfsHash); - - vm.prank(owner); - _startSnapshotGas("EthOsTokenRedeemerTest_test_removeRedeemablePositions_success"); - osTokenRedeemer.removeRedeemablePositions(); - _stopSnapshotGas(); - - // Verify positions were removed - (bytes32 currentRoot, string memory currentIpfsHash) = osTokenRedeemer.redeemablePositions(); - assertEq(currentRoot, EMPTY_ROOT); - assertEq(currentIpfsHash, ""); } function test_permitOsToken_success() public { @@ -727,10 +579,10 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { keccak256(bytes.concat(keccak256(abi.encode(osTokenRedeemer.nonce(), address(vault), 1 ether, user1)))); IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: leaf, ipfsHash: TEST_IPFS_HASH}); - _proposeAndAcceptPositions(redeemablePositions); + _setRedeemablePositions(redeemablePositions); // Call redeemOsTokenPositions with no queued shares - should return early - vm.prank(user1); + vm.prank(positionsManager); osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); // Verify no shares were redeemed @@ -759,7 +611,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { uint256 queuedSharesBefore = osTokenRedeemer.queuedShares(); - vm.prank(user1); + vm.prank(positionsManager); osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); // Verify nothing was redeemed @@ -796,14 +648,14 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { keccak256(bytes.concat(keccak256(abi.encode(osTokenRedeemer.nonce(), address(vault), 1 ether, user1)))); IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: leaf, ipfsHash: TEST_IPFS_HASH}); - _proposeAndAcceptPositions(redeemablePositions); + _setRedeemablePositions(redeemablePositions); bytes32[] memory proof = new bytes32[](0); bool[] memory proofFlags = new bool[](0); uint256 queuedSharesBefore = osTokenRedeemer.queuedShares(); - vm.prank(user1); + vm.prank(positionsManager); osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); // Verify nothing was redeemed @@ -841,7 +693,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { ); IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: leaf, ipfsHash: TEST_IPFS_HASH}); - _proposeAndAcceptPositions(redeemablePositions); + _setRedeemablePositions(redeemablePositions); // Provide wrong proof bytes32[] memory proof = new bytes32[](1); @@ -886,7 +738,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { ); IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: leaf, ipfsHash: TEST_IPFS_HASH}); - _proposeAndAcceptPositions(redeemablePositions); + _setRedeemablePositions(redeemablePositions); bytes32[] memory proof = new bytes32[](0); bool[] memory proofFlags = new bool[](0); @@ -900,7 +752,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { emit IOsTokenRedeemer.OsTokenPositionsRedeemed(expectedRedeemedShares, expectedRedeemedAssets); // Redeem position - vm.prank(user1); + vm.prank(positionsManager); _startSnapshotGas("EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition"); osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); _stopSnapshotGas(); @@ -970,7 +822,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: merkleRoot, ipfsHash: TEST_IPFS_HASH}); - _proposeAndAcceptPositions(redeemablePositions); + _setRedeemablePositions(redeemablePositions); // Create merkle proof bytes32[] memory proof = new bytes32[](0); @@ -986,7 +838,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { uint256 redeemedSharesBefore = osTokenRedeemer.redeemedShares(); // Redeem positions - vm.prank(user1); + vm.prank(positionsManager); _startSnapshotGas("EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions"); osTokenRedeemer.redeemOsTokenPositions(_positions, proof, proofFlags); _stopSnapshotGas(); diff --git a/test/VaultSubVaults.t.sol b/test/VaultSubVaults.t.sol index a02da081..8c3b9bb0 100644 --- a/test/VaultSubVaults.t.sol +++ b/test/VaultSubVaults.t.sol @@ -10,8 +10,9 @@ import {IKeeperRewards} from "../contracts/interfaces/IKeeperRewards.sol"; import {IVaultState} from "../contracts/interfaces/IVaultState.sol"; import {IVaultVersion} from "../contracts/interfaces/IVaultVersion.sol"; import {IVaultEnterExit} from "../contracts/interfaces/IVaultEnterExit.sol"; +import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; import {Errors} from "../contracts/libraries/Errors.sol"; -import {EthMetaVault} from "../contracts/vaults/ethereum/custom/EthMetaVault.sol"; +import {EthMetaVault} from "../contracts/vaults/ethereum/EthMetaVault.sol"; import {BalancedCurator} from "../contracts/curators/BalancedCurator.sol"; import {CuratorsRegistry} from "../contracts/curators/CuratorsRegistry.sol"; import {EthHelpers} from "./helpers/EthHelpers.sol"; @@ -51,7 +52,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { // Deploy meta vault bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, // 10% @@ -287,7 +288,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_addSubVault_firstSubVault() internal { // create new meta vault bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, // 10% @@ -541,7 +542,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_depositToSubVaults_emptySubVaults() public { // Setup: Create a new meta vault bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, // 10% @@ -812,7 +813,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_updateState_noSubVaults() public { // Create a new meta vault without any sub vaults bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, // 10% @@ -1401,7 +1402,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_addSubVault_metaVaultAsSubVault_notCollateralized() public { // Setup: Create meta vault but don't collateralize it bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: type(uint256).max, feePercent: 0, @@ -1671,7 +1672,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function _setupMetaSubVault(address _admin) internal returns (address metaSubVault) { // Deploy meta vault that will be used as sub vault bytes memory initParams = abi.encode( - IEthMetaVault.EthMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: type(uint256).max, feePercent: 0, // 0% diff --git a/test/gnosis/GnoMetaVault.t.sol b/test/gnosis/GnoMetaVault.t.sol index a9145d47..5cca0ce0 100644 --- a/test/gnosis/GnoMetaVault.t.sol +++ b/test/gnosis/GnoMetaVault.t.sol @@ -10,9 +10,10 @@ import {IGnoVault} from "../../contracts/interfaces/IGnoVault.sol"; import {IVaultState} from "../../contracts/interfaces/IVaultState.sol"; import {IVaultSubVaults} from "../../contracts/interfaces/IVaultSubVaults.sol"; import {IVaultEnterExit} from "../../contracts/interfaces/IVaultEnterExit.sol"; +import {IMetaVault} from "../../contracts/interfaces/IMetaVault.sol"; import {Errors} from "../../contracts/libraries/Errors.sol"; -import {GnoMetaVault} from "../../contracts/vaults/gnosis/custom/GnoMetaVault.sol"; -import {GnoMetaVaultFactory} from "../../contracts/vaults/gnosis/custom/GnoMetaVaultFactory.sol"; +import {GnoMetaVault} from "../../contracts/vaults/gnosis/GnoMetaVault.sol"; +import {GnoMetaVaultFactory} from "../../contracts/vaults/gnosis/GnoMetaVaultFactory.sol"; import {BalancedCurator} from "../../contracts/curators/BalancedCurator.sol"; import {CuratorsRegistry} from "../../contracts/curators/CuratorsRegistry.sol"; import {GnoHelpers} from "../helpers/GnoHelpers.sol"; @@ -60,7 +61,7 @@ contract GnoMetaVaultTest is Test, GnoHelpers { // Deploy meta vault bytes memory initParams = abi.encode( - IGnoMetaVault.GnoMetaVaultInitParams({ + IMetaVault.MetaVaultInitParams({ subVaultsCurator: curator, capacity: 1000 ether, feePercent: 1000, // 10% @@ -92,22 +93,6 @@ contract GnoMetaVaultTest is Test, GnoHelpers { return _createVault(VaultType.GnoVault, _admin, initParams, false); } - function test_deployWithZeroAdmin() public { - // Attempt to deploy with zero admin - bytes memory initParams = abi.encode( - IGnoMetaVault.GnoMetaVaultInitParams({ - subVaultsCurator: curator, - capacity: 1000 ether, - feePercent: 1000, // 10% - metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" - }) - ); - - GnoMetaVaultFactory factory = _getOrCreateMetaFactory(VaultType.GnoMetaVault); - contracts.gnoToken.approve(address(factory), _securityDeposit); - vm.expectRevert(abi.encodeWithSelector(Errors.ZeroAddress.selector)); - factory.createVault(address(0), initParams); - } function test_deployment() public view { // Verify the vault was deployed correctly diff --git a/test/gnosis/GnoOsTokenRedeemer.t.sol b/test/gnosis/GnoOsTokenRedeemer.t.sol index f746c18d..e56038ba 100644 --- a/test/gnosis/GnoOsTokenRedeemer.t.sol +++ b/test/gnosis/GnoOsTokenRedeemer.t.sol @@ -48,6 +48,7 @@ contract GnoOsTokenRedeemerTest is Test, GnoHelpers { // Deploy GnoOsTokenRedeemer osTokenRedeemer = new GnoOsTokenRedeemer( address(contracts.gnoToken), + address(contracts.vaultsRegistry), _osToken, address(contracts.osTokenVaultController), owner, @@ -203,11 +204,8 @@ contract GnoOsTokenRedeemerTest is Test, GnoHelpers { IOsTokenRedeemer.RedeemablePositions memory redeemablePositions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: leaf, ipfsHash: "QmTest123"}); - vm.prank(positionsManager); - osTokenRedeemer.proposeRedeemablePositions(redeemablePositions); - vm.prank(owner); - osTokenRedeemer.acceptRedeemablePositions(); + osTokenRedeemer.setRedeemablePositions(redeemablePositions); bytes32[] memory proof = new bytes32[](0); bool[] memory proofFlags = new bool[](0); @@ -221,7 +219,7 @@ contract GnoOsTokenRedeemerTest is Test, GnoHelpers { emit IOsTokenRedeemer.OsTokenPositionsRedeemed(expectedRedeemedShares, expectedRedeemedAssets); // Redeem position - vm.prank(user); + vm.prank(positionsManager); _startSnapshotGas("GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition"); osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); _stopSnapshotGas(); diff --git a/test/helpers/EthHelpers.sol b/test/helpers/EthHelpers.sol index 625e04de..42390093 100644 --- a/test/helpers/EthHelpers.sol +++ b/test/helpers/EthHelpers.sol @@ -12,6 +12,7 @@ import {ISharedMevEscrow} from "../../contracts/interfaces/ISharedMevEscrow.sol" import {IEthValidatorsRegistry} from "../../contracts/interfaces/IEthValidatorsRegistry.sol"; import {IKeeperRewards} from "../../contracts/interfaces/IKeeperRewards.sol"; import {IVaultState} from "../../contracts/interfaces/IVaultState.sol"; +import {IMetaVault} from "../../contracts/interfaces/IMetaVault.sol"; import {IConsolidationsChecker} from "../../contracts/interfaces/IConsolidationsChecker.sol"; import {ConsolidationsChecker} from "../../contracts/validators/ConsolidationsChecker.sol"; import {EthBlocklistErc20Vault} from "../../contracts/vaults/ethereum/EthBlocklistErc20Vault.sol"; @@ -23,8 +24,8 @@ import {EthPrivVault} from "../../contracts/vaults/ethereum/EthPrivVault.sol"; import {EthVault, IEthVault} from "../../contracts/vaults/ethereum/EthVault.sol"; import {EthVaultFactory} from "../../contracts/vaults/ethereum/EthVaultFactory.sol"; import {IEthFoxVault, EthFoxVault} from "../../contracts/vaults/ethereum/custom/EthFoxVault.sol"; -import {IEthMetaVault, EthMetaVault} from "../../contracts/vaults/ethereum/custom/EthMetaVault.sol"; -import {EthMetaVaultFactory} from "../../contracts/vaults/ethereum/custom/EthMetaVaultFactory.sol"; +import {EthMetaVault} from "../../contracts/vaults/ethereum/EthMetaVault.sol"; +import {EthMetaVaultFactory} from "../../contracts/vaults/ethereum/EthMetaVaultFactory.sol"; import {Keeper} from "../../contracts/keeper/Keeper.sol"; import {ValidatorsConsolidationsMock} from "../../contracts/mocks/ValidatorsConsolidationsMock.sol"; import {ValidatorsHelpers} from "./ValidatorsHelpers.sol"; @@ -145,7 +146,7 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { } address impl = _getOrCreateVaultImpl(_vaultType); - EthMetaVaultFactory factory = new EthMetaVaultFactory(address(this), impl, IVaultsRegistry(_vaultsRegistry)); + EthMetaVaultFactory factory = new EthMetaVaultFactory(impl, IVaultsRegistry(_vaultsRegistry)); _vaultFactories[_vaultType] = address(factory); @@ -314,8 +315,9 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { address vaultAddress; if (vaultType == VaultType.EthMetaVault) { EthMetaVaultFactory factory = _getOrCreateMetaFactory(vaultType); - vm.deal(address(this), address(this).balance + _securityDeposit); - vaultAddress = factory.createVault{value: _securityDeposit}(admin, initParams); + vm.deal(admin, admin.balance + _securityDeposit); + vm.prank(admin); + vaultAddress = factory.createVault{value: _securityDeposit}(initParams); } else { EthVaultFactory factory = _getOrCreateFactory(vaultType); vm.deal(admin, admin.balance + _securityDeposit); @@ -438,8 +440,7 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { ); impl = address(new EthFoxVault(ethFoxVaultArgs)); } else if (_vaultType == VaultType.EthMetaVault) { - IEthMetaVault.EthMetaVaultConstructorArgs memory ethMetaVaultArgs = IEthMetaVault - .EthMetaVaultConstructorArgs( + IMetaVault.MetaVaultConstructorArgs memory ethMetaVaultArgs = IMetaVault.MetaVaultConstructorArgs( _keeper, _vaultsRegistry, _osTokenVaultController, diff --git a/test/helpers/GnoHelpers.sol b/test/helpers/GnoHelpers.sol index 97a347fc..feedf686 100644 --- a/test/helpers/GnoHelpers.sol +++ b/test/helpers/GnoHelpers.sol @@ -14,6 +14,7 @@ import {IGnoValidatorsRegistry} from "../../contracts/interfaces/IGnoValidatorsR import {IKeeperRewards} from "../../contracts/interfaces/IKeeperRewards.sol"; import {IVaultState} from "../../contracts/interfaces/IVaultState.sol"; import {IConsolidationsChecker} from "../../contracts/interfaces/IConsolidationsChecker.sol"; +import {IMetaVault} from "../../contracts/interfaces/IMetaVault.sol"; import {ConsolidationsChecker} from "../../contracts/validators/ConsolidationsChecker.sol"; import {GnoBlocklistErc20Vault} from "../../contracts/vaults/gnosis/GnoBlocklistErc20Vault.sol"; import {GnoBlocklistVault} from "../../contracts/vaults/gnosis/GnoBlocklistVault.sol"; @@ -22,8 +23,8 @@ import {GnoGenesisVault} from "../../contracts/vaults/gnosis/GnoGenesisVault.sol import {GnoPrivErc20Vault} from "../../contracts/vaults/gnosis/GnoPrivErc20Vault.sol"; import {GnoPrivVault} from "../../contracts/vaults/gnosis/GnoPrivVault.sol"; import {GnoVault, IGnoVault} from "../../contracts/vaults/gnosis/GnoVault.sol"; -import {IGnoMetaVault, GnoMetaVault} from "../../contracts/vaults/gnosis/custom/GnoMetaVault.sol"; -import {GnoMetaVaultFactory} from "../../contracts/vaults/gnosis/custom/GnoMetaVaultFactory.sol"; +import {GnoMetaVault} from "../../contracts/vaults/gnosis/GnoMetaVault.sol"; +import {GnoMetaVaultFactory} from "../../contracts/vaults/gnosis/GnoMetaVaultFactory.sol"; import {GnoVaultFactory} from "../../contracts/vaults/gnosis/GnoVaultFactory.sol"; import {Keeper} from "../../contracts/keeper/Keeper.sol"; import {ValidatorsConsolidationsMock} from "../../contracts/mocks/ValidatorsConsolidationsMock.sol"; @@ -205,7 +206,7 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { address impl = _getOrCreateVaultImpl(_vaultType); GnoMetaVaultFactory factory = - new GnoMetaVaultFactory(address(this), impl, IVaultsRegistry(_vaultsRegistry), _gnoToken); + new GnoMetaVaultFactory(impl, IVaultsRegistry(_vaultsRegistry), _gnoToken); _vaultFactories[_vaultType] = address(factory); @@ -328,8 +329,10 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { address vaultAddress; if (vaultType == VaultType.GnoMetaVault) { GnoMetaVaultFactory factory = _getOrCreateMetaFactory(vaultType); + vm.startPrank(admin); IERC20(_gnoToken).approve(address(factory), _securityDeposit); - vaultAddress = factory.createVault(admin, initParams); + vaultAddress = factory.createVault(initParams); + vm.stopPrank(); } else { GnoVaultFactory factory = _getOrCreateFactory(vaultType); vm.startPrank(admin); @@ -425,18 +428,17 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { } else if (_vaultType == VaultType.GnoPrivErc20Vault) { impl = address(new GnoPrivErc20Vault(gnoErc20Args)); } else if (_vaultType == VaultType.GnoMetaVault) { - IGnoMetaVault.GnoMetaVaultConstructorArgs memory gnoMetaVaultArgs = IGnoMetaVault - .GnoMetaVaultConstructorArgs( + IMetaVault.MetaVaultConstructorArgs memory gnoMetaVaultArgs = IMetaVault + .MetaVaultConstructorArgs( _keeper, _vaultsRegistry, _osTokenVaultController, _osTokenConfig, _osTokenVaultEscrow, _curatorsRegistry, - _gnoToken, uint64(_exitingAssetsClaimDelay) ); - impl = address(new GnoMetaVault(gnoMetaVaultArgs)); + impl = address(new GnoMetaVault(_gnoToken, gnoMetaVaultArgs)); } _vaultImplementations[_vaultType] = impl; From fab459039ce147b6bf9946dfa5fc9aba8f17fc25 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Tue, 13 Jan 2026 19:00:10 +0200 Subject: [PATCH 04/19] Add SubVaultExits library for managing meta vault exit queues --- contracts/libraries/SubVaultExits.sol | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 contracts/libraries/SubVaultExits.sol diff --git a/contracts/libraries/SubVaultExits.sol b/contracts/libraries/SubVaultExits.sol new file mode 100644 index 00000000..4a3bcf94 --- /dev/null +++ b/contracts/libraries/SubVaultExits.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {Packing} from "@openzeppelin/contracts/utils/Packing.sol"; +import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import {Errors} from "./Errors.sol"; + +/** + * @title SubVaultExits + * @author StakeWise + * @notice Includes the common functionality for managing the meta vault sub-vaults exits + */ +library SubVaultExits { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + + /** + * @dev Fetches the sub-vault exit data + * @param subVaultsExits The mapping of sub-vault exits queues + * @param vault The address of the sub-vault + * @return positionTicket The position ticket of the sub-vault + * @return shares The shares to be exited from the sub-vault + */ + function peekSubVaultExit( + mapping(address vault => DoubleEndedQueue.Bytes32Deque) storage subVaultsExits, + address vault + ) internal view returns (uint160 positionTicket, uint96 shares) { + if (subVaultsExits[vault].empty()) { + return (0, 0); + } + bytes32 packed = subVaultsExits[vault].front(); + positionTicket = uint160(Packing.extract_32_20(packed, 0)); + shares = uint96(Packing.extract_32_12(packed, 20)); + } + + /** + * @dev Stores the sub-vault exit data + * @param subVaultsExits The mapping of sub-vault exits queues + * @param vault The address of the sub-vault + * @param positionTicket The position ticket of the sub-vault + * @param shares The shares to be exited from the sub-vault + * @param front Whether to insert the exit data at the front of the queue + */ + function pushSubVaultExit( + mapping(address vault => DoubleEndedQueue.Bytes32Deque) storage subVaultsExits, + address vault, + uint160 positionTicket, + uint96 shares, + bool front + ) internal { + if (shares == 0) revert Errors.InvalidShares(); + bytes32 packed = Packing.pack_20_12(bytes20(positionTicket), bytes12(shares)); + if (front) { + subVaultsExits[vault].pushFront(packed); + } else { + subVaultsExits[vault].pushBack(packed); + } + } + + /** + * @dev Removes the sub-vault exit data + * @param subVaultsExits The mapping of sub-vault exits queues + * @param vault The address of the sub-vault + * @return positionTicket The position ticket of the sub-vault + * @return shares The shares to be exited from the sub-vault + */ + function popSubVaultExit( + mapping(address vault => DoubleEndedQueue.Bytes32Deque) storage subVaultsExits, + address vault + ) internal returns (uint160 positionTicket, uint96 shares) { + bytes32 packed = subVaultsExits[vault].popFront(); + positionTicket = uint160(Packing.extract_32_20(packed, 0)); + shares = uint96(Packing.extract_32_12(packed, 20)); + } +} From 71af5a2cbc01eaf6fbcb285c736632d0adb15b03 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Jan 2026 09:40:50 +0200 Subject: [PATCH 05/19] Refactor meta vault logic into SubVaultUtils library and MetaVault base --- contracts/interfaces/IVaultSubVaults.sol | 19 ++ contracts/libraries/SubVaultUtils.sol | 338 ++++++++++++++++++++ contracts/vaults/base/MetaVault.sol | 165 ++++++++++ contracts/vaults/modules/VaultSubVaults.sol | 296 +++++++---------- 4 files changed, 634 insertions(+), 184 deletions(-) create mode 100644 contracts/libraries/SubVaultUtils.sol create mode 100644 contracts/vaults/base/MetaVault.sol diff --git a/contracts/interfaces/IVaultSubVaults.sol b/contracts/interfaces/IVaultSubVaults.sol index a1213539..1d79a8a6 100644 --- a/contracts/interfaces/IVaultSubVaults.sol +++ b/contracts/interfaces/IVaultSubVaults.sol @@ -56,6 +56,13 @@ interface IVaultSubVaults { */ event MetaSubVaultProposed(address indexed caller, address indexed vault); + /** + * @notice Emitted when the meta sub-vault is rejected + * @param caller The address of the caller + * @param vault The address of the meta sub-vault + */ + event MetaSubVaultRejected(address indexed caller, address indexed vault); + /** * @notice Emitted when the sub-vault is ejecting * @param caller The address of the caller @@ -89,6 +96,12 @@ interface IVaultSubVaults { */ function ejectingSubVault() external view returns (address); + /** + * @notice Pending meta sub-vault waiting for approval + * @return The address of the pending meta sub-vault + */ + function pendingMetaSubVault() external view returns (address); + /** * @notice Function to get the list sub-vaults * @return An array of addresses of the sub-vaults @@ -138,6 +151,12 @@ interface IVaultSubVaults { */ function acceptMetaSubVault(address metaSubVault) external; + /** + * @notice Function to reject a meta sub-vault. Can only be called by the VaultsRegistry owner or admin. + * @param metaSubVault The address of the meta sub-vault to reject + */ + function rejectMetaSubVault(address metaSubVault) external; + /** * @notice Function to remove a sub-vault. Can only be called by the admin. * All the sub-vault shares will be added to the exit queue. diff --git a/contracts/libraries/SubVaultUtils.sol b/contracts/libraries/SubVaultUtils.sol new file mode 100644 index 00000000..6824f775 --- /dev/null +++ b/contracts/libraries/SubVaultUtils.sol @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import {IVaultsRegistry} from "../interfaces/IVaultsRegistry.sol"; +import {IVaultSubVaults} from "../interfaces/IVaultSubVaults.sol"; +import {IVaultState} from "../interfaces/IVaultState.sol"; +import {IVaultOsToken} from "../interfaces/IVaultOsToken.sol"; +import {IVaultEnterExit} from "../interfaces/IVaultEnterExit.sol"; +import {ISubVaultsCurator} from "../interfaces/ISubVaultsCurator.sol"; +import {IOsTokenVaultController} from "../interfaces/IOsTokenVaultController.sol"; +import {IOsTokenRedeemer} from "../interfaces/IOsTokenRedeemer.sol"; +import {IKeeperRewards} from "../interfaces/IKeeperRewards.sol"; +import {SubVaultExits} from "./SubVaultExits.sol"; +import {Errors} from "./Errors.sol"; + +/** + * @title SubVaultUtils + * @author StakeWise + * @notice Includes the utility functions for managing the meta vault sub-vaults + */ +library SubVaultUtils { + using EnumerableSet for EnumerableSet.AddressSet; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + + uint256 private constant _maxSubVaults = 50; + + /** + * @dev Validates the addition of a sub-vault + * @param subVaults The set of currently added sub-vaults + * @param vaultsRegistry The address of the VaultsRegistry contract + * @param keeper The address of the Keeper contract + * @param vault The address of the sub-vault to be added + */ + function validateSubVault( + EnumerableSet.AddressSet storage subVaults, + address vaultsRegistry, + address keeper, + address vault + ) external view { + // check whether the vault is registered in the registry + if (vault == address(0) || vault == address(this) || !IVaultsRegistry(vaultsRegistry).vaults(vault)) { + revert Errors.InvalidVault(); + } + + // check whether the vault is not already added + if (subVaults.contains(vault)) { + revert Errors.AlreadyAdded(); + } + + // check whether the vault is not exceeding the limit + uint256 subVaultsCount = subVaults.length(); + if (subVaultsCount >= _maxSubVaults) { + revert Errors.CapacityExceeded(); + } + + // check whether vault is collateralized + if (!_isSubVaultCollateralized(keeper, vault)) { + revert Errors.NotCollateralized(); + } + + // check whether legacy exit queue is processed, will revert if vault doesn't have `getExitQueueData` function + (,, uint128 totalExitingTickets, uint128 totalExitingAssets,) = IVaultState(vault).getExitQueueData(); + if (totalExitingTickets != 0 || totalExitingAssets != 0) { + revert Errors.ExitRequestNotProcessed(); + } + } + + /** + * @dev Returns the balances of the given sub-vaults + * @param subVaultsStates The mapping of sub-vault addresses to their states + * @param vaults The addresses of the sub-vaults + * @param calcNewTotalAssets Whether to calculate the new total assets across all sub-vaults + * @return balances The balances of the sub-vaults + * @return newTotalAssets The new total assets across all sub-vaults + */ + function getSubVaultsBalances( + mapping(address vault => IVaultSubVaults.SubVaultState state) storage subVaultsStates, + address[] memory vaults, + bool calcNewTotalAssets + ) public view returns (uint256[] memory balances, uint256 newTotalAssets) { + uint256 vaultsLength = vaults.length; + balances = new uint256[](vaultsLength); + for (uint256 i = 0; i < vaultsLength;) { + address vault = vaults[i]; + IVaultSubVaults.SubVaultState memory vaultState = subVaultsStates[vault]; + if (calcNewTotalAssets) { + uint256 vaultTotalShares = vaultState.stakedShares + vaultState.queuedShares; + if (vaultTotalShares > 0) { + newTotalAssets += IVaultState(vault).convertToAssets(vaultTotalShares); + } + } + + if (vaultState.stakedShares > 0) { + balances[i] = IVaultState(vault).convertToAssets(vaultState.stakedShares); + } else { + balances[i] = 0; + } + unchecked { + // cannot realistically overflow + ++i; + } + } + } + + /** + * @dev Processes the given redeem requests + * @param subVaultsStates The mapping of sub-vault addresses to their states + * @param osTokenVaultController The address of the osToken vault controller contract + * @param redeemer The address of the redeemer + * @param redeemRequests The redeem requests to process + * @return totalRedeemedAssets The total amount of redeemed assets + */ + function processRedeemRequests( + mapping(address vault => IVaultSubVaults.SubVaultState state) storage subVaultsStates, + address osTokenVaultController, + address redeemer, + ISubVaultsCurator.ExitRequest[] memory redeemRequests + ) external returns (uint256 totalRedeemedAssets) { + uint256 redeemRequestsLength = redeemRequests.length; + for (uint256 i = 0; i < redeemRequestsLength;) { + // calculate redeemable assets + ISubVaultsCurator.ExitRequest memory redeemRequest = redeemRequests[i]; + uint256 redeemAssets = Math.min(redeemRequest.assets, IVaultState(redeemRequest.vault).withdrawableAssets()); + if (redeemAssets == 0) { + unchecked { + // cannot realistically overflow + ++i; + } + continue; + } + + // mint osToken shares to redeemer + uint256 osTokenShares = IOsTokenVaultController(osTokenVaultController).convertToShares(redeemAssets); + if (osTokenShares == 0) { + unchecked { + // cannot realistically overflow + ++i; + } + continue; + } + IVaultOsToken(redeemRequest.vault).mintOsToken(redeemer, osTokenShares, address(0)); + + // execute redeem + IOsTokenRedeemer(redeemer).redeemSubVaultOsToken(redeemRequest.vault, osTokenShares); + + // check position is closed + if (IVaultOsToken(redeemRequest.vault).osTokenPositions(address(this)) > 0) { + revert Errors.InvalidPosition(); + } + + // update state + uint256 redeemedShares = IVaultState(redeemRequest.vault).convertToShares(redeemAssets); + subVaultsStates[redeemRequest.vault].stakedShares -= SafeCast.toUint128(redeemedShares); + totalRedeemedAssets += redeemAssets; + + unchecked { + // cannot realistically overflow + ++i; + } + } + } + + /** + * @dev Claims exited assets from sub-vaults based on the given exit requests + * @param subVaultsStates The mapping of sub-vault addresses to their states + * @param subVaultsExits The mapping of sub-vault addresses to their exit queues + * @param exitRequests The exit requests to process + * @return totalExitedAssets The total amount of exited assets claimed + */ + function claimSubVaultsExitedAssets( + mapping(address vault => IVaultSubVaults.SubVaultState state) storage subVaultsStates, + mapping(address vault => DoubleEndedQueue.Bytes32Deque) storage subVaultsExits, + IVaultSubVaults.SubVaultExitRequest[] calldata exitRequests + ) external returns (uint256 totalExitedAssets) { + uint256 exitRequestsLength = exitRequests.length; + for (uint256 i = 0; i < exitRequestsLength;) { + IVaultSubVaults.SubVaultExitRequest calldata exitRequest = exitRequests[i]; + IVaultSubVaults.SubVaultState memory subVaultState = subVaultsStates[exitRequest.vault]; + (uint256 positionTicket, uint256 positionShares) = + SubVaultExits.popSubVaultExit(subVaultsExits, exitRequest.vault); + (uint256 leftShares, uint256 exitedShares, uint256 exitedAssets) = IVaultEnterExit(exitRequest.vault) + .calculateExitedAssets(address(this), positionTicket, exitRequest.timestamp, exitRequest.exitQueueIndex); + + subVaultState.queuedShares -= SafeCast.toUint128(positionShares); + if (leftShares > 0) { + // exit request was not processed in full + SubVaultExits.pushSubVaultExit( + subVaultsExits, + exitRequest.vault, + SafeCast.toUint160(positionTicket + exitedShares), + SafeCast.toUint96(leftShares), + true + ); + subVaultState.queuedShares += SafeCast.toUint128(leftShares); + } + + // update total exited assets, vault state + totalExitedAssets += exitedAssets; + subVaultsStates[exitRequest.vault] = subVaultState; + + // claim exited assets from the vault + IVaultEnterExit(exitRequest.vault) + .claimExitedAssets(positionTicket, exitRequest.timestamp, exitRequest.exitQueueIndex); + + unchecked { + // cannot realistically overflow + ++i; + } + } + } + + /** + * @dev Internal function to check whether a sub-vault is collateralized + * @param subVault The address of the sub-vault + * @return true if the sub-vault is collateralized + */ + function _isSubVaultCollateralized(address keeper, address subVault) private view returns (bool) { + try IVaultSubVaults(subVault).isCollateralized() returns (bool collateralized) { + return collateralized; + } catch {} + + return IKeeperRewards(keeper).isCollateralized(subVault); + } + + /** + * @dev Calculates the required sub-vaults exit requests to fulfill the assets to redeem + * @param subVaultsStates The mapping of sub-vault addresses to their states + * @param subVaultsCurator The address of the sub-vaults curator + * @param vaults The addresses of the sub-vaults + * @param assetsToRedeem The amount of assets to redeem + * @param withdrawableAssets The amount of withdrawable assets in the meta vault + * @param ejectingSubVault The address of the ejecting sub-vault + * @param ejectingSubVaultShares The shares of the ejecting sub-vault + * @return redeemRequests The array of sub-vaults exit requests + */ + function calculateSubVaultsRedemptions( + mapping(address vault => IVaultSubVaults.SubVaultState state) storage subVaultsStates, + address subVaultsCurator, + address[] memory vaults, + uint256 assetsToRedeem, + uint256 withdrawableAssets, + address ejectingSubVault, + uint256 ejectingSubVaultShares + ) external view returns (ISubVaultsCurator.ExitRequest[] memory redeemRequests) { + // check whether enough assets available + unchecked { + assetsToRedeem -= Math.min(assetsToRedeem, withdrawableAssets); + } + if (assetsToRedeem == 0) { + // if enough withdrawable assets, return empty array + return redeemRequests; + } + + // check whether ejecting shares can be consumed + if (ejectingSubVault != address(0) && ejectingSubVaultShares != 0) { + uint256 ejectingVaultAssets = IVaultState(ejectingSubVault).convertToAssets(ejectingSubVaultShares); + unchecked { + assetsToRedeem -= Math.min(assetsToRedeem, ejectingVaultAssets); + } + } + + if (assetsToRedeem == 0) { + // if no assets to redeem, return empty array + return redeemRequests; + } + + // check vaults length + uint256 vaultsLength = vaults.length; + if (vaultsLength == 0) revert Errors.EmptySubVaults(); + + // fetch current sub-vaults balances + uint256[] memory balances; + (balances,) = getSubVaultsBalances(subVaultsStates, vaults, false); + + // fetch redeems from the curator + return ISubVaultsCurator(subVaultsCurator).getExitRequests(assetsToRedeem, vaults, balances, ejectingSubVault); + } + + /** + * @dev Ejects a sub-vault from the meta vault + * @param subVaults The set of currently added sub-vaults + * @param subVaultsStates The mapping of sub-vault addresses to their states + * @param subVaultsExits The mapping of sub-vault addresses to their exit queues + * @param currentEjectingSubVault The address of the currently ejecting sub-vault + * @param vault The address of the sub-vault to eject + * @return ejected Whether the vault was fully ejected (no queued shares) + * @return ejectingShares The amount of shares being ejected + */ + function ejectSubVault( + EnumerableSet.AddressSet storage subVaults, + mapping(address => IVaultSubVaults.SubVaultState) storage subVaultsStates, + mapping(address => DoubleEndedQueue.Bytes32Deque) storage subVaultsExits, + address currentEjectingSubVault, + address vault + ) external returns (bool ejected, uint128 ejectingShares) { + if (currentEjectingSubVault != address(0)) { + revert Errors.EjectingVault(); + } + if (!subVaults.contains(vault)) { + revert Errors.AlreadyRemoved(); + } + if (subVaults.length() == 1) { + revert Errors.EmptySubVaults(); + } + + // check the vault state + IVaultSubVaults.SubVaultState memory state = subVaultsStates[vault]; + if (state.stakedShares > 0) { + // enter exit queue for all the vault staked shares + uint256 positionTicket = IVaultEnterExit(vault).enterExitQueue(state.stakedShares, address(this)); + // add ejecting shares to the vault's exit positions + SubVaultExits.pushSubVaultExit( + subVaultsExits, vault, SafeCast.toUint160(positionTicket), SafeCast.toUint96(state.stakedShares), false + ); + state.queuedShares += state.stakedShares; + ejectingShares = state.stakedShares; + } + + // update state + if (state.queuedShares > 0) { + state.stakedShares = 0; + subVaultsStates[vault] = state; + return (false, ejectingShares); + } else { + // no shares left + subVaultsExits[vault].clear(); + // remove the vault from the list of sub vaults + subVaults.remove(vault); + return (true, 0); + } + } +} diff --git a/contracts/vaults/base/MetaVault.sol b/contracts/vaults/base/MetaVault.sol new file mode 100644 index 00000000..1f541fcb --- /dev/null +++ b/contracts/vaults/base/MetaVault.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol"; +import {ISubVaultsCurator} from "../../interfaces/ISubVaultsCurator.sol"; +import {IMetaVault} from "../../interfaces/IMetaVault.sol"; +import {Errors} from "../../libraries/Errors.sol"; +import {SubVaultUtils} from "../../libraries/SubVaultUtils.sol"; +import {Multicall} from "../../base/Multicall.sol"; +import {VaultImmutables} from "../modules/VaultImmutables.sol"; +import {VaultAdmin} from "../modules/VaultAdmin.sol"; +import {VaultVersion} from "../modules/VaultVersion.sol"; +import {VaultFee} from "../modules/VaultFee.sol"; +import {VaultState, IVaultState} from "../modules/VaultState.sol"; +import {VaultEnterExit, IVaultEnterExit} from "../modules/VaultEnterExit.sol"; +import {VaultOsToken} from "../modules/VaultOsToken.sol"; +import {VaultSubVaults} from "../modules/VaultSubVaults.sol"; + +/** + * @title MetaVault + * @author StakeWise + * @notice Defines the Meta Vault that delegates stake to the sub vaults + */ +abstract contract MetaVault is + VaultImmutables, + VaultAdmin, + VaultVersion, + VaultFee, + VaultState, + VaultEnterExit, + VaultOsToken, + VaultSubVaults, + Multicall, + IMetaVault +{ + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param args The arguments for initializing the MetaVault contract + */ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(MetaVaultConstructorArgs memory args) + VaultImmutables(args.keeper, args.vaultsRegistry) + VaultEnterExit(args.exitingAssetsClaimDelay) + VaultOsToken(args.osTokenVaultController, args.osTokenConfig, args.osTokenVaultEscrow) + VaultSubVaults(args.curatorsRegistry) + {} + + /// @inheritdoc IVaultState + function isStateUpdateRequired() public view override(IVaultState, VaultState, VaultSubVaults) returns (bool) { + return super.isStateUpdateRequired(); + } + + /// @inheritdoc IMetaVault + function calculateSubVaultsRedemptions(uint256 assetsToRedeem) + public + view + override + returns (ISubVaultsCurator.ExitRequest[] memory redeemRequests) + { + _checkHarvested(); + + return SubVaultUtils.calculateSubVaultsRedemptions( + _subVaultsStates, + subVaultsCurator, + getSubVaults(), + assetsToRedeem, + withdrawableAssets(), + ejectingSubVault, + _ejectingSubVaultShares + ); + } + + /// @inheritdoc IMetaVault + function redeemSubVaultsAssets(uint256 assetsToRedeem) + external + override + nonReentrant + returns (uint256 totalRedeemedAssets) + { + if (assetsToRedeem == 0) { + revert Errors.InvalidAssets(); + } + + // check only redeemer can call + address redeemer = _osTokenConfig.redeemer(); + if (msg.sender != redeemer) revert Errors.AccessDenied(); + + // get redeem requests + ISubVaultsCurator.ExitRequest[] memory redeemRequests = calculateSubVaultsRedemptions(assetsToRedeem); + if (redeemRequests.length == 0) { + return totalRedeemedAssets; + } + + // check assets before + uint256 assetsBefore = _vaultAssets(); + + // perform redemptions + totalRedeemedAssets = SubVaultUtils.processRedeemRequests( + _subVaultsStates, address(_osTokenVaultController), redeemer, redeemRequests + ); + + // check redeemed assets transferred back + if (_vaultAssets() - assetsBefore != totalRedeemedAssets) { + revert Errors.InvalidAssets(); + } + + // update sub vaults total assets + _subVaultsTotalAssets -= SafeCast.toUint128(totalRedeemedAssets); + + // emit event + emit SubVaultsAssetsRedeemed(totalRedeemedAssets); + } + + /// @inheritdoc IVaultState + function updateState(IKeeperRewards.HarvestParams calldata harvestParams) + public + override(IVaultState, VaultState, VaultSubVaults) + { + super.updateState(harvestParams); + } + + /// @inheritdoc IVaultEnterExit + function enterExitQueue(uint256 shares, address receiver) + public + virtual + override(IVaultEnterExit, VaultEnterExit, VaultOsToken) + returns (uint256 positionTicket) + { + return super.enterExitQueue(shares, receiver); + } + + /// @inheritdoc VaultImmutables + function _checkHarvested() internal view override(VaultImmutables, VaultSubVaults) { + super._checkHarvested(); + } + + /// @inheritdoc VaultImmutables + function _isCollateralized() internal view virtual override(VaultImmutables, VaultSubVaults) returns (bool) { + return super._isCollateralized(); + } + + /** + * @dev Initializes the MetaVault contract + * @param admin The address of the admin of the Vault + * @param params The parameters for initializing the MetaVault contract + */ + function __MetaVault_init(address admin, MetaVaultInitParams memory params) internal onlyInitializing { + __VaultAdmin_init(admin, params.metadataIpfsHash); + __VaultSubVaults_init(params.subVaultsCurator); + // fee recipient is initially set to admin address + __VaultFee_init(admin, params.feePercent); + __VaultState_init(params.capacity); + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[50] private __gap; +} diff --git a/contracts/vaults/modules/VaultSubVaults.sol b/contracts/vaults/modules/VaultSubVaults.sol index c48708bf..058e8e01 100644 --- a/contracts/vaults/modules/VaultSubVaults.sol +++ b/contracts/vaults/modules/VaultSubVaults.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.22; -import {Packing} from "@openzeppelin/contracts/utils/Packing.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IVaultsRegistry} from "../../interfaces/IVaultsRegistry.sol"; import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol"; import {IVaultEnterExit} from "../../interfaces/IVaultEnterExit.sol"; @@ -18,6 +18,8 @@ import {ICuratorsRegistry} from "../../interfaces/ICuratorsRegistry.sol"; import {IVaultVersion} from "../../interfaces/IVaultVersion.sol"; import {ExitQueue} from "../../libraries/ExitQueue.sol"; import {Errors} from "../../libraries/Errors.sol"; +import {SubVaultUtils} from "../../libraries/SubVaultUtils.sol"; +import {SubVaultExits} from "../../libraries/SubVaultExits.sol"; import {VaultAdmin} from "./VaultAdmin.sol"; import {VaultImmutables} from "./VaultImmutables.sol"; import {VaultState, IVaultState} from "./VaultState.sol"; @@ -38,8 +40,6 @@ abstract contract VaultSubVaults is using EnumerableSet for EnumerableSet.AddressSet; using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; - uint256 private constant _maxSubVaults = 50; - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address private immutable _curatorsRegistry; @@ -51,14 +51,17 @@ abstract contract VaultSubVaults is EnumerableSet.AddressSet internal _subVaults; mapping(address vault => DoubleEndedQueue.Bytes32Deque) private _subVaultsExits; - mapping(address vault => SubVaultState state) private _subVaultsStates; + mapping(address vault => SubVaultState state) internal _subVaultsStates; /// @inheritdoc IVaultSubVaults uint128 public override subVaultsRewardsNonce; - uint128 private _subVaultsTotalAssets; + uint128 internal _subVaultsTotalAssets; uint256 private _totalProcessedExitQueueTickets; - uint256 private _ejectingSubVaultShares; + uint256 internal _ejectingSubVaultShares; + + /// @inheritdoc IVaultSubVaults + address public override pendingMetaSubVault; /** * @dev Constructor @@ -90,82 +93,72 @@ abstract contract VaultSubVaults is /// @inheritdoc IVaultSubVaults function addSubVault(address vault) public virtual override { _checkAdmin(); - // check whether the vault is registered in the registry - if (vault == address(0) || vault == address(this) || !IVaultsRegistry(_vaultsRegistry).vaults(vault)) { - revert Errors.InvalidVault(); - } - // check whether the vault is not already added - if (_subVaults.contains(vault)) { - revert Errors.AlreadyAdded(); + + // check new sub-vault validity + SubVaultUtils.validateSubVault(_subVaults, _vaultsRegistry, _keeper, vault); + + if (_isMetaVault(vault)) { + // meta vault must be approved before being added as a sub vault + if (pendingMetaSubVault != address(0)) { + revert Errors.AlreadyAdded(); + } + pendingMetaSubVault = vault; + emit MetaSubVaultProposed(msg.sender, vault); + } else { + _addSubVault(vault); } - // check whether the vault is not exceeding the limit - uint256 subVaultsCount = _subVaults.length(); - if (subVaultsCount >= _maxSubVaults) { - revert Errors.CapacityExceeded(); + } + + /// @inheritdoc IVaultSubVaults + function acceptMetaSubVault(address metaSubVault) external virtual override { + // only the VaultsRegistry owner can accept a meta vault addition as a sub vault + if (msg.sender != Ownable(_vaultsRegistry).owner()) { + revert Errors.AccessDenied(); } - // check whether vault is collateralized - if (!_isSubVaultCollateralized(vault)) { - revert Errors.NotCollateralized(); + + if (metaSubVault == address(0) || pendingMetaSubVault != metaSubVault) { + revert Errors.InvalidVault(); } - // check whether legacy exit queue is processed, will revert if vault doesn't have `getExitQueueData` function - (,, uint128 totalExitingTickets, uint128 totalExitingAssets,) = IVaultState(vault).getExitQueueData(); - if (totalExitingTickets != 0 || totalExitingAssets != 0) { - revert Errors.ExitRequestNotProcessed(); + // check sub-vault validity + SubVaultUtils.validateSubVault(_subVaults, _vaultsRegistry, _keeper, metaSubVault); + + // update state + delete pendingMetaSubVault; + _addSubVault(metaSubVault); + } + + /// @inheritdoc IVaultSubVaults + function rejectMetaSubVault(address metaSubVault) external virtual override { + // only the VaultsRegistry owner or admin can reject a meta vault addition as a sub vault + if (msg.sender != Ownable(_vaultsRegistry).owner() && msg.sender != admin) { + revert Errors.AccessDenied(); } - // check harvested - uint256 vaultNonce = _getSubVaultRewardsNonce(vault); - uint256 lastSubVaultsRewardsNonce = subVaultsRewardsNonce; - if (subVaultsCount == 0) { - subVaultsRewardsNonce = SafeCast.toUint128(vaultNonce); - emit RewardsNonceUpdated(vaultNonce); - } else if (vaultNonce != lastSubVaultsRewardsNonce) { - revert Errors.NotHarvested(); + if (metaSubVault == address(0) || pendingMetaSubVault != metaSubVault) { + revert Errors.InvalidVault(); } - // add the vault to the list of sub vaults - _subVaults.add(vault); - emit SubVaultAdded(msg.sender, vault); + // update state + delete pendingMetaSubVault; + + // emit event + emit MetaSubVaultRejected(msg.sender, metaSubVault); } /// @inheritdoc IVaultSubVaults function ejectSubVault(address vault) public virtual override { _checkAdmin(); - if (ejectingSubVault != address(0)) { - revert Errors.EjectingVault(); - } - if (!_subVaults.contains(vault)) { - revert Errors.AlreadyRemoved(); - } - if (_subVaults.length() == 1) { - revert Errors.EmptySubVaults(); - } + (bool ejected, uint128 ejectingShares) = + SubVaultUtils.ejectSubVault(_subVaults, _subVaultsStates, _subVaultsExits, ejectingSubVault, vault); - // check the vault state - SubVaultState memory state = _subVaultsStates[vault]; - if (state.stakedShares > 0) { - // enter exit queue for all the vault staked shares - uint256 positionTicket = IVaultEnterExit(vault).enterExitQueue(state.stakedShares, address(this)); - // add ejecting shares to the vault's exit positions - _pushSubVaultExit(vault, SafeCast.toUint160(positionTicket), SafeCast.toUint96(state.stakedShares), false); - state.queuedShares += state.stakedShares; - } - - // update state - if (state.queuedShares > 0) { + if (ejected) { + emit SubVaultEjected(msg.sender, vault); + } else { ejectingSubVault = vault; - _ejectingSubVaultShares = state.stakedShares; - state.stakedShares = 0; - _subVaultsStates[vault] = state; + _ejectingSubVaultShares = ejectingShares; emit SubVaultEjecting(msg.sender, vault); - } else { - // no shares left - _subVaultsExits[vault].clear(); - // remove the vault from the list of sub vaults - _subVaults.remove(vault); - emit SubVaultEjected(msg.sender, vault); } } @@ -239,53 +232,26 @@ abstract contract VaultSubVaults is /// @inheritdoc IVaultSubVaults function claimSubVaultsExitedAssets(SubVaultExitRequest[] calldata exitRequests) external override { - uint256 exitRequestsLength = exitRequests.length; // SLOAD to memory - uint256 subVaultsTotalAssets = _subVaultsTotalAssets; address _ejectingSubVault = ejectingSubVault; - for (uint256 i = 0; i < exitRequestsLength;) { - SubVaultExitRequest calldata exitRequest = exitRequests[i]; - SubVaultState memory subVaultState = _subVaultsStates[exitRequest.vault]; - (uint256 positionTicket, uint256 positionShares) = _popSubVaultExit(exitRequest.vault); - (uint256 leftShares, uint256 exitedShares, uint256 exitedAssets) = IVaultEnterExit(exitRequest.vault) - .calculateExitedAssets(address(this), positionTicket, exitRequest.timestamp, exitRequest.exitQueueIndex); - - subVaultState.queuedShares -= SafeCast.toUint128(positionShares); - if (leftShares > 0) { - // exit request was not processed in full - _pushSubVaultExit( - exitRequest.vault, - SafeCast.toUint160(positionTicket + exitedShares), - SafeCast.toUint96(leftShares), - true - ); - subVaultState.queuedShares += SafeCast.toUint128(leftShares); - } - - // update total assets, vault state - subVaultsTotalAssets -= exitedAssets; - _subVaultsStates[exitRequest.vault] = subVaultState; + uint256 totalExitedAssets = + SubVaultUtils.claimSubVaultsExitedAssets(_subVaultsStates, _subVaultsExits, exitRequests); - // claim exited assets from the vault - IVaultEnterExit(exitRequest.vault).claimExitedAssets( - positionTicket, exitRequest.timestamp, exitRequest.exitQueueIndex - ); - if (_ejectingSubVault == exitRequest.vault && subVaultState.queuedShares == 0) { + if (_ejectingSubVault != address(0)) { + // check whether ejecting vault can be cleaned up + SubVaultState memory subVaultState = _subVaultsStates[_ejectingSubVault]; + if (subVaultState.queuedShares == 0) { // clean up ejecting vault delete ejectingSubVault; delete _ejectingSubVaultShares; - _subVaultsExits[exitRequest.vault].clear(); - _subVaults.remove(exitRequest.vault); - emit SubVaultEjected(msg.sender, exitRequest.vault); - } - - unchecked { - // cannot realistically overflow - ++i; + _subVaultsExits[_ejectingSubVault].clear(); + _subVaults.remove(_ejectingSubVault); + emit SubVaultEjected(msg.sender, _ejectingSubVault); } } - // update sub vaults total assets - _subVaultsTotalAssets = SafeCast.toUint128(subVaultsTotalAssets); + + // update sub vaults total assets + _subVaultsTotalAssets -= SafeCast.toUint128(totalExitedAssets); } /// @inheritdoc IVaultState @@ -305,26 +271,9 @@ abstract contract VaultSubVaults is _checkSubVaultsExitClaims(vaults); // calculate new total assets and save balances in each sub vault + uint256[] memory balances; uint256 newSubVaultsTotalAssets; - uint256[] memory balances = new uint256[](vaultsLength); - for (uint256 i = 0; i < vaultsLength;) { - address vault = vaults[i]; - SubVaultState memory vaultState = _subVaultsStates[vault]; - uint256 vaultTotalShares = vaultState.stakedShares + vaultState.queuedShares; - if (vaultTotalShares > 0) { - newSubVaultsTotalAssets += IVaultState(vault).convertToAssets(vaultTotalShares); - } - - if (vaultState.stakedShares > 0) { - balances[i] = IVaultState(vault).convertToAssets(vaultState.stakedShares); - } else { - balances[i] = 0; - } - unchecked { - // cannot realistically overflow - ++i; - } - } + (balances, newSubVaultsTotalAssets) = SubVaultUtils.getSubVaultsBalances(_subVaultsStates, vaults, true); // store new sub vaults total assets delta int256 totalAssetsDelta = SafeCast.toInt256(newSubVaultsTotalAssets) - SafeCast.toInt256(_subVaultsTotalAssets); @@ -357,6 +306,25 @@ abstract contract VaultSubVaults is return (0, false); } + /** + * @dev Internal function to add a sub-vault + * @param vault The address of the sub-vault to add + */ + function _addSubVault(address vault) private { + // update nonce + uint256 vaultNonce = _getSubVaultRewardsNonce(vault); + uint256 lastSubVaultsRewardsNonce = subVaultsRewardsNonce; + if (_subVaults.length() == 0) { + subVaultsRewardsNonce = SafeCast.toUint128(vaultNonce); + emit RewardsNonceUpdated(vaultNonce); + } else if (vaultNonce != lastSubVaultsRewardsNonce) { + revert Errors.NotHarvested(); + } + + _subVaults.add(vault); + emit SubVaultAdded(msg.sender, vault); + } + /** * @dev Internal function to enter the exit queue for sub vaults * @param vaults The addresses of the sub vaults @@ -421,8 +389,12 @@ abstract contract VaultSubVaults is uint256 positionTicket = IVaultEnterExit(exitRequest.vault).enterExitQueue(vaultShares, address(this)); // save exit request - _pushSubVaultExit( - exitRequest.vault, SafeCast.toUint160(positionTicket), SafeCast.toUint96(vaultShares), false + SubVaultExits.pushSubVaultExit( + _subVaultsExits, + exitRequest.vault, + SafeCast.toUint160(positionTicket), + SafeCast.toUint96(vaultShares), + false ); // update state @@ -451,7 +423,7 @@ abstract contract VaultSubVaults is uint256 vaultsLength = vaults.length; for (uint256 i = 0; i < vaultsLength;) { address vault = vaults[i]; - (uint256 positionTicket, uint256 exitShares) = _peekSubVaultExit(vault); + (uint256 positionTicket, uint256 exitShares) = SubVaultExits.peekSubVaultExit(_subVaultsExits, vault); if (positionTicket == 0 && exitShares == 0) { // no queue positions unchecked { @@ -544,50 +516,6 @@ abstract contract VaultSubVaults is ejectingSubVaultShares - IVaultState(_ejectingSubVault).convertToShares(processedAssets); } - /** - * @dev Fetches the sub-vault exit data - * @param vault The address of the sub-vault - * @return positionTicket The position ticket of the sub-vault - * @return shares The shares to be exited from the sub-vault - */ - function _peekSubVaultExit(address vault) private view returns (uint160 positionTicket, uint96 shares) { - if (_subVaultsExits[vault].empty()) { - return (0, 0); - } - bytes32 packed = _subVaultsExits[vault].front(); - positionTicket = uint160(Packing.extract_32_20(packed, 0)); - shares = uint96(Packing.extract_32_12(packed, 20)); - } - - /** - * @dev Stores the sub-vault exit data - * @param vault The address of the sub-vault - * @param positionTicket The position ticket of the sub-vault - * @param shares The shares to be exited from the sub-vault - * @param front Whether to insert the exit data at the front of the queue - */ - function _pushSubVaultExit(address vault, uint160 positionTicket, uint96 shares, bool front) private { - if (shares == 0) revert Errors.InvalidShares(); - bytes32 packed = Packing.pack_20_12(bytes20(positionTicket), bytes12(shares)); - if (front) { - _subVaultsExits[vault].pushFront(packed); - } else { - _subVaultsExits[vault].pushBack(packed); - } - } - - /** - * @dev Removes the sub-vault exit data - * @param vault The address of the sub-vault - * @return positionTicket The position ticket of the sub-vault - * @return shares The shares to be exited from the sub-vault - */ - function _popSubVaultExit(address vault) private returns (uint160 positionTicket, uint96 shares) { - bytes32 packed = _subVaultsExits[vault].popFront(); - positionTicket = uint160(Packing.extract_32_20(packed, 0)); - shares = uint96(Packing.extract_32_12(packed, 20)); - } - /// @inheritdoc VaultImmutables function _checkHarvested() internal view virtual override { if (isStateUpdateRequired()) { @@ -600,19 +528,6 @@ abstract contract VaultSubVaults is return _subVaults.length() > 0; } - /** - * @dev Internal function to check whether a sub-vault is collateralized - * @param subVault The address of the sub-vault - * @return true if the sub-vault is collateralized - */ - function _isSubVaultCollateralized(address subVault) private view returns (bool) { - try IVaultSubVaults(subVault).isCollateralized() returns (bool collateralized) { - return collateralized; - } catch {} - - return IKeeperRewards(_keeper).isCollateralized(subVault); - } - /** * @dev Internal function to get the rewards nonce of a sub-vault * @param subVault The address of the sub-vault @@ -649,6 +564,19 @@ abstract contract VaultSubVaults is emit SubVaultsCuratorUpdated(msg.sender, curator); } + /** + * @dev Internal function to check whether the vault is a meta vault + * @param vault The address of the vault + * @return True if the vault is a meta vault, false otherwise + */ + function _isMetaVault(address vault) private view returns (bool) { + try IVaultSubVaults(vault).getSubVaults() { + return true; + } catch { + return false; + } + } + /** * @dev Internal function to deposit assets to the vault * @param vault The address of the vault @@ -672,5 +600,5 @@ abstract contract VaultSubVaults is * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[50] private __gap; + uint256[49] private __gap; } From 929df326ce76d5361347804d6d00614f88ea6e3c Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Jan 2026 13:00:32 +0200 Subject: [PATCH 06/19] Add Eth and Gno meta vault implementations with private variants --- contracts/vaults/ethereum/EthMetaVault.sol | 167 ++++++++++++++ .../vaults/ethereum/EthPrivMetaVault.sol | 95 ++++++++ .../vaults/ethereum/custom/EthMetaVault.sol | 205 ------------------ contracts/vaults/gnosis/GnoMetaVault.sol | 152 +++++++++++++ contracts/vaults/gnosis/GnoPrivMetaVault.sol | 85 ++++++++ .../vaults/gnosis/custom/GnoMetaVault.sol | 190 ---------------- 6 files changed, 499 insertions(+), 395 deletions(-) create mode 100644 contracts/vaults/ethereum/EthMetaVault.sol create mode 100644 contracts/vaults/ethereum/EthPrivMetaVault.sol delete mode 100644 contracts/vaults/ethereum/custom/EthMetaVault.sol create mode 100644 contracts/vaults/gnosis/GnoMetaVault.sol create mode 100644 contracts/vaults/gnosis/GnoPrivMetaVault.sol delete mode 100644 contracts/vaults/gnosis/custom/GnoMetaVault.sol diff --git a/contracts/vaults/ethereum/EthMetaVault.sol b/contracts/vaults/ethereum/EthMetaVault.sol new file mode 100644 index 00000000..01784e31 --- /dev/null +++ b/contracts/vaults/ethereum/EthMetaVault.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IEthMetaVault} from "../../interfaces/IEthMetaVault.sol"; +import {IEthMetaVaultFactory} from "../../interfaces/IEthMetaVaultFactory.sol"; +import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol"; +import {IVaultEthStaking} from "../../interfaces/IVaultEthStaking.sol"; +import {Errors} from "../../libraries/Errors.sol"; +import {MetaVault} from "../base/MetaVault.sol"; +import {VaultEnterExit} from "../modules/VaultEnterExit.sol"; +import {VaultState} from "../modules/VaultState.sol"; +import {VaultSubVaults} from "../modules/VaultSubVaults.sol"; +import {IVaultVersion, VaultVersion} from "../modules/VaultVersion.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +/** + * @title EthMetaVault + * @author StakeWise + * @notice Defines the Meta Vault functionality on Ethereum + */ +contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { + using EnumerableSet for EnumerableSet.AddressSet; + + uint8 private constant _version = 6; + uint256 private constant _securityDeposit = 1e9; + + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param args The arguments for initializing the MetaVault contract + */ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor( + MetaVaultConstructorArgs memory args + ) MetaVault(args) { + _disableInitializers(); + } + + /// @inheritdoc IEthMetaVault + function initialize( + bytes calldata params + ) external payable virtual override reinitializer(_version) { + // if admin is already set, it's an upgrade from version 5 to 6 + if (admin != address(0)) { + return; + } + + __EthMetaVault_init(IEthMetaVaultFactory(msg.sender).vaultAdmin(), abi.decode(params, (MetaVaultInitParams))); + } + + /// @inheritdoc IEthMetaVault + function deposit( + address receiver, + address referrer + ) public payable virtual override returns (uint256 shares) { + return _deposit(receiver, msg.value, referrer); + } + + /** + * @dev Function for depositing using fallback function + */ + receive() external payable virtual { + // claim exited assets from the sub vaults should not be processed as deposits + if (_subVaults.contains(msg.sender)) { + return; + } + _deposit(msg.sender, msg.value, address(0)); + } + + /// @inheritdoc IEthMetaVault + function updateStateAndDeposit( + address receiver, + address referrer, + IKeeperRewards.HarvestParams calldata harvestParams + ) public payable virtual override returns (uint256 shares) { + updateState(harvestParams); + return deposit(receiver, referrer); + } + + /// @inheritdoc IEthMetaVault + function depositAndMintOsToken( + address receiver, + uint256 osTokenShares, + address referrer + ) public payable override returns (uint256) { + deposit(msg.sender, referrer); + return mintOsToken(receiver, osTokenShares, referrer); + } + + /// @inheritdoc IEthMetaVault + function updateStateAndDepositAndMintOsToken( + address receiver, + uint256 osTokenShares, + address referrer, + IKeeperRewards.HarvestParams calldata harvestParams + ) external payable override returns (uint256) { + updateState(harvestParams); + return depositAndMintOsToken(receiver, osTokenShares, referrer); + } + + /// @inheritdoc IEthMetaVault + function donateAssets() external payable override { + if (msg.value == 0) { + revert Errors.InvalidAssets(); + } + _donatedAssets += msg.value; + emit AssetsDonated(msg.sender, msg.value); + } + + /// @inheritdoc VaultVersion + function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) { + return keccak256("EthMetaVault"); + } + + /// @inheritdoc IVaultVersion + function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) { + return _version; + } + + /// @inheritdoc VaultSubVaults + function _depositToVault( + address vault, + uint256 assets + ) internal override returns (uint256) { + return IVaultEthStaking(vault).deposit{value: assets}(address(this), address(0)); + } + + /// @inheritdoc VaultState + function _vaultAssets() internal view virtual override returns (uint256) { + return address(this).balance; + } + + /// @inheritdoc VaultEnterExit + function _transferVaultAssets( + address receiver, + uint256 assets + ) internal virtual override nonReentrant { + return Address.sendValue(payable(receiver), assets); + } + + /** + * @dev Initializes the EthMetaVault contract + * @param admin The address of the admin of the Vault + * @param params The parameters for initializing the MetaVault contract + */ + function __EthMetaVault_init( + address admin, + MetaVaultInitParams memory params + ) internal onlyInitializing { + __MetaVault_init(admin, params); + + // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 + if (msg.value < _securityDeposit) revert Errors.InvalidSecurityDeposit(); + _deposit(address(this), msg.value, address(0)); + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[50] private __gap; +} diff --git a/contracts/vaults/ethereum/EthPrivMetaVault.sol b/contracts/vaults/ethereum/EthPrivMetaVault.sol new file mode 100644 index 00000000..01ac77ef --- /dev/null +++ b/contracts/vaults/ethereum/EthPrivMetaVault.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IEthMetaVaultFactory} from "../../interfaces/IEthMetaVaultFactory.sol"; +import {IEthPrivMetaVault} from "../../interfaces/IEthPrivMetaVault.sol"; +import {IVaultOsToken, VaultOsToken} from "../modules/VaultOsToken.sol"; +import {IVaultVersion} from "../modules/VaultVersion.sol"; +import {VaultWhitelist} from "../modules/VaultWhitelist.sol"; +import {EthMetaVault, IEthMetaVault} from "./EthMetaVault.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +/** + * @title EthPrivMetaVault + * @author StakeWise + * @notice Defines the Meta Vault functionality with whitelist on Ethereum + */ +contract EthPrivMetaVault is Initializable, EthMetaVault, VaultWhitelist, IEthPrivMetaVault { + using EnumerableSet for EnumerableSet.AddressSet; + + // slither-disable-next-line shadowing-state + uint8 private constant _version = 6; + + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param args The arguments for initializing the EthMetaVault contract + */ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor( + MetaVaultConstructorArgs memory args + ) EthMetaVault(args) { + _disableInitializers(); + } + + /// @inheritdoc IEthMetaVault + function initialize( + bytes calldata params + ) external payable virtual override(IEthMetaVault, EthMetaVault) reinitializer(_version) { + // initialize deployed vault + address _admin = IEthMetaVaultFactory(msg.sender).vaultAdmin(); + __EthMetaVault_init(_admin, abi.decode(params, (MetaVaultInitParams))); + // whitelister is initially set to admin address + __VaultWhitelist_init(_admin); + } + + /// @inheritdoc IEthMetaVault + function deposit( + address receiver, + address referrer + ) public payable virtual override(IEthMetaVault, EthMetaVault) returns (uint256 shares) { + _checkWhitelist(msg.sender); + _checkWhitelist(receiver); + return super.deposit(receiver, referrer); + } + + /// @inheritdoc EthMetaVault + receive() external payable virtual override { + // claim exited assets from the sub vaults should not be processed as deposits + if (_subVaults.contains(msg.sender)) { + return; + } + _checkWhitelist(msg.sender); + _deposit(msg.sender, msg.value, address(0)); + } + + /// @inheritdoc IVaultOsToken + function mintOsToken( + address receiver, + uint256 osTokenShares, + address referrer + ) public virtual override(IVaultOsToken, VaultOsToken) returns (uint256 assets) { + _checkWhitelist(msg.sender); + return super.mintOsToken(receiver, osTokenShares, referrer); + } + + /// @inheritdoc IVaultVersion + function vaultId() public pure virtual override(IVaultVersion, EthMetaVault) returns (bytes32) { + return keccak256("EthPrivMetaVault"); + } + + /// @inheritdoc IVaultVersion + function version() public pure virtual override(IVaultVersion, EthMetaVault) returns (uint8) { + return _version; + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[50] private __gap; +} diff --git a/contracts/vaults/ethereum/custom/EthMetaVault.sol b/contracts/vaults/ethereum/custom/EthMetaVault.sol deleted file mode 100644 index 3db29134..00000000 --- a/contracts/vaults/ethereum/custom/EthMetaVault.sol +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.22; - -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {IVaultEthStaking} from "../../../interfaces/IVaultEthStaking.sol"; -import {IEthMetaVaultFactory} from "../../../interfaces/IEthMetaVaultFactory.sol"; -import {IKeeperRewards} from "../../../interfaces/IKeeperRewards.sol"; -import {IEthMetaVault} from "../../../interfaces/IEthMetaVault.sol"; -import {Errors} from "../../../libraries/Errors.sol"; -import {VaultImmutables} from "../../modules/VaultImmutables.sol"; -import {VaultAdmin} from "../../modules/VaultAdmin.sol"; -import {VaultVersion, IVaultVersion} from "../../modules/VaultVersion.sol"; -import {VaultFee} from "../../modules/VaultFee.sol"; -import {VaultState, IVaultState} from "../../modules/VaultState.sol"; -import {VaultEnterExit, IVaultEnterExit} from "../../modules/VaultEnterExit.sol"; -import {VaultOsToken} from "../../modules/VaultOsToken.sol"; -import {VaultSubVaults} from "../../modules/VaultSubVaults.sol"; -import {Multicall} from "../../../base/Multicall.sol"; - -/** - * @title EthMetaVault - * @author StakeWise - * @notice Defines the Meta Vault that delegates stake to the sub vaults on Ethereum - */ -contract EthMetaVault is - VaultImmutables, - Initializable, - VaultAdmin, - VaultVersion, - VaultFee, - VaultState, - VaultEnterExit, - VaultOsToken, - VaultSubVaults, - Multicall, - IEthMetaVault -{ - using EnumerableSet for EnumerableSet.AddressSet; - - uint8 private constant _version = 5; - uint256 private constant _securityDeposit = 1e9; - - /** - * @dev Constructor - * @dev Since the immutable variable value is stored in the bytecode, - * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. - * @param args The arguments for initializing the EthMetaVault contract - */ - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(EthMetaVaultConstructorArgs memory args) - VaultImmutables(args.keeper, args.vaultsRegistry) - VaultEnterExit(args.exitingAssetsClaimDelay) - VaultOsToken(args.osTokenVaultController, args.osTokenConfig, args.osTokenVaultEscrow) - VaultSubVaults(args.curatorsRegistry) - { - _disableInitializers(); - } - - /// @inheritdoc IEthMetaVault - function initialize(bytes calldata params) external payable virtual override reinitializer(_version) { - __EthMetaVault_init(IEthMetaVaultFactory(msg.sender).vaultAdmin(), abi.decode(params, (EthMetaVaultInitParams))); - } - - /// @inheritdoc IVaultState - function isStateUpdateRequired() public view override(IVaultState, VaultState, VaultSubVaults) returns (bool) { - return super.isStateUpdateRequired(); - } - - /// @inheritdoc IEthMetaVault - function deposit(address receiver, address referrer) public payable virtual override returns (uint256 shares) { - return _deposit(receiver, msg.value, referrer); - } - - /** - * @dev Function for depositing using fallback function - */ - receive() external payable virtual { - // claim exited assets from the sub vaults should not be processed as deposits - if (_subVaults.contains(msg.sender)) { - return; - } - _deposit(msg.sender, msg.value, address(0)); - } - - // @inheritdoc IVaultState - function updateState(IKeeperRewards.HarvestParams calldata harvestParams) - public - override(IVaultState, VaultState, VaultSubVaults) - { - super.updateState(harvestParams); - } - - /// @inheritdoc IVaultEnterExit - function enterExitQueue(uint256 shares, address receiver) - public - virtual - override(IVaultEnterExit, VaultEnterExit, VaultOsToken) - returns (uint256 positionTicket) - { - return super.enterExitQueue(shares, receiver); - } - - /// @inheritdoc IEthMetaVault - function updateStateAndDeposit( - address receiver, - address referrer, - IKeeperRewards.HarvestParams calldata harvestParams - ) public payable virtual override returns (uint256 shares) { - updateState(harvestParams); - return deposit(receiver, referrer); - } - - /// @inheritdoc IEthMetaVault - function depositAndMintOsToken(address receiver, uint256 osTokenShares, address referrer) - public - payable - override - returns (uint256) - { - deposit(msg.sender, referrer); - return mintOsToken(receiver, osTokenShares, referrer); - } - - /// @inheritdoc IEthMetaVault - function updateStateAndDepositAndMintOsToken( - address receiver, - uint256 osTokenShares, - address referrer, - IKeeperRewards.HarvestParams calldata harvestParams - ) external payable override returns (uint256) { - updateState(harvestParams); - return depositAndMintOsToken(receiver, osTokenShares, referrer); - } - - /// @inheritdoc IEthMetaVault - function donateAssets() external payable override { - if (msg.value == 0) { - revert Errors.InvalidAssets(); - } - _donatedAssets += msg.value; - emit AssetsDonated(msg.sender, msg.value); - } - - /// @inheritdoc VaultVersion - function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) { - return keccak256("EthMetaVault"); - } - - /// @inheritdoc IVaultVersion - function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) { - return _version; - } - - /// @inheritdoc VaultImmutables - function _checkHarvested() internal view override(VaultImmutables, VaultSubVaults) { - super._checkHarvested(); - } - - /// @inheritdoc VaultImmutables - function _isCollateralized() internal view virtual override(VaultImmutables, VaultSubVaults) returns (bool) { - return super._isCollateralized(); - } - - /// @inheritdoc VaultSubVaults - function _depositToVault(address vault, uint256 assets) internal override returns (uint256) { - return IVaultEthStaking(vault).deposit{value: assets}(address(this), address(0)); - } - - /// @inheritdoc VaultState - function _vaultAssets() internal view virtual override returns (uint256) { - return address(this).balance; - } - - /// @inheritdoc VaultEnterExit - function _transferVaultAssets(address receiver, uint256 assets) internal virtual override nonReentrant { - return Address.sendValue(payable(receiver), assets); - } - - /** - * @dev Initializes the EthMetaVault contract - * @param admin The address of the admin of the Vault - * @param params The parameters for initializing the EthMetaVault contract - */ - function __EthMetaVault_init(address admin, EthMetaVaultInitParams memory params) internal onlyInitializing { - __VaultAdmin_init(admin, params.metadataIpfsHash); - __VaultSubVaults_init(params.subVaultsCurator); - // fee recipient is initially set to admin address - __VaultFee_init(admin, params.feePercent); - __VaultState_init(params.capacity); - - // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 - if (msg.value < _securityDeposit) revert Errors.InvalidSecurityDeposit(); - _deposit(address(this), msg.value, address(0)); - } - - /** - * @dev This empty reserved space is put in place to allow future versions to add new - * variables without shifting down storage in the inheritance chain. - * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - */ - uint256[50] private __gap; -} diff --git a/contracts/vaults/gnosis/GnoMetaVault.sol b/contracts/vaults/gnosis/GnoMetaVault.sol new file mode 100644 index 00000000..6fbc1f66 --- /dev/null +++ b/contracts/vaults/gnosis/GnoMetaVault.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IGnoMetaVault} from "../../interfaces/IGnoMetaVault.sol"; +import {IGnoMetaVaultFactory} from "../../interfaces/IGnoMetaVaultFactory.sol"; +import {IVaultGnoStaking} from "../../interfaces/IVaultGnoStaking.sol"; +import {Errors} from "../../libraries/Errors.sol"; +import {MetaVault} from "../base/MetaVault.sol"; +import {VaultEnterExit} from "../modules/VaultEnterExit.sol"; +import {VaultState} from "../modules/VaultState.sol"; +import {IVaultSubVaults, VaultSubVaults} from "../modules/VaultSubVaults.sol"; +import {IVaultVersion, VaultVersion} from "../modules/VaultVersion.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title GnoMetaVault + * @author StakeWise + * @notice Defines the Meta Vault functionality on Gnosis + */ +contract GnoMetaVault is Initializable, MetaVault, IGnoMetaVault { + uint8 private constant _version = 4; + uint256 private constant _securityDeposit = 1e9; + + IERC20 private immutable _gnoToken; + + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param gnoToken The address of the GNO token contract + * @param args The arguments for initializing the MetaVault contract + */ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor( + address gnoToken, + MetaVaultConstructorArgs memory args + ) MetaVault(args) { + _gnoToken = IERC20(gnoToken); + _disableInitializers(); + } + + /// @inheritdoc IGnoMetaVault + function initialize( + bytes calldata params + ) external virtual override reinitializer(_version) { + // if admin is already set, it's an upgrade from version 3 to 4 + if (admin != address(0)) { + return; + } + + __GnoMetaVault_init(IGnoMetaVaultFactory(msg.sender).vaultAdmin(), abi.decode(params, (MetaVaultInitParams))); + } + + /// @inheritdoc IGnoMetaVault + function deposit( + uint256 assets, + address receiver, + address referrer + ) public virtual override returns (uint256 shares) { + // withdraw GNO tokens from the user + SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), assets); + shares = _deposit(receiver, assets, referrer); + } + + /// @inheritdoc IVaultSubVaults + function addSubVault( + address vault + ) public virtual override(IVaultSubVaults, VaultSubVaults) { + super.addSubVault(vault); + // approve transferring GNO to sub-vault + _gnoToken.approve(vault, type(uint256).max); + } + + /// @inheritdoc IVaultSubVaults + function ejectSubVault( + address vault + ) public virtual override(IVaultSubVaults, VaultSubVaults) { + super.ejectSubVault(vault); + // revoke transferring GNO to sub-vault + _gnoToken.approve(vault, 0); + } + + /// @inheritdoc IGnoMetaVault + function donateAssets( + uint256 amount + ) external override nonReentrant { + if (amount == 0) { + revert Errors.InvalidAssets(); + } + SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), amount); + + _donatedAssets += amount; + emit AssetsDonated(msg.sender, amount); + } + + /// @inheritdoc VaultVersion + function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) { + return keccak256("GnoMetaVault"); + } + + /// @inheritdoc IVaultVersion + function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) { + return _version; + } + + /// @inheritdoc VaultSubVaults + function _depositToVault( + address vault, + uint256 assets + ) internal override returns (uint256) { + return IVaultGnoStaking(vault).deposit(assets, address(this), address(0)); + } + + /// @inheritdoc VaultState + function _vaultAssets() internal view virtual override returns (uint256) { + return _gnoToken.balanceOf(address(this)); + } + + /// @inheritdoc VaultEnterExit + function _transferVaultAssets( + address receiver, + uint256 assets + ) internal virtual override nonReentrant { + SafeERC20.safeTransfer(_gnoToken, receiver, assets); + } + + /** + * @dev Initializes the GnoMetaVault contract + * @param admin The address of the admin of the Vault + * @param params The parameters for initializing the MetaVault contract + */ + function __GnoMetaVault_init( + address admin, + MetaVaultInitParams memory params + ) internal onlyInitializing { + __MetaVault_init(admin, params); + + _deposit(address(this), _securityDeposit, address(0)); + // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 + SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), _securityDeposit); + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[50] private __gap; +} diff --git a/contracts/vaults/gnosis/GnoPrivMetaVault.sol b/contracts/vaults/gnosis/GnoPrivMetaVault.sol new file mode 100644 index 00000000..4e3f158d --- /dev/null +++ b/contracts/vaults/gnosis/GnoPrivMetaVault.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.22; + +import {IGnoMetaVaultFactory} from "../../interfaces/IGnoMetaVaultFactory.sol"; +import {IGnoPrivMetaVault} from "../../interfaces/IGnoPrivMetaVault.sol"; +import {IVaultOsToken, VaultOsToken} from "../modules/VaultOsToken.sol"; +import {IVaultVersion, VaultVersion} from "../modules/VaultVersion.sol"; +import {VaultWhitelist} from "../modules/VaultWhitelist.sol"; +import {GnoMetaVault, IGnoMetaVault} from "./GnoMetaVault.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @title GnoPrivMetaVault + * @author StakeWise + * @notice Defines the Meta Vault functionality with whitelist on Gnosis + */ +contract GnoPrivMetaVault is Initializable, GnoMetaVault, VaultWhitelist, IGnoPrivMetaVault { + // slither-disable-next-line shadowing-state + uint8 private constant _version = 4; + + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param gnoToken The address of the GNO token contract + * @param args The arguments for initializing the GnoMetaVault contract + */ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor( + address gnoToken, + MetaVaultConstructorArgs memory args + ) GnoMetaVault(gnoToken, args) { + _disableInitializers(); + } + + /// @inheritdoc IGnoMetaVault + function initialize( + bytes calldata params + ) external virtual override(IGnoMetaVault, GnoMetaVault) reinitializer(_version) { + // initialize deployed vault + address _admin = IGnoMetaVaultFactory(msg.sender).vaultAdmin(); + __GnoMetaVault_init(_admin, abi.decode(params, (MetaVaultInitParams))); + // whitelister is initially set to admin address + __VaultWhitelist_init(_admin); + } + + /// @inheritdoc IGnoMetaVault + function deposit( + uint256 assets, + address receiver, + address referrer + ) public virtual override(IGnoMetaVault, GnoMetaVault) returns (uint256 shares) { + _checkWhitelist(msg.sender); + _checkWhitelist(receiver); + return super.deposit(assets, receiver, referrer); + } + + /// @inheritdoc IVaultOsToken + function mintOsToken( + address receiver, + uint256 osTokenShares, + address referrer + ) public virtual override(IVaultOsToken, VaultOsToken) returns (uint256 assets) { + _checkWhitelist(msg.sender); + return super.mintOsToken(receiver, osTokenShares, referrer); + } + + /// @inheritdoc IVaultVersion + function vaultId() public pure virtual override(IVaultVersion, GnoMetaVault) returns (bytes32) { + return keccak256("GnoPrivMetaVault"); + } + + /// @inheritdoc IVaultVersion + function version() public pure virtual override(IVaultVersion, GnoMetaVault) returns (uint8) { + return _version; + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[50] private __gap; +} diff --git a/contracts/vaults/gnosis/custom/GnoMetaVault.sol b/contracts/vaults/gnosis/custom/GnoMetaVault.sol deleted file mode 100644 index 9e086796..00000000 --- a/contracts/vaults/gnosis/custom/GnoMetaVault.sol +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.22; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {IGnoMetaVaultFactory} from "../../../interfaces/IGnoMetaVaultFactory.sol"; -import {IVaultGnoStaking} from "../../../interfaces/IVaultGnoStaking.sol"; -import {IKeeperRewards} from "../../../interfaces/IKeeperRewards.sol"; -import {IGnoMetaVault} from "../../../interfaces/IGnoMetaVault.sol"; -import {Errors} from "../../../libraries/Errors.sol"; -import {VaultImmutables} from "../../modules/VaultImmutables.sol"; -import {VaultAdmin} from "../../modules/VaultAdmin.sol"; -import {VaultVersion, IVaultVersion} from "../../modules/VaultVersion.sol"; -import {VaultFee} from "../../modules/VaultFee.sol"; -import {VaultState, IVaultState} from "../../modules/VaultState.sol"; -import {VaultEnterExit, IVaultEnterExit} from "../../modules/VaultEnterExit.sol"; -import {VaultOsToken} from "../../modules/VaultOsToken.sol"; -import {IVaultSubVaults, VaultSubVaults} from "../../modules/VaultSubVaults.sol"; -import {Multicall} from "../../../base/Multicall.sol"; - -/** - * @title GnoMetaVault - * @author StakeWise - * @notice Defines the Meta Vault that delegates stake to the sub vaults on Gnosis - */ -contract GnoMetaVault is - VaultImmutables, - Initializable, - VaultAdmin, - VaultVersion, - VaultFee, - VaultState, - VaultEnterExit, - VaultOsToken, - VaultSubVaults, - Multicall, - IGnoMetaVault -{ - using EnumerableSet for EnumerableSet.AddressSet; - - uint8 private constant _version = 3; - uint256 private constant _securityDeposit = 1e9; - - IERC20 private immutable _gnoToken; - - /** - * @dev Constructor - * @dev Since the immutable variable value is stored in the bytecode, - * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. - * @param args The arguments for initializing the GnoMetaVault contract - */ - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(GnoMetaVaultConstructorArgs memory args) - VaultImmutables(args.keeper, args.vaultsRegistry) - VaultEnterExit(args.exitingAssetsClaimDelay) - VaultOsToken(args.osTokenVaultController, args.osTokenConfig, args.osTokenVaultEscrow) - VaultSubVaults(args.curatorsRegistry) - { - _gnoToken = IERC20(args.gnoToken); - _disableInitializers(); - } - - /// @inheritdoc IGnoMetaVault - function initialize(bytes calldata params) external payable virtual override reinitializer(_version) { - __GnoMetaVault_init(IGnoMetaVaultFactory(msg.sender).vaultAdmin(), abi.decode(params, (GnoMetaVaultInitParams))); - } - - /// @inheritdoc IGnoMetaVault - function deposit(uint256 assets, address receiver, address referrer) - public - virtual - override - nonReentrant - returns (uint256 shares) - { - // withdraw GNO tokens from the user - SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), assets); - shares = _deposit(receiver, assets, referrer); - } - - /// @inheritdoc IVaultState - function isStateUpdateRequired() public view override(IVaultState, VaultState, VaultSubVaults) returns (bool) { - return super.isStateUpdateRequired(); - } - - /// @inheritdoc IVaultSubVaults - function addSubVault(address vault) public virtual override(IVaultSubVaults, VaultSubVaults) { - super.addSubVault(vault); - // approve transferring GNO to sub-vault - _gnoToken.approve(vault, type(uint256).max); - } - - /// @inheritdoc IVaultSubVaults - function ejectSubVault(address vault) public virtual override(IVaultSubVaults, VaultSubVaults) { - super.ejectSubVault(vault); - // revoke transferring GNO to sub-vault - _gnoToken.approve(vault, 0); - } - - // @inheritdoc IVaultState - function updateState(IKeeperRewards.HarvestParams calldata harvestParams) - public - override(IVaultState, VaultState, VaultSubVaults) - { - super.updateState(harvestParams); - } - - /// @inheritdoc IVaultEnterExit - function enterExitQueue(uint256 shares, address receiver) - public - virtual - override(IVaultEnterExit, VaultEnterExit, VaultOsToken) - returns (uint256 positionTicket) - { - return super.enterExitQueue(shares, receiver); - } - - /// @inheritdoc IGnoMetaVault - function donateAssets(uint256 amount) external override nonReentrant { - if (amount == 0) { - revert Errors.InvalidAssets(); - } - SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), amount); - - _donatedAssets += amount; - emit AssetsDonated(msg.sender, amount); - } - - /// @inheritdoc VaultVersion - function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) { - return keccak256("GnoMetaVault"); - } - - /// @inheritdoc IVaultVersion - function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) { - return _version; - } - - /// @inheritdoc VaultImmutables - function _checkHarvested() internal view override(VaultImmutables, VaultSubVaults) { - super._checkHarvested(); - } - - /// @inheritdoc VaultImmutables - function _isCollateralized() internal view virtual override(VaultImmutables, VaultSubVaults) returns (bool) { - return super._isCollateralized(); - } - - /// @inheritdoc VaultSubVaults - function _depositToVault(address vault, uint256 assets) internal override returns (uint256) { - return IVaultGnoStaking(vault).deposit(assets, address(this), address(0)); - } - - /// @inheritdoc VaultState - function _vaultAssets() internal view virtual override returns (uint256) { - return _gnoToken.balanceOf(address(this)); - } - - /// @inheritdoc VaultEnterExit - function _transferVaultAssets(address receiver, uint256 assets) internal virtual override nonReentrant { - SafeERC20.safeTransfer(_gnoToken, receiver, assets); - } - - /** - * @dev Initializes the GnoMetaVault contract - * @param admin The address of the admin of the Vault - * @param params The parameters for initializing the GnoMetaVault contract - */ - function __GnoMetaVault_init(address admin, GnoMetaVaultInitParams memory params) internal onlyInitializing { - __VaultAdmin_init(admin, params.metadataIpfsHash); - __VaultSubVaults_init(params.subVaultsCurator); - // fee recipient is initially set to admin address - __VaultFee_init(admin, params.feePercent); - __VaultState_init(params.capacity); - - _deposit(address(this), _securityDeposit, address(0)); - // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 - SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), _securityDeposit); - } - - /** - * @dev This empty reserved space is put in place to allow future versions to add new - * variables without shifting down storage in the inheritance chain. - * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - */ - uint256[50] private __gap; -} From 92eb8c8c86af81b0a7903230194c8a0eb9500b47 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Jan 2026 16:12:16 +0200 Subject: [PATCH 07/19] Refactor OsTokenRedeemer to support meta vaults and simplify positions management --- contracts/tokens/EthOsTokenRedeemer.sol | 24 ++- contracts/tokens/GnoOsTokenRedeemer.sol | 30 ++-- contracts/tokens/OsTokenRedeemer.sol | 230 +++++++++++++++--------- 3 files changed, 184 insertions(+), 100 deletions(-) diff --git a/contracts/tokens/EthOsTokenRedeemer.sol b/contracts/tokens/EthOsTokenRedeemer.sol index a32d88fc..6091ffef 100644 --- a/contracts/tokens/EthOsTokenRedeemer.sol +++ b/contracts/tokens/EthOsTokenRedeemer.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.22; -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {IEthOsTokenRedeemer} from "../interfaces/IEthOsTokenRedeemer.sol"; import {OsTokenRedeemer} from "./OsTokenRedeemer.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /** * @title EthOsTokenRedeemer @@ -15,18 +15,27 @@ import {OsTokenRedeemer} from "./OsTokenRedeemer.sol"; contract EthOsTokenRedeemer is IEthOsTokenRedeemer, ReentrancyGuard, OsTokenRedeemer { /** * @dev Constructor + * @param vaultsRegistry_ The address of the VaultsRegistry contract * @param osToken_ The address of the OsToken contract * @param osTokenVaultController_ The address of the OsTokenVaultController contract * @param owner_ The address of the owner * @param exitQueueUpdateDelay_ The delay in seconds for exit queue updates */ - constructor(address osToken_, address osTokenVaultController_, address owner_, uint256 exitQueueUpdateDelay_) + constructor( + address vaultsRegistry_, + address osToken_, + address osTokenVaultController_, + address owner_, + uint256 exitQueueUpdateDelay_ + ) ReentrancyGuard() - OsTokenRedeemer(osToken_, osTokenVaultController_, owner_, exitQueueUpdateDelay_) + OsTokenRedeemer(vaultsRegistry_, osToken_, osTokenVaultController_, owner_, exitQueueUpdateDelay_) {} /// @inheritdoc IEthOsTokenRedeemer - function swapAssetsToOsTokenShares(address receiver) external payable override returns (uint256 osTokenShares) { + function swapAssetsToOsTokenShares( + address receiver + ) external payable override returns (uint256 osTokenShares) { return _swapAssetsToOsTokenShares(receiver, msg.value); } @@ -36,7 +45,10 @@ contract EthOsTokenRedeemer is IEthOsTokenRedeemer, ReentrancyGuard, OsTokenRede } /// @inheritdoc OsTokenRedeemer - function _transferAssets(address receiver, uint256 assets) internal override nonReentrant { + function _transferAssets( + address receiver, + uint256 assets + ) internal override nonReentrant { return Address.sendValue(payable(receiver), assets); } diff --git a/contracts/tokens/GnoOsTokenRedeemer.sol b/contracts/tokens/GnoOsTokenRedeemer.sol index ed0cde0c..5604033f 100644 --- a/contracts/tokens/GnoOsTokenRedeemer.sol +++ b/contracts/tokens/GnoOsTokenRedeemer.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.22; +import {IGnoOsTokenRedeemer} from "../interfaces/IGnoOsTokenRedeemer.sol"; +import {OsTokenRedeemer} from "./OsTokenRedeemer.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {IGnoOsTokenRedeemer} from "../interfaces/IGnoOsTokenRedeemer.sol"; -import {OsTokenRedeemer} from "./OsTokenRedeemer.sol"; /** * @title GnoOsTokenRedeemer @@ -19,6 +19,7 @@ contract GnoOsTokenRedeemer is IGnoOsTokenRedeemer, OsTokenRedeemer { /** * @dev Constructor * @param gnoToken_ The address of the GNO token contract + * @param vaultsRegistry_ The address of the VaultsRegistry contract * @param osToken_ The address of the OsToken contract * @param osTokenVaultController_ The address of the OsTokenVaultController contract * @param owner_ The address of the owner @@ -26,25 +27,31 @@ contract GnoOsTokenRedeemer is IGnoOsTokenRedeemer, OsTokenRedeemer { */ constructor( address gnoToken_, + address vaultsRegistry_, address osToken_, address osTokenVaultController_, address owner_, uint256 exitQueueUpdateDelay_ - ) OsTokenRedeemer(osToken_, osTokenVaultController_, owner_, exitQueueUpdateDelay_) { + ) OsTokenRedeemer(vaultsRegistry_, osToken_, osTokenVaultController_, owner_, exitQueueUpdateDelay_) { _gnoToken = IERC20(gnoToken_); } /// @inheritdoc IGnoOsTokenRedeemer - function permitGnoToken(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override { + function permitGnoToken( + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override { try IERC20Permit(address(_gnoToken)).permit(msg.sender, address(this), amount, deadline, v, r, s) {} catch {} } /// @inheritdoc IGnoOsTokenRedeemer - function swapAssetsToOsTokenShares(address receiver, uint256 assets) - external - override - returns (uint256 osTokenShares) - { + function swapAssetsToOsTokenShares( + address receiver, + uint256 assets + ) external override returns (uint256 osTokenShares) { SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), assets); return _swapAssetsToOsTokenShares(receiver, assets); } @@ -55,7 +62,10 @@ contract GnoOsTokenRedeemer is IGnoOsTokenRedeemer, OsTokenRedeemer { } /// @inheritdoc OsTokenRedeemer - function _transferAssets(address receiver, uint256 assets) internal override { + function _transferAssets( + address receiver, + uint256 assets + ) internal override { SafeERC20.safeTransfer(_gnoToken, receiver, assets); } } diff --git a/contracts/tokens/OsTokenRedeemer.sol b/contracts/tokens/OsTokenRedeemer.sol index c78cc672..22146b8d 100644 --- a/contracts/tokens/OsTokenRedeemer.sol +++ b/contracts/tokens/OsTokenRedeemer.sol @@ -2,19 +2,22 @@ pragma solidity ^0.8.22; +import {Multicall} from "../base/Multicall.sol"; +import {IMetaVault} from "../interfaces/IMetaVault.sol"; +import {IOsTokenRedeemer} from "../interfaces/IOsTokenRedeemer.sol"; +import {IOsTokenVaultController} from "../interfaces/IOsTokenVaultController.sol"; +import {IVaultOsToken} from "../interfaces/IVaultOsToken.sol"; +import {IVaultSubVaults} from "../interfaces/IVaultSubVaults.sol"; +import {IVaultsRegistry} from "../interfaces/IVaultsRegistry.sol"; +import {Errors} from "../libraries/Errors.sol"; +import {ExitQueue} from "../libraries/ExitQueue.sol"; +import {Ownable, Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol"; -import {IVaultOsToken} from "../interfaces/IVaultOsToken.sol"; -import {IOsTokenVaultController} from "../interfaces/IOsTokenVaultController.sol"; -import {IOsTokenRedeemer} from "../interfaces/IOsTokenRedeemer.sol"; -import {Errors} from "../libraries/Errors.sol"; -import {ExitQueue} from "../libraries/ExitQueue.sol"; -import {Multicall} from "../base/Multicall.sol"; /** * @title OsTokenRedeemer @@ -22,6 +25,7 @@ import {Multicall} from "../base/Multicall.sol"; * @notice This contract is used to redeem OsTokens for the underlying asset. */ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { + IVaultsRegistry private immutable _vaultsRegistry; IERC20 private immutable _osToken; IOsTokenVaultController private immutable _osTokenVaultController; @@ -62,29 +66,34 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { uint256 public override exitQueueTimestamp; RedeemablePositions private _redeemablePositions; - RedeemablePositions private _pendingRedeemablePositions; ExitQueue.History private _exitQueue; /** * @dev Constructor + * @param vaultsRegistry_ The address of the VaultsRegistry contract * @param osToken_ The address of the OsToken contract * @param osTokenVaultController_ The address of the OsTokenVaultController contract * @param owner_ The address of the owner * @param exitQueueUpdateDelay_ The delay in seconds for exit queue updates */ - constructor(address osToken_, address osTokenVaultController_, address owner_, uint256 exitQueueUpdateDelay_) - Ownable(owner_) - { + constructor( + address vaultsRegistry_, + address osToken_, + address osTokenVaultController_, + address owner_, + uint256 exitQueueUpdateDelay_ + ) Ownable(owner_) { if (exitQueueUpdateDelay_ > type(uint64).max) { revert Errors.InvalidDelay(); } + _vaultsRegistry = IVaultsRegistry(vaultsRegistry_); _osToken = IERC20(osToken_); _osTokenVaultController = IOsTokenVaultController(osTokenVaultController_); exitQueueUpdateDelay = exitQueueUpdateDelay_; } /// @inheritdoc IOsTokenRedeemer - function getExitQueueData() external view override returns (uint256, uint256, uint256) { + function getExitQueueData() public view override returns (uint256, uint256, uint256) { return ( queuedShares, unclaimedAssets + redeemedAssets + swappedAssets, @@ -99,13 +108,9 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function pendingRedeemablePositions() external view override returns (bytes32 merkleRoot, string memory ipfsHash) { - merkleRoot = _pendingRedeemablePositions.merkleRoot; - ipfsHash = _pendingRedeemablePositions.ipfsHash; - } - - /// @inheritdoc IOsTokenRedeemer - function getExitQueueIndex(uint256 positionTicket) external view override returns (int256) { + function getExitQueueIndex( + uint256 positionTicket + ) external view override returns (int256) { uint256 checkpointIdx = ExitQueue.getCheckpointIndex(_exitQueue, positionTicket); return checkpointIdx < _exitQueue.checkpoints.length ? int256(checkpointIdx) : -1; } @@ -119,12 +124,11 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function calculateExitedAssets(address receiver, uint256 positionTicket, uint256 exitQueueIndex) - public - view - override - returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) - { + function calculateExitedAssets( + address receiver, + uint256 positionTicket, + uint256 exitQueueIndex + ) public view override returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) { uint256 exitingTickets = exitRequests[keccak256(abi.encode(receiver, positionTicket))]; if (exitingTickets == 0) return (0, 0, 0); @@ -140,7 +144,9 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function setPositionsManager(address positionsManager_) external override onlyOwner { + function setPositionsManager( + address positionsManager_ + ) external override onlyOwner { if (positionsManager_ == address(0)) { revert Errors.ZeroAddress(); } @@ -152,82 +158,66 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function proposeRedeemablePositions(RedeemablePositions calldata newPositions) external override { - if (msg.sender != positionsManager) { - revert Errors.AccessDenied(); - } - if (newPositions.merkleRoot == bytes32(0) || bytes(newPositions.ipfsHash).length == 0) { - revert Errors.InvalidRedeemablePositions(); - } - + function getExitQueueMissingAssets( + uint256 targetCumulativeTickets + ) external view override returns (uint256 missingAssets) { // SLOAD to memory - RedeemablePositions memory pendingPositions = _pendingRedeemablePositions; - if (pendingPositions.merkleRoot != bytes32(0) || bytes(pendingPositions.ipfsHash).length != 0) { - revert Errors.RedeemablePositionsProposed(); - } + (uint256 _queuedShares, uint256 _unclaimedAssets, uint256 totalTickets) = getExitQueueData(); - // SLOAD to memory - RedeemablePositions memory currentPositions = _redeemablePositions; - if (newPositions.merkleRoot == currentPositions.merkleRoot || bytes(pendingPositions.ipfsHash).length != 0) { - revert Errors.ValueNotChanged(); + // check whether already covered + if (totalTickets >= targetCumulativeTickets) { + return 0; } - // update state - _pendingRedeemablePositions = newPositions; + // calculate the amount of tickets that need to be covered + uint256 totalTicketsToCover = targetCumulativeTickets - totalTickets; - // emit event - emit RedeemablePositionsProposed(newPositions.merkleRoot, newPositions.ipfsHash); + // calculate missing assets + missingAssets = _osTokenVaultController.convertToAssets(Math.min(totalTicketsToCover, _queuedShares)); + + // check whether there is enough available assets + uint256 availableAssets = _availableAssets() - _unclaimedAssets; + return availableAssets >= missingAssets ? 0 : missingAssets - availableAssets; } /// @inheritdoc IOsTokenRedeemer - function acceptRedeemablePositions() external override onlyOwner { - // SLOAD to memory - RedeemablePositions memory newPositions = _pendingRedeemablePositions; + function setRedeemablePositions( + RedeemablePositions calldata newPositions + ) external override onlyOwner { if (newPositions.merkleRoot == bytes32(0) || bytes(newPositions.ipfsHash).length == 0) { revert Errors.InvalidRedeemablePositions(); } + // SLOAD to memory + RedeemablePositions memory currentPositions = _redeemablePositions; + if (newPositions.merkleRoot == currentPositions.merkleRoot) { + revert Errors.ValueNotChanged(); + } + // update state nonce += 1; _redeemablePositions = newPositions; - delete _pendingRedeemablePositions; // emit event - emit RedeemablePositionsAccepted(newPositions.merkleRoot, newPositions.ipfsHash); + emit RedeemablePositionsUpdated(newPositions.merkleRoot, newPositions.ipfsHash); } /// @inheritdoc IOsTokenRedeemer - function denyRedeemablePositions() external override onlyOwner { - // SLOAD to memory - RedeemablePositions memory newPositions = _pendingRedeemablePositions; - if (newPositions.merkleRoot == bytes32(0)) { - return; - } - delete _pendingRedeemablePositions; - - // emit event - emit RedeemablePositionsDenied(newPositions.merkleRoot, newPositions.ipfsHash); - } - - /// @inheritdoc IOsTokenRedeemer - function removeRedeemablePositions() external override onlyOwner { - // SLOAD to memory - RedeemablePositions memory positions = _redeemablePositions; - if (positions.merkleRoot == bytes32(0)) { - return; - } - - delete _redeemablePositions; - emit RedeemablePositionsRemoved(positions.merkleRoot, positions.ipfsHash); - } - - /// @inheritdoc IOsTokenRedeemer - function permitOsToken(uint256 shares, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override { + function permitOsToken( + uint256 shares, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override { try IERC20Permit(address(_osToken)).permit(msg.sender, address(this), shares, deadline, v, r, s) {} catch {} } /// @inheritdoc IOsTokenRedeemer - function enterExitQueue(uint256 shares, address receiver) external override returns (uint256 positionTicket) { + function enterExitQueue( + uint256 shares, + address receiver + ) external override returns (uint256 positionTicket) { if (shares == 0) revert Errors.InvalidShares(); if (receiver == address(0)) revert Errors.ZeroAddress(); @@ -253,7 +243,10 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function claimExitedAssets(uint256 positionTicket, uint256 exitQueueIndex) external override { + function claimExitedAssets( + uint256 positionTicket, + uint256 exitQueueIndex + ) external override { // calculate exited tickets and assets (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) = calculateExitedAssets(msg.sender, positionTicket, exitQueueIndex); @@ -280,11 +273,48 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { emit ExitedAssetsClaimed(msg.sender, positionTicket, newPositionTicket, exitedAssets); } + /// @inheritdoc IOsTokenRedeemer + function redeemSubVaultsAssets( + address metaVault, + uint256 assetsToRedeem + ) external override returns (uint256 totalRedeemedAssets) { + if (msg.sender != positionsManager) { + revert Errors.AccessDenied(); + } + + if (!_isMetaVault(metaVault)) { + revert Errors.InvalidVault(); + } + + return IMetaVault(metaVault).redeemSubVaultsAssets(assetsToRedeem); + } + + /// @inheritdoc IOsTokenRedeemer + function redeemSubVaultOsToken( + address subVault, + uint256 osTokenShares + ) external override { + if (!_vaultsRegistry.vaults(subVault) || !_isMetaVault(msg.sender)) { + revert Errors.AccessDenied(); + } + if (osTokenShares == 0) { + revert Errors.InvalidShares(); + } + + // redeem osToken shares from sub vault to meta vault + IVaultOsToken(subVault).redeemOsToken(osTokenShares, msg.sender, msg.sender); + } + + /// @inheritdoc IOsTokenRedeemer function redeemOsTokenPositions( OsTokenPosition[] memory positions, bytes32[] calldata proof, bool[] calldata proofFlags ) external override { + if (msg.sender != positionsManager) { + revert Errors.AccessDenied(); + } + // SLOAD to memory uint256 _queuedShares = queuedShares; uint256 positionsCount = positions.length; @@ -318,8 +348,12 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { // update state if (position.sharesToRedeem > 0) { - _queuedShares -= position.sharesToRedeem; - leafToProcessedShares[leaf] = processedPositionShares + position.sharesToRedeem; + unchecked { + // position.sharesToRedeem <= _queuedShares checked above + _queuedShares -= position.sharesToRedeem; + // cannot realistically overflow + leafToProcessedShares[leaf] = processedPositionShares + position.sharesToRedeem; + } } unchecked { @@ -371,6 +405,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { revert Errors.TooEarlyUpdate(); } + // update state uint256 processedShares = swappedShares + redeemedShares; uint256 processedAssets = swappedAssets + redeemedAssets; swappedShares = 0; @@ -396,7 +431,10 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { * @param assets The number of assets to swap * @return osTokenShares The number of OsToken shares swapped */ - function _swapAssetsToOsTokenShares(address receiver, uint256 assets) internal returns (uint256 osTokenShares) { + function _swapAssetsToOsTokenShares( + address receiver, + uint256 assets + ) internal returns (uint256 osTokenShares) { if (assets == 0) { revert Errors.InvalidAssets(); } @@ -419,6 +457,27 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { emit OsTokenSharesSwapped(msg.sender, receiver, osTokenShares, assets); } + /** + * @dev Internal function to check whether the caller is a meta vault + * @param vault The address of the vault to check + * @return True if the caller is a meta vault, false otherwise + */ + function _isMetaVault( + address vault + ) private view returns (bool) { + // must be a registered vault + if (!_vaultsRegistry.vaults(vault)) { + return false; + } + + // must be a meta vault + try IVaultSubVaults(vault).getSubVaults() { + return true; + } catch { + return false; + } + } + /** * @dev Internal function that must be implemented to return the available assets for exit * @return The amount of available assets for exit @@ -433,5 +492,8 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { * @param receiver The address that will receive the assets * @param assets The number of assets to transfer */ - function _transferAssets(address receiver, uint256 assets) internal virtual; + function _transferAssets( + address receiver, + uint256 assets + ) internal virtual; } From 89d0dda10db1f72ac51bc1ddcd2cc3dba0c8f672 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Jan 2026 19:43:30 +0200 Subject: [PATCH 08/19] Simplify upgrade scripts to deploy only meta vaults and OsToken redeemer --- script/ExecuteGovernorTxs.s.sol | 6 +- script/Network.sol | 70 ++++++++----- script/UpgradeEthNetwork.s.sol | 171 ++++-------------------------- script/UpgradeGnoNetwork.s.sol | 179 +++++--------------------------- 4 files changed, 94 insertions(+), 332 deletions(-) diff --git a/script/ExecuteGovernorTxs.s.sol b/script/ExecuteGovernorTxs.s.sol index 7507c40f..dbd3421a 100644 --- a/script/ExecuteGovernorTxs.s.sol +++ b/script/ExecuteGovernorTxs.s.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.22; -import {console} from "forge-std/console.sol"; -import {stdJson} from "forge-std/StdJson.sol"; +import {Network} from "./Network.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {Network} from "./Network.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {console} from "forge-std/console.sol"; contract ExecuteGovernorTxs is Network { using stdJson for string; diff --git a/script/Network.sol b/script/Network.sol index 7451951f..92b46078 100644 --- a/script/Network.sol +++ b/script/Network.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.22; -import {Script} from "forge-std/Script.sol"; -import {stdJson} from "forge-std/StdJson.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {IOsTokenConfig} from "../contracts/interfaces/IOsTokenConfig.sol"; import {IVaultVersion} from "../contracts/interfaces/IVaultVersion.sol"; import {IVaultsRegistry} from "../contracts/interfaces/IVaultsRegistry.sol"; -import {IOsTokenConfig} from "../contracts/interfaces/IOsTokenConfig.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Script} from "forge-std/Script.sol"; +import {stdJson} from "forge-std/StdJson.sol"; /** * @title Network @@ -40,6 +40,7 @@ abstract contract Network is Script { address erc20VaultFactory; address privErc20VaultFactory; address blocklistErc20VaultFactory; + address metaVaultFactory; address sharedMevEscrow; address osToken; address osTokenConfig; @@ -50,6 +51,10 @@ abstract contract Network is Script { address legacyPoolEscrow; address legacyRewardToken; address merkleDistributor; + address curatorsRegistry; + address balancedCurator; + address consolidationsChecker; + address rewardSplitterFactory; } struct Factory { @@ -110,6 +115,7 @@ abstract contract Network is Script { deployment.erc20VaultFactory = deploymentData.readAddress(".Erc20VaultFactory"); deployment.privErc20VaultFactory = deploymentData.readAddress(".PrivErc20VaultFactory"); deployment.blocklistErc20VaultFactory = deploymentData.readAddress(".BlocklistErc20VaultFactory"); + deployment.metaVaultFactory = deploymentData.readAddress(".MetaVaultFactory"); deployment.sharedMevEscrow = deploymentData.readAddress(".SharedMevEscrow"); deployment.osToken = deploymentData.readAddress(".OsToken"); deployment.osTokenConfig = deploymentData.readAddress(".OsTokenConfig"); @@ -121,6 +127,10 @@ abstract contract Network is Script { deployment.legacyRewardToken = deploymentData.readAddress(".LegacyRewardToken"); deployment.merkleDistributor = deploymentData.readAddress(".MerkleDistributor"); deployment.foxVault = isGnosisNetwork() ? address(0) : deploymentData.readAddress(".FoxVault"); + deployment.curatorsRegistry = deploymentData.readAddress(".CuratorsRegistry"); + deployment.balancedCurator = deploymentData.readAddress(".BalancedCurator"); + deployment.consolidationsChecker = deploymentData.readAddress(".ConsolidationsChecker"); + deployment.rewardSplitterFactory = deploymentData.readAddress(".RewardSplitterFactory"); _deployment = deployment; return deployment; @@ -147,12 +157,7 @@ abstract contract Network is Script { Deployment memory deployment = getDeploymentData(); if (removePrevVaultFactories) { - _governorCalls.push(_serializeRemoveFactory(deployment.vaultFactory)); - _governorCalls.push(_serializeRemoveFactory(deployment.privVaultFactory)); - _governorCalls.push(_serializeRemoveFactory(deployment.blocklistVaultFactory)); - _governorCalls.push(_serializeRemoveFactory(deployment.erc20VaultFactory)); - _governorCalls.push(_serializeRemoveFactory(deployment.privErc20VaultFactory)); - _governorCalls.push(_serializeRemoveFactory(deployment.blocklistErc20VaultFactory)); + _governorCalls.push(_serializeRemoveFactory(deployment.metaVaultFactory)); } _governorCalls.push(_serializeSetOsTokenRedeemer(deployment.osTokenConfig, osTokenRedeemer)); @@ -162,7 +167,9 @@ abstract contract Network is Script { vm.writeJson(output, filePath); } - function generateUpgradesJson(address[] memory vaultImpls) internal { + function generateUpgradesJson( + address[] memory vaultImpls + ) internal { string memory upgrades = "upgrades"; string memory output; @@ -180,10 +187,6 @@ abstract contract Network is Script { function generateAddressesJson( Factory[] memory newFactories, address validatorsChecker, - address consolidationsChecker, - address rewardSplitterFactory, - address curatorsRegistry, - address balancedCurator, address osTokenRedeemer ) internal { Deployment memory deployment = getDeploymentData(); @@ -203,9 +206,17 @@ abstract contract Network is Script { vm.serializeAddress(json, "LegacyPoolEscrow", deployment.legacyPoolEscrow); vm.serializeAddress(json, "LegacyRewardToken", deployment.legacyRewardToken); vm.serializeAddress(json, "MerkleDistributor", deployment.merkleDistributor); - vm.serializeAddress(json, "CuratorsRegistry", curatorsRegistry); - vm.serializeAddress(json, "BalancedCurator", balancedCurator); - vm.serializeAddress(json, "OsTokenRedeemer", osTokenRedeemer); + vm.serializeAddress(json, "CuratorsRegistry", deployment.curatorsRegistry); + vm.serializeAddress(json, "BalancedCurator", deployment.balancedCurator); + vm.serializeAddress(json, "ConsolidationsChecker", deployment.consolidationsChecker); + + vm.serializeAddress(json, "RewardSplitterFactory", deployment.rewardSplitterFactory); + vm.serializeAddress(json, "VaultFactory", deployment.vaultFactory); + vm.serializeAddress(json, "PrivVaultFactory", deployment.privVaultFactory); + vm.serializeAddress(json, "BlocklistVaultFactory", deployment.blocklistVaultFactory); + vm.serializeAddress(json, "Erc20VaultFactory", deployment.erc20VaultFactory); + vm.serializeAddress(json, "PrivErc20VaultFactory", deployment.privErc20VaultFactory); + vm.serializeAddress(json, "BlocklistErc20VaultFactory", deployment.blocklistErc20VaultFactory); for (uint256 i = 0; i < newFactories.length; i++) { Factory memory factory = newFactories[i]; @@ -216,15 +227,15 @@ abstract contract Network is Script { vm.serializeAddress(json, "FoxVault", deployment.foxVault); } - vm.serializeAddress(json, "ValidatorsChecker", validatorsChecker); - vm.serializeAddress(json, "ConsolidationsChecker", consolidationsChecker); - string memory output = vm.serializeAddress(json, "RewardSplitterFactory", rewardSplitterFactory); - + vm.serializeAddress(json, "OsTokenRedeemer", osTokenRedeemer); + string memory output = vm.serializeAddress(json, "ValidatorsChecker", validatorsChecker); string memory path = string.concat("./deployments/", getNetworkName(), "-new.json"); vm.writeJson(output, path); } - function _serializeAddVaultImpl(address vaultImpl) private returns (string memory) { + function _serializeAddVaultImpl( + address vaultImpl + ) private returns (string memory) { string memory object = "addVaultImpl"; Deployment memory deployment = getDeploymentData(); vm.serializeAddress(object, "to", deployment.vaultsRegistry); @@ -242,7 +253,9 @@ abstract contract Network is Script { return vm.serializeAddress(object, "params", params); } - function _serializeAddFactory(address factory) private returns (string memory) { + function _serializeAddFactory( + address factory + ) private returns (string memory) { string memory object = "addFactory"; Deployment memory deployment = getDeploymentData(); vm.serializeAddress(object, "to", deployment.vaultsRegistry); @@ -260,7 +273,9 @@ abstract contract Network is Script { return vm.serializeAddress(object, "params", params); } - function _serializeRemoveFactory(address factory) private returns (string memory) { + function _serializeRemoveFactory( + address factory + ) private returns (string memory) { string memory object = "removeFactory"; Deployment memory deployment = getDeploymentData(); vm.serializeAddress(object, "to", deployment.vaultsRegistry); @@ -278,7 +293,10 @@ abstract contract Network is Script { return vm.serializeAddress(object, "params", params); } - function _serializeSetOsTokenRedeemer(address osTokenConfig, address redeemer) private returns (string memory) { + function _serializeSetOsTokenRedeemer( + address osTokenConfig, + address redeemer + ) private returns (string memory) { string memory object = "setRedeemer"; vm.serializeAddress(object, "to", osTokenConfig); vm.serializeString(object, "operation", "0"); diff --git a/script/UpgradeEthNetwork.s.sol b/script/UpgradeEthNetwork.s.sol index 3dbf97e2..34062715 100644 --- a/script/UpgradeEthNetwork.s.sol +++ b/script/UpgradeEthNetwork.s.sol @@ -2,49 +2,29 @@ pragma solidity ^0.8.22; -import {console} from "forge-std/console.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {IEthVault} from "../contracts/interfaces/IEthVault.sol"; -import {IEthErc20Vault} from "../contracts/interfaces/IEthErc20Vault.sol"; -import {IVaultsRegistry} from "../contracts/interfaces/IVaultsRegistry.sol"; +import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; import {IVaultVersion} from "../contracts/interfaces/IVaultVersion.sol"; -import {ConsolidationsChecker} from "../contracts/validators/ConsolidationsChecker.sol"; -import {EthBlocklistErc20Vault} from "../contracts/vaults/ethereum/EthBlocklistErc20Vault.sol"; -import {EthBlocklistVault} from "../contracts/vaults/ethereum/EthBlocklistVault.sol"; -import {EthErc20Vault} from "../contracts/vaults/ethereum/EthErc20Vault.sol"; -import {EthGenesisVault} from "../contracts/vaults/ethereum/EthGenesisVault.sol"; -import {EthPrivErc20Vault} from "../contracts/vaults/ethereum/EthPrivErc20Vault.sol"; -import {EthPrivVault} from "../contracts/vaults/ethereum/EthPrivVault.sol"; -import {EthVault} from "../contracts/vaults/ethereum/EthVault.sol"; -import {EthMetaVault, IEthMetaVault} from "../contracts/vaults/ethereum/custom/EthMetaVault.sol"; -import {EthVaultFactory} from "../contracts/vaults/ethereum/EthVaultFactory.sol"; -import {EthMetaVaultFactory} from "../contracts/vaults/ethereum/custom/EthMetaVaultFactory.sol"; -import {EthValidatorsChecker} from "../contracts/validators/EthValidatorsChecker.sol"; -import {CuratorsRegistry, ICuratorsRegistry} from "../contracts/curators/CuratorsRegistry.sol"; -import {BalancedCurator} from "../contracts/curators/BalancedCurator.sol"; -import {EthRewardSplitter} from "../contracts/misc/EthRewardSplitter.sol"; -import {RewardSplitterFactory} from "../contracts/misc/RewardSplitterFactory.sol"; +import {IVaultsRegistry} from "../contracts/interfaces/IVaultsRegistry.sol"; import {EthOsTokenRedeemer} from "../contracts/tokens/EthOsTokenRedeemer.sol"; +import {EthValidatorsChecker} from "../contracts/validators/EthValidatorsChecker.sol"; +import {EthMetaVault} from "../contracts/vaults/ethereum/EthMetaVault.sol"; +import {EthMetaVaultFactory} from "../contracts/vaults/ethereum/EthMetaVaultFactory.sol"; +import {EthPrivMetaVault} from "../contracts/vaults/ethereum/EthPrivMetaVault.sol"; import {Network} from "./Network.sol"; +import {console} from "forge-std/console.sol"; contract UpgradeEthNetwork is Network { - address public metaVaultFactoryOwner; address public osTokenRedeemerOwner; address public validatorsRegistry; uint256 public osTokenRedeemerExitQueueUpdateDelay; - address public consolidationsChecker; address public validatorsChecker; - address public rewardSplitterFactory; - address public curatorsRegistry; - address public balancedCurator; address public osTokenRedeemer; address[] public vaultImpls; Factory[] public vaultFactories; function run() external { - metaVaultFactoryOwner = vm.envAddress("META_VAULT_FACTORY_OWNER"); osTokenRedeemerOwner = vm.envAddress("OS_TOKEN_REDEEMER_OWNER"); osTokenRedeemerExitQueueUpdateDelay = vm.envUint("OS_TOKEN_REDEEMER_EXIT_QUEUE_UPDATE_DELAY"); validatorsRegistry = vm.envAddress("VALIDATORS_REGISTRY"); @@ -57,7 +37,6 @@ contract UpgradeEthNetwork is Network { vm.startBroadcast(privateKey); // Deploy common contracts - consolidationsChecker = address(new ConsolidationsChecker(deployment.keeper)); validatorsChecker = address( new EthValidatorsChecker( validatorsRegistry, @@ -67,18 +46,11 @@ contract UpgradeEthNetwork is Network { deployment.legacyPoolEscrow ) ); - address rewardsSplitterImpl = address(new EthRewardSplitter()); - rewardSplitterFactory = address(new RewardSplitterFactory(rewardsSplitterImpl)); - // deploy curators - curatorsRegistry = address(new CuratorsRegistry()); - balancedCurator = address(new BalancedCurator()); - ICuratorsRegistry(curatorsRegistry).addCurator(balancedCurator); - ICuratorsRegistry(curatorsRegistry).initialize(Ownable(deployment.vaultsRegistry).owner()); - - // deploy OsToken redeemer + // Deploy OsToken redeemer osTokenRedeemer = address( new EthOsTokenRedeemer( + deployment.vaultsRegistry, deployment.osToken, deployment.osTokenVaultController, osTokenRedeemerOwner, @@ -92,59 +64,22 @@ contract UpgradeEthNetwork is Network { generateGovernorTxJson(vaultImpls, vaultFactories, osTokenRedeemer); generateUpgradesJson(vaultImpls); - generateAddressesJson( - vaultFactories, - validatorsChecker, - consolidationsChecker, - rewardSplitterFactory, - curatorsRegistry, - balancedCurator, - osTokenRedeemer - ); + generateAddressesJson(vaultFactories, validatorsChecker, osTokenRedeemer); } function _deployImplementations() internal { // constructors for implementations - IEthVault.EthVaultConstructorArgs memory vaultArgs = _getEthVaultConstructorArgs(); - IEthErc20Vault.EthErc20VaultConstructorArgs memory erc20VaultArgs = _getEthErc20VaultConstructorArgs(); - IEthMetaVault.EthMetaVaultConstructorArgs memory metaVaultArgs = _getEthMetaVaultConstructorArgs(); - Deployment memory deployment = getDeploymentData(); - - // update exited assets claim delay for public vaults - vaultArgs.exitingAssetsClaimDelay = PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY; - erc20VaultArgs.exitingAssetsClaimDelay = PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY; + IMetaVault.MetaVaultConstructorArgs memory metaVaultArgs = _getMetaVaultConstructorArgs(); - // deploy genesis vault - EthGenesisVault ethGenesisVault = - new EthGenesisVault(vaultArgs, deployment.legacyPoolEscrow, deployment.legacyRewardToken); - - // deploy public vaults - EthVault ethVault = new EthVault(vaultArgs); - EthErc20Vault ethErc20Vault = new EthErc20Vault(erc20VaultArgs); - - // deploy blocklist vaults - EthBlocklistVault ethBlocklistVault = new EthBlocklistVault(vaultArgs); - EthBlocklistErc20Vault ethBlocklistErc20Vault = new EthBlocklistErc20Vault(erc20VaultArgs); - - // update exited assets claim delay for private vaults - vaultArgs.exitingAssetsClaimDelay = PRIVATE_VAULT_EXITED_ASSETS_CLAIM_DELAY; - erc20VaultArgs.exitingAssetsClaimDelay = PRIVATE_VAULT_EXITED_ASSETS_CLAIM_DELAY; - - // deploy private vaults - EthPrivVault ethPrivVault = new EthPrivVault(vaultArgs); - EthPrivErc20Vault ethPrivErc20Vault = new EthPrivErc20Vault(erc20VaultArgs); - - // deploy MetaVault + // deploy meta vaults + metaVaultArgs.exitingAssetsClaimDelay = PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY; EthMetaVault ethMetaVault = new EthMetaVault(metaVaultArgs); - vaultImpls.push(address(ethGenesisVault)); - vaultImpls.push(address(ethVault)); - vaultImpls.push(address(ethErc20Vault)); - vaultImpls.push(address(ethBlocklistVault)); - vaultImpls.push(address(ethBlocklistErc20Vault)); - vaultImpls.push(address(ethPrivVault)); - vaultImpls.push(address(ethPrivErc20Vault)); + metaVaultArgs.exitingAssetsClaimDelay = PRIVATE_VAULT_EXITED_ASSETS_CLAIM_DELAY; + EthPrivMetaVault ethPrivMetaVault = new EthPrivMetaVault(metaVaultArgs); + vaultImpls.push(address(ethMetaVault)); + vaultImpls.push(address(ethPrivMetaVault)); } function _deployFactories() internal { @@ -153,84 +88,24 @@ contract UpgradeEthNetwork is Network { address vaultImpl = vaultImpls[i]; bytes32 vaultId = IVaultVersion(vaultImpl).vaultId(); - // skip factory creation for EthGenesisVault or EthFoxVault - if (vaultId == keccak256("EthGenesisVault") || vaultId == keccak256("EthFoxVault")) { - continue; - } - - address factory; + address factory = address(new EthMetaVaultFactory(vaultImpl, IVaultsRegistry(deployment.vaultsRegistry))); if (vaultId == keccak256("EthMetaVault")) { - factory = address( - new EthMetaVaultFactory( - metaVaultFactoryOwner, vaultImpl, IVaultsRegistry(deployment.vaultsRegistry) - ) - ); vaultFactories.push(Factory({name: "MetaVaultFactory", factory: factory})); - continue; - } - - factory = address(new EthVaultFactory(vaultImpl, IVaultsRegistry(deployment.vaultsRegistry))); - if (vaultId == keccak256("EthVault")) { - vaultFactories.push(Factory({name: "VaultFactory", factory: factory})); - } else if (vaultId == keccak256("EthErc20Vault")) { - vaultFactories.push(Factory({name: "Erc20VaultFactory", factory: factory})); - } else if (vaultId == keccak256("EthBlocklistVault")) { - vaultFactories.push(Factory({name: "BlocklistVaultFactory", factory: factory})); - } else if (vaultId == keccak256("EthPrivVault")) { - vaultFactories.push(Factory({name: "PrivVaultFactory", factory: factory})); - } else if (vaultId == keccak256("EthBlocklistErc20Vault")) { - vaultFactories.push(Factory({name: "BlocklistErc20VaultFactory", factory: factory})); - } else if (vaultId == keccak256("EthPrivErc20Vault")) { - vaultFactories.push(Factory({name: "PrivErc20VaultFactory", factory: factory})); + } else if (vaultId == keccak256("EthPrivMetaVault")) { + vaultFactories.push(Factory({name: "PrivMetaVaultFactory", factory: factory})); } } } - function _getEthVaultConstructorArgs() internal returns (IEthVault.EthVaultConstructorArgs memory) { - Deployment memory deployment = getDeploymentData(); - return IEthVault.EthVaultConstructorArgs({ - keeper: deployment.keeper, - vaultsRegistry: deployment.vaultsRegistry, - validatorsRegistry: validatorsRegistry, - validatorsWithdrawals: VALIDATORS_WITHDRAWALS, - validatorsConsolidations: VALIDATORS_CONSOLIDATIONS, - consolidationsChecker: consolidationsChecker, - osTokenVaultController: deployment.osTokenVaultController, - osTokenConfig: deployment.osTokenConfig, - osTokenVaultEscrow: deployment.osTokenVaultEscrow, - sharedMevEscrow: deployment.sharedMevEscrow, - depositDataRegistry: deployment.depositDataRegistry, - exitingAssetsClaimDelay: PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY - }); - } - - function _getEthErc20VaultConstructorArgs() internal returns (IEthErc20Vault.EthErc20VaultConstructorArgs memory) { - Deployment memory deployment = getDeploymentData(); - return IEthErc20Vault.EthErc20VaultConstructorArgs({ - keeper: deployment.keeper, - vaultsRegistry: deployment.vaultsRegistry, - validatorsRegistry: validatorsRegistry, - validatorsWithdrawals: VALIDATORS_WITHDRAWALS, - validatorsConsolidations: VALIDATORS_CONSOLIDATIONS, - consolidationsChecker: consolidationsChecker, - osTokenVaultController: deployment.osTokenVaultController, - osTokenConfig: deployment.osTokenConfig, - osTokenVaultEscrow: deployment.osTokenVaultEscrow, - sharedMevEscrow: deployment.sharedMevEscrow, - depositDataRegistry: deployment.depositDataRegistry, - exitingAssetsClaimDelay: PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY - }); - } - - function _getEthMetaVaultConstructorArgs() internal returns (IEthMetaVault.EthMetaVaultConstructorArgs memory) { + function _getMetaVaultConstructorArgs() internal returns (IMetaVault.MetaVaultConstructorArgs memory) { Deployment memory deployment = getDeploymentData(); - return IEthMetaVault.EthMetaVaultConstructorArgs({ + return IMetaVault.MetaVaultConstructorArgs({ keeper: deployment.keeper, vaultsRegistry: deployment.vaultsRegistry, osTokenVaultController: deployment.osTokenVaultController, osTokenConfig: deployment.osTokenConfig, osTokenVaultEscrow: deployment.osTokenVaultEscrow, - curatorsRegistry: curatorsRegistry, + curatorsRegistry: deployment.curatorsRegistry, exitingAssetsClaimDelay: PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY }); } diff --git a/script/UpgradeGnoNetwork.s.sol b/script/UpgradeGnoNetwork.s.sol index b4e1a00f..cbe6e6ca 100644 --- a/script/UpgradeGnoNetwork.s.sol +++ b/script/UpgradeGnoNetwork.s.sol @@ -2,54 +2,32 @@ pragma solidity ^0.8.22; -import {console} from "forge-std/console.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {IGnoVault} from "../contracts/interfaces/IGnoVault.sol"; -import {IGnoErc20Vault} from "../contracts/interfaces/IGnoErc20Vault.sol"; -import {IVaultsRegistry} from "../contracts/interfaces/IVaultsRegistry.sol"; +import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; import {IVaultVersion} from "../contracts/interfaces/IVaultVersion.sol"; -import {ConsolidationsChecker} from "../contracts/validators/ConsolidationsChecker.sol"; -import {GnoValidatorsChecker} from "../contracts/validators/GnoValidatorsChecker.sol"; -import {GnoRewardSplitter} from "../contracts/misc/GnoRewardSplitter.sol"; -import {RewardSplitterFactory} from "../contracts/misc/RewardSplitterFactory.sol"; -import {GnoGenesisVault} from "../contracts/vaults/gnosis/GnoGenesisVault.sol"; -import {GnoVault} from "../contracts/vaults/gnosis/GnoVault.sol"; -import {GnoErc20Vault} from "../contracts/vaults/gnosis/GnoErc20Vault.sol"; -import {GnoBlocklistVault} from "../contracts/vaults/gnosis/GnoBlocklistVault.sol"; -import {GnoBlocklistErc20Vault} from "../contracts/vaults/gnosis/GnoBlocklistErc20Vault.sol"; -import {GnoPrivVault} from "../contracts/vaults/gnosis/GnoPrivVault.sol"; -import {GnoPrivErc20Vault} from "../contracts/vaults/gnosis/GnoPrivErc20Vault.sol"; -import {IGnoMetaVault, GnoMetaVault} from "../contracts/vaults/gnosis/custom/GnoMetaVault.sol"; -import {GnoVaultFactory} from "../contracts/vaults/gnosis/GnoVaultFactory.sol"; -import {GnoMetaVaultFactory} from "../contracts/vaults/gnosis/custom/GnoMetaVaultFactory.sol"; -import {CuratorsRegistry, ICuratorsRegistry} from "../contracts/curators/CuratorsRegistry.sol"; -import {BalancedCurator} from "../contracts/curators/BalancedCurator.sol"; +import {IVaultsRegistry} from "../contracts/interfaces/IVaultsRegistry.sol"; import {GnoOsTokenRedeemer} from "../contracts/tokens/GnoOsTokenRedeemer.sol"; +import {GnoValidatorsChecker} from "../contracts/validators/GnoValidatorsChecker.sol"; +import {GnoMetaVault} from "../contracts/vaults/gnosis/GnoMetaVault.sol"; +import {GnoMetaVaultFactory} from "../contracts/vaults/gnosis/GnoMetaVaultFactory.sol"; +import {GnoPrivMetaVault} from "../contracts/vaults/gnosis/GnoPrivMetaVault.sol"; import {Network} from "./Network.sol"; +import {console} from "forge-std/console.sol"; contract UpgradeGnoNetwork is Network { - address public metaVaultFactoryOwner; address public osTokenRedeemerOwner; address public validatorsRegistry; address public gnoToken; uint256 public osTokenRedeemerExitQueueUpdateDelay; - address public consolidationsChecker; address public validatorsChecker; - address public rewardSplitterFactory; - address public tokensConverterFactory; - address public curatorsRegistry; - address public balancedCurator; address public osTokenRedeemer; address[] public vaultImpls; Factory[] public vaultFactories; function run() external { - metaVaultFactoryOwner = vm.envAddress("META_VAULT_FACTORY_OWNER"); osTokenRedeemerOwner = vm.envAddress("OS_TOKEN_REDEEMER_OWNER"); osTokenRedeemerExitQueueUpdateDelay = vm.envUint("OS_TOKEN_REDEEMER_EXIT_QUEUE_UPDATE_DELAY"); - tokensConverterFactory = vm.envAddress("TOKENS_CONVERTER_FACTORY"); validatorsRegistry = vm.envAddress("VALIDATORS_REGISTRY"); gnoToken = vm.envAddress("GNO_TOKEN"); uint256 privateKey = vm.envUint("PRIVATE_KEY"); @@ -61,7 +39,6 @@ contract UpgradeGnoNetwork is Network { vm.startBroadcast(privateKey); // Deploy common contracts - consolidationsChecker = address(new ConsolidationsChecker(deployment.keeper)); validatorsChecker = address( new GnoValidatorsChecker( validatorsRegistry, @@ -72,19 +49,12 @@ contract UpgradeGnoNetwork is Network { gnoToken ) ); - address rewardsSplitterImpl = address(new GnoRewardSplitter(gnoToken)); - rewardSplitterFactory = address(new RewardSplitterFactory(rewardsSplitterImpl)); - - // deploy curators - curatorsRegistry = address(new CuratorsRegistry()); - balancedCurator = address(new BalancedCurator()); - ICuratorsRegistry(curatorsRegistry).addCurator(balancedCurator); - ICuratorsRegistry(curatorsRegistry).initialize(Ownable(deployment.vaultsRegistry).owner()); - // deploy OsToken redeemer + // Deploy OsToken redeemer osTokenRedeemer = address( new GnoOsTokenRedeemer( gnoToken, + deployment.vaultsRegistry, deployment.osToken, deployment.osTokenVaultController, osTokenRedeemerOwner, @@ -98,59 +68,22 @@ contract UpgradeGnoNetwork is Network { generateGovernorTxJson(vaultImpls, vaultFactories, osTokenRedeemer); generateUpgradesJson(vaultImpls); - generateAddressesJson( - vaultFactories, - validatorsChecker, - consolidationsChecker, - rewardSplitterFactory, - curatorsRegistry, - balancedCurator, - osTokenRedeemer - ); + generateAddressesJson(vaultFactories, validatorsChecker, osTokenRedeemer); } function _deployImplementations() internal { // constructors for implementations - IGnoVault.GnoVaultConstructorArgs memory vaultArgs = _getGnoVaultConstructorArgs(); - IGnoErc20Vault.GnoErc20VaultConstructorArgs memory erc20VaultArgs = _getGnoErc20VaultConstructorArgs(); - IGnoMetaVault.GnoMetaVaultConstructorArgs memory metaVaultArgs = _getGnoMetaVaultConstructorArgs(); - Deployment memory deployment = getDeploymentData(); + IMetaVault.MetaVaultConstructorArgs memory metaVaultArgs = _getMetaVaultConstructorArgs(); - // update exited assets claim delay for public vaults - vaultArgs.exitingAssetsClaimDelay = PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY; - erc20VaultArgs.exitingAssetsClaimDelay = PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY; + // deploy meta vaults + metaVaultArgs.exitingAssetsClaimDelay = PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY; + GnoMetaVault gnoMetaVault = new GnoMetaVault(gnoToken, metaVaultArgs); - // deploy genesis vault - GnoGenesisVault gnoGenesisVault = - new GnoGenesisVault(vaultArgs, deployment.legacyPoolEscrow, deployment.legacyRewardToken); + metaVaultArgs.exitingAssetsClaimDelay = PRIVATE_VAULT_EXITED_ASSETS_CLAIM_DELAY; + GnoPrivMetaVault gnoPrivMetaVault = new GnoPrivMetaVault(gnoToken, metaVaultArgs); - // deploy public vaults - GnoVault gnoVault = new GnoVault(vaultArgs); - GnoErc20Vault gnoErc20Vault = new GnoErc20Vault(erc20VaultArgs); - - // deploy blocklist vaults - GnoBlocklistVault gnoBlocklistVault = new GnoBlocklistVault(vaultArgs); - GnoBlocklistErc20Vault gnoBlocklistErc20Vault = new GnoBlocklistErc20Vault(erc20VaultArgs); - - // update exited assets claim delay for private vaults - vaultArgs.exitingAssetsClaimDelay = PRIVATE_VAULT_EXITED_ASSETS_CLAIM_DELAY; - erc20VaultArgs.exitingAssetsClaimDelay = PRIVATE_VAULT_EXITED_ASSETS_CLAIM_DELAY; - - // deploy private vaults - GnoPrivVault gnoPrivVault = new GnoPrivVault(vaultArgs); - GnoPrivErc20Vault gnoPrivErc20Vault = new GnoPrivErc20Vault(erc20VaultArgs); - - // deploy MetaVault - GnoMetaVault gnoMetaVault = new GnoMetaVault(metaVaultArgs); - - vaultImpls.push(address(gnoGenesisVault)); - vaultImpls.push(address(gnoVault)); - vaultImpls.push(address(gnoErc20Vault)); - vaultImpls.push(address(gnoBlocklistVault)); - vaultImpls.push(address(gnoBlocklistErc20Vault)); - vaultImpls.push(address(gnoPrivVault)); - vaultImpls.push(address(gnoPrivErc20Vault)); vaultImpls.push(address(gnoMetaVault)); + vaultImpls.push(address(gnoPrivMetaVault)); } function _deployFactories() internal { @@ -159,89 +92,25 @@ contract UpgradeGnoNetwork is Network { address vaultImpl = vaultImpls[i]; bytes32 vaultId = IVaultVersion(vaultImpl).vaultId(); - // skip factory creation for GnoGenesisVault - if (vaultId == keccak256("GnoGenesisVault")) { - continue; - } - - address factory; + address factory = + address(new GnoMetaVaultFactory(vaultImpl, IVaultsRegistry(deployment.vaultsRegistry), gnoToken)); if (vaultId == keccak256("GnoMetaVault")) { - factory = address( - new GnoMetaVaultFactory( - metaVaultFactoryOwner, vaultImpl, IVaultsRegistry(deployment.vaultsRegistry), gnoToken - ) - ); vaultFactories.push(Factory({name: "MetaVaultFactory", factory: factory})); - continue; - } - - factory = address(new GnoVaultFactory(vaultImpl, IVaultsRegistry(deployment.vaultsRegistry), gnoToken)); - if (vaultId == keccak256("GnoVault")) { - vaultFactories.push(Factory({name: "VaultFactory", factory: address(factory)})); - } else if (vaultId == keccak256("GnoErc20Vault")) { - vaultFactories.push(Factory({name: "Erc20VaultFactory", factory: address(factory)})); - } else if (vaultId == keccak256("GnoBlocklistVault")) { - vaultFactories.push(Factory({name: "BlocklistVaultFactory", factory: address(factory)})); - } else if (vaultId == keccak256("GnoPrivVault")) { - vaultFactories.push(Factory({name: "PrivVaultFactory", factory: address(factory)})); - } else if (vaultId == keccak256("GnoBlocklistErc20Vault")) { - vaultFactories.push(Factory({name: "BlocklistErc20VaultFactory", factory: address(factory)})); - } else if (vaultId == keccak256("GnoPrivErc20Vault")) { - vaultFactories.push(Factory({name: "PrivErc20VaultFactory", factory: address(factory)})); + } else if (vaultId == keccak256("GnoPrivMetaVault")) { + vaultFactories.push(Factory({name: "PrivMetaVaultFactory", factory: factory})); } } } - function _getGnoVaultConstructorArgs() internal returns (IGnoVault.GnoVaultConstructorArgs memory) { - Deployment memory deployment = getDeploymentData(); - return IGnoVault.GnoVaultConstructorArgs({ - keeper: deployment.keeper, - vaultsRegistry: deployment.vaultsRegistry, - validatorsRegistry: validatorsRegistry, - validatorsWithdrawals: VALIDATORS_WITHDRAWALS, - validatorsConsolidations: VALIDATORS_CONSOLIDATIONS, - consolidationsChecker: consolidationsChecker, - osTokenVaultController: deployment.osTokenVaultController, - osTokenConfig: deployment.osTokenConfig, - osTokenVaultEscrow: deployment.osTokenVaultEscrow, - sharedMevEscrow: deployment.sharedMevEscrow, - depositDataRegistry: deployment.depositDataRegistry, - gnoToken: gnoToken, - tokensConverterFactory: tokensConverterFactory, - exitingAssetsClaimDelay: PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY - }); - } - - function _getGnoErc20VaultConstructorArgs() internal returns (IGnoErc20Vault.GnoErc20VaultConstructorArgs memory) { - Deployment memory deployment = getDeploymentData(); - return IGnoErc20Vault.GnoErc20VaultConstructorArgs({ - keeper: deployment.keeper, - vaultsRegistry: deployment.vaultsRegistry, - validatorsRegistry: validatorsRegistry, - validatorsWithdrawals: VALIDATORS_WITHDRAWALS, - validatorsConsolidations: VALIDATORS_CONSOLIDATIONS, - consolidationsChecker: consolidationsChecker, - osTokenVaultController: deployment.osTokenVaultController, - osTokenConfig: deployment.osTokenConfig, - osTokenVaultEscrow: deployment.osTokenVaultEscrow, - sharedMevEscrow: deployment.sharedMevEscrow, - depositDataRegistry: deployment.depositDataRegistry, - gnoToken: gnoToken, - tokensConverterFactory: tokensConverterFactory, - exitingAssetsClaimDelay: PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY - }); - } - - function _getGnoMetaVaultConstructorArgs() internal returns (IGnoMetaVault.GnoMetaVaultConstructorArgs memory) { + function _getMetaVaultConstructorArgs() internal returns (IMetaVault.MetaVaultConstructorArgs memory) { Deployment memory deployment = getDeploymentData(); - return IGnoMetaVault.GnoMetaVaultConstructorArgs({ + return IMetaVault.MetaVaultConstructorArgs({ keeper: deployment.keeper, vaultsRegistry: deployment.vaultsRegistry, osTokenVaultController: deployment.osTokenVaultController, osTokenConfig: deployment.osTokenConfig, osTokenVaultEscrow: deployment.osTokenVaultEscrow, - curatorsRegistry: curatorsRegistry, - gnoToken: gnoToken, + curatorsRegistry: deployment.curatorsRegistry, exitingAssetsClaimDelay: PUBLIC_VAULT_EXITED_ASSETS_CLAIM_DELAY }); } From f5e8524a0a276db566c1f271f47b688a62e6a186 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Jan 2026 19:45:50 +0200 Subject: [PATCH 09/19] Fix script formatting --- script/Network.sol | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/script/Network.sol b/script/Network.sol index 92b46078..334b6b5f 100644 --- a/script/Network.sol +++ b/script/Network.sol @@ -167,9 +167,7 @@ abstract contract Network is Script { vm.writeJson(output, filePath); } - function generateUpgradesJson( - address[] memory vaultImpls - ) internal { + function generateUpgradesJson(address[] memory vaultImpls) internal { string memory upgrades = "upgrades"; string memory output; @@ -184,11 +182,9 @@ abstract contract Network is Script { vm.writeJson(output, getUpgradesFilePath()); } - function generateAddressesJson( - Factory[] memory newFactories, - address validatorsChecker, - address osTokenRedeemer - ) internal { + function generateAddressesJson(Factory[] memory newFactories, address validatorsChecker, address osTokenRedeemer) + internal + { Deployment memory deployment = getDeploymentData(); string memory json = "addresses"; @@ -233,9 +229,7 @@ abstract contract Network is Script { vm.writeJson(output, path); } - function _serializeAddVaultImpl( - address vaultImpl - ) private returns (string memory) { + function _serializeAddVaultImpl(address vaultImpl) private returns (string memory) { string memory object = "addVaultImpl"; Deployment memory deployment = getDeploymentData(); vm.serializeAddress(object, "to", deployment.vaultsRegistry); @@ -253,9 +247,7 @@ abstract contract Network is Script { return vm.serializeAddress(object, "params", params); } - function _serializeAddFactory( - address factory - ) private returns (string memory) { + function _serializeAddFactory(address factory) private returns (string memory) { string memory object = "addFactory"; Deployment memory deployment = getDeploymentData(); vm.serializeAddress(object, "to", deployment.vaultsRegistry); @@ -273,9 +265,7 @@ abstract contract Network is Script { return vm.serializeAddress(object, "params", params); } - function _serializeRemoveFactory( - address factory - ) private returns (string memory) { + function _serializeRemoveFactory(address factory) private returns (string memory) { string memory object = "removeFactory"; Deployment memory deployment = getDeploymentData(); vm.serializeAddress(object, "to", deployment.vaultsRegistry); @@ -293,10 +283,7 @@ abstract contract Network is Script { return vm.serializeAddress(object, "params", params); } - function _serializeSetOsTokenRedeemer( - address osTokenConfig, - address redeemer - ) private returns (string memory) { + function _serializeSetOsTokenRedeemer(address osTokenConfig, address redeemer) private returns (string memory) { string memory object = "setRedeemer"; vm.serializeAddress(object, "to", osTokenConfig); vm.serializeString(object, "operation", "0"); From 4eb61c3a6e582ff40307ebe43497c3fe4c55985d Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Jan 2026 20:12:22 +0200 Subject: [PATCH 10/19] Fix interfaces formatting --- contracts/interfaces/IEthMetaVault.sol | 1 - contracts/interfaces/IOsTokenRedeemer.sol | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/interfaces/IEthMetaVault.sol b/contracts/interfaces/IEthMetaVault.sol index ca07d179..9e1ddb35 100644 --- a/contracts/interfaces/IEthMetaVault.sol +++ b/contracts/interfaces/IEthMetaVault.sol @@ -11,7 +11,6 @@ import {IMetaVault} from "./IMetaVault.sol"; * @notice Defines the interface for the EthMetaVault contract */ interface IEthMetaVault is IMetaVault { - /** * @notice Initializes or upgrades the EthMetaVault contract. Must transfer security deposit during the deployment. * @param params The encoded parameters for initializing the EthVault contract diff --git a/contracts/interfaces/IOsTokenRedeemer.sol b/contracts/interfaces/IOsTokenRedeemer.sol index 1b8c35b1..e045025e 100644 --- a/contracts/interfaces/IOsTokenRedeemer.sol +++ b/contracts/interfaces/IOsTokenRedeemer.sol @@ -154,7 +154,7 @@ interface IOsTokenRedeemer is IMulticall { /** * @notice The address authorized to redeem OsToken positions - * @return The address of the redeemer + * @return The address of the positions manager */ function positionsManager() external view returns (address); From 0fb62a3e6687d87e58eabb33c2a5f538e6e13869 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Thu, 15 Jan 2026 12:35:46 +0200 Subject: [PATCH 11/19] Fix tests --- test/BalancedCurator.t.sol | 2 +- test/ConsolidationsChecker.t.sol | 6 +-- test/CuratorsRegistry.t.sol | 8 +-- test/DepositDataRegistry.t.sol | 10 ++-- test/EthBlocklistErc20Vault.t.sol | 10 ++-- test/EthBlocklistVault.t.sol | 10 ++-- test/EthErc20Vault.t.sol | 27 ++++++----- test/EthFoxVault.t.sol | 14 +++--- test/EthGenesisVault.t.sol | 59 +--------------------- test/EthMetaVault.t.sol | 20 ++++---- test/EthOsTokenRedeemer.t.sol | 26 +++++----- test/EthOsTokenVaultEscrow.t.sol | 30 ++++++------ test/EthPrivErc20Vault.t.sol | 10 ++-- test/EthPrivVault.t.sol | 12 ++--- test/EthRewardSplitter.t.sol | 14 +++--- test/EthValidatorsChecker.t.sol | 12 ++--- test/EthVault.t.sol | 12 ++--- test/KeeperOracles.t.sol | 6 +-- test/KeeperRewards.t.sol | 6 +-- test/KeeperValidators.t.sol | 6 +-- test/OsToken.t.sol | 14 +++--- test/OsTokenConfig.t.sol | 12 ++--- test/OsTokenFlashLoans.t.sol | 4 +- test/OwnMevEscrow.t.sol | 8 +-- test/PriceFeed.t.sol | 2 +- test/SharedMevEscrow.t.sol | 6 +-- test/VaultAdmin.t.sol | 6 +-- test/VaultEnterExit.t.sol | 12 ++--- test/VaultEthStaking.t.sol | 12 ++--- test/VaultFee.t.sol | 28 +++++------ test/VaultOsToken.t.sol | 34 ++++++------- test/VaultState.t.sol | 10 ++-- test/VaultSubVaults.t.sol | 16 +++--- test/VaultToken.t.sol | 14 +++--- test/VaultValidators.t.sol | 6 +-- test/VaultVersion.t.sol | 4 +- test/VaultsRegistry.t.sol | 16 +++--- test/gnosis/GnoBlocklistErc20Vault.t.sol | 10 ++-- test/gnosis/GnoBlocklistVault.t.sol | 10 ++-- test/gnosis/GnoErc20Vault.t.sol | 14 +++--- test/gnosis/GnoGenesisVault.t.sol | 4 +- test/gnosis/GnoMetaVault.t.sol | 10 ++-- test/gnosis/GnoOsTokenRedeemer.t.sol | 8 +-- test/gnosis/GnoOsTokenVaultEscrow.t.sol | 4 +- test/gnosis/GnoOwnMevEscrow.t.sol | 4 +- test/gnosis/GnoPrivErc20Vault.t.sol | 10 ++-- test/gnosis/GnoPrivVault.t.sol | 12 ++--- test/gnosis/GnoRewardSplitter.t.sol | 10 ++-- test/gnosis/GnoSharedMevEscrow.t.sol | 4 +- test/gnosis/GnoValidatorsChecker.t.sol | 4 +- test/gnosis/GnoVault.t.sol | 12 ++--- test/gnosis/GnoVaultExitQueue.t.sol | 8 +-- test/gnosis/VaultGnoStaking.t.sol | 10 ++-- test/helpers/EthHelpers.sol | 62 ++++++++++++++++-------- test/helpers/GnoHelpers.sol | 28 +++++++++-- 55 files changed, 358 insertions(+), 370 deletions(-) diff --git a/test/BalancedCurator.t.sol b/test/BalancedCurator.t.sol index 41f0eb93..96f8f3b4 100644 --- a/test/BalancedCurator.t.sol +++ b/test/BalancedCurator.t.sol @@ -75,7 +75,7 @@ contract BalancedCuratorTest is Test { // 100 ETH to distribute across 5 vaults, but one is ejecting uint256 assetsToDeposit = 100 ether; address[] memory vaults = subVaults; - address ejecting = makeAddr("unknown"); + address ejecting = makeAddr("Unknown"); // Should revert with EjectingVaultNotFound error vm.expectRevert(Errors.EjectingVaultNotFound.selector); diff --git a/test/ConsolidationsChecker.t.sol b/test/ConsolidationsChecker.t.sol index 6568714d..c67f8a28 100644 --- a/test/ConsolidationsChecker.t.sol +++ b/test/ConsolidationsChecker.t.sol @@ -30,8 +30,8 @@ contract ConsolidationsCheckerTest is Test, EthHelpers { consolidationsChecker = ConsolidationsChecker(address(contracts.consolidationsChecker)); // Set up test accounts - admin = makeAddr("admin"); - vault = makeAddr("vault"); + admin = makeAddr("Admin"); + vault = makeAddr("Vault"); // Store initial min oracles value _validatorsMinOraclesBefore = contracts.keeper.validatorsMinOracles(); @@ -412,7 +412,7 @@ contract ConsolidationsCheckerTest is Test, EthHelpers { _generateValidSignatures(vault, validatorsData, contracts.keeper.validatorsMinOracles()); // Create a different vault address - address differentVault = makeAddr("differentVault"); + address differentVault = makeAddr("DifferentVault"); // Try to verify signatures with different vault _startSnapshotGas("ConsolidationsCheckerTest_test_verifySignatures_differentVault"); diff --git a/test/CuratorsRegistry.t.sol b/test/CuratorsRegistry.t.sol index 725eb6ee..a60f2033 100644 --- a/test/CuratorsRegistry.t.sol +++ b/test/CuratorsRegistry.t.sol @@ -14,10 +14,10 @@ contract CuratorsRegistryTest is Test { function setUp() public { // Create accounts - owner = makeAddr("owner"); - newOwner = makeAddr("newOwner"); - curator = makeAddr("curator"); - nonOwner = makeAddr("nonOwner"); + owner = makeAddr("Owner"); + newOwner = makeAddr("NewOwner"); + curator = makeAddr("Curator"); + nonOwner = makeAddr("NonOwner"); // Deploy registry with owner as the deployer vm.prank(owner); diff --git a/test/DepositDataRegistry.t.sol b/test/DepositDataRegistry.t.sol index 24250334..71f2fb1a 100644 --- a/test/DepositDataRegistry.t.sol +++ b/test/DepositDataRegistry.t.sol @@ -38,7 +38,7 @@ contract DepositDataRegistryTest is Test, EthHelpers { depositDataRegistry = IDepositDataRegistry(_depositDataRegistry); // Create a valid vault (version >= 2) - admin = makeAddr("admin"); + admin = makeAddr("Admin"); validVault = _getOrCreateVault( VaultType.EthVault, admin, @@ -59,13 +59,13 @@ contract DepositDataRegistryTest is Test, EthHelpers { IEthVault(validVault).getExitQueueData(); exitingAssets = totalExitingAssets + IEthVault(validVault).convertToAssets(queuedShares) + unclaimedAssets; - invalidVault = makeAddr("invalidVault"); - nonAdmin = makeAddr("nonAdmin"); - newDepositDataManager = makeAddr("newDepositDataManager"); + invalidVault = makeAddr("InvalidVault"); + nonAdmin = makeAddr("NonAdmin"); + newDepositDataManager = makeAddr("NewDepositDataManager"); // Create or mock a vault with version < 2 // For this test, we'll simulate a vault with version 1 - lowVersionVault = makeAddr("lowVersionVault"); + lowVersionVault = makeAddr("LowVersionVault"); vm.mockCall(lowVersionVault, abi.encodeWithSelector(IVaultVersion.version.selector), abi.encode(uint8(1))); // Mock that lowVersionVault is registered in the vaults registry diff --git a/test/EthBlocklistErc20Vault.t.sol b/test/EthBlocklistErc20Vault.t.sol index cdf3a01e..7d11d7e4 100644 --- a/test/EthBlocklistErc20Vault.t.sol +++ b/test/EthBlocklistErc20Vault.t.sol @@ -30,11 +30,11 @@ contract EthBlocklistErc20VaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - blocklistManager = makeAddr("blocklistManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + blocklistManager = makeAddr("BlocklistManager"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); diff --git a/test/EthBlocklistVault.t.sol b/test/EthBlocklistVault.t.sol index 11c94b03..697d09f3 100644 --- a/test/EthBlocklistVault.t.sol +++ b/test/EthBlocklistVault.t.sol @@ -31,11 +31,11 @@ contract EthBlocklistVaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - blocklistManager = makeAddr("blocklistManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + blocklistManager = makeAddr("BlocklistManager"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); diff --git a/test/EthErc20Vault.t.sol b/test/EthErc20Vault.t.sol index b44d645f..5628b616 100644 --- a/test/EthErc20Vault.t.sol +++ b/test/EthErc20Vault.t.sol @@ -31,10 +31,10 @@ contract EthErc20VaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); @@ -316,8 +316,8 @@ contract EthErc20VaultTest is Test, EthHelpers { // Check results assertGt(mintedAssets, 0, "Should have minted some osToken assets"); assertEq(vault.osTokenPositions(sender), osTokenShares, "Should have minted expected osToken shares"); - assertEq( - vault.balanceOf(sender), vault.convertToShares(depositAmount), "Should have received tokens for the deposit" + assertApproxEqAbs( + vault.balanceOf(sender), vault.convertToShares(depositAmount), 1, "Should have received tokens for the deposit" ); } @@ -342,14 +342,17 @@ contract EthErc20VaultTest is Test, EthHelpers { // Check results assertGt(mintedAssets, 0, "Should have minted some osToken assets"); assertEq(vault.osTokenPositions(sender), osTokenShares, "Should have minted expected osToken shares"); - assertEq( - vault.balanceOf(sender), vault.convertToShares(depositAmount), "Should have received tokens for the deposit" + assertApproxEqAbs( + vault.balanceOf(sender), + vault.convertToShares(depositAmount), + 1, + "Should have received tokens for the deposit" ); } function test_withdrawValidator_validatorsManager() public { // 1. Set validators manager - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); vm.prank(admin); vault.setValidatorsManager(validatorsManager); @@ -369,12 +372,12 @@ contract EthErc20VaultTest is Test, EthHelpers { } function test_withdrawValidator_unknown() public { - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); vm.prank(admin); vault.setValidatorsManager(validatorsManager); // 1. Set unknown address - address unknown = makeAddr("unknown"); + address unknown = makeAddr("Unknown"); uint256 withdrawFee = 0.1 ether; vm.deal(unknown, withdrawFee); @@ -410,7 +413,7 @@ contract EthErc20VaultTest is Test, EthHelpers { require(success, "ETH transfer failed"); // Verify sender received the correct number of tokens - assertEq(vault.balanceOf(sender), shares, "Sender should have received tokens"); + assertApproxEqAbs(vault.balanceOf(sender), shares, 1, "Sender should have received tokens"); } function test_updateExitQueue_emitsTransfer() public { diff --git a/test/EthFoxVault.t.sol b/test/EthFoxVault.t.sol index 32c900c6..e6f94c7d 100644 --- a/test/EthFoxVault.t.sol +++ b/test/EthFoxVault.t.sol @@ -28,11 +28,11 @@ contract EthFoxVaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - blocklistManager = makeAddr("blocklistManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + blocklistManager = makeAddr("BlocklistManager"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); @@ -254,7 +254,7 @@ contract EthFoxVaultTest is Test, EthHelpers { function test_withdrawValidator_validatorsManager() public { // 1. Set validators manager - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); vm.prank(admin); vault.setValidatorsManager(validatorsManager); @@ -275,7 +275,7 @@ contract EthFoxVaultTest is Test, EthHelpers { function test_withdrawValidator_unknown() public { // 1. Set unknown address - address unknown = makeAddr("unknown"); + address unknown = makeAddr("Unknown"); uint256 withdrawFee = 0.1 ether; vm.deal(unknown, withdrawFee); diff --git a/test/EthGenesisVault.t.sol b/test/EthGenesisVault.t.sol index f9f0ab5b..4c7dd730 100644 --- a/test/EthGenesisVault.t.sol +++ b/test/EthGenesisVault.t.sol @@ -30,8 +30,8 @@ contract EthGenesisVaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); // Fund accounts with ETH for testing vm.deal(admin, 100 ether); @@ -61,61 +61,6 @@ contract EthGenesisVaultTest is Test, EthHelpers { IEthGenesisVault(_vault).initialize(initParams); } - function test_upgradesCorrectly() public { - // Get or create a vault - address vaultAddr = _getForkVault(VaultType.EthGenesisVault); - EthGenesisVault existingVault = EthGenesisVault(payable(vaultAddr)); - - vm.deal( - vaultAddr, - IVaultStateV4(address(existingVault)).totalExitingAssets() - + existingVault.convertToAssets(IVaultStateV4(address(existingVault)).queuedShares()) + vaultAddr.balance - ); - _depositToVault(address(existingVault), 40 ether, user, user); - _registerEthValidator(address(existingVault), 32 ether, true); - - vm.prank(user); - existingVault.enterExitQueue(10 ether, user); - - // Record initial state - uint256 initialTotalAssets = existingVault.totalAssets(); - uint256 initialTotalShares = existingVault.totalShares(); - uint256 senderBalanceBefore = existingVault.getShares(user); - uint256 initialCapacity = existingVault.capacity(); - uint256 initialFeePercent = existingVault.feePercent(); - address validatorsManager = existingVault.validatorsManager(); - address feeRecipient = existingVault.feeRecipient(); - address adminBefore = existingVault.admin(); - uint256 queuedSharesBefore = IVaultStateV4(address(existingVault)).queuedShares(); - uint256 totalExitingAssetsBefore = IVaultStateV4(address(existingVault)).totalExitingAssets(); - - assertEq(existingVault.vaultId(), keccak256("EthGenesisVault")); - assertEq(existingVault.version(), 4); - - address newImpl = _getOrCreateVaultImpl(VaultType.EthGenesisVault); - vm.deal(adminBefore, admin.balance + 1 ether); - - _startSnapshotGas("EthGenesisVaultTest_test_upgradesCorrectly"); - vm.prank(adminBefore); - existingVault.upgradeToAndCall(newImpl, "0x"); - _stopSnapshotGas(); - - (uint128 queuedSharesAfter,,, uint128 totalExitingAssetsAfter,) = existingVault.getExitQueueData(); - assertEq(existingVault.vaultId(), keccak256("EthGenesisVault")); - assertEq(existingVault.version(), 5); - assertEq(existingVault.admin(), adminBefore); - assertEq(existingVault.capacity(), initialCapacity); - assertEq(existingVault.feePercent(), initialFeePercent); - assertEq(existingVault.feeRecipient(), feeRecipient); - assertEq(existingVault.validatorsManager(), validatorsManager); - assertEq(queuedSharesAfter, queuedSharesBefore); - assertEq(existingVault.totalShares(), initialTotalShares); - assertEq(existingVault.totalAssets(), initialTotalAssets); - assertEq(totalExitingAssetsAfter, totalExitingAssetsBefore); - assertEq(existingVault.validatorsManagerNonce(), 0); - assertEq(existingVault.getShares(user), senderBalanceBefore); - } - function test_cannotInitializeTwice() public { // Get or create a vault address vaultAddr = _getOrCreateVault(VaultType.EthGenesisVault, admin, initParams, false); diff --git a/test/EthMetaVault.t.sol b/test/EthMetaVault.t.sol index 1a398363..db1c9eb5 100644 --- a/test/EthMetaVault.t.sol +++ b/test/EthMetaVault.t.sol @@ -37,10 +37,10 @@ contract EthMetaVaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - referrer = makeAddr("referrer"); + admin = makeAddr("Admin"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + referrer = makeAddr("Referrer"); // Deal ETH to accounts vm.deal(admin, 100 ether); @@ -90,7 +90,7 @@ contract EthMetaVaultTest is Test, EthHelpers { function test_deployment() public view { // Verify the vault was deployed correctly assertEq(metaVault.vaultId(), keccak256("EthMetaVault"), "Incorrect vault ID"); - assertEq(metaVault.version(), 5, "Incorrect version"); + assertEq(metaVault.version(), 6, "Incorrect version"); assertEq(metaVault.admin(), admin, "Incorrect admin"); assertEq(metaVault.subVaultsCurator(), curator, "Incorrect curator"); assertEq(metaVault.capacity(), 1000 ether, "Incorrect capacity"); @@ -455,14 +455,12 @@ contract EthMetaVaultTest is Test, EthHelpers { } function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault).depth( - 1 - ).checked_write(rewardsNonce); + stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault) + .depth(1).checked_write(rewardsNonce); } function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()").checked_write( - rewardsNonce - ); + stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") + .checked_write(rewardsNonce); } } diff --git a/test/EthOsTokenRedeemer.t.sol b/test/EthOsTokenRedeemer.t.sol index 74aad449..8786e627 100644 --- a/test/EthOsTokenRedeemer.t.sol +++ b/test/EthOsTokenRedeemer.t.sol @@ -44,12 +44,12 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Setup test accounts - owner = makeAddr("owner"); - positionsManager = makeAddr("positionsManager"); - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - user3 = makeAddr("user3"); - admin = makeAddr("admin"); + owner = makeAddr("Owner"); + positionsManager = makeAddr("PositionsManager"); + user1 = makeAddr("User1"); + user2 = makeAddr("User2"); + user3 = makeAddr("User3"); + admin = makeAddr("Admin"); // Fund accounts _fundAccounts(); @@ -199,7 +199,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { } function test_setPositionsManager_notOwner() public { - address newManager = makeAddr("newManager"); + address newManager = makeAddr("NewManager"); vm.prank(user1); vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, user1)); @@ -219,7 +219,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { } function test_setPositionsManager_success() public { - address newManager = makeAddr("newManager"); + address newManager = makeAddr("NewManager"); vm.expectEmit(true, false, false, true); emit IOsTokenRedeemer.PositionsManagerUpdated(newManager); @@ -542,7 +542,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { assertEq(exitRequestShares, osTokenShares, "Exit request not recorded correctly"); // Test entering queue with different receiver - address receiver = makeAddr("receiver"); + address receiver = makeAddr("Receiver"); // Mint more osTokens vm.prank(user1); @@ -979,7 +979,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { function test_processExitQueue_success() public { uint256 queuedShares = 100 ether; - address user = makeAddr("user"); + address user = makeAddr("User"); _enterExitQueue(user, queuedShares); _swapQueuedShares(queuedShares / 2); @@ -1048,7 +1048,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { // Setup: Create a non-existent position ticket uint256 nonExistentTicket = 99999; uint256 nonExistentExitQueueIndex = 0; - address nonExistentUser = makeAddr("nonExistentUser"); + address nonExistentUser = makeAddr("NonExistentUser"); // Act & Assert: Try to claim from a non-existent position vm.prank(nonExistentUser); @@ -1072,7 +1072,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { function test_claimExitedAssets_partialWithdrawal() public { // Setup: Create a user with a large position uint256 sharesToQueue = 100 ether; - address user = makeAddr("user"); + address user = makeAddr("User"); uint256 positionTicket = _enterExitQueue(user, sharesToQueue); // Simulate partial processing by having limited assets available @@ -1142,7 +1142,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { function test_claimExitedAssets_fullWithdrawal() public { // Setup: Create a user with a large position uint256 sharesToQueue = 100 ether; - address user = makeAddr("user"); + address user = makeAddr("User"); uint256 positionTicket = _enterExitQueue(user, sharesToQueue); // Simulate partial processing by having limited assets available diff --git a/test/EthOsTokenVaultEscrow.t.sol b/test/EthOsTokenVaultEscrow.t.sol index 2470c7f0..aa406e38 100644 --- a/test/EthOsTokenVaultEscrow.t.sol +++ b/test/EthOsTokenVaultEscrow.t.sol @@ -34,9 +34,9 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Setup addresses - user = makeAddr("user"); - admin = makeAddr("admin"); - liquidator = makeAddr("liquidator"); + user = makeAddr("User"); + admin = makeAddr("Admin"); + liquidator = makeAddr("Liquidator"); // Fund accounts vm.deal(user, 100 ether); @@ -497,7 +497,7 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { contracts.osTokenVaultEscrow.processExitedAssets(address(vault), exitPositionTicket, timestamp, exitQueueIndex); // Try to claim as a different user - address otherUser = makeAddr("otherUser"); + address otherUser = makeAddr("OtherUser"); _mintOsToken(otherUser, osTokenShares); // Give them the required osToken shares vm.startPrank(otherUser); @@ -808,13 +808,13 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { assertApproxEqAbs( liquidatorBalanceAfter - liquidatorBalanceBefore, exitedAssetsBefore, - 2, + 5, "Liquidator did not receive correct amount of assets" ); // Verify position was updated assertEq(ownerAfter, ownerBefore, "Owner should not change"); - assertApproxEqAbs(exitedAssetsAfter, 0, 2, "Exited assets not correctly reduced"); + assertApproxEqAbs(exitedAssetsAfter, 0, 5, "Exited assets not correctly reduced"); assertLt(sharesAfter, sharesBefore, "Shares not correctly reduced"); } @@ -996,7 +996,7 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { uint256 expectedAssets = contracts.osTokenVaultController.convertToAssets(osTokenShares); // Mint osToken shares to the redeemer - address redeemer = makeAddr("redeemer"); + address redeemer = makeAddr("Redeemer"); _mintOsToken(redeemer, osTokenShares); // set redeemer @@ -1004,7 +1004,7 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { contracts.osTokenConfig.setRedeemer(redeemer); // Record redeemer balance before - address receiver = makeAddr("receiver"); + address receiver = makeAddr("Receiver"); uint256 receiverBalanceBefore = receiver.balance; // Expect OsTokenRedeemed event @@ -1052,7 +1052,7 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { contracts.osTokenVaultEscrow.processExitedAssets(address(vault), exitPositionTicket, timestamp, exitQueueIndex); // Mock the osTokenConfig.redeemer call to return an official redeemer address - address officialRedeemer = makeAddr("officialRedeemer"); + address officialRedeemer = makeAddr("OfficialRedeemer"); vm.mockCall( address(contracts.osTokenConfig), abi.encodeWithSelector(bytes4(keccak256("redeemer()"))), @@ -1060,7 +1060,7 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { ); // Try to redeem from an unauthorized address - address unauthorizedCaller = makeAddr("unauthorizedCaller"); + address unauthorizedCaller = makeAddr("UnauthorizedCaller"); _mintOsToken(unauthorizedCaller, osTokenShares); vm.prank(unauthorizedCaller); @@ -1110,7 +1110,7 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { uint256 newLiqBonusPercent = 1.1e18; // 110% // Get a non-owner address - address nonOwner = makeAddr("nonOwner"); + address nonOwner = makeAddr("NonOwner"); // Call updateLiqConfig as non-owner, should revert vm.prank(nonOwner); @@ -1168,11 +1168,11 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { address currentAuthenticator = contracts.osTokenVaultEscrow.authenticator(); // Create a new authenticator address - address newAuthenticator = makeAddr("newAuthenticator"); + address newAuthenticator = makeAddr("NewAuthenticator"); // Ensure it's different from the current one if (newAuthenticator == currentAuthenticator) { - newAuthenticator = makeAddr("newAuthenticator2"); + newAuthenticator = makeAddr("NewAuthenticator2"); } // Expect AuthenticatorUpdated event @@ -1191,10 +1191,10 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { function test_setAuthenticator_onlyOwner() public { // Create a new authenticator address - address newAuthenticator = makeAddr("newAuthenticator"); + address newAuthenticator = makeAddr("NewAuthenticator"); // Get a non-owner address - address nonOwner = makeAddr("nonOwner"); + address nonOwner = makeAddr("NonOwner"); // Call setAuthenticator as non-owner, should revert vm.prank(nonOwner); diff --git a/test/EthPrivErc20Vault.t.sol b/test/EthPrivErc20Vault.t.sol index a8ea6033..1f9b2fb6 100644 --- a/test/EthPrivErc20Vault.t.sol +++ b/test/EthPrivErc20Vault.t.sol @@ -31,11 +31,11 @@ contract EthPrivErc20VaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - whitelister = makeAddr("whitelister"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + whitelister = makeAddr("Whitelister"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); diff --git a/test/EthPrivVault.t.sol b/test/EthPrivVault.t.sol index a72ea830..e767fa1c 100644 --- a/test/EthPrivVault.t.sol +++ b/test/EthPrivVault.t.sol @@ -31,11 +31,11 @@ contract EthPrivVaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - whitelister = makeAddr("whitelister"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + whitelister = makeAddr("Whitelister"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); @@ -391,7 +391,7 @@ contract EthPrivVaultTest is Test, EthHelpers { } function test_setWhitelister() public { - address newWhitelister = makeAddr("newWhitelister"); + address newWhitelister = makeAddr("NewWhitelister"); // Non-admin cannot set whitelister vm.prank(other); diff --git a/test/EthRewardSplitter.t.sol b/test/EthRewardSplitter.t.sol index d4076f6c..02aba7ef 100644 --- a/test/EthRewardSplitter.t.sol +++ b/test/EthRewardSplitter.t.sol @@ -34,11 +34,11 @@ contract EthRewardSplitterTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - shareholder1 = makeAddr("shareholder1"); - shareholder2 = makeAddr("shareholder2"); - depositor = makeAddr("depositor"); - claimer = makeAddr("claimer"); + admin = makeAddr("Admin"); + shareholder1 = makeAddr("Shareholder1"); + shareholder2 = makeAddr("Shareholder2"); + depositor = makeAddr("Depositor"); + claimer = makeAddr("Claimer"); // Fund accounts vm.deal(admin, 100 ether); @@ -298,7 +298,7 @@ contract EthRewardSplitterTest is Test, EthHelpers { // Someone else enters exit queue on behalf of shareholder1 vm.prank(claimer); uint256 timestamp = vm.getBlockTimestamp(); - vm.expectEmit(true, false, false, true); + vm.expectEmit(true, false, false, false); emit IRewardSplitter.ExitQueueEnteredOnBehalf(shareholder1, 0, rewards); // Position ticket is unknown at this point _startSnapshotGas("EthRewardSplitter_enterExitQueueOnBehalf"); uint256 positionTicket = rewardSplitter.enterExitQueueOnBehalf(rewards, shareholder1); @@ -461,7 +461,7 @@ contract EthRewardSplitterTest is Test, EthHelpers { rewardSplitter.decreaseShares(address(0), 1000); // Also test non-zero but invalid account (one that has no shares) - address randomAccount = makeAddr("randomAccount"); + address randomAccount = makeAddr("RandomAccount"); vm.prank(admin); vm.expectRevert(); // This will revert when trying to decrease below zero, but the error type may vary rewardSplitter.decreaseShares(randomAccount, 1000); diff --git a/test/EthValidatorsChecker.t.sol b/test/EthValidatorsChecker.t.sol index 1ddc1a50..12834085 100644 --- a/test/EthValidatorsChecker.t.sol +++ b/test/EthValidatorsChecker.t.sol @@ -43,8 +43,8 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { ); // Setup accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); vm.deal(user, 100 ether); // Create and prepare a vault with sufficient funds @@ -89,7 +89,7 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { function testValidatorsManagerSignature_InvalidVault() public { // Use non-existent vault - address invalidVault = makeAddr("nonVault"); + address invalidVault = makeAddr("NonVault"); // Test with invalid vault (uint256 blockNumber, IValidatorsChecker.Status status) = @@ -155,7 +155,7 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { function testCheckDepositDataRoot_InvalidVault() public { // Use non-existent vault - address invalidVault = makeAddr("nonVault"); + address invalidVault = makeAddr("NonVault"); // Create params with invalid vault IValidatorsChecker.DepositDataRootCheckParams memory params = IValidatorsChecker.DepositDataRootCheckParams({ @@ -249,7 +249,7 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { _collateralizeEthVault(address(customVault)); // Set a custom validators manager - address customManager = makeAddr("customManager"); + address customManager = makeAddr("CustomManager"); vm.prank(admin); IVaultValidators(customVault).setValidatorsManager(customManager); @@ -360,7 +360,7 @@ contract EthValidatorsCheckerTest is Test, EthHelpers { function test_checkValidatorsManagerSignature_Success() public { // 1. Get the validators manager - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); uint256 validatorsManagerPrivKey = uint256(keccak256(abi.encodePacked("validatorsManager_key"))); validatorsManager = vm.addr(validatorsManagerPrivKey); diff --git a/test/EthVault.t.sol b/test/EthVault.t.sol index 5693697c..de3b6998 100644 --- a/test/EthVault.t.sol +++ b/test/EthVault.t.sol @@ -32,11 +32,11 @@ contract EthVaultTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - referrer = makeAddr("referrer"); - validatorsManager = makeAddr("validatorsManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + referrer = makeAddr("Referrer"); + validatorsManager = makeAddr("ValidatorsManager"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); @@ -289,7 +289,7 @@ contract EthVaultTest is Test, EthHelpers { function test_withdrawValidator_unknown() public { // Create an unknown address - address unknown = makeAddr("unknown"); + address unknown = makeAddr("Unknown"); // Fund the unknown account uint256 withdrawFee = 0.1 ether; diff --git a/test/KeeperOracles.t.sol b/test/KeeperOracles.t.sol index b4796970..e0cf560c 100644 --- a/test/KeeperOracles.t.sol +++ b/test/KeeperOracles.t.sol @@ -23,8 +23,8 @@ contract KeeperOraclesTest is Test, EthHelpers { // Set up test accounts owner = keeper.owner(); - newOracle = makeAddr("newOracle"); - nonOwner = makeAddr("nonOwner"); + newOracle = makeAddr("NewOracle"); + nonOwner = makeAddr("NonOwner"); } // Test cases for addOracle @@ -97,7 +97,7 @@ contract KeeperOraclesTest is Test, EthHelpers { vm.prank(owner); _startSnapshotGas("KeeperOraclesTest_test_addOracle_maxOraclesExceeded"); vm.expectRevert(Errors.MaxOraclesExceeded.selector); - keeper.addOracle(makeAddr("oneMoreOracle")); + keeper.addOracle(makeAddr("OneMoreOracle")); _stopSnapshotGas(); } diff --git a/test/KeeperRewards.t.sol b/test/KeeperRewards.t.sol index 67b967d9..281368b6 100644 --- a/test/KeeperRewards.t.sol +++ b/test/KeeperRewards.t.sol @@ -30,8 +30,8 @@ contract KeeperRewardsTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); // Fund accounts vm.deal(admin, 100 ether); @@ -180,7 +180,7 @@ contract KeeperRewardsTest is Test, EthHelpers { // Make sure we add enough oracles while (contracts.keeper.totalOracles() < newMinOracles) { - address newOracle = makeAddr("newOracle"); + address newOracle = makeAddr("NewOracle"); vm.prank(keeperOwner); contracts.keeper.addOracle(newOracle); } diff --git a/test/KeeperValidators.t.sol b/test/KeeperValidators.t.sol index 2ccc9a46..227d0676 100644 --- a/test/KeeperValidators.t.sol +++ b/test/KeeperValidators.t.sol @@ -29,8 +29,8 @@ contract KeeperValidatorsTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); owner = contracts.keeper.owner(); // Fund accounts @@ -407,7 +407,7 @@ contract KeeperValidatorsTest is Test, EthHelpers { uint256 currentMinOracles = contracts.keeper.validatorsMinOracles(); // Act & Assert: Call from non-owner should fail - address nonOwner = makeAddr("nonOwner"); + address nonOwner = makeAddr("NonOwner"); vm.prank(nonOwner); _startSnapshotGas("KeeperValidatorsTest_test_setValidatorsMinOracles_unauthorized"); vm.expectRevert(abi.encodeWithSignature("OwnableUnauthorizedAccount(address)", nonOwner)); diff --git a/test/OsToken.t.sol b/test/OsToken.t.sol index 3747def2..fb1e1184 100644 --- a/test/OsToken.t.sol +++ b/test/OsToken.t.sol @@ -28,9 +28,9 @@ contract OsTokenTest is Test, EthHelpers { osToken = OsToken(_osToken); // Setup test addresses - owner = makeAddr("owner"); - user = makeAddr("user"); - controller = makeAddr("controller"); + owner = makeAddr("Owner"); + user = makeAddr("User"); + controller = makeAddr("Controller"); // Fund user account for transactions vm.deal(user, 100 ether); @@ -287,7 +287,7 @@ contract OsTokenTest is Test, EthHelpers { uint256 amount = 50 ether; _mintOsToken(user, amount); - address recipient = makeAddr("recipient"); + address recipient = makeAddr("Recipient"); uint256 transferAmount = 10 ether; // Test transfer function @@ -301,7 +301,7 @@ contract OsTokenTest is Test, EthHelpers { assertEq(osToken.balanceOf(user), amount - transferAmount, "User's balance should be reduced"); // Test transferFrom function - address spender = makeAddr("spender"); + address spender = makeAddr("Spender"); uint256 approvalAmount = 20 ether; // Approve spender @@ -354,12 +354,12 @@ contract OsTokenTest is Test, EthHelpers { assertGt(osToken.balanceOf(user), initialOsTokenBalance, "osToken balance should increase"); // Due to conversion rates and fees, the exact amounts may not match perfectly - // We verify the amounts are close enough (within 5%) + // We verify the amounts are close enough (within 6%) uint256 actualIncrease = osToken.balanceOf(user) - initialOsTokenBalance; assertApproxEqRel( actualIncrease, mintedOsTokenShares, - 0.05e18, // 5% tolerance + 0.06e18, // 6% tolerance "Minted osToken amount too far from expected" ); } diff --git a/test/OsTokenConfig.t.sol b/test/OsTokenConfig.t.sol index a480f93f..65e349e3 100644 --- a/test/OsTokenConfig.t.sol +++ b/test/OsTokenConfig.t.sol @@ -32,10 +32,10 @@ contract OsTokenConfigTest is Test, EthHelpers { // Set up test accounts owner = Ownable(address(osTokenConfig)).owner(); - nonOwner = makeAddr("nonOwner"); - newRedeemer = makeAddr("newRedeemer"); - vault = makeAddr("vault"); - anotherVault = makeAddr("anotherVault"); + nonOwner = makeAddr("NonOwner"); + newRedeemer = makeAddr("NewRedeemer"); + vault = makeAddr("Vault"); + anotherVault = makeAddr("AnotherVault"); } // Test for initial contract state @@ -78,7 +78,7 @@ contract OsTokenConfigTest is Test, EthHelpers { // Make sure our new redeemer is different if (newRedeemer == currentRedeemer) { - newRedeemer = makeAddr("anotherNewRedeemer"); + newRedeemer = makeAddr("AnotherNewRedeemer"); } // Set up expected event @@ -366,7 +366,7 @@ contract OsTokenConfigTest is Test, EthHelpers { }); // Test address that doesn't have a specific config - address randomVault = makeAddr("randomVault"); + address randomVault = makeAddr("RandomVault"); // Expect OsTokenConfigUpdated event vm.expectEmit(true, false, true, true); diff --git a/test/OsTokenFlashLoans.t.sol b/test/OsTokenFlashLoans.t.sol index f0c8f903..8432e76d 100644 --- a/test/OsTokenFlashLoans.t.sol +++ b/test/OsTokenFlashLoans.t.sol @@ -34,8 +34,8 @@ contract OsTokenFlashLoansTest is Test, EthHelpers { osToken = OsToken(_osToken); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); vm.deal(user, 100 ether); // Deploy mock recipient diff --git a/test/OwnMevEscrow.t.sol b/test/OwnMevEscrow.t.sol index 21739435..949bab50 100644 --- a/test/OwnMevEscrow.t.sol +++ b/test/OwnMevEscrow.t.sol @@ -22,8 +22,8 @@ contract OwnMevEscrowTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Setup test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); vm.deal(admin, 100 ether); vm.deal(user, 100 ether); @@ -161,7 +161,7 @@ contract OwnMevEscrowTest is Test, EthHelpers { // Test 7: Multiple senders function test_multipleSenders() public { // Create another user - address anotherUser = makeAddr("anotherUser"); + address anotherUser = makeAddr("AnotherUser"); vm.deal(anotherUser, 5 ether); // Send ETH from multiple accounts @@ -190,7 +190,7 @@ contract OwnMevEscrowTest is Test, EthHelpers { // Test 8: Create escrow directly function test_createEscrowDirectly() public { - address newVault = makeAddr("newVault"); + address newVault = makeAddr("NewVault"); // Create a new escrow with newVault as the vault address OwnMevEscrow newEscrow = new OwnMevEscrow(newVault); diff --git a/test/PriceFeed.t.sol b/test/PriceFeed.t.sol index 27ba27b0..be387022 100644 --- a/test/PriceFeed.t.sol +++ b/test/PriceFeed.t.sol @@ -26,7 +26,7 @@ contract PriceFeedTest is Test, EthHelpers { osTokenVaultController = contracts.osTokenVaultController; // Set up test accounts - user = makeAddr("user"); + user = makeAddr("User"); // Deploy the PriceFeed contract priceFeed = new PriceFeed(address(osTokenVaultController), "StakeWise osETH/ETH Price Feed"); diff --git a/test/SharedMevEscrow.t.sol b/test/SharedMevEscrow.t.sol index 2600f06a..a4a2024d 100644 --- a/test/SharedMevEscrow.t.sol +++ b/test/SharedMevEscrow.t.sol @@ -36,9 +36,9 @@ contract SharedMevEscrowTest is Test { function setUp() public { // Setup test accounts - owner = makeAddr("owner"); - nonRegisteredAccount = makeAddr("nonRegisteredAccount"); - mevSender = makeAddr("mevSender"); + owner = makeAddr("Owner"); + nonRegisteredAccount = makeAddr("NonRegisteredAccount"); + mevSender = makeAddr("MevSender"); // Fund accounts vm.deal(mevSender, 10 ether); diff --git a/test/VaultAdmin.t.sol b/test/VaultAdmin.t.sol index 749a035e..d79edea2 100644 --- a/test/VaultAdmin.t.sol +++ b/test/VaultAdmin.t.sol @@ -25,9 +25,9 @@ contract VaultAdminTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - nonAdmin = makeAddr("nonAdmin"); - newAdmin = makeAddr("newAdmin"); + admin = makeAddr("Admin"); + nonAdmin = makeAddr("NonAdmin"); + newAdmin = makeAddr("NewAdmin"); // Create a vault with admin as the admin bytes memory initParams = abi.encode( diff --git a/test/VaultEnterExit.t.sol b/test/VaultEnterExit.t.sol index 426258ea..1b84674b 100644 --- a/test/VaultEnterExit.t.sol +++ b/test/VaultEnterExit.t.sol @@ -26,10 +26,10 @@ contract VaultEnterExitTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - sender2 = makeAddr("sender2"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); + sender = makeAddr("Sender"); + sender2 = makeAddr("Sender2"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); // Fund accounts with ETH for testing vm.deal(sender, 100 ether); @@ -166,7 +166,7 @@ contract VaultEnterExitTest is Test, EthHelpers { function test_deposit_success_withReferrer() public { // Set up a referrer - address validReferrer = makeAddr("referrer"); + address validReferrer = makeAddr("Referrer"); // Record initial balances and state uint256 senderBalanceBefore = sender.balance; @@ -641,7 +641,7 @@ contract VaultEnterExitTest is Test, EthHelpers { } function test_enterExitQueue_afterValidatorExit() public { - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); vm.prank(admin); vault.setValidatorsManager(validatorsManager); diff --git a/test/VaultEthStaking.t.sol b/test/VaultEthStaking.t.sol index 1b6f0306..da8d8468 100644 --- a/test/VaultEthStaking.t.sol +++ b/test/VaultEthStaking.t.sol @@ -29,11 +29,11 @@ contract VaultEthStakingTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - referrer = makeAddr("referrer"); - validatorsManager = makeAddr("validatorsManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + referrer = makeAddr("Referrer"); + validatorsManager = makeAddr("ValidatorsManager"); // Fund accounts for testing vm.deal(sender, 100 ether); @@ -61,7 +61,7 @@ contract VaultEthStakingTest is Test, EthHelpers { function test_invalidSecurityDeposit() public { // Security deposit amount is defined as 1e9 (1 Gwei) in the EthHelpers contract // Create a new admin address for this test - address newAdmin = makeAddr("newAdmin"); + address newAdmin = makeAddr("NewAdmin"); vm.deal(newAdmin, 1 ether); // Get the factory for creating vaults diff --git a/test/VaultFee.t.sol b/test/VaultFee.t.sol index 656a5fe7..5540e737 100644 --- a/test/VaultFee.t.sol +++ b/test/VaultFee.t.sol @@ -28,10 +28,10 @@ contract VaultFeeTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); - feeRecipient = makeAddr("feeRecipient"); - newFeeRecipient = makeAddr("newFeeRecipient"); + admin = makeAddr("Admin"); + user = makeAddr("User"); + feeRecipient = makeAddr("FeeRecipient"); + newFeeRecipient = makeAddr("NewFeeRecipient"); // Fund accounts with ETH for testing vm.deal(admin, 100 ether); @@ -174,13 +174,13 @@ contract VaultFeeTest is Test, EthHelpers { } function test_setFeePercent_tooSoon() public { - // First set fee percentage - uint16 firstFeePercent = 500; // 5% + // First set fee percentage (use a valid increase from initial) + uint16 firstFeePercent = initialFeePercent + 1; vm.prank(admin); vault.setFeePercent(firstFeePercent); // Then try to set again too soon (before feeChangeDelay have passed) - uint16 secondFeePercent = 600; // 6% + uint16 secondFeePercent = firstFeePercent + 1; vm.prank(admin); _startSnapshotGas("VaultFeeTest_test_setFeePercent_tooSoon"); vm.expectRevert(Errors.TooEarlyUpdate.selector); @@ -198,16 +198,16 @@ contract VaultFeeTest is Test, EthHelpers { } function test_setFeePercent_maxIncrease() public { - // First set fee percentage - uint16 firstFeePercent = 500; // 5% + // First set fee percentage to a known value (use a valid increase from initial) + uint16 firstFeePercent = initialFeePercent + 1; vm.prank(admin); vault.setFeePercent(firstFeePercent); // Wait for delay period vm.warp(vm.getBlockTimestamp() + feeChangeDelay + 1); - // Try to increase fee by more than 20% - uint16 invalidIncrease = 700; // 7% (more than 20% increase from 5%) + // Try to increase fee by more than 20% (more than 120% of current) + uint16 invalidIncrease = uint16((uint256(firstFeePercent) * 121) / 100); // ~21% increase vm.prank(admin); _startSnapshotGas("VaultFeeTest_test_setFeePercent_maxIncrease"); vm.expectRevert(Errors.InvalidFeePercent.selector); @@ -217,8 +217,8 @@ contract VaultFeeTest is Test, EthHelpers { // Fee percentage should remain at the first update assertEq(vault.feePercent(), firstFeePercent, "Fee percent should not change"); - // Try a valid increase (below 20%) - uint16 validIncrease = 600; // 6% (20% increase from 5%) + // Try a valid increase (at most 20%) + uint16 validIncrease = uint16((uint256(firstFeePercent) * 120) / 100); // exactly 20% increase vm.prank(admin); vault.setFeePercent(validIncrease); assertEq(vault.feePercent(), validIncrease, "Fee percent should update with valid increase"); @@ -233,7 +233,7 @@ contract VaultFeeTest is Test, EthHelpers { // Test setting fee percentage without harvesting vm.warp(vm.getBlockTimestamp() + feeChangeDelay + 1); - uint16 newFeePercent = 500; // 5% + uint16 newFeePercent = initialFeePercent + 1; // valid increase vm.prank(admin); _startSnapshotGas("VaultFeeTest_test_setFeePercent_requiresHarvest"); vm.expectRevert(Errors.NotHarvested.selector); diff --git a/test/VaultOsToken.t.sol b/test/VaultOsToken.t.sol index 4eda742e..230d20a3 100644 --- a/test/VaultOsToken.t.sol +++ b/test/VaultOsToken.t.sol @@ -43,10 +43,10 @@ contract VaultOsTokenTest is Test, EthHelpers { osTokenConfig = contracts.osTokenConfig; // Set up test accounts - owner = makeAddr("owner"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - referrer = makeAddr("referrer"); + owner = makeAddr("Owner"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + referrer = makeAddr("Referrer"); // Fund accounts for testing vm.deal(owner, 100 ether); @@ -349,8 +349,8 @@ contract VaultOsTokenTest is Test, EthHelpers { // Test minting to different receivers function test_mintOsToken_multipleReceivers() public { - address receiver1 = makeAddr("receiver1"); - address receiver2 = makeAddr("receiver2"); + address receiver1 = makeAddr("Receiver1"); + address receiver2 = makeAddr("Receiver2"); uint256 mintAmount = contracts.osTokenVaultController.convertToShares(1 ether); @@ -469,7 +469,7 @@ contract VaultOsTokenTest is Test, EthHelpers { // Test burning with non-existent position function test_burnOsToken_invalidPosition() public { // Use a different address that has no position - address nonPositionHolder = makeAddr("nonPositionHolder"); + address nonPositionHolder = makeAddr("NonPositionHolder"); uint256 mintAmount = contracts.osTokenVaultController.convertToShares(1 ether); vm.prank(owner); @@ -620,7 +620,7 @@ contract VaultOsTokenTest is Test, EthHelpers { vault.mintOsToken(owner, mintAmount, referrer); // Try to redeem as non-redeemer - address nonRedeemer = makeAddr("nonRedeemer"); + address nonRedeemer = makeAddr("NonRedeemer"); vm.prank(nonRedeemer); _startSnapshotGas("VaultOsTokenTest_test_redeemOsToken_onlyRedeemer"); vm.expectRevert(Errors.AccessDenied.selector); @@ -671,7 +671,7 @@ contract VaultOsTokenTest is Test, EthHelpers { // Test redemption with non-existent position function test_redeemOsToken_nonExistentPosition() public { // Try to redeem from an address with no position - address noPositionAddr = makeAddr("noPosition"); + address noPositionAddr = makeAddr("NoPosition"); address redeemer = osTokenConfig.redeemer(); vm.prank(redeemer); @@ -890,7 +890,7 @@ contract VaultOsTokenTest is Test, EthHelpers { vault.enterExitQueue(exitAmount, owner); // Try to liquidate - will fail because health factor not below threshold yet - address liquidator = makeAddr("liquidator"); + address liquidator = makeAddr("Liquidator"); vm.prank(liquidator); vm.expectRevert(Errors.InvalidHealthFactor.selector); vault.liquidateOsToken(maxOsTokenShares / 2, owner, liquidator); @@ -931,7 +931,7 @@ contract VaultOsTokenTest is Test, EthHelpers { vault.updateState(harvestParams); // Verify the position is now liquidatable - address liquidator = makeAddr("liquidator"); + address liquidator = makeAddr("Liquidator"); uint256 liquidatorInitialBalance = liquidator.balance; _mintOsToken(liquidator, osTokenShares); @@ -985,7 +985,7 @@ contract VaultOsTokenTest is Test, EthHelpers { uint256 expectedAssets = (normalAssets * config.liqBonusPercent) / 1e18; // Prepare liquidator - address liquidator = makeAddr("liquidator"); + address liquidator = makeAddr("Liquidator"); uint256 liquidatorInitialBalance = liquidator.balance; _mintOsToken(liquidator, liquidationAmount); @@ -1020,7 +1020,7 @@ contract VaultOsTokenTest is Test, EthHelpers { // Test that liquidation is disabled when configured function test_liquidateOsToken_liquidationDisabled() public { // Create a vault with disabled liquidations - address adminWithDisabledLiq = makeAddr("adminWithDisabledLiq"); + address adminWithDisabledLiq = makeAddr("AdminWithDisabledLiq"); vm.deal(adminWithDisabledLiq, 100 ether); bytes memory initParams = abi.encode( IEthVault.EthVaultInitParams({ @@ -1044,7 +1044,7 @@ contract VaultOsTokenTest is Test, EthHelpers { vm.stopPrank(); // Deposit to vault and collateralize - address vaultOwner = makeAddr("vaultOwner"); + address vaultOwner = makeAddr("VaultOwner"); vm.deal(vaultOwner, 100 ether); _depositToVault(address(vaultWithDisabledLiq), 50 ether, vaultOwner, vaultOwner); _collateralizeEthVault(address(vaultWithDisabledLiq)); @@ -1060,7 +1060,7 @@ contract VaultOsTokenTest is Test, EthHelpers { vaultWithDisabledLiq.updateState(harvestParams); // Prepare liquidator - address liquidator = makeAddr("liquidator"); + address liquidator = makeAddr("Liquidator"); uint256 liquidationAmount = osTokenShares / 4; _mintOsToken(liquidator, liquidationAmount); @@ -1107,7 +1107,7 @@ contract VaultOsTokenTest is Test, EthHelpers { uint256 initialPosition = vault.osTokenPositions(owner); // Prepare for partial liquidation - address liquidator = makeAddr("liquidator"); + address liquidator = makeAddr("Liquidator"); uint256 liquidationAmount = osTokenShares / 3; _mintOsToken(liquidator, liquidationAmount); @@ -1179,7 +1179,7 @@ contract VaultOsTokenTest is Test, EthHelpers { vault.updateState(harvestParams); // Verify the position is now liquidatable - address liquidator = makeAddr("liquidator"); + address liquidator = makeAddr("Liquidator"); _mintOsToken(liquidator, osTokenShares); // remove withdrawable assets diff --git a/test/VaultState.t.sol b/test/VaultState.t.sol index 568bc13a..fca74901 100644 --- a/test/VaultState.t.sol +++ b/test/VaultState.t.sol @@ -25,10 +25,10 @@ contract VaultStateTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Setup test accounts - owner = makeAddr("owner"); - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - admin = makeAddr("admin"); + owner = makeAddr("Owner"); + user1 = makeAddr("User1"); + user2 = makeAddr("User2"); + admin = makeAddr("Admin"); // Fund accounts vm.deal(owner, 100 ether); @@ -246,8 +246,8 @@ contract VaultStateTest is Test, EthHelpers { // Enter exit queue vm.prank(owner); - uint256 timestamp = vm.getBlockTimestamp(); uint256 positionTicket = vault.enterExitQueue(exitShares, owner); + uint256 timestamp = vm.getBlockTimestamp(); // Verify share reduction uint256 ownerSharesAfter = vault.getShares(owner); diff --git a/test/VaultSubVaults.t.sol b/test/VaultSubVaults.t.sol index 8c3b9bb0..1085b4f3 100644 --- a/test/VaultSubVaults.t.sol +++ b/test/VaultSubVaults.t.sol @@ -41,7 +41,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up accounts - admin = makeAddr("admin"); + admin = makeAddr("Admin"); vm.deal(admin, 100 ether); // Create a curator @@ -78,8 +78,8 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_setSubVaultsCurator_notAdmin() public { // Setup - address nonAdmin = makeAddr("nonAdmin"); - address newCurator = makeAddr("newCurator"); + address nonAdmin = makeAddr("NonAdmin"); + address newCurator = makeAddr("NewCurator"); // Register the new curator in the curators registry vm.prank(CuratorsRegistry(_curatorsRegistry).owner()); @@ -110,7 +110,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_setSubVaultsCurator_notRegisteredCurator() public { // Setup: Create a new curator address that is not registered - address unregisteredCurator = makeAddr("unregisteredCurator"); + address unregisteredCurator = makeAddr("UnregisteredCurator"); // Action & Assert: Expect revert when trying to set an unregistered curator vm.prank(admin); @@ -120,7 +120,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_setSubVaultsCurator_success() public { // Setup: Create and register a new curator - address newCurator = makeAddr("newCurator"); + address newCurator = makeAddr("NewCurator"); vm.prank(CuratorsRegistry(_curatorsRegistry).owner()); CuratorsRegistry(_curatorsRegistry).addCurator(newCurator); @@ -148,7 +148,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVault); // Setup: Create a non-admin user - address nonAdmin = makeAddr("nonAdmin"); + address nonAdmin = makeAddr("NonAdmin"); // Action & Assert: Non-admin cannot add a sub vault vm.prank(nonAdmin); @@ -172,7 +172,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_addSubVault_notRegisteredVault() public { // Setup: Create an address that's not registered as a vault - address fakeVault = makeAddr("fakeVault"); + address fakeVault = makeAddr("FakeVault"); // Action & Assert: Cannot add non-registered vault vm.prank(admin); @@ -362,7 +362,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { address subVaultToEject = subVaults[0]; // Setup: Create a non-admin user - address nonAdmin = makeAddr("nonAdmin"); + address nonAdmin = makeAddr("NonAdmin"); // Action & Assert: Non-admin cannot eject a sub vault vm.prank(nonAdmin); diff --git a/test/VaultToken.t.sol b/test/VaultToken.t.sol index 29b5794e..ce9294c2 100644 --- a/test/VaultToken.t.sol +++ b/test/VaultToken.t.sol @@ -26,10 +26,10 @@ contract VaultTokenTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Setup test accounts - owner = makeAddr("owner"); - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - admin = makeAddr("admin"); + owner = makeAddr("Owner"); + user1 = makeAddr("User1"); + user2 = makeAddr("User2"); + admin = makeAddr("Admin"); // Fund accounts vm.deal(owner, 100 ether); @@ -323,7 +323,7 @@ contract VaultTokenTest is Test, EthHelpers { // Verify queued shares (uint128 queuedShares,,,,) = vault.getExitQueueData(); - assertEq(queuedShares, exitShares, "Queued shares should match exit amount"); + assertApproxEqAbs(queuedShares, exitShares, 1, "Queued shares should match exit amount"); } // Test _updateExitQueue burns shares and emits Transfer @@ -549,7 +549,7 @@ contract VaultTokenTest is Test, EthHelpers { // Test InvalidTokenMeta error for token name too long function test_invalidTokenMetaNameTooLong() public { // Create a new admin for this test - address newAdmin = makeAddr("newAdmin"); + address newAdmin = makeAddr("NewAdmin"); vm.deal(newAdmin, 10 ether); // Try to create vault with name longer than 30 characters @@ -576,7 +576,7 @@ contract VaultTokenTest is Test, EthHelpers { // Test InvalidTokenMeta error for token symbol too long function test_invalidTokenMetaSymbolTooLong() public { // Create a new admin for this test - address newAdmin = makeAddr("newAdmin"); + address newAdmin = makeAddr("NewAdmin"); vm.deal(newAdmin, 10 ether); // Try to create vault with symbol longer than 10 characters diff --git a/test/VaultValidators.t.sol b/test/VaultValidators.t.sol index d181a30b..e3e81a21 100644 --- a/test/VaultValidators.t.sol +++ b/test/VaultValidators.t.sol @@ -28,9 +28,9 @@ contract VaultValidatorsTest is Test, EthHelpers { contracts = _activateEthereumFork(); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); - nonManager = makeAddr("nonManager"); + admin = makeAddr("Admin"); + user = makeAddr("User"); + nonManager = makeAddr("NonManager"); (validatorsManager, validatorsManagerPrivateKey) = makeAddrAndKey("validatorsManager"); // Fund accounts with ETH for testing diff --git a/test/VaultVersion.t.sol b/test/VaultVersion.t.sol index 04e8b441..98314bcb 100644 --- a/test/VaultVersion.t.sol +++ b/test/VaultVersion.t.sol @@ -32,8 +32,8 @@ contract VaultVersionTest is Test, EthHelpers { vaultsRegistry = contracts.vaultsRegistry; // Set up accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); vm.deal(admin, 100 ether); vm.deal(user, 100 ether); diff --git a/test/VaultsRegistry.t.sol b/test/VaultsRegistry.t.sol index b5373223..0ef21767 100644 --- a/test/VaultsRegistry.t.sol +++ b/test/VaultsRegistry.t.sol @@ -23,11 +23,11 @@ contract VaultsRegistryTest is Test, EthHelpers { registry = contracts.vaultsRegistry; // Set up test accounts - owner = makeAddr("owner"); - nonOwner = makeAddr("nonOwner"); - mockFactory = makeAddr("mockFactory"); - mockVaultImpl = makeAddr("mockVaultImpl"); - mockVault = makeAddr("mockVault"); + owner = makeAddr("Owner"); + nonOwner = makeAddr("NonOwner"); + mockFactory = makeAddr("MockFactory"); + mockVaultImpl = makeAddr("MockVaultImpl"); + mockVault = makeAddr("MockVault"); // Since the registry is already deployed on the fork, we need to // impersonate its owner to perform ownership-restricted actions @@ -137,7 +137,7 @@ contract VaultsRegistryTest is Test, EthHelpers { // Deploy a new VaultsRegistry contract to test initialization VaultsRegistry newRegistry = new VaultsRegistry(); - address newOwner = makeAddr("newOwner"); + address newOwner = makeAddr("NewOwner"); vm.prank(newRegistry.owner()); _startSnapshotGas("VaultsRegistryTest_test_initialize"); @@ -211,8 +211,8 @@ contract VaultsRegistryTest is Test, EthHelpers { // Create a new registry and initialize it once VaultsRegistry newRegistry = new VaultsRegistry(); - address newOwner = makeAddr("newOwner"); - address anotherOwner = makeAddr("anotherOwner"); + address newOwner = makeAddr("NewOwner"); + address anotherOwner = makeAddr("AnotherOwner"); vm.prank(newRegistry.owner()); newRegistry.initialize(newOwner); diff --git a/test/gnosis/GnoBlocklistErc20Vault.t.sol b/test/gnosis/GnoBlocklistErc20Vault.t.sol index 2d14d821..c708abc6 100644 --- a/test/gnosis/GnoBlocklistErc20Vault.t.sol +++ b/test/gnosis/GnoBlocklistErc20Vault.t.sol @@ -29,11 +29,11 @@ contract GnoBlocklistErc20VaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - blocklistManager = makeAddr("blocklistManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + blocklistManager = makeAddr("BlocklistManager"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); diff --git a/test/gnosis/GnoBlocklistVault.t.sol b/test/gnosis/GnoBlocklistVault.t.sol index df08c052..e379ae0b 100644 --- a/test/gnosis/GnoBlocklistVault.t.sol +++ b/test/gnosis/GnoBlocklistVault.t.sol @@ -29,11 +29,11 @@ contract GnoBlocklistVaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - blocklistManager = makeAddr("blocklistManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + blocklistManager = makeAddr("BlocklistManager"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); diff --git a/test/gnosis/GnoErc20Vault.t.sol b/test/gnosis/GnoErc20Vault.t.sol index 0fc508f8..68bf8a30 100644 --- a/test/gnosis/GnoErc20Vault.t.sol +++ b/test/gnosis/GnoErc20Vault.t.sol @@ -31,10 +31,10 @@ contract GnoErc20VaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); @@ -305,7 +305,7 @@ contract GnoErc20VaultTest is Test, GnoHelpers { function test_withdrawValidator_validatorsManager() public { // 1. Set validators manager - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); vm.prank(admin); vault.setValidatorsManager(validatorsManager); @@ -325,12 +325,12 @@ contract GnoErc20VaultTest is Test, GnoHelpers { } function test_withdrawValidator_unknown() public { - address validatorsManager = makeAddr("validatorsManager"); + address validatorsManager = makeAddr("ValidatorsManager"); vm.prank(admin); vault.setValidatorsManager(validatorsManager); // 1. Set unknown address - address unknown = makeAddr("unknown"); + address unknown = makeAddr("Unknown"); uint256 withdrawFee = 0.1 ether; vm.deal(unknown, withdrawFee); diff --git a/test/gnosis/GnoGenesisVault.t.sol b/test/gnosis/GnoGenesisVault.t.sol index 6d618428..7d8228c2 100644 --- a/test/gnosis/GnoGenesisVault.t.sol +++ b/test/gnosis/GnoGenesisVault.t.sol @@ -28,8 +28,8 @@ contract GnoGenesisVaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); // Provide GNO to the test accounts _mintGnoToken(admin, 100 ether); diff --git a/test/gnosis/GnoMetaVault.t.sol b/test/gnosis/GnoMetaVault.t.sol index 5cca0ce0..5d90391c 100644 --- a/test/gnosis/GnoMetaVault.t.sol +++ b/test/gnosis/GnoMetaVault.t.sol @@ -42,10 +42,10 @@ contract GnoMetaVaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - admin = makeAddr("admin"); - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - referrer = makeAddr("referrer"); + admin = makeAddr("Admin"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + referrer = makeAddr("Referrer"); // Mint GNO tokens to accounts _mintGnoToken(admin, 100 ether); @@ -97,7 +97,7 @@ contract GnoMetaVaultTest is Test, GnoHelpers { function test_deployment() public view { // Verify the vault was deployed correctly assertEq(metaVault.vaultId(), keccak256("GnoMetaVault"), "Incorrect vault ID"); - assertEq(metaVault.version(), 3, "Incorrect version"); + assertEq(metaVault.version(), 4, "Incorrect version"); assertEq(metaVault.admin(), admin, "Incorrect admin"); assertEq(metaVault.subVaultsCurator(), curator, "Incorrect curator"); assertEq(metaVault.capacity(), 1000 ether, "Incorrect capacity"); diff --git a/test/gnosis/GnoOsTokenRedeemer.t.sol b/test/gnosis/GnoOsTokenRedeemer.t.sol index e56038ba..42a3a91d 100644 --- a/test/gnosis/GnoOsTokenRedeemer.t.sol +++ b/test/gnosis/GnoOsTokenRedeemer.t.sol @@ -33,10 +33,10 @@ contract GnoOsTokenRedeemerTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - owner = makeAddr("owner"); - positionsManager = makeAddr("positionsManager"); - user = makeAddr("user"); - redeemer = makeAddr("redeemer"); + owner = makeAddr("Owner"); + positionsManager = makeAddr("PositionsManager"); + user = makeAddr("User"); + redeemer = makeAddr("Redeemer"); // Fund accounts with GNO tokens and xDAI _mintGnoToken(user, 100 ether); diff --git a/test/gnosis/GnoOsTokenVaultEscrow.t.sol b/test/gnosis/GnoOsTokenVaultEscrow.t.sol index 6ac3b2ac..165ba38c 100644 --- a/test/gnosis/GnoOsTokenVaultEscrow.t.sol +++ b/test/gnosis/GnoOsTokenVaultEscrow.t.sol @@ -29,8 +29,8 @@ contract GnoOsTokenVaultEscrowTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Setup addresses - user = makeAddr("user"); - admin = makeAddr("admin"); + user = makeAddr("User"); + admin = makeAddr("Admin"); // Fund accounts vm.deal(user, 1 ether); diff --git a/test/gnosis/GnoOwnMevEscrow.t.sol b/test/gnosis/GnoOwnMevEscrow.t.sol index 42e5ae3e..ebba5e04 100644 --- a/test/gnosis/GnoOwnMevEscrow.t.sol +++ b/test/gnosis/GnoOwnMevEscrow.t.sol @@ -19,8 +19,8 @@ contract GnoOwnMevEscrowTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - vault = makeAddr("vault"); - other = makeAddr("other"); + vault = makeAddr("Vault"); + other = makeAddr("Other"); vm.deal(other, 10 ether); // Give 'other' some xDAI // Deploy the contract diff --git a/test/gnosis/GnoPrivErc20Vault.t.sol b/test/gnosis/GnoPrivErc20Vault.t.sol index e54a37a9..b5405e96 100644 --- a/test/gnosis/GnoPrivErc20Vault.t.sol +++ b/test/gnosis/GnoPrivErc20Vault.t.sol @@ -29,11 +29,11 @@ contract GnoPrivErc20VaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - whitelister = makeAddr("whitelister"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + whitelister = makeAddr("Whitelister"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); diff --git a/test/gnosis/GnoPrivVault.t.sol b/test/gnosis/GnoPrivVault.t.sol index a82c0292..708f0b0f 100644 --- a/test/gnosis/GnoPrivVault.t.sol +++ b/test/gnosis/GnoPrivVault.t.sol @@ -29,11 +29,11 @@ contract GnoPrivVaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - other = makeAddr("other"); - whitelister = makeAddr("whitelister"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + other = makeAddr("Other"); + whitelister = makeAddr("Whitelister"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); @@ -309,7 +309,7 @@ contract GnoPrivVaultTest is Test, GnoHelpers { } function test_setWhitelister() public { - address newWhitelister = makeAddr("newWhitelister"); + address newWhitelister = makeAddr("NewWhitelister"); // Non-admin cannot set whitelister vm.prank(other); diff --git a/test/gnosis/GnoRewardSplitter.t.sol b/test/gnosis/GnoRewardSplitter.t.sol index 9c5579e0..171780cd 100644 --- a/test/gnosis/GnoRewardSplitter.t.sol +++ b/test/gnosis/GnoRewardSplitter.t.sol @@ -36,11 +36,11 @@ contract GnoRewardSplitterTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - admin = makeAddr("admin"); - shareholder1 = makeAddr("shareholder1"); - shareholder2 = makeAddr("shareholder2"); - depositor = makeAddr("depositor"); - claimer = makeAddr("claimer"); + admin = makeAddr("Admin"); + shareholder1 = makeAddr("Shareholder1"); + shareholder2 = makeAddr("Shareholder2"); + depositor = makeAddr("Depositor"); + claimer = makeAddr("Claimer"); // Fund accounts vm.deal(admin, 100 ether); diff --git a/test/gnosis/GnoSharedMevEscrow.t.sol b/test/gnosis/GnoSharedMevEscrow.t.sol index dd3ba35f..de745dc8 100644 --- a/test/gnosis/GnoSharedMevEscrow.t.sol +++ b/test/gnosis/GnoSharedMevEscrow.t.sol @@ -22,11 +22,11 @@ contract GnoSharedMevEscrowTest is Test, GnoHelpers { sharedMevEscrow = new GnoSharedMevEscrow(address(contracts.vaultsRegistry)); // Set up test account - other = makeAddr("other"); + other = makeAddr("Other"); vm.deal(other, 10 ether); // Register a mock vault in the registry - mockVault = makeAddr("mockVault"); + mockVault = makeAddr("MockVault"); vm.prank(contracts.vaultsRegistry.owner()); contracts.vaultsRegistry.addVault(mockVault); } diff --git a/test/gnosis/GnoValidatorsChecker.t.sol b/test/gnosis/GnoValidatorsChecker.t.sol index eed8069d..7ec22e22 100644 --- a/test/gnosis/GnoValidatorsChecker.t.sol +++ b/test/gnosis/GnoValidatorsChecker.t.sol @@ -35,8 +35,8 @@ contract GnoValidatorsCheckerTest is Test, GnoHelpers { ); // Setup accounts - admin = makeAddr("admin"); - user = makeAddr("user"); + admin = makeAddr("Admin"); + user = makeAddr("User"); _mintGnoToken(user, 100 ether); _mintGnoToken(admin, 100 ether); diff --git a/test/gnosis/GnoVault.t.sol b/test/gnosis/GnoVault.t.sol index f4773f0c..02e217c3 100644 --- a/test/gnosis/GnoVault.t.sol +++ b/test/gnosis/GnoVault.t.sol @@ -31,11 +31,11 @@ contract GnoVaultTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - referrer = makeAddr("referrer"); - validatorsManager = makeAddr("validatorsManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + referrer = makeAddr("Referrer"); + validatorsManager = makeAddr("ValidatorsManager"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); @@ -370,7 +370,7 @@ contract GnoVaultTest is Test, GnoHelpers { function test_withdrawValidator_unknown() public { // Create an unknown address - address unknown = makeAddr("unknown"); + address unknown = makeAddr("Unknown"); // Fund the unknown account uint256 withdrawFee = 0.1 ether; diff --git a/test/gnosis/GnoVaultExitQueue.t.sol b/test/gnosis/GnoVaultExitQueue.t.sol index 5cd8528e..7c1bf524 100644 --- a/test/gnosis/GnoVaultExitQueue.t.sol +++ b/test/gnosis/GnoVaultExitQueue.t.sol @@ -44,10 +44,10 @@ contract GnoVaultExitQueueTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - admin = makeAddr("admin"); - user1 = makeAddr("user1"); - user2 = makeAddr("user2"); - user3 = makeAddr("user3"); + admin = makeAddr("Admin"); + user1 = makeAddr("User1"); + user2 = makeAddr("User2"); + user3 = makeAddr("User3"); // Fund accounts _mintGnoToken(admin, 100 ether); diff --git a/test/gnosis/VaultGnoStaking.t.sol b/test/gnosis/VaultGnoStaking.t.sol index edeb0ffb..ef083216 100644 --- a/test/gnosis/VaultGnoStaking.t.sol +++ b/test/gnosis/VaultGnoStaking.t.sol @@ -29,11 +29,11 @@ contract VaultGnoStakingTest is Test, GnoHelpers { contracts = _activateGnosisFork(); // Set up test accounts - sender = makeAddr("sender"); - receiver = makeAddr("receiver"); - admin = makeAddr("admin"); - referrer = makeAddr("referrer"); - validatorsManager = makeAddr("validatorsManager"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + admin = makeAddr("Admin"); + referrer = makeAddr("Referrer"); + validatorsManager = makeAddr("ValidatorsManager"); // Fund accounts with GNO for testing _mintGnoToken(sender, 100 ether); diff --git a/test/helpers/EthHelpers.sol b/test/helpers/EthHelpers.sol index 42390093..005b8308 100644 --- a/test/helpers/EthHelpers.sol +++ b/test/helpers/EthHelpers.sol @@ -25,6 +25,7 @@ import {EthVault, IEthVault} from "../../contracts/vaults/ethereum/EthVault.sol" import {EthVaultFactory} from "../../contracts/vaults/ethereum/EthVaultFactory.sol"; import {IEthFoxVault, EthFoxVault} from "../../contracts/vaults/ethereum/custom/EthFoxVault.sol"; import {EthMetaVault} from "../../contracts/vaults/ethereum/EthMetaVault.sol"; +import {EthPrivMetaVault} from "../../contracts/vaults/ethereum/EthPrivMetaVault.sol"; import {EthMetaVaultFactory} from "../../contracts/vaults/ethereum/EthMetaVaultFactory.sol"; import {Keeper} from "../../contracts/keeper/Keeper.sol"; import {ValidatorsConsolidationsMock} from "../../contracts/mocks/ValidatorsConsolidationsMock.sol"; @@ -34,7 +35,7 @@ import {VaultsRegistry, IVaultsRegistry} from "../../contracts/vaults/VaultsRegi import {CuratorsRegistry} from "../../contracts/curators/CuratorsRegistry.sol"; abstract contract EthHelpers is Test, ValidatorsHelpers { - uint256 internal constant forkBlockNumber = 22100000; + uint256 internal constant forkBlockNumber = 24235110; uint256 internal constant _securityDeposit = 1e9; address private constant _keeper = 0x6B5815467da09DaA7DC83Db21c9239d98Bb487b5; address private constant _validatorsRegistry = 0x00000000219ab540356cBB839Cbe05303d7705Fa; @@ -48,6 +49,8 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { address internal constant _depositDataRegistry = 0x75AB6DdCe07556639333d3Df1eaa684F5735223e; address internal constant _poolEscrow = 0x2296e122c1a20Fca3CAc3371357BdAd3be0dF079; address internal constant _rewardEthToken = 0x20BC832ca081b91433ff6c17f85701B6e92486c5; + address internal constant _consolidationsChecker = 0x033E5BaE5bdc459CBb7d388b41a9d62020Be810F; + address internal constant _curatorsRegistry = 0xa23F7c8d25f4503cA4cEd84d9CC2428e8745933C; uint256 internal constant _exitingAssetsClaimDelay = 15 hours; enum VaultType { @@ -59,7 +62,8 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { EthBlocklistErc20Vault, EthPrivErc20Vault, EthFoxVault, - EthMetaVault + EthMetaVault, + EthPrivMetaVault } struct ForkContracts { @@ -77,18 +81,14 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { mapping(VaultType vaultType => address vaultFactory) private _vaultFactories; mapping(VaultType vaultType => address vaultFactory) private _vaultPrevFactories; - address internal _consolidationsChecker; address internal _validatorsWithdrawals; address internal _validatorsConsolidations; - address internal _curatorsRegistry; function _activateEthereumFork() internal returns (ForkContracts memory) { vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), forkBlockNumber); _validatorsWithdrawals = address(new ValidatorsWithdrawalsMock()); _validatorsConsolidations = address(new ValidatorsConsolidationsMock()); - _consolidationsChecker = address(new ConsolidationsChecker(address(_keeper))); - _curatorsRegistry = address(new CuratorsRegistry()); return ForkContracts({ keeper: Keeper(_keeper), @@ -175,6 +175,8 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { impl = 0x9488A7dd178F0D927707eEc61A7D8C0ae9558c88; } else if (_vaultType == VaultType.EthBlocklistErc20Vault) { impl = 0x84d44A696539B3eF4162184fb8ab97596A311e9E; + } else if (_vaultType == VaultType.EthMetaVault) { + impl = 0xD0D527B67186d8880f9427ea4Cf9847E89bcE764; } else { return EthVaultFactory(address(0)); } @@ -256,15 +258,19 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { // Update with actual deployed vault addresses for each type if (vaultType == VaultType.EthVault) { - return 0x8A93A876912c9F03F88Bc9114847cf5b63c89f56; + return 0x7Eed3ea8D83ba4Ccc1b20674F46825ece2fce594; } else if (vaultType == VaultType.EthPrivVault) { return 0xD66A71A68392767F26b7EE47e9a0293191A23072; } else if (vaultType == VaultType.EthErc20Vault) { - return 0x7106FA765d45dF6d5340972C58742fC54f0d1Ef9; + return 0x9c29c571847A68A947AceC8bacd303e36bC72ec5; } else if (vaultType == VaultType.EthPrivErc20Vault) { return 0xFB22Ded2bd69aff0907e195F23E448aB44E3cA97; - } else if (vaultType == VaultType.EthFoxVault) { - return 0x4FEF9D741011476750A243aC70b9789a63dd47Df; + } else if (vaultType == VaultType.EthBlocklistVault) { + return 0xf51033647a8ab632B80B69b1c680aaDcC8ADa048; + } else if (vaultType == VaultType.EthBlocklistErc20Vault) { + return 0x498399e4f5FDe641a43DCEAFc0aac858abaF2034; + } else if (vaultType == VaultType.EthMetaVault) { + return 0x15639E82d2072Fa510E5d2b5F0db361c823bCad3; } return address(0); } @@ -277,10 +283,10 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { // Update with actual values if needed for specific vaults if (vault == 0xAC0F906E433d58FA868F936E8A43230473652885) { // Genesis Vault - newTotalReward += 11492988394536925432019; - newUnlockedMevReward += 588134256533622872486; + newTotalReward += 15357936244318545414766; + newUnlockedMevReward += 954581796972242855233; } else if (vault == 0x4FEF9D741011476750A243aC70b9789a63dd47Df) { - newTotalReward += 242948554351000000000; + newTotalReward += 1097049381115000000000; } if (!vm.envBool("TEST_USE_FORK_VAULTS")) { @@ -288,11 +294,14 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { } // Add specific rewards for each vault type - if (vault == 0x8A93A876912c9F03F88Bc9114847cf5b63c89f56) { - newTotalReward += 39158842473943927643; - newUnlockedMevReward += 6210915181493109989; + if (vault == 0x7Eed3ea8D83ba4Ccc1b20674F46825ece2fce594) { + newTotalReward += 1835140592094467096; + newUnlockedMevReward += 246577230094467096; } else if (vault == 0xD66A71A68392767F26b7EE47e9a0293191A23072) { newTotalReward += 17651468000000000; + } else if (vault == 0x9c29c571847A68A947AceC8bacd303e36bC72ec5) { + newTotalReward += 1590862592749045978; + newUnlockedMevReward += 251734367749045978; } return (newTotalReward, newUnlockedMevReward); @@ -341,10 +350,7 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { return vaultAddress; } - function _createV1EthVault(address admin, bytes memory initParams, bool isOwnMevEscrow) - internal - returns (address) - { + function _createV1EthVault(address admin, bytes memory initParams, bool isOwnMevEscrow) internal returns (address) { EthVaultFactory factory = EthVaultFactory(0xDada5a8E3703B1e3EA2bAe5Ab704627eb2659fCC); vm.prank(VaultsRegistry(_vaultsRegistry).owner()); @@ -364,6 +370,9 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { if (vaultType == VaultType.EthFoxVault) { if (currentVersion == 2) return; require(currentVersion == 1, "Invalid vault version"); + } else if (vaultType == VaultType.EthMetaVault || vaultType == VaultType.EthPrivMetaVault) { + if (currentVersion == 6) return; + require(currentVersion == 5, "Invalid vault version"); } else { if (currentVersion == 5) return; require(currentVersion == 4, "Invalid vault version"); @@ -450,6 +459,19 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { uint64(_exitingAssetsClaimDelay) ); impl = address(new EthMetaVault(ethMetaVaultArgs)); + } else if (_vaultType == VaultType.EthPrivMetaVault) { + IMetaVault.MetaVaultConstructorArgs memory ethMetaVaultArgs = IMetaVault.MetaVaultConstructorArgs( + _keeper, + _vaultsRegistry, + _osTokenVaultController, + _osTokenConfig, + _osTokenVaultEscrow, + _curatorsRegistry, + uint64(_exitingAssetsClaimDelay) + ); + impl = address(new EthPrivMetaVault(ethMetaVaultArgs)); + } else { + revert("Unsupported vault type"); } _vaultImplementations[_vaultType] = impl; diff --git a/test/helpers/GnoHelpers.sol b/test/helpers/GnoHelpers.sol index feedf686..98c7f88d 100644 --- a/test/helpers/GnoHelpers.sol +++ b/test/helpers/GnoHelpers.sol @@ -24,6 +24,7 @@ import {GnoPrivErc20Vault} from "../../contracts/vaults/gnosis/GnoPrivErc20Vault import {GnoPrivVault} from "../../contracts/vaults/gnosis/GnoPrivVault.sol"; import {GnoVault, IGnoVault} from "../../contracts/vaults/gnosis/GnoVault.sol"; import {GnoMetaVault} from "../../contracts/vaults/gnosis/GnoMetaVault.sol"; +import {GnoPrivMetaVault} from "../../contracts/vaults/gnosis/GnoPrivMetaVault.sol"; import {GnoMetaVaultFactory} from "../../contracts/vaults/gnosis/GnoMetaVaultFactory.sol"; import {GnoVaultFactory} from "../../contracts/vaults/gnosis/GnoVaultFactory.sol"; import {Keeper} from "../../contracts/keeper/Keeper.sol"; @@ -65,7 +66,8 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { GnoErc20Vault, GnoBlocklistErc20Vault, GnoPrivErc20Vault, - GnoMetaVault + GnoMetaVault, + GnoPrivMetaVault } struct ForkContracts { @@ -83,6 +85,7 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { mapping(VaultType vaultType => address vaultImpl) private _vaultImplementations; mapping(VaultType vaultType => address vaultFactory) private _vaultFactories; + mapping(VaultType vaultType => address vaultFactory) private _vaultPrevFactories; address private _consolidationsChecker; address private _validatorsWithdrawals; @@ -327,7 +330,7 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { returns (address) { address vaultAddress; - if (vaultType == VaultType.GnoMetaVault) { + if (vaultType == VaultType.GnoMetaVault || vaultType == VaultType.GnoPrivMetaVault) { GnoMetaVaultFactory factory = _getOrCreateMetaFactory(vaultType); vm.startPrank(admin); IERC20(_gnoToken).approve(address(factory), _securityDeposit); @@ -364,6 +367,9 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { if (vaultType == VaultType.GnoGenesisVault) { if (currentVersion == 4) return; require(currentVersion == 3, "Invalid vault version"); + } else if (vaultType == VaultType.GnoMetaVault || vaultType == VaultType.GnoPrivMetaVault) { + if (currentVersion == 4) return; + require(currentVersion == 3, "Invalid vault version"); } else { if (currentVersion == 3) return; require(currentVersion == 2, "Invalid vault version"); @@ -428,8 +434,7 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { } else if (_vaultType == VaultType.GnoPrivErc20Vault) { impl = address(new GnoPrivErc20Vault(gnoErc20Args)); } else if (_vaultType == VaultType.GnoMetaVault) { - IMetaVault.MetaVaultConstructorArgs memory gnoMetaVaultArgs = IMetaVault - .MetaVaultConstructorArgs( + IMetaVault.MetaVaultConstructorArgs memory gnoMetaVaultArgs = IMetaVault.MetaVaultConstructorArgs( _keeper, _vaultsRegistry, _osTokenVaultController, @@ -439,10 +444,25 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { uint64(_exitingAssetsClaimDelay) ); impl = address(new GnoMetaVault(_gnoToken, gnoMetaVaultArgs)); + } else if (_vaultType == VaultType.GnoPrivMetaVault) { + IMetaVault.MetaVaultConstructorArgs memory gnoMetaVaultArgs = IMetaVault.MetaVaultConstructorArgs( + _keeper, + _vaultsRegistry, + _osTokenVaultController, + _osTokenConfig, + _osTokenVaultEscrow, + _curatorsRegistry, + uint64(_exitingAssetsClaimDelay) + ); + impl = address(new GnoPrivMetaVault(_gnoToken, gnoMetaVaultArgs)); + } else { + revert("Unsupported vault type"); } _vaultImplementations[_vaultType] = impl; vm.prank(VaultsRegistry(_vaultsRegistry).owner()); VaultsRegistry(_vaultsRegistry).addVaultImpl(impl); + + return impl; } } From 12a1d14d4b667ce73e6c0f9e57a30d4a6f78749a Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Thu, 15 Jan 2026 14:21:26 +0200 Subject: [PATCH 12/19] Add tests for meta vault subvault addition --- contracts/vaults/modules/VaultSubVaults.sol | 2 +- test/VaultSubVaults.t.sol | 277 ++++++++++++++++++-- 2 files changed, 252 insertions(+), 27 deletions(-) diff --git a/contracts/vaults/modules/VaultSubVaults.sol b/contracts/vaults/modules/VaultSubVaults.sol index 058e8e01..f916e43d 100644 --- a/contracts/vaults/modules/VaultSubVaults.sol +++ b/contracts/vaults/modules/VaultSubVaults.sol @@ -6,9 +6,9 @@ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IVaultsRegistry} from "../../interfaces/IVaultsRegistry.sol"; import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol"; import {IVaultEnterExit} from "../../interfaces/IVaultEnterExit.sol"; diff --git a/test/VaultSubVaults.t.sol b/test/VaultSubVaults.t.sol index 1085b4f3..41bf520a 100644 --- a/test/VaultSubVaults.t.sol +++ b/test/VaultSubVaults.t.sol @@ -285,7 +285,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { metaVault.addSubVault(newSubVault); } - function test_addSubVault_firstSubVault() internal { + function test_addSubVault_firstSubVault() public { // create new meta vault bytes memory initParams = abi.encode( IMetaVault.MetaVaultInitParams({ @@ -1364,28 +1364,132 @@ contract VaultSubVaultsTest is Test, EthHelpers { ); } - function test_addSubVault_metaVaultAsSubVault_success() public { + function test_addSubVault_metaVaultAsSubVault_proposesMetaVault() public { // Setup: Create meta vault as sub vault - address metaSubVault = _setupMetaSubVault(admin); + address metaSubVault = _createMetaSubVault(admin); - // Get sub vault count before adding - uint256 subVaultsCountBefore = metaVault.getSubVaults().length; + // Verify pendingMetaSubVault is empty + assertEq(metaVault.pendingMetaSubVault(), address(0), "Pending meta sub vault should be empty"); + + // Expect the MetaSubVaultProposed event + vm.expectEmit(true, true, false, true); + emit IVaultSubVaults.MetaSubVaultProposed(admin, metaSubVault); // Start gas measurement - _startSnapshotGas("VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_success"); + _startSnapshotGas("VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_proposesMetaVault"); - // Expect the SubVaultAdded event - vm.expectEmit(true, true, false, true); - emit IVaultSubVaults.SubVaultAdded(admin, metaSubVault); + // Action: Add the meta vault as sub vault (should only propose, not add) + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Stop gas measurement + _stopSnapshotGas(); + + // Assert: Verify the meta vault is pending, not added + assertEq(metaVault.pendingMetaSubVault(), metaSubVault, "Meta vault should be pending"); + + // Verify the vault was NOT added to the sub vaults list yet + address[] memory subVaultsAfter = metaVault.getSubVaults(); + bool found = false; + for (uint256 i = 0; i < subVaultsAfter.length; i++) { + if (subVaultsAfter[i] == metaSubVault) { + found = true; + break; + } + } + assertFalse(found, "Meta vault should NOT be in sub vaults list yet"); + } + + function test_addSubVault_metaVaultAsSubVault_pendingAlreadyExists() public { + // Setup: Create two meta vaults as sub vaults + address metaSubVault1 = _createMetaSubVault(admin); + address metaSubVault2 = _createMetaSubVault(admin); + + // Propose the first meta sub vault + vm.prank(admin); + metaVault.addSubVault(metaSubVault1); + + // Verify pendingMetaSubVault is set + assertEq(metaVault.pendingMetaSubVault(), metaSubVault1, "First meta sub vault should be pending"); + + // Action & Assert: Cannot propose another meta sub vault while one is pending + vm.prank(admin); + vm.expectRevert(Errors.AlreadyAdded.selector); + metaVault.addSubVault(metaSubVault2); + } - // Action: Add the meta vault as sub vault + function test_acceptMetaSubVault_notVaultsRegistryOwner() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); vm.prank(admin); metaVault.addSubVault(metaSubVault); + // Action & Assert: Non-VaultsRegistry owner cannot accept meta sub vault + address nonOwner = makeAddr("NonOwner"); + vm.prank(nonOwner); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.acceptMetaSubVault(metaSubVault); + + // Also test that admin cannot accept + vm.prank(admin); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.acceptMetaSubVault(metaSubVault); + } + + function test_acceptMetaSubVault_zeroAddress() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Action & Assert: Cannot accept zero address + vm.prank(contracts.vaultsRegistry.owner()); + vm.expectRevert(Errors.InvalidVault.selector); + metaVault.acceptMetaSubVault(address(0)); + } + + function test_acceptMetaSubVault_invalidVault() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Setup: Create another meta sub vault (not pending) + address otherMetaSubVault = _createMetaSubVault(admin); + + // Action & Assert: Cannot accept a vault that is not pending + vm.prank(contracts.vaultsRegistry.owner()); + vm.expectRevert(Errors.InvalidVault.selector); + metaVault.acceptMetaSubVault(otherMetaSubVault); + } + + function test_acceptMetaSubVault_success() public { + // Setup: Create meta vault as sub vault + address metaSubVault = _createMetaSubVault(admin); + + // Propose meta sub vault + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Get sub vault count before accepting + uint256 subVaultsCountBefore = metaVault.getSubVaults().length; + + // Expect the SubVaultAdded event + vm.expectEmit(true, true, false, true); + emit IVaultSubVaults.SubVaultAdded(contracts.vaultsRegistry.owner(), metaSubVault); + + // Start gas measurement + _startSnapshotGas("VaultSubVaultsTest_test_acceptMetaSubVault_success"); + + // Action: Accept meta sub vault by VaultsRegistry owner + vm.prank(contracts.vaultsRegistry.owner()); + metaVault.acceptMetaSubVault(metaSubVault); + // Stop gas measurement _stopSnapshotGas(); // Assert: Verify the meta vault was added as sub vault + assertEq(metaVault.pendingMetaSubVault(), address(0), "Pending meta sub vault should be cleared"); address[] memory subVaultsAfter = metaVault.getSubVaults(); assertEq(subVaultsAfter.length, subVaultsCountBefore + 1, "Sub vault count should increase by 1"); @@ -1399,6 +1503,122 @@ contract VaultSubVaultsTest is Test, EthHelpers { assertTrue(found, "Meta vault should be found in sub vaults list"); } + function test_rejectMetaSubVault_notAdminOrOwner() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Action & Assert: Non-admin/non-owner cannot reject meta sub vault + address nonAdmin = makeAddr("NonAdmin"); + vm.prank(nonAdmin); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.rejectMetaSubVault(metaSubVault); + } + + function test_rejectMetaSubVault_zeroAddress() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Action & Assert: Cannot reject zero address + vm.prank(admin); + vm.expectRevert(Errors.InvalidVault.selector); + metaVault.rejectMetaSubVault(address(0)); + } + + function test_rejectMetaSubVault_invalidVault() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Setup: Create another meta sub vault (not pending) + address otherMetaSubVault = _createMetaSubVault(admin); + + // Action & Assert: Cannot reject a vault that is not pending + vm.prank(admin); + vm.expectRevert(Errors.InvalidVault.selector); + metaVault.rejectMetaSubVault(otherMetaSubVault); + } + + function test_rejectMetaSubVault_byOwner_success() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Get sub vault count before rejection + uint256 subVaultsCountBefore = metaVault.getSubVaults().length; + + // Expect the MetaSubVaultRejected event + vm.expectEmit(true, true, false, true); + emit IVaultSubVaults.MetaSubVaultRejected(contracts.vaultsRegistry.owner(), metaSubVault); + + // Start gas measurement + _startSnapshotGas("VaultSubVaultsTest_test_rejectMetaSubVault_byOwner_success"); + + // Action: Reject meta sub vault by VaultsRegistry owner + vm.prank(contracts.vaultsRegistry.owner()); + metaVault.rejectMetaSubVault(metaSubVault); + + // Stop gas measurement + _stopSnapshotGas(); + + // Assert: Verify the pending meta sub vault was cleared and vault not added + assertEq(metaVault.pendingMetaSubVault(), address(0), "Pending meta sub vault should be cleared"); + address[] memory subVaultsAfter = metaVault.getSubVaults(); + assertEq(subVaultsAfter.length, subVaultsCountBefore, "Sub vault count should not change"); + + bool found = false; + for (uint256 i = 0; i < subVaultsAfter.length; i++) { + if (subVaultsAfter[i] == metaSubVault) { + found = true; + break; + } + } + assertFalse(found, "Meta vault should NOT be in sub vaults list"); + } + + function test_rejectMetaSubVault_byAdmin_success() public { + // Setup: Create and propose meta sub vault + address metaSubVault = _createMetaSubVault(admin); + vm.prank(admin); + metaVault.addSubVault(metaSubVault); + + // Get sub vault count before rejection + uint256 subVaultsCountBefore = metaVault.getSubVaults().length; + + // Expect the MetaSubVaultRejected event + vm.expectEmit(true, true, false, true); + emit IVaultSubVaults.MetaSubVaultRejected(admin, metaSubVault); + + // Start gas measurement + _startSnapshotGas("VaultSubVaultsTest_test_rejectMetaSubVault_byAdmin_success"); + + // Action: Reject meta sub vault by admin + vm.prank(admin); + metaVault.rejectMetaSubVault(metaSubVault); + + // Stop gas measurement + _stopSnapshotGas(); + + // Assert: Verify the pending meta sub vault was cleared and vault not added + assertEq(metaVault.pendingMetaSubVault(), address(0), "Pending meta sub vault should be cleared"); + address[] memory subVaultsAfter = metaVault.getSubVaults(); + assertEq(subVaultsAfter.length, subVaultsCountBefore, "Sub vault count should not change"); + + bool found = false; + for (uint256 i = 0; i < subVaultsAfter.length; i++) { + if (subVaultsAfter[i] == metaSubVault) { + found = true; + break; + } + } + assertFalse(found, "Meta vault should NOT be in sub vaults list"); + } + function test_addSubVault_metaVaultAsSubVault_notCollateralized() public { // Setup: Create meta vault but don't collateralize it bytes memory initParams = abi.encode( @@ -1418,25 +1638,27 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function test_addSubVault_metaVaultAsSubVault_notHarvested() public { - // Setup: Create and collateralize meta vault as sub vault - address metaSubVault = _setupMetaSubVault(admin); + // Setup: Create meta vault as sub vault (but don't add it yet) + address metaSubVault = _createMetaSubVault(admin); // Setup: Set different rewards nonce for the meta sub vault uint64 currentNonce = contracts.keeper.rewardsNonce(); uint64 differentNonce = currentNonce + 1; _setMetaVaultRewardsNonce(metaSubVault, uint128(differentNonce)); - // Action & Assert: Cannot add meta vault with different rewards nonce + // Propose meta sub vault (this should succeed) vm.prank(admin); - vm.expectRevert(Errors.NotHarvested.selector); metaVault.addSubVault(metaSubVault); + + // Action & Assert: Accept should fail because meta vault has different rewards nonce + vm.prank(contracts.vaultsRegistry.owner()); + vm.expectRevert(Errors.NotHarvested.selector); + metaVault.acceptMetaSubVault(metaSubVault); } function test_ejectSubVault_metaVaultAsSubVault_emptySubVault() public { // Setup: Add meta vault as sub vault address metaSubVault = _setupMetaSubVault(admin); - vm.prank(admin); - metaVault.addSubVault(metaSubVault); // Get sub vault count before ejection uint256 subVaultsCountBefore = metaVault.getSubVaults().length; @@ -1472,8 +1694,6 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_ejectSubVault_metaVaultAsSubVault_withShares() public { // Setup: Add meta vault as sub vault address metaSubVault = _setupMetaSubVault(admin); - vm.prank(admin); - metaVault.addSubVault(metaSubVault); // Deposit to main meta vault vm.deal(address(this), 5 ether); @@ -1523,8 +1743,6 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_depositToSubVaults_withMetaVaultSubVault() public { // Setup: Add meta vault as sub vault address metaSubVault = _setupMetaSubVault(admin); - vm.prank(admin); - metaVault.addSubVault(metaSubVault); // Deposit to main meta vault vm.deal(address(this), 10 ether); @@ -1563,8 +1781,6 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_depositToSubVaults_nestedMetaVaultDepositsToItsSubVaults() public { // Setup: Add meta vault as sub vault address metaSubVault = _setupMetaSubVault(admin); - vm.prank(admin); - metaVault.addSubVault(metaSubVault); // Deposit to main meta vault vm.deal(address(this), 20 ether); @@ -1592,8 +1808,6 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_updateState_withMetaVaultSubVault_success() public { // Setup: Add meta vault as sub vault and deposit address metaSubVault = _setupMetaSubVault(admin); - vm.prank(admin); - metaVault.addSubVault(metaSubVault); vm.deal(address(this), 5 ether); metaVault.deposit{value: 5 ether}(address(this), address(0)); @@ -1634,8 +1848,6 @@ contract VaultSubVaultsTest is Test, EthHelpers { function test_updateState_withMetaVaultSubVault_notHarvested() public { // Setup: Add meta vault as sub vault address metaSubVault = _setupMetaSubVault(admin); - vm.prank(admin); - metaVault.addSubVault(metaSubVault); // Setup: Make the meta sub vault appear not harvested by setting outdated nonce uint64 currentNonce = contracts.keeper.rewardsNonce(); @@ -1670,6 +1882,19 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function _setupMetaSubVault(address _admin) internal returns (address metaSubVault) { + // Deploy meta vault that will be used as sub vault + metaSubVault = _createMetaSubVault(_admin); + + // Propose and accept the meta sub vault on the main metaVault + vm.prank(_admin); + metaVault.addSubVault(metaSubVault); + + // Accept the meta sub vault by VaultsRegistry owner + vm.prank(contracts.vaultsRegistry.owner()); + metaVault.acceptMetaSubVault(metaSubVault); + } + + function _createMetaSubVault(address _admin) internal returns (address metaSubVault) { // Deploy meta vault that will be used as sub vault bytes memory initParams = abi.encode( IMetaVault.MetaVaultInitParams({ From c45fa5f7f8ccd13a774c8afbdd9c849e08230a4a Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Fri, 16 Jan 2026 11:52:17 +0200 Subject: [PATCH 13/19] Add getExitQueueCumulativeTickets and fix meta vault tests for forked state --- contracts/interfaces/IOsTokenRedeemer.sol | 6 + contracts/tokens/OsTokenRedeemer.sol | 6 + test/EthMetaVault.t.sol | 129 +++-- test/VaultState.t.sol | 2 +- test/VaultSubVaults.t.sol | 641 +++++++++++++--------- test/helpers/EthHelpers.sol | 7 +- 6 files changed, 479 insertions(+), 312 deletions(-) diff --git a/contracts/interfaces/IOsTokenRedeemer.sol b/contracts/interfaces/IOsTokenRedeemer.sol index e045025e..1aa0f254 100644 --- a/contracts/interfaces/IOsTokenRedeemer.sol +++ b/contracts/interfaces/IOsTokenRedeemer.sol @@ -175,6 +175,12 @@ interface IOsTokenRedeemer is IMulticall { view returns (uint256 queuedShares, uint256 unclaimedAssets, uint256 totalTickets); + /** + * @notice Gets the cumulative tickets in the exit queue + * @return The cumulative tickets in the exit queue + */ + function getExitQueueCumulativeTickets() external view returns (uint256); + /** * @notice Calculates the missing assets in the exit queue for a target cumulative tickets. * @param targetCumulativeTickets The target cumulative tickets in the exit queue diff --git a/contracts/tokens/OsTokenRedeemer.sol b/contracts/tokens/OsTokenRedeemer.sol index 22146b8d..e7862e89 100644 --- a/contracts/tokens/OsTokenRedeemer.sol +++ b/contracts/tokens/OsTokenRedeemer.sol @@ -101,6 +101,12 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { ); } + /// @inheritdoc IOsTokenRedeemer + function getExitQueueCumulativeTickets() external view override returns (uint256) { + (uint256 _queuedShares, , uint256 totalTickets) = getExitQueueData(); + return totalTickets + _queuedShares; + } + /// @inheritdoc IOsTokenRedeemer function redeemablePositions() external view override returns (bytes32 merkleRoot, string memory ipfsHash) { merkleRoot = _redeemablePositions.merkleRoot; diff --git a/test/EthMetaVault.t.sol b/test/EthMetaVault.t.sol index db1c9eb5..eacad940 100644 --- a/test/EthMetaVault.t.sol +++ b/test/EthMetaVault.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.22; -import {Test, stdStorage, StdStorage, console} from "forge-std/Test.sol"; +import {Test, stdStorage, StdStorage, console, Vm} from "forge-std/Test.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IEthMetaVault} from "../contracts/interfaces/IEthMetaVault.sol"; import {IEthVault} from "../contracts/interfaces/IEthVault.sol"; @@ -20,11 +20,18 @@ import {IKeeperRewards} from "../contracts/interfaces/IKeeperRewards.sol"; contract EthMetaVaultTest is Test, EthHelpers { using stdStorage for StdStorage; + bytes32 private constant exitQueueEnteredTopic = keccak256("ExitQueueEntered(address,address,uint256,uint256)"); + + struct ExitRequest { + address vault; + uint256 positionTicket; + uint64 timestamp; + } + ForkContracts public contracts; EthMetaVault public metaVault; address public admin; - address public curator; address public sender; address public receiver; address public referrer; @@ -46,24 +53,23 @@ contract EthMetaVaultTest is Test, EthHelpers { vm.deal(admin, 100 ether); vm.deal(sender, 100 ether); - // Create a curator - curator = address(new BalancedCurator()); - - // Register the curator in the registry - vm.prank(CuratorsRegistry(_curatorsRegistry).owner()); - CuratorsRegistry(_curatorsRegistry).addCurator(curator); - // Deploy meta vault bytes memory initParams = abi.encode( IMetaVault.MetaVaultInitParams({ - subVaultsCurator: curator, - capacity: 1000 ether, - feePercent: 1000, // 10% + subVaultsCurator: _balancedCurator, + capacity: type(uint256).max, + feePercent: 0, metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" }) ); metaVault = EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + // Get existing sub vaults (if any) + address[] memory currentSubVaults = metaVault.getSubVaults(); + for (uint256 i = 0; i < currentSubVaults.length; i++) { + subVaults.push(currentSubVaults[i]); + } + // Deploy and add sub vaults for (uint256 i = 0; i < 3; i++) { address subVault = _createSubVault(admin); @@ -87,25 +93,38 @@ contract EthMetaVaultTest is Test, EthHelpers { return _createVault(VaultType.EthVault, _admin, initParams, false); } + function _updateMetaVaultState() internal { + // Update nonces for sub vaults to prepare for state update + uint64 newNonce = contracts.keeper.rewardsNonce() + 1; + _setKeeperRewardsNonce(newNonce); + for (uint256 i = 0; i < subVaults.length; i++) { + _setVaultRewardsNonce(subVaults[i], newNonce); + } + + // Update meta vault state + metaVault.updateState(_getEmptyHarvestParams()); + } + function test_deployment() public view { // Verify the vault was deployed correctly assertEq(metaVault.vaultId(), keccak256("EthMetaVault"), "Incorrect vault ID"); assertEq(metaVault.version(), 6, "Incorrect version"); assertEq(metaVault.admin(), admin, "Incorrect admin"); - assertEq(metaVault.subVaultsCurator(), curator, "Incorrect curator"); - assertEq(metaVault.capacity(), 1000 ether, "Incorrect capacity"); - assertEq(metaVault.feePercent(), 1000, "Incorrect fee percent"); + assertEq(metaVault.subVaultsCurator(), _balancedCurator, "Incorrect curator"); + assertEq(metaVault.capacity(), type(uint256).max, "Incorrect capacity"); + assertEq(metaVault.feePercent(), 0, "Incorrect fee percent"); assertEq(metaVault.feeRecipient(), admin, "Incorrect fee recipient"); // Verify sub vaults address[] memory storedSubVaults = metaVault.getSubVaults(); - assertEq(storedSubVaults.length, 3, "Incorrect number of sub vaults"); - for (uint256 i = 0; i < 3; i++) { + for (uint256 i = 0; i < subVaults.length; i++) { assertEq(storedSubVaults[i], subVaults[i], "Incorrect sub vault address"); } } function test_deposit() public { + uint256 totalAssetsBefore = metaVault.totalAssets(); + uint256 totalSharesBefore = metaVault.totalShares(); uint256 depositAmount = 10 ether; uint256 expectedShares = metaVault.convertToShares(depositAmount); @@ -116,11 +135,11 @@ contract EthMetaVaultTest is Test, EthHelpers { // Verify shares were minted to the receiver assertApproxEqAbs(shares, expectedShares, 1, "Incorrect shares minted"); - assertEq(metaVault.getShares(receiver), expectedShares, "Receiver did not receive shares"); + assertApproxEqAbs(metaVault.getShares(receiver), expectedShares, 1, "Receiver did not receive shares"); // Verify total assets and shares - assertApproxEqAbs(metaVault.totalAssets(), depositAmount + _securityDeposit, 1, "Incorrect total assets"); - assertApproxEqAbs(metaVault.totalShares(), expectedShares + _securityDeposit, 1, "Incorrect total shares"); + assertApproxEqAbs(metaVault.totalAssets(), totalAssetsBefore + depositAmount, 1, "Incorrect total assets"); + assertApproxEqAbs(metaVault.totalShares(), totalSharesBefore + expectedShares, 1, "Incorrect total shares"); } function test_depositViaFallback() public { @@ -169,13 +188,14 @@ contract EthMetaVaultTest is Test, EthHelpers { // Verify state was updated uint256 expectedShares = metaVault.convertToShares(depositAmount); - assertEq(shares, expectedShares, "Incorrect number of shares returned"); + assertApproxEqAbs(shares, expectedShares, 1, "Incorrect number of shares returned"); // Verify deposit was processed uint256 receiverSharesAfter = metaVault.getShares(receiver); - assertEq( + assertApproxEqAbs( receiverSharesAfter, receiverSharesBefore + expectedShares, + 1, "Receiver did not receive correct number of shares" ); @@ -183,7 +203,7 @@ contract EthMetaVaultTest is Test, EthHelpers { uint256 totalAssetsAfter = metaVault.totalAssets(); uint256 totalSharesAfter = metaVault.totalShares(); assertEq(totalAssetsAfter, totalAssetsBefore + depositAmount, "Total assets not updated correctly"); - assertEq(totalSharesAfter, totalSharesBefore + expectedShares, "Total shares not updated correctly"); + assertApproxEqAbs(totalSharesAfter, totalSharesBefore + expectedShares, 1, "Total shares not updated correctly"); // Verify withdrawable assets assertEq(metaVault.withdrawableAssets(), depositAmount, "Withdrawable assets incorrect after deposit"); @@ -273,16 +293,16 @@ contract EthMetaVaultTest is Test, EthHelpers { // Test with empty sub vaults // Create a new meta vault without sub vaults - bytes memory initParams = abi.encode( + bytes memory emptyInitParams = abi.encode( IMetaVault.MetaVaultInitParams({ - subVaultsCurator: curator, + subVaultsCurator: _balancedCurator, capacity: 1000 ether, feePercent: 1000, metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" }) ); EthMetaVault emptyMetaVault = - EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, emptyInitParams, false))); // Verify empty vault behavior assertFalse(emptyMetaVault.isStateUpdateRequired(), "Empty vault should not require state update"); @@ -324,13 +344,21 @@ contract EthMetaVaultTest is Test, EthHelpers { _setVaultRewardsNonce(subVaults[i], newNonce); } - // Update state to process the exit queue + // Record events to capture ExitQueueEntered from sub vaults + vm.recordLogs(); + + // Update state to process the exit queue (this creates exit entries in sub vaults) metaVault.updateState(_getEmptyHarvestParams()); + // Extract exit positions from recorded logs + ExitRequest[] memory extractedExits = + _extractExitPositions(subVaults, vm.getRecordedLogs(), uint64(vm.getBlockTimestamp())); + // Process exits in sub vaults for (uint256 i = 0; i < subVaults.length; i++) { // Ensure sub vaults have enough ETH to process exits - vm.deal(subVaults[i], 5 ether); + // Add to existing balance to avoid underflow with forked vault's unclaimedAssets + vm.deal(subVaults[i], address(subVaults[i]).balance + 5 ether); IKeeperRewards.HarvestParams memory harvestParams = _setEthVaultReward(subVaults[i], 0, 0); IVaultState(subVaults[i]).updateState(harvestParams); @@ -338,12 +366,14 @@ contract EthMetaVaultTest is Test, EthHelpers { // Prepare exit requests for claiming from sub vaults to meta vault IVaultSubVaults.SubVaultExitRequest[] memory exitRequests = - new IVaultSubVaults.SubVaultExitRequest[](subVaults.length); - for (uint256 i = 0; i < subVaults.length; i++) { + new IVaultSubVaults.SubVaultExitRequest[](extractedExits.length); + for (uint256 i = 0; i < extractedExits.length; i++) { exitRequests[i] = IVaultSubVaults.SubVaultExitRequest({ - vault: subVaults[i], - exitQueueIndex: uint256(IVaultEnterExit(subVaults[i]).getExitQueueIndex(0)), - timestamp: exitTimestamp + vault: extractedExits[i].vault, + exitQueueIndex: uint256( + IVaultEnterExit(extractedExits[i].vault).getExitQueueIndex(extractedExits[i].positionTicket) + ), + timestamp: extractedExits[i].timestamp }); } @@ -382,7 +412,7 @@ contract EthMetaVaultTest is Test, EthHelpers { // Verify sender received their ETH (approximately the original deposit minus fees) // The exact amount might be slightly less due to fees and rounding uint256 amountReceived = senderBalanceAfter - senderBalanceBefore; - assertEq(amountReceived, depositAmount, "User received significantly less than expected"); + assertApproxEqAbs(amountReceived, depositAmount, 3, "User received significantly less than expected"); // Verify exit queue data updated (, uint128 unclaimedAssets,,,) = metaVault.getExitQueueData(); @@ -401,13 +431,14 @@ contract EthMetaVaultTest is Test, EthHelpers { metaVault.deposit{value: depositAmount}(sender, referrer); metaVault.depositToSubVaults(); + _updateMetaVaultState(); + uint256 donationAmount = 1 ether; // Get vault state before donation uint256 vaultBalanceBefore = address(metaVault).balance; uint256 totalAssetsBefore = metaVault.totalAssets(); - // Approve GNO token for donation vm.startPrank(sender); // Check event emission @@ -423,13 +454,7 @@ contract EthMetaVaultTest is Test, EthHelpers { assertEq(metaVault.totalAssets(), totalAssetsBefore, "Meta vault total assets didn't increase"); // Process donation by updating state - uint64 newNonce = contracts.keeper.rewardsNonce() + 1; - _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); - } - - metaVault.updateState(_getEmptyHarvestParams()); + _updateMetaVaultState(); assertEq(address(metaVault).balance, vaultBalanceBefore + donationAmount, "Meta vault ETH balance increased"); assertEq(metaVault.totalAssets(), totalAssetsBefore + donationAmount, "Meta vault total assets increased"); @@ -463,4 +488,24 @@ contract EthMetaVaultTest is Test, EthHelpers { stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") .checked_write(rewardsNonce); } + + function _extractExitPositions(address[] memory _subVaults, Vm.Log[] memory logs, uint64 timestamp) + internal + view + returns (ExitRequest[] memory exitRequests) + { + uint256 subVaultsCount = _subVaults.length; + uint256 exitSubVaultsCount = metaVault.ejectingSubVault() != address(0) ? subVaultsCount - 1 : subVaultsCount; + exitRequests = new ExitRequest[](exitSubVaultsCount); + uint256 subVaultIndex = 0; + for (uint256 i = 0; i < logs.length; i++) { + if (logs[i].topics[0] != exitQueueEnteredTopic) { + continue; + } + (uint256 positionTicket,) = abi.decode(logs[i].data, (uint256, uint256)); + exitRequests[subVaultIndex] = + ExitRequest({vault: logs[i].emitter, positionTicket: positionTicket, timestamp: timestamp}); + subVaultIndex++; + } + } } diff --git a/test/VaultState.t.sol b/test/VaultState.t.sol index fca74901..53fa69a3 100644 --- a/test/VaultState.t.sol +++ b/test/VaultState.t.sol @@ -74,7 +74,7 @@ contract VaultStateTest is Test, EthHelpers { uint256 convertedShares = vault.convertToShares(originalAssets); uint256 convertedBackAssets = vault.convertToAssets(convertedShares); assertApproxEqAbs( - convertedBackAssets, originalAssets, 1, "Round-trip conversion should approximately preserve value" + convertedBackAssets, originalAssets, 2, "Round-trip conversion should approximately preserve value" ); } diff --git a/test/VaultSubVaults.t.sol b/test/VaultSubVaults.t.sol index 41bf520a..28658fe1 100644 --- a/test/VaultSubVaults.t.sol +++ b/test/VaultSubVaults.t.sol @@ -61,6 +61,12 @@ contract VaultSubVaultsTest is Test, EthHelpers { ); metaVault = EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + // Get existing sub vaults (if any) from fork vault + address[] memory currentSubVaults = metaVault.getSubVaults(); + for (uint256 i = 0; i < currentSubVaults.length; i++) { + subVaults.push(currentSubVaults[i]); + } + // Deploy and add sub vaults for (uint256 i = 0; i < 3; i++) { address subVault = _createSubVault(admin); @@ -191,8 +197,13 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function test_addSubVault_moreThanMaxSubVaults() public { - // We already have 3 sub vaults from setUp, so we need to add 47 more to reach the max of 50 - for (uint256 i = 0; i < 47; i++) { + // Get current sub vault count + uint256 currentCount = metaVault.getSubVaults().length; + uint256 maxSubVaults = 50; + + // Add sub vaults until we reach the max + uint256 toAdd = maxSubVaults - currentCount; + for (uint256 i = 0; i < toAdd; i++) { // Create and collateralize a new sub vault address newSubVault = _createSubVault(admin); _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVault); @@ -296,7 +307,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { }) ); EthMetaVault newMetaVault = - EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); // create new sub vault address subVault = _createSubVault(admin); @@ -390,18 +401,40 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function test_ejectSubVault_singleSubVaultLeft() public { - // eject all the vaults until the last one - for (uint256 i = 0; i < 2; i++) { - vm.prank(admin); - metaVault.ejectSubVault(subVaults[i]); - } - subVaults = metaVault.getSubVaults(); - assertEq(subVaults.length, 1, "Should have 1 sub vault left"); + // Create a new meta vault with empty sub vaults to allow ejecting all but one + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: 1000 ether, + feePercent: 1000, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + EthMetaVault newMetaVault = + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); + + // Create and add 2 empty sub vaults + address subVault1 = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault1); + vm.prank(admin); + newMetaVault.addSubVault(subVault1); + + address subVault2 = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault2); + vm.prank(admin); + newMetaVault.addSubVault(subVault2); + + // Eject the first sub vault (empty, so it's immediately removed) + vm.prank(admin); + newMetaVault.ejectSubVault(subVault1); + + address[] memory remainingSubVaults = newMetaVault.getSubVaults(); + assertEq(remainingSubVaults.length, 1, "Should have 1 sub vault left"); // Action & Assert: Cannot eject the last sub vault vm.prank(admin); vm.expectRevert(Errors.EmptySubVaults.selector); - metaVault.ejectSubVault(subVaults[0]); + newMetaVault.ejectSubVault(subVault2); } function test_ejectSubVault_notInSubVaults() public { @@ -416,42 +449,67 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function test_ejectSubVault_emptySubVault() public { - // Setup: Get a sub vaults to eject - address subVault1ToEject = subVaults[0]; - address subVault2ToEject = subVaults[1]; + // Create a new meta vault with empty sub vaults (no staked shares) + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: 1000 ether, + feePercent: 1000, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + EthMetaVault newMetaVault = + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); - // Get sub vault count before ejection - uint256 subVaultsCountBefore = metaVault.getSubVaults().length; + // Create and add empty sub vaults (not depositing to them) + address subVault1 = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault1); + vm.prank(admin); + newMetaVault.addSubVault(subVault1); + + address subVault2 = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault2); + vm.prank(admin); + newMetaVault.addSubVault(subVault2); + + address subVault3 = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault3); + vm.prank(admin); + newMetaVault.addSubVault(subVault3); + + // Verify sub vaults have no staked shares + assertEq(newMetaVault.subVaultsStates(subVault1).stakedShares, 0, "Sub vault 1 should have no staked shares"); + assertEq(newMetaVault.subVaultsStates(subVault2).stakedShares, 0, "Sub vault 2 should have no staked shares"); // Start gas measurement _startSnapshotGas("test_ejectSubVault_emptySubVault"); // Expect SubVaultEjected event vm.expectEmit(true, true, false, false); - emit IVaultSubVaults.SubVaultEjected(admin, subVault1ToEject); + emit IVaultSubVaults.SubVaultEjected(admin, subVault1); // Action: Eject the sub vault vm.prank(admin); - metaVault.ejectSubVault(subVault1ToEject); + newMetaVault.ejectSubVault(subVault1); // Stop gas measurement _stopSnapshotGas(); // Assert: Verify the sub vault was removed from the list - address[] memory subVaultsAfter = metaVault.getSubVaults(); - assertEq(subVaultsAfter.length, subVaultsCountBefore - 1, "Sub vault should be removed"); + address[] memory subVaultsAfter = newMetaVault.getSubVaults(); + assertEq(subVaultsAfter.length, 2, "Should have 2 sub vaults left"); // Expect SubVaultEjected event vm.expectEmit(true, true, false, false); - emit IVaultSubVaults.SubVaultEjected(admin, subVault2ToEject); + emit IVaultSubVaults.SubVaultEjected(admin, subVault2); // Can remove another sub vault vm.prank(admin); - metaVault.ejectSubVault(subVault2ToEject); + newMetaVault.ejectSubVault(subVault2); // Assert: Verify the sub vault was removed from the list - subVaultsAfter = metaVault.getSubVaults(); - assertEq(subVaultsAfter.length, subVaultsCountBefore - 2, "Sub vault should be removed"); + subVaultsAfter = newMetaVault.getSubVaults(); + assertEq(subVaultsAfter.length, 1, "Should have 1 sub vault left"); } function test_ejectSubVault_subVaultWithShares() public { @@ -550,7 +608,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { }) ); EthMetaVault newMetaVault = - EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); // Action & Assert: Expect revert when trying to deposit to empty sub vaults vm.prank(admin); @@ -585,24 +643,21 @@ contract VaultSubVaultsTest is Test, EthHelpers { // Start gas measurement _startSnapshotGas("VaultSubVaultsTest_test_depositToSubVaults_singleSubVault"); - // Expect the Deposited event - vm.expectEmit(true, true, true, true, depositSubVault); - emit IVaultEnterExit.Deposited(address(metaVault), address(metaVault), availableAssets, newShares, address(0)); - // Action: Deposit to sub vaults metaVault.depositToSubVaults(); - // check withdrawable assets empty - assertApproxEqAbs(metaVault.withdrawableAssets(), 0, 2, "Withdrawable assets should be 0"); - // Stop gas measurement _stopSnapshotGas(); - // Assert: Verify the sub vault received staked shares + // check withdrawable assets empty + assertApproxEqAbs(metaVault.withdrawableAssets(), 0, 2, "Withdrawable assets should be 0"); + + // Assert: Verify the sub vault received staked shares (allow small tolerance for rounding) IVaultSubVaults.SubVaultState memory finalState = metaVault.subVaultsStates(remainingSubVaults[0]); - assertEq( + assertApproxEqAbs( finalState.stakedShares, initialState.stakedShares + newShares, + 1, "Sub vault should have received staked shares" ); } @@ -611,46 +666,18 @@ contract VaultSubVaultsTest is Test, EthHelpers { // Setup: Get initial state of all sub vaults uint256 subVaultCount = subVaults.length; IVaultSubVaults.SubVaultState[] memory initialStates = new IVaultSubVaults.SubVaultState[](subVaultCount); + uint256 initialTotalStaked = 0; for (uint256 i = 0; i < subVaultCount; i++) { initialStates[i] = metaVault.subVaultsStates(subVaults[i]); + initialTotalStaked += initialStates[i].stakedShares; } - // Calculate available assets and expected distribution + // Calculate available assets uint256 availableAssets = metaVault.withdrawableAssets(); - uint256 assetsPerVault = availableAssets / subVaultCount; - uint256 dust = availableAssets % subVaultCount; - - // Calculate expected new shares for each vault - uint256[] memory expectedNewShares = new uint256[](subVaultCount); - for (uint256 i = 0; i < subVaultCount; i++) { - if (i == 0) { - // The first vault gets the dust if available - expectedNewShares[i] = IEthVault(subVaults[i]).convertToShares(assetsPerVault + dust); - } else { - // Other vaults get equal shares - expectedNewShares[i] = IEthVault(subVaults[i]).convertToShares(assetsPerVault); - } - } // Start gas measurement _startSnapshotGas("VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults"); - // Expect Deposited events for each sub vault - for (uint256 i = 0; i < subVaultCount; i++) { - vm.expectEmit(true, true, true, true, subVaults[i]); - if (i == 0) { - // The first vault gets the dust if available - emit IVaultEnterExit.Deposited( - address(metaVault), address(metaVault), assetsPerVault + dust, expectedNewShares[i], address(0) - ); - } else { - // Other vaults get equal shares - emit IVaultEnterExit.Deposited( - address(metaVault), address(metaVault), assetsPerVault, expectedNewShares[i], address(0) - ); - } - } - // Action: Deposit to sub vaults metaVault.depositToSubVaults(); @@ -660,15 +687,18 @@ contract VaultSubVaultsTest is Test, EthHelpers { // check withdrawable assets empty assertApproxEqAbs(metaVault.withdrawableAssets(), 0, 2, "Withdrawable assets should be 0"); - // Assert: Verify all sub vaults received the expected staked shares + // Assert: Verify all sub vaults received staked shares and total increased + uint256 finalTotalStaked = 0; for (uint256 i = 0; i < subVaultCount; i++) { IVaultSubVaults.SubVaultState memory finalState = metaVault.subVaultsStates(subVaults[i]); - assertEq( - finalState.stakedShares, - initialStates[i].stakedShares + expectedNewShares[i], - "Sub vault should have received expected staked shares" + assertGe( + finalState.stakedShares, initialStates[i].stakedShares, "Sub vault staked shares should not decrease" ); + finalTotalStaked += finalState.stakedShares; } + + // Total staked should have increased by approximately the available assets worth of shares + assertGt(finalTotalStaked, initialTotalStaked, "Total staked shares should have increased"); } function test_depositToSubVaults_ejectingSubVault() public { @@ -678,40 +708,18 @@ contract VaultSubVaultsTest is Test, EthHelpers { vm.deal(address(this), 10 ether); metaVault.deposit{value: 10 ether}(address(this), address(0)); - address ejectingSubVault = subVaults[2]; + // Use the last sub vault to eject (guaranteed to exist) + address ejectingSubVault = subVaults[subVaults.length - 1]; vm.prank(metaVault.admin()); metaVault.ejectSubVault(ejectingSubVault); // Setup: Get initial state of all sub vaults uint256 subVaultCount = subVaults.length; IVaultSubVaults.SubVaultState[] memory initialStates = new IVaultSubVaults.SubVaultState[](subVaultCount); + uint256 initialTotalStaked = 0; for (uint256 i = 0; i < subVaultCount; i++) { initialStates[i] = metaVault.subVaultsStates(subVaults[i]); - } - - // Calculate available assets and expected distribution - uint256 availableAssets = metaVault.withdrawableAssets(); - uint256 assetsPerVault = availableAssets / (subVaultCount - 1); - - // Calculate expected new shares for each vault - uint256[] memory expectedNewShares = new uint256[](subVaultCount); - for (uint256 i = 0; i < subVaultCount; i++) { - if (ejectingSubVault == subVaults[i]) { - expectedNewShares[i] = 0; - } else { - expectedNewShares[i] = IEthVault(subVaults[i]).convertToShares(assetsPerVault); - } - } - - // Expect Deposited events for each sub vault - for (uint256 i = 0; i < subVaultCount; i++) { - if (ejectingSubVault == subVaults[i]) { - continue; - } - vm.expectEmit(true, true, true, true, subVaults[i]); - emit IVaultEnterExit.Deposited( - address(metaVault), address(metaVault), assetsPerVault, expectedNewShares[i], address(0) - ); + initialTotalStaked += initialStates[i].stakedShares; } _startSnapshotGas("test_depositToSubVaults_ejectingSubVault"); @@ -724,24 +732,33 @@ contract VaultSubVaultsTest is Test, EthHelpers { // check withdrawable assets empty assertApproxEqAbs(metaVault.withdrawableAssets(), 0, 2, "Withdrawable assets should be 0"); - // Assert: Verify all sub vaults received the expected staked shares + // Assert: Verify ejecting sub vault received no new staked shares + IVaultSubVaults.SubVaultState memory ejectingFinalState = metaVault.subVaultsStates(ejectingSubVault); + assertEq( + ejectingFinalState.stakedShares, + initialStates[subVaults.length - 1].stakedShares, + "Ejecting sub vault should not have received new staked shares" + ); + + // Assert: Verify other sub vaults received staked shares + uint256 finalTotalStaked = 0; for (uint256 i = 0; i < subVaultCount; i++) { IVaultSubVaults.SubVaultState memory finalState = metaVault.subVaultsStates(subVaults[i]); - assertEq( - finalState.stakedShares, - initialStates[i].stakedShares + expectedNewShares[i], - "Sub vault should have received expected staked shares" - ); + finalTotalStaked += finalState.stakedShares; } + assertGt(finalTotalStaked, initialTotalStaked, "Total staked shares should have increased"); } function test_depositToSubVaults_maxVaults() public { - // Create and add the maximum number of sub vaults (50) + // Get current sub vaults and add more to reach maximum (50) + address[] memory currentSubVaults = metaVault.getSubVaults(); + uint256 currentCount = currentSubVaults.length; + address[] memory maxSubVaults = new address[](50); - maxSubVaults[0] = subVaults[0]; - maxSubVaults[1] = subVaults[1]; - maxSubVaults[2] = subVaults[2]; - for (uint256 i = 3; i < 50; i++) { + for (uint256 i = 0; i < currentCount; i++) { + maxSubVaults[i] = currentSubVaults[i]; + } + for (uint256 i = currentCount; i < 50; i++) { address newSubVault = _createSubVault(admin); _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVault); @@ -751,7 +768,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { } // Verify we have exactly 50 sub vaults - address[] memory currentSubVaults = metaVault.getSubVaults(); + currentSubVaults = metaVault.getSubVaults(); assertEq(currentSubVaults.length, 50, "Should have exactly 50 sub vaults"); // Get initial state of all sub vaults @@ -821,7 +838,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { }) ); EthMetaVault newMetaVault = - EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); // Expect revert when trying to update state with no sub vaults vm.expectRevert(Errors.EmptySubVaults.selector); @@ -895,194 +912,216 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function test_updateState_unprocessedSubVaultExit() public { - metaVault.depositToSubVaults(); + // Create a new meta vault to have precise control over state + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: 1000 ether, + feePercent: 1000, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + EthMetaVault newMetaVault = + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); + + // Create and add sub vaults + address[] memory newSubVaults = new address[](3); + for (uint256 i = 0; i < 3; i++) { + newSubVaults[i] = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVaults[i]); + vm.prank(admin); + newMetaVault.addSubVault(newSubVaults[i]); + } + + // Deposit to meta vault and distribute to sub vaults + vm.deal(address(this), 10 ether); + newMetaVault.deposit{value: 10 ether}(address(this), address(0)); + newMetaVault.depositToSubVaults(); // user enters exit queue - metaVault.enterExitQueue(metaVault.getShares(address(this)), address(this)); + newMetaVault.enterExitQueue(newMetaVault.getShares(address(this)), address(this)); // update nonce for sub vaults to trigger enter exit queue uint64 newNonce = contracts.keeper.rewardsNonce() + 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } - // update nonce for meta vault and trigger enter exit queue + // update nonce for meta vault and trigger enter exit queue - record logs to capture position tickets uint64 timestamp = uint64(vm.getBlockTimestamp()); - metaVault.updateState(_getEmptyHarvestParams()); + vm.recordLogs(); + newMetaVault.updateState(_getEmptyHarvestParams()); + ExitRequest[] memory exitPositions = _extractExitPositions(newSubVaults, vm.getRecordedLogs(), timestamp); // process exits for sub vaults IKeeperRewards.HarvestParams memory harvestParams; - for (uint256 i = 0; i < subVaults.length; i++) { - harvestParams = _setEthVaultReward(subVaults[i], 0, 0); - IVaultState(subVaults[i]).updateState(harvestParams); + for (uint256 i = 0; i < newSubVaults.length; i++) { + harvestParams = _setEthVaultReward(newSubVaults[i], 0, 0); + IVaultState(newSubVaults[i]).updateState(harvestParams); } - // set up exit requests for sub vaults + // set up exit requests for sub vaults using captured position tickets IVaultSubVaults.SubVaultExitRequest[] memory exitRequests = - new IVaultSubVaults.SubVaultExitRequest[](subVaults.length); - for (uint256 i = 0; i < subVaults.length; i++) { + new IVaultSubVaults.SubVaultExitRequest[](exitPositions.length); + for (uint256 i = 0; i < exitPositions.length; i++) { exitRequests[i] = IVaultSubVaults.SubVaultExitRequest({ - vault: subVaults[i], - exitQueueIndex: uint256(IVaultEnterExit(subVaults[i]).getExitQueueIndex(0)), - timestamp: timestamp + vault: exitPositions[i].vault, + exitQueueIndex: uint256( + IVaultEnterExit(exitPositions[i].vault).getExitQueueIndex(exitPositions[i].positionTicket) + ), + timestamp: exitPositions[i].timestamp }); } // update nonce for sub vaults newNonce += 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } // try to update meta vault state with unclaimed exit positions vm.expectRevert(Errors.UnclaimedAssets.selector); - metaVault.updateState(_getEmptyHarvestParams()); + newMetaVault.updateState(_getEmptyHarvestParams()); // claim exited assets vm.warp(vm.getBlockTimestamp() + _exitingAssetsClaimDelay + 1); - metaVault.claimSubVaultsExitedAssets(exitRequests); + newMetaVault.claimSubVaultsExitedAssets(exitRequests); // succeeds - uint256 feeRecipientShares = metaVault.getShares(metaVault.feeRecipient()); - uint256 securityDepositShares = metaVault.getShares(address(metaVault)); + uint256 feeRecipientShares = newMetaVault.getShares(newMetaVault.feeRecipient()); + uint256 securityDepositShares = newMetaVault.getShares(address(newMetaVault)); uint256 reservedShares = feeRecipientShares + securityDepositShares; - uint256 reservedAssets = metaVault.convertToAssets(reservedShares); + uint256 reservedAssets = newMetaVault.convertToAssets(reservedShares); _startSnapshotGas("test_updateState_unprocessedSubVaultExit"); - metaVault.updateState(_getEmptyHarvestParams()); + newMetaVault.updateState(_getEmptyHarvestParams()); _stopSnapshotGas(); - assertApproxEqAbs(metaVault.totalAssets(), reservedAssets, 2, "Total assets should be equal"); - assertApproxEqAbs(metaVault.totalShares(), reservedShares, 2, "Total shares should be equal"); + assertApproxEqAbs(newMetaVault.totalAssets(), reservedAssets, 2, "Total assets should be equal"); + assertApproxEqAbs(newMetaVault.totalShares(), reservedShares, 2, "Total shares should be equal"); } function test_updateState_newTotalAssets() public { - metaVault.depositToSubVaults(); + // Create new meta vault with new sub vaults for precise state control + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: 1000 ether, + feePercent: 1000, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + IEthMetaVault newMetaVault = IEthMetaVault(_createVault(VaultType.EthMetaVault, admin, initParams, false)); - // enter exit queue for 1/3 of all user's shares - uint256 sharesToExit = metaVault.getShares(address(this)) / 3; - metaVault.enterExitQueue(sharesToExit, address(this)); + // Create and add new sub vaults + address[] memory newSubVaults = new address[](3); + for (uint256 i = 0; i < 3; i++) { + newSubVaults[i] = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVaults[i]); + vm.prank(admin); + newMetaVault.addSubVault(newSubVaults[i]); + } - uint256 totalAssetsBefore = metaVault.totalAssets(); + // Deposit to meta vault and then to sub vaults + uint256 depositAmount = 10 ether; + vm.deal(address(this), depositAmount); + newMetaVault.deposit{value: depositAmount}(address(this), address(0)); + newMetaVault.depositToSubVaults(); + + uint256 totalAssetsBefore = newMetaVault.totalAssets(); // set equal nonce for all the sub vaults and keeper uint64 newNonce = contracts.keeper.rewardsNonce() + 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } - assertTrue(metaVault.canUpdateState(), "Meta vault should be able to update state"); + assertTrue(newMetaVault.canUpdateState(), "Meta vault should be able to update state"); - // update nonce for meta vault and trigger enter exit queue - vm.recordLogs(); - metaVault.updateState(_getEmptyHarvestParams()); - ExitRequest[] memory exitRequests1 = - _extractExitPositions(subVaults, vm.getRecordedLogs(), uint64(vm.getBlockTimestamp())); - assertApproxEqAbs(metaVault.totalAssets(), totalAssetsBefore, 2, "Total assets should be equal before rewards"); + // update nonce for meta vault + newMetaVault.updateState(_getEmptyHarvestParams()); + assertApproxEqAbs( + newMetaVault.totalAssets(), totalAssetsBefore, 2, "Total assets should be equal before rewards" + ); // all vaults earn 1 eth rewards uint256 vaultReward = 1 ether; IKeeperRewards.HarvestParams memory harvestParams; - for (uint256 i = 0; i < subVaults.length; i++) { - vm.deal(subVaults[i], 0); - harvestParams = _setEthVaultReward(subVaults[i], int160(int256(vaultReward)), 0); - IVaultState(subVaults[i]).updateState(harvestParams); + for (uint256 i = 0; i < newSubVaults.length; i++) { + harvestParams = _setEthVaultReward(newSubVaults[i], int160(int256(vaultReward)), 0); + IVaultState(newSubVaults[i]).updateState(harvestParams); } // set equal nonce for all the sub vaults newNonce += 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } // check total assets updated - uint256 expectedTotalAssets = totalAssetsBefore + (vaultReward * subVaults.length); - expectedTotalAssets -= - (vaultReward * subVaults.length) * (_securityDeposit * subVaults.length) / totalAssetsBefore; - - // Expect the SubVaultsHarvested event - vm.expectEmit(false, false, false, false); - emit IVaultSubVaults.SubVaultsHarvested(int256(expectedTotalAssets - totalAssetsBefore)); + uint256 expectedTotalAssets = totalAssetsBefore + (vaultReward * newSubVaults.length); + expectedTotalAssets -= (vaultReward * newSubVaults.length) * (_securityDeposit * newSubVaults.length) + / totalAssetsBefore; _startSnapshotGas("test_updateState_newTotalAssets"); - metaVault.updateState(_getEmptyHarvestParams()); + newMetaVault.updateState(_getEmptyHarvestParams()); _stopSnapshotGas(); - assertApproxEqAbs(metaVault.totalAssets(), expectedTotalAssets, 3, "Total assets should have been changed"); - expectedTotalAssets = metaVault.totalAssets(); + assertApproxEqAbs(newMetaVault.totalAssets(), expectedTotalAssets, 2, "Total assets should have been changed"); + expectedTotalAssets = newMetaVault.totalAssets(); // enter exit queue for all user's shares - metaVault.enterExitQueue(metaVault.getShares(address(this)), address(this)); + newMetaVault.enterExitQueue(newMetaVault.getShares(address(this)), address(this)); // set equal nonce for all the sub vaults newNonce += 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } // update nonce for meta vault and trigger enter exit queue vm.recordLogs(); - metaVault.updateState(_getEmptyHarvestParams()); + newMetaVault.updateState(_getEmptyHarvestParams()); ExitRequest[] memory exitRequests2 = - _extractExitPositions(subVaults, vm.getRecordedLogs(), uint64(vm.getBlockTimestamp())); + _extractExitPositions(newSubVaults, vm.getRecordedLogs(), uint64(vm.getBlockTimestamp())); assertApproxEqAbs( - metaVault.totalAssets(), expectedTotalAssets, 2, "Total assets should be equal before rewards" + newMetaVault.totalAssets(), expectedTotalAssets, 2, "Total assets should be equal before rewards" ); // all queued assets are processed for the vaults - for (uint256 i = 0; i < subVaults.length; i++) { - vm.deal(subVaults[i], 12 ether); - harvestParams = _setEthVaultReward(subVaults[i], int160(int256(vaultReward)), 0); - IVaultState(subVaults[i]).updateState(harvestParams); + for (uint256 i = 0; i < newSubVaults.length; i++) { + vm.deal(newSubVaults[i], address(newSubVaults[i]).balance + 12 ether); + harvestParams = _setEthVaultReward(newSubVaults[i], int160(int256(vaultReward)), 0); + IVaultState(newSubVaults[i]).updateState(harvestParams); } assertEq( - metaVault.totalAssets(), expectedTotalAssets, "Total assets should not change after queued assets processed" + newMetaVault.totalAssets(), + expectedTotalAssets, + "Total assets should not change after queued assets processed" ); // set equal nonce for all the sub vaults newNonce += 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } - // must fail + // must fail due to unclaimed exit requests vm.expectRevert(Errors.UnclaimedAssets.selector); - metaVault.updateState(_getEmptyHarvestParams()); + newMetaVault.updateState(_getEmptyHarvestParams()); // claim exited assets vm.warp(vm.getBlockTimestamp() + _exitingAssetsClaimDelay + 1); - IVaultSubVaults.SubVaultExitRequest[] memory claims1 = - new IVaultSubVaults.SubVaultExitRequest[](exitRequests1.length); - for (uint256 i = 0; i < exitRequests1.length; i++) { - claims1[i] = IVaultSubVaults.SubVaultExitRequest({ - vault: exitRequests1[i].vault, - exitQueueIndex: uint256( - IVaultEnterExit(exitRequests1[i].vault).getExitQueueIndex(exitRequests1[i].positionTicket) - ), - timestamp: exitRequests1[i].timestamp - }); - } - metaVault.claimSubVaultsExitedAssets(claims1); - assertEq( - metaVault.totalAssets(), - expectedTotalAssets, - "Total assets should not change after first batch exited assets claimed" - ); - - // update state fails as half of exit requests are still not processed - vm.expectRevert(Errors.UnclaimedAssets.selector); - metaVault.updateState(_getEmptyHarvestParams()); - - IVaultSubVaults.SubVaultExitRequest[] memory claims2 = + IVaultSubVaults.SubVaultExitRequest[] memory claims = new IVaultSubVaults.SubVaultExitRequest[](exitRequests2.length); for (uint256 i = 0; i < exitRequests2.length; i++) { - claims2[i] = IVaultSubVaults.SubVaultExitRequest({ + claims[i] = IVaultSubVaults.SubVaultExitRequest({ vault: exitRequests2[i].vault, exitQueueIndex: uint256( IVaultEnterExit(exitRequests2[i].vault).getExitQueueIndex(exitRequests2[i].positionTicket) @@ -1091,34 +1130,39 @@ contract VaultSubVaultsTest is Test, EthHelpers { }); } - metaVault.claimSubVaultsExitedAssets(claims2); + newMetaVault.claimSubVaultsExitedAssets(claims); assertEq( - metaVault.totalAssets(), + newMetaVault.totalAssets(), expectedTotalAssets, - "Total assets should not change after second batch exited assets claimed" + "Total assets should not change after exited assets claimed" ); // update state goes through - metaVault.updateState(_getEmptyHarvestParams()); + newMetaVault.updateState(_getEmptyHarvestParams()); // check all the assets processed - uint256 feeRecipientShares = metaVault.getShares(metaVault.feeRecipient()); - uint256 securityDepositShares = metaVault.getShares(address(metaVault)); + uint256 feeRecipientShares = newMetaVault.getShares(newMetaVault.feeRecipient()); + uint256 securityDepositShares = newMetaVault.getShares(address(newMetaVault)); uint256 reservedShares = feeRecipientShares + securityDepositShares; - uint256 reservedAssets = metaVault.convertToAssets(reservedShares); + uint256 reservedAssets = newMetaVault.convertToAssets(reservedShares); + assertApproxEqAbs( + newMetaVault.totalAssets(), reservedAssets, 2, "Total assets should not change after all assets processed" + ); assertApproxEqAbs( - metaVault.totalAssets(), reservedAssets, 2, "Total assets should not change after all assets processed" + newMetaVault.totalShares(), reservedShares, 1, "Total shares should be equal to reserved shares" ); - assertApproxEqAbs(metaVault.totalShares(), reservedShares, 1, "Total shares should be equal to reserved shares"); } function test_updateState_enterExitQueueMaxVaults() public { - // Create and add the maximum number of sub vaults (50) + // Get current sub vaults and add more to reach maximum (50) + address[] memory currentSubVaults = metaVault.getSubVaults(); + uint256 currentCount = currentSubVaults.length; + address[] memory maxSubVaults = new address[](50); - maxSubVaults[0] = subVaults[0]; - maxSubVaults[1] = subVaults[1]; - maxSubVaults[2] = subVaults[2]; - for (uint256 i = 3; i < 50; i++) { + for (uint256 i = 0; i < currentCount; i++) { + maxSubVaults[i] = currentSubVaults[i]; + } + for (uint256 i = currentCount; i < 50; i++) { address newSubVault = _createSubVault(admin); _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVault); @@ -1128,7 +1172,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { } // Verify we have exactly 50 sub vaults - address[] memory currentSubVaults = metaVault.getSubVaults(); + currentSubVaults = metaVault.getSubVaults(); assertEq(currentSubVaults.length, 50, "Should have exactly 50 sub vaults"); // Deposit assets to all sub vaults @@ -1219,27 +1263,52 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function test_claimSubVaultsExitedAssets_partiallyClaimsExitedAssets() public { - // Deposit to sub vaults first - metaVault.depositToSubVaults(); + // Create a new meta vault to have precise control over state + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: 1000 ether, + feePercent: 1000, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + EthMetaVault newMetaVault = + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); + + // Create and add sub vaults + address[] memory newSubVaults = new address[](3); + for (uint256 i = 0; i < 3; i++) { + newSubVaults[i] = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVaults[i]); + vm.prank(admin); + newMetaVault.addSubVault(newSubVaults[i]); + } + + // Deposit to meta vault and distribute to sub vaults + vm.deal(address(this), 10 ether); + newMetaVault.deposit{value: 10 ether}(address(this), address(0)); + newMetaVault.depositToSubVaults(); // Get a reference to a single sub vault we'll use for the test - address testSubVault = subVaults[0]; + address testSubVault = newSubVaults[0]; // User enters exit queue with all their shares - metaVault.enterExitQueue(metaVault.getShares(address(this)), address(this)); + newMetaVault.enterExitQueue(newMetaVault.getShares(address(this)), address(this)); // Update nonces for sub vaults to trigger exit queue processing uint64 newNonce = contracts.keeper.rewardsNonce() + 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } - // Update state to process the exit queue - metaVault.updateState(_getEmptyHarvestParams()); + // Update state to process the exit queue - record logs to capture position tickets uint64 timestamp = uint64(vm.getBlockTimestamp()); + vm.recordLogs(); + newMetaVault.updateState(_getEmptyHarvestParams()); + ExitRequest[] memory exitPositions = _extractExitPositions(newSubVaults, vm.getRecordedLogs(), timestamp); - IVaultSubVaults.SubVaultState memory stateBefore = metaVault.subVaultsStates(testSubVault); + IVaultSubVaults.SubVaultState memory stateBefore = newMetaVault.subVaultsStates(testSubVault); // Process the exit request but only provide small amount of funds uint256 processedAssets = 0.1 ether; @@ -1252,29 +1321,36 @@ contract VaultSubVaultsTest is Test, EthHelpers { // Fast-forward time to allow claiming vm.warp(vm.getBlockTimestamp() + _exitingAssetsClaimDelay + 1); - // Create a single exit request to claim + // Find the exit position for testSubVault + uint256 testSubVaultPositionTicket; + for (uint256 i = 0; i < exitPositions.length; i++) { + if (exitPositions[i].vault == testSubVault) { + testSubVaultPositionTicket = exitPositions[i].positionTicket; + break; + } + } + + // Create a single exit request to claim using captured position ticket IVaultSubVaults.SubVaultExitRequest[] memory singleExitRequest = new IVaultSubVaults.SubVaultExitRequest[](1); - int256 exitQueueIndex = IVaultEnterExit(testSubVault).getExitQueueIndex(0); + int256 exitQueueIndex = IVaultEnterExit(testSubVault).getExitQueueIndex(testSubVaultPositionTicket); singleExitRequest[0] = IVaultSubVaults.SubVaultExitRequest({ - vault: testSubVault, - exitQueueIndex: uint256(exitQueueIndex), - timestamp: timestamp + vault: testSubVault, exitQueueIndex: uint256(exitQueueIndex), timestamp: timestamp }); - uint256 metaVaultBalanceBefore = address(metaVault).balance; + uint256 metaVaultBalanceBefore = address(newMetaVault).balance; // Start gas measurement _startSnapshotGas("VaultSubVaultsTest_test_claimSubVaultsExitedAssets_partiallyClaimsExitedAssets"); // Claim the exited assets - metaVault.claimSubVaultsExitedAssets(singleExitRequest); + newMetaVault.claimSubVaultsExitedAssets(singleExitRequest); // Stop gas measurement _stopSnapshotGas(); // Verify balance after claiming - uint256 metaVaultBalanceAfter = address(metaVault).balance; + uint256 metaVaultBalanceAfter = address(newMetaVault).balance; // The meta vault should have received the assets assertGt(metaVaultBalanceAfter, metaVaultBalanceBefore, "Meta vault balance should increase"); @@ -1282,83 +1358,114 @@ contract VaultSubVaultsTest is Test, EthHelpers { // Check how much we actually received uint256 claimedAssets = metaVaultBalanceAfter - metaVaultBalanceBefore; - // Should be approximately half of the total needed assets + // Should be approximately equal to processed assets assertEq(claimedAssets, processedAssets, "Claimed assets should be equal to processed assets"); // The sub vault's queued shares should decrease but not to zero - IVaultSubVaults.SubVaultState memory stateAfter = metaVault.subVaultsStates(testSubVault); + IVaultSubVaults.SubVaultState memory stateAfter = newMetaVault.subVaultsStates(testSubVault); assertLt(stateAfter.queuedShares, stateBefore.queuedShares, "Queued shares should decrease"); - assertGt(stateAfter.queuedShares, 0, "Queued shares should not be zero - only half was processed"); + assertGt(stateAfter.queuedShares, 0, "Queued shares should not be zero - only partial was processed"); } function test_claimSubVaultsExitedAssets_ejectingSubVault() public { - // Deposit to sub vaults first - metaVault.depositToSubVaults(); + // Create a new meta vault to have precise control over state + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: 1000 ether, + feePercent: 1000, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + EthMetaVault newMetaVault = + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); + + // Create and add sub vaults + address[] memory newSubVaults = new address[](3); + for (uint256 i = 0; i < 3; i++) { + newSubVaults[i] = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), newSubVaults[i]); + vm.prank(admin); + newMetaVault.addSubVault(newSubVaults[i]); + } + + // Deposit to meta vault and distribute to sub vaults + vm.deal(address(this), 10 ether); + newMetaVault.deposit{value: 10 ether}(address(this), address(0)); + newMetaVault.depositToSubVaults(); // Choose a sub vault to eject - address ejectingSubVault = subVaults[0]; + address ejectingSubVault = newSubVaults[0]; - // Eject the sub vault + // Eject the sub vault - record logs to capture position ticket + vm.recordLogs(); vm.prank(admin); - metaVault.ejectSubVault(ejectingSubVault); + newMetaVault.ejectSubVault(ejectingSubVault); + uint64 ejectTimestamp = uint64(vm.getBlockTimestamp()); + address[] memory ejectingVaults = new address[](1); + ejectingVaults[0] = ejectingSubVault; + ExitRequest[] memory ejectPositions = + _extractExitPositions(ejectingVaults, vm.getRecordedLogs(), ejectTimestamp); // Verify the ejecting sub vault is set correctly - assertEq(metaVault.ejectingSubVault(), ejectingSubVault, "Ejecting sub vault should be set"); + assertEq(newMetaVault.ejectingSubVault(), ejectingSubVault, "Ejecting sub vault should be set"); // Verify the vault has moved from staked to queued shares - IVaultSubVaults.SubVaultState memory state = metaVault.subVaultsStates(ejectingSubVault); + IVaultSubVaults.SubVaultState memory state = newMetaVault.subVaultsStates(ejectingSubVault); assertEq(state.stakedShares, 0, "Staked shares should be zero after ejection"); assertGt(state.queuedShares, 0, "Queued shares should be positive after ejection"); // Now have a user enter the exit queue with all of their shares - metaVault.enterExitQueue(metaVault.getShares(address(this)), address(this)); + newMetaVault.enterExitQueue(newMetaVault.getShares(address(this)), address(this)); // Update nonces to process exit queue uint64 newNonce = contracts.keeper.rewardsNonce() + 1; _setKeeperRewardsNonce(newNonce); - for (uint256 i = 0; i < subVaults.length; i++) { - _setVaultRewardsNonce(subVaults[i], newNonce); + for (uint256 i = 0; i < newSubVaults.length; i++) { + _setVaultRewardsNonce(newSubVaults[i], newNonce); } // Start gas measurement _startSnapshotGas("VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares"); // Update state which should process the user's exit and consume ejecting vault's shares - metaVault.updateState(_getEmptyHarvestParams()); - uint64 timestamp = uint64(vm.getBlockTimestamp()); + newMetaVault.updateState(_getEmptyHarvestParams()); // Stop gas measurement _stopSnapshotGas(); // Process the exit for sub vaults - for (uint256 i = 0; i < subVaults.length; i++) { - IKeeperRewards.HarvestParams memory harvestParams = _setEthVaultReward(subVaults[i], 0, 0); - IVaultState(subVaults[i]).updateState(harvestParams); + for (uint256 i = 0; i < newSubVaults.length; i++) { + IKeeperRewards.HarvestParams memory harvestParams = _setEthVaultReward(newSubVaults[i], 0, 0); + IVaultState(newSubVaults[i]).updateState(harvestParams); } // Fast-forward time to allow claiming vm.warp(vm.getBlockTimestamp() + _exitingAssetsClaimDelay + 1); - // Create exit requests for claiming + // Create exit requests for claiming using captured position ticket from ejection + require(ejectPositions.length > 0, "Should have captured position ticket from ejection"); IVaultSubVaults.SubVaultExitRequest[] memory claimRequests = new IVaultSubVaults.SubVaultExitRequest[](1); claimRequests[0] = IVaultSubVaults.SubVaultExitRequest({ vault: ejectingSubVault, - exitQueueIndex: uint256(IVaultEnterExit(ejectingSubVault).getExitQueueIndex(0)), - timestamp: timestamp + exitQueueIndex: uint256( + IVaultEnterExit(ejectingSubVault).getExitQueueIndex(ejectPositions[0].positionTicket) + ), + timestamp: ejectTimestamp }); // Claim exited assets - metaVault.claimSubVaultsExitedAssets(claimRequests); + newMetaVault.claimSubVaultsExitedAssets(claimRequests); // Verify the ejecting sub vault is removed from the state - assertEq(metaVault.ejectingSubVault(), address(0), "Ejecting sub vault should be removed"); + assertEq(newMetaVault.ejectingSubVault(), address(0), "Ejecting sub vault should be removed"); assertEq( - metaVault.subVaultsStates(ejectingSubVault).stakedShares, + newMetaVault.subVaultsStates(ejectingSubVault).stakedShares, 0, "Ejecting sub vault should have zero staked shares after claim" ); assertEq( - metaVault.subVaultsStates(ejectingSubVault).queuedShares, + newMetaVault.subVaultsStates(ejectingSubVault).queuedShares, 0, "Ejecting sub vault should have zero queued shares after claim" ); @@ -1629,7 +1736,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" }) ); - address unCollateralizedMetaVault = _getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false); + address unCollateralizedMetaVault = _createVault(VaultType.EthMetaVault, admin, initParams, false); // Action & Assert: Cannot add non-collateralized meta vault vm.prank(admin); @@ -1904,7 +2011,7 @@ contract VaultSubVaultsTest is Test, EthHelpers { metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" }) ); - metaSubVault = _getOrCreateVault(VaultType.EthMetaVault, _admin, initParams, false); + metaSubVault = _createVault(VaultType.EthMetaVault, _admin, initParams, false); // Create regular vault as sub vaults of the meta sub vault address subVault = _createSubVault(_admin); @@ -1930,9 +2037,8 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault).depth( - 1 - ).checked_write(rewardsNonce); + stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault) + .depth(1).checked_write(rewardsNonce); } function _setMetaVaultRewardsNonce(address vault, uint128 rewardsNonce) internal { @@ -1940,9 +2046,8 @@ contract VaultSubVaultsTest is Test, EthHelpers { } function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()").checked_write( - rewardsNonce - ); + stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") + .checked_write(rewardsNonce); } function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { diff --git a/test/helpers/EthHelpers.sol b/test/helpers/EthHelpers.sol index 005b8308..64572f95 100644 --- a/test/helpers/EthHelpers.sol +++ b/test/helpers/EthHelpers.sol @@ -51,6 +51,7 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { address internal constant _rewardEthToken = 0x20BC832ca081b91433ff6c17f85701B6e92486c5; address internal constant _consolidationsChecker = 0x033E5BaE5bdc459CBb7d388b41a9d62020Be810F; address internal constant _curatorsRegistry = 0xa23F7c8d25f4503cA4cEd84d9CC2428e8745933C; + address internal constant _balancedCurator = 0xD30E7e4bDbd396cfBe72Ad2f4856769C54eA6b0b; uint256 internal constant _exitingAssetsClaimDelay = 15 hours; enum VaultType { @@ -122,6 +123,10 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { vm.prank(currentAdmin); IEthVault(vault).setAdmin(admin); } + if (IEthVault(vault).feeRecipient() != admin) { + vm.prank(admin); + IEthVault(vault).setFeeRecipient(admin); + } } function _getOrCreateFactory(VaultType _vaultType) internal returns (EthVaultFactory) { @@ -270,7 +275,7 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { } else if (vaultType == VaultType.EthBlocklistErc20Vault) { return 0x498399e4f5FDe641a43DCEAFc0aac858abaF2034; } else if (vaultType == VaultType.EthMetaVault) { - return 0x15639E82d2072Fa510E5d2b5F0db361c823bCad3; + return 0x34284C27A2304132aF751b0dEc5bBa2CF98eD039; } return address(0); } From 6daf0c99ddee87acb54a2c09be2a8e2bcbd66088 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Fri, 16 Jan 2026 18:03:40 +0200 Subject: [PATCH 14/19] Add meta vault upgrade test and expand OsTokenRedeemer test coverage --- contracts/vaults/base/MetaVault.sol | 8 +- test/EthMetaVault.t.sol | 89 +++++++ test/EthOsTokenRedeemer.t.sol | 344 ++++++++++++++++++++++++++-- 3 files changed, 413 insertions(+), 28 deletions(-) diff --git a/contracts/vaults/base/MetaVault.sol b/contracts/vaults/base/MetaVault.sol index 1f541fcb..563496d4 100644 --- a/contracts/vaults/base/MetaVault.sol +++ b/contracts/vaults/base/MetaVault.sol @@ -81,14 +81,14 @@ abstract contract MetaVault is nonReentrant returns (uint256 totalRedeemedAssets) { - if (assetsToRedeem == 0) { - revert Errors.InvalidAssets(); - } - // check only redeemer can call address redeemer = _osTokenConfig.redeemer(); if (msg.sender != redeemer) revert Errors.AccessDenied(); + if (assetsToRedeem == 0) { + revert Errors.InvalidAssets(); + } + // get redeem requests ISubVaultsCurator.ExitRequest[] memory redeemRequests = calculateSubVaultsRedemptions(assetsToRedeem); if (redeemRequests.length == 0) { diff --git a/test/EthMetaVault.t.sol b/test/EthMetaVault.t.sol index eacad940..c84dc821 100644 --- a/test/EthMetaVault.t.sol +++ b/test/EthMetaVault.t.sol @@ -21,6 +21,7 @@ contract EthMetaVaultTest is Test, EthHelpers { using stdStorage for StdStorage; bytes32 private constant exitQueueEnteredTopic = keccak256("ExitQueueEntered(address,address,uint256,uint256)"); + address private constant FORK_META_VAULT = 0x34284C27A2304132aF751b0dEc5bBa2CF98eD039; struct ExitRequest { address vault; @@ -28,6 +29,20 @@ contract EthMetaVaultTest is Test, EthHelpers { uint64 timestamp; } + struct PreUpgradeState { + address admin; + address feeRecipient; + uint16 feePercent; + uint256 totalShares; + uint256 totalAssets; + uint256 capacity; + address curator; + uint128 rewardsNonce; + uint128 queuedShares; + uint128 unclaimedAssets; + uint256 totalTickets; + } + ForkContracts public contracts; EthMetaVault public metaVault; @@ -39,6 +54,11 @@ contract EthMetaVaultTest is Test, EthHelpers { // Sub vaults address[] public subVaults; + // Pre-upgrade state for fork vault upgrade test + PreUpgradeState public preUpgradeState; + address[] public preUpgradeSubVaults; + mapping(address => IVaultSubVaults.SubVaultState) public preUpgradeSubVaultStates; + function setUp() public { // Activate Ethereum fork and get the contracts contracts = _activateEthereumFork(); @@ -53,6 +73,11 @@ contract EthMetaVaultTest is Test, EthHelpers { vm.deal(admin, 100 ether); vm.deal(sender, 100 ether); + // Capture pre-upgrade state if using fork vaults + if (vm.envBool("TEST_USE_FORK_VAULTS")) { + _capturePreUpgradeState(); + } + // Deploy meta vault bytes memory initParams = abi.encode( IMetaVault.MetaVaultInitParams({ @@ -81,6 +106,28 @@ contract EthMetaVaultTest is Test, EthHelpers { } } + function _capturePreUpgradeState() internal { + EthMetaVault vault = EthMetaVault(payable(FORK_META_VAULT)); + require(vault.version() == 5, "Fork vault is not version 5"); + + preUpgradeState.admin = vault.admin(); + preUpgradeState.feeRecipient = vault.feeRecipient(); + preUpgradeState.feePercent = vault.feePercent(); + preUpgradeState.totalShares = vault.totalShares(); + preUpgradeState.totalAssets = vault.totalAssets(); + preUpgradeState.capacity = vault.capacity(); + preUpgradeState.curator = vault.subVaultsCurator(); + preUpgradeState.rewardsNonce = vault.subVaultsRewardsNonce(); + (preUpgradeState.queuedShares, preUpgradeState.unclaimedAssets,,, preUpgradeState.totalTickets) = + vault.getExitQueueData(); + + address[] memory vaultSubVaults = vault.getSubVaults(); + for (uint256 i = 0; i < vaultSubVaults.length; i++) { + preUpgradeSubVaults.push(vaultSubVaults[i]); + preUpgradeSubVaultStates[vaultSubVaults[i]] = vault.subVaultsStates(vaultSubVaults[i]); + } + } + function _createSubVault(address _admin) internal returns (address) { bytes memory initParams = abi.encode( IEthVault.EthVaultInitParams({ @@ -473,6 +520,48 @@ contract EthMetaVaultTest is Test, EthHelpers { metaVault.donateAssets{value: 0}(); } + function test_forkMetaVaultUpgrade_preservesState() public view { + // Skip if not using fork vaults or no pre-upgrade state was captured + if (!vm.envBool("TEST_USE_FORK_VAULTS")) { + return; + } + + EthMetaVault vault = EthMetaVault(payable(FORK_META_VAULT)); + + // Verify version was upgraded + assertEq(vault.version(), 6, "Vault should be version 6 after upgrade"); + assertEq(vault.vaultId(), keccak256("EthMetaVault"), "Vault ID should be preserved"); + + // Note: admin and feeRecipient are intentionally changed by _getOrCreateVault for testing + // Verify fee percent is preserved (admin/feeRecipient changes are expected) + assertEq(vault.feePercent(), preUpgradeState.feePercent, "Fee percent should be preserved"); + + // Verify vault state preserved + assertEq(vault.totalShares(), preUpgradeState.totalShares, "Total shares should be preserved"); + assertEq(vault.totalAssets(), preUpgradeState.totalAssets, "Total assets should be preserved"); + assertEq(vault.capacity(), preUpgradeState.capacity, "Capacity should be preserved"); + assertEq(vault.subVaultsCurator(), preUpgradeState.curator, "Curator should be preserved"); + assertEq(vault.subVaultsRewardsNonce(), preUpgradeState.rewardsNonce, "Rewards nonce should be preserved"); + + // Verify sub vaults state preserved (original sub vaults should still be present) + address[] memory postSubVaults = vault.getSubVaults(); + assertGe(postSubVaults.length, preUpgradeSubVaults.length, "Should have at least original sub vaults"); + for (uint256 i = 0; i < preUpgradeSubVaults.length; i++) { + // Original sub vaults should be at the beginning of the list + assertEq(postSubVaults[i], preUpgradeSubVaults[i], "Sub vault address should be preserved"); + IVaultSubVaults.SubVaultState memory postState = vault.subVaultsStates(preUpgradeSubVaults[i]); + IVaultSubVaults.SubVaultState memory preState = preUpgradeSubVaultStates[preUpgradeSubVaults[i]]; + assertEq(postState.stakedShares, preState.stakedShares, "Staked shares should be preserved"); + assertEq(postState.queuedShares, preState.queuedShares, "Queued shares should be preserved"); + } + + // Verify exit queue data preserved + (uint128 postQueuedShares, uint128 postUnclaimedAssets,,, uint256 postTotalTickets) = vault.getExitQueueData(); + assertEq(postQueuedShares, preUpgradeState.queuedShares, "Queued shares should be preserved"); + assertEq(postUnclaimedAssets, preUpgradeState.unclaimedAssets, "Unclaimed assets should be preserved"); + assertEq(postTotalTickets, preUpgradeState.totalTickets, "Total tickets should be preserved"); + } + function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { bytes32[] memory emptyProof; return diff --git a/test/EthOsTokenRedeemer.t.sol b/test/EthOsTokenRedeemer.t.sol index 8786e627..8cf12cf2 100644 --- a/test/EthOsTokenRedeemer.t.sol +++ b/test/EthOsTokenRedeemer.t.sol @@ -1,22 +1,29 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.22; -import {Test} from "forge-std/Test.sol"; +import {Test, stdStorage, StdStorage} from "forge-std/Test.sol"; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {EthOsTokenRedeemer} from "../contracts/tokens/EthOsTokenRedeemer.sol"; import {IOsTokenRedeemer} from "../contracts/interfaces/IOsTokenRedeemer.sol"; import {EthVault, IEthVault} from "../contracts/vaults/ethereum/EthVault.sol"; +import {EthMetaVault} from "../contracts/vaults/ethereum/EthMetaVault.sol"; +import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; import {Errors} from "../contracts/libraries/Errors.sol"; +import {IKeeperRewards} from "../contracts/interfaces/IKeeperRewards.sol"; import {EthHelpers} from "./helpers/EthHelpers.sol"; contract EthOsTokenRedeemerTest is Test, EthHelpers { + using stdStorage for StdStorage; + // Test contracts ForkContracts public contracts; EthOsTokenRedeemer public osTokenRedeemer; EthVault public vault; EthVault public vault2; + EthMetaVault public metaVault; + address[] public subVaults; // Test accounts address public owner; @@ -55,8 +62,13 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { _fundAccounts(); // Deploy OsTokenRedeemer - osTokenRedeemer = - new EthOsTokenRedeemer(address(contracts.vaultsRegistry), _osToken, address(contracts.osTokenVaultController), owner, EXIT_QUEUE_UPDATE_DELAY); + osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + owner, + EXIT_QUEUE_UPDATE_DELAY + ); // Set up manager vm.prank(owner); @@ -116,6 +128,66 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { _depositToVault(address(vault2), DEPOSIT_AMOUNT, user3, user3); } + function _setupMetaVault() internal { + // Deploy meta vault + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: _balancedCurator, + capacity: type(uint256).max, + feePercent: 0, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + metaVault = EthMetaVault(payable(_getOrCreateVault(VaultType.EthMetaVault, admin, initParams, false))); + + // Get existing sub vaults (if any from fork) + address[] memory currentSubVaults = metaVault.getSubVaults(); + for (uint256 i = 0; i < currentSubVaults.length; i++) { + subVaults.push(currentSubVaults[i]); + } + + // Deploy and add sub vaults + for (uint256 i = 0; i < 2; i++) { + address subVault = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault); + subVaults.push(subVault); + + vm.prank(admin); + metaVault.addSubVault(subVault); + } + + // Deposit to meta vault to make it have assets + vm.deal(user1, 100 ether); + vm.prank(user1); + metaVault.deposit{value: 50 ether}(user1, address(0)); + + // Update meta vault state to distribute to sub vaults + _updateMetaVaultState(); + } + + function _createSubVault(address _admin) internal returns (address) { + bytes memory initParams = abi.encode( + IEthVault.EthVaultInitParams({ + capacity: 1000 ether, + feePercent: 5, // 5% + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + return _createVault(VaultType.EthVault, _admin, initParams, false); + } + + function _updateMetaVaultState() internal { + // Update nonces for sub vaults to prepare for state update + uint64 newNonce = contracts.keeper.rewardsNonce() + 1; + _setKeeperRewardsNonce(newNonce); + for (uint256 i = 0; i < subVaults.length; i++) { + _setVaultRewardsNonce(subVaults[i], newNonce); + } + + // Update meta vault state + metaVault.updateState(_getEmptyHarvestParams()); + } + function _setRedeemablePositions(IOsTokenRedeemer.RedeemablePositions memory positions) internal { vm.prank(owner); osTokenRedeemer.setRedeemablePositions(positions); @@ -149,10 +221,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { // Create merkle tree for positions IOsTokenRedeemer.OsTokenPosition[] memory positions = new IOsTokenRedeemer.OsTokenPosition[](1); positions[0] = IOsTokenRedeemer.OsTokenPosition({ - vault: address(vault), - owner: user1, - leafShares: shares, - sharesToRedeem: shares + vault: address(vault), owner: user1, leafShares: shares, sharesToRedeem: shares }); // Create merkle root @@ -195,7 +264,13 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { uint256 invalidExitQueueDelay = uint256(type(uint64).max) + 1; vm.expectRevert(Errors.InvalidDelay.selector); - new EthOsTokenRedeemer(address(contracts.vaultsRegistry),_osToken, address(contracts.osTokenVaultController), owner, invalidExitQueueDelay); + new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + owner, + invalidExitQueueDelay + ); } function test_setPositionsManager_notOwner() public { @@ -245,6 +320,37 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { osTokenRedeemer.setRedeemablePositions(positions); } + function test_setRedeemablePositions_sameValue() public { + IOsTokenRedeemer.RedeemablePositions memory positions = + IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); + + // First set + vm.prank(owner); + osTokenRedeemer.setRedeemablePositions(positions); + + // Try setting same value again + vm.prank(owner); + vm.expectRevert(Errors.ValueNotChanged.selector); + osTokenRedeemer.setRedeemablePositions(positions); + } + + function test_setRedeemablePositions_zeroRoot() public { + // Try to set redeemable positions with zero merkle root + IOsTokenRedeemer.RedeemablePositions memory positions = + IOsTokenRedeemer.RedeemablePositions({merkleRoot: bytes32(0), ipfsHash: TEST_IPFS_HASH}); + + vm.prank(owner); + vm.expectRevert(Errors.InvalidRedeemablePositions.selector); + osTokenRedeemer.setRedeemablePositions(positions); + + // Try to set redeemable positions with empty ipfsHash + positions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: ""}); + + vm.prank(owner); + vm.expectRevert(Errors.InvalidRedeemablePositions.selector); + osTokenRedeemer.setRedeemablePositions(positions); + } + function test_setRedeemablePositions_success() public { IOsTokenRedeemer.RedeemablePositions memory positions = IOsTokenRedeemer.RedeemablePositions({merkleRoot: keccak256("test"), ipfsHash: TEST_IPFS_HASH}); @@ -501,6 +607,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { uint256 initialQueuedShares = osTokenRedeemer.queuedShares(); uint256 initialOsTokenBalance = IERC20(_osToken).balanceOf(user1); uint256 initialRedeemerBalance = IERC20(_osToken).balanceOf(address(osTokenRedeemer)); + uint256 initialCumulativeTickets = osTokenRedeemer.getExitQueueCumulativeTickets(); // Calculate expected position ticket (,, uint256 totalTickets) = osTokenRedeemer.getExitQueueData(); @@ -524,6 +631,13 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { osTokenRedeemer.queuedShares(), initialQueuedShares + osTokenShares, "Queued shares not updated correctly" ); + // Verify cumulative tickets increased correctly + assertEq( + osTokenRedeemer.getExitQueueCumulativeTickets(), + initialCumulativeTickets + osTokenShares, + "Cumulative tickets not updated correctly" + ); + // Verify osToken was transferred from user to redeemer assertEq( IERC20(_osToken).balanceOf(user1), @@ -561,14 +675,155 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { assertEq(exitRequestShares2, osTokenShares, "Exit request with different receiver not recorded correctly"); } + function test_redeemSubVaultsAssets_notPositionsManager() public { + _setupMetaVault(); + + // Try to call redeemSubVaultsAssets as a non-positionsManager + vm.prank(user1); + vm.expectRevert(Errors.AccessDenied.selector); + osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), 1 ether); + + // Try with owner (still not positionsManager) + vm.prank(owner); + vm.expectRevert(Errors.AccessDenied.selector); + osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), 1 ether); + } + + function test_redeemSubVaultsAssets_invalidMetaVault() public { + // Try with a regular vault (not a meta vault) + vm.prank(positionsManager); + vm.expectRevert(Errors.InvalidVault.selector); + osTokenRedeemer.redeemSubVaultsAssets(address(vault), 1 ether); + + // Try with a random address (not registered in vaults registry) + address randomAddress = makeAddr("RandomAddress"); + vm.prank(positionsManager); + vm.expectRevert(Errors.InvalidVault.selector); + osTokenRedeemer.redeemSubVaultsAssets(randomAddress, 1 ether); + } + + function test_redeemSubVaultsAssets_zeroAssetsToRedeem() public { + _setupMetaVault(); + + // Try to redeem zero assets + vm.prank(positionsManager); + vm.expectRevert(Errors.InvalidAssets.selector); + osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), 0); + } + + function test_redeemSubVaultOsToken_invalidMetaVault() public { + _setupMetaVault(); + + // Try calling from a non-meta vault (user) + vm.prank(user1); + vm.expectRevert(Errors.AccessDenied.selector); + osTokenRedeemer.redeemSubVaultOsToken(subVaults[0], 1 ether); + + // Try calling from a regular vault (not a meta vault) + vm.prank(address(vault)); + vm.expectRevert(Errors.AccessDenied.selector); + osTokenRedeemer.redeemSubVaultOsToken(subVaults[0], 1 ether); + + // Try with invalid sub vault (not registered) + address randomSubVault = makeAddr("RandomSubVault"); + vm.prank(address(metaVault)); + vm.expectRevert(Errors.AccessDenied.selector); + osTokenRedeemer.redeemSubVaultOsToken(randomSubVault, 1 ether); + } + + function test_redeemSubVaultOsToken_zeroSharesToRedeem() public { + _setupMetaVault(); + + // Try to redeem zero shares + vm.prank(address(metaVault)); + vm.expectRevert(Errors.InvalidShares.selector); + osTokenRedeemer.redeemSubVaultOsToken(subVaults[0], 0); + } + + function test_getExitQueueMissingAssets_noMissingAssets() public { + // Setup: Enter exit queue and process to create some totalTickets + uint256 sharesToQueue = 10 ether; + address user = makeAddr("User"); + _enterExitQueue(user, sharesToQueue); + + // Swap all queued shares to process them + _swapQueuedShares(sharesToQueue); + + // Process the exit queue + vm.warp(block.timestamp + EXIT_QUEUE_UPDATE_DELAY + 1); + osTokenRedeemer.processExitQueue(); + + // Get the totalTickets after processing + (,, uint256 totalTickets) = osTokenRedeemer.getExitQueueData(); + + // Query with targetCumulativeTickets <= totalTickets should return 0 + uint256 missingAssets = osTokenRedeemer.getExitQueueMissingAssets(totalTickets); + assertEq(missingAssets, 0, "Missing assets should be 0 when target <= totalTickets"); + + // Query with targetCumulativeTickets < totalTickets should also return 0 + missingAssets = osTokenRedeemer.getExitQueueMissingAssets(totalTickets / 2); + assertEq(missingAssets, 0, "Missing assets should be 0 when target < totalTickets"); + } + + function test_getExitQueueMissingAssets_availableAssetsExceedMissing() public { + // Setup: Enter exit queue with some shares + uint256 sharesToQueue = 10 ether; + address user = makeAddr("User"); + _enterExitQueue(user, sharesToQueue); + + // Send ETH directly to the redeemer contract (not via swap) to create available assets + // This way availableAssets > unclaimedAssets + uint256 directDeposit = 20 ether; + vm.deal(address(this), directDeposit); + (bool success,) = address(osTokenRedeemer).call{value: directDeposit}(""); + require(success, "Transfer failed"); + + // Get exit queue data + (,, uint256 totalTickets) = osTokenRedeemer.getExitQueueData(); + + // Target slightly above totalTickets - the direct deposit should cover this + uint256 ticketsToCover = 1 ether; + uint256 targetCumulativeTickets = totalTickets + ticketsToCover; + + // Query missing assets - should be 0 because available assets exceed the missing amount + uint256 missingAssets = osTokenRedeemer.getExitQueueMissingAssets(targetCumulativeTickets); + + // The contract has enough ETH to cover the missing assets + assertEq(missingAssets, 0, "Missing assets should be 0 when available assets exceed missing"); + } + + function test_getExitQueueMissingAssets_missingAssetsExceedAvailable() public { + // Setup: Enter exit queue with shares + uint256 sharesToQueue = 100 ether; + address user = makeAddr("User"); + _enterExitQueue(user, sharesToQueue); + + // Don't add any available assets - the redeemer contract has no ETH + // Get exit queue data + (,, uint256 totalTickets) = osTokenRedeemer.getExitQueueData(); + uint256 cumulativeTickets = osTokenRedeemer.getExitQueueCumulativeTickets(); + + // Target the full cumulativeTickets (all queued shares) + uint256 targetCumulativeTickets = cumulativeTickets; + + // Calculate expected missing assets + uint256 ticketsToCover = targetCumulativeTickets - totalTickets; + uint256 expectedMissingAssets = contracts.osTokenVaultController.convertToAssets(ticketsToCover); + + // Query missing assets + uint256 missingAssets = osTokenRedeemer.getExitQueueMissingAssets(targetCumulativeTickets); + + // Since redeemer has no available assets, missing assets should equal the full amount needed + assertEq( + missingAssets, expectedMissingAssets, "Missing assets should equal full amount when no available assets" + ); + } + function test_redeemOsTokenPositions_noQueuedShares() public { // Prepare merkle tree data IOsTokenRedeemer.OsTokenPosition[] memory positions = new IOsTokenRedeemer.OsTokenPosition[](1); positions[0] = IOsTokenRedeemer.OsTokenPosition({ - vault: address(vault), - owner: user1, - leafShares: 1 ether, - sharesToRedeem: 1 ether + vault: address(vault), owner: user1, leafShares: 1 ether, sharesToRedeem: 1 ether }); bytes32[] memory proof = new bytes32[](0); @@ -681,10 +936,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { // Create position IOsTokenRedeemer.OsTokenPosition[] memory positions = new IOsTokenRedeemer.OsTokenPosition[](1); positions[0] = IOsTokenRedeemer.OsTokenPosition({ - vault: address(vault), - owner: user1, - leafShares: 1 ether, - sharesToRedeem: 1 ether + vault: address(vault), owner: user1, leafShares: 1 ether, sharesToRedeem: 1 ether }); // Set up merkle root with different values (invalid proof) @@ -707,6 +959,38 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); } + function test_redeemOsTokenPositions_zeroRoot() public { + // Setup: Create queued shares + _collateralizeEthVault(address(vault)); + _depositToVault(address(vault), DEPOSIT_AMOUNT, user1, user1); + + uint256 osTokenShares = contracts.osTokenVaultController.convertToShares(1 ether); + vm.prank(user1); + vault.mintOsToken(user1, osTokenShares, address(0)); + + vm.prank(user1); + IERC20(_osToken).approve(address(osTokenRedeemer), osTokenShares); + + vm.prank(user1); + osTokenRedeemer.enterExitQueue(osTokenShares, user1); + + // Create position (redeemable positions not set - merkle root is zero) + IOsTokenRedeemer.OsTokenPosition[] memory positions = new IOsTokenRedeemer.OsTokenPosition[](1); + positions[0] = IOsTokenRedeemer.OsTokenPosition({ + vault: address(vault), owner: user1, leafShares: osTokenShares, sharesToRedeem: osTokenShares + }); + + bytes32[] memory proof = new bytes32[](0); + bool[] memory proofFlags = new bool[](0); + + // When redeemable positions were never set, nonce is 0 + // The contract uses nonce - 1 for leaf calculation, which causes an underflow + // This effectively protects against calling redeemOsTokenPositions without setting positions first + vm.prank(positionsManager); + vm.expectRevert(); // Arithmetic underflow because nonce = 0 and code does nonce - 1 + osTokenRedeemer.redeemOsTokenPositions(positions, proof, proofFlags); + } + function test_redeemOsTokenPositions_success_singlePosition() public { // Setup: Create vault position and mint osTokens _collateralizeEthVault(address(vault)); @@ -800,16 +1084,10 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { // Create multiple positions IOsTokenRedeemer.OsTokenPosition[] memory positions = new IOsTokenRedeemer.OsTokenPosition[](2); positions[0] = IOsTokenRedeemer.OsTokenPosition({ - vault: address(vault), - owner: user1, - leafShares: user1Shares, - sharesToRedeem: user1Shares / 2 + vault: address(vault), owner: user1, leafShares: user1Shares, sharesToRedeem: user1Shares / 2 }); positions[1] = IOsTokenRedeemer.OsTokenPosition({ - vault: address(vault2), - owner: user2, - leafShares: user2Shares, - sharesToRedeem: user2Shares + vault: address(vault2), owner: user2, leafShares: user2Shares, sharesToRedeem: user2Shares }); // Create merkle tree @@ -1183,4 +1461,22 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { userBalanceAfter - userBalanceBefore, exitedAssets, "User should receive the partially processed assets" ); } + + // ========== Helper Functions for Meta Vault Tests ========== + + function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { + bytes32[] memory emptyProof; + return + IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); + } + + function _setVaultRewardsNonce(address _vault, uint64 rewardsNonce) internal { + stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(_vault) + .depth(1).checked_write(rewardsNonce); + } + + function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { + stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") + .checked_write(rewardsNonce); + } } From 14b6024bd0fafacd435652ae14aa13649d7e0900 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 19 Jan 2026 09:06:17 +0200 Subject: [PATCH 15/19] Fix OsTokenRedeemer shares tracking and add private meta vault tests --- contracts/interfaces/IOsTokenRedeemer.sol | 15 +- contracts/libraries/SubVaultUtils.sol | 8 +- contracts/tokens/EthOsTokenRedeemer.sol | 13 +- contracts/tokens/GnoOsTokenRedeemer.sol | 26 +- contracts/tokens/OsTokenRedeemer.sol | 98 +- contracts/vaults/base/MetaVault.sol | 40 +- snapshots/ConsolidationsCheckerTest.json | 20 +- snapshots/DepositDataRegistryTest.json | 14 +- snapshots/EthBlocklistErc20VaultTest.json | 4 +- snapshots/EthBlocklistVaultTest.json | 4 +- snapshots/EthErc20VaultTest.json | 12 +- snapshots/EthFoxVaultTest.json | 8 +- snapshots/EthGenesisVaultTest.json | 11 +- snapshots/EthMetaVaultTest.json | 22 +- snapshots/EthOsTokenRedeemerTest.json | 29 +- snapshots/EthOsTokenVaultEscrowTest.json | 30 +- snapshots/EthPrivErc20VaultTest.json | 4 +- snapshots/EthPrivMetaVaultTest.json | 11 + snapshots/EthPrivVaultTest.json | 6 +- snapshots/EthRewardSplitterTest.json | 16 +- snapshots/EthVaultTest.json | 10 +- snapshots/GnoBlocklistErc20VaultTest.json | 6 +- snapshots/GnoBlocklistVaultTest.json | 6 +- snapshots/GnoErc20VaultTest.json | 6 +- snapshots/GnoGenesisVaultTest.json | 6 +- snapshots/GnoMetaVaultTest.json | 14 +- snapshots/GnoOsTokenRedeemerTest.json | 6 +- snapshots/GnoOsTokenVaultEscrowTest.json | 2 +- snapshots/GnoPrivErc20VaultTest.json | 6 +- snapshots/GnoPrivMetaVaultTest.json | 8 + snapshots/GnoPrivVaultTest.json | 6 +- snapshots/GnoRewardSplitterTest.json | 18 +- snapshots/GnoVaultExitQueueTest.json | 8 +- snapshots/GnoVaultTest.json | 6 +- snapshots/KeeperOraclesTest.json | 16 +- snapshots/KeeperRewardsTest.json | 20 +- snapshots/KeeperValidatorsTest.json | 20 +- snapshots/OsTokenConfigTest.json | 8 +- snapshots/OsTokenTest.json | 22 +- snapshots/VaultEnterExitTest.json | 32 +- snapshots/VaultEthStakingTest.json | 18 +- snapshots/VaultFeeTest.json | 6 +- snapshots/VaultGnoStakingTest.json | 8 +- snapshots/VaultOsTokenTest.json | 46 +- snapshots/VaultSubVaultsTest.json | 40 +- snapshots/VaultTokenTest.json | 28 +- snapshots/VaultValidatorsTest.json | 46 +- snapshots/VaultVersionTest.json | 12 +- snapshots/VaultsRegistryTest.json | 8 +- test/EthMetaVault.t.sol | 1216 ++++++++++++++++++++- test/EthOsTokenRedeemer.t.sol | 240 +++- test/EthPrivMetaVault.t.sol | 582 ++++++++++ test/VaultSubVaults.t.sol | 19 - test/gnosis/GnoMetaVault.t.sol | 21 +- test/gnosis/GnoPrivMetaVault.t.sol | 475 ++++++++ test/helpers/EthHelpers.sol | 22 +- test/helpers/GnoHelpers.sol | 22 +- 57 files changed, 2954 insertions(+), 472 deletions(-) create mode 100644 snapshots/EthPrivMetaVaultTest.json create mode 100644 snapshots/GnoPrivMetaVaultTest.json create mode 100644 test/EthPrivMetaVault.t.sol create mode 100644 test/gnosis/GnoPrivMetaVault.t.sol diff --git a/contracts/interfaces/IOsTokenRedeemer.sol b/contracts/interfaces/IOsTokenRedeemer.sol index 1aa0f254..609532c3 100644 --- a/contracts/interfaces/IOsTokenRedeemer.sol +++ b/contracts/interfaces/IOsTokenRedeemer.sol @@ -25,7 +25,7 @@ interface IOsTokenRedeemer is IMulticall { * @param vault The address of the Vault * @param owner The address of the position owner * @param leafShares The amount of OsToken shares used to calculate the merkle leaf - * @param osTokenSharesToRedeem The amount of OsToken shares to redeem + * @param sharesToRedeem The amount of OsToken shares to redeem */ struct OsTokenPosition { address vault; @@ -175,11 +175,11 @@ interface IOsTokenRedeemer is IMulticall { view returns (uint256 queuedShares, uint256 unclaimedAssets, uint256 totalTickets); - /** - * @notice Gets the cumulative tickets in the exit queue - * @return The cumulative tickets in the exit queue - */ - function getExitQueueCumulativeTickets() external view returns (uint256); + /** + * @notice Gets the cumulative tickets in the exit queue + * @return The cumulative tickets in the exit queue + */ + function getExitQueueCumulativeTickets() external view returns (uint256); /** * @notice Calculates the missing assets in the exit queue for a target cumulative tickets. @@ -263,8 +263,9 @@ interface IOsTokenRedeemer is IMulticall { * @notice Redeem OsToken shares from a specific sub-vault. Can only be called by the meta vault. * @param subVault The address of the sub-vault * @param osTokenShares The number of OsToken shares to redeem + * @return The amount of redeemed assets */ - function redeemSubVaultOsToken(address subVault, uint256 osTokenShares) external; + function redeemSubVaultOsToken(address subVault, uint256 osTokenShares) external returns (uint256); /** * @notice Redeem assets from the sub-vaults to the meta vault. Can only be called by the positions manager. diff --git a/contracts/libraries/SubVaultUtils.sol b/contracts/libraries/SubVaultUtils.sol index 6824f775..a60d8bb8 100644 --- a/contracts/libraries/SubVaultUtils.sol +++ b/contracts/libraries/SubVaultUtils.sol @@ -146,16 +146,18 @@ library SubVaultUtils { } IVaultOsToken(redeemRequest.vault).mintOsToken(redeemer, osTokenShares, address(0)); + // get shares before redemption to track actual consumption + uint256 sharesBefore = IVaultState(redeemRequest.vault).getShares(address(this)); + // execute redeem - IOsTokenRedeemer(redeemer).redeemSubVaultOsToken(redeemRequest.vault, osTokenShares); + redeemAssets = IOsTokenRedeemer(redeemer).redeemSubVaultOsToken(redeemRequest.vault, osTokenShares); // check position is closed if (IVaultOsToken(redeemRequest.vault).osTokenPositions(address(this)) > 0) { revert Errors.InvalidPosition(); } - // update state - uint256 redeemedShares = IVaultState(redeemRequest.vault).convertToShares(redeemAssets); + uint256 redeemedShares = sharesBefore - IVaultState(redeemRequest.vault).getShares(address(this)); subVaultsStates[redeemRequest.vault].stakedShares -= SafeCast.toUint128(redeemedShares); totalRedeemedAssets += redeemAssets; diff --git a/contracts/tokens/EthOsTokenRedeemer.sol b/contracts/tokens/EthOsTokenRedeemer.sol index 6091ffef..52cb93d1 100644 --- a/contracts/tokens/EthOsTokenRedeemer.sol +++ b/contracts/tokens/EthOsTokenRedeemer.sol @@ -33,22 +33,17 @@ contract EthOsTokenRedeemer is IEthOsTokenRedeemer, ReentrancyGuard, OsTokenRede {} /// @inheritdoc IEthOsTokenRedeemer - function swapAssetsToOsTokenShares( - address receiver - ) external payable override returns (uint256 osTokenShares) { + function swapAssetsToOsTokenShares(address receiver) external payable override returns (uint256 osTokenShares) { return _swapAssetsToOsTokenShares(receiver, msg.value); } /// @inheritdoc OsTokenRedeemer - function _availableAssets() internal view override returns (uint256) { - return address(this).balance; + function _getAssets(address account) internal view override returns (uint256) { + return account.balance; } /// @inheritdoc OsTokenRedeemer - function _transferAssets( - address receiver, - uint256 assets - ) internal override nonReentrant { + function _transferAssets(address receiver, uint256 assets) internal override nonReentrant { return Address.sendValue(payable(receiver), assets); } diff --git a/contracts/tokens/GnoOsTokenRedeemer.sol b/contracts/tokens/GnoOsTokenRedeemer.sol index 5604033f..591343e1 100644 --- a/contracts/tokens/GnoOsTokenRedeemer.sol +++ b/contracts/tokens/GnoOsTokenRedeemer.sol @@ -37,35 +37,27 @@ contract GnoOsTokenRedeemer is IGnoOsTokenRedeemer, OsTokenRedeemer { } /// @inheritdoc IGnoOsTokenRedeemer - function permitGnoToken( - uint256 amount, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external override { + function permitGnoToken(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override { try IERC20Permit(address(_gnoToken)).permit(msg.sender, address(this), amount, deadline, v, r, s) {} catch {} } /// @inheritdoc IGnoOsTokenRedeemer - function swapAssetsToOsTokenShares( - address receiver, - uint256 assets - ) external override returns (uint256 osTokenShares) { + function swapAssetsToOsTokenShares(address receiver, uint256 assets) + external + override + returns (uint256 osTokenShares) + { SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), assets); return _swapAssetsToOsTokenShares(receiver, assets); } /// @inheritdoc OsTokenRedeemer - function _availableAssets() internal view override returns (uint256) { - return _gnoToken.balanceOf(address(this)); + function _getAssets(address account) internal view override returns (uint256) { + return _gnoToken.balanceOf(account); } /// @inheritdoc OsTokenRedeemer - function _transferAssets( - address receiver, - uint256 assets - ) internal override { + function _transferAssets(address receiver, uint256 assets) internal override { SafeERC20.safeTransfer(_gnoToken, receiver, assets); } } diff --git a/contracts/tokens/OsTokenRedeemer.sol b/contracts/tokens/OsTokenRedeemer.sol index e7862e89..5bee73a3 100644 --- a/contracts/tokens/OsTokenRedeemer.sol +++ b/contracts/tokens/OsTokenRedeemer.sol @@ -103,7 +103,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { /// @inheritdoc IOsTokenRedeemer function getExitQueueCumulativeTickets() external view override returns (uint256) { - (uint256 _queuedShares, , uint256 totalTickets) = getExitQueueData(); + (uint256 _queuedShares,, uint256 totalTickets) = getExitQueueData(); return totalTickets + _queuedShares; } @@ -114,9 +114,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function getExitQueueIndex( - uint256 positionTicket - ) external view override returns (int256) { + function getExitQueueIndex(uint256 positionTicket) external view override returns (int256) { uint256 checkpointIdx = ExitQueue.getCheckpointIndex(_exitQueue, positionTicket); return checkpointIdx < _exitQueue.checkpoints.length ? int256(checkpointIdx) : -1; } @@ -130,11 +128,12 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function calculateExitedAssets( - address receiver, - uint256 positionTicket, - uint256 exitQueueIndex - ) public view override returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) { + function calculateExitedAssets(address receiver, uint256 positionTicket, uint256 exitQueueIndex) + public + view + override + returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) + { uint256 exitingTickets = exitRequests[keccak256(abi.encode(receiver, positionTicket))]; if (exitingTickets == 0) return (0, 0, 0); @@ -150,9 +149,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function setPositionsManager( - address positionsManager_ - ) external override onlyOwner { + function setPositionsManager(address positionsManager_) external override onlyOwner { if (positionsManager_ == address(0)) { revert Errors.ZeroAddress(); } @@ -164,9 +161,12 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function getExitQueueMissingAssets( - uint256 targetCumulativeTickets - ) external view override returns (uint256 missingAssets) { + function getExitQueueMissingAssets(uint256 targetCumulativeTickets) + external + view + override + returns (uint256 missingAssets) + { // SLOAD to memory (uint256 _queuedShares, uint256 _unclaimedAssets, uint256 totalTickets) = getExitQueueData(); @@ -182,14 +182,12 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { missingAssets = _osTokenVaultController.convertToAssets(Math.min(totalTicketsToCover, _queuedShares)); // check whether there is enough available assets - uint256 availableAssets = _availableAssets() - _unclaimedAssets; + uint256 availableAssets = _getAssets(address(this)) - _unclaimedAssets; return availableAssets >= missingAssets ? 0 : missingAssets - availableAssets; } /// @inheritdoc IOsTokenRedeemer - function setRedeemablePositions( - RedeemablePositions calldata newPositions - ) external override onlyOwner { + function setRedeemablePositions(RedeemablePositions calldata newPositions) external override onlyOwner { if (newPositions.merkleRoot == bytes32(0) || bytes(newPositions.ipfsHash).length == 0) { revert Errors.InvalidRedeemablePositions(); } @@ -209,21 +207,12 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function permitOsToken( - uint256 shares, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external override { + function permitOsToken(uint256 shares, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override { try IERC20Permit(address(_osToken)).permit(msg.sender, address(this), shares, deadline, v, r, s) {} catch {} } /// @inheritdoc IOsTokenRedeemer - function enterExitQueue( - uint256 shares, - address receiver - ) external override returns (uint256 positionTicket) { + function enterExitQueue(uint256 shares, address receiver) external override returns (uint256 positionTicket) { if (shares == 0) revert Errors.InvalidShares(); if (receiver == address(0)) revert Errors.ZeroAddress(); @@ -249,10 +238,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function claimExitedAssets( - uint256 positionTicket, - uint256 exitQueueIndex - ) external override { + function claimExitedAssets(uint256 positionTicket, uint256 exitQueueIndex) external override { // calculate exited tickets and assets (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) = calculateExitedAssets(msg.sender, positionTicket, exitQueueIndex); @@ -280,10 +266,11 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function redeemSubVaultsAssets( - address metaVault, - uint256 assetsToRedeem - ) external override returns (uint256 totalRedeemedAssets) { + function redeemSubVaultsAssets(address metaVault, uint256 assetsToRedeem) + external + override + returns (uint256 totalRedeemedAssets) + { if (msg.sender != positionsManager) { revert Errors.AccessDenied(); } @@ -296,10 +283,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /// @inheritdoc IOsTokenRedeemer - function redeemSubVaultOsToken( - address subVault, - uint256 osTokenShares - ) external override { + function redeemSubVaultOsToken(address subVault, uint256 osTokenShares) external override returns (uint256) { if (!_vaultsRegistry.vaults(subVault) || !_isMetaVault(msg.sender)) { revert Errors.AccessDenied(); } @@ -307,8 +291,13 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { revert Errors.InvalidShares(); } + uint256 _vaultAssetsBefore = _getAssets(msg.sender); + // redeem osToken shares from sub vault to meta vault IVaultOsToken(subVault).redeemOsToken(osTokenShares, msg.sender, msg.sender); + + // calculate redeemed assets + return _getAssets(msg.sender) - _vaultAssetsBefore; } /// @inheritdoc IOsTokenRedeemer @@ -378,7 +367,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } // redeem positions - uint256 availableAssetsBefore = _availableAssets(); + uint256 availableAssetsBefore = _getAssets(address(this)); for (uint256 i = 0; i < positionsCount;) { OsTokenPosition memory position = positions[i]; if (position.sharesToRedeem > 0) { @@ -393,7 +382,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } // calculate processed assets and shares - uint256 processedAssets = _availableAssets() - availableAssetsBefore; + uint256 processedAssets = _getAssets(address(this)) - availableAssetsBefore; uint256 processedShares = queuedShares - _queuedShares; // update state @@ -437,10 +426,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { * @param assets The number of assets to swap * @return osTokenShares The number of OsToken shares swapped */ - function _swapAssetsToOsTokenShares( - address receiver, - uint256 assets - ) internal returns (uint256 osTokenShares) { + function _swapAssetsToOsTokenShares(address receiver, uint256 assets) internal returns (uint256 osTokenShares) { if (assets == 0) { revert Errors.InvalidAssets(); } @@ -468,9 +454,7 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { * @param vault The address of the vault to check * @return True if the caller is a meta vault, false otherwise */ - function _isMetaVault( - address vault - ) private view returns (bool) { + function _isMetaVault(address vault) private view returns (bool) { // must be a registered vault if (!_vaultsRegistry.vaults(vault)) { return false; @@ -485,10 +469,11 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { } /** - * @dev Internal function that must be implemented to return the available assets for exit - * @return The amount of available assets for exit + * @dev Internal function that must be implemented to return the account assets + * @param account The address of the account + * @return The amount of assets in the vault */ - function _availableAssets() internal view virtual returns (uint256); + function _getAssets(address account) internal view virtual returns (uint256); /** * @dev Internal function for transferring assets to the receiver @@ -498,8 +483,5 @@ abstract contract OsTokenRedeemer is Ownable2Step, Multicall, IOsTokenRedeemer { * @param receiver The address that will receive the assets * @param assets The number of assets to transfer */ - function _transferAssets( - address receiver, - uint256 assets - ) internal virtual; + function _transferAssets(address receiver, uint256 assets) internal virtual; } diff --git a/contracts/vaults/base/MetaVault.sol b/contracts/vaults/base/MetaVault.sol index 563496d4..2e4a0c39 100644 --- a/contracts/vaults/base/MetaVault.sol +++ b/contracts/vaults/base/MetaVault.sol @@ -56,22 +56,12 @@ abstract contract MetaVault is /// @inheritdoc IMetaVault function calculateSubVaultsRedemptions(uint256 assetsToRedeem) - public + external view override returns (ISubVaultsCurator.ExitRequest[] memory redeemRequests) { - _checkHarvested(); - - return SubVaultUtils.calculateSubVaultsRedemptions( - _subVaultsStates, - subVaultsCurator, - getSubVaults(), - assetsToRedeem, - withdrawableAssets(), - ejectingSubVault, - _ejectingSubVaultShares - ); + return _calculateSubVaultsRedemptions(assetsToRedeem, true); } /// @inheritdoc IMetaVault @@ -90,7 +80,7 @@ abstract contract MetaVault is } // get redeem requests - ISubVaultsCurator.ExitRequest[] memory redeemRequests = calculateSubVaultsRedemptions(assetsToRedeem); + ISubVaultsCurator.ExitRequest[] memory redeemRequests = _calculateSubVaultsRedemptions(assetsToRedeem, false); if (redeemRequests.length == 0) { return totalRedeemedAssets; } @@ -138,6 +128,30 @@ abstract contract MetaVault is super._checkHarvested(); } + /** + * @dev Calculates the required sub-vaults exit requests to fulfill the assets to redeem + * @param assetsToRedeem The amount of assets to redeem + * @param includeEjectingSubVaultShares Whether to take into account shares from the ejecting sub-vault + * @return redeemRequests The array of sub-vaults exit requests + */ + function _calculateSubVaultsRedemptions(uint256 assetsToRedeem, bool includeEjectingSubVaultShares) + private + view + returns (ISubVaultsCurator.ExitRequest[] memory redeemRequests) + { + _checkHarvested(); + + return SubVaultUtils.calculateSubVaultsRedemptions( + _subVaultsStates, + subVaultsCurator, + getSubVaults(), + assetsToRedeem, + withdrawableAssets(), + ejectingSubVault, + includeEjectingSubVaultShares ? _ejectingSubVaultShares : 0 + ); + } + /// @inheritdoc VaultImmutables function _isCollateralized() internal view virtual override(VaultImmutables, VaultSubVaults) returns (bool) { return super._isCollateralized(); diff --git a/snapshots/ConsolidationsCheckerTest.json b/snapshots/ConsolidationsCheckerTest.json index da841d2e..6d56cf6e 100644 --- a/snapshots/ConsolidationsCheckerTest.json +++ b/snapshots/ConsolidationsCheckerTest.json @@ -1,12 +1,12 @@ { - "ConsolidationsCheckerTest_test_verifySignatures_differentValidatorData": "23484", - "ConsolidationsCheckerTest_test_verifySignatures_differentVault": "21375", - "ConsolidationsCheckerTest_test_verifySignatures_emptySignatures": "17460", - "ConsolidationsCheckerTest_test_verifySignatures_exactMinimumSignatures": "38203", - "ConsolidationsCheckerTest_test_verifySignatures_moreThanMinimumSignatures": "38216", - "ConsolidationsCheckerTest_test_verifySignatures_nonOracleSigner": "29123", - "ConsolidationsCheckerTest_test_verifySignatures_repeatedSigner": "25705", - "ConsolidationsCheckerTest_test_verifySignatures_success": "38205", - "ConsolidationsCheckerTest_test_verifySignatures_tooFewSignatures": "13654", - "ConsolidationsCheckerTest_test_verifySignatures_unsortedSignatures": "25701" + "ConsolidationsCheckerTest_test_verifySignatures_differentValidatorData": "23314", + "ConsolidationsCheckerTest_test_verifySignatures_differentVault": "21205", + "ConsolidationsCheckerTest_test_verifySignatures_emptySignatures": "17545", + "ConsolidationsCheckerTest_test_verifySignatures_exactMinimumSignatures": "37705", + "ConsolidationsCheckerTest_test_verifySignatures_moreThanMinimumSignatures": "37718", + "ConsolidationsCheckerTest_test_verifySignatures_nonOracleSigner": "28794", + "ConsolidationsCheckerTest_test_verifySignatures_repeatedSigner": "25331", + "ConsolidationsCheckerTest_test_verifySignatures_success": "37707", + "ConsolidationsCheckerTest_test_verifySignatures_tooFewSignatures": "13679", + "ConsolidationsCheckerTest_test_verifySignatures_unsortedSignatures": "25327" } \ No newline at end of file diff --git a/snapshots/DepositDataRegistryTest.json b/snapshots/DepositDataRegistryTest.json index 186c7c64..4c42af58 100644 --- a/snapshots/DepositDataRegistryTest.json +++ b/snapshots/DepositDataRegistryTest.json @@ -1,9 +1,9 @@ { - "DepositDataRegistryTest_test_registerValidator_succeedsWith0x01Validator": "272487", - "DepositDataRegistryTest_test_registerValidator_succeedsWith0x02Validator": "295393", - "DepositDataRegistryTest_test_registerValidators_successWith0x01Validators": "313965", - "DepositDataRegistryTest_test_registerValidators_successWith0x02Validators": "338034", - "DepositDataRegistryTest_test_setDepositDataManager_succeeds": "66206", - "DepositDataRegistryTest_test_setDepositDataRoot_succeeds": "65127", - "DepositDataRegistryTest_test_updateVaultState_succeeds": "128302" + "DepositDataRegistryTest_test_registerValidator_succeedsWith0x01Validator": "273443", + "DepositDataRegistryTest_test_registerValidator_succeedsWith0x02Validator": "296337", + "DepositDataRegistryTest_test_registerValidators_successWith0x01Validators": "314933", + "DepositDataRegistryTest_test_registerValidators_successWith0x02Validators": "339002", + "DepositDataRegistryTest_test_setDepositDataManager_succeeds": "68682", + "DepositDataRegistryTest_test_setDepositDataRoot_succeeds": "65115", + "DepositDataRegistryTest_test_updateVaultState_succeeds": "128278" } \ No newline at end of file diff --git a/snapshots/EthBlocklistErc20VaultTest.json b/snapshots/EthBlocklistErc20VaultTest.json index 2bd80124..ede6ef52 100644 --- a/snapshots/EthBlocklistErc20VaultTest.json +++ b/snapshots/EthBlocklistErc20VaultTest.json @@ -1,8 +1,8 @@ { - "EthBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "89924", + "EthBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "89912", "EthBlocklistErc20VaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "84302", "EthBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "161501", "EthBlocklistErc20VaultTest_test_deploysCorrectly": "625440", "EthBlocklistErc20VaultTest_test_transfer": "61693", - "EthBlocklistErc20VaultTest_test_upgradesCorrectly": "112276" + "EthBlocklistErc20VaultTest_test_upgradesCorrectly": "119882" } \ No newline at end of file diff --git a/snapshots/EthBlocklistVaultTest.json b/snapshots/EthBlocklistVaultTest.json index bdb38bdd..266ac9f6 100644 --- a/snapshots/EthBlocklistVaultTest.json +++ b/snapshots/EthBlocklistVaultTest.json @@ -1,7 +1,7 @@ { - "EthBlocklistVaultTest_test_canDepositAsNonBlockedUser": "87971", + "EthBlocklistVaultTest_test_canDepositAsNonBlockedUser": "87959", "EthBlocklistVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "82360", "EthBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "161528", "EthBlocklistVaultTest_test_deploysCorrectly": "550241", - "EthBlocklistVaultTest_test_upgradesCorrectly": "111607" + "EthBlocklistVaultTest_test_upgradesCorrectly": "119213" } \ No newline at end of file diff --git a/snapshots/EthErc20VaultTest.json b/snapshots/EthErc20VaultTest.json index 2b8c33c2..787ccce1 100644 --- a/snapshots/EthErc20VaultTest.json +++ b/snapshots/EthErc20VaultTest.json @@ -2,14 +2,14 @@ "EthErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90077", "EthErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96608", "EthErc20VaultTest_test_deploysCorrectly": "455018", - "EthErc20VaultTest_test_depositAndMintOsToken": "201247", - "EthErc20VaultTest_test_depositViaReceiveFallback_emitsTransfer": "75451", - "EthErc20VaultTest_test_deposit_emitsTransfer": "78913", + "EthErc20VaultTest_test_depositAndMintOsToken": "203747", + "EthErc20VaultTest_test_depositViaReceiveFallback_emitsTransfer": "77951", + "EthErc20VaultTest_test_deposit_emitsTransfer": "81413", "EthErc20VaultTest_test_enterExitQueue_emitsTransfer": "89780", "EthErc20VaultTest_test_redeem_emitsEvent": "58285", - "EthErc20VaultTest_test_updateExitQueue_emitsTransfer": "177077", - "EthErc20VaultTest_test_updateStateAndDepositAndMintOsToken": "227535", - "EthErc20VaultTest_test_upgradesCorrectly": "112150", + "EthErc20VaultTest_test_updateExitQueue_emitsTransfer": "177065", + "EthErc20VaultTest_test_updateStateAndDepositAndMintOsToken": "230047", + "EthErc20VaultTest_test_upgradesCorrectly": "119756", "EthErc20VaultTest_test_withdrawValidator_unknown": "55951", "EthErc20VaultTest_test_withdrawValidator_validatorsManager": "74092" } \ No newline at end of file diff --git a/snapshots/EthFoxVaultTest.json b/snapshots/EthFoxVaultTest.json index 4b7f8ac4..9883cf17 100644 --- a/snapshots/EthFoxVaultTest.json +++ b/snapshots/EthFoxVaultTest.json @@ -1,8 +1,8 @@ { - "EthFoxVaultTest_test_canDepositAsNonBlockedUser": "87565", - "EthFoxVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "81932", - "EthFoxVaultTest_test_ejectUser": "107257", - "EthFoxVaultTest_test_ejectUserWithNoShares": "62862", + "EthFoxVaultTest_test_canDepositAsNonBlockedUser": "90053", + "EthFoxVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "84432", + "EthFoxVaultTest_test_ejectUser": "109757", + "EthFoxVaultTest_test_ejectUserWithNoShares": "65362", "EthFoxVaultTest_test_withdrawValidator_unknown": "55886", "EthFoxVaultTest_test_withdrawValidator_validatorsManager": "73964" } \ No newline at end of file diff --git a/snapshots/EthGenesisVaultTest.json b/snapshots/EthGenesisVaultTest.json index 2b6602e7..a8226ddb 100644 --- a/snapshots/EthGenesisVaultTest.json +++ b/snapshots/EthGenesisVaultTest.json @@ -1,8 +1,7 @@ { - "EthGenesisVaultTest_test_claimsPoolEscrowAssets": "77662", - "EthGenesisVaultTest_test_fallback_acceptsEtherFromPoolEscrow": "33277", - "EthGenesisVaultTest_test_fallback_acceptsEtherFromUser": "77540", - "EthGenesisVaultTest_test_migrate_works": "201753", - "EthGenesisVaultTest_test_upgradesCorrectly": "91142", - "GnoGenesisVaultTest_test_pullWithdrawals_claimEscrowAssets": "803339" + "EthGenesisVaultTest_test_claimsPoolEscrowAssets": "78175", + "EthGenesisVaultTest_test_fallback_acceptsEtherFromPoolEscrow": "35807", + "EthGenesisVaultTest_test_fallback_acceptsEtherFromUser": "79346", + "EthGenesisVaultTest_test_migrate_works": "204956", + "GnoGenesisVaultTest_test_pullWithdrawals_claimEscrowAssets": "819842" } \ No newline at end of file diff --git a/snapshots/EthMetaVaultTest.json b/snapshots/EthMetaVaultTest.json index 166cc670..520a8996 100644 --- a/snapshots/EthMetaVaultTest.json +++ b/snapshots/EthMetaVaultTest.json @@ -1,9 +1,17 @@ { - "EthMetaVaultTest_test_deposit": "83013", - "EthMetaVaultTest_test_depositAndMintOsToken": "179864", - "EthMetaVaultTest_test_depositViaFallback": "79631", - "EthMetaVaultTest_test_isStateUpdateRequired_true": "138057", - "EthMetaVaultTest_test_updateStateAndDeposit": "188941", - "EthMetaVaultTest_test_updateStateAndDepositAndMintOsToken": "200835", - "EthMetaVaultTest_test_userClaimExitedAssets": "51730" + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_exactWithdrawableAssets": "47214", + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets": "96978", + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_success": "97007", + "EthMetaVaultTest_test_deposit": "85504", + "EthMetaVaultTest_test_depositAndMintOsToken": "179869", + "EthMetaVaultTest_test_depositViaFallback": "82122", + "EthMetaVaultTest_test_isStateUpdateRequired_true": "136011", + "EthMetaVaultTest_test_redeemSubVaultsAssets_noRedeemRequests": "69102", + "EthMetaVaultTest_test_redeemSubVaultsAssets_noRoundingErrors": "549703", + "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets": "548661", + "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets": "549679", + "EthMetaVaultTest_test_redeemSubVaultsAssets_success": "549679", + "EthMetaVaultTest_test_updateStateAndDeposit": "197478", + "EthMetaVaultTest_test_updateStateAndDepositAndMintOsToken": "200840", + "EthMetaVaultTest_test_userClaimExitedAssets": "54213" } \ No newline at end of file diff --git a/snapshots/EthOsTokenRedeemerTest.json b/snapshots/EthOsTokenRedeemerTest.json index 389fe399..03035fdf 100644 --- a/snapshots/EthOsTokenRedeemerTest.json +++ b/snapshots/EthOsTokenRedeemerTest.json @@ -1,17 +1,16 @@ { - "EthOsTokenRedeemerTest_test_acceptRedeemablePositions_success": "94939", - "EthOsTokenRedeemerTest_test_denyRedeemablePositions_success": "30268", - "EthOsTokenRedeemerTest_test_enterExitQueue_success": "107164", - "EthOsTokenRedeemerTest_test_permitOsToken_success": "82596", - "EthOsTokenRedeemerTest_test_processExitQueue_nothingToProcess": "31498", - "EthOsTokenRedeemerTest_test_processExitQueue_success": "99215", - "EthOsTokenRedeemerTest_test_proposeRedeemablePositions_success": "83218", - "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions": "292793", - "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "199173", - "EthOsTokenRedeemerTest_test_removeRedeemablePositions_success": "30304", - "EthOsTokenRedeemerTest_test_setPositionsManager": "35796", - "EthOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "101315", - "test_claimExitedAssets_fullWithdrawal": "50998", - "test_claimExitedAssets_noPosition": "30156", - "test_claimExitedAssets_partialWithdrawal": "75835" + "EthOsTokenRedeemerTest_test_enterExitQueue_success": "109662", + "EthOsTokenRedeemerTest_test_permitOsToken_success": "82575", + "EthOsTokenRedeemerTest_test_processExitQueue_nothingToProcess": "33998", + "EthOsTokenRedeemerTest_test_processExitQueue_success": "101715", + "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions": "297443", + "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "201436", + "EthOsTokenRedeemerTest_test_setPositionsManager": "35834", + "EthOsTokenRedeemerTest_test_setRedeemablePositions_success": "100561", + "EthOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "101264", + "test_claimExitedAssets_fullWithdrawal": "53564", + "test_claimExitedAssets_noPosition": "30222", + "test_claimExitedAssets_partialWithdrawal": "75901", + "test_redeemSubVaultsAssets_concurrentMultipleSubVaults": "675026", + "test_redeemSubVaultsAssets_largeAmountAcrossAllSubVaults": "816491" } \ No newline at end of file diff --git a/snapshots/EthOsTokenVaultEscrowTest.json b/snapshots/EthOsTokenVaultEscrowTest.json index dbd93706..33bc1d77 100644 --- a/snapshots/EthOsTokenVaultEscrowTest.json +++ b/snapshots/EthOsTokenVaultEscrowTest.json @@ -1,35 +1,35 @@ { "EthOsTokenVaultEscrowTest_test_claimExitedAssets_insufficientShares": "66982", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_minimalAmount": "92563", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_minimalAmount": "95063", "EthOsTokenVaultEscrowTest_test_claimExitedAssets_noProcessedAssets": "91451", "EthOsTokenVaultEscrowTest_test_claimExitedAssets_nonExistentPosition": "50879", "EthOsTokenVaultEscrowTest_test_claimExitedAssets_notOwner": "65015", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_withFeeAccrual": "101458", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_claimExitedAssets": "77940", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_withFeeAccrual": "103958", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_claimExitedAssets": "80440", "EthOsTokenVaultEscrowTest_test_processExitedAssets_exitRequestNotProcessed": "49774", "EthOsTokenVaultEscrowTest_test_processExitedAssets_invalidPosition": "33255", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_partialClaim": "93137", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_partialClaim": "95637", "EthOsTokenVaultEscrowTest_test_processExitedAssets_success": "71278", - "EthOsTokenVaultEscrowTest_test_register_accessDenied": "31199", - "EthOsTokenVaultEscrowTest_test_register_directCall": "81100", + "EthOsTokenVaultEscrowTest_test_register_accessDenied": "33699", + "EthOsTokenVaultEscrowTest_test_register_directCall": "83600", "EthOsTokenVaultEscrowTest_test_register_fullFlow": "163659", - "EthOsTokenVaultEscrowTest_test_register_invalidShares": "31242", - "EthOsTokenVaultEscrowTest_test_register_zeroAddress": "30886", + "EthOsTokenVaultEscrowTest_test_register_invalidShares": "33742", + "EthOsTokenVaultEscrowTest_test_register_zeroAddress": "33386", "EthOsTokenVaultEscrowTest_test_setAuthenticator_onlyOwner": "30174", - "EthOsTokenVaultEscrowTest_test_setAuthenticator_success": "33014", - "EthOsTokenVaultEscrowTest_test_setAuthenticator_valueNotChanged": "29509", + "EthOsTokenVaultEscrowTest_test_setAuthenticator_success": "35514", + "EthOsTokenVaultEscrowTest_test_setAuthenticator_valueNotChanged": "32009", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidBonus_high": "27548", - "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidBonus_low": "27626", + "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidBonus_low": "30126", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidThreshold_max": "27337", - "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidThreshold_zero": "27470", + "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidThreshold_zero": "29970", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_onlyOwner": "30318", - "EthOsTokenVaultEscrowTest_test_updateLiqConfig_success": "38745", - "OsTokenLiquidationTest_test_liquidateOsToken_invalidHealthFactor": "49969", + "EthOsTokenVaultEscrowTest_test_updateLiqConfig_success": "41245", + "OsTokenLiquidationTest_test_liquidateOsToken_invalidHealthFactor": "52469", "OsTokenLiquidationTest_test_liquidateOsToken_invalidPosition": "41078", "OsTokenLiquidationTest_test_liquidateOsToken_invalidReceivedAssets": "41999", "OsTokenLiquidationTest_test_liquidateOsToken_partialLiquidation": "94624", "OsTokenLiquidationTest_test_liquidateOsToken_success": "97295", "OsTokenLiquidationTest_test_liquidateOsToken_zeroAddress": "24032", "OsTokenLiquidationTest_test_redeemOsToken_notRedeemer": "27478", - "OsTokenLiquidationTest_test_redeemOsToken_success": "118722" + "OsTokenLiquidationTest_test_redeemOsToken_success": "118710" } \ No newline at end of file diff --git a/snapshots/EthPrivErc20VaultTest.json b/snapshots/EthPrivErc20VaultTest.json index eb18d641..b776b47e 100644 --- a/snapshots/EthPrivErc20VaultTest.json +++ b/snapshots/EthPrivErc20VaultTest.json @@ -1,9 +1,9 @@ { "EthPrivErc20VaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser": "206655", - "EthPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "81407", + "EthPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "81395", "EthPrivErc20VaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77693", "EthPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161601", "EthPrivErc20VaultTest_test_deploysCorrectly": "625462", "EthPrivErc20VaultTest_test_transfer": "59709", - "EthPrivErc20VaultTest_test_upgradesCorrectly": "112296" + "EthPrivErc20VaultTest_test_upgradesCorrectly": "119902" } \ No newline at end of file diff --git a/snapshots/EthPrivMetaVaultTest.json b/snapshots/EthPrivMetaVaultTest.json new file mode 100644 index 00000000..dd76de74 --- /dev/null +++ b/snapshots/EthPrivMetaVaultTest.json @@ -0,0 +1,11 @@ +{ + "EthPrivMetaVaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser": "182769", + "EthPrivMetaVaultTest_test_canDepositAsWhitelistedUser": "83505", + "EthPrivMetaVaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "79859", + "EthPrivMetaVaultTest_test_canMintOsTokenAsWhitelistedUser": "161264", + "EthPrivMetaVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "112889", + "EthPrivMetaVaultTest_test_depositToSubVaultsWorksWithWhitelistedUser": "295390", + "EthPrivMetaVaultTest_test_enterExitQueueWorksForWhitelistedUserAfterRemoval": "84654", + "EthPrivMetaVaultTest_test_setWhitelister": "36586", + "EthPrivMetaVaultTest_test_updateWhitelist": "54499" +} \ No newline at end of file diff --git a/snapshots/EthPrivVaultTest.json b/snapshots/EthPrivVaultTest.json index c580c2f1..8dd7660c 100644 --- a/snapshots/EthPrivVaultTest.json +++ b/snapshots/EthPrivVaultTest.json @@ -1,11 +1,11 @@ { - "EthPrivVaultTest_test_canDepositAsWhitelistedUser": "79443", + "EthPrivVaultTest_test_canDepositAsWhitelistedUser": "79431", "EthPrivVaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "75857", "EthPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161628", - "EthPrivVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "138187", + "EthPrivVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "140687", "EthPrivVaultTest_test_deploysCorrectly": "550259", "EthPrivVaultTest_test_depositAndMintOsTokenAsWhitelistedUser": "200570", "EthPrivVaultTest_test_setWhitelister": "36608", "EthPrivVaultTest_test_updateWhitelist": "54543", - "EthPrivVaultTest_test_upgradesCorrectly": "111722" + "EthPrivVaultTest_test_upgradesCorrectly": "119328" } \ No newline at end of file diff --git a/snapshots/EthRewardSplitterTest.json b/snapshots/EthRewardSplitterTest.json index 2c83421a..6d3d62db 100644 --- a/snapshots/EthRewardSplitterTest.json +++ b/snapshots/EthRewardSplitterTest.json @@ -1,14 +1,14 @@ { - "EthRewardSplitter_claimExitedAssetsOnBehalf": "749608", - "EthRewardSplitter_claimVaultTokens": "754243", - "EthRewardSplitter_decreaseShares": "87633", - "EthRewardSplitter_enterExitQueue": "166850", - "EthRewardSplitter_enterExitQueueMaxWithdrawal": "128878", - "EthRewardSplitter_enterExitQueueOnBehalf": "947262", + "EthRewardSplitter_claimExitedAssetsOnBehalf": "767084", + "EthRewardSplitter_claimVaultTokens": "771719", + "EthRewardSplitter_decreaseShares": "90133", + "EthRewardSplitter_enterExitQueue": "171850", + "EthRewardSplitter_enterExitQueueMaxWithdrawal": "131378", + "EthRewardSplitter_enterExitQueueOnBehalf": "964738", "EthRewardSplitter_increaseShares": "73128", "EthRewardSplitter_receiveEth": "31194", "EthRewardSplitter_syncRewards": "77682", - "EthRewardSplitter_syncRewardsDetailed": "75182", + "EthRewardSplitter_syncRewardsDetailed": "77682", "EthRewardSplitter_test_setClaimer": "66269", - "EthRewardSplitter_updateVaultState": "132625" + "EthRewardSplitter_updateVaultState": "132613" } \ No newline at end of file diff --git a/snapshots/EthVaultTest.json b/snapshots/EthVaultTest.json index 04ae719f..a17dfa31 100644 --- a/snapshots/EthVaultTest.json +++ b/snapshots/EthVaultTest.json @@ -1,10 +1,10 @@ { "EthVaultTest_test_deploysCorrectly": "379895", - "EthVaultTest_test_depositAndMintOsToken": "199548", - "EthVaultTest_test_exitQueue_works": "96643", - "EthVaultTest_test_fallbackDeposit": "75127", - "EthVaultTest_test_updateStateAndDepositAndMintOsToken": "228158", - "EthVaultTest_test_upgradesCorrectly": "111566", + "EthVaultTest_test_depositAndMintOsToken": "202060", + "EthVaultTest_test_exitQueue_works": "99131", + "EthVaultTest_test_fallbackDeposit": "77627", + "EthVaultTest_test_updateStateAndDepositAndMintOsToken": "228182", + "EthVaultTest_test_upgradesCorrectly": "119172", "EthVaultTest_test_withdrawValidator_unknown": "55973", "EthVaultTest_test_withdrawValidator_validatorsManager": "74114" } \ No newline at end of file diff --git a/snapshots/GnoBlocklistErc20VaultTest.json b/snapshots/GnoBlocklistErc20VaultTest.json index afabf55d..0b4b6845 100644 --- a/snapshots/GnoBlocklistErc20VaultTest.json +++ b/snapshots/GnoBlocklistErc20VaultTest.json @@ -1,7 +1,7 @@ { - "GnoBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "160088", + "GnoBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "160108", "GnoBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "161610", - "GnoBlocklistErc20VaultTest_test_deploysCorrectly": "953673", + "GnoBlocklistErc20VaultTest_test_deploysCorrectly": "953762", "GnoBlocklistErc20VaultTest_test_transfer": "61693", - "GnoBlocklistErc20VaultTest_test_upgradesCorrectly": "335109" + "GnoBlocklistErc20VaultTest_test_upgradesCorrectly": "340215" } \ No newline at end of file diff --git a/snapshots/GnoBlocklistVaultTest.json b/snapshots/GnoBlocklistVaultTest.json index d5257935..c4ab5037 100644 --- a/snapshots/GnoBlocklistVaultTest.json +++ b/snapshots/GnoBlocklistVaultTest.json @@ -1,6 +1,6 @@ { - "GnoBlocklistVaultTest_test_canDepositAsNonBlockedUser": "158104", + "GnoBlocklistVaultTest_test_canDepositAsNonBlockedUser": "158124", "GnoBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "161570", - "GnoBlocklistVaultTest_test_deploysCorrectly": "702298", - "GnoBlocklistVaultTest_test_upgradesCorrectly": "334405" + "GnoBlocklistVaultTest_test_deploysCorrectly": "702387", + "GnoBlocklistVaultTest_test_upgradesCorrectly": "339511" } \ No newline at end of file diff --git a/snapshots/GnoErc20VaultTest.json b/snapshots/GnoErc20VaultTest.json index e324c666..dd4c1c4f 100644 --- a/snapshots/GnoErc20VaultTest.json +++ b/snapshots/GnoErc20VaultTest.json @@ -1,11 +1,11 @@ { "GnoErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90100", "GnoErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96637", - "GnoErc20VaultTest_test_deploysCorrectly": "753241", - "GnoErc20VaultTest_test_deposit_emitsTransfer": "98232", + "GnoErc20VaultTest_test_deploysCorrectly": "753330", + "GnoErc20VaultTest_test_deposit_emitsTransfer": "100732", "GnoErc20VaultTest_test_enterExitQueue_emitsTransfer": "89780", "GnoErc20VaultTest_test_redeem_emitsEvent": "77069", - "GnoErc20VaultTest_test_upgradesCorrectly": "335083", + "GnoErc20VaultTest_test_upgradesCorrectly": "340189", "VaultGnoErc20VaultTest_test_withdrawValidator_unknown": "55973", "VaultGnoErc20VaultTest_test_withdrawValidator_validatorsManager": "74114" } \ No newline at end of file diff --git a/snapshots/GnoGenesisVaultTest.json b/snapshots/GnoGenesisVaultTest.json index 1644737d..748a467a 100644 --- a/snapshots/GnoGenesisVaultTest.json +++ b/snapshots/GnoGenesisVaultTest.json @@ -1,5 +1,5 @@ { - "GnoGenesisVaultTest_test_migrate_works": "204216", - "GnoGenesisVaultTest_test_pullWithdrawals_claimEscrowAssets": "867236", - "GnoGenesisVaultTest_test_upgradesCorrectly": "5823320" + "GnoGenesisVaultTest_test_migrate_works": "206716", + "GnoGenesisVaultTest_test_pullWithdrawals_claimEscrowAssets": "879736", + "GnoGenesisVaultTest_test_upgradesCorrectly": "5830820" } \ No newline at end of file diff --git a/snapshots/GnoMetaVaultTest.json b/snapshots/GnoMetaVaultTest.json index 0ab977f6..ffe00ffa 100644 --- a/snapshots/GnoMetaVaultTest.json +++ b/snapshots/GnoMetaVaultTest.json @@ -1,9 +1,9 @@ { - "GnoMetaVaultTest_test_addSubVault": "154448", - "GnoMetaVaultTest_test_claimExitedAssets": "67870", - "GnoMetaVaultTest_test_deposit": "103288", - "GnoMetaVaultTest_test_depositToSubVaults": "326938", - "GnoMetaVaultTest_test_ejectSubVault": "211758", - "GnoMetaVaultTest_test_enterExitQueue": "86712", - "GnoMetaVaultTest_test_updateState": "149972" + "GnoMetaVaultTest_test_addSubVault": "161623", + "GnoMetaVaultTest_test_claimExitedAssets": "67835", + "GnoMetaVaultTest_test_deposit": "103300", + "GnoMetaVaultTest_test_depositToSubVaults": "329543", + "GnoMetaVaultTest_test_ejectSubVault": "218113", + "GnoMetaVaultTest_test_enterExitQueue": "89168", + "GnoMetaVaultTest_test_updateState": "158452" } \ No newline at end of file diff --git a/snapshots/GnoOsTokenRedeemerTest.json b/snapshots/GnoOsTokenRedeemerTest.json index 84c531eb..1bb452bb 100644 --- a/snapshots/GnoOsTokenRedeemerTest.json +++ b/snapshots/GnoOsTokenRedeemerTest.json @@ -1,6 +1,6 @@ { - "GnoOsTokenRedeemerTest_test_claimExitedAssets_fullWithdrawal": "80429", + "GnoOsTokenRedeemerTest_test_claimExitedAssets_fullWithdrawal": "80495", "GnoOsTokenRedeemerTest_test_permitGnoToken_success": "91926", - "GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "250801", - "GnoOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "138041" + "GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "252803", + "GnoOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "137991" } \ No newline at end of file diff --git a/snapshots/GnoOsTokenVaultEscrowTest.json b/snapshots/GnoOsTokenVaultEscrowTest.json index 8f353a4e..7e35444f 100644 --- a/snapshots/GnoOsTokenVaultEscrowTest.json +++ b/snapshots/GnoOsTokenVaultEscrowTest.json @@ -1,5 +1,5 @@ { "GnoOsTokenVaultEscrowTest_test_transferAssets_claim": "140340", - "GnoOsTokenVaultEscrowTest_test_transferAssets_process": "873932", + "GnoOsTokenVaultEscrowTest_test_transferAssets_process": "893932", "GnoOsTokenVaultEscrowTest_test_transferAssets_transfer": "163157" } \ No newline at end of file diff --git a/snapshots/GnoPrivErc20VaultTest.json b/snapshots/GnoPrivErc20VaultTest.json index 381fca97..89d1f7cb 100644 --- a/snapshots/GnoPrivErc20VaultTest.json +++ b/snapshots/GnoPrivErc20VaultTest.json @@ -1,7 +1,7 @@ { - "GnoPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "156017", + "GnoPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "156037", "GnoPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161543", - "GnoPrivErc20VaultTest_test_deploysCorrectly": "953717", + "GnoPrivErc20VaultTest_test_deploysCorrectly": "953806", "GnoPrivErc20VaultTest_test_transfer": "59709", - "GnoPrivErc20VaultTest_test_upgradesCorrectly": "335246" + "GnoPrivErc20VaultTest_test_upgradesCorrectly": "340352" } \ No newline at end of file diff --git a/snapshots/GnoPrivMetaVaultTest.json b/snapshots/GnoPrivMetaVaultTest.json new file mode 100644 index 00000000..433487f0 --- /dev/null +++ b/snapshots/GnoPrivMetaVaultTest.json @@ -0,0 +1,8 @@ +{ + "GnoPrivMetaVaultTest_test_canDepositAsWhitelistedUser": "103337", + "GnoPrivMetaVaultTest_test_canMintOsTokenAsWhitelistedUser": "161126", + "GnoPrivMetaVaultTest_test_depositToSubVaultsWorksWithWhitelistedUser": "329543", + "GnoPrivMetaVaultTest_test_enterExitQueueWorksForWhitelistedUserAfterRemoval": "84646", + "GnoPrivMetaVaultTest_test_setWhitelister": "36586", + "GnoPrivMetaVaultTest_test_updateWhitelist": "54477" +} \ No newline at end of file diff --git a/snapshots/GnoPrivVaultTest.json b/snapshots/GnoPrivVaultTest.json index 6a30f899..09e2f1da 100644 --- a/snapshots/GnoPrivVaultTest.json +++ b/snapshots/GnoPrivVaultTest.json @@ -1,8 +1,8 @@ { - "GnoPrivVaultTest_test_canDepositAsWhitelistedUser": "154120", + "GnoPrivVaultTest_test_canDepositAsWhitelistedUser": "154140", "GnoPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161480", - "GnoPrivVaultTest_test_deploysCorrectly": "702342", + "GnoPrivVaultTest_test_deploysCorrectly": "702431", "GnoPrivVaultTest_test_setWhitelister": "36608", "GnoPrivVaultTest_test_updateWhitelist": "54521", - "GnoPrivVaultTest_test_upgradesCorrectly": "334541" + "GnoPrivVaultTest_test_upgradesCorrectly": "339647" } \ No newline at end of file diff --git a/snapshots/GnoRewardSplitterTest.json b/snapshots/GnoRewardSplitterTest.json index 31c8eead..28733f70 100644 --- a/snapshots/GnoRewardSplitterTest.json +++ b/snapshots/GnoRewardSplitterTest.json @@ -1,14 +1,14 @@ { - "GnoRewardSplitter_claimExitedAssets": "92309", - "GnoRewardSplitter_claimExitedAssetsOnBehalf": "806894", - "GnoRewardSplitter_claimVaultTokens": "838126", - "GnoRewardSplitter_decreaseShares": "96041", - "GnoRewardSplitter_enterExitQueue": "184229", - "GnoRewardSplitter_enterExitQueueMaxWithdrawal": "128900", - "GnoRewardSplitter_enterExitQueueOnBehalf": "1011528", - "GnoRewardSplitter_increaseShares": "70672", + "GnoRewardSplitter_claimExitedAssets": "94809", + "GnoRewardSplitter_claimExitedAssetsOnBehalf": "824394", + "GnoRewardSplitter_claimVaultTokens": "855626", + "GnoRewardSplitter_decreaseShares": "101041", + "GnoRewardSplitter_enterExitQueue": "186729", + "GnoRewardSplitter_enterExitQueueMaxWithdrawal": "131400", + "GnoRewardSplitter_enterExitQueueOnBehalf": "1029028", + "GnoRewardSplitter_increaseShares": "73172", "GnoRewardSplitter_setClaimer": "66291", "GnoRewardSplitter_syncRewards": "77704", - "GnoRewardSplitter_syncRewardsDetailed": "75204", + "GnoRewardSplitter_syncRewardsDetailed": "77704", "GnoRewardSplitter_updateVaultState": "165440" } \ No newline at end of file diff --git a/snapshots/GnoVaultExitQueueTest.json b/snapshots/GnoVaultExitQueueTest.json index c8efe3e0..024f3091 100644 --- a/snapshots/GnoVaultExitQueueTest.json +++ b/snapshots/GnoVaultExitQueueTest.json @@ -1,7 +1,7 @@ { "GnoVaultExitQueueTest_test_ExitingAssetsPenalized_event": "194845", - "GnoVaultExitQueueTest_test_claim_position1_after_upgrade": "6621623", - "GnoVaultExitQueueTest_test_claim_position2_before_upgrade": "123260", - "GnoVaultExitQueueTest_test_claim_position3_after_upgrade": "160251", - "GnoVaultExitQueueTest_test_claim_position4_after_upgrade": "155429" + "GnoVaultExitQueueTest_test_claim_position1_after_upgrade": "6654229", + "GnoVaultExitQueueTest_test_claim_position2_before_upgrade": "125766", + "GnoVaultExitQueueTest_test_claim_position3_after_upgrade": "165242", + "GnoVaultExitQueueTest_test_claim_position4_after_upgrade": "160429" } \ No newline at end of file diff --git a/snapshots/GnoVaultTest.json b/snapshots/GnoVaultTest.json index 121303d3..0a898884 100644 --- a/snapshots/GnoVaultTest.json +++ b/snapshots/GnoVaultTest.json @@ -1,7 +1,7 @@ { - "GnoVaultTest_test_deploysCorrectly": "678256", - "GnoVaultTest_test_exitQueue_works": "96643", - "GnoVaultTest_test_upgradesCorrectly": "334443", + "GnoVaultTest_test_deploysCorrectly": "678345", + "GnoVaultTest_test_exitQueue_works": "99131", + "GnoVaultTest_test_upgradesCorrectly": "339549", "GnoVaultTest_test_withdrawValidator_unknown": "55950", "GnoVaultTest_test_withdrawValidator_validatorsManager": "74091" } \ No newline at end of file diff --git a/snapshots/KeeperOraclesTest.json b/snapshots/KeeperOraclesTest.json index a1d30fcf..4ded9d36 100644 --- a/snapshots/KeeperOraclesTest.json +++ b/snapshots/KeeperOraclesTest.json @@ -1,12 +1,12 @@ { - "KeeperOraclesTest_test_addOracle_alreadyAdded": "28022", - "KeeperOraclesTest_test_addOracle_maxOraclesExceeded": "33511", - "KeeperOraclesTest_test_addOracle_onlyOwner": "32780", - "KeeperOraclesTest_test_addOracle_success": "55972", - "KeeperOraclesTest_test_removeOracle_alreadyRemoved": "32141", - "KeeperOraclesTest_test_removeOracle_onlyOwner": "26396", - "KeeperOraclesTest_test_removeOracle_success": "34154", + "KeeperOraclesTest_test_addOracle_alreadyAdded": "28034", + "KeeperOraclesTest_test_addOracle_maxOraclesExceeded": "36011", + "KeeperOraclesTest_test_addOracle_onlyOwner": "32792", + "KeeperOraclesTest_test_addOracle_success": "58484", + "KeeperOraclesTest_test_removeOracle_alreadyRemoved": "34653", + "KeeperOraclesTest_test_removeOracle_onlyOwner": "26408", + "KeeperOraclesTest_test_removeOracle_success": "36666", "KeeperOraclesTest_test_updateConfig_onlyOwner": "31726", "KeeperOraclesTest_test_updateConfig_success": "32787", - "KeeperOraclesTest_test_verifySignatures_throughKeeperRewards": "234771" + "KeeperOraclesTest_test_verifySignatures_throughKeeperRewards": "242271" } \ No newline at end of file diff --git a/snapshots/KeeperRewardsTest.json b/snapshots/KeeperRewardsTest.json index 9192df01..78e34a09 100644 --- a/snapshots/KeeperRewardsTest.json +++ b/snapshots/KeeperRewardsTest.json @@ -1,20 +1,20 @@ { "KeeperRewardsTest_test_canHarvest": "12938", "KeeperRewardsTest_test_harvest": "130843", - "KeeperRewardsTest_test_harvestWithPenalties": "92536", - "KeeperRewardsTest_test_harvest_alreadyHarvested": "47972", + "KeeperRewardsTest_test_harvestWithPenalties": "95048", + "KeeperRewardsTest_test_harvest_alreadyHarvested": "50472", "KeeperRewardsTest_test_harvest_invalidProof": "49041", "KeeperRewardsTest_test_harvest_invalidRewardsRoot": "49601", "KeeperRewardsTest_test_harvest_nonVault": "35559", "KeeperRewardsTest_test_isCollateralized": "10123", "KeeperRewardsTest_test_isHarvestRequired": "12620", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_1": "135325", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_2": "581737", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_3": "581747", - "KeeperRewardsTest_test_setRewardsMinOracles": "35248", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_1": "135313", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_2": "606725", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_3": "606771", + "KeeperRewardsTest_test_setRewardsMinOracles": "37748", "KeeperRewardsTest_test_setRewardsMinOracles_tooMany": "29542", - "KeeperRewardsTest_test_setRewardsMinOracles_zero": "27531", - "KeeperRewardsTest_test_updateRewards": "98923", - "KeeperRewardsTest_test_updateRewards_invalidAvgRewardPerSecond": "30759", - "KeeperRewardsTest_test_updateRewards_tooEarly": "30750" + "KeeperRewardsTest_test_setRewardsMinOracles_zero": "30031", + "KeeperRewardsTest_test_updateRewards": "101423", + "KeeperRewardsTest_test_updateRewards_invalidAvgRewardPerSecond": "33259", + "KeeperRewardsTest_test_updateRewards_tooEarly": "33250" } \ No newline at end of file diff --git a/snapshots/KeeperValidatorsTest.json b/snapshots/KeeperValidatorsTest.json index 14d1236d..30f8465b 100644 --- a/snapshots/KeeperValidatorsTest.json +++ b/snapshots/KeeperValidatorsTest.json @@ -1,15 +1,15 @@ { - "KeeperValidatorsTest_test_approveValidators_accessDenied": "139566", + "KeeperValidatorsTest_test_approveValidators_accessDenied": "139546", "KeeperValidatorsTest_test_approveValidators_invalidDeadline": "31828", - "KeeperValidatorsTest_test_approveValidators_invalidRegistry": "134465", - "KeeperValidatorsTest_test_approveValidators_success": "179045", - "KeeperValidatorsTest_test_setValidatorsMinOracles_success": "35099", + "KeeperValidatorsTest_test_approveValidators_invalidRegistry": "134445", + "KeeperValidatorsTest_test_approveValidators_success": "179025", + "KeeperValidatorsTest_test_setValidatorsMinOracles_success": "37599", "KeeperValidatorsTest_test_setValidatorsMinOracles_tooHigh": "29460", - "KeeperValidatorsTest_test_setValidatorsMinOracles_unauthorized": "27745", - "KeeperValidatorsTest_test_setValidatorsMinOracles_zero": "27382", - "KeeperValidatorsTest_test_updateExitSignatures_duplicateUpdate": "45635", - "KeeperValidatorsTest_test_updateExitSignatures_expiredDeadline": "34338", + "KeeperValidatorsTest_test_setValidatorsMinOracles_unauthorized": "30245", + "KeeperValidatorsTest_test_setValidatorsMinOracles_zero": "29882", + "KeeperValidatorsTest_test_updateExitSignatures_duplicateUpdate": "45623", + "KeeperValidatorsTest_test_updateExitSignatures_expiredDeadline": "36838", "KeeperValidatorsTest_test_updateExitSignatures_invalidVault": "31986", - "KeeperValidatorsTest_test_updateExitSignatures_notCollateralized": "34313", - "KeeperValidatorsTest_test_updateExitSignatures_success": "68010" + "KeeperValidatorsTest_test_updateExitSignatures_notCollateralized": "34301", + "KeeperValidatorsTest_test_updateExitSignatures_success": "70498" } \ No newline at end of file diff --git a/snapshots/OsTokenConfigTest.json b/snapshots/OsTokenConfigTest.json index d687b7ca..e072ef42 100644 --- a/snapshots/OsTokenConfigTest.json +++ b/snapshots/OsTokenConfigTest.json @@ -1,7 +1,7 @@ { - "OsTokenConfigForkTest_test_setRedeemer": "32972", - "OsTokenConfigForkTest_test_setRedeemer_notOwner": "29710", - "OsTokenConfigForkTest_test_setRedeemer_sameValue": "29381", + "OsTokenConfigForkTest_test_setRedeemer": "35472", + "OsTokenConfigForkTest_test_setRedeemer_notOwner": "32210", + "OsTokenConfigForkTest_test_setRedeemer_sameValue": "31881", "OsTokenConfigForkTest_test_updateConfig_disabledLiquidations": "56787", "OsTokenConfigForkTest_test_updateConfig_forVault": "55186", "OsTokenConfigForkTest_test_updateConfig_invalidDisabledLiquidations": "33162", @@ -11,7 +11,7 @@ "OsTokenConfigForkTest_test_updateConfig_invalidLiqThresholdPercent_tooHigh": "28640", "OsTokenConfigForkTest_test_updateConfig_invalidLiqThresholdPercent_zero": "33104", "OsTokenConfigForkTest_test_updateConfig_invalidLtvPercent_tooHigh": "28671", - "OsTokenConfigForkTest_test_updateConfig_invalidLtvPercent_zero": "30479", + "OsTokenConfigForkTest_test_updateConfig_invalidLtvPercent_zero": "32979", "OsTokenConfigForkTest_test_updateConfig_notOwner": "33393", "OsTokenConfigTest_test_updateDefaultConfig_success": "37692" } \ No newline at end of file diff --git a/snapshots/OsTokenTest.json b/snapshots/OsTokenTest.json index 95b2c256..7291565c 100644 --- a/snapshots/OsTokenTest.json +++ b/snapshots/OsTokenTest.json @@ -1,19 +1,19 @@ { - "OsTokenTest_test_burn": "36742", + "OsTokenTest_test_burn": "39242", "OsTokenTest_test_burn_onlyController": "27956", - "OsTokenTest_test_controllerIntegration_burn": "46843", - "OsTokenTest_test_controllerIntegration_mint": "105994", - "OsTokenTest_test_erc20_transfer": "54454", - "OsTokenTest_test_erc20_transferFrom": "112658", + "OsTokenTest_test_controllerIntegration_burn": "51843", + "OsTokenTest_test_controllerIntegration_mint": "106082", + "OsTokenTest_test_erc20_transfer": "54466", + "OsTokenTest_test_erc20_transferFrom": "117670", "OsTokenTest_test_fullDepositFlow": "197995", - "OsTokenTest_test_mint": "58400", + "OsTokenTest_test_mint": "60900", "OsTokenTest_test_mint_onlyController": "30281", - "OsTokenTest_test_permit": "77632", + "OsTokenTest_test_permit": "80144", "OsTokenTest_test_permit_expiredDeadline": "32235", - "OsTokenTest_test_permit_invalidSignature": "53951", + "OsTokenTest_test_permit_invalidSignature": "56463", "OsTokenTest_test_permit_zeroAddress": "56033", - "OsTokenTest_test_setController_add": "53103", + "OsTokenTest_test_setController_add": "55603", "OsTokenTest_test_setController_onlyOwner": "32836", - "OsTokenTest_test_setController_remove": "39137", - "OsTokenTest_test_setController_zeroAddress": "27570" + "OsTokenTest_test_setController_remove": "41637", + "OsTokenTest_test_setController_zeroAddress": "30070" } \ No newline at end of file diff --git a/snapshots/VaultEnterExitTest.json b/snapshots/VaultEnterExitTest.json index 7024d403..e2c63547 100644 --- a/snapshots/VaultEnterExitTest.json +++ b/snapshots/VaultEnterExitTest.json @@ -1,25 +1,25 @@ { "VaultEnterExitTest_test_calculateExitedAssets_invalidPosition": "18600", - "VaultEnterExitTest_test_claimExitedAssets": "730355", - "VaultEnterExitTest_test_claimExitedAssets_insufficientDelay": "41424", + "VaultEnterExitTest_test_claimExitedAssets": "752879", + "VaultEnterExitTest_test_claimExitedAssets_insufficientDelay": "43924", "VaultEnterExitTest_test_claimExitedAssets_invalidCheckpoint": "36507", "VaultEnterExitTest_test_deposit_exceedingCapacity": "47943", - "VaultEnterExitTest_test_deposit_success_basic": "80660", - "VaultEnterExitTest_test_deposit_success_differentReceiver": "80563", + "VaultEnterExitTest_test_deposit_success_basic": "83160", + "VaultEnterExitTest_test_deposit_success_differentReceiver": "83051", "VaultEnterExitTest_test_deposit_success_multipleDeposits": "57463", - "VaultEnterExitTest_test_deposit_success_receiveFunction": "77118", - "VaultEnterExitTest_test_deposit_success_withReferrer": "76785", + "VaultEnterExitTest_test_deposit_success_receiveFunction": "79618", + "VaultEnterExitTest_test_deposit_success_withReferrer": "76797", "VaultEnterExitTest_test_deposit_zeroAddress": "49918", - "VaultEnterExitTest_test_deposit_zeroAmount": "43483", - "VaultEnterExitTest_test_enterExitQueue_afterValidatorExit": "91855", - "VaultEnterExitTest_test_enterExitQueue_basicFlow": "91843", - "VaultEnterExitTest_test_enterExitQueue_directRedemption": "81906", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_tooManyShares": "62322", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroAddress": "45832", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroShares": "31356", + "VaultEnterExitTest_test_deposit_zeroAmount": "43471", + "VaultEnterExitTest_test_enterExitQueue_afterValidatorExit": "94343", + "VaultEnterExitTest_test_enterExitQueue_basicFlow": "94331", + "VaultEnterExitTest_test_enterExitQueue_directRedemption": "84394", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_tooManyShares": "64810", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroAddress": "48332", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroShares": "31344", "VaultEnterExitTest_test_enterExitQueue_multiUser_sender2": "74995", - "VaultEnterExitTest_test_enterExitQueue_multiUser_user1": "91843", - "VaultEnterExitTest_test_enterExitQueue_multipleUpdates": "89746", - "VaultEnterExitTest_test_enterExitQueue_partialExit": "96643", + "VaultEnterExitTest_test_enterExitQueue_multiUser_user1": "94331", + "VaultEnterExitTest_test_enterExitQueue_multipleUpdates": "92234", + "VaultEnterExitTest_test_enterExitQueue_partialExit": "99131", "VaultEnterExitTest_test_rescueAssets": "46295" } \ No newline at end of file diff --git a/snapshots/VaultEthStakingTest.json b/snapshots/VaultEthStakingTest.json index dc29fcf5..cf1c5e76 100644 --- a/snapshots/VaultEthStakingTest.json +++ b/snapshots/VaultEthStakingTest.json @@ -1,18 +1,18 @@ { - "VaultEthStakingTest_test_deposit": "82825", - "VaultEthStakingTest_test_depositAndMintOsToken": "200035", + "VaultEthStakingTest_test_deposit": "85325", + "VaultEthStakingTest_test_depositAndMintOsToken": "200047", "VaultEthStakingTest_test_fundValidators_invalid": "57140", "VaultEthStakingTest_test_fundValidators_valid": "140479", "VaultEthStakingTest_test_harvestAssets": "130845", "VaultEthStakingTest_test_invalidSecurityDeposit": "308363", - "VaultEthStakingTest_test_receive": "75018", + "VaultEthStakingTest_test_receive": "77518", "VaultEthStakingTest_test_receiveFromMevEscrow_fail": "36570", "VaultEthStakingTest_test_receiveFromMevEscrow_success": "37826", - "VaultEthStakingTest_test_registerValidators_01prefix": "243841", - "VaultEthStakingTest_test_registerValidators_02prefix": "500994", - "VaultEthStakingTest_test_transferVaultAssets": "51751", - "VaultEthStakingTest_test_updateStateAndDeposit": "164173", - "VaultEthStakingTest_test_updateStateAndDepositAndMintOsToken": "254268", - "VaultEthStakingTest_test_validatorMinMaxEffectiveBalance": "199916", + "VaultEthStakingTest_test_registerValidators_01prefix": "244809", + "VaultEthStakingTest_test_registerValidators_02prefix": "500922", + "VaultEthStakingTest_test_transferVaultAssets": "54251", + "VaultEthStakingTest_test_updateStateAndDeposit": "166673", + "VaultEthStakingTest_test_updateStateAndDepositAndMintOsToken": "254280", + "VaultEthStakingTest_test_validatorMinMaxEffectiveBalance": "199908", "VaultEthStakingTest_test_withdrawValidator_fullFlow": "74114" } \ No newline at end of file diff --git a/snapshots/VaultFeeTest.json b/snapshots/VaultFeeTest.json index 50c82e89..399199f7 100644 --- a/snapshots/VaultFeeTest.json +++ b/snapshots/VaultFeeTest.json @@ -1,12 +1,12 @@ { - "VaultFeeTest_test_feeCollection": "121931", + "VaultFeeTest_test_feeCollection": "121919", "VaultFeeTest_test_feePercent_changeAffectsFutureRewards": "87733", "VaultFeeTest_test_setFeePercent_aboveMaximum": "40380", - "VaultFeeTest_test_setFeePercent_initialZeroToOne": "42312", + "VaultFeeTest_test_setFeePercent_initialZeroToOne": "44812", "VaultFeeTest_test_setFeePercent_maxIncrease": "38472", "VaultFeeTest_test_setFeePercent_notAdmin": "34556", "VaultFeeTest_test_setFeePercent_requiresHarvest": "42501", - "VaultFeeTest_test_setFeePercent_success": "44575", + "VaultFeeTest_test_setFeePercent_success": "47075", "VaultFeeTest_test_setFeePercent_tooSoon": "38137", "VaultFeeTest_test_setFeeRecipient_notAdmin": "36854", "VaultFeeTest_test_setFeeRecipient_requiresHarvest": "44796", diff --git a/snapshots/VaultGnoStakingTest.json b/snapshots/VaultGnoStakingTest.json index 1fc421b2..5f5bc848 100644 --- a/snapshots/VaultGnoStakingTest.json +++ b/snapshots/VaultGnoStakingTest.json @@ -2,12 +2,12 @@ "VaultGnoStakingCoverageTest_test_processTotalAssetsDelta_smallXdaiBalance": "175401", "VaultGnoStakingCoverageTest_test_registerValidator_topUp_invalid": "57128", "VaultGnoStakingCoverageTest_test_registerValidator_topUp_valid": "182150", - "VaultGnoStakingTest_test_deposit": "98637", + "VaultGnoStakingTest_test_deposit": "101137", "VaultGnoStakingTest_test_processTotalAssetsDelta": "303158", - "VaultGnoStakingTest_test_pullWithdrawals": "90421", + "VaultGnoStakingTest_test_pullWithdrawals": "92921", "VaultGnoStakingTest_test_receive_xDai": "213139", - "VaultGnoStakingTest_test_transferVaultAssets": "87452", - "VaultGnoStakingTest_test_vaultGnoStaking_init": "678256", + "VaultGnoStakingTest_test_transferVaultAssets": "89952", + "VaultGnoStakingTest_test_vaultGnoStaking_init": "678313", "VaultGnoStakingTest_test_withdrawValidator_fullFlow": "74091", "test_registerValidators_succeeds_0x01": "286528", "test_registerValidators_succeeds_0x02": "630112" diff --git a/snapshots/VaultOsTokenTest.json b/snapshots/VaultOsTokenTest.json index 8fb25fa6..44d8888e 100644 --- a/snapshots/VaultOsTokenTest.json +++ b/snapshots/VaultOsTokenTest.json @@ -1,27 +1,27 @@ { - "VaultOsTokenTest_test_burnOsToken_afterFeeSync": "73489", - "VaultOsTokenTest_test_burnOsToken_allShares": "68187", - "VaultOsTokenTest_test_burnOsToken_basic": "72987", - "VaultOsTokenTest_test_burnOsToken_exceedingShares": "48701", + "VaultOsTokenTest_test_burnOsToken_afterFeeSync": "76001", + "VaultOsTokenTest_test_burnOsToken_allShares": "70699", + "VaultOsTokenTest_test_burnOsToken_basic": "75499", + "VaultOsTokenTest_test_burnOsToken_exceedingShares": "48725", "VaultOsTokenTest_test_burnOsToken_improvesLTV": "70999", "VaultOsTokenTest_test_burnOsToken_invalidPosition": "66210", - "VaultOsTokenTest_test_burnOsToken_multipleBurns": "72987", + "VaultOsTokenTest_test_burnOsToken_multipleBurns": "75499", "VaultOsTokenTest_test_burnOsToken_zeroShares": "37846", "VaultOsTokenTest_test_enterExitQueue_ltvViolation": "118315", "VaultOsTokenTest_test_liquidateOsToken_basic": "120719", "VaultOsTokenTest_test_liquidateOsToken_bonus": "125219", "VaultOsTokenTest_test_liquidateOsToken_invalidReceivedAssets": "75759", - "VaultOsTokenTest_test_liquidateOsToken_liquidationDisabled": "69199", - "VaultOsTokenTest_test_liquidateOsToken_partialLiquidation": "122719", - "VaultOsTokenTest_test_mintOsToken_basic": "162098", - "VaultOsTokenTest_test_mintOsToken_feeSync": "105554", + "VaultOsTokenTest_test_liquidateOsToken_liquidationDisabled": "69187", + "VaultOsTokenTest_test_liquidateOsToken_partialLiquidation": "125219", + "VaultOsTokenTest_test_mintOsToken_basic": "162110", + "VaultOsTokenTest_test_mintOsToken_feeSync": "108078", "VaultOsTokenTest_test_mintOsToken_ltvValidation": "144971", - "VaultOsTokenTest_test_mintOsToken_maxAmount": "159979", - "VaultOsTokenTest_test_mintOsToken_multipleReceivers": "119965", - "VaultOsTokenTest_test_mintOsToken_notCollateralized": "41953", + "VaultOsTokenTest_test_mintOsToken_maxAmount": "162479", + "VaultOsTokenTest_test_mintOsToken_multipleReceivers": "119989", + "VaultOsTokenTest_test_mintOsToken_notCollateralized": "41965", "VaultOsTokenTest_test_mintOsToken_notHarvested": "47615", "VaultOsTokenTest_test_mintOsToken_repeatedMinting": "105670", - "VaultOsTokenTest_test_mintOsToken_zeroAddressReceiver": "86812", + "VaultOsTokenTest_test_mintOsToken_zeroAddressReceiver": "86836", "VaultOsTokenTest_test_mintOsToken_zeroShares": "89092", "VaultOsTokenTest_test_redeemOsToken_afterFeeSync": "134490", "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_fail": "50939", @@ -29,20 +29,20 @@ "VaultOsTokenTest_test_redeemOsToken_basic": "133988", "VaultOsTokenTest_test_redeemOsToken_fullPosition": "133988", "VaultOsTokenTest_test_redeemOsToken_goodHealthFactor": "134071", - "VaultOsTokenTest_test_redeemOsToken_insufficientShares": "112652", - "VaultOsTokenTest_test_redeemOsToken_nonExistentPosition": "87008", + "VaultOsTokenTest_test_redeemOsToken_insufficientShares": "112664", + "VaultOsTokenTest_test_redeemOsToken_nonExistentPosition": "86996", "VaultOsTokenTest_test_redeemOsToken_onlyRedeemer": "36360", - "VaultOsTokenTest_test_redeemOsToken_zeroAddressReceiver": "40673", - "VaultOsTokenTest_test_redeemOsToken_zeroShares": "90254", + "VaultOsTokenTest_test_redeemOsToken_zeroAddressReceiver": "40685", + "VaultOsTokenTest_test_redeemOsToken_zeroShares": "90242", "VaultOsTokenTest_test_redeemVsLiquidate": "134071", "VaultOsTokenTest_test_test_liquidateOsToken_notHarvested": "43548", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_basic": "165145", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_basic": "167657", "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_claim": "107765", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_maxAmount": "165157", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_moreThanOwned": "44795", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_maxAmount": "167657", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_moreThanOwned": "44819", "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_noPosition": "43502", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_notHarvested": "41330", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_partialTransfer": "173208", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process": "721172", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_notHarvested": "41342", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_partialTransfer": "173220", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process": "736184", "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_zeroShares": "47545" } \ No newline at end of file diff --git a/snapshots/VaultSubVaultsTest.json b/snapshots/VaultSubVaultsTest.json index 036ccd5b..47f4c6bb 100644 --- a/snapshots/VaultSubVaultsTest.json +++ b/snapshots/VaultSubVaultsTest.json @@ -1,20 +1,24 @@ { - "VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_success": "115287", - "VaultSubVaultsTest_test_addSubVault_success": "126277", - "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares": "414205", - "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_partiallyClaimsExitedAssets": "116896", - "VaultSubVaultsTest_test_depositToSubVaults_maxVaults": "3874609", - "VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults": "313640", - "VaultSubVaultsTest_test_depositToSubVaults_singleSubVault": "161455", - "VaultSubVaultsTest_test_depositToSubVaults_withMetaVaultSubVault": "368578", - "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_emptySubVault": "51327", - "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_withShares": "200486", - "VaultSubVaultsTest_test_setSubVaultsCurator_success": "51083", - "VaultSubVaultsTest_test_updateState_enterExitQueueMaxVaults": "7013881", - "VaultSubVaultsTest_test_updateState_withMetaVaultSubVault_success": "160403", - "test_depositToSubVaults_ejectingSubVault": "151889", - "test_ejectSubVault_emptySubVault": "61797", - "test_ejectSubVault_subVaultWithShares": "202108", - "test_updateState_newTotalAssets": "208042", - "test_updateState_unprocessedSubVaultExit": "198058" + "VaultSubVaultsTest_test_acceptMetaSubVault_success": "128444", + "VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_proposesMetaVault": "92907", + "VaultSubVaultsTest_test_addSubVault_success": "130867", + "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares": "417792", + "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_partiallyClaimsExitedAssets": "120643", + "VaultSubVaultsTest_test_depositToSubVaults_maxVaults": "3874692", + "VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults": "295050", + "VaultSubVaultsTest_test_depositToSubVaults_singleSubVault": "142644", + "VaultSubVaultsTest_test_depositToSubVaults_withMetaVaultSubVault": "371161", + "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_emptySubVault": "57635", + "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_withShares": "206794", + "VaultSubVaultsTest_test_rejectMetaSubVault_byAdmin_success": "42237", + "VaultSubVaultsTest_test_rejectMetaSubVault_byOwner_success": "50033", + "VaultSubVaultsTest_test_setSubVaultsCurator_success": "51010", + "VaultSubVaultsTest_test_updateState_enterExitQueueMaxVaults": "7041487", + "VaultSubVaultsTest_test_updateState_withMetaVaultSubVault_success": "169251", + "test_addSubVault_firstSubVault": "145160", + "test_depositToSubVaults_ejectingSubVault": "153972", + "test_ejectSubVault_emptySubVault": "66011", + "test_ejectSubVault_subVaultWithShares": "205950", + "test_updateState_newTotalAssets": "178910", + "test_updateState_unprocessedSubVaultExit": "204479" } \ No newline at end of file diff --git a/snapshots/VaultTokenTest.json b/snapshots/VaultTokenTest.json index c1bebe88..e738ddbc 100644 --- a/snapshots/VaultTokenTest.json +++ b/snapshots/VaultTokenTest.json @@ -1,22 +1,22 @@ { - "VaultTokenTest_test_approve": "56346", + "VaultTokenTest_test_approve": "58846", "VaultTokenTest_test_approveZeroAddress": "33703", - "VaultTokenTest_test_depositEmitsTransferEvent": "80993", - "VaultTokenTest_test_enterExitQueueEmitsTransferEvent": "94566", + "VaultTokenTest_test_depositEmitsTransferEvent": "83493", + "VaultTokenTest_test_enterExitQueueEmitsTransferEvent": "97066", "VaultTokenTest_test_invalidTokenMetaNameTooLong": "478037", "VaultTokenTest_test_invalidTokenMetaSymbolTooLong": "311105", - "VaultTokenTest_test_permit": "84690", - "VaultTokenTest_test_permitExpiredDeadline": "32817", - "VaultTokenTest_test_permitInvalidSigner": "61070", - "VaultTokenTest_test_permitZeroAddressSpender": "32351", - "VaultTokenTest_test_transfer": "63941", - "VaultTokenTest_test_transferFrom": "65201", + "VaultTokenTest_test_permit": "87202", + "VaultTokenTest_test_permitExpiredDeadline": "35305", + "VaultTokenTest_test_permitInvalidSigner": "63558", + "VaultTokenTest_test_permitZeroAddressSpender": "34863", + "VaultTokenTest_test_transfer": "66441", + "VaultTokenTest_test_transferFrom": "67701", "VaultTokenTest_test_transferFromMoreThanAllowance": "32480", "VaultTokenTest_test_transferFromMoreThanBalance": "37928", "VaultTokenTest_test_transferFromZeroAddress": "38535", - "VaultTokenTest_test_transferMoreThanBalance": "35862", - "VaultTokenTest_test_transferToZeroAddress": "31234", - "VaultTokenTest_test_transferWithOsTokenPosition": "94084", - "VaultTokenTest_test_unlimitedAllowance": "66861", - "VaultTokenTest_test_updateExitQueueBurnsShares": "146979" + "VaultTokenTest_test_transferMoreThanBalance": "38362", + "VaultTokenTest_test_transferToZeroAddress": "33734", + "VaultTokenTest_test_transferWithOsTokenPosition": "96584", + "VaultTokenTest_test_unlimitedAllowance": "69361", + "VaultTokenTest_test_updateExitQueueBurnsShares": "146991" } \ No newline at end of file diff --git a/snapshots/VaultValidatorsTest.json b/snapshots/VaultValidatorsTest.json index de12b7d6..3db88da5 100644 --- a/snapshots/VaultValidatorsTest.json +++ b/snapshots/VaultValidatorsTest.json @@ -1,43 +1,43 @@ { "VaultValidatorsTest_test_consolidateValidators_byManager": "79212", "VaultValidatorsTest_test_consolidateValidators_feeHandling": "86213", - "VaultValidatorsTest_test_consolidateValidators_invalidSignature": "69620", + "VaultValidatorsTest_test_consolidateValidators_invalidSignature": "72120", "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsEmpty": "55608", "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsLength": "57330", - "VaultValidatorsTest_test_consolidateValidators_multipleValidators": "99418", + "VaultValidatorsTest_test_consolidateValidators_multipleValidators": "99394", "VaultValidatorsTest_test_consolidateValidators_notManager": "57250", - "VaultValidatorsTest_test_consolidateValidators_untrackedDestination": "76208", - "VaultValidatorsTest_test_consolidateValidators_withOracleSignatures": "113484", - "VaultValidatorsTest_test_consolidateValidators_withSignature": "109193", + "VaultValidatorsTest_test_consolidateValidators_untrackedDestination": "78708", + "VaultValidatorsTest_test_consolidateValidators_withOracleSignatures": "115804", + "VaultValidatorsTest_test_consolidateValidators_withSignature": "111705", "VaultValidatorsTest_test_fundValidators_byManager": "105234", "VaultValidatorsTest_test_fundValidators_insufficientAssets": "103060", - "VaultValidatorsTest_test_fundValidators_invalidSignature": "61201", + "VaultValidatorsTest_test_fundValidators_invalidSignature": "63689", "VaultValidatorsTest_test_fundValidators_invalidValidators": "45735", - "VaultValidatorsTest_test_fundValidators_multipleValidators": "147775", - "VaultValidatorsTest_test_fundValidators_nonExistingValidator": "57128", + "VaultValidatorsTest_test_fundValidators_multipleValidators": "144799", + "VaultValidatorsTest_test_fundValidators_nonExistingValidator": "57140", "VaultValidatorsTest_test_fundValidators_notHarvested": "44394", "VaultValidatorsTest_test_fundValidators_notManager": "48806", "VaultValidatorsTest_test_fundValidators_v1Validators": "52712", - "VaultValidatorsTest_test_fundValidators_withSignature": "135199", - "VaultValidatorsTest_test_registerValidators_byManager": "266735", + "VaultValidatorsTest_test_fundValidators_withSignature": "137699", + "VaultValidatorsTest_test_registerValidators_byManager": "267691", "VaultValidatorsTest_test_registerValidators_insufficientAssets": "41930", - "VaultValidatorsTest_test_registerValidators_invalidSignature": "206444", - "VaultValidatorsTest_test_registerValidators_invalidValidatorLength": "194386", - "VaultValidatorsTest_test_registerValidators_invalidValidators": "188451", - "VaultValidatorsTest_test_registerValidators_multipleValidators": "303619", - "VaultValidatorsTest_test_registerValidators_nonceIncrement": "145899", - "VaultValidatorsTest_test_registerValidators_notHarvested": "170709", - "VaultValidatorsTest_test_registerValidators_notManager": "193605", - "VaultValidatorsTest_test_registerValidators_v1Validators": "241336", - "VaultValidatorsTest_test_registerValidators_v2Validators": "264235", - "VaultValidatorsTest_test_registerValidators_withSignature": "299199", - "VaultValidatorsTest_test_setValidatorsManager_valueNotChanged": "34429", + "VaultValidatorsTest_test_registerValidators_invalidSignature": "206436", + "VaultValidatorsTest_test_registerValidators_invalidValidatorLength": "194366", + "VaultValidatorsTest_test_registerValidators_invalidValidators": "188419", + "VaultValidatorsTest_test_registerValidators_multipleValidators": "304623", + "VaultValidatorsTest_test_registerValidators_nonceIncrement": "148369", + "VaultValidatorsTest_test_registerValidators_notHarvested": "170713", + "VaultValidatorsTest_test_registerValidators_notManager": "193585", + "VaultValidatorsTest_test_registerValidators_v1Validators": "244804", + "VaultValidatorsTest_test_registerValidators_v2Validators": "267691", + "VaultValidatorsTest_test_registerValidators_withSignature": "300155", + "VaultValidatorsTest_test_setValidatorsManager_valueNotChanged": "36929", "VaultValidatorsTest_test_withdrawValidators_byManager": "74102", "VaultValidatorsTest_test_withdrawValidators_feeHandling": "81103", - "VaultValidatorsTest_test_withdrawValidators_invalidSignature": "68361", + "VaultValidatorsTest_test_withdrawValidators_invalidSignature": "70861", "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsEmpty": "55082", "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsLength": "56493", "VaultValidatorsTest_test_withdrawValidators_multipleValidators": "89825", "VaultValidatorsTest_test_withdrawValidators_notAuthorized": "56024", - "VaultValidatorsTest_test_withdrawValidators_withSignature": "104039" + "VaultValidatorsTest_test_withdrawValidators_withSignature": "106539" } \ No newline at end of file diff --git a/snapshots/VaultVersionTest.json b/snapshots/VaultVersionTest.json index 9fcd9c3d..b0ac9537 100644 --- a/snapshots/VaultVersionTest.json +++ b/snapshots/VaultVersionTest.json @@ -1,12 +1,12 @@ { "VaultVersionTest_test_reinitializeFails": "30923", - "VaultVersionTest_test_upgradeMultipleSteps": "58533", - "VaultVersionTest_test_upgradeNonAdminFails": "38860", - "VaultVersionTest_test_upgradeToDifferentVaultIdFails": "42364", - "VaultVersionTest_test_upgradeToNextVersion": "83669", - "VaultVersionTest_test_upgradeToSameVersionFails": "34605", + "VaultVersionTest_test_upgradeMultipleSteps": "61033", + "VaultVersionTest_test_upgradeNonAdminFails": "38872", + "VaultVersionTest_test_upgradeToDifferentVaultIdFails": "42352", + "VaultVersionTest_test_upgradeToNextVersion": "83681", + "VaultVersionTest_test_upgradeToSameVersionFails": "37105", "VaultVersionTest_test_upgradeToSkipVersionFails": "43074", "VaultVersionTest_test_upgradeToUnapprovedImplementationFails": "46371", "VaultVersionTest_test_upgradeToZeroAddressFails": "36640", - "VaultVersionTest_test_upgradeWithInvalidCallDataFails": "60718" + "VaultVersionTest_test_upgradeWithInvalidCallDataFails": "60730" } \ No newline at end of file diff --git a/snapshots/VaultsRegistryTest.json b/snapshots/VaultsRegistryTest.json index 1e58d9a1..bcc1bfd3 100644 --- a/snapshots/VaultsRegistryTest.json +++ b/snapshots/VaultsRegistryTest.json @@ -8,13 +8,13 @@ "VaultsRegistryTest_test_addVaultImpl_notOwner": "32404", "VaultsRegistryTest_test_addVault_asOwner": "59391", "VaultsRegistryTest_test_addVault_notFactoryOrOwner": "33976", - "VaultsRegistryTest_test_initialize": "53876", + "VaultsRegistryTest_test_initialize": "56376", "VaultsRegistryTest_test_initialize_alreadyInitialized": "27356", - "VaultsRegistryTest_test_initialize_zeroAddress": "24983", - "VaultsRegistryTest_test_removeFactory": "32212", + "VaultsRegistryTest_test_initialize_zeroAddress": "27483", + "VaultsRegistryTest_test_removeFactory": "34712", "VaultsRegistryTest_test_removeFactory_alreadyRemoved": "34077", "VaultsRegistryTest_test_removeFactory_notOwner": "25794", - "VaultsRegistryTest_test_removeVaultImpl": "32163", + "VaultsRegistryTest_test_removeVaultImpl": "34663", "VaultsRegistryTest_test_removeVaultImpl_alreadyRemoved": "34044", "VaultsRegistryTest_test_removeVaultImpl_notOwner": "25750" } \ No newline at end of file diff --git a/test/EthMetaVault.t.sol b/test/EthMetaVault.t.sol index c84dc821..772e5279 100644 --- a/test/EthMetaVault.t.sol +++ b/test/EthMetaVault.t.sol @@ -1,14 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.22; -import {Test, stdStorage, StdStorage, console, Vm} from "forge-std/Test.sol"; +import {Test, console, Vm} from "forge-std/Test.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IEthMetaVault} from "../contracts/interfaces/IEthMetaVault.sol"; import {IEthVault} from "../contracts/interfaces/IEthVault.sol"; import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; import {IVaultState} from "../contracts/interfaces/IVaultState.sol"; import {IVaultSubVaults} from "../contracts/interfaces/IVaultSubVaults.sol"; import {IVaultEnterExit} from "../contracts/interfaces/IVaultEnterExit.sol"; +import {IVaultOsToken} from "../contracts/interfaces/IVaultOsToken.sol"; +import {ISubVaultsCurator} from "../contracts/interfaces/ISubVaultsCurator.sol"; import {Errors} from "../contracts/libraries/Errors.sol"; import {EthMetaVault} from "../contracts/vaults/ethereum/EthMetaVault.sol"; import {EthMetaVaultFactory} from "../contracts/vaults/ethereum/EthMetaVaultFactory.sol"; @@ -16,10 +19,9 @@ import {BalancedCurator} from "../contracts/curators/BalancedCurator.sol"; import {CuratorsRegistry} from "../contracts/curators/CuratorsRegistry.sol"; import {EthHelpers} from "./helpers/EthHelpers.sol"; import {IKeeperRewards} from "../contracts/interfaces/IKeeperRewards.sol"; +import {EthOsTokenRedeemer} from "../contracts/tokens/EthOsTokenRedeemer.sol"; contract EthMetaVaultTest is Test, EthHelpers { - using stdStorage for StdStorage; - bytes32 private constant exitQueueEnteredTopic = keccak256("ExitQueueEntered(address,address,uint256,uint256)"); address private constant FORK_META_VAULT = 0x34284C27A2304132aF751b0dEc5bBa2CF98eD039; @@ -562,20 +564,1208 @@ contract EthMetaVaultTest is Test, EthHelpers { assertEq(postTotalTickets, preUpgradeState.totalTickets, "Total tickets should be preserved"); } - function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { - bytes32[] memory emptyProof; - return - IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); + function test_calculateSubVaultsRedemptions_notHarvested() public { + // First deposit to meta vault and sub vaults to establish initial state + uint256 depositAmount = 10 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Increase keeper nonce by 2 to trigger NotHarvested + uint64 initialNonce = contracts.keeper.rewardsNonce(); + _setKeeperRewardsNonce(initialNonce + 2); + + // Verify state update is required + assertTrue(metaVault.isStateUpdateRequired(), "State update should be required"); + + // Try to call calculateSubVaultsRedemptions - should revert with NotHarvested + vm.expectRevert(Errors.NotHarvested.selector); + metaVault.calculateSubVaultsRedemptions(1 ether); + } + + function test_calculateSubVaultsRedemptions_withMetaSubVault() public { + // First, update main meta vault state to establish baseline nonce + _updateMetaVaultState(); + + // Get the current nonce that main meta vault is at + uint128 currentNonce = metaVault.subVaultsRewardsNonce(); + + // Create another meta vault to use as a sub vault + bytes memory metaInitParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: _balancedCurator, + capacity: type(uint256).max, + feePercent: 0, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + EthMetaVault subMetaVault = + EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, metaInitParams, false))); + + // Add a sub vault to the sub meta vault and collateralize it + address nestedSubVault = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), nestedSubVault); + + // Set the nested sub vault's nonce to match what the sub meta vault expects + _setVaultRewardsNonce(nestedSubVault, uint64(currentNonce)); + + vm.prank(admin); + subMetaVault.addSubVault(nestedSubVault); + + // Deposit to sub meta vault + vm.deal(admin, 50 ether); + vm.prank(admin); + subMetaVault.deposit{value: 20 ether}(admin, address(0)); + + // Update sub meta vault state to sync nonces - first increment the keeper + uint64 newNonce = uint64(currentNonce) + 1; + _setKeeperRewardsNonce(newNonce); + _setVaultRewardsNonce(nestedSubVault, newNonce); + + // Update all existing sub vaults of main meta vault too + for (uint256 i = 0; i < subVaults.length; i++) { + _setVaultRewardsNonce(subVaults[i], newNonce); + } + + // Update both meta vaults + subMetaVault.updateState(_getEmptyHarvestParams()); + metaVault.updateState(_getEmptyHarvestParams()); + + // Deposit to sub vaults + subMetaVault.depositToSubVaults(); + + // Propose adding meta vault as sub vault (requires approval) + vm.prank(admin); + metaVault.addSubVault(address(subMetaVault)); + + // Accept the meta sub vault (requires VaultsRegistry owner) + address registryOwner = contracts.vaultsRegistry.owner(); + vm.prank(registryOwner); + metaVault.acceptMetaSubVault(address(subMetaVault)); + + // Deposit to main meta vault + vm.prank(sender); + metaVault.deposit{value: 10 ether}(sender, referrer); + + // Update all vaults for next nonce + newNonce = newNonce + 1; + _setKeeperRewardsNonce(newNonce); + _setVaultRewardsNonce(nestedSubVault, newNonce); + for (uint256 i = 0; i < subVaults.length; i++) { + _setVaultRewardsNonce(subVaults[i], newNonce); + } + subMetaVault.updateState(_getEmptyHarvestParams()); + metaVault.updateState(_getEmptyHarvestParams()); + + // Deposit main meta vault assets to sub vaults to make withdrawable assets 0 + metaVault.depositToSubVaults(); + + // Set meta vault balance to cover unclaimed assets only (withdrawable = 0) + (, uint256 unclaimedAssets,,,) = metaVault.getExitQueueData(); + vm.deal(address(metaVault), unclaimedAssets); + + // Set sub meta vault balance to cover its unclaimed assets only + (, uint256 subMetaUnclaimedAssets,,,) = subMetaVault.getExitQueueData(); + vm.deal(address(subMetaVault), subMetaUnclaimedAssets); + + // Set all regular sub vaults to have 0 withdrawable assets + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 subVaultUnclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], subVaultUnclaimed); + } + + // Set nested sub vault to have 0 withdrawable assets + (, uint256 nestedUnclaimed,,,) = IVaultState(nestedSubVault).getExitQueueData(); + vm.deal(nestedSubVault, nestedUnclaimed); + + // Verify withdrawable assets are 0 + assertEq(metaVault.withdrawableAssets(), 0, "Meta vault withdrawable should be 0"); + assertEq(subMetaVault.withdrawableAssets(), 0, "Sub meta vault withdrawable should be 0"); + + // Test calculateSubVaultsRedemptions with meta sub vault present + uint256 assetsToRedeem = 5 ether; + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(assetsToRedeem); + + // Should return exit requests since withdrawable assets are 0 + assertGt(requests.length, 0, "Should return exit requests when no withdrawable assets"); + + // Calculate total requested assets + uint256 totalRequestedAssets; + for (uint256 i = 0; i < requests.length; i++) { + totalRequestedAssets += requests[i].assets; + + // If the request is for the sub meta vault, verify it can calculate its own redemptions + if (requests[i].vault == address(subMetaVault)) { + ISubVaultsCurator.ExitRequest[] memory subMetaRequests = + subMetaVault.calculateSubVaultsRedemptions(requests[i].assets); + + // Sub meta vault should also return exit requests from its nested sub vault + assertGt(subMetaRequests.length, 0, "Sub meta vault should return exit requests"); + + // Verify the nested sub vault is included in sub meta vault's requests + bool hasNestedSubVault = false; + for (uint256 j = 0; j < subMetaRequests.length; j++) { + if (subMetaRequests[j].vault == nestedSubVault) { + hasNestedSubVault = true; + break; + } + } + assertTrue(hasNestedSubVault, "Sub meta vault should request from nested sub vault"); + } + } + + // Total requested should cover the assets to redeem + assertGe(totalRequestedAssets, assetsToRedeem, "Total requested should cover redemption amount"); + } + + function test_calculateSubVaultsRedemptions_withEjectingSubVault() public { + // First deposit to meta vault and sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Start ejecting one of the sub vaults + address vaultToEject = subVaults[0]; + vm.prank(admin); + metaVault.ejectSubVault(vaultToEject); + + // Verify ejecting sub vault is set + assertEq(metaVault.ejectingSubVault(), vaultToEject, "Ejecting sub vault should be set"); + + // Update meta vault state after ejection + _updateMetaVaultState(); + + // Get withdrawable assets (should be 0 since all deposited to sub vaults) + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + + // Get ejecting sub vault assets - these are counted as available in calculateSubVaultsRedemptions + IVaultSubVaults.SubVaultState memory ejectingState = metaVault.subVaultsStates(vaultToEject); + uint256 ejectingAssets = 0; + if (ejectingState.queuedShares > 0) { + ejectingAssets = IVaultState(vaultToEject).convertToAssets(ejectingState.queuedShares); + } + + // Request redemption for more than withdrawable + ejecting assets to force requests from other sub vaults + uint256 assetsToRedeem = withdrawableAssets + ejectingAssets + 5 ether; + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(assetsToRedeem); + + // Should return exit requests since we're requesting more than available + assertGt(requests.length, 0, "Should return exit requests"); + + // Calculate total assets from redemption requests + uint256 totalRequestedAssets; + for (uint256 i = 0; i < requests.length; i++) { + totalRequestedAssets += requests[i].assets; + } + + // Check that total requests + ejecting assets + withdrawable assets cover assets to redeem + uint256 totalAvailable = totalRequestedAssets + ejectingAssets + withdrawableAssets; + assertGe(totalAvailable, assetsToRedeem, "Total available should cover assets to redeem"); + + // Verify ejecting sub vault has 0 assets in redemption requests + // (ejecting sub vault is included by the curator but with 0 assets since its shares are in exit queue) + bool ejectingVaultFound = false; + for (uint256 i = 0; i < requests.length; i++) { + if (requests[i].vault == vaultToEject) { + ejectingVaultFound = true; + assertEq(requests[i].assets, 0, "Ejecting sub vault should have 0 assets in redemption requests"); + break; + } + } + assertTrue(ejectingVaultFound, "Ejecting sub vault should be in redemption requests"); + } + + function test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets() public { + // First deposit to meta vault and sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Get withdrawable assets (should be 0 since all deposited to sub vaults) + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + assertEq(withdrawableAssets, 0, "Withdrawable assets should be 0 after deposit to sub vaults"); + + // Request more assets than withdrawable + uint256 assetsToRedeem = 10 ether; + _startSnapshotGas("EthMetaVaultTest_test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets"); + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(assetsToRedeem); + _stopSnapshotGas(); + + // Should return exit requests from sub vaults + assertGt(requests.length, 0, "Should return exit requests when withdrawable assets insufficient"); + + // Calculate total requested assets + uint256 totalRequestedAssets; + for (uint256 i = 0; i < requests.length; i++) { + totalRequestedAssets += requests[i].assets; + } + + // Total requested should cover the assets to redeem + assertGe(totalRequestedAssets, assetsToRedeem, "Total requested should cover redemption"); + } + + function test_calculateSubVaultsRedemptions_exactWithdrawableAssets() public { + // Deposit to meta vault but don't deposit to sub vaults + uint256 depositAmount = 10 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Update meta vault state (needed for harvested check) + _updateMetaVaultState(); + + // Get withdrawable assets (may include small amounts from fork state) + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + assertGe(withdrawableAssets, depositAmount, "Withdrawable assets should be at least the deposit amount"); + + // Request exactly the withdrawable assets + _startSnapshotGas("EthMetaVaultTest_test_calculateSubVaultsRedemptions_exactWithdrawableAssets"); + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(withdrawableAssets); + _stopSnapshotGas(); + + // Should return empty array since withdrawable assets exactly match + assertEq(requests.length, 0, "Should return empty requests when withdrawable equals redemption amount"); + } + + function test_calculateSubVaultsRedemptions_success() public { + // Deposit to meta vault and sub vaults + uint256 depositAmount = 50 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Get withdrawable assets + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + + // Request redemption exceeding withdrawable assets + uint256 assetsToRedeem = withdrawableAssets + 20 ether; + + _startSnapshotGas("EthMetaVaultTest_test_calculateSubVaultsRedemptions_success"); + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(assetsToRedeem); + _stopSnapshotGas(); + + // Should return exit requests from sub vaults + assertGt(requests.length, 0, "Should return exit requests"); + + // Verify all requests are for valid sub vaults + for (uint256 i = 0; i < requests.length; i++) { + bool isValidSubVault = false; + for (uint256 j = 0; j < subVaults.length; j++) { + if (requests[i].vault == subVaults[j]) { + isValidSubVault = true; + break; + } + } + assertTrue(isValidSubVault, "Exit request should be for a valid sub vault"); + assertGt(requests[i].assets, 0, "Exit request assets should be greater than 0"); + } + + // Calculate total requested assets + uint256 totalRequestedAssets; + for (uint256 i = 0; i < requests.length; i++) { + totalRequestedAssets += requests[i].assets; + } + + // Total requested should cover the assets to redeem minus withdrawable + uint256 expectedMinAssets = assetsToRedeem - withdrawableAssets; + assertGe(totalRequestedAssets, expectedMinAssets, "Total requested should cover needed redemption"); + } + + function test_calculateSubVaultsRedemptions_zeroAssets() public { + // Deposit to meta vault + uint256 depositAmount = 10 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Update meta vault state + _updateMetaVaultState(); + + // Request zero assets - should return empty array + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(0); + assertEq(requests.length, 0, "Should return empty requests for zero assets"); + } + + function test_calculateSubVaultsRedemptions_lessThanWithdrawable() public { + // Deposit to meta vault but don't deposit to sub vaults + uint256 depositAmount = 10 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Update meta vault state + _updateMetaVaultState(); + + // Request less than withdrawable + uint256 assetsToRedeem = 5 ether; + ISubVaultsCurator.ExitRequest[] memory requests = metaVault.calculateSubVaultsRedemptions(assetsToRedeem); + + // Should return empty array since withdrawable covers it + assertEq(requests.length, 0, "Should return empty requests when less than withdrawable"); + } + + function test_redeemSubVaultsAssets_accessControl() public { + // Only the redeemer can call redeemSubVaultsAssets + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.redeemSubVaultsAssets(1 ether); + + vm.prank(admin); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.redeemSubVaultsAssets(1 ether); + } + + function test_redeemSubVaultsAssets_zeroAssets() public { + // Get the redeemer address + address redeemer = contracts.osTokenConfig.redeemer(); + + // Try to redeem zero assets + vm.prank(redeemer); + vm.expectRevert(Errors.InvalidAssets.selector); + metaVault.redeemSubVaultsAssets(0); + } + + function test_redeemSubVaultsAssets_noRedeemRequests() public { + // Deposit to meta vault but don't deposit to sub vaults (keep assets withdrawable) + uint256 depositAmount = 10 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Update meta vault state + _updateMetaVaultState(); + + // Verify withdrawable assets cover the redemption amount + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + assertGe(withdrawableAssets, depositAmount, "Withdrawable should cover deposit"); + + // Get the redeemer address + address redeemer = contracts.osTokenConfig.redeemer(); + + // Record meta vault balance before + uint256 metaVaultBalanceBefore = address(metaVault).balance; + + // Call redeemSubVaultsAssets with amount less than withdrawable + // Should return 0 since no redeem requests needed (withdrawable covers it) + vm.prank(redeemer); + _startSnapshotGas("EthMetaVaultTest_test_redeemSubVaultsAssets_noRedeemRequests"); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(depositAmount); + _stopSnapshotGas(); + + // Verify meta vault balance unchanged (no redemptions occurred) + assertEq(address(metaVault).balance, metaVaultBalanceBefore, "Meta vault balance should not change"); + + // Should return 0 since withdrawable assets cover the redemption + assertEq(totalRedeemed, 0, "Should return 0 when no redeem requests needed"); + } + + function test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets() public { + // Deploy and set EthOsTokenRedeemer as the redeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit to meta vault and sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Verify withdrawable assets are 0 (all deposited to sub vaults) + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + assertEq(withdrawableAssets, 0, "Withdrawable should be 0 after deposit to sub vaults"); + + // Add extra ETH to sub vaults to ensure they have withdrawable assets + // (unclaimed assets + extra balance = withdrawable > 0) + uint256 totalSubVaultWithdrawable = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + (depositAmount / subVaults.length / 2)); + totalSubVaultWithdrawable += IVaultState(subVaults[i]).withdrawableAssets(); + } + assertGt(totalSubVaultWithdrawable, 0, "Sub vaults should have withdrawable assets"); + assertGt(depositAmount, totalSubVaultWithdrawable, "Deposit should exceed sub vaults' withdrawable"); + + // Request to redeem more than what's withdrawable from sub vaults + // Using 2x withdrawable to test the "exceed withdrawable" scenario + // without hitting rounding issues with totalAssets() + uint256 assetsToRedeem = 2 * totalSubVaultWithdrawable; + + // Record meta vault balance before + uint256 metaVaultBalanceBefore = address(metaVault).balance; + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + _startSnapshotGas("EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets"); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(assetsToRedeem); + _stopSnapshotGas(); + + // Verify redemption occurred - total redeemed should equal total sub vault withdrawable + assertLe(totalRedeemed, totalSubVaultWithdrawable, "Total redeemed should not exceed sub vaults' withdrawable"); + + // Verify meta vault received the redeemed assets + assertEq( + address(metaVault).balance, + metaVaultBalanceBefore + totalRedeemed, + "Meta vault should receive redeemed assets" + ); + } + + function test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets() public { + // Deploy and set EthOsTokenRedeemer as the redeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit to meta vault and sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Give sub vaults significantly more withdrawable assets than we will request + uint256 assetsToRedeem = 5 ether; + uint256 subVaultExtraBalance = 15 ether; // Each sub vault gets 15 ETH extra + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + subVaultExtraBalance); + } + + // Calculate total sub vault withdrawable assets + uint256 totalSubVaultWithdrawable = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalSubVaultWithdrawable += IVaultState(subVaults[i]).withdrawableAssets(); + } + + // Verify sub vaults have significantly more withdrawable than we're requesting + assertGt(totalSubVaultWithdrawable, assetsToRedeem, "Sub vaults should have more withdrawable than requested"); + assertGt( + totalSubVaultWithdrawable, assetsToRedeem * 2, "Sub vaults should have at least 2x the requested amount" + ); + + // Record meta vault balance before + uint256 metaVaultBalanceBefore = address(metaVault).balance; + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + _startSnapshotGas("EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets"); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(assetsToRedeem); + _stopSnapshotGas(); + + // Verify redemption occurred and matches requested amount (not more) + assertGt(totalRedeemed, 0, "Should redeem assets from sub vaults"); + assertApproxEqAbs(totalRedeemed, assetsToRedeem, 10, "Redeemed should match requested amount"); + assertLt(totalRedeemed, totalSubVaultWithdrawable, "Redeemed should be less than total withdrawable"); + + // Verify meta vault received the redeemed assets + assertEq( + address(metaVault).balance, + metaVaultBalanceBefore + totalRedeemed, + "Meta vault should receive redeemed assets" + ); + + // Verify sub vaults still have remaining withdrawable assets + uint256 totalSubVaultWithdrawableAfter = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalSubVaultWithdrawableAfter += IVaultState(subVaults[i]).withdrawableAssets(); + } + assertApproxEqAbs( + totalSubVaultWithdrawableAfter, + totalSubVaultWithdrawable - totalRedeemed, + 10, + "Sub vaults should have reduced withdrawable by redeemed amount" + ); + } + + function test_redeemSubVaultsAssets_success() public { + // Deploy and set EthOsTokenRedeemer as the redeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit to meta vault and sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Verify withdrawable assets are 0 (all deposited to sub vaults) + uint256 withdrawableAssets = metaVault.withdrawableAssets(); + assertEq(withdrawableAssets, 0, "Withdrawable should be 0 after deposit to sub vaults"); + + // Give sub vaults sufficient withdrawable assets to cover redemption + uint256 assetsToRedeem = 10 ether; + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + // Give each sub vault more than enough to cover its portion + vm.deal(subVaults[i], unclaimed + (assetsToRedeem / subVaults.length) + 1 ether); + } + + // Calculate total sub vault withdrawable assets + uint256 totalSubVaultWithdrawable = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalSubVaultWithdrawable += IVaultState(subVaults[i]).withdrawableAssets(); + } + assertGe(totalSubVaultWithdrawable, assetsToRedeem, "Sub vaults should have sufficient withdrawable assets"); + + // Record meta vault balance before + uint256 metaVaultBalanceBefore = address(metaVault).balance; + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + _startSnapshotGas("EthMetaVaultTest_test_redeemSubVaultsAssets_success"); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(assetsToRedeem); + _stopSnapshotGas(); + + // Verify redemption occurred + assertGt(totalRedeemed, 0, "Should redeem assets from sub vaults"); + assertApproxEqAbs(totalRedeemed, assetsToRedeem, 10, "Redeemed amount should match requested"); + + // Verify meta vault received the redeemed assets + assertEq( + address(metaVault).balance, + metaVaultBalanceBefore + totalRedeemed, + "Meta vault should receive redeemed assets" + ); + } + + function test_redeemSubVaultsAssets_noRoundingErrors() public { + // Deploy and set EthOsTokenRedeemer as the redeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit to meta vault and sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Update meta vault state + _updateMetaVaultState(); + + // Give sub vaults sufficient withdrawable assets + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + 15 ether); + } + + // Test with an odd amount that could cause rounding issues + uint256 assetsToRedeem = 7.123456789012345678 ether; + + // Record balances before + uint256 metaVaultBalanceBefore = address(metaVault).balance; + uint256 metaVaultTotalAssetsBefore = metaVault.totalAssets(); + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + _startSnapshotGas("EthMetaVaultTest_test_redeemSubVaultsAssets_noRoundingErrors"); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(assetsToRedeem); + _stopSnapshotGas(); + + // Verify no rounding errors - redeemed amount should match requested exactly or be very close + // Allow up to 10 wei difference for rounding (share-to-asset conversions can cause small differences) + assertApproxEqAbs(totalRedeemed, assetsToRedeem, 10, "Redeemed should match requested with minimal rounding"); + + // Verify meta vault balance increased by exactly the redeemed amount + uint256 metaVaultBalanceAfter = address(metaVault).balance; + assertEq( + metaVaultBalanceAfter - metaVaultBalanceBefore, + totalRedeemed, + "Balance increase should exactly match redeemed amount" + ); + + // Verify total assets remain consistent (redeemed assets moved from sub vaults to meta vault) + uint256 metaVaultTotalAssetsAfter = metaVault.totalAssets(); + assertApproxEqAbs( + metaVaultTotalAssetsAfter, + metaVaultTotalAssetsBefore, + 10, + "Total assets should remain the same after redemption" + ); + + // Verify sub vault states were properly updated (no extra shares remaining) + uint256 totalSubVaultAssets = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + IVaultSubVaults.SubVaultState memory state = metaVault.subVaultsStates(subVaults[i]); + if (state.stakedShares > 0) { + totalSubVaultAssets += IVaultState(subVaults[i]).convertToAssets(state.stakedShares); + } + } + + // Total assets should equal sub vault assets plus withdrawable assets + uint256 expectedTotalAssets = totalSubVaultAssets + metaVault.withdrawableAssets(); + assertApproxEqAbs( + metaVaultTotalAssetsAfter, + expectedTotalAssets, + 10, + "Total assets should match sub vault assets plus withdrawable" + ); + } + + function test_redeemSubVaultsAssets_notHarvested() public { + // Setup: deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Increase keeper nonce by 2 to trigger NotHarvested + uint64 initialNonce = contracts.keeper.rewardsNonce(); + _setKeeperRewardsNonce(initialNonce + 2); + + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Verify state update is required + assertTrue(metaVault.isStateUpdateRequired(), "State update should be required"); + + // Try to redeem - should revert with NotHarvested + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + vm.expectRevert(Errors.NotHarvested.selector); + metaVault.redeemSubVaultsAssets(1 ether); + } + + function test_redeemSubVaultsAssets_emitsEvent() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Give sub vaults withdrawable assets + uint256 assetsToRedeem = 10 ether; + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + (assetsToRedeem / subVaults.length) + 1 ether); + } + + // Perform redemption and check event is emitted + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + vm.recordLogs(); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(assetsToRedeem); + + // Find and verify the SubVaultsAssetsRedeemed event + Vm.Log[] memory logs = vm.getRecordedLogs(); + bool eventFound = false; + bytes32 eventSig = keccak256("SubVaultsAssetsRedeemed(uint256)"); + for (uint256 i = 0; i < logs.length; i++) { + if (logs[i].topics[0] == eventSig) { + uint256 emittedAmount = abi.decode(logs[i].data, (uint256)); + assertEq(emittedAmount, totalRedeemed, "Event amount should match redeemed amount"); + eventFound = true; + break; + } + } + assertTrue(eventFound, "SubVaultsAssetsRedeemed event should be emitted"); + } + + function test_redeemSubVaultsAssets_updatesSubVaultStates() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Record staked shares before + uint256[] memory stakedSharesBefore = new uint256[](subVaults.length); + for (uint256 i = 0; i < subVaults.length; i++) { + IVaultSubVaults.SubVaultState memory state = metaVault.subVaultsStates(subVaults[i]); + stakedSharesBefore[i] = state.stakedShares; + } + + // Give sub vaults withdrawable assets + uint256 assetsToRedeem = 10 ether; + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + (assetsToRedeem / subVaults.length) + 1 ether); + } + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + metaVault.redeemSubVaultsAssets(assetsToRedeem); + + // Verify staked shares decreased for at least one sub vault + bool anySharesReduced = false; + for (uint256 i = 0; i < subVaults.length; i++) { + IVaultSubVaults.SubVaultState memory stateAfter = metaVault.subVaultsStates(subVaults[i]); + if (stateAfter.stakedShares < stakedSharesBefore[i]) { + anySharesReduced = true; + break; + } + } + assertTrue(anySharesReduced, "At least one sub vault should have reduced staked shares"); + } + + function test_redeemSubVaultsAssets_withEjectingSubVault() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Start ejecting one sub vault (use last one since it's always a newly created vault) + address vaultToEject = subVaults[subVaults.length - 1]; + vm.prank(admin); + metaVault.ejectSubVault(vaultToEject); + + _updateMetaVaultState(); + + // Get the ejecting vault's queued shares value - these are counted toward redemption + // but can't be redeemed immediately (they're in exit queue) + IVaultSubVaults.SubVaultState memory ejectingState = metaVault.subVaultsStates(vaultToEject); + uint256 ejectingVaultAssets = IVaultState(vaultToEject).convertToAssets(ejectingState.queuedShares); + + // Give remaining sub vaults (all except the ejecting one) withdrawable assets + for (uint256 i = 0; i < subVaults.length - 1; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + 10 ether); + } + + // Request more than what the ejecting vault has in queued shares + // This forces redemption from other sub vaults + uint256 assetsToRedeem = ejectingVaultAssets + 5 ether; + + // Record ejecting vault's state before + IVaultSubVaults.SubVaultState memory ejectingStateBefore = metaVault.subVaultsStates(vaultToEject); + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(assetsToRedeem); + + // Verify ejecting vault's state unchanged (ejecting shares are not modified by redemption) + IVaultSubVaults.SubVaultState memory ejectingStateAfter = metaVault.subVaultsStates(vaultToEject); + assertEq( + ejectingStateAfter.stakedShares, + ejectingStateBefore.stakedShares, + "Ejecting vault staked shares should not change" + ); + assertEq( + ejectingStateAfter.queuedShares, + ejectingStateBefore.queuedShares, + "Ejecting vault queued shares should not change" + ); + + // Verify assets were redeemed from other sub vaults (ejecting vault is skipped) + assertGt(totalRedeemed, 0, "Should redeem some assets from other sub vaults"); + assertApproxEqAbs( + totalRedeemed, assetsToRedeem, 100, "Should redeem the requested amount from other sub vaults" + ); + } + + function test_redeemSubVaultsAssets_allSubVaultsZeroWithdrawable() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Set all sub vaults to have exactly their unclaimed assets (0 withdrawable) + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed); + } + + // Verify all sub vaults have 0 withdrawable + for (uint256 i = 0; i < subVaults.length; i++) { + assertEq(IVaultState(subVaults[i]).withdrawableAssets(), 0, "Sub vault should have 0 withdrawable"); + } + + // Try to redeem - should return 0 since no sub vaults have withdrawable assets + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(5 ether); + + assertEq(totalRedeemed, 0, "Should redeem 0 when all sub vaults have 0 withdrawable"); + } + + function test_redeemSubVaultsAssets_multipleRedemptions() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Give sub vaults plenty of withdrawable assets + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + 20 ether); + } + + address redeemer = contracts.osTokenConfig.redeemer(); + + // Track total redeemed across all calls + uint256 totalRedeemed = 0; + uint256 metaVaultBalanceBefore = address(metaVault).balance; + + // Perform first redemption + vm.prank(redeemer); + uint256 redeemed1 = metaVault.redeemSubVaultsAssets(3 ether); + totalRedeemed += redeemed1; + + // Note: After first redemption, meta vault now has withdrawable assets (redeemed1) + // Subsequent redemptions will first use meta vault's withdrawable before going to sub vaults + // So we need to request more than what's now withdrawable + + // Second redemption - request more than current withdrawable + uint256 metaVaultWithdrawable = metaVault.withdrawableAssets(); + vm.prank(redeemer); + uint256 redeemed2 = metaVault.redeemSubVaultsAssets(metaVaultWithdrawable + 4 ether); + totalRedeemed += redeemed2; + + // Third redemption + metaVaultWithdrawable = metaVault.withdrawableAssets(); + vm.prank(redeemer); + uint256 redeemed3 = metaVault.redeemSubVaultsAssets(metaVaultWithdrawable + 2 ether); + totalRedeemed += redeemed3; + + // Verify total redeemed is sum of all redemptions + assertApproxEqAbs(totalRedeemed, redeemed1 + redeemed2 + redeemed3, 1, "Total should equal sum of redemptions"); + + // Verify meta vault balance increased by total redeemed + assertEq( + address(metaVault).balance, + metaVaultBalanceBefore + totalRedeemed, + "Meta vault balance should increase by total redeemed" + ); + + // Verify we actually redeemed significant assets + assertGt(totalRedeemed, 5 ether, "Should have redeemed significant assets across all calls"); + } + + function test_redeemSubVaultsAssets_verySmallAmount() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Give sub vaults withdrawable assets + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + 1 ether); + } + + // Try to redeem a small amount + address redeemer = contracts.osTokenConfig.redeemer(); + uint256 smallAmount = 10; + vm.prank(redeemer); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(smallAmount); + + // Verify redemption succeeded with minimal rounding + assertApproxEqAbs(totalRedeemed, smallAmount, 5, "Small redemption should work with minimal rounding"); + } + + function test_redeemSubVaultsAssets_partialWithdrawable() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit but don't push all to sub vaults - keep some withdrawable in meta vault + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Deposit only part to sub vaults by manipulating the balance + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Add some ETH back to meta vault to simulate partial withdrawable + vm.deal(address(metaVault), 5 ether); + + // Verify meta vault has some withdrawable (allow small rounding in fork mode) + uint256 metaVaultWithdrawable = metaVault.withdrawableAssets(); + assertApproxEqAbs(metaVaultWithdrawable, 5 ether, 100, "Meta vault should have ~5 ether withdrawable"); + + // Give sub vaults additional withdrawable + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + 5 ether); + } + + // Redeem more than meta vault's withdrawable but less than total + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(8 ether); + + // Should only redeem from sub vaults the amount exceeding meta vault's withdrawable (3 ether) + // Allow slightly larger delta for fork state rounding differences + assertApproxEqAbs(totalRedeemed, 3 ether, 100, "Should redeem ~3 ether from sub vaults"); } - function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault) - .depth(1).checked_write(rewardsNonce); + function test_redeemSubVaultsAssets_singleSubVaultHasWithdrawable() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Record staked shares before for first sub vault + IVaultSubVaults.SubVaultState memory stateBefore = metaVault.subVaultsStates(subVaults[0]); + uint256 stakedAssetsBefore = IVaultState(subVaults[0]).convertToAssets(stateBefore.stakedShares); + + // Set all sub vaults to have 0 withdrawable except the first one + uint256 firstSubVaultWithdrawable = 10 ether; + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + if (i == 0) { + // First sub vault gets extra balance + vm.deal(subVaults[i], unclaimed + firstSubVaultWithdrawable); + } else { + // Others have exactly unclaimed (0 withdrawable) + vm.deal(subVaults[i], unclaimed); + } + } + + // Verify only first sub vault has withdrawable + uint256 actualFirstWithdrawable = IVaultState(subVaults[0]).withdrawableAssets(); + assertGt(actualFirstWithdrawable, 0, "First sub vault should have withdrawable"); + for (uint256 i = 1; i < subVaults.length; i++) { + assertEq(IVaultState(subVaults[i]).withdrawableAssets(), 0, "Other sub vaults should have 0 withdrawable"); + } + + // Perform redemption - the curator will distribute requests across vaults, + // but only the first vault can actually provide assets + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + uint256 totalRedeemed = metaVault.redeemSubVaultsAssets(5 ether); + + // The actual redemption is limited by what the curator requests from the first vault + // The curator uses balanced distribution, so it may request less from each vault + // than the total, and only the first vault can provide assets + assertGt(totalRedeemed, 0, "Should redeem some assets from the first sub vault"); + + // Verify first sub vault's staked shares decreased + IVaultSubVaults.SubVaultState memory stateAfter = metaVault.subVaultsStates(subVaults[0]); + uint256 stakedAssetsAfter = IVaultState(subVaults[0]).convertToAssets(stateAfter.stakedShares); + assertLt(stakedAssetsAfter, stakedAssetsBefore, "First sub vault staked assets should decrease"); + + // Verify the redeemed amount matches the decrease in staked assets + assertApproxEqAbs( + stakedAssetsBefore - stakedAssetsAfter, + totalRedeemed, + 10, + "Staked assets decrease should match redeemed amount" + ); } - function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") - .checked_write(rewardsNonce); + function test_redeemSubVaultsAssets_osTokenPositionsClosed() public { + // Deploy and set EthOsTokenRedeemer + address redeemerOwner = makeAddr("RedeemerOwner"); + EthOsTokenRedeemer osTokenRedeemer = new EthOsTokenRedeemer( + address(contracts.vaultsRegistry), + _osToken, + address(contracts.osTokenVaultController), + redeemerOwner, + 12 hours + ); + vm.prank(Ownable(address(contracts.osTokenConfig)).owner()); + contracts.osTokenConfig.setRedeemer(address(osTokenRedeemer)); + + // Deposit and push to sub vaults + uint256 depositAmount = 30 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + _updateMetaVaultState(); + + // Give sub vaults withdrawable assets + for (uint256 i = 0; i < subVaults.length; i++) { + (, uint256 unclaimed,,,) = IVaultState(subVaults[i]).getExitQueueData(); + vm.deal(subVaults[i], unclaimed + 10 ether); + } + + // Verify meta vault has no osToken positions in sub vaults before + for (uint256 i = 0; i < subVaults.length; i++) { + assertEq( + IVaultOsToken(subVaults[i]).osTokenPositions(address(metaVault)), + 0, + "Meta vault should have no osToken position before" + ); + } + + // Perform redemption + address redeemer = contracts.osTokenConfig.redeemer(); + vm.prank(redeemer); + metaVault.redeemSubVaultsAssets(10 ether); + + // Verify meta vault still has no osToken positions in sub vaults after + for (uint256 i = 0; i < subVaults.length; i++) { + assertEq( + IVaultOsToken(subVaults[i]).osTokenPositions(address(metaVault)), + 0, + "Meta vault should have no osToken position after" + ); + } } function _extractExitPositions(address[] memory _subVaults, Vm.Log[] memory logs, uint64 timestamp) diff --git a/test/EthOsTokenRedeemer.t.sol b/test/EthOsTokenRedeemer.t.sol index 8cf12cf2..4a62f2b0 100644 --- a/test/EthOsTokenRedeemer.t.sol +++ b/test/EthOsTokenRedeemer.t.sol @@ -10,6 +10,7 @@ import {IOsTokenRedeemer} from "../contracts/interfaces/IOsTokenRedeemer.sol"; import {EthVault, IEthVault} from "../contracts/vaults/ethereum/EthVault.sol"; import {EthMetaVault} from "../contracts/vaults/ethereum/EthMetaVault.sol"; import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; +import {IVaultState} from "../contracts/interfaces/IVaultState.sol"; import {Errors} from "../contracts/libraries/Errors.sol"; import {IKeeperRewards} from "../contracts/interfaces/IKeeperRewards.sol"; import {EthHelpers} from "./helpers/EthHelpers.sol"; @@ -1462,21 +1463,238 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { ); } - // ========== Helper Functions for Meta Vault Tests ========== + // ========== Concurrent Redemption Tests ========== - function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { - bytes32[] memory emptyProof; - return - IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); + function test_redeemSubVaultsAssets_concurrentMultipleSubVaults() public { + // Setup meta vault with 4 sub vaults to test concurrent redemptions + _setupMetaVaultWithSubVaults(4); + + // Deposit significant amount and distribute to sub vaults + uint256 depositAmount = 100 ether; + vm.deal(user1, depositAmount); + vm.prank(user1); + metaVault.deposit{value: depositAmount}(user1, address(0)); + + // Distribute assets to sub-vaults + metaVault.depositToSubVaults(); + _updateMetaVaultState(); + + // Record sub vault states before redemption + uint256[] memory stakedSharesBefore = new uint256[](subVaults.length); + for (uint256 i = 0; i < subVaults.length; i++) { + stakedSharesBefore[i] = metaVault.subVaultsStates(subVaults[i]).stakedShares; + } + + // Request redemption that will require multiple sub-vaults + uint256 assetsToRedeem = 30 ether; + + vm.prank(positionsManager); + _startSnapshotGas("test_redeemSubVaultsAssets_concurrentMultipleSubVaults"); + uint256 totalRedeemed = osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), assetsToRedeem); + _stopSnapshotGas(); + + // Verify assets were redeemed from multiple sub-vaults + uint256 subVaultsWithRedemptions = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + uint256 stakedSharesAfter = metaVault.subVaultsStates(subVaults[i]).stakedShares; + if (stakedSharesAfter < stakedSharesBefore[i]) { + subVaultsWithRedemptions++; + } + } + + assertGt(subVaultsWithRedemptions, 1, "Should redeem from multiple sub-vaults concurrently"); + assertApproxEqRel(totalRedeemed, assetsToRedeem, 1, "Total redeemed should match requested"); + } + + function test_redeemSubVaultsAssets_multipleSequentialRedemptions() public { + // Setup meta vault + _setupMetaVaultWithSubVaults(3); + + // User deposits + vm.deal(user1, 100 ether); + vm.prank(user1); + metaVault.deposit{value: 100 ether}(user1, address(0)); + + // Distribute assets to sub-vaults + metaVault.depositToSubVaults(); + _updateMetaVaultState(); + + // Use fixed amounts for redemption + uint256 firstRedeemAmount = 10 ether; + + vm.prank(positionsManager); + uint256 redeemed1 = osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), firstRedeemAmount); + + // Verify first redemption succeeded (may be less than requested if limited by withdrawable) + assertGt(redeemed1, 0, "First redemption should succeed"); + + // Second redemption + vm.prank(positionsManager); + uint256 redeemed2 = osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), firstRedeemAmount); + + // Verify second redemption succeeded + assertGt(redeemed2, 0, "Second redemption should succeed"); + + // Total should be greater than zero and both redemptions should work + assertGt(redeemed1 + redeemed2, redeemed1, "Second redemption should add to total"); } - function _setVaultRewardsNonce(address _vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(_vault) - .depth(1).checked_write(rewardsNonce); + function test_redeemSubVaultsAssets_stateUpdatesAcrossSubVaults() public { + // Setup with known configuration + _setupMetaVaultWithSubVaults(3); + + uint256 depositAmount = 60 ether; + vm.deal(user1, depositAmount); + vm.prank(user1); + metaVault.deposit{value: depositAmount}(user1, address(0)); + + // Distribute assets to sub-vaults + metaVault.depositToSubVaults(); + _updateMetaVaultState(); + + // Record initial states + uint256 totalStakedBefore = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalStakedBefore += metaVault.subVaultsStates(subVaults[i]).stakedShares; + } + + // Redeem significant amount + uint256 assetsToRedeem = 40 ether; + vm.prank(positionsManager); + osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), assetsToRedeem); + + // Verify state updates were properly applied + uint256 totalStakedAfter = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalStakedAfter += metaVault.subVaultsStates(subVaults[i]).stakedShares; + } + + // Total staked shares should decrease + assertLt(totalStakedAfter, totalStakedBefore, "Total staked shares should decrease after redemption"); + + // Verify no negative shares or overflow + for (uint256 i = 0; i < subVaults.length; i++) { + uint128 stakedShares = metaVault.subVaultsStates(subVaults[i]).stakedShares; + assertGe(stakedShares, 0, "Staked shares should never be negative"); + } } - function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") - .checked_write(rewardsNonce); + function test_redeemSubVaultsAssets_largeAmountAcrossAllSubVaults() public { + // Setup with many sub vaults + _setupMetaVaultWithSubVaults(5); + + // Large deposit + uint256 depositAmount = 200 ether; + vm.deal(user1, depositAmount); + vm.prank(user1); + metaVault.deposit{value: depositAmount}(user1, address(0)); + + // Distribute assets to sub-vaults + metaVault.depositToSubVaults(); + _updateMetaVaultState(); + + // Record initial staked shares + uint256[] memory initialShares = new uint256[](subVaults.length); + for (uint256 i = 0; i < subVaults.length; i++) { + initialShares[i] = metaVault.subVaultsStates(subVaults[i]).stakedShares; + } + + // Request a large redemption amount (uses fixed amount like other working tests) + uint256 assetsToRedeem = 100 ether; + + vm.prank(positionsManager); + _startSnapshotGas("test_redeemSubVaultsAssets_largeAmountAcrossAllSubVaults"); + uint256 totalRedeemed = osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), assetsToRedeem); + _stopSnapshotGas(); + + // Verify we redeemed something + assertGt(totalRedeemed, 0, "Should redeem some assets"); + + // Check that some sub-vaults had their staked shares reduced + uint256 vaultsWithReductions = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + uint256 currentShares = metaVault.subVaultsStates(subVaults[i]).stakedShares; + if (currentShares < initialShares[i]) { + vaultsWithReductions++; + } + } + + // Multiple vaults should have contributed to the redemption + assertGt(vaultsWithReductions, 0, "Some sub-vaults should have been redeemed from"); + } + + function test_redeemSubVaultsAssets_accountingConsistencyAfterRedemption() public { + // Test that sub vault accounting remains consistent after redemption + _setupMetaVaultWithSubVaults(4); + + uint256 depositAmount = 100 ether; + vm.deal(user1, depositAmount); + vm.prank(user1); + metaVault.deposit{value: depositAmount}(user1, address(0)); + + // Distribute assets to sub-vaults + metaVault.depositToSubVaults(); + _updateMetaVaultState(); + + // Record initial values + uint256 metaVaultBalanceBefore = address(metaVault).balance; + uint256 totalSubVaultSharesBefore = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalSubVaultSharesBefore += metaVault.subVaultsStates(subVaults[i]).stakedShares; + } + + // Perform a redemption + uint256 redeemAmount = 20 ether; + vm.prank(positionsManager); + uint256 redeemed = osTokenRedeemer.redeemSubVaultsAssets(address(metaVault), redeemAmount); + + // Verify redemption succeeded + assertGt(redeemed, 0, "Should have redeemed some assets"); + + // Verify sub vault shares decreased + uint256 totalSubVaultSharesAfter = 0; + for (uint256 i = 0; i < subVaults.length; i++) { + totalSubVaultSharesAfter += metaVault.subVaultsStates(subVaults[i]).stakedShares; + } + assertLt(totalSubVaultSharesAfter, totalSubVaultSharesBefore, "Sub vault shares should decrease"); + + // Meta vault's balance should have increased (it received the redeemed assets) + uint256 metaVaultBalanceAfter = address(metaVault).balance; + assertGt(metaVaultBalanceAfter, metaVaultBalanceBefore, "Meta vault balance should increase"); + + // The balance increase should approximately match the redeemed amount + assertApproxEqRel( + metaVaultBalanceAfter - metaVaultBalanceBefore, + redeemed, + 5e16, + "Balance increase should match redeemed" + ); + } + + // Helper to setup meta vault with specific number of sub vaults + function _setupMetaVaultWithSubVaults(uint256 numSubVaults) internal { + // Clear existing subVaults array + delete subVaults; + + // Deploy meta vault + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: _balancedCurator, + capacity: type(uint256).max, + feePercent: 0, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + metaVault = EthMetaVault(payable(_createVault(VaultType.EthMetaVault, admin, initParams, false))); + + // Deploy and add sub vaults + for (uint256 i = 0; i < numSubVaults; i++) { + address subVault = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault); + subVaults.push(subVault); + + vm.prank(admin); + metaVault.addSubVault(subVault); + } } } diff --git a/test/EthPrivMetaVault.t.sol b/test/EthPrivMetaVault.t.sol new file mode 100644 index 00000000..5b46a784 --- /dev/null +++ b/test/EthPrivMetaVault.t.sol @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.22; + +import {Test} from "forge-std/Test.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IEthPrivMetaVault} from "../contracts/interfaces/IEthPrivMetaVault.sol"; +import {IEthMetaVault} from "../contracts/interfaces/IEthMetaVault.sol"; +import {IEthVault} from "../contracts/interfaces/IEthVault.sol"; +import {IMetaVault} from "../contracts/interfaces/IMetaVault.sol"; +import {IVaultSubVaults} from "../contracts/interfaces/IVaultSubVaults.sol"; +import {IKeeperRewards} from "../contracts/interfaces/IKeeperRewards.sol"; +import {Errors} from "../contracts/libraries/Errors.sol"; +import {EthPrivMetaVault} from "../contracts/vaults/ethereum/EthPrivMetaVault.sol"; +import {EthHelpers} from "./helpers/EthHelpers.sol"; + +contract EthPrivMetaVaultTest is Test, EthHelpers { + ForkContracts public contracts; + EthPrivMetaVault public metaVault; + + address public admin; + address public sender; + address public receiver; + address public referrer; + address public whitelister; + address public other; + + // Sub vaults + address[] public subVaults; + + function setUp() public { + // Activate Ethereum fork and get the contracts + contracts = _activateEthereumFork(); + + // Set up test accounts + admin = makeAddr("Admin"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + referrer = makeAddr("Referrer"); + whitelister = makeAddr("Whitelister"); + other = makeAddr("Other"); + + // Deal ETH to accounts + vm.deal(admin, 100 ether); + vm.deal(sender, 100 ether); + vm.deal(other, 100 ether); + + // Deploy private meta vault + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: _balancedCurator, + capacity: type(uint256).max, + feePercent: 0, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + metaVault = EthPrivMetaVault(payable(_getOrCreateVault(VaultType.EthPrivMetaVault, admin, initParams, false))); + + // Get existing sub vaults (if any) + address[] memory currentSubVaults = metaVault.getSubVaults(); + for (uint256 i = 0; i < currentSubVaults.length; i++) { + subVaults.push(currentSubVaults[i]); + } + + // Deploy and add sub vaults + for (uint256 i = 0; i < 3; i++) { + address subVault = _createSubVault(admin); + _collateralizeVault(address(contracts.keeper), address(contracts.validatorsRegistry), subVault); + subVaults.push(subVault); + + vm.prank(admin); + metaVault.addSubVault(subVault); + } + } + + function _createSubVault(address _admin) internal returns (address) { + bytes memory initParams = abi.encode( + IEthVault.EthVaultInitParams({ + capacity: 1000 ether, + feePercent: 5, // 5% + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + + return _createVault(VaultType.EthVault, _admin, initParams, false); + } + + function _updateMetaVaultState() internal { + // Update nonces for sub vaults to prepare for state update + uint64 newNonce = contracts.keeper.rewardsNonce() + 1; + _setKeeperRewardsNonce(newNonce); + for (uint256 i = 0; i < subVaults.length; i++) { + _setVaultRewardsNonce(subVaults[i], newNonce); + } + + // Update meta vault state + metaVault.updateState(_getEmptyHarvestParams()); + } + + // ============ Deployment Tests ============ + + function test_deployment() public view { + assertEq(metaVault.vaultId(), keccak256("EthPrivMetaVault"), "Incorrect vault ID"); + assertEq(metaVault.version(), 6, "Incorrect version"); + assertEq(metaVault.admin(), admin, "Incorrect admin"); + assertEq(metaVault.whitelister(), admin, "Whitelister should be admin initially"); + assertEq(metaVault.subVaultsCurator(), _balancedCurator, "Incorrect curator"); + assertEq(metaVault.capacity(), type(uint256).max, "Incorrect capacity"); + assertEq(metaVault.feePercent(), 0, "Incorrect fee percent"); + assertEq(metaVault.feeRecipient(), admin, "Incorrect fee recipient"); + + // Verify sub vaults + address[] memory storedSubVaults = metaVault.getSubVaults(); + for (uint256 i = 0; i < subVaults.length; i++) { + assertEq(storedSubVaults[i], subVaults[i], "Incorrect sub vault address"); + } + } + + function test_cannotInitializeTwice() public { + vm.expectRevert(Initializable.InvalidInitialization.selector); + metaVault.initialize{value: 0}("0x"); + } + + // ============ Whitelist Deposit Tests ============ + + function test_cannotDepositFromNotWhitelistedSender() public { + uint256 amount = 1 ether; + + // Set whitelister and whitelist receiver but not sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(receiver, true); + + // Try to deposit from non-whitelisted user + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.deposit{value: amount}(receiver, referrer); + } + + function test_cannotDepositToNotWhitelistedReceiver() public { + uint256 amount = 1 ether; + + // Set whitelister and whitelist sender but not receiver + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Try to deposit to non-whitelisted receiver + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.deposit{value: amount}(receiver, referrer); + } + + function test_canDepositAsWhitelistedUser() public { + uint256 amount = 1 ether; + uint256 totalSharesBefore = metaVault.totalShares(); + uint256 expectedShares = metaVault.convertToShares(amount); + + // Set whitelister and whitelist both sender and receiver + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.startPrank(whitelister); + metaVault.updateWhitelist(sender, true); + metaVault.updateWhitelist(receiver, true); + vm.stopPrank(); + + // Deposit as whitelisted user + vm.prank(sender); + _startSnapshotGas("EthPrivMetaVaultTest_test_canDepositAsWhitelistedUser"); + uint256 shares = metaVault.deposit{value: amount}(receiver, referrer); + _stopSnapshotGas(); + + // Check balances + assertApproxEqAbs(shares, expectedShares, 1, "Incorrect shares minted"); + assertApproxEqAbs(metaVault.getShares(receiver), expectedShares, 1, "Receiver did not receive shares"); + assertApproxEqAbs(metaVault.totalShares(), totalSharesBefore + expectedShares, 1, "Incorrect total shares"); + } + + // ============ Whitelist Receive Tests ============ + + function test_cannotDepositUsingReceiveAsNotWhitelistedUser() public { + uint256 amount = 1 ether; + + // Set whitelister and don't whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Try to deposit using receive function as non-whitelisted user + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + Address.sendValue(payable(metaVault), amount); + } + + function test_canDepositUsingReceiveAsWhitelistedUser() public { + uint256 amount = 1 ether; + uint256 expectedShares = metaVault.convertToShares(amount); + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Deposit using receive function as whitelisted user + vm.prank(sender); + _startSnapshotGas("EthPrivMetaVaultTest_test_canDepositUsingReceiveAsWhitelistedUser"); + Address.sendValue(payable(metaVault), amount); + _stopSnapshotGas(); + + // Check balances + assertApproxEqAbs(metaVault.getShares(sender), expectedShares, 1, "Sender did not receive shares"); + } + + function test_subVaultCanSendEthWithoutWhitelist() public { + // Set whitelister + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Whitelist sender for initial deposit + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Deposit and push to sub vaults + uint256 depositAmount = 10 ether; + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Sub vault is NOT whitelisted + assertFalse(metaVault.whitelistedAccounts(subVaults[0]), "Sub vault should not be whitelisted"); + + // Fund sub vault with ETH to simulate claiming + vm.deal(subVaults[0], 1 ether); + + // Sub vault should be able to send ETH (simulating claim) without being whitelisted + uint256 metaVaultBalanceBefore = address(metaVault).balance; + + vm.prank(subVaults[0]); + Address.sendValue(payable(metaVault), 0.5 ether); + + uint256 metaVaultBalanceAfter = address(metaVault).balance; + assertEq( + metaVaultBalanceAfter, metaVaultBalanceBefore + 0.5 ether, "Meta vault should receive ETH from sub vault" + ); + } + + // ============ Whitelist MintOsToken Tests ============ + + function test_cannotMintOsTokenFromNotWhitelistedUser() public { + uint256 depositAmount = 10 ether; + + // Set whitelister and whitelist user for initial deposit + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.startPrank(whitelister); + metaVault.updateWhitelist(sender, true); + vm.stopPrank(); + + // Deposit ETH to get vault shares + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Deposit to sub vaults to collateralize + metaVault.depositToSubVaults(); + + // Remove sender from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + // Try to mint osToken from non-whitelisted user + uint256 osTokenShares = depositAmount / 2; + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.mintOsToken(sender, osTokenShares, referrer); + } + + function test_canMintOsTokenAsWhitelistedUser() public { + uint256 depositAmount = 10 ether; + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Deposit ETH to get vault shares + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Deposit to sub vaults to collateralize + metaVault.depositToSubVaults(); + + // Mint osToken as whitelisted user + uint256 osTokenShares = depositAmount / 2; + vm.prank(sender); + _startSnapshotGas("EthPrivMetaVaultTest_test_canMintOsTokenAsWhitelistedUser"); + uint256 assets = metaVault.mintOsToken(sender, osTokenShares, referrer); + _stopSnapshotGas(); + + // Check osToken position + uint128 shares = metaVault.osTokenPositions(sender); + assertEq(shares, osTokenShares, "Incorrect osToken shares"); + assertGt(assets, 0, "No osToken assets minted"); + } + + // ============ Whitelist DepositAndMintOsToken Tests ============ + + function test_cannotDepositAndMintOsTokenAsNotWhitelistedUser() public { + uint256 depositAmount = 10 ether; + + // First collateralize the meta vault + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(admin, true); + + vm.prank(admin); + metaVault.deposit{value: depositAmount}(admin, referrer); + metaVault.depositToSubVaults(); + + // Whitelist receiver but not sender + vm.prank(whitelister); + metaVault.updateWhitelist(receiver, true); + + // Try to deposit and mint osToken as non-whitelisted user + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.depositAndMintOsToken{value: depositAmount}(receiver, depositAmount / 2, referrer); + } + + function test_canDepositAndMintOsTokenAsWhitelistedUser() public { + uint256 depositAmount = 10 ether; + + // First collateralize the meta vault + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.startPrank(whitelister); + metaVault.updateWhitelist(sender, true); + vm.stopPrank(); + + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + metaVault.depositToSubVaults(); + + // Deposit and mint osToken as whitelisted user + uint256 osTokenShares = depositAmount / 2; + vm.prank(sender); + _startSnapshotGas("EthPrivMetaVaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser"); + uint256 assets = metaVault.depositAndMintOsToken{value: depositAmount}(sender, osTokenShares, referrer); + _stopSnapshotGas(); + + // Check osToken position + uint128 shares = metaVault.osTokenPositions(sender); + assertEq(shares, osTokenShares, "Incorrect osToken shares"); + assertGt(assets, 0, "No osToken assets minted"); + } + + // ============ Whitelist UpdateStateAndDeposit Tests ============ + + function test_cannotUpdateStateAndDepositFromNotWhitelistedUser() public { + // Set whitelister and don't whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + IKeeperRewards.HarvestParams memory harvestParams = _getEmptyHarvestParams(); + + // Try to update state and deposit from non-whitelisted user + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.updateStateAndDeposit{value: 1 ether}(receiver, referrer, harvestParams); + } + + function test_canUpdateStateAndDepositAsWhitelistedUser() public { + // Set whitelister and whitelist both sender and receiver + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.startPrank(whitelister); + metaVault.updateWhitelist(sender, true); + metaVault.updateWhitelist(receiver, true); + vm.stopPrank(); + + IKeeperRewards.HarvestParams memory harvestParams = _getEmptyHarvestParams(); + + // Update state and deposit as whitelisted user + uint256 amount = 1 ether; + uint256 expectedShares = metaVault.convertToShares(amount); + vm.prank(sender); + _startSnapshotGas("EthPrivMetaVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser"); + uint256 shares = metaVault.updateStateAndDeposit{value: amount}(receiver, referrer, harvestParams); + _stopSnapshotGas(); + + // Check balances + assertApproxEqAbs(shares, expectedShares, 1, "Incorrect shares minted"); + assertApproxEqAbs(metaVault.getShares(receiver), expectedShares, 1, "Receiver did not receive shares"); + } + + // ============ Whitelister Management Tests ============ + + function test_setWhitelister() public { + address newWhitelister = makeAddr("NewWhitelister"); + + // Non-admin cannot set whitelister + vm.prank(other); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.setWhitelister(newWhitelister); + + // Admin can set whitelister + vm.prank(admin); + _startSnapshotGas("EthPrivMetaVaultTest_test_setWhitelister"); + metaVault.setWhitelister(newWhitelister); + _stopSnapshotGas(); + + assertEq(metaVault.whitelister(), newWhitelister, "Whitelister not set correctly"); + } + + function test_setWhitelister_valueNotChanged() public { + address currentWhitelister = metaVault.whitelister(); + + vm.prank(admin); + vm.expectRevert(Errors.ValueNotChanged.selector); + metaVault.setWhitelister(currentWhitelister); + } + + function test_updateWhitelist() public { + // Set whitelister + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Non-whitelister cannot update whitelist + vm.prank(other); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.updateWhitelist(sender, true); + + // Whitelister can update whitelist + vm.prank(whitelister); + _startSnapshotGas("EthPrivMetaVaultTest_test_updateWhitelist"); + metaVault.updateWhitelist(sender, true); + _stopSnapshotGas(); + + assertTrue(metaVault.whitelistedAccounts(sender), "Account not whitelisted correctly"); + + // Whitelister can remove from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + assertFalse(metaVault.whitelistedAccounts(sender), "Account not removed from whitelist correctly"); + } + + // ============ Whitelist State Preservation Tests ============ + + function test_whitelistUpdateDoesNotAffectExistingFunds() public { + uint256 amount = 1 ether; + uint256 expectedShares = metaVault.convertToShares(amount); + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Deposit ETH to get vault shares + vm.prank(sender); + metaVault.deposit{value: amount}(sender, referrer); + + uint256 initialBalance = metaVault.getShares(sender); + assertApproxEqAbs(initialBalance, expectedShares, 1, "Initial shares incorrect"); + + // Remove sender from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + // Verify share balance remains the same + assertEq(metaVault.getShares(sender), initialBalance, "Balance should not change when whitelisting is removed"); + + // Verify cannot make new deposits but still has existing shares + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.deposit{value: amount}(sender, referrer); + } + + function test_changingWhitelisterPreservesWhitelistState() public { + // Set initial whitelister + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Whitelist sender + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + assertTrue(metaVault.whitelistedAccounts(sender), "Sender should be whitelisted"); + + // Change whitelister + address newWhitelister = makeAddr("NewWhitelister"); + vm.prank(admin); + metaVault.setWhitelister(newWhitelister); + + // Verify sender is still whitelisted + assertTrue(metaVault.whitelistedAccounts(sender), "Sender whitelist status should be preserved"); + + // New whitelister can modify whitelist + vm.prank(newWhitelister); + metaVault.updateWhitelist(sender, false); + + assertFalse(metaVault.whitelistedAccounts(sender), "Sender should be removed from whitelist"); + } + + // ============ Meta Vault Operations with Whitelist Tests ============ + + function test_depositToSubVaultsWorksWithWhitelistedUser() public { + uint256 depositAmount = 10 ether; + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Deposit + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + // Get sub vault states before deposit + IVaultSubVaults.SubVaultState[] memory initialStates = new IVaultSubVaults.SubVaultState[](subVaults.length); + for (uint256 i = 0; i < subVaults.length; i++) { + initialStates[i] = metaVault.subVaultsStates(subVaults[i]); + } + + // Deposit to sub vaults (anyone can call this) + _startSnapshotGas("EthPrivMetaVaultTest_test_depositToSubVaultsWorksWithWhitelistedUser"); + metaVault.depositToSubVaults(); + _stopSnapshotGas(); + + // Verify sub vault balances increased + for (uint256 i = 0; i < subVaults.length; i++) { + IVaultSubVaults.SubVaultState memory finalState = metaVault.subVaultsStates(subVaults[i]); + assertGt(finalState.stakedShares, initialStates[i].stakedShares, "Sub vault staked shares should increase"); + } + } + + function test_enterExitQueueWorksForWhitelistedUserAfterRemoval() public { + uint256 depositAmount = 10 ether; + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Deposit + vm.prank(sender); + metaVault.deposit{value: depositAmount}(sender, referrer); + + uint256 senderShares = metaVault.getShares(sender); + + // Remove from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + // Should still be able to exit (enter exit queue) + vm.prank(sender); + _startSnapshotGas("EthPrivMetaVaultTest_test_enterExitQueueWorksForWhitelistedUserAfterRemoval"); + metaVault.enterExitQueue(senderShares, sender); + _stopSnapshotGas(); + + // Verify shares were reduced + assertEq(metaVault.getShares(sender), 0, "Shares should be 0 after entering exit queue"); + } +} diff --git a/test/VaultSubVaults.t.sol b/test/VaultSubVaults.t.sol index 28658fe1..088a81fb 100644 --- a/test/VaultSubVaults.t.sol +++ b/test/VaultSubVaults.t.sol @@ -672,9 +672,6 @@ contract VaultSubVaultsTest is Test, EthHelpers { initialTotalStaked += initialStates[i].stakedShares; } - // Calculate available assets - uint256 availableAssets = metaVault.withdrawableAssets(); - // Start gas measurement _startSnapshotGas("VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults"); @@ -2036,26 +2033,10 @@ contract VaultSubVaultsTest is Test, EthHelpers { EthMetaVault(payable(metaSubVault)).updateState(_getEmptyHarvestParams()); } - function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault) - .depth(1).checked_write(rewardsNonce); - } - function _setMetaVaultRewardsNonce(address vault, uint128 rewardsNonce) internal { stdstore.target(vault).sig("subVaultsRewardsNonce()").checked_write(rewardsNonce); } - function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()") - .checked_write(rewardsNonce); - } - - function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { - bytes32[] memory emptyProof; - return - IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); - } - function _extractExitPositions(address[] memory _subVaults, Vm.Log[] memory logs, uint64 timestamp) internal view diff --git a/test/gnosis/GnoMetaVault.t.sol b/test/gnosis/GnoMetaVault.t.sol index 5d90391c..0f142755 100644 --- a/test/gnosis/GnoMetaVault.t.sol +++ b/test/gnosis/GnoMetaVault.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.22; -import {Test, stdStorage, StdStorage, console} from "forge-std/Test.sol"; +import {Test, console} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -20,7 +20,6 @@ import {GnoHelpers} from "../helpers/GnoHelpers.sol"; import {IKeeperRewards} from "../../contracts/interfaces/IKeeperRewards.sol"; contract GnoMetaVaultTest is Test, GnoHelpers { - using stdStorage for StdStorage; ForkContracts public contracts; GnoMetaVault public metaVault; @@ -453,22 +452,4 @@ contract GnoMetaVaultTest is Test, GnoHelpers { vm.expectRevert(Errors.InvalidAssets.selector); metaVault.donateAssets(0); } - - function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { - bytes32[] memory emptyProof; - return - IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); - } - - function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewards(address)").with_key(vault).depth( - 1 - ).checked_write(rewardsNonce); - } - - function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(address(contracts.keeper)).sig("rewardsNonce()").checked_write( - rewardsNonce - ); - } } diff --git a/test/gnosis/GnoPrivMetaVault.t.sol b/test/gnosis/GnoPrivMetaVault.t.sol new file mode 100644 index 00000000..29d1ba81 --- /dev/null +++ b/test/gnosis/GnoPrivMetaVault.t.sol @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.22; + +import {Test} from "forge-std/Test.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IGnoPrivMetaVault} from "../../contracts/interfaces/IGnoPrivMetaVault.sol"; +import {IGnoMetaVault} from "../../contracts/interfaces/IGnoMetaVault.sol"; +import {IGnoVault} from "../../contracts/interfaces/IGnoVault.sol"; +import {IMetaVault} from "../../contracts/interfaces/IMetaVault.sol"; +import {IVaultSubVaults} from "../../contracts/interfaces/IVaultSubVaults.sol"; +import {IKeeperRewards} from "../../contracts/interfaces/IKeeperRewards.sol"; +import {Errors} from "../../contracts/libraries/Errors.sol"; +import {GnoPrivMetaVault} from "../../contracts/vaults/gnosis/GnoPrivMetaVault.sol"; +import {BalancedCurator} from "../../contracts/curators/BalancedCurator.sol"; +import {CuratorsRegistry} from "../../contracts/curators/CuratorsRegistry.sol"; +import {GnoHelpers} from "../helpers/GnoHelpers.sol"; + +contract GnoPrivMetaVaultTest is Test, GnoHelpers { + ForkContracts public contracts; + GnoPrivMetaVault public metaVault; + + address public admin; + address public sender; + address public receiver; + address public referrer; + address public whitelister; + address public other; + address public curator; + + // Sub vaults + address[] public subVaults; + + // Test constants + uint256 constant GNO_AMOUNT = 10 ether; + + function setUp() public { + // Activate Gnosis fork and get contracts + contracts = _activateGnosisFork(); + + // Set up test accounts + admin = makeAddr("Admin"); + sender = makeAddr("Sender"); + receiver = makeAddr("Receiver"); + referrer = makeAddr("Referrer"); + whitelister = makeAddr("Whitelister"); + other = makeAddr("Other"); + + // Mint GNO tokens to accounts + _mintGnoToken(admin, 100 ether); + _mintGnoToken(sender, 100 ether); + _mintGnoToken(other, 100 ether); + _mintGnoToken(address(this), 100 ether); + + // Create a curator + curator = address(new BalancedCurator()); + + // Register the curator in the registry + vm.prank(CuratorsRegistry(_curatorsRegistry).owner()); + CuratorsRegistry(_curatorsRegistry).addCurator(curator); + + // Deploy private meta vault + bytes memory initParams = abi.encode( + IMetaVault.MetaVaultInitParams({ + subVaultsCurator: curator, + capacity: type(uint256).max, + feePercent: 0, + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + metaVault = GnoPrivMetaVault(payable(_getOrCreateVault(VaultType.GnoPrivMetaVault, admin, initParams, false))); + + // Deploy and add sub vaults + for (uint256 i = 0; i < 3; i++) { + address subVault = _createSubVault(admin); + _collateralizeGnoVault(subVault); + subVaults.push(subVault); + + vm.prank(admin); + metaVault.addSubVault(subVault); + } + } + + function _createSubVault(address _admin) internal returns (address) { + bytes memory initParams = abi.encode( + IGnoVault.GnoVaultInitParams({ + capacity: 1000 ether, + feePercent: 5, // 5% + metadataIpfsHash: "bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u" + }) + ); + + return _createVault(VaultType.GnoVault, _admin, initParams, false); + } + + function _updateMetaVaultState() internal { + // Update nonces for sub vaults to prepare for state update + uint64 newNonce = contracts.keeper.rewardsNonce() + 1; + _setKeeperRewardsNonce(newNonce); + for (uint256 i = 0; i < subVaults.length; i++) { + _setVaultRewardsNonce(subVaults[i], newNonce); + } + + // Update meta vault state + metaVault.updateState(_getEmptyHarvestParams()); + } + + // ============ Deployment Tests ============ + + function test_deployment() public view { + assertEq(metaVault.vaultId(), keccak256("GnoPrivMetaVault"), "Incorrect vault ID"); + assertEq(metaVault.version(), 4, "Incorrect version"); + assertEq(metaVault.admin(), admin, "Incorrect admin"); + assertEq(metaVault.whitelister(), admin, "Whitelister should be admin initially"); + assertEq(metaVault.subVaultsCurator(), curator, "Incorrect curator"); + assertEq(metaVault.capacity(), type(uint256).max, "Incorrect capacity"); + assertEq(metaVault.feePercent(), 0, "Incorrect fee percent"); + assertEq(metaVault.feeRecipient(), admin, "Incorrect fee recipient"); + + // Verify sub vaults + address[] memory storedSubVaults = metaVault.getSubVaults(); + assertEq(storedSubVaults.length, 3, "Incorrect number of sub vaults"); + for (uint256 i = 0; i < 3; i++) { + assertEq(storedSubVaults[i], subVaults[i], "Incorrect sub vault address"); + } + } + + function test_cannotInitializeTwice() public { + vm.expectRevert(Initializable.InvalidInitialization.selector); + metaVault.initialize("0x"); + } + + // ============ Whitelist Deposit Tests ============ + + function test_cannotDepositFromNotWhitelistedSender() public { + uint256 amount = GNO_AMOUNT; + + // Set whitelister and whitelist receiver but not sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(receiver, true); + + // Approve tokens + vm.prank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), amount); + + // Try to deposit from non-whitelisted user + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.deposit(amount, receiver, referrer); + } + + function test_cannotDepositToNotWhitelistedReceiver() public { + uint256 amount = GNO_AMOUNT; + + // Set whitelister and whitelist sender but not receiver + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Approve tokens + vm.prank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), amount); + + // Try to deposit to non-whitelisted receiver + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.deposit(amount, receiver, referrer); + } + + function test_canDepositAsWhitelistedUser() public { + uint256 amount = GNO_AMOUNT; + uint256 totalSharesBefore = metaVault.totalShares(); + uint256 expectedShares = metaVault.convertToShares(amount); + + // Set whitelister and whitelist both sender and receiver + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.startPrank(whitelister); + metaVault.updateWhitelist(sender, true); + metaVault.updateWhitelist(receiver, true); + vm.stopPrank(); + + // Approve tokens + vm.prank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), amount); + + // Deposit as whitelisted user + vm.prank(sender); + _startSnapshotGas("GnoPrivMetaVaultTest_test_canDepositAsWhitelistedUser"); + uint256 shares = metaVault.deposit(amount, receiver, referrer); + _stopSnapshotGas(); + + // Check balances + assertEq(shares, expectedShares, "Incorrect shares minted"); + assertEq(metaVault.getShares(receiver), expectedShares, "Receiver did not receive shares"); + assertEq(metaVault.totalShares(), totalSharesBefore + expectedShares, "Incorrect total shares"); + } + + // ============ Whitelist MintOsToken Tests ============ + + function test_cannotMintOsTokenFromNotWhitelistedUser() public { + uint256 depositAmount = GNO_AMOUNT; + + // Set whitelister and whitelist user for initial deposit + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Approve and deposit GNO to get vault shares + vm.startPrank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), depositAmount); + metaVault.deposit(depositAmount, sender, referrer); + vm.stopPrank(); + + // Deposit to sub vaults to collateralize + metaVault.depositToSubVaults(); + + // Remove sender from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + // Try to mint osToken from non-whitelisted user + uint256 osTokenShares = depositAmount / 2; + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.mintOsToken(sender, osTokenShares, referrer); + } + + function test_canMintOsTokenAsWhitelistedUser() public { + uint256 depositAmount = GNO_AMOUNT; + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Approve and deposit GNO to get vault shares + vm.startPrank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), depositAmount); + metaVault.deposit(depositAmount, sender, referrer); + vm.stopPrank(); + + // Deposit to sub vaults to collateralize + metaVault.depositToSubVaults(); + + // Mint osToken as whitelisted user + uint256 osTokenShares = depositAmount / 2; + vm.prank(sender); + _startSnapshotGas("GnoPrivMetaVaultTest_test_canMintOsTokenAsWhitelistedUser"); + uint256 assets = metaVault.mintOsToken(sender, osTokenShares, referrer); + _stopSnapshotGas(); + + // Check osToken position + uint128 shares = metaVault.osTokenPositions(sender); + assertEq(shares, osTokenShares, "Incorrect osToken shares"); + assertGt(assets, 0, "No osToken assets minted"); + } + + // ============ Whitelister Management Tests ============ + + function test_setWhitelister() public { + address newWhitelister = makeAddr("NewWhitelister"); + + // Non-admin cannot set whitelister + vm.prank(other); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.setWhitelister(newWhitelister); + + // Admin can set whitelister + vm.prank(admin); + _startSnapshotGas("GnoPrivMetaVaultTest_test_setWhitelister"); + metaVault.setWhitelister(newWhitelister); + _stopSnapshotGas(); + + assertEq(metaVault.whitelister(), newWhitelister, "Whitelister not set correctly"); + } + + function test_setWhitelister_valueNotChanged() public { + address currentWhitelister = metaVault.whitelister(); + + vm.prank(admin); + vm.expectRevert(Errors.ValueNotChanged.selector); + metaVault.setWhitelister(currentWhitelister); + } + + function test_updateWhitelist() public { + // Set whitelister + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Non-whitelister cannot update whitelist + vm.prank(other); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.updateWhitelist(sender, true); + + // Whitelister can update whitelist + vm.prank(whitelister); + _startSnapshotGas("GnoPrivMetaVaultTest_test_updateWhitelist"); + metaVault.updateWhitelist(sender, true); + _stopSnapshotGas(); + + assertTrue(metaVault.whitelistedAccounts(sender), "Account not whitelisted correctly"); + + // Whitelister can remove from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + assertFalse(metaVault.whitelistedAccounts(sender), "Account not removed from whitelist correctly"); + } + + // ============ Whitelist State Preservation Tests ============ + + function test_whitelistUpdateDoesNotAffectExistingFunds() public { + uint256 amount = GNO_AMOUNT; + uint256 expectedShares = metaVault.convertToShares(amount); + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Approve and deposit GNO to get vault shares + vm.startPrank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), amount * 2); + metaVault.deposit(amount, sender, referrer); + vm.stopPrank(); + + uint256 initialBalance = metaVault.getShares(sender); + assertEq(initialBalance, expectedShares, "Initial shares incorrect"); + + // Remove sender from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + // Verify share balance remains the same + assertEq(metaVault.getShares(sender), initialBalance, "Balance should not change when whitelisting is removed"); + + // Verify cannot make new deposits but still has existing shares + vm.prank(sender); + vm.expectRevert(Errors.AccessDenied.selector); + metaVault.deposit(amount, sender, referrer); + } + + function test_changingWhitelisterPreservesWhitelistState() public { + // Set initial whitelister + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Whitelist sender + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + assertTrue(metaVault.whitelistedAccounts(sender), "Sender should be whitelisted"); + + // Change whitelister + address newWhitelister = makeAddr("NewWhitelister"); + vm.prank(admin); + metaVault.setWhitelister(newWhitelister); + + // Verify sender is still whitelisted + assertTrue(metaVault.whitelistedAccounts(sender), "Sender whitelist status should be preserved"); + + // New whitelister can modify whitelist + vm.prank(newWhitelister); + metaVault.updateWhitelist(sender, false); + + assertFalse(metaVault.whitelistedAccounts(sender), "Sender should be removed from whitelist"); + } + + // ============ Meta Vault Operations with Whitelist Tests ============ + + function test_depositToSubVaultsWorksWithWhitelistedUser() public { + uint256 depositAmount = GNO_AMOUNT; + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Approve and deposit + vm.startPrank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), depositAmount); + metaVault.deposit(depositAmount, sender, referrer); + vm.stopPrank(); + + // Get sub vault states before deposit + IVaultSubVaults.SubVaultState[] memory initialStates = new IVaultSubVaults.SubVaultState[](subVaults.length); + for (uint256 i = 0; i < subVaults.length; i++) { + initialStates[i] = metaVault.subVaultsStates(subVaults[i]); + } + + // Deposit to sub vaults (anyone can call this) + _startSnapshotGas("GnoPrivMetaVaultTest_test_depositToSubVaultsWorksWithWhitelistedUser"); + metaVault.depositToSubVaults(); + _stopSnapshotGas(); + + // Verify sub vault balances increased + for (uint256 i = 0; i < subVaults.length; i++) { + IVaultSubVaults.SubVaultState memory finalState = metaVault.subVaultsStates(subVaults[i]); + assertGt(finalState.stakedShares, initialStates[i].stakedShares, "Sub vault staked shares should increase"); + } + } + + function test_enterExitQueueWorksForWhitelistedUserAfterRemoval() public { + uint256 depositAmount = GNO_AMOUNT; + + // Set whitelister and whitelist sender + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + vm.prank(whitelister); + metaVault.updateWhitelist(sender, true); + + // Approve and deposit + vm.startPrank(sender); + IERC20(address(contracts.gnoToken)).approve(address(metaVault), depositAmount); + metaVault.deposit(depositAmount, sender, referrer); + vm.stopPrank(); + + uint256 senderShares = metaVault.getShares(sender); + + // Remove from whitelist + vm.prank(whitelister); + metaVault.updateWhitelist(sender, false); + + // Should still be able to exit (enter exit queue) + vm.prank(sender); + _startSnapshotGas("GnoPrivMetaVaultTest_test_enterExitQueueWorksForWhitelistedUserAfterRemoval"); + metaVault.enterExitQueue(senderShares, sender); + _stopSnapshotGas(); + + // Verify shares were reduced + assertEq(metaVault.getShares(sender), 0, "Shares should be 0 after entering exit queue"); + } + + function test_donateAssets_whitelistNotRequired() public { + uint256 donationAmount = 1 ether; + + // Set whitelister (don't whitelist sender) + vm.prank(admin); + metaVault.setWhitelister(whitelister); + + // Get vault state before donation + uint256 vaultBalanceBefore = contracts.gnoToken.balanceOf(address(metaVault)); + + // Approve GNO token for donation + vm.startPrank(sender); + contracts.gnoToken.approve(address(metaVault), donationAmount); + + // Donation should work without whitelisting + metaVault.donateAssets(donationAmount); + vm.stopPrank(); + + // Verify donation was received + assertEq( + contracts.gnoToken.balanceOf(address(metaVault)), + vaultBalanceBefore + donationAmount, + "Meta vault GNO balance should increase" + ); + } +} diff --git a/test/helpers/EthHelpers.sol b/test/helpers/EthHelpers.sol index 64572f95..9a8bec08 100644 --- a/test/helpers/EthHelpers.sol +++ b/test/helpers/EthHelpers.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.22; -import {Test} from "forge-std/Test.sol"; +import {Test, stdStorage, StdStorage} from "forge-std/Test.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IKeeperValidators} from "../../contracts/interfaces/IKeeperValidators.sol"; import {IOsTokenConfig} from "../../contracts/interfaces/IOsTokenConfig.sol"; @@ -35,6 +35,8 @@ import {VaultsRegistry, IVaultsRegistry} from "../../contracts/vaults/VaultsRegi import {CuratorsRegistry} from "../../contracts/curators/CuratorsRegistry.sol"; abstract contract EthHelpers is Test, ValidatorsHelpers { + using stdStorage for StdStorage; + uint256 internal constant forkBlockNumber = 24235110; uint256 internal constant _securityDeposit = 1e9; address private constant _keeper = 0x6B5815467da09DaA7DC83Db21c9239d98Bb487b5; @@ -486,4 +488,22 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { return impl; } + + // ============ Shared Meta Vault Test Helpers ============ + + function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { + bytes32[] memory emptyProof; + return + IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); + } + + function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { + stdstore.enable_packed_slots().target(_keeper).sig("rewards(address)").with_key(vault).depth(1).checked_write( + rewardsNonce + ); + } + + function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { + stdstore.enable_packed_slots().target(_keeper).sig("rewardsNonce()").checked_write(rewardsNonce); + } } diff --git a/test/helpers/GnoHelpers.sol b/test/helpers/GnoHelpers.sol index 98c7f88d..6f6a4ca1 100644 --- a/test/helpers/GnoHelpers.sol +++ b/test/helpers/GnoHelpers.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.22; -import {Test} from "forge-std/Test.sol"; +import {Test, stdStorage, StdStorage} from "forge-std/Test.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IKeeperValidators} from "../../contracts/interfaces/IKeeperValidators.sol"; @@ -40,6 +40,8 @@ interface IGnoToken { } abstract contract GnoHelpers is Test, ValidatorsHelpers { + using stdStorage for StdStorage; + uint256 internal constant forkBlockNumber = 40107000; uint256 internal constant _securityDeposit = 1e9; address private constant _keeper = 0xcAC0e3E35d3BA271cd2aaBE688ac9DB1898C26aa; @@ -465,4 +467,22 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { return impl; } + + // ============ Shared Meta Vault Test Helpers ============ + + function _getEmptyHarvestParams() internal pure returns (IKeeperRewards.HarvestParams memory) { + bytes32[] memory emptyProof; + return + IKeeperRewards.HarvestParams({rewardsRoot: bytes32(0), proof: emptyProof, reward: 0, unlockedMevReward: 0}); + } + + function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { + stdstore.enable_packed_slots().target(_keeper).sig("rewards(address)").with_key(vault).depth(1).checked_write( + rewardsNonce + ); + } + + function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { + stdstore.enable_packed_slots().target(_keeper).sig("rewardsNonce()").checked_write(rewardsNonce); + } } From e3f32f3043d6254fde80419988bfad571da54661 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 19 Jan 2026 17:35:10 +0200 Subject: [PATCH 16/19] Apply code formatting and cleanup env variables --- .env.example | 6 --- contracts/interfaces/IOsTokenVaultEscrow.sol | 3 +- contracts/interfaces/IVaultValidators.sol | 4 +- contracts/keeper/KeeperRewards.sol | 6 +-- contracts/keeper/KeeperValidators.sol | 4 +- contracts/libraries/EIP712Utils.sol | 7 +-- contracts/tokens/EthOsTokenVaultEscrow.sol | 7 +-- contracts/tokens/GnoOsTokenVaultEscrow.sol | 7 +-- contracts/tokens/OsTokenFlashLoans.sol | 2 + contracts/tokens/OsTokenVaultEscrow.sol | 5 +- contracts/validators/DepositDataRegistry.sol | 6 +-- contracts/vaults/ethereum/EthGenesisVault.sol | 8 +--- contracts/vaults/ethereum/EthMetaVault.sol | 39 +++++---------- .../vaults/ethereum/EthMetaVaultFactory.sol | 7 +-- .../vaults/ethereum/EthPrivMetaVault.sol | 36 ++++++++------ contracts/vaults/ethereum/EthVaultFactory.sol | 7 +-- contracts/vaults/gnosis/GnoErc20Vault.sol | 4 +- contracts/vaults/gnosis/GnoMetaVault.sol | 47 ++++++------------- .../vaults/gnosis/GnoMetaVaultFactory.sol | 3 +- contracts/vaults/gnosis/GnoPrivMetaVault.sol | 36 +++++++------- contracts/vaults/modules/VaultEnterExit.sol | 7 +-- contracts/vaults/modules/VaultGnoStaking.sol | 15 +++--- contracts/vaults/modules/VaultOsToken.sol | 6 +-- contracts/vaults/modules/VaultValidators.sol | 11 ++--- test/EthErc20Vault.t.sol | 5 +- test/EthOsTokenRedeemer.t.sol | 5 +- test/EthOsTokenVaultEscrow.t.sol | 26 +++++----- test/EthVault.t.sol | 6 +-- test/KeeperValidators.t.sol | 13 ++--- test/OsTokenConfig.t.sol | 4 +- test/SharedMevEscrow.t.sol | 2 +- test/VaultEnterExit.t.sol | 13 ++--- test/VaultOsToken.t.sol | 7 +-- test/VaultValidators.t.sol | 20 ++++---- test/gnosis/GnoMetaVault.t.sol | 2 - test/gnosis/GnoOsTokenVaultEscrow.t.sol | 7 +-- test/gnosis/GnoRewardSplitter.t.sol | 5 +- test/gnosis/GnoVault.t.sol | 6 +-- test/helpers/EthHelpers.sol | 7 ++- test/helpers/GnoHelpers.sol | 10 ++-- 40 files changed, 170 insertions(+), 251 deletions(-) diff --git a/.env.example b/.env.example index eb165fc7..e1b089f9 100644 --- a/.env.example +++ b/.env.example @@ -21,29 +21,23 @@ REMOVE_PREV_FACTORIES=false # contract variables # mainnet -#META_VAULT_FACTORY_OWNER=0x2685C0e39EEAAd383fB71ec3F493991d532A87ae #OS_TOKEN_REDEEMER_OWNER=0x2685C0e39EEAAd383fB71ec3F493991d532A87ae #OS_TOKEN_REDEEMER_EXIT_QUEUE_UPDATE_DELAY=43200 #VALIDATORS_REGISTRY=0x00000000219ab540356cBB839Cbe05303d7705Fa # hoodi -#META_VAULT_FACTORY_OWNER=0xFF2B6d2d5c205b99E2e6f607B6aFA3127B9957B6 #OS_TOKEN_REDEEMER_OWNER=0xFF2B6d2d5c205b99E2e6f607B6aFA3127B9957B6 #OS_TOKEN_REDEEMER_EXIT_QUEUE_UPDATE_DELAY=43200 #VALIDATORS_REGISTRY=0x00000000219ab540356cBB839Cbe05303d7705Fa # chiado -#META_VAULT_FACTORY_OWNER=0xFF2B6d2d5c205b99E2e6f607B6aFA3127B9957B6 #OS_TOKEN_REDEEMER_OWNER=0xFF2B6d2d5c205b99E2e6f607B6aFA3127B9957B6 #OS_TOKEN_REDEEMER_EXIT_QUEUE_UPDATE_DELAY=43200 -#TOKENS_CONVERTER_FACTORY=0xd1C58a7a8809fe48dD4BE2a43e2a604a68e51503 #VALIDATORS_REGISTRY=0xb97036A26259B7147018913bD58a774cf91acf25 #GNO_TOKEN=0x19C653Da7c37c66208fbfbE8908A5051B57b4C70 # gnosis -#META_VAULT_FACTORY_OWNER=0x2685C0e39EEAAd383fB71ec3F493991d532A87ae #OS_TOKEN_REDEEMER_OWNER=0x2685C0e39EEAAd383fB71ec3F493991d532A87ae #OS_TOKEN_REDEEMER_EXIT_QUEUE_UPDATE_DELAY=43200 -#TOKENS_CONVERTER_FACTORY=0xdeC758323bF734c72F909965841fC2dba3C8c007 #VALIDATORS_REGISTRY=0x0B98057eA310F4d31F2a452B414647007d1645d9 #GNO_TOKEN=0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb \ No newline at end of file diff --git a/contracts/interfaces/IOsTokenVaultEscrow.sol b/contracts/interfaces/IOsTokenVaultEscrow.sol index 459b9b9d..27aaf7f6 100644 --- a/contracts/interfaces/IOsTokenVaultEscrow.sol +++ b/contracts/interfaces/IOsTokenVaultEscrow.sol @@ -192,8 +192,7 @@ interface IOsTokenVaultEscrow is IMulticall { * @param osTokenShares The amount of osToken shares to redeem * @param receiver The address of the receiver of the redeemed assets */ - function redeemOsToken(address vault, uint256 exitPositionTicket, uint256 osTokenShares, address receiver) - external; + function redeemOsToken(address vault, uint256 exitPositionTicket, uint256 osTokenShares, address receiver) external; /** * @notice Updates the authenticator. Can only be called by the owner. diff --git a/contracts/interfaces/IVaultValidators.sol b/contracts/interfaces/IVaultValidators.sol index 156d6c84..37fae766 100644 --- a/contracts/interfaces/IVaultValidators.sol +++ b/contracts/interfaces/IVaultValidators.sol @@ -100,9 +100,7 @@ interface IVaultValidators is IVaultAdmin, IVaultState { * @param validators The concatenated validators data * @param validatorsManagerSignature The optional signature from the validators manager */ - function withdrawValidators(bytes calldata validators, bytes calldata validatorsManagerSignature) - external - payable; + function withdrawValidators(bytes calldata validators, bytes calldata validatorsManagerSignature) external payable; /** * @notice Function for consolidating single or multiple validators diff --git a/contracts/keeper/KeeperRewards.sol b/contracts/keeper/KeeperRewards.sol index 8a15e238..0f3f668a 100644 --- a/contracts/keeper/KeeperRewards.sol +++ b/contracts/keeper/KeeperRewards.sol @@ -178,13 +178,11 @@ abstract contract KeeperRewards is KeeperOracles, IKeeperRewards { } // verify the proof - if ( - !MerkleProof.verifyCalldata( + if (!MerkleProof.verifyCalldata( params.proof, params.rewardsRoot, keccak256(bytes.concat(keccak256(abi.encode(msg.sender, params.reward, params.unlockedMevReward)))) - ) - ) { + )) { revert Errors.InvalidProof(); } diff --git a/contracts/keeper/KeeperValidators.sol b/contracts/keeper/KeeperValidators.sol index d0279593..70342a2a 100644 --- a/contracts/keeper/KeeperValidators.sol +++ b/contracts/keeper/KeeperValidators.sol @@ -80,7 +80,9 @@ abstract contract KeeperValidators is KeeperOracles, KeeperRewards, IKeeperValid string calldata exitSignaturesIpfsHash, bytes calldata oraclesSignatures ) external override { - if (!(_vaultsRegistry.vaults(vault) && isCollateralized(vault))) revert Errors.InvalidVault(); + if (!(_vaultsRegistry.vaults(vault) && isCollateralized(vault))) { + revert Errors.InvalidVault(); + } if (deadline < block.timestamp) revert Errors.DeadlineExpired(); // SLOAD to memory diff --git a/contracts/libraries/EIP712Utils.sol b/contracts/libraries/EIP712Utils.sol index e5470f1d..cea132a4 100644 --- a/contracts/libraries/EIP712Utils.sol +++ b/contracts/libraries/EIP712Utils.sol @@ -20,8 +20,9 @@ library EIP712Utils { * @return The hash of the EIP712 typed data */ function computeDomainSeparator(string memory name, address verifyingContract) external view returns (bytes32) { - return keccak256( - abi.encode(_domainTypeHash, keccak256(bytes(name)), _versionHash, block.chainid, verifyingContract) - ); + return + keccak256( + abi.encode(_domainTypeHash, keccak256(bytes(name)), _versionHash, block.chainid, verifyingContract) + ); } } diff --git a/contracts/tokens/EthOsTokenVaultEscrow.sol b/contracts/tokens/EthOsTokenVaultEscrow.sol index 7d06665c..415b29dc 100644 --- a/contracts/tokens/EthOsTokenVaultEscrow.sol +++ b/contracts/tokens/EthOsTokenVaultEscrow.sol @@ -39,12 +39,7 @@ contract EthOsTokenVaultEscrow is ReentrancyGuard, OsTokenVaultEscrow { ) ReentrancyGuard() OsTokenVaultEscrow( - osTokenVaultController, - osTokenConfig, - initialOwner, - _authenticator, - _liqThresholdPercent, - _liqBonusPercent + osTokenVaultController, osTokenConfig, initialOwner, _authenticator, _liqThresholdPercent, _liqBonusPercent ) {} diff --git a/contracts/tokens/GnoOsTokenVaultEscrow.sol b/contracts/tokens/GnoOsTokenVaultEscrow.sol index 3cbfb924..0ce3b8e0 100644 --- a/contracts/tokens/GnoOsTokenVaultEscrow.sol +++ b/contracts/tokens/GnoOsTokenVaultEscrow.sol @@ -34,12 +34,7 @@ contract GnoOsTokenVaultEscrow is OsTokenVaultEscrow { address gnoToken ) OsTokenVaultEscrow( - osTokenVaultController, - osTokenConfig, - initialOwner, - _authenticator, - _liqThresholdPercent, - _liqBonusPercent + osTokenVaultController, osTokenConfig, initialOwner, _authenticator, _liqThresholdPercent, _liqBonusPercent ) { _gnoToken = IERC20(gnoToken); diff --git a/contracts/tokens/OsTokenFlashLoans.sol b/contracts/tokens/OsTokenFlashLoans.sol index d914dbdd..81d43fa1 100644 --- a/contracts/tokens/OsTokenFlashLoans.sol +++ b/contracts/tokens/OsTokenFlashLoans.sol @@ -27,6 +27,7 @@ contract OsTokenFlashLoans is ReentrancyGuard, IOsTokenFlashLoans { } /// @inheritdoc IOsTokenFlashLoans + // slither-disable-start reentrancy-balance function flashLoan(uint256 osTokenShares, bytes memory userData) external override nonReentrant { // check if not more than max flash loan amount requested if (osTokenShares == 0 || osTokenShares > _maxFlashLoanAmount) { @@ -56,4 +57,5 @@ contract OsTokenFlashLoans is ReentrancyGuard, IOsTokenFlashLoans { // emit event emit OsTokenFlashLoan(msg.sender, osTokenShares); } + // slither-disable-end reentrancy-balance } diff --git a/contracts/tokens/OsTokenVaultEscrow.sol b/contracts/tokens/OsTokenVaultEscrow.sol index 0c0a4812..79ed71e6 100644 --- a/contracts/tokens/OsTokenVaultEscrow.sol +++ b/contracts/tokens/OsTokenVaultEscrow.sol @@ -106,9 +106,8 @@ abstract contract OsTokenVaultEscrow is Ownable2Step, Multicall, IOsTokenVaultEs if (position.owner == address(0)) revert Errors.InvalidPosition(); // claim exited assets - (uint256 leftTickets,, uint256 exitedAssets) = IVaultEnterExit(vault).calculateExitedAssets( - address(this), exitPositionTicket, timestamp, uint256(exitQueueIndex) - ); + (uint256 leftTickets,, uint256 exitedAssets) = IVaultEnterExit(vault) + .calculateExitedAssets(address(this), exitPositionTicket, timestamp, uint256(exitQueueIndex)); // the exit request must be fully processed (1 ticket could be a rounding error) if (leftTickets > 1) revert Errors.ExitRequestNotProcessed(); IVaultEnterExit(vault).claimExitedAssets(exitPositionTicket, timestamp, uint256(exitQueueIndex)); diff --git a/contracts/validators/DepositDataRegistry.sol b/contracts/validators/DepositDataRegistry.sol index 8b119ccd..aea029c2 100644 --- a/contracts/validators/DepositDataRegistry.sol +++ b/contracts/validators/DepositDataRegistry.sol @@ -96,13 +96,11 @@ contract DepositDataRegistry is Multicall, IDepositDataRegistry { bytes32 depositDataRoot = depositDataRoots[vault]; // check matches merkle root and next validator index - if ( - !MerkleProof.verifyCalldata( + if (!MerkleProof.verifyCalldata( proof, depositDataRoot, keccak256(bytes.concat(keccak256(abi.encode(keeperParams.validators, currentIndex)))) - ) - ) { + )) { revert Errors.InvalidProof(); } diff --git a/contracts/vaults/ethereum/EthGenesisVault.sol b/contracts/vaults/ethereum/EthGenesisVault.sol index 4708af13..d47c42c8 100644 --- a/contracts/vaults/ethereum/EthGenesisVault.sol +++ b/contracts/vaults/ethereum/EthGenesisVault.sol @@ -47,13 +47,7 @@ contract EthGenesisVault is Initializable, EthVault, IEthGenesisVault { } /// @inheritdoc IEthVault - function initialize(bytes calldata) - external - payable - virtual - override(IEthVault, EthVault) - reinitializer(_version) - { + function initialize(bytes calldata) external payable virtual override(IEthVault, EthVault) reinitializer(_version) { if (admin == address(0)) { revert Errors.UpgradeFailed(); } diff --git a/contracts/vaults/ethereum/EthMetaVault.sol b/contracts/vaults/ethereum/EthMetaVault.sol index 01784e31..30ad96e9 100644 --- a/contracts/vaults/ethereum/EthMetaVault.sol +++ b/contracts/vaults/ethereum/EthMetaVault.sol @@ -34,16 +34,12 @@ contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { * @param args The arguments for initializing the MetaVault contract */ /// @custom:oz-upgrades-unsafe-allow constructor - constructor( - MetaVaultConstructorArgs memory args - ) MetaVault(args) { + constructor(MetaVaultConstructorArgs memory args) MetaVault(args) { _disableInitializers(); } /// @inheritdoc IEthMetaVault - function initialize( - bytes calldata params - ) external payable virtual override reinitializer(_version) { + function initialize(bytes calldata params) external payable virtual override reinitializer(_version) { // if admin is already set, it's an upgrade from version 5 to 6 if (admin != address(0)) { return; @@ -53,10 +49,7 @@ contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { } /// @inheritdoc IEthMetaVault - function deposit( - address receiver, - address referrer - ) public payable virtual override returns (uint256 shares) { + function deposit(address receiver, address referrer) public payable virtual override returns (uint256 shares) { return _deposit(receiver, msg.value, referrer); } @@ -82,11 +75,12 @@ contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { } /// @inheritdoc IEthMetaVault - function depositAndMintOsToken( - address receiver, - uint256 osTokenShares, - address referrer - ) public payable override returns (uint256) { + function depositAndMintOsToken(address receiver, uint256 osTokenShares, address referrer) + public + payable + override + returns (uint256) + { deposit(msg.sender, referrer); return mintOsToken(receiver, osTokenShares, referrer); } @@ -122,10 +116,7 @@ contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { } /// @inheritdoc VaultSubVaults - function _depositToVault( - address vault, - uint256 assets - ) internal override returns (uint256) { + function _depositToVault(address vault, uint256 assets) internal override returns (uint256) { return IVaultEthStaking(vault).deposit{value: assets}(address(this), address(0)); } @@ -135,10 +126,7 @@ contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { } /// @inheritdoc VaultEnterExit - function _transferVaultAssets( - address receiver, - uint256 assets - ) internal virtual override nonReentrant { + function _transferVaultAssets(address receiver, uint256 assets) internal virtual override nonReentrant { return Address.sendValue(payable(receiver), assets); } @@ -147,10 +135,7 @@ contract EthMetaVault is Initializable, MetaVault, IEthMetaVault { * @param admin The address of the admin of the Vault * @param params The parameters for initializing the MetaVault contract */ - function __EthMetaVault_init( - address admin, - MetaVaultInitParams memory params - ) internal onlyInitializing { + function __EthMetaVault_init(address admin, MetaVaultInitParams memory params) internal onlyInitializing { __MetaVault_init(admin, params); // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 diff --git a/contracts/vaults/ethereum/EthMetaVaultFactory.sol b/contracts/vaults/ethereum/EthMetaVaultFactory.sol index ee0da47e..f048f151 100644 --- a/contracts/vaults/ethereum/EthMetaVaultFactory.sol +++ b/contracts/vaults/ethereum/EthMetaVaultFactory.sol @@ -33,12 +33,7 @@ contract EthMetaVaultFactory is IEthMetaVaultFactory { } /// @inheritdoc IEthMetaVaultFactory - function createVault(bytes calldata params) - external - payable - override - returns (address vault) - { + function createVault(bytes calldata params) external payable override returns (address vault) { // create vault vault = address(new ERC1967Proxy(implementation, "")); diff --git a/contracts/vaults/ethereum/EthPrivMetaVault.sol b/contracts/vaults/ethereum/EthPrivMetaVault.sol index 01ac77ef..badd1eee 100644 --- a/contracts/vaults/ethereum/EthPrivMetaVault.sol +++ b/contracts/vaults/ethereum/EthPrivMetaVault.sol @@ -29,16 +29,18 @@ contract EthPrivMetaVault is Initializable, EthMetaVault, VaultWhitelist, IEthPr * @param args The arguments for initializing the EthMetaVault contract */ /// @custom:oz-upgrades-unsafe-allow constructor - constructor( - MetaVaultConstructorArgs memory args - ) EthMetaVault(args) { + constructor(MetaVaultConstructorArgs memory args) EthMetaVault(args) { _disableInitializers(); } /// @inheritdoc IEthMetaVault - function initialize( - bytes calldata params - ) external payable virtual override(IEthMetaVault, EthMetaVault) reinitializer(_version) { + function initialize(bytes calldata params) + external + payable + virtual + override(IEthMetaVault, EthMetaVault) + reinitializer(_version) + { // initialize deployed vault address _admin = IEthMetaVaultFactory(msg.sender).vaultAdmin(); __EthMetaVault_init(_admin, abi.decode(params, (MetaVaultInitParams))); @@ -47,10 +49,13 @@ contract EthPrivMetaVault is Initializable, EthMetaVault, VaultWhitelist, IEthPr } /// @inheritdoc IEthMetaVault - function deposit( - address receiver, - address referrer - ) public payable virtual override(IEthMetaVault, EthMetaVault) returns (uint256 shares) { + function deposit(address receiver, address referrer) + public + payable + virtual + override(IEthMetaVault, EthMetaVault) + returns (uint256 shares) + { _checkWhitelist(msg.sender); _checkWhitelist(receiver); return super.deposit(receiver, referrer); @@ -67,11 +72,12 @@ contract EthPrivMetaVault is Initializable, EthMetaVault, VaultWhitelist, IEthPr } /// @inheritdoc IVaultOsToken - function mintOsToken( - address receiver, - uint256 osTokenShares, - address referrer - ) public virtual override(IVaultOsToken, VaultOsToken) returns (uint256 assets) { + function mintOsToken(address receiver, uint256 osTokenShares, address referrer) + public + virtual + override(IVaultOsToken, VaultOsToken) + returns (uint256 assets) + { _checkWhitelist(msg.sender); return super.mintOsToken(receiver, osTokenShares, referrer); } diff --git a/contracts/vaults/ethereum/EthVaultFactory.sol b/contracts/vaults/ethereum/EthVaultFactory.sol index 787aadb7..c5c13b6d 100644 --- a/contracts/vaults/ethereum/EthVaultFactory.sol +++ b/contracts/vaults/ethereum/EthVaultFactory.sol @@ -36,12 +36,7 @@ contract EthVaultFactory is IEthVaultFactory { } /// @inheritdoc IEthVaultFactory - function createVault(bytes calldata params, bool isOwnMevEscrow) - external - payable - override - returns (address vault) - { + function createVault(bytes calldata params, bool isOwnMevEscrow) external payable override returns (address vault) { // create vault vault = address(new ERC1967Proxy(implementation, "")); diff --git a/contracts/vaults/gnosis/GnoErc20Vault.sol b/contracts/vaults/gnosis/GnoErc20Vault.sol index 4c9aa517..007cd59c 100644 --- a/contracts/vaults/gnosis/GnoErc20Vault.sol +++ b/contracts/vaults/gnosis/GnoErc20Vault.sol @@ -165,9 +165,7 @@ contract GnoErc20Vault is * @param ownMevEscrow The address of the MEV escrow owned by the Vault. Zero address if shared MEV escrow is used. * @param params The decoded parameters for initializing the GnoErc20Vault contract */ - function __GnoErc20Vault_init(address admin, address ownMevEscrow, GnoErc20VaultInitParams memory params) - internal - { + function __GnoErc20Vault_init(address admin, address ownMevEscrow, GnoErc20VaultInitParams memory params) internal { __VaultAdmin_init(admin, params.metadataIpfsHash); // fee recipient is initially set to admin address __VaultFee_init(admin, params.feePercent); diff --git a/contracts/vaults/gnosis/GnoMetaVault.sol b/contracts/vaults/gnosis/GnoMetaVault.sol index 6fbc1f66..e1429170 100644 --- a/contracts/vaults/gnosis/GnoMetaVault.sol +++ b/contracts/vaults/gnosis/GnoMetaVault.sol @@ -34,18 +34,13 @@ contract GnoMetaVault is Initializable, MetaVault, IGnoMetaVault { * @param args The arguments for initializing the MetaVault contract */ /// @custom:oz-upgrades-unsafe-allow constructor - constructor( - address gnoToken, - MetaVaultConstructorArgs memory args - ) MetaVault(args) { + constructor(address gnoToken, MetaVaultConstructorArgs memory args) MetaVault(args) { _gnoToken = IERC20(gnoToken); _disableInitializers(); } /// @inheritdoc IGnoMetaVault - function initialize( - bytes calldata params - ) external virtual override reinitializer(_version) { + function initialize(bytes calldata params) external virtual override reinitializer(_version) { // if admin is already set, it's an upgrade from version 3 to 4 if (admin != address(0)) { return; @@ -55,38 +50,33 @@ contract GnoMetaVault is Initializable, MetaVault, IGnoMetaVault { } /// @inheritdoc IGnoMetaVault - function deposit( - uint256 assets, - address receiver, - address referrer - ) public virtual override returns (uint256 shares) { + function deposit(uint256 assets, address receiver, address referrer) + public + virtual + override + returns (uint256 shares) + { // withdraw GNO tokens from the user SafeERC20.safeTransferFrom(_gnoToken, msg.sender, address(this), assets); shares = _deposit(receiver, assets, referrer); } /// @inheritdoc IVaultSubVaults - function addSubVault( - address vault - ) public virtual override(IVaultSubVaults, VaultSubVaults) { + function addSubVault(address vault) public virtual override(IVaultSubVaults, VaultSubVaults) { super.addSubVault(vault); // approve transferring GNO to sub-vault _gnoToken.approve(vault, type(uint256).max); } /// @inheritdoc IVaultSubVaults - function ejectSubVault( - address vault - ) public virtual override(IVaultSubVaults, VaultSubVaults) { + function ejectSubVault(address vault) public virtual override(IVaultSubVaults, VaultSubVaults) { super.ejectSubVault(vault); // revoke transferring GNO to sub-vault _gnoToken.approve(vault, 0); } /// @inheritdoc IGnoMetaVault - function donateAssets( - uint256 amount - ) external override nonReentrant { + function donateAssets(uint256 amount) external override nonReentrant { if (amount == 0) { revert Errors.InvalidAssets(); } @@ -107,10 +97,7 @@ contract GnoMetaVault is Initializable, MetaVault, IGnoMetaVault { } /// @inheritdoc VaultSubVaults - function _depositToVault( - address vault, - uint256 assets - ) internal override returns (uint256) { + function _depositToVault(address vault, uint256 assets) internal override returns (uint256) { return IVaultGnoStaking(vault).deposit(assets, address(this), address(0)); } @@ -120,10 +107,7 @@ contract GnoMetaVault is Initializable, MetaVault, IGnoMetaVault { } /// @inheritdoc VaultEnterExit - function _transferVaultAssets( - address receiver, - uint256 assets - ) internal virtual override nonReentrant { + function _transferVaultAssets(address receiver, uint256 assets) internal virtual override nonReentrant { SafeERC20.safeTransfer(_gnoToken, receiver, assets); } @@ -132,10 +116,7 @@ contract GnoMetaVault is Initializable, MetaVault, IGnoMetaVault { * @param admin The address of the admin of the Vault * @param params The parameters for initializing the MetaVault contract */ - function __GnoMetaVault_init( - address admin, - MetaVaultInitParams memory params - ) internal onlyInitializing { + function __GnoMetaVault_init(address admin, MetaVaultInitParams memory params) internal onlyInitializing { __MetaVault_init(admin, params); _deposit(address(this), _securityDeposit, address(0)); diff --git a/contracts/vaults/gnosis/GnoMetaVaultFactory.sol b/contracts/vaults/gnosis/GnoMetaVaultFactory.sol index 7e913fd2..8b51ef67 100644 --- a/contracts/vaults/gnosis/GnoMetaVaultFactory.sol +++ b/contracts/vaults/gnosis/GnoMetaVaultFactory.sol @@ -34,8 +34,7 @@ contract GnoMetaVaultFactory is IGnoMetaVaultFactory { * @param vaultsRegistry The address of the VaultsRegistry contract * @param gnoToken The address of the GNO token contract */ - constructor(address _implementation, IVaultsRegistry vaultsRegistry, address gnoToken) - { + constructor(address _implementation, IVaultsRegistry vaultsRegistry, address gnoToken) { implementation = _implementation; _vaultsRegistry = vaultsRegistry; _gnoToken = IERC20(gnoToken); diff --git a/contracts/vaults/gnosis/GnoPrivMetaVault.sol b/contracts/vaults/gnosis/GnoPrivMetaVault.sol index 4e3f158d..fe72671a 100644 --- a/contracts/vaults/gnosis/GnoPrivMetaVault.sol +++ b/contracts/vaults/gnosis/GnoPrivMetaVault.sol @@ -27,17 +27,17 @@ contract GnoPrivMetaVault is Initializable, GnoMetaVault, VaultWhitelist, IGnoPr * @param args The arguments for initializing the GnoMetaVault contract */ /// @custom:oz-upgrades-unsafe-allow constructor - constructor( - address gnoToken, - MetaVaultConstructorArgs memory args - ) GnoMetaVault(gnoToken, args) { + constructor(address gnoToken, MetaVaultConstructorArgs memory args) GnoMetaVault(gnoToken, args) { _disableInitializers(); } /// @inheritdoc IGnoMetaVault - function initialize( - bytes calldata params - ) external virtual override(IGnoMetaVault, GnoMetaVault) reinitializer(_version) { + function initialize(bytes calldata params) + external + virtual + override(IGnoMetaVault, GnoMetaVault) + reinitializer(_version) + { // initialize deployed vault address _admin = IGnoMetaVaultFactory(msg.sender).vaultAdmin(); __GnoMetaVault_init(_admin, abi.decode(params, (MetaVaultInitParams))); @@ -46,22 +46,24 @@ contract GnoPrivMetaVault is Initializable, GnoMetaVault, VaultWhitelist, IGnoPr } /// @inheritdoc IGnoMetaVault - function deposit( - uint256 assets, - address receiver, - address referrer - ) public virtual override(IGnoMetaVault, GnoMetaVault) returns (uint256 shares) { + function deposit(uint256 assets, address receiver, address referrer) + public + virtual + override(IGnoMetaVault, GnoMetaVault) + returns (uint256 shares) + { _checkWhitelist(msg.sender); _checkWhitelist(receiver); return super.deposit(assets, receiver, referrer); } /// @inheritdoc IVaultOsToken - function mintOsToken( - address receiver, - uint256 osTokenShares, - address referrer - ) public virtual override(IVaultOsToken, VaultOsToken) returns (uint256 assets) { + function mintOsToken(address receiver, uint256 osTokenShares, address referrer) + public + virtual + override(IVaultOsToken, VaultOsToken) + returns (uint256 assets) + { _checkWhitelist(msg.sender); return super.mintOsToken(receiver, osTokenShares, referrer); } diff --git a/contracts/vaults/modules/VaultEnterExit.sol b/contracts/vaults/modules/VaultEnterExit.sol index 1acdd044..4e91d4cd 100644 --- a/contracts/vaults/modules/VaultEnterExit.sol +++ b/contracts/vaults/modules/VaultEnterExit.sol @@ -40,12 +40,7 @@ abstract contract VaultEnterExit is VaultImmutables, Initializable, VaultState, } /// @inheritdoc IVaultEnterExit - function enterExitQueue(uint256 shares, address receiver) - public - virtual - override - returns (uint256 positionTicket) - { + function enterExitQueue(uint256 shares, address receiver) public virtual override returns (uint256 positionTicket) { return _enterExitQueue(msg.sender, shares, receiver); } diff --git a/contracts/vaults/modules/VaultGnoStaking.sol b/contracts/vaults/modules/VaultGnoStaking.sol index b13c966f..88b6872c 100644 --- a/contracts/vaults/modules/VaultGnoStaking.sol +++ b/contracts/vaults/modules/VaultGnoStaking.sol @@ -94,13 +94,14 @@ abstract contract VaultGnoStaking is depositData.depositAmount /= 32; // deposit GNO tokens to the validators registry - IGnoValidatorsRegistry(_validatorsRegistry).deposit( - depositData.publicKey, - depositData.withdrawalCredentials, - depositData.signature, - depositData.depositDataRoot, - depositData.depositAmount - ); + IGnoValidatorsRegistry(_validatorsRegistry) + .deposit( + depositData.publicKey, + depositData.withdrawalCredentials, + depositData.signature, + depositData.depositDataRoot, + depositData.depositAmount + ); // will revert if not enough assets availableAssets -= depositData.depositAmount; diff --git a/contracts/vaults/modules/VaultOsToken.sol b/contracts/vaults/modules/VaultOsToken.sol index de2aeb4e..af80f443 100644 --- a/contracts/vaults/modules/VaultOsToken.sol +++ b/contracts/vaults/modules/VaultOsToken.sol @@ -97,11 +97,7 @@ abstract contract VaultOsToken is VaultImmutables, VaultState, VaultEnterExit, I } /// @inheritdoc IVaultOsToken - function transferOsTokenPositionToEscrow(uint256 osTokenShares) - external - override - returns (uint256 positionTicket) - { + function transferOsTokenPositionToEscrow(uint256 osTokenShares) external override returns (uint256 positionTicket) { // check whether vault assets are up to date _checkHarvested(); diff --git a/contracts/vaults/modules/VaultValidators.sol b/contracts/vaults/modules/VaultValidators.sol index b43db359..1057f82e 100644 --- a/contracts/vaults/modules/VaultValidators.sol +++ b/contracts/vaults/modules/VaultValidators.sol @@ -101,11 +101,9 @@ abstract contract VaultValidators is _checkHarvested(); // check access - if ( - !_isValidatorsManager( + if (!_isValidatorsManager( keeperParams.validators, keeperParams.validatorsRegistryRoot, validatorsManagerSignature - ) - ) { + )) { revert Errors.AccessDenied(); } @@ -249,9 +247,8 @@ abstract contract VaultValidators is // migrate deposit data variables to DepositDataRegistry contract if (__deprecated__validatorsRoot != bytes32(0)) { - IDepositDataRegistry(_depositDataRegistry).migrate( - __deprecated__validatorsRoot, __deprecated__validatorIndex, validatorsManager - ); + IDepositDataRegistry(_depositDataRegistry) + .migrate(__deprecated__validatorsRoot, __deprecated__validatorIndex, validatorsManager); delete __deprecated__validatorIndex; delete __deprecated__validatorsRoot; delete validatorsManager; diff --git a/test/EthErc20Vault.t.sol b/test/EthErc20Vault.t.sol index 5628b616..837060f5 100644 --- a/test/EthErc20Vault.t.sol +++ b/test/EthErc20Vault.t.sol @@ -317,7 +317,10 @@ contract EthErc20VaultTest is Test, EthHelpers { assertGt(mintedAssets, 0, "Should have minted some osToken assets"); assertEq(vault.osTokenPositions(sender), osTokenShares, "Should have minted expected osToken shares"); assertApproxEqAbs( - vault.balanceOf(sender), vault.convertToShares(depositAmount), 1, "Should have received tokens for the deposit" + vault.balanceOf(sender), + vault.convertToShares(depositAmount), + 1, + "Should have received tokens for the deposit" ); } diff --git a/test/EthOsTokenRedeemer.t.sol b/test/EthOsTokenRedeemer.t.sol index 4a62f2b0..2922641a 100644 --- a/test/EthOsTokenRedeemer.t.sol +++ b/test/EthOsTokenRedeemer.t.sol @@ -1664,10 +1664,7 @@ contract EthOsTokenRedeemerTest is Test, EthHelpers { // The balance increase should approximately match the redeemed amount assertApproxEqRel( - metaVaultBalanceAfter - metaVaultBalanceBefore, - redeemed, - 5e16, - "Balance increase should match redeemed" + metaVaultBalanceAfter - metaVaultBalanceBefore, redeemed, 5e16, "Balance increase should match redeemed" ); } diff --git a/test/EthOsTokenVaultEscrow.t.sol b/test/EthOsTokenVaultEscrow.t.sol index aa406e38..52318775 100644 --- a/test/EthOsTokenVaultEscrow.t.sol +++ b/test/EthOsTokenVaultEscrow.t.sol @@ -540,11 +540,12 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { vm.prank(user); _startSnapshotGas("EthOsTokenVaultEscrowTest_test_claimExitedAssets_insufficientShares"); vm.expectRevert(Errors.InvalidShares.selector); - contracts.osTokenVaultEscrow.claimExitedAssets( - address(vault), - exitPositionTicket, - osTokenShares * 2 // More than available - ); + contracts.osTokenVaultEscrow + .claimExitedAssets( + address(vault), + exitPositionTicket, + osTokenShares * 2 // More than available + ); _stopSnapshotGas(); } @@ -794,9 +795,8 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { // Liquidate the position vm.prank(liquidator); _startSnapshotGas("OsTokenLiquidationTest_test_liquidateOsToken_success"); - contracts.osTokenVaultEscrow.liquidateOsToken( - address(vault), exitPositionTicket_, liquidationShares, liquidator - ); + contracts.osTokenVaultEscrow + .liquidateOsToken(address(vault), exitPositionTicket_, liquidationShares, liquidator); _stopSnapshotGas(); // Get position details after liquidation @@ -942,9 +942,8 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { // Liquidate half the position vm.prank(liquidator); _startSnapshotGas("OsTokenLiquidationTest_test_liquidateOsToken_partialLiquidation"); - contracts.osTokenVaultEscrow.liquidateOsToken( - address(vault), exitPositionTicket, halfLiquidationShares, liquidator - ); + contracts.osTokenVaultEscrow + .liquidateOsToken(address(vault), exitPositionTicket, halfLiquidationShares, liquidator); _stopSnapshotGas(); // push down the stack @@ -1066,9 +1065,8 @@ contract EthOsTokenVaultEscrowTest is Test, EthHelpers { vm.prank(unauthorizedCaller); _startSnapshotGas("OsTokenLiquidationTest_test_redeemOsToken_notRedeemer"); vm.expectRevert(Errors.AccessDenied.selector); - contracts.osTokenVaultEscrow.redeemOsToken( - address(vault), exitPositionTicket, osTokenShares, unauthorizedCaller - ); + contracts.osTokenVaultEscrow + .redeemOsToken(address(vault), exitPositionTicket, osTokenShares, unauthorizedCaller); _stopSnapshotGas(); // Clear the mock diff --git a/test/EthVault.t.sol b/test/EthVault.t.sol index de3b6998..374ce49f 100644 --- a/test/EthVault.t.sol +++ b/test/EthVault.t.sol @@ -178,8 +178,7 @@ contract EthVaultTest is Test, EthHelpers { uint256 senderSharesBefore = vault.getShares(sender); ( uint128 queuedSharesBefore, - uint128 unclaimedAssetsBefore, - , + uint128 unclaimedAssetsBefore,, uint128 totalExitingAssetsBefore, uint256 totalTicketsBefore ) = vault.getExitQueueData(); @@ -196,8 +195,7 @@ contract EthVaultTest is Test, EthHelpers { ( uint128 queuedSharesAfter, - uint128 unclaimedAssetsAfter, - , + uint128 unclaimedAssetsAfter,, uint128 totalExitingAssetsAfter, uint256 totalTicketsAfter ) = vault.getExitQueueData(); diff --git a/test/KeeperValidators.t.sol b/test/KeeperValidators.t.sol index 227d0676..665e9326 100644 --- a/test/KeeperValidators.t.sol +++ b/test/KeeperValidators.t.sol @@ -242,12 +242,13 @@ contract KeeperValidatorsTest is Test, EthHelpers { // Act & Assert: Call should fail due to invalid vault _startSnapshotGas("KeeperValidatorsTest_test_updateExitSignatures_invalidVault"); vm.expectRevert(Errors.InvalidVault.selector); - contracts.keeper.updateExitSignatures( - address(contracts.keeper), // Using Keeper as vault (invalid) - deadline, - exitSignaturesIpfsHash, - signatures - ); + contracts.keeper + .updateExitSignatures( + address(contracts.keeper), // Using Keeper as vault (invalid) + deadline, + exitSignaturesIpfsHash, + signatures + ); _stopSnapshotGas(); // Clean up diff --git a/test/OsTokenConfig.t.sol b/test/OsTokenConfig.t.sol index 65e349e3..91c6ccb6 100644 --- a/test/OsTokenConfig.t.sol +++ b/test/OsTokenConfig.t.sol @@ -360,9 +360,7 @@ contract OsTokenConfigTest is Test, EthHelpers { uint128 newLiqBonusPercent = 1.1e18; // 110% IOsTokenConfig.Config memory newDefaultConfig = IOsTokenConfig.Config({ - ltvPercent: newLtvPercent, - liqThresholdPercent: newLiqThresholdPercent, - liqBonusPercent: newLiqBonusPercent + ltvPercent: newLtvPercent, liqThresholdPercent: newLiqThresholdPercent, liqBonusPercent: newLiqBonusPercent }); // Test address that doesn't have a specific config diff --git a/test/SharedMevEscrow.t.sol b/test/SharedMevEscrow.t.sol index a4a2024d..1b4c29b7 100644 --- a/test/SharedMevEscrow.t.sol +++ b/test/SharedMevEscrow.t.sol @@ -19,7 +19,7 @@ contract MockVaultEthStaking { // Mock non-compliant vault that doesn't implement receiveFromMevEscrow contract MockNonCompliantVault { -// No receiveFromMevEscrow function + // No receiveFromMevEscrow function } contract SharedMevEscrowTest is Test { diff --git a/test/VaultEnterExit.t.sol b/test/VaultEnterExit.t.sol index 1b84674b..ce54e585 100644 --- a/test/VaultEnterExit.t.sol +++ b/test/VaultEnterExit.t.sol @@ -448,12 +448,13 @@ contract VaultEnterExitTest is Test, EthHelpers { function test_calculateExitedAssets_invalidPosition() public { // Try to calculate exited assets for a non-existent position _startSnapshotGas("VaultEnterExitTest_test_calculateExitedAssets_invalidPosition"); - (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) = vault.calculateExitedAssets( - receiver, - 999, // Non-existent position ticket - vm.getBlockTimestamp(), - 0 - ); + (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) = + vault.calculateExitedAssets( + receiver, + 999, // Non-existent position ticket + vm.getBlockTimestamp(), + 0 + ); _stopSnapshotGas(); assertEq(leftTickets, 0, "Left tickets should be 0 for non-existent position"); diff --git a/test/VaultOsToken.t.sol b/test/VaultOsToken.t.sol index 230d20a3..424d2ff7 100644 --- a/test/VaultOsToken.t.sol +++ b/test/VaultOsToken.t.sol @@ -1235,9 +1235,10 @@ contract VaultOsTokenTest is Test, EthHelpers { // Process the exited assets _startSnapshotGas("VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process"); - contracts.osTokenVaultEscrow.processExitedAssets( - address(vault), exitPositionTicket, timestamp, uint256(vault.getExitQueueIndex(exitPositionTicket)) - ); + contracts.osTokenVaultEscrow + .processExitedAssets( + address(vault), exitPositionTicket, timestamp, uint256(vault.getExitQueueIndex(exitPositionTicket)) + ); _stopSnapshotGas(); // Record user's ETH balance before claiming diff --git a/test/VaultValidators.t.sol b/test/VaultValidators.t.sol index e3e81a21..c86d3f30 100644 --- a/test/VaultValidators.t.sol +++ b/test/VaultValidators.t.sol @@ -533,8 +533,9 @@ contract VaultValidatorsTest is Test, EthHelpers { bytes memory validTopUpData = bytes.concat(publicKey, signature, depositDataRoot, bytes8(uint64(topUpAmount))); // Create validator manager signature - bytes32 message = - _getValidatorsManagerSigningMessage(address(vault), bytes32(vault.validatorsManagerNonce()), validTopUpData); + bytes32 message = _getValidatorsManagerSigningMessage( + address(vault), bytes32(vault.validatorsManagerNonce()), validTopUpData + ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(validatorsManagerPrivateKey, message); bytes memory validatorManagerSignature = abi.encodePacked(r, s, v); @@ -625,8 +626,9 @@ contract VaultValidatorsTest is Test, EthHelpers { // Create invalid signature (wrong signer) (, uint256 wrongPrivateKey) = makeAddrAndKey("wrong"); - bytes32 message = - _getValidatorsManagerSigningMessage(address(vault), bytes32(vault.validatorsManagerNonce()), validTopUpData); + bytes32 message = _getValidatorsManagerSigningMessage( + address(vault), bytes32(vault.validatorsManagerNonce()), validTopUpData + ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, message); bytes memory invalidSignature = abi.encodePacked(r, s, v); @@ -851,8 +853,9 @@ contract VaultValidatorsTest is Test, EthHelpers { bytes memory withdrawalData = abi.encodePacked(publicKey, bytes8(uint64(withdrawalAmount))); // 3. Create validator manager signature - bytes32 message = - _getValidatorsManagerSigningMessage(address(vault), bytes32(vault.validatorsManagerNonce()), withdrawalData); + bytes32 message = _getValidatorsManagerSigningMessage( + address(vault), bytes32(vault.validatorsManagerNonce()), withdrawalData + ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(validatorsManagerPrivateKey, message); bytes memory signature = abi.encodePacked(r, s, v); @@ -910,8 +913,9 @@ contract VaultValidatorsTest is Test, EthHelpers { // 3. Create invalid signature (wrong signer) (, uint256 wrongPrivateKey) = makeAddrAndKey("wrong"); - bytes32 message = - _getValidatorsManagerSigningMessage(address(vault), bytes32(vault.validatorsManagerNonce()), withdrawalData); + bytes32 message = _getValidatorsManagerSigningMessage( + address(vault), bytes32(vault.validatorsManagerNonce()), withdrawalData + ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, message); bytes memory invalidSignature = abi.encodePacked(r, s, v); diff --git a/test/gnosis/GnoMetaVault.t.sol b/test/gnosis/GnoMetaVault.t.sol index 0f142755..d37e3271 100644 --- a/test/gnosis/GnoMetaVault.t.sol +++ b/test/gnosis/GnoMetaVault.t.sol @@ -20,7 +20,6 @@ import {GnoHelpers} from "../helpers/GnoHelpers.sol"; import {IKeeperRewards} from "../../contracts/interfaces/IKeeperRewards.sol"; contract GnoMetaVaultTest is Test, GnoHelpers { - ForkContracts public contracts; GnoMetaVault public metaVault; @@ -92,7 +91,6 @@ contract GnoMetaVaultTest is Test, GnoHelpers { return _createVault(VaultType.GnoVault, _admin, initParams, false); } - function test_deployment() public view { // Verify the vault was deployed correctly assertEq(metaVault.vaultId(), keccak256("GnoMetaVault"), "Incorrect vault ID"); diff --git a/test/gnosis/GnoOsTokenVaultEscrow.t.sol b/test/gnosis/GnoOsTokenVaultEscrow.t.sol index 165ba38c..2c123fcf 100644 --- a/test/gnosis/GnoOsTokenVaultEscrow.t.sol +++ b/test/gnosis/GnoOsTokenVaultEscrow.t.sol @@ -99,9 +99,10 @@ contract GnoOsTokenVaultEscrowTest is Test, GnoHelpers { vm.warp(timestamp + _exitingAssetsClaimDelay + 1); _startSnapshotGas("GnoOsTokenVaultEscrowTest_test_transferAssets_process"); - contracts.osTokenVaultEscrow.processExitedAssets( - address(vault), exitPositionTicket, timestamp, uint256(vault.getExitQueueIndex(exitPositionTicket)) - ); + contracts.osTokenVaultEscrow + .processExitedAssets( + address(vault), exitPositionTicket, timestamp, uint256(vault.getExitQueueIndex(exitPositionTicket)) + ); _stopSnapshotGas(); // User claims exited assets diff --git a/test/gnosis/GnoRewardSplitter.t.sol b/test/gnosis/GnoRewardSplitter.t.sol index 171780cd..3dec50f8 100644 --- a/test/gnosis/GnoRewardSplitter.t.sol +++ b/test/gnosis/GnoRewardSplitter.t.sol @@ -317,9 +317,8 @@ contract GnoRewardSplitterTest is Test, GnoHelpers { int256 exitQueueIndex = IVaultEnterExit(vault).getExitQueueIndex(positionTicket); // Expected reward amount to be claimed - (,, uint256 exitedAssets) = IVaultEnterExit(vault).calculateExitedAssets( - address(rewardSplitter), positionTicket, timestamp, uint256(exitQueueIndex) - ); + (,, uint256 exitedAssets) = IVaultEnterExit(vault) + .calculateExitedAssets(address(rewardSplitter), positionTicket, timestamp, uint256(exitQueueIndex)); vm.prank(admin); vm.expectEmit(true, false, false, true); diff --git a/test/gnosis/GnoVault.t.sol b/test/gnosis/GnoVault.t.sol index 02e217c3..8fb0d1ca 100644 --- a/test/gnosis/GnoVault.t.sol +++ b/test/gnosis/GnoVault.t.sol @@ -257,8 +257,7 @@ contract GnoVaultTest is Test, GnoHelpers { uint256 senderSharesBefore = vault.getShares(sender); ( uint128 queuedSharesBefore, - uint128 unclaimedAssetsBefore, - , + uint128 unclaimedAssetsBefore,, uint128 totalExitingAssetsBefore, uint256 totalTicketsBefore ) = vault.getExitQueueData(); @@ -275,8 +274,7 @@ contract GnoVaultTest is Test, GnoHelpers { ( uint128 queuedSharesAfter, - uint128 unclaimedAssetsAfter, - , + uint128 unclaimedAssetsAfter,, uint128 totalExitingAssetsAfter, uint256 totalTicketsAfter ) = vault.getExitQueueData(); diff --git a/test/helpers/EthHelpers.sol b/test/helpers/EthHelpers.sol index 9a8bec08..186067f8 100644 --- a/test/helpers/EthHelpers.sol +++ b/test/helpers/EthHelpers.sol @@ -390,7 +390,7 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { vm.deal(admin, admin.balance + 1 ether); vm.prank(admin); - vaultContract.upgradeToAndCall(newImpl, "0x"); + vaultContract.upgradeToAndCall(newImpl, ""); } function _getOrCreateVaultImpl(VaultType _vaultType) internal returns (address impl) { @@ -498,9 +498,8 @@ abstract contract EthHelpers is Test, ValidatorsHelpers { } function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(_keeper).sig("rewards(address)").with_key(vault).depth(1).checked_write( - rewardsNonce - ); + stdstore.enable_packed_slots().target(_keeper).sig("rewards(address)").with_key(vault).depth(1) + .checked_write(rewardsNonce); } function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { diff --git a/test/helpers/GnoHelpers.sol b/test/helpers/GnoHelpers.sol index 6f6a4ca1..16b22812 100644 --- a/test/helpers/GnoHelpers.sol +++ b/test/helpers/GnoHelpers.sol @@ -210,8 +210,7 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { } address impl = _getOrCreateVaultImpl(_vaultType); - GnoMetaVaultFactory factory = - new GnoMetaVaultFactory(impl, IVaultsRegistry(_vaultsRegistry), _gnoToken); + GnoMetaVaultFactory factory = new GnoMetaVaultFactory(impl, IVaultsRegistry(_vaultsRegistry), _gnoToken); _vaultFactories[_vaultType] = address(factory); @@ -381,7 +380,7 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { vm.deal(admin, admin.balance + 1 ether); vm.prank(admin); - vaultContract.upgradeToAndCall(newImpl, "0x"); + vaultContract.upgradeToAndCall(newImpl, ""); } function _getOrCreateVaultImpl(VaultType _vaultType) internal returns (address impl) { @@ -477,9 +476,8 @@ abstract contract GnoHelpers is Test, ValidatorsHelpers { } function _setVaultRewardsNonce(address vault, uint64 rewardsNonce) internal { - stdstore.enable_packed_slots().target(_keeper).sig("rewards(address)").with_key(vault).depth(1).checked_write( - rewardsNonce - ); + stdstore.enable_packed_slots().target(_keeper).sig("rewards(address)").with_key(vault).depth(1) + .checked_write(rewardsNonce); } function _setKeeperRewardsNonce(uint64 rewardsNonce) internal { From 5b4473c59adc2d13e633f8dccf6236fa589e0679 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 19 Jan 2026 17:36:57 +0200 Subject: [PATCH 17/19] Update gas snapshots --- snapshots/DepositDataRegistryTest.json | 14 ++-- snapshots/EthBlocklistErc20VaultTest.json | 12 +-- snapshots/EthBlocklistVaultTest.json | 10 +-- snapshots/EthErc20VaultTest.json | 26 +++---- snapshots/EthMetaVaultTest.json | 30 ++++---- snapshots/EthOsTokenRedeemerTest.json | 4 +- snapshots/EthOsTokenVaultEscrowTest.json | 34 ++++----- snapshots/EthPrivErc20VaultTest.json | 14 ++-- snapshots/EthPrivVaultTest.json | 18 ++--- snapshots/EthRewardSplitterTest.json | 22 +++--- snapshots/EthVaultTest.json | 16 ++-- snapshots/GnoBlocklistErc20VaultTest.json | 2 +- snapshots/GnoBlocklistVaultTest.json | 8 +- snapshots/GnoErc20VaultTest.json | 18 ++--- snapshots/GnoGenesisVaultTest.json | 2 +- snapshots/GnoOsTokenRedeemerTest.json | 2 +- snapshots/GnoOsTokenVaultEscrowTest.json | 6 +- snapshots/GnoPrivErc20VaultTest.json | 10 +-- snapshots/GnoPrivVaultTest.json | 12 +-- snapshots/GnoRewardSplitterTest.json | 24 +++--- snapshots/GnoVaultExitQueueTest.json | 2 +- snapshots/GnoVaultTest.json | 10 +-- snapshots/KeeperOraclesTest.json | 2 +- snapshots/KeeperRewardsTest.json | 4 +- snapshots/VaultAdminTest.json | 18 ++--- snapshots/VaultEnterExitTest.json | 40 +++++----- snapshots/VaultEthStakingTest.json | 32 ++++---- snapshots/VaultFeeTest.json | 26 +++---- snapshots/VaultGnoStakingTest.json | 22 +++--- snapshots/VaultOsTokenTest.json | 92 +++++++++++------------ snapshots/VaultSubVaultsTest.json | 36 ++++----- snapshots/VaultTokenTest.json | 40 +++++----- snapshots/VaultValidatorsTest.json | 82 ++++++++++---------- snapshots/VaultVersionTest.json | 20 ++--- 34 files changed, 355 insertions(+), 355 deletions(-) diff --git a/snapshots/DepositDataRegistryTest.json b/snapshots/DepositDataRegistryTest.json index 4c42af58..eeae33a8 100644 --- a/snapshots/DepositDataRegistryTest.json +++ b/snapshots/DepositDataRegistryTest.json @@ -1,9 +1,9 @@ { - "DepositDataRegistryTest_test_registerValidator_succeedsWith0x01Validator": "273443", - "DepositDataRegistryTest_test_registerValidator_succeedsWith0x02Validator": "296337", - "DepositDataRegistryTest_test_registerValidators_successWith0x01Validators": "314933", - "DepositDataRegistryTest_test_registerValidators_successWith0x02Validators": "339002", - "DepositDataRegistryTest_test_setDepositDataManager_succeeds": "68682", - "DepositDataRegistryTest_test_setDepositDataRoot_succeeds": "65115", - "DepositDataRegistryTest_test_updateVaultState_succeeds": "128278" + "DepositDataRegistryTest_test_registerValidator_succeedsWith0x01Validator": "252873", + "DepositDataRegistryTest_test_registerValidator_succeedsWith0x02Validator": "275779", + "DepositDataRegistryTest_test_registerValidators_successWith0x01Validators": "294364", + "DepositDataRegistryTest_test_registerValidators_successWith0x02Validators": "318425", + "DepositDataRegistryTest_test_setDepositDataManager_succeeds": "68540", + "DepositDataRegistryTest_test_setDepositDataRoot_succeeds": "45950", + "DepositDataRegistryTest_test_updateVaultState_succeeds": "112878" } \ No newline at end of file diff --git a/snapshots/EthBlocklistErc20VaultTest.json b/snapshots/EthBlocklistErc20VaultTest.json index ede6ef52..12073b0e 100644 --- a/snapshots/EthBlocklistErc20VaultTest.json +++ b/snapshots/EthBlocklistErc20VaultTest.json @@ -1,8 +1,8 @@ { - "EthBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "89912", - "EthBlocklistErc20VaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "84302", - "EthBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "161501", - "EthBlocklistErc20VaultTest_test_deploysCorrectly": "625440", - "EthBlocklistErc20VaultTest_test_transfer": "61693", - "EthBlocklistErc20VaultTest_test_upgradesCorrectly": "119882" + "EthBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "90179", + "EthBlocklistErc20VaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "83416", + "EthBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "162374", + "EthBlocklistErc20VaultTest_test_deploysCorrectly": "7141423", + "EthBlocklistErc20VaultTest_test_transfer": "62311", + "EthBlocklistErc20VaultTest_test_upgradesCorrectly": "5912023" } \ No newline at end of file diff --git a/snapshots/EthBlocklistVaultTest.json b/snapshots/EthBlocklistVaultTest.json index 266ac9f6..019791ff 100644 --- a/snapshots/EthBlocklistVaultTest.json +++ b/snapshots/EthBlocklistVaultTest.json @@ -1,7 +1,7 @@ { - "EthBlocklistVaultTest_test_canDepositAsNonBlockedUser": "87959", - "EthBlocklistVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "82360", - "EthBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "161528", - "EthBlocklistVaultTest_test_deploysCorrectly": "550241", - "EthBlocklistVaultTest_test_upgradesCorrectly": "119213" + "EthBlocklistVaultTest_test_canDepositAsNonBlockedUser": "88065", + "EthBlocklistVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "81588", + "EthBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "162185", + "EthBlocklistVaultTest_test_deploysCorrectly": "6424841", + "EthBlocklistVaultTest_test_upgradesCorrectly": "5269766" } \ No newline at end of file diff --git a/snapshots/EthErc20VaultTest.json b/snapshots/EthErc20VaultTest.json index 787ccce1..e21b071a 100644 --- a/snapshots/EthErc20VaultTest.json +++ b/snapshots/EthErc20VaultTest.json @@ -1,15 +1,15 @@ { - "EthErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90077", - "EthErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96608", - "EthErc20VaultTest_test_deploysCorrectly": "455018", - "EthErc20VaultTest_test_depositAndMintOsToken": "203747", - "EthErc20VaultTest_test_depositViaReceiveFallback_emitsTransfer": "77951", - "EthErc20VaultTest_test_deposit_emitsTransfer": "81413", - "EthErc20VaultTest_test_enterExitQueue_emitsTransfer": "89780", - "EthErc20VaultTest_test_redeem_emitsEvent": "58285", - "EthErc20VaultTest_test_updateExitQueue_emitsTransfer": "177065", - "EthErc20VaultTest_test_updateStateAndDepositAndMintOsToken": "230047", - "EthErc20VaultTest_test_upgradesCorrectly": "119756", - "EthErc20VaultTest_test_withdrawValidator_unknown": "55951", - "EthErc20VaultTest_test_withdrawValidator_validatorsManager": "74092" + "EthErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90024", + "EthErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96042", + "EthErc20VaultTest_test_deploysCorrectly": "1181159", + "EthErc20VaultTest_test_depositAndMintOsToken": "183195", + "EthErc20VaultTest_test_depositViaReceiveFallback_emitsTransfer": "80036", + "EthErc20VaultTest_test_deposit_emitsTransfer": "83498", + "EthErc20VaultTest_test_enterExitQueue_emitsTransfer": "79620", + "EthErc20VaultTest_test_redeem_emitsEvent": "63109", + "EthErc20VaultTest_test_updateExitQueue_emitsTransfer": "145031", + "EthErc20VaultTest_test_updateStateAndDepositAndMintOsToken": "214650", + "EthErc20VaultTest_test_upgradesCorrectly": "119424", + "EthErc20VaultTest_test_withdrawValidator_unknown": "55874", + "EthErc20VaultTest_test_withdrawValidator_validatorsManager": "74015" } \ No newline at end of file diff --git a/snapshots/EthMetaVaultTest.json b/snapshots/EthMetaVaultTest.json index 520a8996..e86f5499 100644 --- a/snapshots/EthMetaVaultTest.json +++ b/snapshots/EthMetaVaultTest.json @@ -1,17 +1,17 @@ { - "EthMetaVaultTest_test_calculateSubVaultsRedemptions_exactWithdrawableAssets": "47214", - "EthMetaVaultTest_test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets": "96978", - "EthMetaVaultTest_test_calculateSubVaultsRedemptions_success": "97007", - "EthMetaVaultTest_test_deposit": "85504", - "EthMetaVaultTest_test_depositAndMintOsToken": "179869", - "EthMetaVaultTest_test_depositViaFallback": "82122", - "EthMetaVaultTest_test_isStateUpdateRequired_true": "136011", - "EthMetaVaultTest_test_redeemSubVaultsAssets_noRedeemRequests": "69102", - "EthMetaVaultTest_test_redeemSubVaultsAssets_noRoundingErrors": "549703", - "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets": "548661", - "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets": "549679", - "EthMetaVaultTest_test_redeemSubVaultsAssets_success": "549679", - "EthMetaVaultTest_test_updateStateAndDeposit": "197478", - "EthMetaVaultTest_test_updateStateAndDepositAndMintOsToken": "200840", - "EthMetaVaultTest_test_userClaimExitedAssets": "54213" + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_exactWithdrawableAssets": "49570", + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets": "114252", + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_success": "114288", + "EthMetaVaultTest_test_deposit": "85427", + "EthMetaVaultTest_test_depositAndMintOsToken": "177450", + "EthMetaVaultTest_test_depositViaFallback": "82045", + "EthMetaVaultTest_test_isStateUpdateRequired_true": "135934", + "EthMetaVaultTest_test_redeemSubVaultsAssets_noRedeemRequests": "71422", + "EthMetaVaultTest_test_redeemSubVaultsAssets_noRoundingErrors": "699872", + "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets": "699884", + "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets": "698818", + "EthMetaVaultTest_test_redeemSubVaultsAssets_success": "698818", + "EthMetaVaultTest_test_updateStateAndDeposit": "230401", + "EthMetaVaultTest_test_updateStateAndDepositAndMintOsToken": "201506", + "EthMetaVaultTest_test_userClaimExitedAssets": "83553" } \ No newline at end of file diff --git a/snapshots/EthOsTokenRedeemerTest.json b/snapshots/EthOsTokenRedeemerTest.json index 03035fdf..c0661bfa 100644 --- a/snapshots/EthOsTokenRedeemerTest.json +++ b/snapshots/EthOsTokenRedeemerTest.json @@ -3,8 +3,8 @@ "EthOsTokenRedeemerTest_test_permitOsToken_success": "82575", "EthOsTokenRedeemerTest_test_processExitQueue_nothingToProcess": "33998", "EthOsTokenRedeemerTest_test_processExitQueue_success": "101715", - "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions": "297443", - "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "201436", + "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions": "275836", + "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "201282", "EthOsTokenRedeemerTest_test_setPositionsManager": "35834", "EthOsTokenRedeemerTest_test_setRedeemablePositions_success": "100561", "EthOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "101264", diff --git a/snapshots/EthOsTokenVaultEscrowTest.json b/snapshots/EthOsTokenVaultEscrowTest.json index 33bc1d77..303aaf07 100644 --- a/snapshots/EthOsTokenVaultEscrowTest.json +++ b/snapshots/EthOsTokenVaultEscrowTest.json @@ -1,18 +1,18 @@ { - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_insufficientShares": "66982", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_minimalAmount": "95063", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_noProcessedAssets": "91451", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_insufficientShares": "66561", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_minimalAmount": "95171", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_noProcessedAssets": "91032", "EthOsTokenVaultEscrowTest_test_claimExitedAssets_nonExistentPosition": "50879", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_notOwner": "65015", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_withFeeAccrual": "103958", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_claimExitedAssets": "80440", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_exitRequestNotProcessed": "49774", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_notOwner": "64594", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_withFeeAccrual": "104066", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_claimExitedAssets": "80548", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_exitRequestNotProcessed": "49805", "EthOsTokenVaultEscrowTest_test_processExitedAssets_invalidPosition": "33255", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_partialClaim": "95637", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_success": "71278", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_partialClaim": "95745", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_success": "78632", "EthOsTokenVaultEscrowTest_test_register_accessDenied": "33699", "EthOsTokenVaultEscrowTest_test_register_directCall": "83600", - "EthOsTokenVaultEscrowTest_test_register_fullFlow": "163659", + "EthOsTokenVaultEscrowTest_test_register_fullFlow": "148598", "EthOsTokenVaultEscrowTest_test_register_invalidShares": "33742", "EthOsTokenVaultEscrowTest_test_register_zeroAddress": "33386", "EthOsTokenVaultEscrowTest_test_setAuthenticator_onlyOwner": "30174", @@ -24,12 +24,12 @@ "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidThreshold_zero": "29970", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_onlyOwner": "30318", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_success": "41245", - "OsTokenLiquidationTest_test_liquidateOsToken_invalidHealthFactor": "52469", + "OsTokenLiquidationTest_test_liquidateOsToken_invalidHealthFactor": "52577", "OsTokenLiquidationTest_test_liquidateOsToken_invalidPosition": "41078", - "OsTokenLiquidationTest_test_liquidateOsToken_invalidReceivedAssets": "41999", - "OsTokenLiquidationTest_test_liquidateOsToken_partialLiquidation": "94624", - "OsTokenLiquidationTest_test_liquidateOsToken_success": "97295", - "OsTokenLiquidationTest_test_liquidateOsToken_zeroAddress": "24032", - "OsTokenLiquidationTest_test_redeemOsToken_notRedeemer": "27478", - "OsTokenLiquidationTest_test_redeemOsToken_success": "118710" + "OsTokenLiquidationTest_test_liquidateOsToken_invalidReceivedAssets": "42107", + "OsTokenLiquidationTest_test_liquidateOsToken_partialLiquidation": "94732", + "OsTokenLiquidationTest_test_liquidateOsToken_success": "97403", + "OsTokenLiquidationTest_test_liquidateOsToken_zeroAddress": "24140", + "OsTokenLiquidationTest_test_redeemOsToken_notRedeemer": "27586", + "OsTokenLiquidationTest_test_redeemOsToken_success": "118818" } \ No newline at end of file diff --git a/snapshots/EthPrivErc20VaultTest.json b/snapshots/EthPrivErc20VaultTest.json index b776b47e..fc805a6b 100644 --- a/snapshots/EthPrivErc20VaultTest.json +++ b/snapshots/EthPrivErc20VaultTest.json @@ -1,9 +1,9 @@ { - "EthPrivErc20VaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser": "206655", - "EthPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "81395", - "EthPrivErc20VaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77693", - "EthPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161601", - "EthPrivErc20VaultTest_test_deploysCorrectly": "625462", - "EthPrivErc20VaultTest_test_transfer": "59709", - "EthPrivErc20VaultTest_test_upgradesCorrectly": "119902" + "EthPrivErc20VaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser": "206492", + "EthPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "81309", + "EthPrivErc20VaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77607", + "EthPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161447", + "EthPrivErc20VaultTest_test_deploysCorrectly": "1351603", + "EthPrivErc20VaultTest_test_transfer": "59632", + "EthPrivErc20VaultTest_test_upgradesCorrectly": "119559" } \ No newline at end of file diff --git a/snapshots/EthPrivVaultTest.json b/snapshots/EthPrivVaultTest.json index 8dd7660c..caba06c4 100644 --- a/snapshots/EthPrivVaultTest.json +++ b/snapshots/EthPrivVaultTest.json @@ -1,11 +1,11 @@ { - "EthPrivVaultTest_test_canDepositAsWhitelistedUser": "79431", - "EthPrivVaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "75857", - "EthPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161628", - "EthPrivVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "140687", - "EthPrivVaultTest_test_deploysCorrectly": "550259", - "EthPrivVaultTest_test_depositAndMintOsTokenAsWhitelistedUser": "200570", - "EthPrivVaultTest_test_setWhitelister": "36608", - "EthPrivVaultTest_test_updateWhitelist": "54543", - "EthPrivVaultTest_test_upgradesCorrectly": "119328" + "EthPrivVaultTest_test_canDepositAsWhitelistedUser": "81507", + "EthPrivVaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77933", + "EthPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "141052", + "EthPrivVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "123490", + "EthPrivVaultTest_test_deploysCorrectly": "1276416", + "EthPrivVaultTest_test_depositAndMintOsTokenAsWhitelistedUser": "179985", + "EthPrivVaultTest_test_setWhitelister": "36531", + "EthPrivVaultTest_test_updateWhitelist": "54466", + "EthPrivVaultTest_test_upgradesCorrectly": "118985" } \ No newline at end of file diff --git a/snapshots/EthRewardSplitterTest.json b/snapshots/EthRewardSplitterTest.json index 6d3d62db..46618681 100644 --- a/snapshots/EthRewardSplitterTest.json +++ b/snapshots/EthRewardSplitterTest.json @@ -1,14 +1,14 @@ { - "EthRewardSplitter_claimExitedAssetsOnBehalf": "767084", - "EthRewardSplitter_claimVaultTokens": "771719", - "EthRewardSplitter_decreaseShares": "90133", - "EthRewardSplitter_enterExitQueue": "171850", - "EthRewardSplitter_enterExitQueueMaxWithdrawal": "131378", - "EthRewardSplitter_enterExitQueueOnBehalf": "964738", - "EthRewardSplitter_increaseShares": "73128", + "EthRewardSplitter_claimExitedAssetsOnBehalf": "761821", + "EthRewardSplitter_claimVaultTokens": "768781", + "EthRewardSplitter_decreaseShares": "89902", + "EthRewardSplitter_enterExitQueue": "156712", + "EthRewardSplitter_enterExitQueueMaxWithdrawal": "116240", + "EthRewardSplitter_enterExitQueueOnBehalf": "934262", + "EthRewardSplitter_increaseShares": "72897", "EthRewardSplitter_receiveEth": "31194", - "EthRewardSplitter_syncRewards": "77682", - "EthRewardSplitter_syncRewardsDetailed": "77682", - "EthRewardSplitter_test_setClaimer": "66269", - "EthRewardSplitter_updateVaultState": "132613" + "EthRewardSplitter_syncRewards": "77528", + "EthRewardSplitter_syncRewardsDetailed": "77528", + "EthRewardSplitter_test_setClaimer": "66192", + "EthRewardSplitter_updateVaultState": "117222" } \ No newline at end of file diff --git a/snapshots/EthVaultTest.json b/snapshots/EthVaultTest.json index a17dfa31..d3e5c6f9 100644 --- a/snapshots/EthVaultTest.json +++ b/snapshots/EthVaultTest.json @@ -1,10 +1,10 @@ { - "EthVaultTest_test_deploysCorrectly": "379895", - "EthVaultTest_test_depositAndMintOsToken": "202060", - "EthVaultTest_test_exitQueue_works": "99131", - "EthVaultTest_test_fallbackDeposit": "77627", - "EthVaultTest_test_updateStateAndDepositAndMintOsToken": "228182", - "EthVaultTest_test_upgradesCorrectly": "119172", - "EthVaultTest_test_withdrawValidator_unknown": "55973", - "EthVaultTest_test_withdrawValidator_validatorsManager": "74114" + "EthVaultTest_test_deploysCorrectly": "1106052", + "EthVaultTest_test_depositAndMintOsToken": "201897", + "EthVaultTest_test_exitQueue_works": "84171", + "EthVaultTest_test_fallbackDeposit": "79703", + "EthVaultTest_test_updateStateAndDepositAndMintOsToken": "212767", + "EthVaultTest_test_upgradesCorrectly": "118828", + "EthVaultTest_test_withdrawValidator_unknown": "55896", + "EthVaultTest_test_withdrawValidator_validatorsManager": "74037" } \ No newline at end of file diff --git a/snapshots/GnoBlocklistErc20VaultTest.json b/snapshots/GnoBlocklistErc20VaultTest.json index 0b4b6845..2f80b1dd 100644 --- a/snapshots/GnoBlocklistErc20VaultTest.json +++ b/snapshots/GnoBlocklistErc20VaultTest.json @@ -3,5 +3,5 @@ "GnoBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "161610", "GnoBlocklistErc20VaultTest_test_deploysCorrectly": "953762", "GnoBlocklistErc20VaultTest_test_transfer": "61693", - "GnoBlocklistErc20VaultTest_test_upgradesCorrectly": "340215" + "GnoBlocklistErc20VaultTest_test_upgradesCorrectly": "339872" } \ No newline at end of file diff --git a/snapshots/GnoBlocklistVaultTest.json b/snapshots/GnoBlocklistVaultTest.json index c4ab5037..1d0751d4 100644 --- a/snapshots/GnoBlocklistVaultTest.json +++ b/snapshots/GnoBlocklistVaultTest.json @@ -1,6 +1,6 @@ { - "GnoBlocklistVaultTest_test_canDepositAsNonBlockedUser": "158124", - "GnoBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "161570", - "GnoBlocklistVaultTest_test_deploysCorrectly": "702387", - "GnoBlocklistVaultTest_test_upgradesCorrectly": "339511" + "GnoBlocklistVaultTest_test_canDepositAsNonBlockedUser": "160200", + "GnoBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "140994", + "GnoBlocklistVaultTest_test_deploysCorrectly": "1554711", + "GnoBlocklistVaultTest_test_upgradesCorrectly": "339168" } \ No newline at end of file diff --git a/snapshots/GnoErc20VaultTest.json b/snapshots/GnoErc20VaultTest.json index dd4c1c4f..58cfb178 100644 --- a/snapshots/GnoErc20VaultTest.json +++ b/snapshots/GnoErc20VaultTest.json @@ -1,11 +1,11 @@ { - "GnoErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90100", - "GnoErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96637", - "GnoErc20VaultTest_test_deploysCorrectly": "753330", - "GnoErc20VaultTest_test_deposit_emitsTransfer": "100732", - "GnoErc20VaultTest_test_enterExitQueue_emitsTransfer": "89780", - "GnoErc20VaultTest_test_redeem_emitsEvent": "77069", - "GnoErc20VaultTest_test_upgradesCorrectly": "340189", - "VaultGnoErc20VaultTest_test_withdrawValidator_unknown": "55973", - "VaultGnoErc20VaultTest_test_withdrawValidator_validatorsManager": "74114" + "GnoErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90047", + "GnoErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96066", + "GnoErc20VaultTest_test_deploysCorrectly": "1605639", + "GnoErc20VaultTest_test_deposit_emitsTransfer": "102817", + "GnoErc20VaultTest_test_enterExitQueue_emitsTransfer": "79620", + "GnoErc20VaultTest_test_redeem_emitsEvent": "81893", + "GnoErc20VaultTest_test_upgradesCorrectly": "339858", + "VaultGnoErc20VaultTest_test_withdrawValidator_unknown": "55896", + "VaultGnoErc20VaultTest_test_withdrawValidator_validatorsManager": "74037" } \ No newline at end of file diff --git a/snapshots/GnoGenesisVaultTest.json b/snapshots/GnoGenesisVaultTest.json index 748a467a..3ac51c48 100644 --- a/snapshots/GnoGenesisVaultTest.json +++ b/snapshots/GnoGenesisVaultTest.json @@ -1,5 +1,5 @@ { "GnoGenesisVaultTest_test_migrate_works": "206716", "GnoGenesisVaultTest_test_pullWithdrawals_claimEscrowAssets": "879736", - "GnoGenesisVaultTest_test_upgradesCorrectly": "5830820" + "GnoGenesisVaultTest_test_upgradesCorrectly": "5830554" } \ No newline at end of file diff --git a/snapshots/GnoOsTokenRedeemerTest.json b/snapshots/GnoOsTokenRedeemerTest.json index 1bb452bb..45c895b7 100644 --- a/snapshots/GnoOsTokenRedeemerTest.json +++ b/snapshots/GnoOsTokenRedeemerTest.json @@ -1,6 +1,6 @@ { "GnoOsTokenRedeemerTest_test_claimExitedAssets_fullWithdrawal": "80495", "GnoOsTokenRedeemerTest_test_permitGnoToken_success": "91926", - "GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "252803", + "GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "252637", "GnoOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "137991" } \ No newline at end of file diff --git a/snapshots/GnoOsTokenVaultEscrowTest.json b/snapshots/GnoOsTokenVaultEscrowTest.json index 7e35444f..50ebb412 100644 --- a/snapshots/GnoOsTokenVaultEscrowTest.json +++ b/snapshots/GnoOsTokenVaultEscrowTest.json @@ -1,5 +1,5 @@ { - "GnoOsTokenVaultEscrowTest_test_transferAssets_claim": "140340", - "GnoOsTokenVaultEscrowTest_test_transferAssets_process": "893932", - "GnoOsTokenVaultEscrowTest_test_transferAssets_transfer": "163157" + "GnoOsTokenVaultEscrowTest_test_transferAssets_claim": "140422", + "GnoOsTokenVaultEscrowTest_test_transferAssets_process": "897276", + "GnoOsTokenVaultEscrowTest_test_transferAssets_transfer": "165196" } \ No newline at end of file diff --git a/snapshots/GnoPrivErc20VaultTest.json b/snapshots/GnoPrivErc20VaultTest.json index 89d1f7cb..40ca55e2 100644 --- a/snapshots/GnoPrivErc20VaultTest.json +++ b/snapshots/GnoPrivErc20VaultTest.json @@ -1,7 +1,7 @@ { - "GnoPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "156037", - "GnoPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161543", - "GnoPrivErc20VaultTest_test_deploysCorrectly": "953806", - "GnoPrivErc20VaultTest_test_transfer": "59709", - "GnoPrivErc20VaultTest_test_upgradesCorrectly": "340352" + "GnoPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "158113", + "GnoPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "140967", + "GnoPrivErc20VaultTest_test_deploysCorrectly": "1806115", + "GnoPrivErc20VaultTest_test_transfer": "64456", + "GnoPrivErc20VaultTest_test_upgradesCorrectly": "340008" } \ No newline at end of file diff --git a/snapshots/GnoPrivVaultTest.json b/snapshots/GnoPrivVaultTest.json index 09e2f1da..5c9fbb88 100644 --- a/snapshots/GnoPrivVaultTest.json +++ b/snapshots/GnoPrivVaultTest.json @@ -1,8 +1,8 @@ { - "GnoPrivVaultTest_test_canDepositAsWhitelistedUser": "154140", - "GnoPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161480", - "GnoPrivVaultTest_test_deploysCorrectly": "702431", - "GnoPrivVaultTest_test_setWhitelister": "36608", - "GnoPrivVaultTest_test_updateWhitelist": "54521", - "GnoPrivVaultTest_test_upgradesCorrectly": "339647" + "GnoPrivVaultTest_test_canDepositAsWhitelistedUser": "156204", + "GnoPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161326", + "GnoPrivVaultTest_test_deploysCorrectly": "1554755", + "GnoPrivVaultTest_test_setWhitelister": "36531", + "GnoPrivVaultTest_test_updateWhitelist": "54444", + "GnoPrivVaultTest_test_upgradesCorrectly": "339304" } \ No newline at end of file diff --git a/snapshots/GnoRewardSplitterTest.json b/snapshots/GnoRewardSplitterTest.json index 28733f70..e74b0ec3 100644 --- a/snapshots/GnoRewardSplitterTest.json +++ b/snapshots/GnoRewardSplitterTest.json @@ -1,14 +1,14 @@ { - "GnoRewardSplitter_claimExitedAssets": "94809", - "GnoRewardSplitter_claimExitedAssetsOnBehalf": "824394", - "GnoRewardSplitter_claimVaultTokens": "855626", - "GnoRewardSplitter_decreaseShares": "101041", - "GnoRewardSplitter_enterExitQueue": "186729", - "GnoRewardSplitter_enterExitQueueMaxWithdrawal": "131400", - "GnoRewardSplitter_enterExitQueueOnBehalf": "1029028", - "GnoRewardSplitter_increaseShares": "73172", - "GnoRewardSplitter_setClaimer": "66291", - "GnoRewardSplitter_syncRewards": "77704", - "GnoRewardSplitter_syncRewardsDetailed": "77704", - "GnoRewardSplitter_updateVaultState": "165440" + "GnoRewardSplitter_claimExitedAssets": "97033", + "GnoRewardSplitter_claimExitedAssetsOnBehalf": "816246", + "GnoRewardSplitter_claimVaultTokens": "850283", + "GnoRewardSplitter_decreaseShares": "100810", + "GnoRewardSplitter_enterExitQueue": "171591", + "GnoRewardSplitter_enterExitQueueMaxWithdrawal": "116262", + "GnoRewardSplitter_enterExitQueueOnBehalf": "996950", + "GnoRewardSplitter_increaseShares": "72941", + "GnoRewardSplitter_setClaimer": "66214", + "GnoRewardSplitter_syncRewards": "77550", + "GnoRewardSplitter_syncRewardsDetailed": "77550", + "GnoRewardSplitter_updateVaultState": "148330" } \ No newline at end of file diff --git a/snapshots/GnoVaultExitQueueTest.json b/snapshots/GnoVaultExitQueueTest.json index 024f3091..d4a57a9a 100644 --- a/snapshots/GnoVaultExitQueueTest.json +++ b/snapshots/GnoVaultExitQueueTest.json @@ -1,6 +1,6 @@ { "GnoVaultExitQueueTest_test_ExitingAssetsPenalized_event": "194845", - "GnoVaultExitQueueTest_test_claim_position1_after_upgrade": "6654229", + "GnoVaultExitQueueTest_test_claim_position1_after_upgrade": "6654158", "GnoVaultExitQueueTest_test_claim_position2_before_upgrade": "125766", "GnoVaultExitQueueTest_test_claim_position3_after_upgrade": "165242", "GnoVaultExitQueueTest_test_claim_position4_after_upgrade": "160429" diff --git a/snapshots/GnoVaultTest.json b/snapshots/GnoVaultTest.json index 0a898884..54bd8e19 100644 --- a/snapshots/GnoVaultTest.json +++ b/snapshots/GnoVaultTest.json @@ -1,7 +1,7 @@ { - "GnoVaultTest_test_deploysCorrectly": "678345", - "GnoVaultTest_test_exitQueue_works": "99131", - "GnoVaultTest_test_upgradesCorrectly": "339549", - "GnoVaultTest_test_withdrawValidator_unknown": "55950", - "GnoVaultTest_test_withdrawValidator_validatorsManager": "74091" + "GnoVaultTest_test_deploysCorrectly": "1530669", + "GnoVaultTest_test_exitQueue_works": "101271", + "GnoVaultTest_test_upgradesCorrectly": "339206", + "GnoVaultTest_test_withdrawValidator_unknown": "55873", + "GnoVaultTest_test_withdrawValidator_validatorsManager": "74014" } \ No newline at end of file diff --git a/snapshots/KeeperOraclesTest.json b/snapshots/KeeperOraclesTest.json index 4ded9d36..b2c3ed87 100644 --- a/snapshots/KeeperOraclesTest.json +++ b/snapshots/KeeperOraclesTest.json @@ -8,5 +8,5 @@ "KeeperOraclesTest_test_removeOracle_success": "36666", "KeeperOraclesTest_test_updateConfig_onlyOwner": "31726", "KeeperOraclesTest_test_updateConfig_success": "32787", - "KeeperOraclesTest_test_verifySignatures_throughKeeperRewards": "242271" + "KeeperOraclesTest_test_verifySignatures_throughKeeperRewards": "242384" } \ No newline at end of file diff --git a/snapshots/KeeperRewardsTest.json b/snapshots/KeeperRewardsTest.json index 78e34a09..3598dde0 100644 --- a/snapshots/KeeperRewardsTest.json +++ b/snapshots/KeeperRewardsTest.json @@ -9,8 +9,8 @@ "KeeperRewardsTest_test_isCollateralized": "10123", "KeeperRewardsTest_test_isHarvestRequired": "12620", "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_1": "135313", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_2": "606725", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_3": "606771", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_2": "606838", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_3": "606884", "KeeperRewardsTest_test_setRewardsMinOracles": "37748", "KeeperRewardsTest_test_setRewardsMinOracles_tooMany": "29542", "KeeperRewardsTest_test_setRewardsMinOracles_zero": "30031", diff --git a/snapshots/VaultAdminTest.json b/snapshots/VaultAdminTest.json index 27e434c9..be8037ca 100644 --- a/snapshots/VaultAdminTest.json +++ b/snapshots/VaultAdminTest.json @@ -1,11 +1,11 @@ { - "VaultAdminTest_test_checkAdmin_withOtherFunctions_admin": "48301", - "VaultAdminTest_test_checkAdmin_withOtherFunctions_nonAdmin": "34853", - "VaultAdminTest_test_initialization": "378686", - "VaultAdminTest_test_setAdmin_byAdmin": "39247", - "VaultAdminTest_test_setAdmin_byNonAdmin": "36921", - "VaultAdminTest_test_setAdmin_toSameValue": "35135", - "VaultAdminTest_test_setAdmin_toZeroAddress": "34636", - "VaultAdminTest_test_setMetadata_byAdmin": "37018", - "VaultAdminTest_test_setMetadata_byNonAdmin": "35330" + "VaultAdminTest_test_checkAdmin_withOtherFunctions_admin": "50386", + "VaultAdminTest_test_checkAdmin_withOtherFunctions_nonAdmin": "34776", + "VaultAdminTest_test_initialization": "1104846", + "VaultAdminTest_test_setAdmin_byAdmin": "39170", + "VaultAdminTest_test_setAdmin_byNonAdmin": "36844", + "VaultAdminTest_test_setAdmin_toSameValue": "35058", + "VaultAdminTest_test_setAdmin_toZeroAddress": "34559", + "VaultAdminTest_test_setMetadata_byAdmin": "36941", + "VaultAdminTest_test_setMetadata_byNonAdmin": "35253" } \ No newline at end of file diff --git a/snapshots/VaultEnterExitTest.json b/snapshots/VaultEnterExitTest.json index e2c63547..4daf252e 100644 --- a/snapshots/VaultEnterExitTest.json +++ b/snapshots/VaultEnterExitTest.json @@ -1,25 +1,25 @@ { - "VaultEnterExitTest_test_calculateExitedAssets_invalidPosition": "18600", - "VaultEnterExitTest_test_claimExitedAssets": "752879", - "VaultEnterExitTest_test_claimExitedAssets_insufficientDelay": "43924", - "VaultEnterExitTest_test_claimExitedAssets_invalidCheckpoint": "36507", - "VaultEnterExitTest_test_deposit_exceedingCapacity": "47943", - "VaultEnterExitTest_test_deposit_success_basic": "83160", - "VaultEnterExitTest_test_deposit_success_differentReceiver": "83051", - "VaultEnterExitTest_test_deposit_success_multipleDeposits": "57463", - "VaultEnterExitTest_test_deposit_success_receiveFunction": "79618", - "VaultEnterExitTest_test_deposit_success_withReferrer": "76797", - "VaultEnterExitTest_test_deposit_zeroAddress": "49918", - "VaultEnterExitTest_test_deposit_zeroAmount": "43471", - "VaultEnterExitTest_test_enterExitQueue_afterValidatorExit": "94343", - "VaultEnterExitTest_test_enterExitQueue_basicFlow": "94331", + "VaultEnterExitTest_test_calculateExitedAssets_invalidPosition": "18523", + "VaultEnterExitTest_test_claimExitedAssets": "739476", + "VaultEnterExitTest_test_claimExitedAssets_insufficientDelay": "46261", + "VaultEnterExitTest_test_claimExitedAssets_invalidCheckpoint": "36538", + "VaultEnterExitTest_test_deposit_exceedingCapacity": "47695", + "VaultEnterExitTest_test_deposit_success_basic": "85236", + "VaultEnterExitTest_test_deposit_success_differentReceiver": "85127", + "VaultEnterExitTest_test_deposit_success_multipleDeposits": "59539", + "VaultEnterExitTest_test_deposit_success_receiveFunction": "81694", + "VaultEnterExitTest_test_deposit_success_withReferrer": "78873", + "VaultEnterExitTest_test_deposit_zeroAddress": "52003", + "VaultEnterExitTest_test_deposit_zeroAmount": "45556", + "VaultEnterExitTest_test_enterExitQueue_afterValidatorExit": "79383", + "VaultEnterExitTest_test_enterExitQueue_basicFlow": "79371", "VaultEnterExitTest_test_enterExitQueue_directRedemption": "84394", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_tooManyShares": "64810", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroAddress": "48332", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroShares": "31344", - "VaultEnterExitTest_test_enterExitQueue_multiUser_sender2": "74995", - "VaultEnterExitTest_test_enterExitQueue_multiUser_user1": "94331", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_tooManyShares": "89821", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroAddress": "48202", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroShares": "31267", + "VaultEnterExitTest_test_enterExitQueue_multiUser_sender2": "77129", + "VaultEnterExitTest_test_enterExitQueue_multiUser_user1": "79371", "VaultEnterExitTest_test_enterExitQueue_multipleUpdates": "92234", - "VaultEnterExitTest_test_enterExitQueue_partialExit": "99131", + "VaultEnterExitTest_test_enterExitQueue_partialExit": "84171", "VaultEnterExitTest_test_rescueAssets": "46295" } \ No newline at end of file diff --git a/snapshots/VaultEthStakingTest.json b/snapshots/VaultEthStakingTest.json index cf1c5e76..729ee335 100644 --- a/snapshots/VaultEthStakingTest.json +++ b/snapshots/VaultEthStakingTest.json @@ -1,18 +1,18 @@ { - "VaultEthStakingTest_test_deposit": "85325", - "VaultEthStakingTest_test_depositAndMintOsToken": "200047", - "VaultEthStakingTest_test_fundValidators_invalid": "57140", - "VaultEthStakingTest_test_fundValidators_valid": "140479", - "VaultEthStakingTest_test_harvestAssets": "130845", - "VaultEthStakingTest_test_invalidSecurityDeposit": "308363", - "VaultEthStakingTest_test_receive": "77518", - "VaultEthStakingTest_test_receiveFromMevEscrow_fail": "36570", - "VaultEthStakingTest_test_receiveFromMevEscrow_success": "37826", - "VaultEthStakingTest_test_registerValidators_01prefix": "244809", - "VaultEthStakingTest_test_registerValidators_02prefix": "500922", - "VaultEthStakingTest_test_transferVaultAssets": "54251", - "VaultEthStakingTest_test_updateStateAndDeposit": "166673", - "VaultEthStakingTest_test_updateStateAndDepositAndMintOsToken": "254280", - "VaultEthStakingTest_test_validatorMinMaxEffectiveBalance": "199908", - "VaultEthStakingTest_test_withdrawValidator_fullFlow": "74114" + "VaultEthStakingTest_test_deposit": "87401", + "VaultEthStakingTest_test_depositAndMintOsToken": "199884", + "VaultEthStakingTest_test_fundValidators_invalid": "57051", + "VaultEthStakingTest_test_fundValidators_valid": "140378", + "VaultEthStakingTest_test_harvestAssets": "115278", + "VaultEthStakingTest_test_invalidSecurityDeposit": "308152", + "VaultEthStakingTest_test_receive": "79594", + "VaultEthStakingTest_test_receiveFromMevEscrow_fail": "36493", + "VaultEthStakingTest_test_receiveFromMevEscrow_success": "37749", + "VaultEthStakingTest_test_registerValidators_01prefix": "224304", + "VaultEthStakingTest_test_registerValidators_02prefix": "500771", + "VaultEthStakingTest_test_transferVaultAssets": "61183", + "VaultEthStakingTest_test_updateStateAndDeposit": "151229", + "VaultEthStakingTest_test_updateStateAndDepositAndMintOsToken": "238753", + "VaultEthStakingTest_test_validatorMinMaxEffectiveBalance": "179403", + "VaultEthStakingTest_test_withdrawValidator_fullFlow": "74037" } \ No newline at end of file diff --git a/snapshots/VaultFeeTest.json b/snapshots/VaultFeeTest.json index 399199f7..f0023439 100644 --- a/snapshots/VaultFeeTest.json +++ b/snapshots/VaultFeeTest.json @@ -1,16 +1,16 @@ { - "VaultFeeTest_test_feeCollection": "121919", - "VaultFeeTest_test_feePercent_changeAffectsFutureRewards": "87733", - "VaultFeeTest_test_setFeePercent_aboveMaximum": "40380", + "VaultFeeTest_test_feeCollection": "106513", + "VaultFeeTest_test_feePercent_changeAffectsFutureRewards": "89410", + "VaultFeeTest_test_setFeePercent_aboveMaximum": "42465", "VaultFeeTest_test_setFeePercent_initialZeroToOne": "44812", - "VaultFeeTest_test_setFeePercent_maxIncrease": "38472", - "VaultFeeTest_test_setFeePercent_notAdmin": "34556", - "VaultFeeTest_test_setFeePercent_requiresHarvest": "42501", - "VaultFeeTest_test_setFeePercent_success": "47075", - "VaultFeeTest_test_setFeePercent_tooSoon": "38137", - "VaultFeeTest_test_setFeeRecipient_notAdmin": "36854", - "VaultFeeTest_test_setFeeRecipient_requiresHarvest": "44796", - "VaultFeeTest_test_setFeeRecipient_sameValue": "42843", - "VaultFeeTest_test_setFeeRecipient_success": "48949", - "VaultFeeTest_test_setFeeRecipient_zeroAddress": "40343" + "VaultFeeTest_test_setFeePercent_maxIncrease": "40557", + "VaultFeeTest_test_setFeePercent_notAdmin": "34479", + "VaultFeeTest_test_setFeePercent_requiresHarvest": "42424", + "VaultFeeTest_test_setFeePercent_success": "49160", + "VaultFeeTest_test_setFeePercent_tooSoon": "40222", + "VaultFeeTest_test_setFeeRecipient_notAdmin": "36777", + "VaultFeeTest_test_setFeeRecipient_requiresHarvest": "44719", + "VaultFeeTest_test_setFeeRecipient_sameValue": "44928", + "VaultFeeTest_test_setFeeRecipient_success": "51034", + "VaultFeeTest_test_setFeeRecipient_zeroAddress": "42428" } \ No newline at end of file diff --git a/snapshots/VaultGnoStakingTest.json b/snapshots/VaultGnoStakingTest.json index 5f5bc848..7a54b8d8 100644 --- a/snapshots/VaultGnoStakingTest.json +++ b/snapshots/VaultGnoStakingTest.json @@ -1,14 +1,14 @@ { - "VaultGnoStakingCoverageTest_test_processTotalAssetsDelta_smallXdaiBalance": "175401", - "VaultGnoStakingCoverageTest_test_registerValidator_topUp_invalid": "57128", - "VaultGnoStakingCoverageTest_test_registerValidator_topUp_valid": "182150", - "VaultGnoStakingTest_test_deposit": "101137", - "VaultGnoStakingTest_test_processTotalAssetsDelta": "303158", + "VaultGnoStakingCoverageTest_test_processTotalAssetsDelta_smallXdaiBalance": "140889", + "VaultGnoStakingCoverageTest_test_registerValidator_topUp_invalid": "57075", + "VaultGnoStakingCoverageTest_test_registerValidator_topUp_valid": "182085", + "VaultGnoStakingTest_test_deposit": "103213", + "VaultGnoStakingTest_test_processTotalAssetsDelta": "285829", "VaultGnoStakingTest_test_pullWithdrawals": "92921", - "VaultGnoStakingTest_test_receive_xDai": "213139", - "VaultGnoStakingTest_test_transferVaultAssets": "89952", - "VaultGnoStakingTest_test_vaultGnoStaking_init": "678313", - "VaultGnoStakingTest_test_withdrawValidator_fullFlow": "74091", - "test_registerValidators_succeeds_0x01": "286528", - "test_registerValidators_succeeds_0x02": "630112" + "VaultGnoStakingTest_test_receive_xDai": "213062", + "VaultGnoStakingTest_test_transferVaultAssets": "97089", + "VaultGnoStakingTest_test_vaultGnoStaking_init": "1530605", + "VaultGnoStakingTest_test_withdrawValidator_fullFlow": "74014", + "test_registerValidators_succeeds_0x01": "270486", + "test_registerValidators_succeeds_0x02": "629961" } \ No newline at end of file diff --git a/snapshots/VaultOsTokenTest.json b/snapshots/VaultOsTokenTest.json index 44d8888e..24be0c25 100644 --- a/snapshots/VaultOsTokenTest.json +++ b/snapshots/VaultOsTokenTest.json @@ -1,48 +1,48 @@ { - "VaultOsTokenTest_test_burnOsToken_afterFeeSync": "76001", - "VaultOsTokenTest_test_burnOsToken_allShares": "70699", - "VaultOsTokenTest_test_burnOsToken_basic": "75499", - "VaultOsTokenTest_test_burnOsToken_exceedingShares": "48725", - "VaultOsTokenTest_test_burnOsToken_improvesLTV": "70999", - "VaultOsTokenTest_test_burnOsToken_invalidPosition": "66210", - "VaultOsTokenTest_test_burnOsToken_multipleBurns": "75499", - "VaultOsTokenTest_test_burnOsToken_zeroShares": "37846", - "VaultOsTokenTest_test_enterExitQueue_ltvViolation": "118315", - "VaultOsTokenTest_test_liquidateOsToken_basic": "120719", - "VaultOsTokenTest_test_liquidateOsToken_bonus": "125219", - "VaultOsTokenTest_test_liquidateOsToken_invalidReceivedAssets": "75759", - "VaultOsTokenTest_test_liquidateOsToken_liquidationDisabled": "69187", - "VaultOsTokenTest_test_liquidateOsToken_partialLiquidation": "125219", - "VaultOsTokenTest_test_mintOsToken_basic": "162110", - "VaultOsTokenTest_test_mintOsToken_feeSync": "108078", - "VaultOsTokenTest_test_mintOsToken_ltvValidation": "144971", - "VaultOsTokenTest_test_mintOsToken_maxAmount": "162479", - "VaultOsTokenTest_test_mintOsToken_multipleReceivers": "119989", - "VaultOsTokenTest_test_mintOsToken_notCollateralized": "41965", - "VaultOsTokenTest_test_mintOsToken_notHarvested": "47615", - "VaultOsTokenTest_test_mintOsToken_repeatedMinting": "105670", - "VaultOsTokenTest_test_mintOsToken_zeroAddressReceiver": "86836", - "VaultOsTokenTest_test_mintOsToken_zeroShares": "89092", - "VaultOsTokenTest_test_redeemOsToken_afterFeeSync": "134490", - "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_fail": "50939", - "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_success": "265876", - "VaultOsTokenTest_test_redeemOsToken_basic": "133988", - "VaultOsTokenTest_test_redeemOsToken_fullPosition": "133988", - "VaultOsTokenTest_test_redeemOsToken_goodHealthFactor": "134071", - "VaultOsTokenTest_test_redeemOsToken_insufficientShares": "112664", - "VaultOsTokenTest_test_redeemOsToken_nonExistentPosition": "86996", - "VaultOsTokenTest_test_redeemOsToken_onlyRedeemer": "36360", - "VaultOsTokenTest_test_redeemOsToken_zeroAddressReceiver": "40685", - "VaultOsTokenTest_test_redeemOsToken_zeroShares": "90242", - "VaultOsTokenTest_test_redeemVsLiquidate": "134071", - "VaultOsTokenTest_test_test_liquidateOsToken_notHarvested": "43548", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_basic": "167657", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_claim": "107765", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_maxAmount": "167657", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_moreThanOwned": "44819", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_noPosition": "43502", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_notHarvested": "41342", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_partialTransfer": "173220", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process": "736184", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_zeroShares": "47545" + "VaultOsTokenTest_test_burnOsToken_afterFeeSync": "75924", + "VaultOsTokenTest_test_burnOsToken_allShares": "70622", + "VaultOsTokenTest_test_burnOsToken_basic": "75422", + "VaultOsTokenTest_test_burnOsToken_exceedingShares": "48648", + "VaultOsTokenTest_test_burnOsToken_improvesLTV": "70922", + "VaultOsTokenTest_test_burnOsToken_invalidPosition": "66133", + "VaultOsTokenTest_test_burnOsToken_multipleBurns": "75422", + "VaultOsTokenTest_test_burnOsToken_zeroShares": "37769", + "VaultOsTokenTest_test_enterExitQueue_ltvViolation": "103355", + "VaultOsTokenTest_test_liquidateOsToken_basic": "120642", + "VaultOsTokenTest_test_liquidateOsToken_bonus": "125142", + "VaultOsTokenTest_test_liquidateOsToken_invalidReceivedAssets": "75682", + "VaultOsTokenTest_test_liquidateOsToken_liquidationDisabled": "69110", + "VaultOsTokenTest_test_liquidateOsToken_partialLiquidation": "125142", + "VaultOsTokenTest_test_mintOsToken_basic": "161956", + "VaultOsTokenTest_test_mintOsToken_feeSync": "107924", + "VaultOsTokenTest_test_mintOsToken_ltvValidation": "144817", + "VaultOsTokenTest_test_mintOsToken_maxAmount": "162325", + "VaultOsTokenTest_test_mintOsToken_multipleReceivers": "119835", + "VaultOsTokenTest_test_mintOsToken_notCollateralized": "41718", + "VaultOsTokenTest_test_mintOsToken_notHarvested": "47538", + "VaultOsTokenTest_test_mintOsToken_repeatedMinting": "105516", + "VaultOsTokenTest_test_mintOsToken_zeroAddressReceiver": "86682", + "VaultOsTokenTest_test_mintOsToken_zeroShares": "88938", + "VaultOsTokenTest_test_redeemOsToken_afterFeeSync": "134413", + "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_fail": "50862", + "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_success": "250372", + "VaultOsTokenTest_test_redeemOsToken_basic": "133911", + "VaultOsTokenTest_test_redeemOsToken_fullPosition": "133911", + "VaultOsTokenTest_test_redeemOsToken_goodHealthFactor": "133994", + "VaultOsTokenTest_test_redeemOsToken_insufficientShares": "112587", + "VaultOsTokenTest_test_redeemOsToken_nonExistentPosition": "86919", + "VaultOsTokenTest_test_redeemOsToken_onlyRedeemer": "36283", + "VaultOsTokenTest_test_redeemOsToken_zeroAddressReceiver": "40608", + "VaultOsTokenTest_test_redeemOsToken_zeroShares": "90165", + "VaultOsTokenTest_test_redeemVsLiquidate": "133994", + "VaultOsTokenTest_test_test_liquidateOsToken_notHarvested": "43471", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_basic": "152596", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_claim": "107885", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_maxAmount": "152596", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_moreThanOwned": "44742", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_noPosition": "43425", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_notHarvested": "41265", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_partialTransfer": "158159", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process": "719076", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_zeroShares": "47468" } \ No newline at end of file diff --git a/snapshots/VaultSubVaultsTest.json b/snapshots/VaultSubVaultsTest.json index 47f4c6bb..ecb80863 100644 --- a/snapshots/VaultSubVaultsTest.json +++ b/snapshots/VaultSubVaultsTest.json @@ -1,24 +1,24 @@ { - "VaultSubVaultsTest_test_acceptMetaSubVault_success": "128444", - "VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_proposesMetaVault": "92907", - "VaultSubVaultsTest_test_addSubVault_success": "130867", - "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares": "417792", + "VaultSubVaultsTest_test_acceptMetaSubVault_success": "128367", + "VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_proposesMetaVault": "92830", + "VaultSubVaultsTest_test_addSubVault_success": "130790", + "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares": "417793", "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_partiallyClaimsExitedAssets": "120643", - "VaultSubVaultsTest_test_depositToSubVaults_maxVaults": "3874692", - "VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults": "295050", - "VaultSubVaultsTest_test_depositToSubVaults_singleSubVault": "142644", - "VaultSubVaultsTest_test_depositToSubVaults_withMetaVaultSubVault": "371161", - "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_emptySubVault": "57635", - "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_withShares": "206794", - "VaultSubVaultsTest_test_rejectMetaSubVault_byAdmin_success": "42237", - "VaultSubVaultsTest_test_rejectMetaSubVault_byOwner_success": "50033", - "VaultSubVaultsTest_test_setSubVaultsCurator_success": "51010", - "VaultSubVaultsTest_test_updateState_enterExitQueueMaxVaults": "7041487", - "VaultSubVaultsTest_test_updateState_withMetaVaultSubVault_success": "169251", + "VaultSubVaultsTest_test_depositToSubVaults_maxVaults": "3851800", + "VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults": "339984", + "VaultSubVaultsTest_test_depositToSubVaults_singleSubVault": "108481", + "VaultSubVaultsTest_test_depositToSubVaults_withMetaVaultSubVault": "416371", + "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_emptySubVault": "57558", + "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_withShares": "206717", + "VaultSubVaultsTest_test_rejectMetaSubVault_byAdmin_success": "42160", + "VaultSubVaultsTest_test_rejectMetaSubVault_byOwner_success": "49956", + "VaultSubVaultsTest_test_setSubVaultsCurator_success": "50933", + "VaultSubVaultsTest_test_updateState_enterExitQueueMaxVaults": "6764216", + "VaultSubVaultsTest_test_updateState_withMetaVaultSubVault_success": "212284", "test_addSubVault_firstSubVault": "145160", - "test_depositToSubVaults_ejectingSubVault": "153972", + "test_depositToSubVaults_ejectingSubVault": "198989", "test_ejectSubVault_emptySubVault": "66011", - "test_ejectSubVault_subVaultWithShares": "205950", + "test_ejectSubVault_subVaultWithShares": "174390", "test_updateState_newTotalAssets": "178910", - "test_updateState_unprocessedSubVaultExit": "204479" + "test_updateState_unprocessedSubVaultExit": "204480" } \ No newline at end of file diff --git a/snapshots/VaultTokenTest.json b/snapshots/VaultTokenTest.json index e738ddbc..a2aea0aa 100644 --- a/snapshots/VaultTokenTest.json +++ b/snapshots/VaultTokenTest.json @@ -1,22 +1,22 @@ { - "VaultTokenTest_test_approve": "58846", - "VaultTokenTest_test_approveZeroAddress": "33703", - "VaultTokenTest_test_depositEmitsTransferEvent": "83493", - "VaultTokenTest_test_enterExitQueueEmitsTransferEvent": "97066", - "VaultTokenTest_test_invalidTokenMetaNameTooLong": "478037", - "VaultTokenTest_test_invalidTokenMetaSymbolTooLong": "311105", - "VaultTokenTest_test_permit": "87202", - "VaultTokenTest_test_permitExpiredDeadline": "35305", - "VaultTokenTest_test_permitInvalidSigner": "63558", - "VaultTokenTest_test_permitZeroAddressSpender": "34863", - "VaultTokenTest_test_transfer": "66441", - "VaultTokenTest_test_transferFrom": "67701", - "VaultTokenTest_test_transferFromMoreThanAllowance": "32480", - "VaultTokenTest_test_transferFromMoreThanBalance": "37928", - "VaultTokenTest_test_transferFromZeroAddress": "38535", - "VaultTokenTest_test_transferMoreThanBalance": "38362", - "VaultTokenTest_test_transferToZeroAddress": "33734", - "VaultTokenTest_test_transferWithOsTokenPosition": "96584", - "VaultTokenTest_test_unlimitedAllowance": "69361", - "VaultTokenTest_test_updateExitQueueBurnsShares": "146991" + "VaultTokenTest_test_approve": "58769", + "VaultTokenTest_test_approveZeroAddress": "33626", + "VaultTokenTest_test_depositEmitsTransferEvent": "83416", + "VaultTokenTest_test_enterExitQueueEmitsTransferEvent": "82106", + "VaultTokenTest_test_invalidTokenMetaNameTooLong": "477774", + "VaultTokenTest_test_invalidTokenMetaSymbolTooLong": "310843", + "VaultTokenTest_test_permit": "87125", + "VaultTokenTest_test_permitExpiredDeadline": "35216", + "VaultTokenTest_test_permitInvalidSigner": "63493", + "VaultTokenTest_test_permitZeroAddressSpender": "34786", + "VaultTokenTest_test_transfer": "66388", + "VaultTokenTest_test_transferFrom": "67648", + "VaultTokenTest_test_transferFromMoreThanAllowance": "32415", + "VaultTokenTest_test_transferFromMoreThanBalance": "37863", + "VaultTokenTest_test_transferFromZeroAddress": "38458", + "VaultTokenTest_test_transferMoreThanBalance": "38297", + "VaultTokenTest_test_transferToZeroAddress": "33681", + "VaultTokenTest_test_transferWithOsTokenPosition": "96531", + "VaultTokenTest_test_unlimitedAllowance": "69308", + "VaultTokenTest_test_updateExitQueueBurnsShares": "115022" } \ No newline at end of file diff --git a/snapshots/VaultValidatorsTest.json b/snapshots/VaultValidatorsTest.json index 3db88da5..5ad6d7ad 100644 --- a/snapshots/VaultValidatorsTest.json +++ b/snapshots/VaultValidatorsTest.json @@ -1,43 +1,43 @@ { - "VaultValidatorsTest_test_consolidateValidators_byManager": "79212", - "VaultValidatorsTest_test_consolidateValidators_feeHandling": "86213", - "VaultValidatorsTest_test_consolidateValidators_invalidSignature": "72120", - "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsEmpty": "55608", - "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsLength": "57330", - "VaultValidatorsTest_test_consolidateValidators_multipleValidators": "99394", - "VaultValidatorsTest_test_consolidateValidators_notManager": "57250", - "VaultValidatorsTest_test_consolidateValidators_untrackedDestination": "78708", - "VaultValidatorsTest_test_consolidateValidators_withOracleSignatures": "115804", - "VaultValidatorsTest_test_consolidateValidators_withSignature": "111705", - "VaultValidatorsTest_test_fundValidators_byManager": "105234", - "VaultValidatorsTest_test_fundValidators_insufficientAssets": "103060", - "VaultValidatorsTest_test_fundValidators_invalidSignature": "63689", - "VaultValidatorsTest_test_fundValidators_invalidValidators": "45735", - "VaultValidatorsTest_test_fundValidators_multipleValidators": "144799", - "VaultValidatorsTest_test_fundValidators_nonExistingValidator": "57140", - "VaultValidatorsTest_test_fundValidators_notHarvested": "44394", - "VaultValidatorsTest_test_fundValidators_notManager": "48806", - "VaultValidatorsTest_test_fundValidators_v1Validators": "52712", - "VaultValidatorsTest_test_fundValidators_withSignature": "137699", - "VaultValidatorsTest_test_registerValidators_byManager": "267691", - "VaultValidatorsTest_test_registerValidators_insufficientAssets": "41930", - "VaultValidatorsTest_test_registerValidators_invalidSignature": "206436", - "VaultValidatorsTest_test_registerValidators_invalidValidatorLength": "194366", - "VaultValidatorsTest_test_registerValidators_invalidValidators": "188419", - "VaultValidatorsTest_test_registerValidators_multipleValidators": "304623", - "VaultValidatorsTest_test_registerValidators_nonceIncrement": "148369", - "VaultValidatorsTest_test_registerValidators_notHarvested": "170713", - "VaultValidatorsTest_test_registerValidators_notManager": "193585", - "VaultValidatorsTest_test_registerValidators_v1Validators": "244804", - "VaultValidatorsTest_test_registerValidators_v2Validators": "267691", - "VaultValidatorsTest_test_registerValidators_withSignature": "300155", - "VaultValidatorsTest_test_setValidatorsManager_valueNotChanged": "36929", - "VaultValidatorsTest_test_withdrawValidators_byManager": "74102", - "VaultValidatorsTest_test_withdrawValidators_feeHandling": "81103", - "VaultValidatorsTest_test_withdrawValidators_invalidSignature": "70861", - "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsEmpty": "55082", - "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsLength": "56493", - "VaultValidatorsTest_test_withdrawValidators_multipleValidators": "89825", - "VaultValidatorsTest_test_withdrawValidators_notAuthorized": "56024", - "VaultValidatorsTest_test_withdrawValidators_withSignature": "106539" + "VaultValidatorsTest_test_consolidateValidators_byManager": "79135", + "VaultValidatorsTest_test_consolidateValidators_feeHandling": "86136", + "VaultValidatorsTest_test_consolidateValidators_invalidSignature": "72043", + "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsEmpty": "55534", + "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsLength": "57264", + "VaultValidatorsTest_test_consolidateValidators_multipleValidators": "99317", + "VaultValidatorsTest_test_consolidateValidators_notManager": "57173", + "VaultValidatorsTest_test_consolidateValidators_untrackedDestination": "78631", + "VaultValidatorsTest_test_consolidateValidators_withOracleSignatures": "115727", + "VaultValidatorsTest_test_consolidateValidators_withSignature": "111628", + "VaultValidatorsTest_test_fundValidators_byManager": "105133", + "VaultValidatorsTest_test_fundValidators_insufficientAssets": "102959", + "VaultValidatorsTest_test_fundValidators_invalidSignature": "63600", + "VaultValidatorsTest_test_fundValidators_invalidValidators": "45658", + "VaultValidatorsTest_test_fundValidators_multipleValidators": "144698", + "VaultValidatorsTest_test_fundValidators_nonExistingValidator": "57053", + "VaultValidatorsTest_test_fundValidators_notHarvested": "44293", + "VaultValidatorsTest_test_fundValidators_notManager": "48705", + "VaultValidatorsTest_test_fundValidators_v1Validators": "52635", + "VaultValidatorsTest_test_fundValidators_withSignature": "137598", + "VaultValidatorsTest_test_registerValidators_byManager": "247198", + "VaultValidatorsTest_test_registerValidators_insufficientAssets": "41865", + "VaultValidatorsTest_test_registerValidators_invalidSignature": "185943", + "VaultValidatorsTest_test_registerValidators_invalidValidatorLength": "173850", + "VaultValidatorsTest_test_registerValidators_invalidValidators": "167927", + "VaultValidatorsTest_test_registerValidators_multipleValidators": "284119", + "VaultValidatorsTest_test_registerValidators_nonceIncrement": "148304", + "VaultValidatorsTest_test_registerValidators_notHarvested": "170603", + "VaultValidatorsTest_test_registerValidators_notManager": "173092", + "VaultValidatorsTest_test_registerValidators_v1Validators": "224299", + "VaultValidatorsTest_test_registerValidators_v2Validators": "247198", + "VaultValidatorsTest_test_registerValidators_withSignature": "279662", + "VaultValidatorsTest_test_setValidatorsManager_valueNotChanged": "36852", + "VaultValidatorsTest_test_withdrawValidators_byManager": "74025", + "VaultValidatorsTest_test_withdrawValidators_feeHandling": "81026", + "VaultValidatorsTest_test_withdrawValidators_invalidSignature": "70784", + "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsEmpty": "55005", + "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsLength": "56438", + "VaultValidatorsTest_test_withdrawValidators_multipleValidators": "89748", + "VaultValidatorsTest_test_withdrawValidators_notAuthorized": "55947", + "VaultValidatorsTest_test_withdrawValidators_withSignature": "106450" } \ No newline at end of file diff --git a/snapshots/VaultVersionTest.json b/snapshots/VaultVersionTest.json index b0ac9537..acb84cd4 100644 --- a/snapshots/VaultVersionTest.json +++ b/snapshots/VaultVersionTest.json @@ -1,12 +1,12 @@ { - "VaultVersionTest_test_reinitializeFails": "30923", - "VaultVersionTest_test_upgradeMultipleSteps": "61033", - "VaultVersionTest_test_upgradeNonAdminFails": "38872", - "VaultVersionTest_test_upgradeToDifferentVaultIdFails": "42352", - "VaultVersionTest_test_upgradeToNextVersion": "83681", - "VaultVersionTest_test_upgradeToSameVersionFails": "37105", - "VaultVersionTest_test_upgradeToSkipVersionFails": "43074", - "VaultVersionTest_test_upgradeToUnapprovedImplementationFails": "46371", - "VaultVersionTest_test_upgradeToZeroAddressFails": "36640", - "VaultVersionTest_test_upgradeWithInvalidCallDataFails": "60730" + "VaultVersionTest_test_reinitializeFails": "30846", + "VaultVersionTest_test_upgradeMultipleSteps": "60956", + "VaultVersionTest_test_upgradeNonAdminFails": "38795", + "VaultVersionTest_test_upgradeToDifferentVaultIdFails": "42287", + "VaultVersionTest_test_upgradeToNextVersion": "83604", + "VaultVersionTest_test_upgradeToSameVersionFails": "37028", + "VaultVersionTest_test_upgradeToSkipVersionFails": "42997", + "VaultVersionTest_test_upgradeToUnapprovedImplementationFails": "46282", + "VaultVersionTest_test_upgradeToZeroAddressFails": "36563", + "VaultVersionTest_test_upgradeWithInvalidCallDataFails": "60653" } \ No newline at end of file From 1f8d0c676a418e0cfd3f80e78f887497a628945a Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 19 Jan 2026 17:51:37 +0200 Subject: [PATCH 18/19] Fix formatting --- test/SharedMevEscrow.t.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/SharedMevEscrow.t.sol b/test/SharedMevEscrow.t.sol index 1b4c29b7..e70667ff 100644 --- a/test/SharedMevEscrow.t.sol +++ b/test/SharedMevEscrow.t.sol @@ -20,7 +20,8 @@ contract MockVaultEthStaking { // Mock non-compliant vault that doesn't implement receiveFromMevEscrow contract MockNonCompliantVault { // No receiveFromMevEscrow function -} + + } contract SharedMevEscrowTest is Test { VaultsRegistry public vaultsRegistry; From fa5978386af6e93f9e9b389932c94dc5c6d62913 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 19 Jan 2026 17:57:36 +0200 Subject: [PATCH 19/19] update gas snapshots --- snapshots/DepositDataRegistryTest.json | 14 ++-- snapshots/EthBlocklistErc20VaultTest.json | 12 +-- snapshots/EthBlocklistVaultTest.json | 10 +-- snapshots/EthErc20VaultTest.json | 24 +++--- snapshots/EthMetaVaultTest.json | 30 ++++---- snapshots/EthOsTokenRedeemerTest.json | 4 +- snapshots/EthOsTokenVaultEscrowTest.json | 34 ++++----- snapshots/EthPrivErc20VaultTest.json | 12 +-- snapshots/EthPrivVaultTest.json | 16 ++-- snapshots/EthRewardSplitterTest.json | 22 +++--- snapshots/EthVaultTest.json | 14 ++-- snapshots/GnoBlocklistVaultTest.json | 6 +- snapshots/GnoErc20VaultTest.json | 16 ++-- snapshots/GnoOsTokenRedeemerTest.json | 2 +- snapshots/GnoOsTokenVaultEscrowTest.json | 6 +- snapshots/GnoPrivErc20VaultTest.json | 8 +- snapshots/GnoPrivVaultTest.json | 10 +-- snapshots/GnoRewardSplitterTest.json | 24 +++--- snapshots/GnoVaultExitQueueTest.json | 2 +- snapshots/GnoVaultTest.json | 8 +- snapshots/KeeperOraclesTest.json | 2 +- snapshots/KeeperRewardsTest.json | 4 +- snapshots/VaultAdminTest.json | 18 ++--- snapshots/VaultEnterExitTest.json | 40 +++++----- snapshots/VaultEthStakingTest.json | 32 ++++---- snapshots/VaultFeeTest.json | 26 +++---- snapshots/VaultGnoStakingTest.json | 22 +++--- snapshots/VaultOsTokenTest.json | 92 +++++++++++------------ snapshots/VaultSubVaultsTest.json | 36 ++++----- snapshots/VaultTokenTest.json | 40 +++++----- snapshots/VaultValidatorsTest.json | 82 ++++++++++---------- snapshots/VaultVersionTest.json | 20 ++--- 32 files changed, 344 insertions(+), 344 deletions(-) diff --git a/snapshots/DepositDataRegistryTest.json b/snapshots/DepositDataRegistryTest.json index eeae33a8..4c42af58 100644 --- a/snapshots/DepositDataRegistryTest.json +++ b/snapshots/DepositDataRegistryTest.json @@ -1,9 +1,9 @@ { - "DepositDataRegistryTest_test_registerValidator_succeedsWith0x01Validator": "252873", - "DepositDataRegistryTest_test_registerValidator_succeedsWith0x02Validator": "275779", - "DepositDataRegistryTest_test_registerValidators_successWith0x01Validators": "294364", - "DepositDataRegistryTest_test_registerValidators_successWith0x02Validators": "318425", - "DepositDataRegistryTest_test_setDepositDataManager_succeeds": "68540", - "DepositDataRegistryTest_test_setDepositDataRoot_succeeds": "45950", - "DepositDataRegistryTest_test_updateVaultState_succeeds": "112878" + "DepositDataRegistryTest_test_registerValidator_succeedsWith0x01Validator": "273443", + "DepositDataRegistryTest_test_registerValidator_succeedsWith0x02Validator": "296337", + "DepositDataRegistryTest_test_registerValidators_successWith0x01Validators": "314933", + "DepositDataRegistryTest_test_registerValidators_successWith0x02Validators": "339002", + "DepositDataRegistryTest_test_setDepositDataManager_succeeds": "68682", + "DepositDataRegistryTest_test_setDepositDataRoot_succeeds": "65115", + "DepositDataRegistryTest_test_updateVaultState_succeeds": "128278" } \ No newline at end of file diff --git a/snapshots/EthBlocklistErc20VaultTest.json b/snapshots/EthBlocklistErc20VaultTest.json index 12073b0e..940d521b 100644 --- a/snapshots/EthBlocklistErc20VaultTest.json +++ b/snapshots/EthBlocklistErc20VaultTest.json @@ -1,8 +1,8 @@ { - "EthBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "90179", - "EthBlocklistErc20VaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "83416", - "EthBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "162374", - "EthBlocklistErc20VaultTest_test_deploysCorrectly": "7141423", - "EthBlocklistErc20VaultTest_test_transfer": "62311", - "EthBlocklistErc20VaultTest_test_upgradesCorrectly": "5912023" + "EthBlocklistErc20VaultTest_test_canDepositAsNonBlockedUser": "89912", + "EthBlocklistErc20VaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "84302", + "EthBlocklistErc20VaultTest_test_canMintOsTokenAsNonBlockedUser": "161501", + "EthBlocklistErc20VaultTest_test_deploysCorrectly": "625440", + "EthBlocklistErc20VaultTest_test_transfer": "61693", + "EthBlocklistErc20VaultTest_test_upgradesCorrectly": "119538" } \ No newline at end of file diff --git a/snapshots/EthBlocklistVaultTest.json b/snapshots/EthBlocklistVaultTest.json index 019791ff..7c46536f 100644 --- a/snapshots/EthBlocklistVaultTest.json +++ b/snapshots/EthBlocklistVaultTest.json @@ -1,7 +1,7 @@ { - "EthBlocklistVaultTest_test_canDepositAsNonBlockedUser": "88065", - "EthBlocklistVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "81588", - "EthBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "162185", - "EthBlocklistVaultTest_test_deploysCorrectly": "6424841", - "EthBlocklistVaultTest_test_upgradesCorrectly": "5269766" + "EthBlocklistVaultTest_test_canDepositAsNonBlockedUser": "87959", + "EthBlocklistVaultTest_test_canDepositUsingReceiveAsNotBlockedUser": "82360", + "EthBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "161528", + "EthBlocklistVaultTest_test_deploysCorrectly": "550241", + "EthBlocklistVaultTest_test_upgradesCorrectly": "118869" } \ No newline at end of file diff --git a/snapshots/EthErc20VaultTest.json b/snapshots/EthErc20VaultTest.json index e21b071a..d27828c7 100644 --- a/snapshots/EthErc20VaultTest.json +++ b/snapshots/EthErc20VaultTest.json @@ -1,15 +1,15 @@ { - "EthErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90024", - "EthErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96042", - "EthErc20VaultTest_test_deploysCorrectly": "1181159", - "EthErc20VaultTest_test_depositAndMintOsToken": "183195", - "EthErc20VaultTest_test_depositViaReceiveFallback_emitsTransfer": "80036", - "EthErc20VaultTest_test_deposit_emitsTransfer": "83498", - "EthErc20VaultTest_test_enterExitQueue_emitsTransfer": "79620", - "EthErc20VaultTest_test_redeem_emitsEvent": "63109", - "EthErc20VaultTest_test_updateExitQueue_emitsTransfer": "145031", - "EthErc20VaultTest_test_updateStateAndDepositAndMintOsToken": "214650", + "EthErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90077", + "EthErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96608", + "EthErc20VaultTest_test_deploysCorrectly": "455018", + "EthErc20VaultTest_test_depositAndMintOsToken": "203747", + "EthErc20VaultTest_test_depositViaReceiveFallback_emitsTransfer": "77951", + "EthErc20VaultTest_test_deposit_emitsTransfer": "81413", + "EthErc20VaultTest_test_enterExitQueue_emitsTransfer": "89780", + "EthErc20VaultTest_test_redeem_emitsEvent": "58285", + "EthErc20VaultTest_test_updateExitQueue_emitsTransfer": "177065", + "EthErc20VaultTest_test_updateStateAndDepositAndMintOsToken": "230047", "EthErc20VaultTest_test_upgradesCorrectly": "119424", - "EthErc20VaultTest_test_withdrawValidator_unknown": "55874", - "EthErc20VaultTest_test_withdrawValidator_validatorsManager": "74015" + "EthErc20VaultTest_test_withdrawValidator_unknown": "55951", + "EthErc20VaultTest_test_withdrawValidator_validatorsManager": "74092" } \ No newline at end of file diff --git a/snapshots/EthMetaVaultTest.json b/snapshots/EthMetaVaultTest.json index e86f5499..520a8996 100644 --- a/snapshots/EthMetaVaultTest.json +++ b/snapshots/EthMetaVaultTest.json @@ -1,17 +1,17 @@ { - "EthMetaVaultTest_test_calculateSubVaultsRedemptions_exactWithdrawableAssets": "49570", - "EthMetaVaultTest_test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets": "114252", - "EthMetaVaultTest_test_calculateSubVaultsRedemptions_success": "114288", - "EthMetaVaultTest_test_deposit": "85427", - "EthMetaVaultTest_test_depositAndMintOsToken": "177450", - "EthMetaVaultTest_test_depositViaFallback": "82045", - "EthMetaVaultTest_test_isStateUpdateRequired_true": "135934", - "EthMetaVaultTest_test_redeemSubVaultsAssets_noRedeemRequests": "71422", - "EthMetaVaultTest_test_redeemSubVaultsAssets_noRoundingErrors": "699872", - "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets": "699884", - "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets": "698818", - "EthMetaVaultTest_test_redeemSubVaultsAssets_success": "698818", - "EthMetaVaultTest_test_updateStateAndDeposit": "230401", - "EthMetaVaultTest_test_updateStateAndDepositAndMintOsToken": "201506", - "EthMetaVaultTest_test_userClaimExitedAssets": "83553" + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_exactWithdrawableAssets": "47214", + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_insufficientWithdrawableAssets": "96978", + "EthMetaVaultTest_test_calculateSubVaultsRedemptions_success": "97007", + "EthMetaVaultTest_test_deposit": "85504", + "EthMetaVaultTest_test_depositAndMintOsToken": "179869", + "EthMetaVaultTest_test_depositViaFallback": "82122", + "EthMetaVaultTest_test_isStateUpdateRequired_true": "136011", + "EthMetaVaultTest_test_redeemSubVaultsAssets_noRedeemRequests": "69102", + "EthMetaVaultTest_test_redeemSubVaultsAssets_noRoundingErrors": "549703", + "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsExceedSubVaultsWithdrawableAssets": "548661", + "EthMetaVaultTest_test_redeemSubVaultsAssets_redeemAssetsLessThanSubVaultsWithdrawableAssets": "549679", + "EthMetaVaultTest_test_redeemSubVaultsAssets_success": "549679", + "EthMetaVaultTest_test_updateStateAndDeposit": "197478", + "EthMetaVaultTest_test_updateStateAndDepositAndMintOsToken": "200840", + "EthMetaVaultTest_test_userClaimExitedAssets": "54213" } \ No newline at end of file diff --git a/snapshots/EthOsTokenRedeemerTest.json b/snapshots/EthOsTokenRedeemerTest.json index c0661bfa..03035fdf 100644 --- a/snapshots/EthOsTokenRedeemerTest.json +++ b/snapshots/EthOsTokenRedeemerTest.json @@ -3,8 +3,8 @@ "EthOsTokenRedeemerTest_test_permitOsToken_success": "82575", "EthOsTokenRedeemerTest_test_processExitQueue_nothingToProcess": "33998", "EthOsTokenRedeemerTest_test_processExitQueue_success": "101715", - "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions": "275836", - "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "201282", + "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_multiplePositions": "297443", + "EthOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "201436", "EthOsTokenRedeemerTest_test_setPositionsManager": "35834", "EthOsTokenRedeemerTest_test_setRedeemablePositions_success": "100561", "EthOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "101264", diff --git a/snapshots/EthOsTokenVaultEscrowTest.json b/snapshots/EthOsTokenVaultEscrowTest.json index 303aaf07..33bc1d77 100644 --- a/snapshots/EthOsTokenVaultEscrowTest.json +++ b/snapshots/EthOsTokenVaultEscrowTest.json @@ -1,18 +1,18 @@ { - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_insufficientShares": "66561", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_minimalAmount": "95171", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_noProcessedAssets": "91032", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_insufficientShares": "66982", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_minimalAmount": "95063", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_noProcessedAssets": "91451", "EthOsTokenVaultEscrowTest_test_claimExitedAssets_nonExistentPosition": "50879", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_notOwner": "64594", - "EthOsTokenVaultEscrowTest_test_claimExitedAssets_withFeeAccrual": "104066", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_claimExitedAssets": "80548", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_exitRequestNotProcessed": "49805", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_notOwner": "65015", + "EthOsTokenVaultEscrowTest_test_claimExitedAssets_withFeeAccrual": "103958", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_claimExitedAssets": "80440", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_exitRequestNotProcessed": "49774", "EthOsTokenVaultEscrowTest_test_processExitedAssets_invalidPosition": "33255", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_partialClaim": "95745", - "EthOsTokenVaultEscrowTest_test_processExitedAssets_success": "78632", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_partialClaim": "95637", + "EthOsTokenVaultEscrowTest_test_processExitedAssets_success": "71278", "EthOsTokenVaultEscrowTest_test_register_accessDenied": "33699", "EthOsTokenVaultEscrowTest_test_register_directCall": "83600", - "EthOsTokenVaultEscrowTest_test_register_fullFlow": "148598", + "EthOsTokenVaultEscrowTest_test_register_fullFlow": "163659", "EthOsTokenVaultEscrowTest_test_register_invalidShares": "33742", "EthOsTokenVaultEscrowTest_test_register_zeroAddress": "33386", "EthOsTokenVaultEscrowTest_test_setAuthenticator_onlyOwner": "30174", @@ -24,12 +24,12 @@ "EthOsTokenVaultEscrowTest_test_updateLiqConfig_invalidThreshold_zero": "29970", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_onlyOwner": "30318", "EthOsTokenVaultEscrowTest_test_updateLiqConfig_success": "41245", - "OsTokenLiquidationTest_test_liquidateOsToken_invalidHealthFactor": "52577", + "OsTokenLiquidationTest_test_liquidateOsToken_invalidHealthFactor": "52469", "OsTokenLiquidationTest_test_liquidateOsToken_invalidPosition": "41078", - "OsTokenLiquidationTest_test_liquidateOsToken_invalidReceivedAssets": "42107", - "OsTokenLiquidationTest_test_liquidateOsToken_partialLiquidation": "94732", - "OsTokenLiquidationTest_test_liquidateOsToken_success": "97403", - "OsTokenLiquidationTest_test_liquidateOsToken_zeroAddress": "24140", - "OsTokenLiquidationTest_test_redeemOsToken_notRedeemer": "27586", - "OsTokenLiquidationTest_test_redeemOsToken_success": "118818" + "OsTokenLiquidationTest_test_liquidateOsToken_invalidReceivedAssets": "41999", + "OsTokenLiquidationTest_test_liquidateOsToken_partialLiquidation": "94624", + "OsTokenLiquidationTest_test_liquidateOsToken_success": "97295", + "OsTokenLiquidationTest_test_liquidateOsToken_zeroAddress": "24032", + "OsTokenLiquidationTest_test_redeemOsToken_notRedeemer": "27478", + "OsTokenLiquidationTest_test_redeemOsToken_success": "118710" } \ No newline at end of file diff --git a/snapshots/EthPrivErc20VaultTest.json b/snapshots/EthPrivErc20VaultTest.json index fc805a6b..74df5a12 100644 --- a/snapshots/EthPrivErc20VaultTest.json +++ b/snapshots/EthPrivErc20VaultTest.json @@ -1,9 +1,9 @@ { - "EthPrivErc20VaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser": "206492", - "EthPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "81309", - "EthPrivErc20VaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77607", - "EthPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161447", - "EthPrivErc20VaultTest_test_deploysCorrectly": "1351603", - "EthPrivErc20VaultTest_test_transfer": "59632", + "EthPrivErc20VaultTest_test_canDepositAndMintOsTokenAsWhitelistedUser": "206655", + "EthPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "81395", + "EthPrivErc20VaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77693", + "EthPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161601", + "EthPrivErc20VaultTest_test_deploysCorrectly": "625462", + "EthPrivErc20VaultTest_test_transfer": "59709", "EthPrivErc20VaultTest_test_upgradesCorrectly": "119559" } \ No newline at end of file diff --git a/snapshots/EthPrivVaultTest.json b/snapshots/EthPrivVaultTest.json index caba06c4..e860566b 100644 --- a/snapshots/EthPrivVaultTest.json +++ b/snapshots/EthPrivVaultTest.json @@ -1,11 +1,11 @@ { - "EthPrivVaultTest_test_canDepositAsWhitelistedUser": "81507", - "EthPrivVaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "77933", - "EthPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "141052", - "EthPrivVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "123490", - "EthPrivVaultTest_test_deploysCorrectly": "1276416", - "EthPrivVaultTest_test_depositAndMintOsTokenAsWhitelistedUser": "179985", - "EthPrivVaultTest_test_setWhitelister": "36531", - "EthPrivVaultTest_test_updateWhitelist": "54466", + "EthPrivVaultTest_test_canDepositAsWhitelistedUser": "79431", + "EthPrivVaultTest_test_canDepositUsingReceiveAsWhitelistedUser": "75857", + "EthPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161628", + "EthPrivVaultTest_test_canUpdateStateAndDepositAsWhitelistedUser": "140687", + "EthPrivVaultTest_test_deploysCorrectly": "550259", + "EthPrivVaultTest_test_depositAndMintOsTokenAsWhitelistedUser": "200570", + "EthPrivVaultTest_test_setWhitelister": "36608", + "EthPrivVaultTest_test_updateWhitelist": "54543", "EthPrivVaultTest_test_upgradesCorrectly": "118985" } \ No newline at end of file diff --git a/snapshots/EthRewardSplitterTest.json b/snapshots/EthRewardSplitterTest.json index 46618681..6d3d62db 100644 --- a/snapshots/EthRewardSplitterTest.json +++ b/snapshots/EthRewardSplitterTest.json @@ -1,14 +1,14 @@ { - "EthRewardSplitter_claimExitedAssetsOnBehalf": "761821", - "EthRewardSplitter_claimVaultTokens": "768781", - "EthRewardSplitter_decreaseShares": "89902", - "EthRewardSplitter_enterExitQueue": "156712", - "EthRewardSplitter_enterExitQueueMaxWithdrawal": "116240", - "EthRewardSplitter_enterExitQueueOnBehalf": "934262", - "EthRewardSplitter_increaseShares": "72897", + "EthRewardSplitter_claimExitedAssetsOnBehalf": "767084", + "EthRewardSplitter_claimVaultTokens": "771719", + "EthRewardSplitter_decreaseShares": "90133", + "EthRewardSplitter_enterExitQueue": "171850", + "EthRewardSplitter_enterExitQueueMaxWithdrawal": "131378", + "EthRewardSplitter_enterExitQueueOnBehalf": "964738", + "EthRewardSplitter_increaseShares": "73128", "EthRewardSplitter_receiveEth": "31194", - "EthRewardSplitter_syncRewards": "77528", - "EthRewardSplitter_syncRewardsDetailed": "77528", - "EthRewardSplitter_test_setClaimer": "66192", - "EthRewardSplitter_updateVaultState": "117222" + "EthRewardSplitter_syncRewards": "77682", + "EthRewardSplitter_syncRewardsDetailed": "77682", + "EthRewardSplitter_test_setClaimer": "66269", + "EthRewardSplitter_updateVaultState": "132613" } \ No newline at end of file diff --git a/snapshots/EthVaultTest.json b/snapshots/EthVaultTest.json index d3e5c6f9..395ae7c5 100644 --- a/snapshots/EthVaultTest.json +++ b/snapshots/EthVaultTest.json @@ -1,10 +1,10 @@ { - "EthVaultTest_test_deploysCorrectly": "1106052", - "EthVaultTest_test_depositAndMintOsToken": "201897", - "EthVaultTest_test_exitQueue_works": "84171", - "EthVaultTest_test_fallbackDeposit": "79703", - "EthVaultTest_test_updateStateAndDepositAndMintOsToken": "212767", + "EthVaultTest_test_deploysCorrectly": "379895", + "EthVaultTest_test_depositAndMintOsToken": "202060", + "EthVaultTest_test_exitQueue_works": "99131", + "EthVaultTest_test_fallbackDeposit": "77627", + "EthVaultTest_test_updateStateAndDepositAndMintOsToken": "228182", "EthVaultTest_test_upgradesCorrectly": "118828", - "EthVaultTest_test_withdrawValidator_unknown": "55896", - "EthVaultTest_test_withdrawValidator_validatorsManager": "74037" + "EthVaultTest_test_withdrawValidator_unknown": "55973", + "EthVaultTest_test_withdrawValidator_validatorsManager": "74114" } \ No newline at end of file diff --git a/snapshots/GnoBlocklistVaultTest.json b/snapshots/GnoBlocklistVaultTest.json index 1d0751d4..27856b9d 100644 --- a/snapshots/GnoBlocklistVaultTest.json +++ b/snapshots/GnoBlocklistVaultTest.json @@ -1,6 +1,6 @@ { - "GnoBlocklistVaultTest_test_canDepositAsNonBlockedUser": "160200", - "GnoBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "140994", - "GnoBlocklistVaultTest_test_deploysCorrectly": "1554711", + "GnoBlocklistVaultTest_test_canDepositAsNonBlockedUser": "158124", + "GnoBlocklistVaultTest_test_canMintOsTokenAsNonBlockedUser": "161570", + "GnoBlocklistVaultTest_test_deploysCorrectly": "702387", "GnoBlocklistVaultTest_test_upgradesCorrectly": "339168" } \ No newline at end of file diff --git a/snapshots/GnoErc20VaultTest.json b/snapshots/GnoErc20VaultTest.json index 58cfb178..7581c144 100644 --- a/snapshots/GnoErc20VaultTest.json +++ b/snapshots/GnoErc20VaultTest.json @@ -1,11 +1,11 @@ { - "GnoErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90047", - "GnoErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96066", - "GnoErc20VaultTest_test_deploysCorrectly": "1605639", - "GnoErc20VaultTest_test_deposit_emitsTransfer": "102817", - "GnoErc20VaultTest_test_enterExitQueue_emitsTransfer": "79620", - "GnoErc20VaultTest_test_redeem_emitsEvent": "81893", + "GnoErc20VaultTest_test_canTransferFromSharesWithHighLtv": "90100", + "GnoErc20VaultTest_test_cannotTransferFromSharesWithLowLtv": "96637", + "GnoErc20VaultTest_test_deploysCorrectly": "753330", + "GnoErc20VaultTest_test_deposit_emitsTransfer": "100732", + "GnoErc20VaultTest_test_enterExitQueue_emitsTransfer": "89780", + "GnoErc20VaultTest_test_redeem_emitsEvent": "77069", "GnoErc20VaultTest_test_upgradesCorrectly": "339858", - "VaultGnoErc20VaultTest_test_withdrawValidator_unknown": "55896", - "VaultGnoErc20VaultTest_test_withdrawValidator_validatorsManager": "74037" + "VaultGnoErc20VaultTest_test_withdrawValidator_unknown": "55973", + "VaultGnoErc20VaultTest_test_withdrawValidator_validatorsManager": "74114" } \ No newline at end of file diff --git a/snapshots/GnoOsTokenRedeemerTest.json b/snapshots/GnoOsTokenRedeemerTest.json index 45c895b7..1bb452bb 100644 --- a/snapshots/GnoOsTokenRedeemerTest.json +++ b/snapshots/GnoOsTokenRedeemerTest.json @@ -1,6 +1,6 @@ { "GnoOsTokenRedeemerTest_test_claimExitedAssets_fullWithdrawal": "80495", "GnoOsTokenRedeemerTest_test_permitGnoToken_success": "91926", - "GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "252637", + "GnoOsTokenRedeemerTest_test_redeemOsTokenPositions_success_singlePosition": "252803", "GnoOsTokenRedeemerTest_test_swapAssetsToOsTokenShares_success": "137991" } \ No newline at end of file diff --git a/snapshots/GnoOsTokenVaultEscrowTest.json b/snapshots/GnoOsTokenVaultEscrowTest.json index 50ebb412..7e35444f 100644 --- a/snapshots/GnoOsTokenVaultEscrowTest.json +++ b/snapshots/GnoOsTokenVaultEscrowTest.json @@ -1,5 +1,5 @@ { - "GnoOsTokenVaultEscrowTest_test_transferAssets_claim": "140422", - "GnoOsTokenVaultEscrowTest_test_transferAssets_process": "897276", - "GnoOsTokenVaultEscrowTest_test_transferAssets_transfer": "165196" + "GnoOsTokenVaultEscrowTest_test_transferAssets_claim": "140340", + "GnoOsTokenVaultEscrowTest_test_transferAssets_process": "893932", + "GnoOsTokenVaultEscrowTest_test_transferAssets_transfer": "163157" } \ No newline at end of file diff --git a/snapshots/GnoPrivErc20VaultTest.json b/snapshots/GnoPrivErc20VaultTest.json index 40ca55e2..2a1d7fa1 100644 --- a/snapshots/GnoPrivErc20VaultTest.json +++ b/snapshots/GnoPrivErc20VaultTest.json @@ -1,7 +1,7 @@ { - "GnoPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "158113", - "GnoPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "140967", - "GnoPrivErc20VaultTest_test_deploysCorrectly": "1806115", - "GnoPrivErc20VaultTest_test_transfer": "64456", + "GnoPrivErc20VaultTest_test_canDepositAsWhitelistedUser": "156037", + "GnoPrivErc20VaultTest_test_canMintOsTokenAsWhitelistedUser": "161543", + "GnoPrivErc20VaultTest_test_deploysCorrectly": "953806", + "GnoPrivErc20VaultTest_test_transfer": "59709", "GnoPrivErc20VaultTest_test_upgradesCorrectly": "340008" } \ No newline at end of file diff --git a/snapshots/GnoPrivVaultTest.json b/snapshots/GnoPrivVaultTest.json index 5c9fbb88..ff248b50 100644 --- a/snapshots/GnoPrivVaultTest.json +++ b/snapshots/GnoPrivVaultTest.json @@ -1,8 +1,8 @@ { - "GnoPrivVaultTest_test_canDepositAsWhitelistedUser": "156204", - "GnoPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161326", - "GnoPrivVaultTest_test_deploysCorrectly": "1554755", - "GnoPrivVaultTest_test_setWhitelister": "36531", - "GnoPrivVaultTest_test_updateWhitelist": "54444", + "GnoPrivVaultTest_test_canDepositAsWhitelistedUser": "154140", + "GnoPrivVaultTest_test_canMintOsTokenAsWhitelistedUser": "161480", + "GnoPrivVaultTest_test_deploysCorrectly": "702431", + "GnoPrivVaultTest_test_setWhitelister": "36608", + "GnoPrivVaultTest_test_updateWhitelist": "54521", "GnoPrivVaultTest_test_upgradesCorrectly": "339304" } \ No newline at end of file diff --git a/snapshots/GnoRewardSplitterTest.json b/snapshots/GnoRewardSplitterTest.json index e74b0ec3..28733f70 100644 --- a/snapshots/GnoRewardSplitterTest.json +++ b/snapshots/GnoRewardSplitterTest.json @@ -1,14 +1,14 @@ { - "GnoRewardSplitter_claimExitedAssets": "97033", - "GnoRewardSplitter_claimExitedAssetsOnBehalf": "816246", - "GnoRewardSplitter_claimVaultTokens": "850283", - "GnoRewardSplitter_decreaseShares": "100810", - "GnoRewardSplitter_enterExitQueue": "171591", - "GnoRewardSplitter_enterExitQueueMaxWithdrawal": "116262", - "GnoRewardSplitter_enterExitQueueOnBehalf": "996950", - "GnoRewardSplitter_increaseShares": "72941", - "GnoRewardSplitter_setClaimer": "66214", - "GnoRewardSplitter_syncRewards": "77550", - "GnoRewardSplitter_syncRewardsDetailed": "77550", - "GnoRewardSplitter_updateVaultState": "148330" + "GnoRewardSplitter_claimExitedAssets": "94809", + "GnoRewardSplitter_claimExitedAssetsOnBehalf": "824394", + "GnoRewardSplitter_claimVaultTokens": "855626", + "GnoRewardSplitter_decreaseShares": "101041", + "GnoRewardSplitter_enterExitQueue": "186729", + "GnoRewardSplitter_enterExitQueueMaxWithdrawal": "131400", + "GnoRewardSplitter_enterExitQueueOnBehalf": "1029028", + "GnoRewardSplitter_increaseShares": "73172", + "GnoRewardSplitter_setClaimer": "66291", + "GnoRewardSplitter_syncRewards": "77704", + "GnoRewardSplitter_syncRewardsDetailed": "77704", + "GnoRewardSplitter_updateVaultState": "165440" } \ No newline at end of file diff --git a/snapshots/GnoVaultExitQueueTest.json b/snapshots/GnoVaultExitQueueTest.json index d4a57a9a..d054d0a7 100644 --- a/snapshots/GnoVaultExitQueueTest.json +++ b/snapshots/GnoVaultExitQueueTest.json @@ -1,6 +1,6 @@ { "GnoVaultExitQueueTest_test_ExitingAssetsPenalized_event": "194845", - "GnoVaultExitQueueTest_test_claim_position1_after_upgrade": "6654158", + "GnoVaultExitQueueTest_test_claim_position1_after_upgrade": "6653963", "GnoVaultExitQueueTest_test_claim_position2_before_upgrade": "125766", "GnoVaultExitQueueTest_test_claim_position3_after_upgrade": "165242", "GnoVaultExitQueueTest_test_claim_position4_after_upgrade": "160429" diff --git a/snapshots/GnoVaultTest.json b/snapshots/GnoVaultTest.json index 54bd8e19..0ddb8366 100644 --- a/snapshots/GnoVaultTest.json +++ b/snapshots/GnoVaultTest.json @@ -1,7 +1,7 @@ { - "GnoVaultTest_test_deploysCorrectly": "1530669", - "GnoVaultTest_test_exitQueue_works": "101271", + "GnoVaultTest_test_deploysCorrectly": "678345", + "GnoVaultTest_test_exitQueue_works": "99131", "GnoVaultTest_test_upgradesCorrectly": "339206", - "GnoVaultTest_test_withdrawValidator_unknown": "55873", - "GnoVaultTest_test_withdrawValidator_validatorsManager": "74014" + "GnoVaultTest_test_withdrawValidator_unknown": "55950", + "GnoVaultTest_test_withdrawValidator_validatorsManager": "74091" } \ No newline at end of file diff --git a/snapshots/KeeperOraclesTest.json b/snapshots/KeeperOraclesTest.json index b2c3ed87..4ded9d36 100644 --- a/snapshots/KeeperOraclesTest.json +++ b/snapshots/KeeperOraclesTest.json @@ -8,5 +8,5 @@ "KeeperOraclesTest_test_removeOracle_success": "36666", "KeeperOraclesTest_test_updateConfig_onlyOwner": "31726", "KeeperOraclesTest_test_updateConfig_success": "32787", - "KeeperOraclesTest_test_verifySignatures_throughKeeperRewards": "242384" + "KeeperOraclesTest_test_verifySignatures_throughKeeperRewards": "242271" } \ No newline at end of file diff --git a/snapshots/KeeperRewardsTest.json b/snapshots/KeeperRewardsTest.json index 3598dde0..78e34a09 100644 --- a/snapshots/KeeperRewardsTest.json +++ b/snapshots/KeeperRewardsTest.json @@ -9,8 +9,8 @@ "KeeperRewardsTest_test_isCollateralized": "10123", "KeeperRewardsTest_test_isHarvestRequired": "12620", "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_1": "135313", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_2": "606838", - "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_3": "606884", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_2": "606725", + "KeeperRewardsTest_test_multipleRewardUpdatesAndHarvests_round_3": "606771", "KeeperRewardsTest_test_setRewardsMinOracles": "37748", "KeeperRewardsTest_test_setRewardsMinOracles_tooMany": "29542", "KeeperRewardsTest_test_setRewardsMinOracles_zero": "30031", diff --git a/snapshots/VaultAdminTest.json b/snapshots/VaultAdminTest.json index be8037ca..27e434c9 100644 --- a/snapshots/VaultAdminTest.json +++ b/snapshots/VaultAdminTest.json @@ -1,11 +1,11 @@ { - "VaultAdminTest_test_checkAdmin_withOtherFunctions_admin": "50386", - "VaultAdminTest_test_checkAdmin_withOtherFunctions_nonAdmin": "34776", - "VaultAdminTest_test_initialization": "1104846", - "VaultAdminTest_test_setAdmin_byAdmin": "39170", - "VaultAdminTest_test_setAdmin_byNonAdmin": "36844", - "VaultAdminTest_test_setAdmin_toSameValue": "35058", - "VaultAdminTest_test_setAdmin_toZeroAddress": "34559", - "VaultAdminTest_test_setMetadata_byAdmin": "36941", - "VaultAdminTest_test_setMetadata_byNonAdmin": "35253" + "VaultAdminTest_test_checkAdmin_withOtherFunctions_admin": "48301", + "VaultAdminTest_test_checkAdmin_withOtherFunctions_nonAdmin": "34853", + "VaultAdminTest_test_initialization": "378686", + "VaultAdminTest_test_setAdmin_byAdmin": "39247", + "VaultAdminTest_test_setAdmin_byNonAdmin": "36921", + "VaultAdminTest_test_setAdmin_toSameValue": "35135", + "VaultAdminTest_test_setAdmin_toZeroAddress": "34636", + "VaultAdminTest_test_setMetadata_byAdmin": "37018", + "VaultAdminTest_test_setMetadata_byNonAdmin": "35330" } \ No newline at end of file diff --git a/snapshots/VaultEnterExitTest.json b/snapshots/VaultEnterExitTest.json index 4daf252e..e2c63547 100644 --- a/snapshots/VaultEnterExitTest.json +++ b/snapshots/VaultEnterExitTest.json @@ -1,25 +1,25 @@ { - "VaultEnterExitTest_test_calculateExitedAssets_invalidPosition": "18523", - "VaultEnterExitTest_test_claimExitedAssets": "739476", - "VaultEnterExitTest_test_claimExitedAssets_insufficientDelay": "46261", - "VaultEnterExitTest_test_claimExitedAssets_invalidCheckpoint": "36538", - "VaultEnterExitTest_test_deposit_exceedingCapacity": "47695", - "VaultEnterExitTest_test_deposit_success_basic": "85236", - "VaultEnterExitTest_test_deposit_success_differentReceiver": "85127", - "VaultEnterExitTest_test_deposit_success_multipleDeposits": "59539", - "VaultEnterExitTest_test_deposit_success_receiveFunction": "81694", - "VaultEnterExitTest_test_deposit_success_withReferrer": "78873", - "VaultEnterExitTest_test_deposit_zeroAddress": "52003", - "VaultEnterExitTest_test_deposit_zeroAmount": "45556", - "VaultEnterExitTest_test_enterExitQueue_afterValidatorExit": "79383", - "VaultEnterExitTest_test_enterExitQueue_basicFlow": "79371", + "VaultEnterExitTest_test_calculateExitedAssets_invalidPosition": "18600", + "VaultEnterExitTest_test_claimExitedAssets": "752879", + "VaultEnterExitTest_test_claimExitedAssets_insufficientDelay": "43924", + "VaultEnterExitTest_test_claimExitedAssets_invalidCheckpoint": "36507", + "VaultEnterExitTest_test_deposit_exceedingCapacity": "47943", + "VaultEnterExitTest_test_deposit_success_basic": "83160", + "VaultEnterExitTest_test_deposit_success_differentReceiver": "83051", + "VaultEnterExitTest_test_deposit_success_multipleDeposits": "57463", + "VaultEnterExitTest_test_deposit_success_receiveFunction": "79618", + "VaultEnterExitTest_test_deposit_success_withReferrer": "76797", + "VaultEnterExitTest_test_deposit_zeroAddress": "49918", + "VaultEnterExitTest_test_deposit_zeroAmount": "43471", + "VaultEnterExitTest_test_enterExitQueue_afterValidatorExit": "94343", + "VaultEnterExitTest_test_enterExitQueue_basicFlow": "94331", "VaultEnterExitTest_test_enterExitQueue_directRedemption": "84394", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_tooManyShares": "89821", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroAddress": "48202", - "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroShares": "31267", - "VaultEnterExitTest_test_enterExitQueue_multiUser_sender2": "77129", - "VaultEnterExitTest_test_enterExitQueue_multiUser_user1": "79371", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_tooManyShares": "64810", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroAddress": "48332", + "VaultEnterExitTest_test_enterExitQueue_invalidParams_zeroShares": "31344", + "VaultEnterExitTest_test_enterExitQueue_multiUser_sender2": "74995", + "VaultEnterExitTest_test_enterExitQueue_multiUser_user1": "94331", "VaultEnterExitTest_test_enterExitQueue_multipleUpdates": "92234", - "VaultEnterExitTest_test_enterExitQueue_partialExit": "84171", + "VaultEnterExitTest_test_enterExitQueue_partialExit": "99131", "VaultEnterExitTest_test_rescueAssets": "46295" } \ No newline at end of file diff --git a/snapshots/VaultEthStakingTest.json b/snapshots/VaultEthStakingTest.json index 729ee335..cf1c5e76 100644 --- a/snapshots/VaultEthStakingTest.json +++ b/snapshots/VaultEthStakingTest.json @@ -1,18 +1,18 @@ { - "VaultEthStakingTest_test_deposit": "87401", - "VaultEthStakingTest_test_depositAndMintOsToken": "199884", - "VaultEthStakingTest_test_fundValidators_invalid": "57051", - "VaultEthStakingTest_test_fundValidators_valid": "140378", - "VaultEthStakingTest_test_harvestAssets": "115278", - "VaultEthStakingTest_test_invalidSecurityDeposit": "308152", - "VaultEthStakingTest_test_receive": "79594", - "VaultEthStakingTest_test_receiveFromMevEscrow_fail": "36493", - "VaultEthStakingTest_test_receiveFromMevEscrow_success": "37749", - "VaultEthStakingTest_test_registerValidators_01prefix": "224304", - "VaultEthStakingTest_test_registerValidators_02prefix": "500771", - "VaultEthStakingTest_test_transferVaultAssets": "61183", - "VaultEthStakingTest_test_updateStateAndDeposit": "151229", - "VaultEthStakingTest_test_updateStateAndDepositAndMintOsToken": "238753", - "VaultEthStakingTest_test_validatorMinMaxEffectiveBalance": "179403", - "VaultEthStakingTest_test_withdrawValidator_fullFlow": "74037" + "VaultEthStakingTest_test_deposit": "85325", + "VaultEthStakingTest_test_depositAndMintOsToken": "200047", + "VaultEthStakingTest_test_fundValidators_invalid": "57140", + "VaultEthStakingTest_test_fundValidators_valid": "140479", + "VaultEthStakingTest_test_harvestAssets": "130845", + "VaultEthStakingTest_test_invalidSecurityDeposit": "308363", + "VaultEthStakingTest_test_receive": "77518", + "VaultEthStakingTest_test_receiveFromMevEscrow_fail": "36570", + "VaultEthStakingTest_test_receiveFromMevEscrow_success": "37826", + "VaultEthStakingTest_test_registerValidators_01prefix": "244809", + "VaultEthStakingTest_test_registerValidators_02prefix": "500922", + "VaultEthStakingTest_test_transferVaultAssets": "54251", + "VaultEthStakingTest_test_updateStateAndDeposit": "166673", + "VaultEthStakingTest_test_updateStateAndDepositAndMintOsToken": "254280", + "VaultEthStakingTest_test_validatorMinMaxEffectiveBalance": "199908", + "VaultEthStakingTest_test_withdrawValidator_fullFlow": "74114" } \ No newline at end of file diff --git a/snapshots/VaultFeeTest.json b/snapshots/VaultFeeTest.json index f0023439..399199f7 100644 --- a/snapshots/VaultFeeTest.json +++ b/snapshots/VaultFeeTest.json @@ -1,16 +1,16 @@ { - "VaultFeeTest_test_feeCollection": "106513", - "VaultFeeTest_test_feePercent_changeAffectsFutureRewards": "89410", - "VaultFeeTest_test_setFeePercent_aboveMaximum": "42465", + "VaultFeeTest_test_feeCollection": "121919", + "VaultFeeTest_test_feePercent_changeAffectsFutureRewards": "87733", + "VaultFeeTest_test_setFeePercent_aboveMaximum": "40380", "VaultFeeTest_test_setFeePercent_initialZeroToOne": "44812", - "VaultFeeTest_test_setFeePercent_maxIncrease": "40557", - "VaultFeeTest_test_setFeePercent_notAdmin": "34479", - "VaultFeeTest_test_setFeePercent_requiresHarvest": "42424", - "VaultFeeTest_test_setFeePercent_success": "49160", - "VaultFeeTest_test_setFeePercent_tooSoon": "40222", - "VaultFeeTest_test_setFeeRecipient_notAdmin": "36777", - "VaultFeeTest_test_setFeeRecipient_requiresHarvest": "44719", - "VaultFeeTest_test_setFeeRecipient_sameValue": "44928", - "VaultFeeTest_test_setFeeRecipient_success": "51034", - "VaultFeeTest_test_setFeeRecipient_zeroAddress": "42428" + "VaultFeeTest_test_setFeePercent_maxIncrease": "38472", + "VaultFeeTest_test_setFeePercent_notAdmin": "34556", + "VaultFeeTest_test_setFeePercent_requiresHarvest": "42501", + "VaultFeeTest_test_setFeePercent_success": "47075", + "VaultFeeTest_test_setFeePercent_tooSoon": "38137", + "VaultFeeTest_test_setFeeRecipient_notAdmin": "36854", + "VaultFeeTest_test_setFeeRecipient_requiresHarvest": "44796", + "VaultFeeTest_test_setFeeRecipient_sameValue": "42843", + "VaultFeeTest_test_setFeeRecipient_success": "48949", + "VaultFeeTest_test_setFeeRecipient_zeroAddress": "40343" } \ No newline at end of file diff --git a/snapshots/VaultGnoStakingTest.json b/snapshots/VaultGnoStakingTest.json index 7a54b8d8..5f5bc848 100644 --- a/snapshots/VaultGnoStakingTest.json +++ b/snapshots/VaultGnoStakingTest.json @@ -1,14 +1,14 @@ { - "VaultGnoStakingCoverageTest_test_processTotalAssetsDelta_smallXdaiBalance": "140889", - "VaultGnoStakingCoverageTest_test_registerValidator_topUp_invalid": "57075", - "VaultGnoStakingCoverageTest_test_registerValidator_topUp_valid": "182085", - "VaultGnoStakingTest_test_deposit": "103213", - "VaultGnoStakingTest_test_processTotalAssetsDelta": "285829", + "VaultGnoStakingCoverageTest_test_processTotalAssetsDelta_smallXdaiBalance": "175401", + "VaultGnoStakingCoverageTest_test_registerValidator_topUp_invalid": "57128", + "VaultGnoStakingCoverageTest_test_registerValidator_topUp_valid": "182150", + "VaultGnoStakingTest_test_deposit": "101137", + "VaultGnoStakingTest_test_processTotalAssetsDelta": "303158", "VaultGnoStakingTest_test_pullWithdrawals": "92921", - "VaultGnoStakingTest_test_receive_xDai": "213062", - "VaultGnoStakingTest_test_transferVaultAssets": "97089", - "VaultGnoStakingTest_test_vaultGnoStaking_init": "1530605", - "VaultGnoStakingTest_test_withdrawValidator_fullFlow": "74014", - "test_registerValidators_succeeds_0x01": "270486", - "test_registerValidators_succeeds_0x02": "629961" + "VaultGnoStakingTest_test_receive_xDai": "213139", + "VaultGnoStakingTest_test_transferVaultAssets": "89952", + "VaultGnoStakingTest_test_vaultGnoStaking_init": "678313", + "VaultGnoStakingTest_test_withdrawValidator_fullFlow": "74091", + "test_registerValidators_succeeds_0x01": "286528", + "test_registerValidators_succeeds_0x02": "630112" } \ No newline at end of file diff --git a/snapshots/VaultOsTokenTest.json b/snapshots/VaultOsTokenTest.json index 24be0c25..44d8888e 100644 --- a/snapshots/VaultOsTokenTest.json +++ b/snapshots/VaultOsTokenTest.json @@ -1,48 +1,48 @@ { - "VaultOsTokenTest_test_burnOsToken_afterFeeSync": "75924", - "VaultOsTokenTest_test_burnOsToken_allShares": "70622", - "VaultOsTokenTest_test_burnOsToken_basic": "75422", - "VaultOsTokenTest_test_burnOsToken_exceedingShares": "48648", - "VaultOsTokenTest_test_burnOsToken_improvesLTV": "70922", - "VaultOsTokenTest_test_burnOsToken_invalidPosition": "66133", - "VaultOsTokenTest_test_burnOsToken_multipleBurns": "75422", - "VaultOsTokenTest_test_burnOsToken_zeroShares": "37769", - "VaultOsTokenTest_test_enterExitQueue_ltvViolation": "103355", - "VaultOsTokenTest_test_liquidateOsToken_basic": "120642", - "VaultOsTokenTest_test_liquidateOsToken_bonus": "125142", - "VaultOsTokenTest_test_liquidateOsToken_invalidReceivedAssets": "75682", - "VaultOsTokenTest_test_liquidateOsToken_liquidationDisabled": "69110", - "VaultOsTokenTest_test_liquidateOsToken_partialLiquidation": "125142", - "VaultOsTokenTest_test_mintOsToken_basic": "161956", - "VaultOsTokenTest_test_mintOsToken_feeSync": "107924", - "VaultOsTokenTest_test_mintOsToken_ltvValidation": "144817", - "VaultOsTokenTest_test_mintOsToken_maxAmount": "162325", - "VaultOsTokenTest_test_mintOsToken_multipleReceivers": "119835", - "VaultOsTokenTest_test_mintOsToken_notCollateralized": "41718", - "VaultOsTokenTest_test_mintOsToken_notHarvested": "47538", - "VaultOsTokenTest_test_mintOsToken_repeatedMinting": "105516", - "VaultOsTokenTest_test_mintOsToken_zeroAddressReceiver": "86682", - "VaultOsTokenTest_test_mintOsToken_zeroShares": "88938", - "VaultOsTokenTest_test_redeemOsToken_afterFeeSync": "134413", - "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_fail": "50862", - "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_success": "250372", - "VaultOsTokenTest_test_redeemOsToken_basic": "133911", - "VaultOsTokenTest_test_redeemOsToken_fullPosition": "133911", - "VaultOsTokenTest_test_redeemOsToken_goodHealthFactor": "133994", - "VaultOsTokenTest_test_redeemOsToken_insufficientShares": "112587", - "VaultOsTokenTest_test_redeemOsToken_nonExistentPosition": "86919", - "VaultOsTokenTest_test_redeemOsToken_onlyRedeemer": "36283", - "VaultOsTokenTest_test_redeemOsToken_zeroAddressReceiver": "40608", - "VaultOsTokenTest_test_redeemOsToken_zeroShares": "90165", - "VaultOsTokenTest_test_redeemVsLiquidate": "133994", - "VaultOsTokenTest_test_test_liquidateOsToken_notHarvested": "43471", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_basic": "152596", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_claim": "107885", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_maxAmount": "152596", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_moreThanOwned": "44742", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_noPosition": "43425", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_notHarvested": "41265", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_partialTransfer": "158159", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process": "719076", - "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_zeroShares": "47468" + "VaultOsTokenTest_test_burnOsToken_afterFeeSync": "76001", + "VaultOsTokenTest_test_burnOsToken_allShares": "70699", + "VaultOsTokenTest_test_burnOsToken_basic": "75499", + "VaultOsTokenTest_test_burnOsToken_exceedingShares": "48725", + "VaultOsTokenTest_test_burnOsToken_improvesLTV": "70999", + "VaultOsTokenTest_test_burnOsToken_invalidPosition": "66210", + "VaultOsTokenTest_test_burnOsToken_multipleBurns": "75499", + "VaultOsTokenTest_test_burnOsToken_zeroShares": "37846", + "VaultOsTokenTest_test_enterExitQueue_ltvViolation": "118315", + "VaultOsTokenTest_test_liquidateOsToken_basic": "120719", + "VaultOsTokenTest_test_liquidateOsToken_bonus": "125219", + "VaultOsTokenTest_test_liquidateOsToken_invalidReceivedAssets": "75759", + "VaultOsTokenTest_test_liquidateOsToken_liquidationDisabled": "69187", + "VaultOsTokenTest_test_liquidateOsToken_partialLiquidation": "125219", + "VaultOsTokenTest_test_mintOsToken_basic": "162110", + "VaultOsTokenTest_test_mintOsToken_feeSync": "108078", + "VaultOsTokenTest_test_mintOsToken_ltvValidation": "144971", + "VaultOsTokenTest_test_mintOsToken_maxAmount": "162479", + "VaultOsTokenTest_test_mintOsToken_multipleReceivers": "119989", + "VaultOsTokenTest_test_mintOsToken_notCollateralized": "41965", + "VaultOsTokenTest_test_mintOsToken_notHarvested": "47615", + "VaultOsTokenTest_test_mintOsToken_repeatedMinting": "105670", + "VaultOsTokenTest_test_mintOsToken_zeroAddressReceiver": "86836", + "VaultOsTokenTest_test_mintOsToken_zeroShares": "89092", + "VaultOsTokenTest_test_redeemOsToken_afterFeeSync": "134490", + "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_fail": "50939", + "VaultOsTokenTest_test_redeemOsToken_afterStateUpdate_success": "265876", + "VaultOsTokenTest_test_redeemOsToken_basic": "133988", + "VaultOsTokenTest_test_redeemOsToken_fullPosition": "133988", + "VaultOsTokenTest_test_redeemOsToken_goodHealthFactor": "134071", + "VaultOsTokenTest_test_redeemOsToken_insufficientShares": "112664", + "VaultOsTokenTest_test_redeemOsToken_nonExistentPosition": "86996", + "VaultOsTokenTest_test_redeemOsToken_onlyRedeemer": "36360", + "VaultOsTokenTest_test_redeemOsToken_zeroAddressReceiver": "40685", + "VaultOsTokenTest_test_redeemOsToken_zeroShares": "90242", + "VaultOsTokenTest_test_redeemVsLiquidate": "134071", + "VaultOsTokenTest_test_test_liquidateOsToken_notHarvested": "43548", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_basic": "167657", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_claim": "107765", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_maxAmount": "167657", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_moreThanOwned": "44819", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_noPosition": "43502", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_notHarvested": "41342", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_partialTransfer": "173220", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_process": "736184", + "VaultOsTokenTest_test_transferOsTokenPositionToEscrow_zeroShares": "47545" } \ No newline at end of file diff --git a/snapshots/VaultSubVaultsTest.json b/snapshots/VaultSubVaultsTest.json index ecb80863..47f4c6bb 100644 --- a/snapshots/VaultSubVaultsTest.json +++ b/snapshots/VaultSubVaultsTest.json @@ -1,24 +1,24 @@ { - "VaultSubVaultsTest_test_acceptMetaSubVault_success": "128367", - "VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_proposesMetaVault": "92830", - "VaultSubVaultsTest_test_addSubVault_success": "130790", - "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares": "417793", + "VaultSubVaultsTest_test_acceptMetaSubVault_success": "128444", + "VaultSubVaultsTest_test_addSubVault_metaVaultAsSubVault_proposesMetaVault": "92907", + "VaultSubVaultsTest_test_addSubVault_success": "130867", + "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_ejectionConsumesShares": "417792", "VaultSubVaultsTest_test_claimSubVaultsExitedAssets_partiallyClaimsExitedAssets": "120643", - "VaultSubVaultsTest_test_depositToSubVaults_maxVaults": "3851800", - "VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults": "339984", - "VaultSubVaultsTest_test_depositToSubVaults_singleSubVault": "108481", - "VaultSubVaultsTest_test_depositToSubVaults_withMetaVaultSubVault": "416371", - "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_emptySubVault": "57558", - "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_withShares": "206717", - "VaultSubVaultsTest_test_rejectMetaSubVault_byAdmin_success": "42160", - "VaultSubVaultsTest_test_rejectMetaSubVault_byOwner_success": "49956", - "VaultSubVaultsTest_test_setSubVaultsCurator_success": "50933", - "VaultSubVaultsTest_test_updateState_enterExitQueueMaxVaults": "6764216", - "VaultSubVaultsTest_test_updateState_withMetaVaultSubVault_success": "212284", + "VaultSubVaultsTest_test_depositToSubVaults_maxVaults": "3874692", + "VaultSubVaultsTest_test_depositToSubVaults_multipleSubVaults": "295050", + "VaultSubVaultsTest_test_depositToSubVaults_singleSubVault": "142644", + "VaultSubVaultsTest_test_depositToSubVaults_withMetaVaultSubVault": "371161", + "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_emptySubVault": "57635", + "VaultSubVaultsTest_test_ejectSubVault_metaVaultAsSubVault_withShares": "206794", + "VaultSubVaultsTest_test_rejectMetaSubVault_byAdmin_success": "42237", + "VaultSubVaultsTest_test_rejectMetaSubVault_byOwner_success": "50033", + "VaultSubVaultsTest_test_setSubVaultsCurator_success": "51010", + "VaultSubVaultsTest_test_updateState_enterExitQueueMaxVaults": "7041487", + "VaultSubVaultsTest_test_updateState_withMetaVaultSubVault_success": "169251", "test_addSubVault_firstSubVault": "145160", - "test_depositToSubVaults_ejectingSubVault": "198989", + "test_depositToSubVaults_ejectingSubVault": "153972", "test_ejectSubVault_emptySubVault": "66011", - "test_ejectSubVault_subVaultWithShares": "174390", + "test_ejectSubVault_subVaultWithShares": "205950", "test_updateState_newTotalAssets": "178910", - "test_updateState_unprocessedSubVaultExit": "204480" + "test_updateState_unprocessedSubVaultExit": "204479" } \ No newline at end of file diff --git a/snapshots/VaultTokenTest.json b/snapshots/VaultTokenTest.json index a2aea0aa..e738ddbc 100644 --- a/snapshots/VaultTokenTest.json +++ b/snapshots/VaultTokenTest.json @@ -1,22 +1,22 @@ { - "VaultTokenTest_test_approve": "58769", - "VaultTokenTest_test_approveZeroAddress": "33626", - "VaultTokenTest_test_depositEmitsTransferEvent": "83416", - "VaultTokenTest_test_enterExitQueueEmitsTransferEvent": "82106", - "VaultTokenTest_test_invalidTokenMetaNameTooLong": "477774", - "VaultTokenTest_test_invalidTokenMetaSymbolTooLong": "310843", - "VaultTokenTest_test_permit": "87125", - "VaultTokenTest_test_permitExpiredDeadline": "35216", - "VaultTokenTest_test_permitInvalidSigner": "63493", - "VaultTokenTest_test_permitZeroAddressSpender": "34786", - "VaultTokenTest_test_transfer": "66388", - "VaultTokenTest_test_transferFrom": "67648", - "VaultTokenTest_test_transferFromMoreThanAllowance": "32415", - "VaultTokenTest_test_transferFromMoreThanBalance": "37863", - "VaultTokenTest_test_transferFromZeroAddress": "38458", - "VaultTokenTest_test_transferMoreThanBalance": "38297", - "VaultTokenTest_test_transferToZeroAddress": "33681", - "VaultTokenTest_test_transferWithOsTokenPosition": "96531", - "VaultTokenTest_test_unlimitedAllowance": "69308", - "VaultTokenTest_test_updateExitQueueBurnsShares": "115022" + "VaultTokenTest_test_approve": "58846", + "VaultTokenTest_test_approveZeroAddress": "33703", + "VaultTokenTest_test_depositEmitsTransferEvent": "83493", + "VaultTokenTest_test_enterExitQueueEmitsTransferEvent": "97066", + "VaultTokenTest_test_invalidTokenMetaNameTooLong": "478037", + "VaultTokenTest_test_invalidTokenMetaSymbolTooLong": "311105", + "VaultTokenTest_test_permit": "87202", + "VaultTokenTest_test_permitExpiredDeadline": "35305", + "VaultTokenTest_test_permitInvalidSigner": "63558", + "VaultTokenTest_test_permitZeroAddressSpender": "34863", + "VaultTokenTest_test_transfer": "66441", + "VaultTokenTest_test_transferFrom": "67701", + "VaultTokenTest_test_transferFromMoreThanAllowance": "32480", + "VaultTokenTest_test_transferFromMoreThanBalance": "37928", + "VaultTokenTest_test_transferFromZeroAddress": "38535", + "VaultTokenTest_test_transferMoreThanBalance": "38362", + "VaultTokenTest_test_transferToZeroAddress": "33734", + "VaultTokenTest_test_transferWithOsTokenPosition": "96584", + "VaultTokenTest_test_unlimitedAllowance": "69361", + "VaultTokenTest_test_updateExitQueueBurnsShares": "146991" } \ No newline at end of file diff --git a/snapshots/VaultValidatorsTest.json b/snapshots/VaultValidatorsTest.json index 5ad6d7ad..3db88da5 100644 --- a/snapshots/VaultValidatorsTest.json +++ b/snapshots/VaultValidatorsTest.json @@ -1,43 +1,43 @@ { - "VaultValidatorsTest_test_consolidateValidators_byManager": "79135", - "VaultValidatorsTest_test_consolidateValidators_feeHandling": "86136", - "VaultValidatorsTest_test_consolidateValidators_invalidSignature": "72043", - "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsEmpty": "55534", - "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsLength": "57264", - "VaultValidatorsTest_test_consolidateValidators_multipleValidators": "99317", - "VaultValidatorsTest_test_consolidateValidators_notManager": "57173", - "VaultValidatorsTest_test_consolidateValidators_untrackedDestination": "78631", - "VaultValidatorsTest_test_consolidateValidators_withOracleSignatures": "115727", - "VaultValidatorsTest_test_consolidateValidators_withSignature": "111628", - "VaultValidatorsTest_test_fundValidators_byManager": "105133", - "VaultValidatorsTest_test_fundValidators_insufficientAssets": "102959", - "VaultValidatorsTest_test_fundValidators_invalidSignature": "63600", - "VaultValidatorsTest_test_fundValidators_invalidValidators": "45658", - "VaultValidatorsTest_test_fundValidators_multipleValidators": "144698", - "VaultValidatorsTest_test_fundValidators_nonExistingValidator": "57053", - "VaultValidatorsTest_test_fundValidators_notHarvested": "44293", - "VaultValidatorsTest_test_fundValidators_notManager": "48705", - "VaultValidatorsTest_test_fundValidators_v1Validators": "52635", - "VaultValidatorsTest_test_fundValidators_withSignature": "137598", - "VaultValidatorsTest_test_registerValidators_byManager": "247198", - "VaultValidatorsTest_test_registerValidators_insufficientAssets": "41865", - "VaultValidatorsTest_test_registerValidators_invalidSignature": "185943", - "VaultValidatorsTest_test_registerValidators_invalidValidatorLength": "173850", - "VaultValidatorsTest_test_registerValidators_invalidValidators": "167927", - "VaultValidatorsTest_test_registerValidators_multipleValidators": "284119", - "VaultValidatorsTest_test_registerValidators_nonceIncrement": "148304", - "VaultValidatorsTest_test_registerValidators_notHarvested": "170603", - "VaultValidatorsTest_test_registerValidators_notManager": "173092", - "VaultValidatorsTest_test_registerValidators_v1Validators": "224299", - "VaultValidatorsTest_test_registerValidators_v2Validators": "247198", - "VaultValidatorsTest_test_registerValidators_withSignature": "279662", - "VaultValidatorsTest_test_setValidatorsManager_valueNotChanged": "36852", - "VaultValidatorsTest_test_withdrawValidators_byManager": "74025", - "VaultValidatorsTest_test_withdrawValidators_feeHandling": "81026", - "VaultValidatorsTest_test_withdrawValidators_invalidSignature": "70784", - "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsEmpty": "55005", - "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsLength": "56438", - "VaultValidatorsTest_test_withdrawValidators_multipleValidators": "89748", - "VaultValidatorsTest_test_withdrawValidators_notAuthorized": "55947", - "VaultValidatorsTest_test_withdrawValidators_withSignature": "106450" + "VaultValidatorsTest_test_consolidateValidators_byManager": "79212", + "VaultValidatorsTest_test_consolidateValidators_feeHandling": "86213", + "VaultValidatorsTest_test_consolidateValidators_invalidSignature": "72120", + "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsEmpty": "55608", + "VaultValidatorsTest_test_consolidateValidators_invalidValidatorsLength": "57330", + "VaultValidatorsTest_test_consolidateValidators_multipleValidators": "99394", + "VaultValidatorsTest_test_consolidateValidators_notManager": "57250", + "VaultValidatorsTest_test_consolidateValidators_untrackedDestination": "78708", + "VaultValidatorsTest_test_consolidateValidators_withOracleSignatures": "115804", + "VaultValidatorsTest_test_consolidateValidators_withSignature": "111705", + "VaultValidatorsTest_test_fundValidators_byManager": "105234", + "VaultValidatorsTest_test_fundValidators_insufficientAssets": "103060", + "VaultValidatorsTest_test_fundValidators_invalidSignature": "63689", + "VaultValidatorsTest_test_fundValidators_invalidValidators": "45735", + "VaultValidatorsTest_test_fundValidators_multipleValidators": "144799", + "VaultValidatorsTest_test_fundValidators_nonExistingValidator": "57140", + "VaultValidatorsTest_test_fundValidators_notHarvested": "44394", + "VaultValidatorsTest_test_fundValidators_notManager": "48806", + "VaultValidatorsTest_test_fundValidators_v1Validators": "52712", + "VaultValidatorsTest_test_fundValidators_withSignature": "137699", + "VaultValidatorsTest_test_registerValidators_byManager": "267691", + "VaultValidatorsTest_test_registerValidators_insufficientAssets": "41930", + "VaultValidatorsTest_test_registerValidators_invalidSignature": "206436", + "VaultValidatorsTest_test_registerValidators_invalidValidatorLength": "194366", + "VaultValidatorsTest_test_registerValidators_invalidValidators": "188419", + "VaultValidatorsTest_test_registerValidators_multipleValidators": "304623", + "VaultValidatorsTest_test_registerValidators_nonceIncrement": "148369", + "VaultValidatorsTest_test_registerValidators_notHarvested": "170713", + "VaultValidatorsTest_test_registerValidators_notManager": "193585", + "VaultValidatorsTest_test_registerValidators_v1Validators": "244804", + "VaultValidatorsTest_test_registerValidators_v2Validators": "267691", + "VaultValidatorsTest_test_registerValidators_withSignature": "300155", + "VaultValidatorsTest_test_setValidatorsManager_valueNotChanged": "36929", + "VaultValidatorsTest_test_withdrawValidators_byManager": "74102", + "VaultValidatorsTest_test_withdrawValidators_feeHandling": "81103", + "VaultValidatorsTest_test_withdrawValidators_invalidSignature": "70861", + "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsEmpty": "55082", + "VaultValidatorsTest_test_withdrawValidators_invalidValidatorsLength": "56493", + "VaultValidatorsTest_test_withdrawValidators_multipleValidators": "89825", + "VaultValidatorsTest_test_withdrawValidators_notAuthorized": "56024", + "VaultValidatorsTest_test_withdrawValidators_withSignature": "106539" } \ No newline at end of file diff --git a/snapshots/VaultVersionTest.json b/snapshots/VaultVersionTest.json index acb84cd4..b0ac9537 100644 --- a/snapshots/VaultVersionTest.json +++ b/snapshots/VaultVersionTest.json @@ -1,12 +1,12 @@ { - "VaultVersionTest_test_reinitializeFails": "30846", - "VaultVersionTest_test_upgradeMultipleSteps": "60956", - "VaultVersionTest_test_upgradeNonAdminFails": "38795", - "VaultVersionTest_test_upgradeToDifferentVaultIdFails": "42287", - "VaultVersionTest_test_upgradeToNextVersion": "83604", - "VaultVersionTest_test_upgradeToSameVersionFails": "37028", - "VaultVersionTest_test_upgradeToSkipVersionFails": "42997", - "VaultVersionTest_test_upgradeToUnapprovedImplementationFails": "46282", - "VaultVersionTest_test_upgradeToZeroAddressFails": "36563", - "VaultVersionTest_test_upgradeWithInvalidCallDataFails": "60653" + "VaultVersionTest_test_reinitializeFails": "30923", + "VaultVersionTest_test_upgradeMultipleSteps": "61033", + "VaultVersionTest_test_upgradeNonAdminFails": "38872", + "VaultVersionTest_test_upgradeToDifferentVaultIdFails": "42352", + "VaultVersionTest_test_upgradeToNextVersion": "83681", + "VaultVersionTest_test_upgradeToSameVersionFails": "37105", + "VaultVersionTest_test_upgradeToSkipVersionFails": "43074", + "VaultVersionTest_test_upgradeToUnapprovedImplementationFails": "46371", + "VaultVersionTest_test_upgradeToZeroAddressFails": "36640", + "VaultVersionTest_test_upgradeWithInvalidCallDataFails": "60730" } \ No newline at end of file