diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bca9f299..226c63ef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: '18' + node-version: '22' cache: 'npm' cache-dependency-path: package-lock.json diff --git a/.solhint.json b/.solhint.json index 28d25245..d6d276b3 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,7 +1,7 @@ { "extends": "solhint:recommended", "rules": { - "compiler-version": ["error", "0.8.29"], + "compiler-version": ["error", "0.8.30"], "no-inline-assembly": "off", "no-unused-import": "error", "func-named-parameters": "off", diff --git a/.tool-versions b/.tool-versions index 89f278a5..40366f0a 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -solidity 0.8.29 +solidity 0.8.30 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ed225b8e..5dffac3a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,45 @@ # Release Notes +## [v0.2.0] 2024-06-xx +### Description +- fix(strategy-manager): change slashing and obligationUpdate event order +- fix(strategy): revert slashing if strategy not opted in +- feat: add and enforce basic checks for config vars +- Enrich SSVBasedApps interface (#51) +- Remove IERC165 Interface Check (#52) +- feat: check bApp registered during OptIn (#53) +- chore: update docs + add release notes +- chore: sepolia deployment, bump version +- Example ECDSA verifier (#72) +- Fix: Propose Obligation Update storage ref (#74) +- feat: add script for implementation update and solidity 0.8.30 + +### Contracts +#### New (examples) +- `contract ECDSAVerifier is OwnableBasedApp` + +### Interfaces +#### New +- `interface IViews` + +#### Modified +- `interface IBasedApp is IERC165` -> `interface IBasedApp` + +### Functions +#### Modified +- `function registerBApp( + address[] calldata tokens, + uint32[] calldata sharedRiskLevels, + string calldata metadataURI) external` + +-> +- `function registerBApp( + ICore.TokenConfig[] calldata tokenConfigs, + string calldata metadataURI +) external +` + + ## [v0.1.1 - fix] 2024-06-17 ### Description - Fix to update the storage references when proposing an obligation @@ -9,13 +49,11 @@ ## [v0.1.1] 2024-06-xx ### Functions - #### Modified - `function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external` - `function updateDisabledFeatures(uint32 value) external onlyOwner` ### Errors - #### New - `error InvalidDisabledFeatures();` - `error InvalidFeeExpireTime();` @@ -28,6 +66,5 @@ - `error InvalidWithdrawalTimelockPeriod();` ### Events - #### Modified - `event BAppRegistered(address indexed bApp, ICore.TokenConfig[] tokenConfigs, string metadataURI);` diff --git a/artifacts/deploy-sepolia.json b/artifacts/deploy-sepolia.json new file mode 100644 index 00000000..aa2551f8 --- /dev/null +++ b/artifacts/deploy-sepolia.json @@ -0,0 +1,25 @@ +{ + "addresses": { + "BAppsModule": "0x7321E62c0aC3C49b84464cD2EacE43e6995a1ca5", + "ProtocolModule": "0x7a30a199ff232Ddbf2863bB5B9517D67B482484c", + "SSVBasedAppsImpl": "0x9fCD30C040B40D633bae83d49b3A2B79D02899b0", + "SSVBasedAppsProxy": "0x413eEE616aEC463E5605B008EBC2DDDC536a0cE8", + "StrategyModule": "0x868C2789045d7ffC144635Da7D8cC0a974C58f89" + }, + "chainInfo": { + "chainId": 11155111, + "deploymentBlock": 8489872 + }, + "parameters": { + "disabledFeatures": 0, + "feeExpireTime": 3600, + "feeTimelockPeriod": 300, + "maxFeeIncrement": 500, + "maxShares": 100000000000000000000000000000000000000000000000000, + "obligationExpireTime": 3600, + "obligationTimelockPeriod": 300, + "tokenUpdateTimelockPeriod": 300, + "withdrawalExpireTime": 3600, + "withdrawalTimelockPeriod": 300 + } +} \ No newline at end of file diff --git a/docs/src/README.md b/docs/src/README.md index ad5e85ce..dda8eab2 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -65,6 +65,8 @@ __`❍ forge test`__ [Slashing & Withdrawals](./guides/slashing-and-withdrawals.md) +[Generation pattern](./guides/generations.md) + ## :gear: _Feature Activation_ [Feature Activation](./guides/feature-activation.md) @@ -78,7 +80,6 @@ __`❍ forge test`__ [Based Apps Onboarding Guide](./guides/bApp-onboarding.md) -  ## :rocket: _Deployments_ diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 2a0eba86..2a23b608 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -8,6 +8,7 @@ - [IProtocolManager](src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md) - [ISSVBasedApps](src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md) - [IStrategyManager](src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md) + - [IViews](src/core/interfaces/IViews.sol/interface.IViews.md) - [❱ libraries](src/core/libraries/README.md) - [SSVCoreModules](src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md) - [CoreStorageLib](src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md) diff --git a/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md b/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md index 879f1405..57b5d4f6 100644 --- a/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md +++ b/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md @@ -1,8 +1,8 @@ # SSVBasedApps -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/SSVBasedApps.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/SSVBasedApps.sol) **Inherits:** -[ISSVBasedApps](/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md), UUPSUpgradeable, Ownable2StepUpgradeable, [IBasedAppManager](/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md), [IStrategyManager](/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md), [IProtocolManager](/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md) +[ISSVBasedApps](/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md), UUPSUpgradeable, Ownable2StepUpgradeable **Author:** @@ -91,7 +91,7 @@ function updateBAppMetadataURI(string calldata metadataURI) external; ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external; +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; ``` ### updateBAppsTokens @@ -329,7 +329,7 @@ function updateMaxFeeIncrement(uint32 value) external onlyOwner; ```solidity -function updateDisabledFeatures(uint32 disabledFeatures) external onlyOwner; +function updateDisabledFeatures(uint32 value) external onlyOwner; ``` ### delegations diff --git a/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md b/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md index 0f5a03d7..4fb15889 100644 --- a/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md +++ b/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md @@ -1,5 +1,5 @@ # IBasedAppManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/IBasedAppManager.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IBasedAppManager.sol) ## Functions @@ -7,7 +7,7 @@ ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external; +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; ``` ### updateBAppMetadataURI @@ -34,7 +34,7 @@ event BAppMetadataURIUpdated(address indexed bApp, string metadataURI); ### BAppRegistered ```solidity -event BAppRegistered(address indexed bApp, address[] tokens, uint32[] sharedRiskLevel, string metadataURI); +event BAppRegistered(address indexed bApp, ICore.TokenConfig[] tokenConfigs, string metadataURI); ``` ### BAppTokensUpdated diff --git a/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md b/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md index c14f6948..24ae6f6e 100644 --- a/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md +++ b/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md @@ -1,5 +1,5 @@ # ICore -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/ICore.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/ICore.sol) ## Structs diff --git a/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md b/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md index 4672c707..bb7fa5b1 100644 --- a/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md +++ b/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md @@ -1,5 +1,5 @@ # IProtocolManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/IProtocolManager.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IProtocolManager.sol) ## Functions diff --git a/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md b/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md index cdffb5cb..ca7e186e 100644 --- a/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md +++ b/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md @@ -1,5 +1,8 @@ # ISSVBasedApps -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/ISSVBasedApps.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/ISSVBasedApps.sol) + +**Inherits:** +[IStrategyManager](/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md), [IBasedAppManager](/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md), [IProtocolManager](/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md), [IViews](/src/core/interfaces/IViews.sol/interface.IViews.md) ## Functions @@ -10,13 +13,6 @@ function getModuleAddress(SSVCoreModules moduleId) external view returns (address); ``` -### getVersion - - -```solidity -function getVersion() external pure returns (string memory version); -``` - ### initialize @@ -51,6 +47,60 @@ event ModuleUpdated(SSVCoreModules indexed moduleId, address moduleAddress); error InvalidMaxFeeIncrement(); ``` +### InvalidMaxShares + +```solidity +error InvalidMaxShares(); +``` + +### InvalidFeeTimelockPeriod + +```solidity +error InvalidFeeTimelockPeriod(); +``` + +### InvalidFeeExpireTime + +```solidity +error InvalidFeeExpireTime(); +``` + +### InvalidWithdrawalTimelockPeriod + +```solidity +error InvalidWithdrawalTimelockPeriod(); +``` + +### InvalidWithdrawalExpireTime + +```solidity +error InvalidWithdrawalExpireTime(); +``` + +### InvalidObligationTimelockPeriod + +```solidity +error InvalidObligationTimelockPeriod(); +``` + +### InvalidObligationExpireTime + +```solidity +error InvalidObligationExpireTime(); +``` + +### InvalidTokenUpdateTimelockPeriod + +```solidity +error InvalidTokenUpdateTimelockPeriod(); +``` + +### InvalidDisabledFeatures + +```solidity +error InvalidDisabledFeatures(); +``` + ### TargetModuleDoesNotExist ```solidity diff --git a/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md b/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md index 27fcf9dc..e77b31e8 100644 --- a/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md +++ b/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md @@ -1,5 +1,5 @@ # IStrategyManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/IStrategyManager.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IStrategyManager.sol) ## Functions diff --git a/docs/src/src/core/interfaces/IViews.sol/interface.IViews.md b/docs/src/src/core/interfaces/IViews.sol/interface.IViews.md new file mode 100644 index 00000000..a50a20aa --- /dev/null +++ b/docs/src/src/core/interfaces/IViews.sol/interface.IViews.md @@ -0,0 +1,208 @@ +# IViews +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IViews.sol) + + +## Functions +### delegations + + +```solidity +function delegations(address account, address receiver) external view returns (uint32); +``` + +### totalDelegatedPercentage + + +```solidity +function totalDelegatedPercentage(address delegator) external view returns (uint32); +``` + +### registeredBApps + + +```solidity +function registeredBApps(address bApp) external view returns (bool isRegistered); +``` + +### strategies + + +```solidity +function strategies(uint32 strategyId) external view returns (address strategyOwner, uint32 fee); +``` + +### ownedStrategies + + +```solidity +function ownedStrategies(address owner) external view returns (uint32[] memory strategyIds); +``` + +### strategyAccountShares + + +```solidity +function strategyAccountShares(uint32 strategyId, address account, address token) external view returns (uint256); +``` + +### strategyTotalBalance + + +```solidity +function strategyTotalBalance(uint32 strategyId, address token) external view returns (uint256); +``` + +### strategyTotalShares + + +```solidity +function strategyTotalShares(uint32 strategyId, address token) external view returns (uint256); +``` + +### strategyGeneration + + +```solidity +function strategyGeneration(uint32 strategyId, address token) external view returns (uint256); +``` + +### obligations + + +```solidity +function obligations(uint32 strategyId, address bApp, address token) external view returns (uint32 percentage, bool isSet); +``` + +### bAppTokens + + +```solidity +function bAppTokens(address bApp, address token) external view returns (uint32 currentValue, bool isSet, uint32 pendingValue, uint32 effectTime); +``` + +### accountBAppStrategy + + +```solidity +function accountBAppStrategy(address account, address bApp) external view returns (uint32); +``` + +### feeUpdateRequests + + +```solidity +function feeUpdateRequests(uint32 strategyId) external view returns (uint32 percentage, uint32 requestTime); +``` + +### withdrawalRequests + + +```solidity +function withdrawalRequests(uint32 strategyId, address account, address token) external view returns (uint256 shares, uint32 requestTime); +``` + +### obligationRequests + + +```solidity +function obligationRequests(uint32 strategyId, address token, address bApp) external view returns (uint32 percentage, uint32 requestTime); +``` + +### slashingFund + + +```solidity +function slashingFund(address account, address token) external view returns (uint256); +``` + +### maxPercentage + + +```solidity +function maxPercentage() external pure returns (uint32); +``` + +### ethAddress + + +```solidity +function ethAddress() external pure returns (address); +``` + +### maxShares + + +```solidity +function maxShares() external view returns (uint256); +``` + +### maxFeeIncrement + + +```solidity +function maxFeeIncrement() external view returns (uint32); +``` + +### feeTimelockPeriod + + +```solidity +function feeTimelockPeriod() external view returns (uint32); +``` + +### feeExpireTime + + +```solidity +function feeExpireTime() external view returns (uint32); +``` + +### withdrawalTimelockPeriod + + +```solidity +function withdrawalTimelockPeriod() external view returns (uint32); +``` + +### withdrawalExpireTime + + +```solidity +function withdrawalExpireTime() external view returns (uint32); +``` + +### obligationTimelockPeriod + + +```solidity +function obligationTimelockPeriod() external view returns (uint32); +``` + +### obligationExpireTime + + +```solidity +function obligationExpireTime() external view returns (uint32); +``` + +### disabledFeatures + + +```solidity +function disabledFeatures() external view returns (uint32); +``` + +### tokenUpdateTimelockPeriod + + +```solidity +function tokenUpdateTimelockPeriod() external view returns (uint32); +``` + +### getVersion + + +```solidity +function getVersion() external pure returns (string memory); +``` + diff --git a/docs/src/src/core/interfaces/README.md b/docs/src/src/core/interfaces/README.md index 6997ead1..0593724a 100644 --- a/docs/src/src/core/interfaces/README.md +++ b/docs/src/src/core/interfaces/README.md @@ -6,3 +6,4 @@ - [IProtocolManager](IProtocolManager.sol/interface.IProtocolManager.md) - [ISSVBasedApps](ISSVBasedApps.sol/interface.ISSVBasedApps.md) - [IStrategyManager](IStrategyManager.sol/interface.IStrategyManager.md) +- [IViews](IViews.sol/interface.IViews.md) diff --git a/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md b/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md index b20a0c76..68a026db 100644 --- a/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md +++ b/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md @@ -1,5 +1,5 @@ # SSVCoreModules -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/libraries/CoreStorageLib.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/CoreStorageLib.sol) ```solidity diff --git a/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md b/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md index aba2eba0..cfb1ab50 100644 --- a/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md +++ b/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md @@ -1,5 +1,5 @@ # CoreStorageLib -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/libraries/CoreStorageLib.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/CoreStorageLib.sol) ## State Variables diff --git a/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md b/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md index aa331895..bce8a21b 100644 --- a/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md +++ b/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md @@ -1,5 +1,5 @@ # ProtocolStorageLib -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/libraries/ProtocolStorageLib.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/ProtocolStorageLib.sol) ## State Variables diff --git a/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md b/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md index 08c55976..a39eaf59 100644 --- a/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md +++ b/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md @@ -1,5 +1,17 @@ # Constants -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/libraries/ValidationLib.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/ValidationLib.sol) + +### MIN_TIME_LOCK_PERIOD + +```solidity +uint32 constant MIN_TIME_LOCK_PERIOD = 1 days; +``` + +### MIN_EXPIRE_TIME + +```solidity +uint32 constant MIN_EXPIRE_TIME = 1 hours; +``` ### MAX_PERCENTAGE diff --git a/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md b/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md index b62afb71..0440a706 100644 --- a/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md +++ b/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md @@ -1,5 +1,5 @@ # ValidationLib -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/libraries/ValidationLib.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/ValidationLib.sol) ## Functions diff --git a/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md b/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md index 84eb2c0a..e31ce82a 100644 --- a/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md +++ b/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md @@ -1,5 +1,5 @@ # BasedAppsManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/modules/BasedAppsManager.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/modules/BasedAppsManager.sol) **Inherits:** [IBasedAppManager](/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md) @@ -23,14 +23,13 @@ Registers a bApp. ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external; +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`tokens`|`address[]`|The list of tokens the bApp accepts; can be empty.| -|`sharedRiskLevels`|`uint32[]`|The shared risk level of the bApp.| +|`tokenConfigs`|`ICore.TokenConfig[]`|The list of tokens configs the bApp accepts; can be empty.| |`metadataURI`|`string`|The metadata URI of the bApp, which is a link (e.g., http://example.com) to a JSON file containing metadata such as the name, description, logo, etc.| @@ -62,14 +61,13 @@ Function to add tokens to a bApp ```solidity -function _addNewTokens(address bApp, address[] calldata tokens, uint32[] calldata sharedRiskLevels) internal; +function _addNewTokens(address bApp, ICore.TokenConfig[] calldata tokenConfigs) internal; ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`bApp`|`address`|The address of the bApp| -|`tokens`|`address[]`|The list of tokens to add| -|`sharedRiskLevels`|`uint32[]`|The shared risk levels of the tokens| +|`tokenConfigs`|`ICore.TokenConfig[]`|The list of tokens to add| diff --git a/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md b/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md index 605925a0..8d080770 100644 --- a/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md +++ b/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md @@ -1,5 +1,5 @@ # ProtocolManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/modules/ProtocolManager.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/modules/ProtocolManager.sol) **Inherits:** [IProtocolManager](/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md) diff --git a/docs/src/src/core/modules/StrategyManager.sol/contract.StrategyManager.md b/docs/src/src/core/modules/StrategyManager.sol/contract.StrategyManager.md index b8697d3c..15c37bd1 100644 --- a/docs/src/src/core/modules/StrategyManager.sol/contract.StrategyManager.md +++ b/docs/src/src/core/modules/StrategyManager.sol/contract.StrategyManager.md @@ -1,5 +1,5 @@ # StrategyManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/modules/StrategyManager.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/modules/StrategyManager.sol) **Inherits:** ReentrancyGuardTransient, [IStrategyManager](/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md) @@ -162,13 +162,13 @@ function optInToBApp(uint32 strategyId, address bApp, address[] calldata tokens, |`data`|`bytes`|Optional parameter that could be required by the service| -### _isBApp +### _isContract -Function to check if an address uses the correct bApp interface +Function to check if an address is a contract ```solidity -function _isBApp(address bApp) private view returns (bool); +function _isContract(address bApp) private view returns (bool); ``` **Parameters** @@ -180,7 +180,7 @@ function _isBApp(address bApp) private view returns (bool); |Name|Type|Description| |----|----|-----------| -|``|`bool`|True if the address uses the correct bApp interface| +|``|`bool`|True if the address is a contract| ### depositERC20 @@ -501,6 +501,13 @@ function getSlashableBalance(CoreStorageLib.Data storage s, uint32 strategyId, a |`slashableBalance`|`uint256`|The slashable balance| +### _checkStrategyOptedIn + + +```solidity +function _checkStrategyOptedIn(CoreStorageLib.Data storage s, uint32 strategyId, address bApp) internal view; +``` + ### slash Slash a strategy @@ -538,7 +545,7 @@ function _adjustObligation( address token, uint256 amount, ICore.Shares storage strategyTokenShares -) internal; +) internal returns (uint32 obligationPercentage); ``` ### withdrawSlashingFund diff --git a/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md b/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md index 4df60b4f..e1a56051 100644 --- a/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md +++ b/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md @@ -1,5 +1,5 @@ # WhitelistExample -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/examples/WhitelistExample.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/examples/WhitelistExample.sol) **Inherits:** [OwnableBasedApp](/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md), [BasedAppWhitelisted](/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md) diff --git a/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md b/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md index 0573fdac..8ad4607b 100644 --- a/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md +++ b/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md @@ -1,8 +1,5 @@ # IBasedApp -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/interfaces/IBasedApp.sol) - -**Inherits:** -IERC165 +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/interfaces/IBasedApp.sol) ## Functions @@ -17,7 +14,7 @@ function optInToBApp(uint32 strategyId, address[] calldata tokens, uint32[] call ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external; +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; ``` ### slash @@ -29,13 +26,6 @@ function slash(uint32 strategyId, address token, uint32 percentage, address send returns (bool success, address receiver, bool exit); ``` -### supportsInterface - - -```solidity -function supportsInterface(bytes4 interfaceId) external view returns (bool); -``` - ### updateBAppMetadataURI diff --git a/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md b/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md index 0c9bda76..5db5cac9 100644 --- a/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md +++ b/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md @@ -1,5 +1,5 @@ # IBasedAppWhitelisted -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/interfaces/IBasedAppWhitelisted.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/interfaces/IBasedAppWhitelisted.sol) ## Functions diff --git a/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md b/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md index d373b492..4a1798b8 100644 --- a/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md +++ b/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md @@ -1,5 +1,5 @@ # BasedAppWhitelisted -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/modules/BasedAppWhitelisted.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/modules/BasedAppWhitelisted.sol) **Inherits:** [IBasedAppWhitelisted](/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md) diff --git a/docs/src/src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md b/docs/src/src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md index 10043154..9103c220 100644 --- a/docs/src/src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md +++ b/docs/src/src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md @@ -1,5 +1,5 @@ # AccessControlBasedApp -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/modules/core+roles/AccessControlBasedApp.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/modules/core+roles/AccessControlBasedApp.sol) **Inherits:** [BasedAppCore](/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md), AccessControl @@ -57,14 +57,13 @@ Registers a BApp calling the SSV SSVBasedApps ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external override onlyRole(MANAGER_ROLE); +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external override onlyRole(MANAGER_ROLE); ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`tokens`|`address[]`|array of token addresses| -|`sharedRiskLevels`|`uint32[]`|array of shared risk levels| +|`tokenConfigs`|`ICore.TokenConfig[]`|array of token addresses and shared risk levels| |`metadataURI`|`string`|URI of the metadata| @@ -83,24 +82,3 @@ function updateBAppMetadataURI(string calldata metadataURI) external override on |`metadataURI`|`string`|new metadata URI| -### supportsInterface - -Checks if the contract supports the interface - - -```solidity -function supportsInterface(bytes4 interfaceId) public pure override(AccessControl, BasedAppCore) returns (bool isSupported); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`interfaceId`|`bytes4`|interface id| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|`isSupported`|`bool`|if the contract supports the interface| - - diff --git a/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md b/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md index 45ec53a6..fa6eec5b 100644 --- a/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md +++ b/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md @@ -1,5 +1,5 @@ # OwnableBasedApp -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/modules/core+roles/OwnableBasedApp.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/modules/core+roles/OwnableBasedApp.sol) **Inherits:** Ownable, [BasedAppCore](/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md) @@ -28,14 +28,13 @@ Registers a BApp calling the SSV SSVBasedApps ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external override onlyOwner; +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external override onlyOwner; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`tokens`|`address[]`|array of token addresses| -|`sharedRiskLevels`|`uint32[]`|array of shared risk levels| +|`tokenConfigs`|`ICore.TokenConfig[]`|array of token addresses and shared risk levels| |`metadataURI`|`string`|URI of the metadata| @@ -54,24 +53,3 @@ function updateBAppMetadataURI(string calldata metadataURI) external override on |`metadataURI`|`string`|new metadata URI| -### supportsInterface - -Checks if the contract supports the interface - - -```solidity -function supportsInterface(bytes4 interfaceId) public pure override(BasedAppCore) returns (bool isSupported); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`interfaceId`|`bytes4`|interface id| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|`isSupported`|`bool`|if the contract supports the interface| - - diff --git a/docs/src/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md b/docs/src/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md index 6dae71ec..17d90255 100644 --- a/docs/src/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md +++ b/docs/src/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md @@ -1,5 +1,5 @@ # BasedAppCore -[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/modules/core/BasedAppCore.sol) +[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/modules/core/BasedAppCore.sol) **Inherits:** [IBasedApp](/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md) @@ -43,7 +43,7 @@ constructor(address _ssvBasedAppsNetwork); ### registerBApp -Registers a BApp calling the SSV SSVBasedApps +Registers a BApp calling the SSVBasedApps *metadata should point to a json that respect template: { @@ -56,14 +56,13 @@ Registers a BApp calling the SSV SSVBasedApps ```solidity -function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external virtual; +function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external virtual; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`tokens`|`address[]`|array of token addresses| -|`sharedRiskLevels`|`uint32[]`|array of shared risk levels| +|`tokenConfigs`|`ICore.TokenConfig[]`|array of token configs (address, shared risk level)| |`metadataURI`|`string`|URI of the metadata| @@ -131,9 +130,7 @@ function optInToBApp(uint32, address[] calldata, uint32[] calldata, bytes callda function slash(uint32, address, uint32, address, bytes calldata) external virtual onlySSVBasedAppManager returns (bool, address, bool); ``` -### supportsInterface - -Checks if the contract supports the interface +### receive *--- CORE LOGIC (TO BE IMPLEMENTED) ---* @@ -142,25 +139,6 @@ Checks if the contract supports the interface *--- RETURN RECEIVER ADDRESS FOR THE SLASHED FUNDS ---* -```solidity -function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`interfaceId`|`bytes4`|interface id| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`bool`|true if the contract supports the interface| - - -### receive - - ```solidity receive() external payable virtual; ``` diff --git a/foundry.toml b/foundry.toml index 4ccf72d1..18ce4381 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc = "0.8.29" +solc = "0.8.30" src = "src" out = "out" libs = ["lib"] diff --git a/package-lock.json b/package-lock.json index 2dab8159..fdc49a07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "based-applications", - "version": "0.1.1", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "based-applications", - "version": "0.1.1", + "version": "0.2.0", "license": "GPL-3.0", "devDependencies": { "@openzeppelin/contracts-upgradeable": "5.3.0", @@ -189,26 +189,26 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -318,17 +318,13 @@ } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -367,103 +363,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -492,13 +391,13 @@ "license": "MIT" }, "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", + "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/config-chain": { @@ -607,9 +506,9 @@ } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -899,13 +798,16 @@ "license": "MIT" }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-number": { @@ -1033,29 +935,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/commander": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", - "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, "node_modules/listr2": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", @@ -1108,32 +987,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", @@ -1167,22 +1020,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -1609,9 +1446,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -1635,18 +1472,17 @@ } }, "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" @@ -1685,6 +1521,59 @@ "prettier": "^2.8.3" } }, + "node_modules/solhint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/solhint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/solhint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/solhint/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/solhint/node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", @@ -1702,6 +1591,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/solhint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -1713,31 +1615,37 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-json-comments": { @@ -1797,6 +1705,49 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -1804,6 +1755,52 @@ "dev": true, "license": "MIT" }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -1852,73 +1849,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 3c19fd07..7ca0f3be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "based-applications", - "version": "0.1.1", + "version": "0.2.0", "description": "SSV Based Applications", "author": "SSV.Network", "repository": { @@ -20,9 +20,14 @@ "scripts": { "compile": "forge compile", "build": "npm run lint:fix && forge clean && forge build", + "deploy:sepolia": "source .env && forge script script/DeployAllSepolia.s.sol --rpc-url $SEPOLIA_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", "deploy:hoodi-stage": "source .env && forge script script/DeployAllHoodi.s.sol false --sig 'run(bool)' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", "deploy:hoodi-prod": "source .env && forge script script/DeployAllHoodi.s.sol true --sig 'run(bool)' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", "deploy:mainnet": "source .env && forge script script/DeployAllMainnet.s.sol --rpc-url $MAINNET_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", + "update-modules:hoodi-stage":"source .env && forge script script/UpdateModulesHoodi.s.sol false [0,1,2] --sig 'run(bool,uint8[])' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", + "update-modules:hoodi-prod":"source .env && forge script script/UpdateModulesHoodi.s.sol true [0,1,2] --sig 'run(bool,uint8[])' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", + "update-implementation:hoodi-stage":"source .env && forge script script/UpdateNewImplHoodi.s.sol false --sig 'run(bool)' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", + "update-implementation:hoodi-prod":"source .env && forge script script/UpdateNewImplHoodi.s.sol true --sig 'run(bool)' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", "gas-report": "forge test --gas-report", "generate-docs": "solc --include-path node_modules --base-path . --combined-json userdoc,devdoc src/SSVBasedApps.sol --output-dir ./docs --overwrite", "update-modules:hoodi-stage":"source .env && forge script script/UpdateModulesHoodi.s.sol false [2] --sig 'run(bool,uint8[])' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", diff --git a/script/DeployAll.sol b/script/DeployAll.sol index 4856923a..c9690e3c 100644 --- a/script/DeployAll.sol +++ b/script/DeployAll.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { ERC1967Proxy @@ -16,7 +16,7 @@ import { ProtocolStorageLib } from "src/core/libraries/ProtocolStorageLib.sol"; contract DeployAll is Script { using stdJson for string; - function _deployAll(string memory raw) internal returns (string memory) { + function _deployAll(string memory json) internal returns (string memory) { vm.startBroadcast(); SSVBasedApps impl = new SSVBasedApps(); @@ -29,7 +29,7 @@ contract DeployAll is Script { strategyMod, bAppsMod, protocolMod, - raw + json ); vm.stopBroadcast(); @@ -40,7 +40,8 @@ contract DeployAll is Script { console.log("ProtocolModule: ", address(protocolMod)); console.log("SSVBasedApps Proxy: ", address(proxy)); - return saveToJson(impl, proxy, strategyMod, bAppsMod, protocolMod, raw); + return + saveToJson(impl, proxy, strategyMod, bAppsMod, protocolMod, json); } function saveToJson( @@ -49,7 +50,7 @@ contract DeployAll is Script { StrategyManager strategyMod, BasedAppsManager bAppsMod, ProtocolManager protocolMod, - string memory raw + string memory json ) internal returns (string memory) { string memory parent = "parent"; @@ -85,48 +86,48 @@ contract DeployAll is Script { vm.serializeUint( parameters, "feeTimelockPeriod", - raw.readUint(".feeTimelockPeriod") + json.readUint(".feeTimelockPeriod") ); vm.serializeUint( parameters, "feeExpireTime", - raw.readUint(".feeExpireTime") + json.readUint(".feeExpireTime") ); vm.serializeUint( parameters, "withdrawalTimelockPeriod", - raw.readUint(".withdrawalTimelockPeriod") + json.readUint(".withdrawalTimelockPeriod") ); vm.serializeUint( parameters, "withdrawalExpireTime", - raw.readUint(".withdrawalExpireTime") + json.readUint(".withdrawalExpireTime") ); vm.serializeUint( parameters, "obligationTimelockPeriod", - raw.readUint(".obligationTimelockPeriod") + json.readUint(".obligationTimelockPeriod") ); vm.serializeUint( parameters, "obligationExpireTime", - raw.readUint(".obligationExpireTime") + json.readUint(".obligationExpireTime") ); vm.serializeUint( parameters, "tokenUpdateTimelockPeriod", - raw.readUint(".tokenUpdateTimelockPeriod") + json.readUint(".tokenUpdateTimelockPeriod") ); - vm.serializeUint(parameters, "maxShares", raw.readUint(".maxShares")); + vm.serializeUint(parameters, "maxShares", json.readUint(".maxShares")); vm.serializeUint( parameters, "maxFeeIncrement", - raw.readUint(".maxFeeIncrement") + json.readUint(".maxFeeIncrement") ); string memory parameters_output = vm.serializeUint( parameters, "disabledFeatures", - raw.readUint(".disabledFeatures") + json.readUint(".disabledFeatures") ); string memory chain_info = "chainInfo"; @@ -152,7 +153,7 @@ contract DeployAll is Script { StrategyManager strategyMod, BasedAppsManager bAppsMod, ProtocolManager protocolMod, - string memory raw + string memory json ) internal returns (ERC1967Proxy proxy) { return new ERC1967Proxy( @@ -165,30 +166,30 @@ contract DeployAll is Script { address(protocolMod), ProtocolStorageLib.Data({ feeTimelockPeriod: uint32( - raw.readUint(".feeTimelockPeriod") + json.readUint(".feeTimelockPeriod") ), - feeExpireTime: uint32(raw.readUint(".feeExpireTime")), + feeExpireTime: uint32(json.readUint(".feeExpireTime")), withdrawalTimelockPeriod: uint32( - raw.readUint(".withdrawalTimelockPeriod") + json.readUint(".withdrawalTimelockPeriod") ), withdrawalExpireTime: uint32( - raw.readUint(".withdrawalExpireTime") + json.readUint(".withdrawalExpireTime") ), obligationTimelockPeriod: uint32( - raw.readUint(".obligationTimelockPeriod") + json.readUint(".obligationTimelockPeriod") ), obligationExpireTime: uint32( - raw.readUint(".obligationExpireTime") + json.readUint(".obligationExpireTime") ), tokenUpdateTimelockPeriod: uint32( - raw.readUint(".tokenUpdateTimelockPeriod") + json.readUint(".tokenUpdateTimelockPeriod") ), - maxShares: raw.readUint(".maxShares"), + maxShares: json.readUint(".maxShares"), maxFeeIncrement: uint32( - raw.readUint(".maxFeeIncrement") + json.readUint(".maxFeeIncrement") ), disabledFeatures: uint32( - raw.readUint(".disabledFeatures") + json.readUint(".disabledFeatures") ) }) ) diff --git a/script/DeployAllHoodi.s.sol b/script/DeployAllHoodi.s.sol index 95432c9a..f83e9454 100644 --- a/script/DeployAllHoodi.s.sol +++ b/script/DeployAllHoodi.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Script } from "forge-std/Script.sol"; import { DeployAll } from "./DeployAll.sol"; @@ -7,7 +7,7 @@ import { DeployAll } from "./DeployAll.sol"; contract DeployAllHoodi is Script, DeployAll { function run(bool isProd) external { if (block.chainid != 560_048) { - revert("This script is only for the Hoodi prod"); + revert("This script is only for the Hoodi"); } string memory cfgPath; diff --git a/script/DeployAllMainnet.s.sol b/script/DeployAllMainnet.s.sol index 4da956cc..5c730021 100644 --- a/script/DeployAllMainnet.s.sol +++ b/script/DeployAllMainnet.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Script, console } from "forge-std/Script.sol"; import { DeployAll } from "./DeployAll.sol"; diff --git a/script/DeployAllSepolia.s.sol b/script/DeployAllSepolia.s.sol new file mode 100644 index 00000000..55a70afc --- /dev/null +++ b/script/DeployAllSepolia.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +import { Script, console } from "forge-std/Script.sol"; +import { DeployAll } from "./DeployAll.sol"; + +contract DeployAllHoodi is Script, DeployAll { + function run() external { + if (block.chainid != 11155111) { + revert("This script is only for Sepolia"); + } + + string memory finalJson = _deployAll( + vm.readFile("script/config/sepolia.json") + ); + + vm.writeJson(finalJson, "artifacts/deploy-sepolia.json"); + } +} diff --git a/script/UpdateModules.s.sol b/script/UpdateModules.s.sol index 57c2874c..c9492830 100644 --- a/script/UpdateModules.s.sol +++ b/script/UpdateModules.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Script, console } from "forge-std/Script.sol"; import { stdJson } from "forge-std/StdJson.sol"; diff --git a/script/UpdateModulesHoodi.s.sol b/script/UpdateModulesHoodi.s.sol index ee4a8712..b95100a2 100644 --- a/script/UpdateModulesHoodi.s.sol +++ b/script/UpdateModulesHoodi.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Script } from "forge-std/Script.sol"; diff --git a/script/UpdateNewImpl.s.sol b/script/UpdateNewImpl.s.sol index 963d6c55..f78a354a 100644 --- a/script/UpdateNewImpl.s.sol +++ b/script/UpdateNewImpl.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Script, console } from "forge-std/Script.sol"; import { stdJson } from "forge-std/StdJson.sol"; diff --git a/script/UpdateNewImplHoodi.s.sol b/script/UpdateNewImplHoodi.s.sol index 50fc219c..c45c33bf 100644 --- a/script/UpdateNewImplHoodi.s.sol +++ b/script/UpdateNewImplHoodi.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Script } from "forge-std/Script.sol"; import { stdJson } from "forge-std/StdJson.sol"; diff --git a/script/config/sepolia.json b/script/config/sepolia.json new file mode 100644 index 00000000..b7dc49ca --- /dev/null +++ b/script/config/sepolia.json @@ -0,0 +1,12 @@ +{ + "feeTimelockPeriod": 300, + "feeExpireTime": 3600, + "withdrawalTimelockPeriod": 300, + "withdrawalExpireTime": 3600, + "obligationTimelockPeriod": 300, + "obligationExpireTime": 3600, + "tokenUpdateTimelockPeriod": 300, + "maxShares": "1e50", + "maxFeeIncrement": 500, + "disabledFeatures": 0 +} diff --git a/src/core/SSVBasedApps.sol b/src/core/SSVBasedApps.sol index e214cac4..550e34c3 100644 --- a/src/core/SSVBasedApps.sol +++ b/src/core/SSVBasedApps.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { @@ -10,6 +10,8 @@ import { } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { + MIN_EXPIRE_TIME, + MIN_TIME_LOCK_PERIOD, MAX_PERCENTAGE, ETH_ADDRESS } from "@ssv/src/core/libraries/ValidationLib.sol"; @@ -77,10 +79,7 @@ import { contract SSVBasedApps is ISSVBasedApps, UUPSUpgradeable, - Ownable2StepUpgradeable, - IBasedAppManager, - IStrategyManager, - IProtocolManager + Ownable2StepUpgradeable { // *************************** // ** Section: Initializers ** @@ -121,10 +120,49 @@ contract SSVBasedApps is protocolManager_ ); - if (config.maxFeeIncrement == 0 || config.maxFeeIncrement > 10_000) { + if ( + config.maxFeeIncrement == 0 || + config.maxFeeIncrement > MAX_PERCENTAGE + ) { revert InvalidMaxFeeIncrement(); } + if (config.maxShares == 0 || config.maxShares < 1e50) { + revert InvalidMaxShares(); + } + + if (config.feeTimelockPeriod < MIN_TIME_LOCK_PERIOD) { + revert InvalidFeeTimelockPeriod(); + } + + if (config.feeExpireTime < MIN_EXPIRE_TIME) { + revert InvalidFeeExpireTime(); + } + + if (config.withdrawalTimelockPeriod < MIN_TIME_LOCK_PERIOD) { + revert InvalidWithdrawalTimelockPeriod(); + } + + if (config.withdrawalExpireTime < MIN_EXPIRE_TIME) { + revert InvalidWithdrawalExpireTime(); + } + + if (config.obligationTimelockPeriod < MIN_TIME_LOCK_PERIOD) { + revert InvalidObligationTimelockPeriod(); + } + + if (config.obligationExpireTime < MIN_EXPIRE_TIME) { + revert InvalidObligationExpireTime(); + } + + if (config.tokenUpdateTimelockPeriod < MIN_EXPIRE_TIME) { + revert InvalidTokenUpdateTimelockPeriod(); + } + + if (config.disabledFeatures > 3) { + revert InvalidDisabledFeatures(); + } + sp.maxFeeIncrement = config.maxFeeIncrement; sp.feeTimelockPeriod = config.feeTimelockPeriod; sp.feeExpireTime = config.feeExpireTime; @@ -160,8 +198,7 @@ contract SSVBasedApps is } function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external { _delegateTo(SSVCoreModules.SSV_BAPPS_MANAGER); @@ -586,7 +623,7 @@ contract SSVBasedApps is } function getVersion() external pure returns (string memory) { - return "0.1.1"; + return "0.2.0"; } // ********************************* diff --git a/src/core/interfaces/IBasedAppManager.sol b/src/core/interfaces/IBasedAppManager.sol index 57e94954..11e9de48 100644 --- a/src/core/interfaces/IBasedAppManager.sol +++ b/src/core/interfaces/IBasedAppManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; @@ -7,8 +7,7 @@ interface IBasedAppManager { event BAppMetadataURIUpdated(address indexed bApp, string metadataURI); event BAppRegistered( address indexed bApp, - address[] tokens, - uint32[] sharedRiskLevel, + ICore.TokenConfig[] tokenConfigs, string metadataURI ); event BAppTokensUpdated( @@ -17,8 +16,7 @@ interface IBasedAppManager { ); function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external; function updateBAppMetadataURI(string calldata metadataURI) external; diff --git a/src/core/interfaces/ICore.sol b/src/core/interfaces/ICore.sol index 645306b9..02f642df 100644 --- a/src/core/interfaces/ICore.sol +++ b/src/core/interfaces/ICore.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; interface ICore { /// @notice Represents a SharedRiskLevel diff --git a/src/core/interfaces/IProtocolManager.sol b/src/core/interfaces/IProtocolManager.sol index 65801c25..6f04b17c 100644 --- a/src/core/interfaces/IProtocolManager.sol +++ b/src/core/interfaces/IProtocolManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; interface IProtocolManager { event FeeExpireTimeUpdated(uint32 feeExpireTime); diff --git a/src/core/interfaces/ISSVBasedApps.sol b/src/core/interfaces/ISSVBasedApps.sol index 11847720..bc67c424 100644 --- a/src/core/interfaces/ISSVBasedApps.sol +++ b/src/core/interfaces/ISSVBasedApps.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IStrategyManager @@ -10,18 +10,23 @@ import { import { IProtocolManager } from "@ssv/src/core/interfaces/IProtocolManager.sol"; +import { IViews } from "@ssv/src/core/interfaces/IViews.sol"; import { SSVCoreModules } from "@ssv/src/core/libraries/CoreStorageLib.sol"; import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; -interface ISSVBasedApps { +interface ISSVBasedApps is + IStrategyManager, + IBasedAppManager, + IProtocolManager, + IViews +{ event ModuleUpdated(SSVCoreModules indexed moduleId, address moduleAddress); function getModuleAddress( SSVCoreModules moduleId ) external view returns (address); - function getVersion() external pure returns (string memory version); function initialize( address owner_, IBasedAppManager ssvBasedAppManger_, @@ -35,5 +40,14 @@ interface ISSVBasedApps { ) external; error InvalidMaxFeeIncrement(); + error InvalidMaxShares(); + error InvalidFeeTimelockPeriod(); + error InvalidFeeExpireTime(); + error InvalidWithdrawalTimelockPeriod(); + error InvalidWithdrawalExpireTime(); + error InvalidObligationTimelockPeriod(); + error InvalidObligationExpireTime(); + error InvalidTokenUpdateTimelockPeriod(); + error InvalidDisabledFeatures(); error TargetModuleDoesNotExist(uint8 moduleId); } diff --git a/src/core/interfaces/IStrategyManager.sol b/src/core/interfaces/IStrategyManager.sol index f2506d2e..332d8d01 100644 --- a/src/core/interfaces/IStrategyManager.sol +++ b/src/core/interfaces/IStrategyManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/core/interfaces/IViews.sol b/src/core/interfaces/IViews.sol new file mode 100644 index 00000000..e8331199 --- /dev/null +++ b/src/core/interfaces/IViews.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +interface IViews { + function delegations( + address account, + address receiver + ) external view returns (uint32); + function totalDelegatedPercentage( + address delegator + ) external view returns (uint32); + function registeredBApps( + address bApp + ) external view returns (bool isRegistered); + function strategies( + uint32 strategyId + ) external view returns (address strategyOwner, uint32 fee); + function ownedStrategies( + address owner + ) external view returns (uint32[] memory strategyIds); + function strategyAccountShares( + uint32 strategyId, + address account, + address token + ) external view returns (uint256); + function strategyTotalBalance( + uint32 strategyId, + address token + ) external view returns (uint256); + function strategyTotalShares( + uint32 strategyId, + address token + ) external view returns (uint256); + function strategyGeneration( + uint32 strategyId, + address token + ) external view returns (uint256); + function obligations( + uint32 strategyId, + address bApp, + address token + ) external view returns (uint32 percentage, bool isSet); + function bAppTokens( + address bApp, + address token + ) + external + view + returns ( + uint32 currentValue, + bool isSet, + uint32 pendingValue, + uint32 effectTime + ); + function accountBAppStrategy( + address account, + address bApp + ) external view returns (uint32); + function feeUpdateRequests( + uint32 strategyId + ) external view returns (uint32 percentage, uint32 requestTime); + function withdrawalRequests( + uint32 strategyId, + address account, + address token + ) external view returns (uint256 shares, uint32 requestTime); + function obligationRequests( + uint32 strategyId, + address token, + address bApp + ) external view returns (uint32 percentage, uint32 requestTime); + function slashingFund( + address account, + address token + ) external view returns (uint256); + + // External Protocol Views + function maxPercentage() external pure returns (uint32); + function ethAddress() external pure returns (address); + function maxShares() external view returns (uint256); + function maxFeeIncrement() external view returns (uint32); + function feeTimelockPeriod() external view returns (uint32); + function feeExpireTime() external view returns (uint32); + function withdrawalTimelockPeriod() external view returns (uint32); + function withdrawalExpireTime() external view returns (uint32); + function obligationTimelockPeriod() external view returns (uint32); + function obligationExpireTime() external view returns (uint32); + function disabledFeatures() external view returns (uint32); + function tokenUpdateTimelockPeriod() external view returns (uint32); + function getVersion() external pure returns (string memory); +} diff --git a/src/core/libraries/CoreStorageLib.sol b/src/core/libraries/CoreStorageLib.sol index c130ce43..0d24e68c 100644 --- a/src/core/libraries/CoreStorageLib.sol +++ b/src/core/libraries/CoreStorageLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; diff --git a/src/core/libraries/ProtocolStorageLib.sol b/src/core/libraries/ProtocolStorageLib.sol index 2f277342..5c5c9e8d 100644 --- a/src/core/libraries/ProtocolStorageLib.sol +++ b/src/core/libraries/ProtocolStorageLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; library ProtocolStorageLib { /// @title SSV Based Apps Storage Protocol diff --git a/src/core/libraries/ValidationLib.sol b/src/core/libraries/ValidationLib.sol index 7dd4ebec..26d25ff0 100644 --- a/src/core/libraries/ValidationLib.sol +++ b/src/core/libraries/ValidationLib.sol @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.29; +pragma solidity 0.8.30; + +uint32 constant MIN_TIME_LOCK_PERIOD = 1 days; +uint32 constant MIN_EXPIRE_TIME = 1 hours; uint32 constant MAX_PERCENTAGE = 1e4; // 100% in basis points address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; diff --git a/src/core/modules/BasedAppsManager.sol b/src/core/modules/BasedAppsManager.sol index 9a7b0816..109f7a9f 100644 --- a/src/core/modules/BasedAppsManager.sol +++ b/src/core/modules/BasedAppsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; import { @@ -20,14 +20,12 @@ contract BasedAppsManager is IBasedAppManager { } /// @notice Registers a bApp. - /// @param tokens The list of tokens the bApp accepts; can be empty. - /// @param sharedRiskLevels The shared risk level of the bApp. + /// @param tokenConfigs The list of tokens configs the bApp accepts; can be empty. /// @param metadataURI The metadata URI of the bApp, which is a link (e.g., http://example.com) /// to a JSON file containing metadata such as the name, description, logo, etc. /// @dev Allows creating a bApp even with an empty token list. function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external { CoreStorageLib.Data storage s = CoreStorageLib.load(); @@ -38,9 +36,9 @@ contract BasedAppsManager is IBasedAppManager { s.registeredBApps[msg.sender] = true; - _addNewTokens(msg.sender, tokens, sharedRiskLevels); + _addNewTokens(msg.sender, tokenConfigs); - emit BAppRegistered(msg.sender, tokens, sharedRiskLevels, metadataURI); + emit BAppRegistered(msg.sender, tokenConfigs, metadataURI); } /// @notice Function to update the metadata URI of the Based Application @@ -83,25 +81,22 @@ contract BasedAppsManager is IBasedAppManager { /// @notice Function to add tokens to a bApp /// @param bApp The address of the bApp - /// @param tokens The list of tokens to add - /// @param sharedRiskLevels The shared risk levels of the tokens + /// @param tokenConfigs The list of tokens to add function _addNewTokens( address bApp, - address[] calldata tokens, - uint32[] calldata sharedRiskLevels + ICore.TokenConfig[] calldata tokenConfigs ) internal { - ValidationLib.validateArrayLengths(tokens, sharedRiskLevels); - uint256 length = tokens.length; + uint256 length = tokenConfigs.length; address token; CoreStorageLib.Data storage s = CoreStorageLib.load(); for (uint256 i = 0; i < length; ) { - token = tokens[i]; + token = tokenConfigs[i].token; ValidationLib.validateNonZeroAddress(token); if (s.bAppTokens[bApp][token].isSet) { revert IBasedAppManager.TokenAlreadyAddedToBApp(token); } ICore.SharedRiskLevel storage tokenData = s.bAppTokens[bApp][token]; - tokenData.currentValue = sharedRiskLevels[i]; + tokenData.currentValue = tokenConfigs[i].sharedRiskLevel; tokenData.isSet = true; unchecked { i++; diff --git a/src/core/modules/ProtocolManager.sol b/src/core/modules/ProtocolManager.sol index ddade666..50c62d90 100644 --- a/src/core/modules/ProtocolManager.sol +++ b/src/core/modules/ProtocolManager.sol @@ -1,23 +1,32 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.29; +pragma solidity 0.8.30; +import { + MIN_EXPIRE_TIME, + MIN_TIME_LOCK_PERIOD +} from "@ssv/src/core/libraries/ValidationLib.sol"; import { IProtocolManager } from "@ssv/src/core/interfaces/IProtocolManager.sol"; import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; +import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; contract ProtocolManager is IProtocolManager { uint32 private constant SLASHING_DISABLED = 1 << 0; uint32 private constant WITHDRAWALS_DISABLED = 1 << 1; function updateFeeTimelockPeriod(uint32 feeTimelockPeriod) external { + if (feeTimelockPeriod < MIN_TIME_LOCK_PERIOD) + revert ISSVBasedApps.InvalidFeeTimelockPeriod(); ProtocolStorageLib.load().feeTimelockPeriod = feeTimelockPeriod; emit FeeTimelockPeriodUpdated(feeTimelockPeriod); } function updateFeeExpireTime(uint32 feeExpireTime) external { + if (feeExpireTime < MIN_EXPIRE_TIME) + revert ISSVBasedApps.InvalidFeeExpireTime(); ProtocolStorageLib.load().feeExpireTime = feeExpireTime; emit FeeExpireTimeUpdated(feeExpireTime); } @@ -25,6 +34,8 @@ contract ProtocolManager is IProtocolManager { function updateWithdrawalTimelockPeriod( uint32 withdrawalTimelockPeriod ) external { + if (withdrawalTimelockPeriod < MIN_TIME_LOCK_PERIOD) + revert ISSVBasedApps.InvalidWithdrawalTimelockPeriod(); ProtocolStorageLib .load() .withdrawalTimelockPeriod = withdrawalTimelockPeriod; @@ -32,6 +43,8 @@ contract ProtocolManager is IProtocolManager { } function updateWithdrawalExpireTime(uint32 withdrawalExpireTime) external { + if (withdrawalExpireTime < MIN_EXPIRE_TIME) + revert ISSVBasedApps.InvalidWithdrawalExpireTime(); ProtocolStorageLib.load().withdrawalExpireTime = withdrawalExpireTime; emit WithdrawalExpireTimeUpdated(withdrawalExpireTime); } @@ -39,6 +52,8 @@ contract ProtocolManager is IProtocolManager { function updateObligationTimelockPeriod( uint32 obligationTimelockPeriod ) external { + if (obligationTimelockPeriod < MIN_TIME_LOCK_PERIOD) + revert ISSVBasedApps.InvalidObligationTimelockPeriod(); ProtocolStorageLib .load() .obligationTimelockPeriod = obligationTimelockPeriod; @@ -46,6 +61,8 @@ contract ProtocolManager is IProtocolManager { } function updateObligationExpireTime(uint32 obligationExpireTime) external { + if (obligationExpireTime < MIN_EXPIRE_TIME) + revert ISSVBasedApps.InvalidObligationExpireTime(); ProtocolStorageLib.load().obligationExpireTime = obligationExpireTime; emit ObligationExpireTimeUpdated(obligationExpireTime); } @@ -53,6 +70,8 @@ contract ProtocolManager is IProtocolManager { function updateTokenUpdateTimelockPeriod( uint32 tokenUpdateTimelockPeriod ) external { + if (tokenUpdateTimelockPeriod < MIN_TIME_LOCK_PERIOD) + revert ISSVBasedApps.InvalidTokenUpdateTimelockPeriod(); ProtocolStorageLib .load() .tokenUpdateTimelockPeriod = tokenUpdateTimelockPeriod; @@ -60,11 +79,15 @@ contract ProtocolManager is IProtocolManager { } function updateMaxShares(uint256 maxShares) external { + if (maxShares < 1e38) revert ISSVBasedApps.InvalidMaxShares(); ProtocolStorageLib.load().maxShares = maxShares; emit StrategyMaxSharesUpdated(maxShares); } function updateMaxFeeIncrement(uint32 maxFeeIncrement) external { + if (maxFeeIncrement < 50) + // 0.5% increment + revert ISSVBasedApps.InvalidMaxFeeIncrement(); ProtocolStorageLib.load().maxFeeIncrement = maxFeeIncrement; emit StrategyMaxFeeIncrementUpdated(maxFeeIncrement); } diff --git a/src/core/modules/StrategyManager.sol b/src/core/modules/StrategyManager.sol index 87dd9e4f..7e1c3531 100644 --- a/src/core/modules/StrategyManager.sol +++ b/src/core/modules/StrategyManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { @@ -8,9 +8,6 @@ import { import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { - ERC165Checker -} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { ValidationLib, @@ -199,6 +196,10 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { ValidationLib.validateArrayLengths(tokens, obligationPercentages); + if (!s.registeredBApps[bApp]) { + revert IBasedAppManager.BAppNotRegistered(); + } + // Check if a strategy exists for the given bApp. // It is not possible opt-in to the same bApp twice with the same strategy owner. if (s.accountBAppStrategy[msg.sender][bApp] != 0) { @@ -214,7 +215,7 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { s.accountBAppStrategy[msg.sender][bApp] = strategyId; - if (_isBApp(bApp)) { + if (_isContract(bApp)) { bool success = IBasedApp(bApp).optInToBApp( strategyId, tokens, @@ -233,12 +234,11 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { ); } - /// @notice Function to check if an address uses the correct bApp interface + /// @notice Function to check if an address is a contract /// @param bApp The address of the bApp - /// @return True if the address uses the correct bApp interface - function _isBApp(address bApp) private view returns (bool) { - return - ERC165Checker.supportsInterface(bApp, type(IBasedApp).interfaceId); + /// @return True if the address is a contract + function _isContract(address bApp) private view returns (bool) { + return bApp.code.length > 0; } /// @notice Deposit ERC20 tokens into the strategy @@ -572,7 +572,6 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { revert BAppNotOptedIn(); } - //if (obligationPercentage > MAX_PERCENTAGE) revert ICore.InvalidPercentage(); ValidationLib.validatePercentage(obligationPercentage); if ( @@ -751,6 +750,18 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { return (balance * percentage) / MAX_PERCENTAGE; } + function _checkStrategyOptedIn( + CoreStorageLib.Data storage s, + uint32 strategyId, + address bApp + ) internal view { + // It is possible to slash only if the strategy owner has opted-in to the bApp + address strategyOwner = s.strategies[strategyId].owner; // Load the strategy to check if it exists + if (s.accountBAppStrategy[strategyOwner][bApp] != strategyId) { + revert BAppNotOptedIn(); + } + } + /// @notice Slash a strategy /// @param strategyId The ID of the strategy /// @param bApp The address of the bApp @@ -774,6 +785,8 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { revert IBasedAppManager.BAppNotRegistered(); } + _checkStrategyOptedIn(s, strategyId, bApp); + ICore.Shares storage strategyTokenShares = s.strategyTokenShares[ strategyId ][token]; @@ -790,8 +803,8 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { address receiver; bool exit; bool success; - - if (_isBApp(bApp)) { + uint32 obligationPercentage; + if (_isContract(bApp)) { (success, receiver, exit) = IBasedApp(bApp).slash( strategyId, token, @@ -801,9 +814,11 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { ); if (!success) revert IStrategyManager.BAppSlashingFailed(); - if (exit) _exitStrategy(s, strategyId, bApp, token); - else - _adjustObligation( + if (exit) { + _exitStrategy(s, strategyId, bApp, token); + obligationPercentage = 0; + } else + obligationPercentage = _adjustObligation( s, strategyId, bApp, @@ -812,7 +827,7 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { strategyTokenShares ); } else { - // Only the bApp EOA or non-compliant bapp owner can slash + // Only the bApp EOA can slash if (msg.sender != bApp) revert InvalidBAppOwner(msg.sender, bApp); receiver = bApp; _exitStrategy(s, strategyId, bApp, token); @@ -834,6 +849,13 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { percentage, receiver ); + + emit IStrategyManager.ObligationUpdated( + strategyId, + bApp, + token, + obligationPercentage + ); } function _exitStrategy( @@ -843,8 +865,6 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { address token ) private { s.obligations[strategyId][bApp][token].percentage = 0; - - emit IStrategyManager.ObligationUpdated(strategyId, bApp, token, 0); } function _adjustObligation( @@ -854,7 +874,7 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { address token, uint256 amount, ICore.Shares storage strategyTokenShares - ) internal { + ) internal returns (uint32 obligationPercentage) { ICore.Obligation storage obligation = s.obligations[strategyId][bApp][ token ]; @@ -865,19 +885,14 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { uint256 postSlashObligatedBalance = currentObligatedBalance - amount; if (postSlashStrategyBalance == 0) { obligation.percentage = 0; - emit IStrategyManager.ObligationUpdated(strategyId, bApp, token, 0); + return 0; } else { uint32 postSlashObligationPercentage = uint32( (postSlashObligatedBalance / postSlashStrategyBalance) * MAX_PERCENTAGE ); obligation.percentage = postSlashObligationPercentage; - emit IStrategyManager.ObligationUpdated( - strategyId, - bApp, - token, - postSlashObligationPercentage - ); + return postSlashObligationPercentage; } } diff --git a/src/middleware/examples/ECDSAVerifier.sol b/src/middleware/examples/ECDSAVerifier.sol new file mode 100644 index 00000000..ead9057d --- /dev/null +++ b/src/middleware/examples/ECDSAVerifier.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +import { + OwnableBasedApp +} from "@ssv/src/middleware/modules/core+roles/OwnableBasedApp.sol"; + +import { + SignatureChecker +} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; + +contract ECDSAVerifier is OwnableBasedApp { + mapping(address => bool) public hasOptedIn; + + error InvalidSignature(); + error SignerAlreadyOptedIn(); + + constructor( + address _basedAppManager, + address _initOwner + ) OwnableBasedApp(_basedAppManager, _initOwner) {} + + function optInToBApp( + uint32, + address[] calldata, + uint32[] calldata, + bytes calldata data + ) external override onlySSVBasedAppManager returns (bool success) { + (address signer, bytes32 messageHash, bytes memory signature) = abi + .decode(data, (address, bytes32, bytes)); + + if (hasOptedIn[signer]) { + revert SignerAlreadyOptedIn(); + } + + success = SignatureChecker.isValidSignatureNow( + signer, + messageHash, + signature + ); + + if (success) hasOptedIn[signer] = true; + else revert InvalidSignature(); + } +} diff --git a/src/middleware/examples/WhitelistExample.sol b/src/middleware/examples/WhitelistExample.sol index 45ed2b7e..4fa3f734 100644 --- a/src/middleware/examples/WhitelistExample.sol +++ b/src/middleware/examples/WhitelistExample.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { BasedAppWhitelisted diff --git a/src/middleware/interfaces/IBasedApp.sol b/src/middleware/interfaces/IBasedApp.sol index 613e8471..3ef3ab73 100644 --- a/src/middleware/interfaces/IBasedApp.sol +++ b/src/middleware/interfaces/IBasedApp.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; -import { - IERC165 -} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; -interface IBasedApp is IERC165 { +interface IBasedApp { function optInToBApp( uint32 strategyId, address[] calldata tokens, @@ -14,8 +11,7 @@ interface IBasedApp is IERC165 { bytes calldata data ) external returns (bool); function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external; function slash( @@ -25,7 +21,6 @@ interface IBasedApp is IERC165 { address sender, bytes calldata data ) external returns (bool success, address receiver, bool exit); - function supportsInterface(bytes4 interfaceId) external view returns (bool); function updateBAppMetadataURI(string calldata metadataURI) external; function updateBAppTokens( ICore.TokenConfig[] calldata tokenConfigs diff --git a/src/middleware/interfaces/IBasedAppWhitelisted.sol b/src/middleware/interfaces/IBasedAppWhitelisted.sol index 783a78f2..ed17d3fc 100644 --- a/src/middleware/interfaces/IBasedAppWhitelisted.sol +++ b/src/middleware/interfaces/IBasedAppWhitelisted.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; interface IBasedAppWhitelisted { function addWhitelisted(uint32 strategyId) external; diff --git a/src/middleware/modules/BasedAppWhitelisted.sol b/src/middleware/modules/BasedAppWhitelisted.sol index aebb90ef..448ac545 100644 --- a/src/middleware/modules/BasedAppWhitelisted.sol +++ b/src/middleware/modules/BasedAppWhitelisted.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IBasedAppWhitelisted diff --git a/src/middleware/modules/core+roles/AccessControlBasedApp.sol b/src/middleware/modules/core+roles/AccessControlBasedApp.sol index 91c91adc..19cf2d3e 100644 --- a/src/middleware/modules/core+roles/AccessControlBasedApp.sol +++ b/src/middleware/modules/core+roles/AccessControlBasedApp.sol @@ -1,14 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; -import { - IERC165 -} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol"; import { BasedAppCore } from "@ssv/src/middleware/modules/core/BasedAppCore.sol"; @@ -16,6 +11,7 @@ import { import { IBasedAppManager } from "@ssv/src/core/interfaces/IBasedAppManager.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; abstract contract AccessControlBasedApp is BasedAppCore, AccessControl { bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); @@ -42,8 +38,7 @@ abstract contract AccessControlBasedApp is BasedAppCore, AccessControl { } /// @notice Registers a BApp calling the SSV SSVBasedApps - /// @param tokens array of token addresses - /// @param sharedRiskLevels array of shared risk levels + /// @param tokenConfigs array of token addresses and shared risk levels /// @param metadataURI URI of the metadata /// @dev metadata should point to a json that respect template: /// { @@ -54,13 +49,11 @@ abstract contract AccessControlBasedApp is BasedAppCore, AccessControl { /// "social": "https://x.com/ssv_network" /// } function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external override onlyRole(MANAGER_ROLE) { IBasedAppManager(SSV_BASED_APPS_NETWORK).registerBApp( - tokens, - sharedRiskLevels, + tokenConfigs, metadataURI ); } @@ -74,20 +67,4 @@ abstract contract AccessControlBasedApp is BasedAppCore, AccessControl { metadataURI ); } - - /// @notice Checks if the contract supports the interface - /// @param interfaceId interface id - /// @return isSupported if the contract supports the interface - function supportsInterface( - bytes4 interfaceId - ) - public - pure - override(AccessControl, BasedAppCore) - returns (bool isSupported) - { - return - interfaceId == type(IBasedApp).interfaceId || - interfaceId == type(IERC165).interfaceId; - } } diff --git a/src/middleware/modules/core+roles/OwnableBasedApp.sol b/src/middleware/modules/core+roles/OwnableBasedApp.sol index 7465784d..ba54b179 100644 --- a/src/middleware/modules/core+roles/OwnableBasedApp.sol +++ b/src/middleware/modules/core+roles/OwnableBasedApp.sol @@ -1,12 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { - IERC165 -} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol"; import { BasedAppCore } from "@ssv/src/middleware/modules/core/BasedAppCore.sol"; @@ -14,6 +9,7 @@ import { import { IBasedAppManager } from "@ssv/src/core/interfaces/IBasedAppManager.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; abstract contract OwnableBasedApp is Ownable, BasedAppCore { constructor( @@ -22,8 +18,7 @@ abstract contract OwnableBasedApp is Ownable, BasedAppCore { ) BasedAppCore(_basedAppManager) Ownable(_initOwner) {} /// @notice Registers a BApp calling the SSV SSVBasedApps - /// @param tokens array of token addresses - /// @param sharedRiskLevels array of shared risk levels + /// @param tokenConfigs array of token addresses and shared risk levels /// @param metadataURI URI of the metadata /// @dev metadata should point to a json that respect template: /// { @@ -34,13 +29,11 @@ abstract contract OwnableBasedApp is Ownable, BasedAppCore { /// "social": "https://x.com/ssv_network" /// } function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external override onlyOwner { IBasedAppManager(SSV_BASED_APPS_NETWORK).registerBApp( - tokens, - sharedRiskLevels, + tokenConfigs, metadataURI ); } @@ -54,15 +47,4 @@ abstract contract OwnableBasedApp is Ownable, BasedAppCore { metadataURI ); } - - /// @notice Checks if the contract supports the interface - /// @param interfaceId interface id - /// @return isSupported if the contract supports the interface - function supportsInterface( - bytes4 interfaceId - ) public pure override(BasedAppCore) returns (bool isSupported) { - return - interfaceId == type(IBasedApp).interfaceId || - interfaceId == type(IERC165).interfaceId; - } } diff --git a/src/middleware/modules/core/BasedAppCore.sol b/src/middleware/modules/core/BasedAppCore.sol index 80cc9fab..c67a4c9b 100644 --- a/src/middleware/modules/core/BasedAppCore.sol +++ b/src/middleware/modules/core/BasedAppCore.sol @@ -1,9 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; - -import { - IERC165 -} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +pragma solidity 0.8.30; import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol"; @@ -43,9 +39,8 @@ abstract contract BasedAppCore is IBasedApp { SSV_BASED_APPS_NETWORK = _ssvBasedAppsNetwork; } - /// @notice Registers a BApp calling the SSV SSVBasedApps - /// @param tokens array of token addresses - /// @param sharedRiskLevels array of shared risk levels + /// @notice Registers a BApp calling the SSVBasedApps + /// @param tokenConfigs array of token configs (address, shared risk level) /// @param metadataURI URI of the metadata /// @dev metadata should point to a json that respect template: /// { @@ -56,13 +51,11 @@ abstract contract BasedAppCore is IBasedApp { /// "social": "https://x.com/ssv_network" /// } function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external virtual { IBasedAppManager(SSV_BASED_APPS_NETWORK).registerBApp( - tokens, - sharedRiskLevels, + tokenConfigs, metadataURI ); } @@ -141,17 +134,6 @@ abstract contract BasedAppCore is IBasedApp { return (true, address(this), true); } - /// @notice Checks if the contract supports the interface - /// @param interfaceId interface id - /// @return true if the contract supports the interface - function supportsInterface( - bytes4 interfaceId - ) public pure virtual returns (bool) { - return - interfaceId == type(IBasedApp).interfaceId || - interfaceId == type(IERC165).interfaceId; - } - // Receive function to accept plain Ether transfers receive() external payable virtual {} } diff --git a/test/Config.t.sol b/test/Config.t.sol new file mode 100644 index 00000000..94bfdc96 --- /dev/null +++ b/test/Config.t.sol @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +import { Test } from "forge-std/Test.sol"; + +import { + ERC1967Proxy +} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +import { BasedAppsManager } from "@ssv/src/core/modules/BasedAppsManager.sol"; +import { + IBasedAppManager +} from "@ssv/src/core/interfaces/IBasedAppManager.sol"; +import { + IProtocolManager +} from "@ssv/src/core/interfaces/IProtocolManager.sol"; +import { + IStrategyManager +} from "@ssv/src/core/interfaces/IStrategyManager.sol"; +import { SSVBasedApps } from "@ssv/src/core/SSVBasedApps.sol"; +import { ProtocolManager } from "@ssv/src/core/modules/ProtocolManager.sol"; +import { StrategyManager } from "@ssv/src/core/modules/StrategyManager.sol"; +import { + ProtocolStorageLib +} from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; +import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; + +contract Config is Test { + // Main Contract + SSVBasedApps public implementation; + // Modules + StrategyManager public strategyManagerMod; + BasedAppsManager public basedAppsManagerMod; + ProtocolManager public protocolManagerMod; + + // Proxies + ERC1967Proxy public proxy; // UUPS Proxy contract + SSVBasedApps public proxiedManager; // Proxy interface for interaction + + // EOAs + address public immutable OWNER = makeAddr("Owner"); + + // Constants + address public constant ETH_ADDRESS = + 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + uint32 public constant MAX_FEE_INCREMENT = 500; // 5% + // Array containing all the BApps created + ProtocolStorageLib.Data public config; + + function setUp() public virtual { + vm.label(OWNER, "Owner"); + vm.startPrank(OWNER); + basedAppsManagerMod = new BasedAppsManager(); + strategyManagerMod = new StrategyManager(); + protocolManagerMod = new ProtocolManager(); + implementation = new SSVBasedApps(); + vm.stopPrank(); + } + + function testRevertInvalidMaxFeeIncrementWithZeroFee() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: 0, + feeTimelockPeriod: 0 days, + feeExpireTime: 1 days, + withdrawalTimelockPeriod: 14 days, + withdrawalExpireTime: 3 days, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidMaxFeeIncrement.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidMaxFeeIncrementWithExcessiveFee() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: 10001, + feeTimelockPeriod: 0 days, + feeExpireTime: 1 days, + withdrawalTimelockPeriod: 14 days, + withdrawalExpireTime: 3 days, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidMaxFeeIncrement.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidFeeTimelockPeriod() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 0 days, + feeExpireTime: 1 days, + withdrawalTimelockPeriod: 14 days, + withdrawalExpireTime: 3 days, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidFeeTimelockPeriod.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidFeeExpireTime() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 59 minutes, + withdrawalTimelockPeriod: 14 days, + withdrawalExpireTime: 3 days, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector(ISSVBasedApps.InvalidFeeExpireTime.selector) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidWithdrawalTimelockPeriod() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 0 days, + withdrawalExpireTime: 3 days, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidWithdrawalTimelockPeriod.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidWithdrawalExpireTime() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 1 days, + withdrawalExpireTime: 0 hours, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidWithdrawalExpireTime.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidObligationTimelockPeriod() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 1 days, + withdrawalExpireTime: 1 hours, + obligationTimelockPeriod: 23 hours, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidObligationTimelockPeriod.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidObligationExpireTime() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 1 days, + withdrawalExpireTime: 1 hours, + obligationTimelockPeriod: 1 days, + obligationExpireTime: 0 hours, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidObligationExpireTime.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidTokenUpdateTimelockPeriod() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 1 days, + withdrawalExpireTime: 1 hours, + obligationTimelockPeriod: 1 days, + obligationExpireTime: 1 hours, + tokenUpdateTimelockPeriod: 0 days, + maxShares: 1e50, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidTokenUpdateTimelockPeriod.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + + function testRevertInvalidMaxShares() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 1 days, + withdrawalExpireTime: 1 hours, + obligationTimelockPeriod: 1 days, + obligationExpireTime: 1 hours, + tokenUpdateTimelockPeriod: 1 days, + maxShares: 1e49, + disabledFeatures: 0 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector(ISSVBasedApps.InvalidMaxShares.selector) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } + function testRevertInvalidDisabledFeatures() public virtual { + config = ProtocolStorageLib.Data({ + maxFeeIncrement: MAX_FEE_INCREMENT, + feeTimelockPeriod: 1 days, + feeExpireTime: 1 hours, + withdrawalTimelockPeriod: 1 days, + withdrawalExpireTime: 3 days, + obligationTimelockPeriod: 14 days, + obligationExpireTime: 3 days, + tokenUpdateTimelockPeriod: 14 days, + maxShares: 1e50, + disabledFeatures: 4 + }); + + bytes memory data = abi.encodeWithSelector( + implementation.initialize.selector, + address(OWNER), + IBasedAppManager(basedAppsManagerMod), + IStrategyManager(strategyManagerMod), + IProtocolManager(protocolManagerMod), + config + ); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidDisabledFeatures.selector + ) + ); + proxy = new ERC1967Proxy(address(implementation), data); + } +} diff --git a/test/SSVBasedApps.t.sol b/test/SSVBasedApps.t.sol index 7943663a..5d70ae98 100644 --- a/test/SSVBasedApps.t.sol +++ b/test/SSVBasedApps.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Ownable2StepUpgradeable @@ -10,14 +10,9 @@ import { IStrategyManager, IBasedAppManager, IProtocolManager, - SSVBasedApps, - ERC1967Proxy + SSVBasedApps } from "@ssv/test/helpers/Setup.t.sol"; import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; -import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; -import { - ProtocolStorageLib -} from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; import { SSVCoreModules } from "@ssv/src/core/libraries/CoreStorageLib.sol"; contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { @@ -165,66 +160,6 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { ); } - function testRevertInitializeWithZeroFee() public { - ProtocolStorageLib.Data memory configZeroFee = ProtocolStorageLib.Data({ - maxFeeIncrement: 0, - feeTimelockPeriod: 7 days, - feeExpireTime: 1 days, - withdrawalTimelockPeriod: 14 days, - withdrawalExpireTime: 3 days, - obligationTimelockPeriod: 14 days, - obligationExpireTime: 3 days, - tokenUpdateTimelockPeriod: 14 days, - maxShares: 1e50, - disabledFeatures: 0 - }); - vm.expectRevert( - abi.encodeWithSelector( - ISSVBasedApps.InvalidMaxFeeIncrement.selector - ) - ); - - bytes memory initData = abi.encodeWithSelector( - implementation.initialize.selector, - address(OWNER), - address(basedAppsManagerMod), - address(strategyManagerMod), - address(protocolManagerMod), - configZeroFee - ); - proxy = new ERC1967Proxy(address(implementation), initData); - } - - function testRevertInitializeWithExcessiveFee() public { - ProtocolStorageLib.Data memory configExcessiveFee = ProtocolStorageLib - .Data({ - feeTimelockPeriod: 7 days, - feeExpireTime: 1 days, - withdrawalTimelockPeriod: 14 days, - maxShares: 1e50, - withdrawalExpireTime: 3 days, - obligationTimelockPeriod: 14 days, - obligationExpireTime: 3 days, - tokenUpdateTimelockPeriod: 14 days, - maxFeeIncrement: 10_001, - disabledFeatures: 0 - }); - vm.expectRevert( - abi.encodeWithSelector( - ISSVBasedApps.InvalidMaxFeeIncrement.selector - ) - ); - bytes memory initData = abi.encodeWithSelector( - implementation.initialize.selector, - address(OWNER), - address(basedAppsManagerMod), - address(strategyManagerMod), - address(protocolManagerMod), - configExcessiveFee - ); - proxy = new ERC1967Proxy(address(implementation), initData); - } - function testUpdateStrategyModule() public { SSVCoreModules[] memory moduleIds = new SSVCoreModules[](1); address[] memory moduleAddresses = new address[](1); diff --git a/test/helpers/Setup.t.sol b/test/helpers/Setup.t.sol index dd328549..8f9b3ff3 100644 --- a/test/helpers/Setup.t.sol +++ b/test/helpers/Setup.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Test } from "forge-std/Test.sol"; @@ -33,6 +33,7 @@ import { import { WhitelistExample } from "@ssv/src/middleware/examples/WhitelistExample.sol"; +import { ECDSAVerifier } from "@ssv/src/middleware/examples/ECDSAVerifier.sol"; import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol"; contract Setup is Test { @@ -53,6 +54,7 @@ contract Setup is Test { BasedAppMock4 public bApp4; NonCompliantBApp public nonCompliantBApp; WhitelistExample public whitelistExample; + ECDSAVerifier public ecdsaVerifierExample; // Tokens IERC20 public erc20mock; IERC20 public erc20mock2; @@ -74,10 +76,10 @@ contract Setup is Test { uint32 public constant STRATEGY3 = 3; uint32 public constant STRATEGY4 = 4; // Fees - uint32 public constant STRATEGY1_INITIAL_FEE = 5; - uint32 public constant STRATEGY2_INITIAL_FEE = 0; - uint32 public constant STRATEGY3_INITIAL_FEE = 1000; - uint32 public constant STRATEGY4_INITIAL_FEE = 900; + uint32 public constant STRATEGY1_INITIAL_FEE = 5; // %0.05 + uint32 public constant STRATEGY2_INITIAL_FEE = 0; // %0.00 + uint32 public constant STRATEGY3_INITIAL_FEE = 1000; // %10.00 + uint32 public constant STRATEGY4_INITIAL_FEE = 900; // %9.00 uint32 public constant STRATEGY1_UPDATE_FEE = 10; // Initial Balances uint256 public constant INITIAL_USER1_BALANCE_ERC20 = 1000 * 10 ** 18; @@ -94,7 +96,7 @@ contract Setup is Test { // Constants address public constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - uint32 public constant MAX_FEE_INCREMENT = 500; + uint32 public constant MAX_FEE_INCREMENT = 500; // 5% // Array containing all the BApps created IBasedApp[] public bApps; ProtocolStorageLib.Data public config; @@ -136,7 +138,7 @@ contract Setup is Test { ); proxy = new ERC1967Proxy(address(implementation), data); proxiedManager = SSVBasedApps(payable(address(proxy))); - assertEq(proxiedManager.getVersion(), "0.1.1", "Version mismatch"); + assertEq(proxiedManager.getVersion(), "0.2.0", "Version mismatch"); assertEq( proxiedManager.maxFeeIncrement(), 500, @@ -157,6 +159,10 @@ contract Setup is Test { nonCompliantBApp = new NonCompliantBApp(address(proxiedManager)); whitelistExample = new WhitelistExample(address(proxiedManager), USER1); + ecdsaVerifierExample = new ECDSAVerifier( + address(proxiedManager), + USER1 + ); bApps.push(bApp1); bApps.push(bApp2); @@ -170,6 +176,7 @@ contract Setup is Test { vm.label(address(bApp4), "BasedApp4"); vm.label(address(nonCompliantBApp), "NonCompliantBApp"); vm.label(address(whitelistExample), "WhitelistExample"); + vm.label(address(ecdsaVerifierExample), "ECDSAVerifierExample"); vm.label(address(proxiedManager), "BasedAppManagerProxy"); vm.deal(USER1, INITIAL_USER1_BALANCE_ETH); diff --git a/test/helpers/Utils.t.sol b/test/helpers/Utils.t.sol index aeb408eb..9aeb0728 100644 --- a/test/helpers/Utils.t.sol +++ b/test/helpers/Utils.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { SSVBasedApps } from "@ssv/src/core/SSVBasedApps.sol"; import { Setup } from "@ssv/test/helpers/Setup.t.sol"; @@ -22,6 +22,16 @@ contract UtilsTest is Setup { sharedRiskLevelInput = new uint32[](1); sharedRiskLevelInput[0] = sharedRiskLevel; } + function createSingleTokenConfig( + address token, + uint32 sharedRiskLevel + ) internal pure returns (ICore.TokenConfig[] memory tokenConfigs) { + tokenConfigs = new ICore.TokenConfig[](1); + tokenConfigs[0] = ICore.TokenConfig({ + token: token, + sharedRiskLevel: sharedRiskLevel + }); + } function createSingleTokenAndSingleObligationPercentage( address token, @@ -41,23 +51,17 @@ contract UtilsTest is Setup { } function checkBAppInfo( - address[] memory tokensInput, - uint32[] memory riskLevelInput, + ICore.TokenConfig[] memory tokenConfigsInput, address bApp, SSVBasedApps proxiedManager ) internal view { - assertEq( - tokensInput.length, - riskLevelInput.length, - "BApp tokens and sharedRiskLevel length" - ); bool isRegistered = proxiedManager.registeredBApps(bApp); assertEq(isRegistered, true, "BApp registered"); - for (uint32 i = 0; i < tokensInput.length; i++) { + for (uint32 i = 0; i < tokenConfigsInput.length; i++) { (uint32 sharedRiskLevel, bool isSet, , ) = proxiedManager - .bAppTokens(bApp, tokensInput[i]); + .bAppTokens(bApp, tokenConfigsInput[i].token); assertEq( - riskLevelInput[i], + tokenConfigsInput[i].sharedRiskLevel, sharedRiskLevel, "BApp risk level percentage" ); diff --git a/test/libraries/ValidationLib.t.sol b/test/libraries/ValidationLib.t.sol index c4a7e8c2..aec22612 100644 --- a/test/libraries/ValidationLib.t.sol +++ b/test/libraries/ValidationLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { ValidationLib } from "@ssv/src/core/libraries/ValidationLib.sol"; import { Setup } from "@ssv/test/helpers/Setup.t.sol"; diff --git a/test/middleware/examples/ECDSAVerifier/ECDSAVerifier.t.sol b/test/middleware/examples/ECDSAVerifier/ECDSAVerifier.t.sol new file mode 100644 index 00000000..30f652e9 --- /dev/null +++ b/test/middleware/examples/ECDSAVerifier/ECDSAVerifier.t.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +import { IBasedApp } from "@ssv/test/helpers/Setup.t.sol"; +import { UtilsTest } from "@ssv/test/helpers/Utils.t.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; +import { ECDSAVerifier } from "@ssv/src/middleware/examples/ECDSAVerifier.sol"; + +contract WhitelistExampleTest is UtilsTest { + function testCreateStrategies() public { + vm.startPrank(USER1); + erc20mock.approve(address(proxiedManager), INITIAL_USER1_BALANCE_ERC20); + erc20mock2.approve( + address(proxiedManager), + INITIAL_USER1_BALANCE_ERC20 + ); + uint32 strategyId1 = proxiedManager.createStrategy( + STRATEGY1_INITIAL_FEE, + "" + ); + assertEq(strategyId1, STRATEGY1, "Should set the correct strategy ID"); + (address owner, uint32 delegationFeeOnRewards) = proxiedManager + .strategies(strategyId1); + assertEq(owner, USER1, "Should set the correct strategy owner"); + assertEq( + delegationFeeOnRewards, + STRATEGY1_INITIAL_FEE, + "Should set the correct strategy fee" + ); + vm.stopPrank(); + vm.prank(USER2); + uint32 strategyId2 = proxiedManager.createStrategy( + STRATEGY1_INITIAL_FEE, + "" + ); + assertEq(strategyId2, STRATEGY2, "Should set the correct strategy ID"); + } + + function testRegisterECDSAVerifierExampleBApp() public { + vm.startPrank(USER1); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(erc20mock), + 102 + ); + ecdsaVerifierExample.registerBApp(tokenConfigsInput, ""); + checkBAppInfo( + tokenConfigsInput, + address(ecdsaVerifierExample), + proxiedManager + ); + vm.stopPrank(); + } + + function testRevertOptInToBAppWithUnauthorizedCaller() public { + vm.prank(USER1); + ( + address[] memory tokensInput, + uint32[] memory riskLevelInput + ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000); + vm.expectRevert( + abi.encodeWithSelector(IBasedApp.UnauthorizedCaller.selector) + ); + ecdsaVerifierExample.optInToBApp( + STRATEGY1, + tokensInput, + riskLevelInput, + "" + ); + } + + function testOptInToBApp() public { + testCreateStrategies(); + testRegisterECDSAVerifierExampleBApp(); + ( + address[] memory tokensInput, + uint32[] memory riskLevelInput + ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000); + address signer = 0x763569566a3CE4f8D73f96c4996aBdf297f74ADE; + bytes32 messageHash = 0x5b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed86; + bytes + memory signature = hex"ddc30e871857b9a4d2dce47f49aec426404e69c98e05abc345df2f096e47fcb33d3e061da2435584d92df8731522d35ae485447311eb4a3c0bfdb09150cbad081b"; + + bytes memory data = abi.encode(signer, messageHash, signature); + vm.prank(USER1); + proxiedManager.optInToBApp( + STRATEGY1, + address(ecdsaVerifierExample), + tokensInput, + riskLevelInput, + data + ); + } + + function testRevertOptInToBAppWithInvalidSignature() public { + testCreateStrategies(); + testRegisterECDSAVerifierExampleBApp(); + ( + address[] memory tokensInput, + uint32[] memory riskLevelInput + ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000); + address signer = 0x763569566a3CE4f8D73f96c4996aBdf297f74ADE; + bytes32 messageHash = 0x5b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed86; + bytes + memory signature = hex"ddc30e871857b9a4d2dce47f49aec426404e69c98e05abc345df2f096e47fcb33d3e061da2435584d92df8731522d35ae485447311eb4a3c0bfdb09150cbad081c"; + + bytes memory data = abi.encode(signer, messageHash, signature); + vm.prank(USER1); + vm.expectRevert( + abi.encodeWithSelector(ECDSAVerifier.InvalidSignature.selector) + ); + proxiedManager.optInToBApp( + STRATEGY1, + address(ecdsaVerifierExample), + tokensInput, + riskLevelInput, + data + ); + } + + function testRevertReplayAttackOnAnotherStrategy() public { + testOptInToBApp(); + ( + address[] memory tokensInput, + uint32[] memory riskLevelInput + ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000); + address signer = 0x763569566a3CE4f8D73f96c4996aBdf297f74ADE; + bytes32 messageHash = 0x5b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed86; + bytes + memory signature = hex"ddc30e871857b9a4d2dce47f49aec426404e69c98e05abc345df2f096e47fcb33d3e061da2435584d92df8731522d35ae485447311eb4a3c0bfdb09150cbad081b"; + + bytes memory data = abi.encode(signer, messageHash, signature); + vm.prank(USER2); + vm.expectRevert( + abi.encodeWithSelector(ECDSAVerifier.SignerAlreadyOptedIn.selector) + ); + proxiedManager.optInToBApp( + STRATEGY2, + address(ecdsaVerifierExample), + tokensInput, + riskLevelInput, + data + ); + } +} diff --git a/test/middleware/examples/ECDSAVerifier/client/package-lock.json b/test/middleware/examples/ECDSAVerifier/client/package-lock.json new file mode 100644 index 00000000..f3854f8c --- /dev/null +++ b/test/middleware/examples/ECDSAVerifier/client/package-lock.json @@ -0,0 +1,122 @@ +{ + "name": "client", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "ethers": "^6.14.3" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/ethers": { + "version": "6.14.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.14.3.tgz", + "integrity": "sha512-qq7ft/oCJohoTcsNPFaXSQUm457MA5iWqkf1Mb11ujONdg7jBI6sAOrHaTi3j0CBqIGFSCeR/RMc+qwRRub7IA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/test/middleware/examples/ECDSAVerifier/client/package.json b/test/middleware/examples/ECDSAVerifier/client/package.json new file mode 100644 index 00000000..3dd5c081 --- /dev/null +++ b/test/middleware/examples/ECDSAVerifier/client/package.json @@ -0,0 +1,15 @@ +{ + "name": "ecdsa-verifier-client", + "version": "1.0.0", + "main": "signature.js", + "type": "module", + "author": "", + "license": "UNLICENSED", + "description": "client for generating a signed message with ECDSA", + "dependencies": { + "ethers": "^6.14.3" + }, + "scripts": { + "start": "node signature.js" + } +} diff --git a/test/middleware/examples/ECDSAVerifier/client/signature.js b/test/middleware/examples/ECDSAVerifier/client/signature.js new file mode 100644 index 00000000..c5387094 --- /dev/null +++ b/test/middleware/examples/ECDSAVerifier/client/signature.js @@ -0,0 +1,20 @@ +import { Wallet, ethers } from "ethers"; + +const wallet = Wallet.createRandom(); +const signer = wallet.address; +const signingKey = new ethers.SigningKey(wallet.privateKey); + +const message = "Hello, Ethereum!"; +const messageHash = ethers.hashMessage(message) + +const signature = signingKey.sign(messageHash); + +if(signer != ethers.recoverAddress(messageHash, signature)) { + throw new Error("Signature verification failed"); +} + +console.log("✅ Generated test input:"); +console.log("Signer Address :", signer); +console.log("Message :", message); +console.log("Message Hash :", messageHash); +console.log("Signature :", signature.serialized); diff --git a/test/middleware/examples/WhitelistExample.t.sol b/test/middleware/examples/WhitelistExample.t.sol index 211788c3..1c9a0e68 100644 --- a/test/middleware/examples/WhitelistExample.t.sol +++ b/test/middleware/examples/WhitelistExample.t.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IBasedAppWhitelisted } from "@ssv/src/middleware/interfaces/IBasedAppWhitelisted.sol"; import { IBasedApp } from "@ssv/test/helpers/Setup.t.sol"; import { UtilsTest } from "@ssv/test/helpers/Utils.t.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; contract WhitelistExampleTest is UtilsTest { function testCreateStrategies() public { @@ -33,14 +34,13 @@ contract WhitelistExampleTest is UtilsTest { function testRegisterWhitelistExampleBApp() public { vm.startPrank(USER1); - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 102); - whitelistExample.registerBApp(tokensInput, sharedRiskLevelInput, ""); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(erc20mock), + 102 + ); + whitelistExample.registerBApp(tokenConfigsInput, ""); checkBAppInfo( - tokensInput, - sharedRiskLevelInput, + tokenConfigsInput, address(whitelistExample), proxiedManager ); @@ -117,7 +117,7 @@ contract WhitelistExampleTest is UtilsTest { assertEq(whitelistExample.isWhitelisted(STRATEGY1), false); } - function testRevertOddWhitelistedAccount() public { + function testRevertAddWhitelistedAccount() public { testAddWhitelistedAccount(); vm.prank(USER1); vm.expectRevert( @@ -136,7 +136,7 @@ contract WhitelistExampleTest is UtilsTest { whitelistExample.removeWhitelisted(STRATEGY1); } - function testRevertOddWhitelistedZeroID() public { + function testRevertAddWhitelistedZeroID() public { vm.prank(USER1); vm.expectRevert( abi.encodeWithSelector(IBasedAppWhitelisted.ZeroID.selector) diff --git a/test/middleware/modules/AccessControlBasedApp.t.sol b/test/middleware/modules/AccessControlBasedApp.t.sol index 99ac5c89..595ccb7b 100644 --- a/test/middleware/modules/AccessControlBasedApp.t.sol +++ b/test/middleware/modules/AccessControlBasedApp.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Setup } from "@ssv/test/helpers/Setup.t.sol"; diff --git a/test/mocks/MockBApp.sol b/test/mocks/MockBApp.sol index 3e895316..b9feeb45 100644 --- a/test/mocks/MockBApp.sol +++ b/test/mocks/MockBApp.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { OwnableBasedApp diff --git a/test/mocks/MockBApp2.sol b/test/mocks/MockBApp2.sol index 7df5ea44..434a0459 100644 --- a/test/mocks/MockBApp2.sol +++ b/test/mocks/MockBApp2.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { BasedAppCore diff --git a/test/mocks/MockBApp4RejectEth.sol b/test/mocks/MockBApp4RejectEth.sol index ac790371..0eb16dd3 100644 --- a/test/mocks/MockBApp4RejectEth.sol +++ b/test/mocks/MockBApp4RejectEth.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { OwnableBasedApp diff --git a/test/mocks/MockBAppAccessControl.sol b/test/mocks/MockBAppAccessControl.sol index 51ee107f..6f3aa639 100644 --- a/test/mocks/MockBAppAccessControl.sol +++ b/test/mocks/MockBAppAccessControl.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { AccessControlBasedApp diff --git a/test/mocks/MockERC20.sol b/test/mocks/MockERC20.sol index bdb2903f..488bd6ae 100644 --- a/test/mocks/MockERC20.sol +++ b/test/mocks/MockERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) -pragma solidity 0.8.29; +pragma solidity 0.8.30; import "@openzeppelin/contracts/interfaces/IERC20.sol"; import "@openzeppelin/contracts/utils/Context.sol"; diff --git a/test/mocks/MockNonCompliantBApp.sol b/test/mocks/MockNonCompliantBApp.sol index aa9bc64d..16d9df29 100644 --- a/test/mocks/MockNonCompliantBApp.sol +++ b/test/mocks/MockNonCompliantBApp.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; + +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; interface ICustomBasedAppManager { function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external; function slash( @@ -33,13 +34,11 @@ contract NonCompliantBApp { } function registerBApp( - address[] calldata tokens, - uint32[] calldata sharedRiskLevels, + ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI ) external { ICustomBasedAppManager(BASED_APP_MANAGER).registerBApp( - tokens, - sharedRiskLevels, + tokenConfigs, metadataURI ); } diff --git a/test/modules/BasedAppsManager.t.sol b/test/modules/BasedAppsManager.t.sol index 0e3aa5b0..4504a778 100644 --- a/test/modules/BasedAppsManager.t.sol +++ b/test/modules/BasedAppsManager.t.sol @@ -1,14 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { - IERC165 -} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { UtilsTest } from "@ssv/test/helpers/Utils.t.sol"; import { IBasedAppManager, IBasedApp } from "@ssv/test/helpers/Setup.t.sol"; @@ -33,77 +29,50 @@ contract BasedAppsManagerTest is UtilsTest { function createTwoTokenAndRiskInputs() private view - returns ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) + returns (ICore.TokenConfig[] memory tokenConfigsInput) { - tokensInput = new address[](2); - sharedRiskLevelInput = new uint32[](2); + address[] memory tokensInput = new address[](2); + uint32[] memory sharedRiskLevelInput = new uint32[](2); tokensInput[0] = address(erc20mock); tokensInput[1] = address(erc20mock2); sharedRiskLevelInput[0] = 102; sharedRiskLevelInput[1] = 103; + tokenConfigsInput = new ICore.TokenConfig[](2); + for (uint256 i = 0; i < tokensInput.length; i++) { + tokenConfigsInput[i] = ICore.TokenConfig({ + token: tokensInput[i], + sharedRiskLevel: sharedRiskLevelInput[i] + }); + } } function createTwoTokenAndRiskInputsWithTheSameToken() private view - returns ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) + returns (ICore.TokenConfig[] memory tokenConfigsInput) { - tokensInput = new address[](2); - sharedRiskLevelInput = new uint32[](2); + tokenConfigsInput = new ICore.TokenConfig[](2); + address[] memory tokensInput = new address[](2); + uint32[] memory sharedRiskLevelInput = new uint32[](2); tokensInput[0] = address(erc20mock); tokensInput[1] = address(erc20mock); sharedRiskLevelInput[0] = 102; sharedRiskLevelInput[1] = 103; - } - - function createTwoTokenAndMoreRiskInputs() - private - view - returns ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) - { - tokensInput = new address[](2); - sharedRiskLevelInput = new uint32[](3); - tokensInput[0] = address(erc20mock); - tokensInput[1] = address(erc20mock2); - sharedRiskLevelInput[0] = 102; - sharedRiskLevelInput[1] = 103; - sharedRiskLevelInput[1] = 104; - } - - function createTwoTokenAndLessRiskInputs() - private - view - returns ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) - { - tokensInput = new address[](2); - sharedRiskLevelInput = new uint32[](1); - tokensInput[0] = address(erc20mock); - tokensInput[1] = address(erc20mock2); - sharedRiskLevelInput[0] = 102; + for (uint256 i = 0; i < tokensInput.length; i++) { + tokenConfigsInput[i] = ICore.TokenConfig({ + token: tokensInput[i], + sharedRiskLevel: sharedRiskLevelInput[i] + }); + } } function createFiveTokenAndRiskInputs() private view - returns ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) + returns (ICore.TokenConfig[] memory tokenConfigsInput) { - tokensInput = new address[](5); - sharedRiskLevelInput = new uint32[](5); + address[] memory tokensInput = new address[](5); + uint32[] memory sharedRiskLevelInput = new uint32[](5); tokensInput[0] = address(erc20mock); tokensInput[1] = address(erc20mock2); tokensInput[2] = address(erc20mock3); @@ -114,127 +83,93 @@ contract BasedAppsManagerTest is UtilsTest { sharedRiskLevelInput[2] = 104; sharedRiskLevelInput[3] = 105; sharedRiskLevelInput[4] = 106; + tokenConfigsInput = new ICore.TokenConfig[](5); + for (uint256 i = 0; i < tokensInput.length; i++) { + tokenConfigsInput[i] = ICore.TokenConfig({ + token: tokensInput[i], + sharedRiskLevel: sharedRiskLevelInput[i] + }); + } } function testRegisterBApp() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 102); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(erc20mock), + 102 + ); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); vm.expectEmit(true, true, true, true); emit IBasedAppManager.BAppRegistered( address(bApps[i]), - tokensInput, - sharedRiskLevelInput, + tokenConfigsInput, metadataURIs[i] ); - bApps[i].registerBApp( - tokensInput, - sharedRiskLevelInput, - metadataURIs[i] - ); - checkBAppInfo( - tokensInput, - sharedRiskLevelInput, - address(bApps[i]), - proxiedManager - ); + bApps[i].registerBApp(tokenConfigsInput, metadataURIs[i]); + checkBAppInfo(tokenConfigsInput, address(bApps[i]), proxiedManager); } } function testRegisterBAppWithEOA() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 102); - vm.prank(USER1); - proxiedManager.registerBApp( - tokensInput, - sharedRiskLevelInput, - metadataURIs[0] + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(erc20mock), + 102 ); - checkBAppInfo(tokensInput, sharedRiskLevelInput, USER1, proxiedManager); + vm.prank(USER1); + proxiedManager.registerBApp(tokenConfigsInput, metadataURIs[0]); + checkBAppInfo(tokenConfigsInput, USER1, proxiedManager); } function testRegisterBAppWithEOAWithEth() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createSingleTokenAndSingleRiskLevel(ETH_ADDRESS, 102); - vm.prank(USER1); - proxiedManager.registerBApp( - tokensInput, - sharedRiskLevelInput, - metadataURIs[0] + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + ETH_ADDRESS, + 102 ); - checkBAppInfo(tokensInput, sharedRiskLevelInput, USER1, proxiedManager); + vm.prank(USER1); + proxiedManager.registerBApp(tokenConfigsInput, metadataURIs[0]); + checkBAppInfo(tokenConfigsInput, USER1, proxiedManager); } function testRegisterBAppWith2Tokens() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createTwoTokenAndRiskInputs(); + ICore.TokenConfig[] + memory tokenConfigsInput = createTwoTokenAndRiskInputs(); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - bApps[i].registerBApp(tokensInput, sharedRiskLevelInput, ""); - checkBAppInfo( - tokensInput, - sharedRiskLevelInput, - address(bApps[i]), - proxiedManager - ); + bApps[i].registerBApp(tokenConfigsInput, ""); + checkBAppInfo(tokenConfigsInput, address(bApps[i]), proxiedManager); } } function testRegisterBAppWithETH() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createSingleTokenAndSingleRiskLevel(ETH_ADDRESS, 100); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + ETH_ADDRESS, + 100 + ); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - bApps[i].registerBApp(tokensInput, sharedRiskLevelInput, ""); - checkBAppInfo( - tokensInput, - sharedRiskLevelInput, - address(bApps[i]), - proxiedManager - ); + bApps[i].registerBApp(tokenConfigsInput, ""); + checkBAppInfo(tokenConfigsInput, address(bApps[i]), proxiedManager); } } function testRegisterBAppWithNoTokens() public { - address[] memory tokensInput = new address[](0); - uint32[] memory sharedRiskLevelInput = new uint32[](0); + ICore.TokenConfig[] memory tokenConfigsInput = new ICore.TokenConfig[]( + 0 + ); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - bApps[i].registerBApp(tokensInput, sharedRiskLevelInput, ""); - checkBAppInfo( - tokensInput, - sharedRiskLevelInput, - address(bApps[i]), - proxiedManager - ); + bApps[i].registerBApp(tokenConfigsInput, ""); + checkBAppInfo(tokenConfigsInput, address(bApps[i]), proxiedManager); } } function testRegisterBAppWithFiveTokens() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createFiveTokenAndRiskInputs(); + ICore.TokenConfig[] + memory tokenConfigsInput = createFiveTokenAndRiskInputs(); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - bApps[i].registerBApp(tokensInput, sharedRiskLevelInput, ""); - checkBAppInfo( - tokensInput, - sharedRiskLevelInput, - address(bApps[i]), - proxiedManager - ); + bApps[i].registerBApp(tokenConfigsInput, ""); + checkBAppInfo(tokenConfigsInput, address(bApps[i]), proxiedManager); } } @@ -245,40 +180,42 @@ contract BasedAppsManagerTest is UtilsTest { uint32[] memory sharedRiskLevelInput = new uint32[](2); sharedRiskLevelInput[0] = 102; sharedRiskLevelInput[1] = 102; + ICore.TokenConfig[] memory tokenConfigsInput = new ICore.TokenConfig[]( + tokensInput.length + ); + for (uint256 i = 0; i < tokensInput.length; i++) { + tokenConfigsInput[i] = ICore.TokenConfig({ + token: tokensInput[i], + sharedRiskLevel: sharedRiskLevelInput[i] + }); + } for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - bApps[i].registerBApp(tokensInput, sharedRiskLevelInput, ""); - checkBAppInfo( - tokensInput, - sharedRiskLevelInput, - address(bApps[i]), - proxiedManager - ); + bApps[i].registerBApp(tokenConfigsInput, ""); + checkBAppInfo(tokenConfigsInput, address(bApps[i]), proxiedManager); } } function testRevertRegisterBAppWithSameTokens() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createTwoTokenAndRiskInputsWithTheSameToken(); + ICore.TokenConfig[] + memory tokenConfigsInput = createTwoTokenAndRiskInputsWithTheSameToken(); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); vm.expectRevert( abi.encodeWithSelector( IBasedAppManager.TokenAlreadyAddedToBApp.selector, - tokensInput[0] + tokenConfigsInput[0].token ) ); - bApps[i].registerBApp(tokensInput, sharedRiskLevelInput, ""); + bApps[i].registerBApp(tokenConfigsInput, ""); } } function testRevertRegisterBAppWithTokenZero() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createSingleTokenAndSingleRiskLevel(address(0), 102); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(0), + 102 + ); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); vm.expectRevert( @@ -286,100 +223,55 @@ contract BasedAppsManagerTest is UtilsTest { ValidationLib.ZeroAddressNotAllowed.selector ) ); - bApps[i].registerBApp( - tokensInput, - sharedRiskLevelInput, - metadataURIs[i] - ); + bApps[i].registerBApp(tokenConfigsInput, metadataURIs[i]); } } function testRevertRegisterBAppTwice() public { vm.startPrank(USER1); - address[] memory tokensInput = new address[](1); - tokensInput[0] = address(erc20mock); - uint32[] memory sharedRiskLevelInput = new uint32[](1); - sharedRiskLevelInput[0] = 102; - bApp1.registerBApp(tokensInput, sharedRiskLevelInput, ""); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(erc20mock), + 102 + ); + bApp1.registerBApp(tokenConfigsInput, ""); vm.expectRevert( abi.encodeWithSelector( IBasedAppManager.BAppAlreadyRegistered.selector ) ); - bApp1.registerBApp(tokensInput, sharedRiskLevelInput, ""); - bApp2.registerBApp(tokensInput, sharedRiskLevelInput, ""); + bApp1.registerBApp(tokenConfigsInput, ""); + bApp2.registerBApp(tokenConfigsInput, ""); vm.expectRevert( abi.encodeWithSelector( IBasedAppManager.BAppAlreadyRegistered.selector ) ); - bApp2.registerBApp(tokensInput, sharedRiskLevelInput, ""); - bApp3.registerBApp(tokensInput, sharedRiskLevelInput, ""); + bApp2.registerBApp(tokenConfigsInput, ""); + bApp3.registerBApp(tokenConfigsInput, ""); vm.expectRevert( abi.encodeWithSelector( IBasedAppManager.BAppAlreadyRegistered.selector ) ); - bApp3.registerBApp(tokensInput, sharedRiskLevelInput, ""); + bApp3.registerBApp(tokenConfigsInput, ""); vm.stopPrank(); } function testRegisterBAppFromNonBAppContract() public { vm.startPrank(USER1); - address[] memory tokensInput = new address[](1); - tokensInput[0] = address(erc20mock); - uint32[] memory sharedRiskLevelInput = new uint32[](1); - sharedRiskLevelInput[0] = 102; - nonCompliantBApp.registerBApp(tokensInput, sharedRiskLevelInput, ""); + ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig( + address(erc20mock), + 102 + ); + nonCompliantBApp.registerBApp(tokenConfigsInput, ""); checkBAppInfo( - tokensInput, - sharedRiskLevelInput, + tokenConfigsInput, address(nonCompliantBApp), proxiedManager ); vm.stopPrank(); } - function testRevertRegisterBAppWithMismatchTokenRiskLengthOne() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createTwoTokenAndMoreRiskInputs(); - for (uint256 i = 0; i < bApps.length; i++) { - vm.prank(USER1); - vm.expectRevert( - abi.encodeWithSelector( - ValidationLib.LengthsNotMatching.selector - ) - ); - bApps[i].registerBApp( - tokensInput, - sharedRiskLevelInput, - metadataURIs[i] - ); - } - } - - function testRevertRegisterBAppWithMismatchTokenRiskLengthTwo() public { - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createTwoTokenAndLessRiskInputs(); - for (uint256 i = 0; i < bApps.length; i++) { - vm.prank(USER1); - vm.expectRevert( - abi.encodeWithSelector( - ValidationLib.LengthsNotMatching.selector - ) - ); - bApps[i].registerBApp( - tokensInput, - sharedRiskLevelInput, - metadataURIs[i] - ); - } - } - function testUpdateBAppMetadata() public { testRegisterBApp(); for (uint256 i = 0; i < bApps.length; i++) { @@ -440,55 +332,28 @@ contract BasedAppsManagerTest is UtilsTest { } } - function testSupportInterface() public { - vm.startPrank(USER1); - for (uint256 i = 0; i < bApps.length; i++) { - bool success = bApps[i].supportsInterface( - type(IBasedApp).interfaceId - ); - assertEq(success, true, "supportsInterface based app"); - bool failed = bApps[i].supportsInterface( - type(IBasedAppManager).interfaceId - ); - assertEq( - failed, - false, - "does not supportsInterface based app manager" - ); - bool failed2 = bApps[i].supportsInterface(type(IERC20).interfaceId); - assertEq(failed2, false, "does not supportsInterface"); - bool success2 = bApps[i].supportsInterface( - type(IERC165).interfaceId - ); - assertEq(success2, true, "does supportsInterface of IERC165"); - } - vm.stopPrank(); - } - function testUpdateBAppTokens() public { testRegisterBApp(); - ( - address[] memory tokensInput, - uint32[] memory sharedRiskLevelInput - ) = createTwoTokenAndRiskInputs(); - ICore.TokenConfig[] memory tokenConfigs = new ICore.TokenConfig[]( - tokensInput.length - ); - for (uint256 i = 0; i < tokensInput.length; i++) { - tokenConfigs[i] = ICore.TokenConfig({ - token: tokensInput[i], - sharedRiskLevel: sharedRiskLevelInput[i] + 1000 - }); - } + ICore.TokenConfig[] + memory tokenConfigsInput = createTwoTokenAndRiskInputs(); + // ICore.TokenConfig[] memory tokenConfigs = new ICore.TokenConfig[]( + // tokensInput.length + // ); + // for (uint256 i = 0; i < tokensInput.length; i++) { + // tokenConfigs[i] = ICore.TokenConfig({ + // token: tokensInput[i], + // sharedRiskLevel: sharedRiskLevelInput[i] + 1000 + // }); + // } for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); vm.expectEmit(true, true, false, false); emit IBasedAppManager.BAppTokensUpdated( address(bApps[i]), - tokenConfigs + tokenConfigsInput ); - bApps[i].updateBAppTokens(tokenConfigs); - checkBAppUpdatedTokens(tokenConfigs, address(bApps[i])); + bApps[i].updateBAppTokens(tokenConfigsInput); + checkBAppUpdatedTokens(tokenConfigsInput, address(bApps[i])); } } } diff --git a/test/modules/DelegationManager.t.sol b/test/modules/DelegationManager.t.sol index e9326ddf..0a6f55e2 100644 --- a/test/modules/DelegationManager.t.sol +++ b/test/modules/DelegationManager.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Setup } from "@ssv/test/helpers/Setup.t.sol"; import { diff --git a/test/modules/ProtocolManager.t.sol b/test/modules/ProtocolManager.t.sol index fcf4674a..39da54f6 100644 --- a/test/modules/ProtocolManager.t.sol +++ b/test/modules/ProtocolManager.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { Ownable2StepUpgradeable @@ -20,6 +20,7 @@ import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import { SSVBasedApps } from "@ssv/src/core/SSVBasedApps.sol"; +import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; contract ProtocolManagerTest is Setup, Ownable2StepUpgradeable { function testUpdateFeeTimelockPeriod() public { @@ -110,7 +111,7 @@ contract ProtocolManagerTest is Setup, Ownable2StepUpgradeable { function testUpdateMaxShares() public { vm.prank(OWNER); - uint256 newValue = 1e18; + uint256 newValue = 1e38; proxiedManager.updateMaxShares(newValue); assertEq( proxiedManager.maxShares(), @@ -192,7 +193,7 @@ contract ProtocolManagerTest is Setup, Ownable2StepUpgradeable { address(ATTACKER) ) ); - proxiedManager.updateObligationExpireTime(1 days); + proxiedManager.updateObligationExpireTime(59 minutes); } function testRevertUpdateTokenUpdateTimelockPeriodWithNonOwner() public { @@ -344,4 +345,91 @@ contract ProtocolManagerTest is Setup, Ownable2StepUpgradeable { "feeTimelockPeriod should update despite flags" ); } + + function testRevertUpdateFeeTimelockPeriod() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidFeeTimelockPeriod.selector + ) + ); + proxiedManager.updateFeeTimelockPeriod(23 hours); + } + + function testRevertUpdateFeeExpireTime() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector(ISSVBasedApps.InvalidFeeExpireTime.selector) + ); + proxiedManager.updateFeeExpireTime(59 minutes); + } + + function testRevertUpdateWithdrawalTimelockPeriod() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidWithdrawalTimelockPeriod.selector + ) + ); + proxiedManager.updateWithdrawalTimelockPeriod(23 hours); + } + + function testRevertUpdateWithdrawalExpireTime() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidWithdrawalExpireTime.selector + ) + ); + proxiedManager.updateWithdrawalExpireTime(59 minutes); + } + + function testRevertUpdateObligationTimelockPeriod() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidObligationTimelockPeriod.selector + ) + ); + proxiedManager.updateObligationTimelockPeriod(23 hours); + } + + function testRevertUpdateObligationExpireTime() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidObligationExpireTime.selector + ) + ); + proxiedManager.updateObligationExpireTime(59 minutes); + } + + function testRevertUpdateTokenUpdateTimelockPeriod() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidTokenUpdateTimelockPeriod.selector + ) + ); + proxiedManager.updateTokenUpdateTimelockPeriod(23 hours); + } + + function testRevertUpdateMaxShares() public { + vm.prank(OWNER); + uint256 newValue = 1e37; + vm.expectRevert( + abi.encodeWithSelector(ISSVBasedApps.InvalidMaxShares.selector) + ); + proxiedManager.updateMaxShares(newValue); + } + + function testRevertUpdateMaxFeeIncrement() public { + vm.prank(OWNER); + vm.expectRevert( + abi.encodeWithSelector( + ISSVBasedApps.InvalidMaxFeeIncrement.selector + ) + ); + proxiedManager.updateMaxFeeIncrement(49); + } } diff --git a/test/modules/SlashingManager.bapp.t.sol b/test/modules/SlashingManager.bapp.t.sol index 1874b4ab..b5b544fc 100644 --- a/test/modules/SlashingManager.bapp.t.sol +++ b/test/modules/SlashingManager.bapp.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IERC20, @@ -915,4 +915,33 @@ contract SlashingManagerTest is StrategyManagerTest { ); bApp4.withdrawETHSlashingFund(1 ether); } + + function testRevertSlashStrategyNotOptedInToBApp() public { + uint256 depositAmount = 100_000; + uint32 slashPercentage = 100; + + testCreateStrategies(); + testRegisterBAppWith2Tokens(); + + vm.prank(USER2); + proxiedManager.depositERC20( + STRATEGY1, + IERC20(erc20mock), + depositAmount + ); + vm.prank(USER1); + vm.expectRevert( + abi.encodeWithSelector( + IStrategyManager.BAppNotOptedIn.selector, + STRATEGY1 + ) + ); + proxiedManager.slash( + STRATEGY1, + address(bApp1), + address(erc20mock), + slashPercentage, + abi.encodePacked("0x00") + ); + } } diff --git a/test/modules/SlashingManager.eoa.t.sol b/test/modules/SlashingManager.eoa.t.sol index b8f33a6d..0dbc34cc 100644 --- a/test/modules/SlashingManager.eoa.t.sol +++ b/test/modules/SlashingManager.eoa.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IERC20, @@ -250,7 +250,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { proxiedManager.withdrawETHSlashingFund(slashAmount); } - // Slash Non Compatible BApp + // Slash Non Compatible BApp, does not implement the slash function inside the contract function testSlashNonCompatibleBApp(uint32 slashPercentage) public { uint32 percentage = 9000; uint256 depositAmount = 100_000; @@ -259,9 +259,6 @@ contract SlashingManagerEOATest is StrategyManagerTest { slashPercentage > 0 && slashPercentage <= proxiedManager.maxPercentage() ); - uint256 slashAmount = (depositAmount * percentage * slashPercentage) / - proxiedManager.maxPercentage() / - proxiedManager.maxPercentage(); testStrategyOptInToBAppNonCompliant(percentage); vm.prank(USER2); proxiedManager.depositERC20( @@ -270,25 +267,8 @@ contract SlashingManagerEOATest is StrategyManagerTest { depositAmount ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); - emit IStrategyManager.StrategySlashed( - STRATEGY1, - address(nonCompliantBApp), - token, - slashPercentage, - address(nonCompliantBApp) - ); + vm.expectRevert(); nonCompliantBApp.slash(STRATEGY1, token, slashPercentage); - uint256 newStrategyBalance = depositAmount - slashAmount; - checkTotalSharesAndTotalBalance( - STRATEGY1, - token, - depositAmount, - newStrategyBalance - ); - checkAccountShares(STRATEGY1, USER2, token, depositAmount); - checkSlashableBalance(STRATEGY1, address(nonCompliantBApp), token, 0); - checkSlashingFund(address(nonCompliantBApp), token, slashAmount); } function testRevertSlashNonCompatibleBAppWithNonOwner() public { @@ -304,13 +284,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { IERC20(erc20mock), depositAmount ); - vm.expectRevert( - abi.encodeWithSelector( - IStrategyManager.InvalidBAppOwner.selector, - USER2, - address(nonCompliantBApp) - ) - ); + vm.expectRevert(); proxiedManager.slash( STRATEGY1, address(nonCompliantBApp), @@ -344,6 +318,35 @@ contract SlashingManagerEOATest is StrategyManagerTest { ); } + function testRevertSlashStrategyNotOptedInToEOA() public { + uint256 depositAmount = 100_000; + uint32 slashPercentage = 100; + + testCreateStrategies(); + testRegisterBAppWithEOA(); + + vm.prank(USER2); + proxiedManager.depositERC20( + STRATEGY1, + IERC20(erc20mock), + depositAmount + ); + vm.prank(USER1); + vm.expectRevert( + abi.encodeWithSelector( + IStrategyManager.BAppNotOptedIn.selector, + STRATEGY1 + ) + ); + proxiedManager.slash( + STRATEGY1, + USER1, + address(erc20mock), + slashPercentage, + abi.encodePacked("0x00") + ); + } + function testRevertWithdrawSlashingFundErc20WithEthEOA() public { uint256 slashAmount = 100; uint32 slashPercentage = 100; diff --git a/test/modules/StrategyManager.t.sol b/test/modules/StrategyManager.t.sol index 03a6de28..d96fe930 100644 --- a/test/modules/StrategyManager.t.sol +++ b/test/modules/StrategyManager.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; +pragma solidity 0.8.30; import { IERC20, BasedAppMock } from "@ssv/test/helpers/Setup.t.sol"; import { BasedAppsManagerTest } from "@ssv/test/modules/BasedAppsManager.t.sol"; @@ -7,10 +7,11 @@ import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; import { - IStrategyManager -} from "@ssv/src/core/interfaces/IStrategyManager.sol"; + IBasedAppManager +} from "@ssv/src/core/interfaces/IBasedAppManager.sol"; import { UtilsTest } from "@ssv/test/helpers/Utils.t.sol"; import { ValidationLib } from "@ssv/src/core/libraries/ValidationLib.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function updateObligation( @@ -669,8 +670,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { abi.encodePacked("0x00") ); checkBAppInfo( - new address[](0), - new uint32[](0), + new ICore.TokenConfig[](0), address(bApps[i]), proxiedManager ); @@ -1045,6 +1045,34 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { vm.stopPrank(); } + function testRevertStrategyOptingInToNonExistingBApp( + uint32 percentage + ) public { + vm.assume( + percentage > 0 && percentage <= proxiedManager.maxPercentage() + ); + testCreateStrategies(); + vm.startPrank(USER1); + address[] memory tokensInput = new address[](0); + uint32[] memory obligationPercentagesInput = new uint32[](0); + for (uint256 i = 0; i < bApps.length; i++) { + vm.expectRevert( + abi.encodeWithSelector( + IBasedAppManager.BAppNotRegistered.selector, + address(erc20mock2) + ) + ); + proxiedManager.optInToBApp( + STRATEGY1, + address(bApps[i]), + tokensInput, + obligationPercentagesInput, + abi.encodePacked("0x00") + ); + } + vm.stopPrank(); + } + function testStrategyOwnerDepositERC20WithNoObligation( uint256 amount ) public { @@ -1349,7 +1377,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { returns (uint32 proposedFee) { testStrategyOptInToBApp(9000); - proposedFee = 20; + proposedFee = 505; vm.prank(USER1); vm.expectEmit(true, true, true, true); emit IStrategyManager.StrategyFeeUpdateProposed(