diff --git a/.github/renovate.json b/.github/renovate.json index 21545acc..206d422b 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,6 +1,4 @@ { - "extends": [ - "github>ssvlabs/shared-configs//renovate/renovate.json" - ], - "baseBranches": ["develop"] + "extends": ["github>ssvlabs/shared-configs//renovate/renovate.json"], + "baseBranchPatterns": ["develop"] } \ No newline at end of file diff --git a/.github/workflows/foundry-gas-diff.yml b/.github/workflows/foundry-gas-diff.yml deleted file mode 100644 index 33ab66e1..00000000 --- a/.github/workflows/foundry-gas-diff.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Report gas diff - -on: - push: - branches: - - main - pull_request: - -jobs: - compare_gas_reports: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1 - with: - version: nightly - - # Add any step generating a gas report to a temporary file named gasreport.ansi. For example: - - name: Run tests - run: forge test --gas-report > gasreport.ansi # <- this file name should be unique in your repository! - env: - # make fuzzing semi-deterministic to avoid noisy gas cost estimation - # due to non-deterministic fuzzing (but still use pseudo-random fuzzing seeds) - FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }} - - - name: Compare gas reports - uses: Rubilmax/foundry-gas-diff@60e763d02526ee3299bd04278cb178d1547b134b # v3 - with: - summaryQuantile: 0.9 # only display the 10% most significant gas diffs in the summary (defaults to 20%) - sortCriteria: avg,max # sort diff rows by criteria - sortOrders: desc,asc # and directions - ignore: test-foundry/**/* # filter out gas reports from specific paths (test/ is included by default) - id: gas_diff - - - name: Add gas diff to sticky comment - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2 - with: - # delete the comment in case changes no longer impact gas costs - delete: ${{ !steps.gas_diff.outputs.markdown }} - message: ${{ steps.gas_diff.outputs.markdown }} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 5b17a995..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "solidity.compileUsingRemoteVersion": "v0.8.29+commit.ab55807c" -} \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 4e42a1bc..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book/ \ No newline at end of file diff --git a/docs/book.css b/docs/book.css deleted file mode 100644 index b5ce903f..00000000 --- a/docs/book.css +++ /dev/null @@ -1,13 +0,0 @@ -table { - margin: 0 auto; - border-collapse: collapse; - width: 100%; -} - -table td:first-child { - width: 15%; -} - -table td:nth-child(2) { - width: 25%; -} \ No newline at end of file diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index a2e2683a..00000000 --- a/docs/book.toml +++ /dev/null @@ -1,13 +0,0 @@ -[book] -src = "src" -title = "" - -[output.html] -no-section-label = true -additional-js = ["solidity.min.js"] -additional-css = ["book.css"] -mathjax-support = true -git-repository-url = "https://github.com/ssvlabs/based-applications" - -[output.html.fold] -enable = true diff --git a/docs/solidity.min.js b/docs/solidity.min.js deleted file mode 100644 index 19249329..00000000 --- a/docs/solidity.min.js +++ /dev/null @@ -1,74 +0,0 @@ -hljs.registerLanguage("solidity",(()=>{"use strict";function e(){try{return!0 -}catch(e){return!1}} -var a=/-?(\b0[xX]([a-fA-F0-9]_?)*[a-fA-F0-9]|(\b[1-9](_?\d)*(\.((\d_?)*\d)?)?|\.\d(_?\d)*)([eE][-+]?\d(_?\d)*)?|\b0)(?!\w|\$)/ -;e()&&(a=a.source.replace(/\\b/g,"(?{ -var a=r(e),o=l(e),c=/[A-Za-z_$][A-Za-z_$0-9.]*/,d=e.inherit(e.TITLE_MODE,{ -begin:/[A-Za-z$_][0-9A-Za-z$_]*/,lexemes:c,keywords:n}),u={className:"params", -begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,lexemes:c,keywords:n, -contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,o,s]},_={ -className:"operator",begin:/:=|->/};return{keywords:n,lexemes:c, -contains:[a,o,i,t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,_,{ -className:"function",lexemes:c,beginKeywords:"function",end:"{",excludeEnd:!0, -contains:[d,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,_]}]}}, -solAposStringMode:r,solQuoteStringMode:l,HEX_APOS_STRING_MODE:i, -HEX_QUOTE_STRING_MODE:t,SOL_NUMBER:s,isNegativeLookbehindAvailable:e} -;const{baseAssembly:c,solAposStringMode:d,solQuoteStringMode:u,HEX_APOS_STRING_MODE:_,HEX_QUOTE_STRING_MODE:m,SOL_NUMBER:b,isNegativeLookbehindAvailable:E}=o -;return e=>{for(var a=d(e),s=u(e),n=[],i=0;i<32;i++)n[i]=i+1 -;var t=n.map((e=>8*e)),r=[];for(i=0;i<=80;i++)r[i]=i -;var l=n.map((e=>"bytes"+e)).join(" ")+" ",o=t.map((e=>"uint"+e)).join(" ")+" ",g=t.map((e=>"int"+e)).join(" ")+" ",M=[].concat.apply([],t.map((e=>r.map((a=>e+"x"+a))))),p={ -keyword:"var bool string int uint "+g+o+"byte bytes "+l+"fixed ufixed "+M.map((e=>"fixed"+e)).join(" ")+" "+M.map((e=>"ufixed"+e)).join(" ")+" enum struct mapping address new delete if else for while continue break return throw emit try catch revert unchecked _ function modifier event constructor fallback receive error virtual override constant immutable anonymous indexed storage memory calldata external public internal payable pure view private returns import from as using pragma contract interface library is abstract type assembly", -literal:"true false wei gwei szabo finney ether seconds minutes hours days weeks years", -built_in:"self this super selfdestruct suicide now msg block tx abi blockhash gasleft assert require Error Panic sha3 sha256 keccak256 ripemd160 ecrecover addmod mulmod log0 log1 log2 log3 log4" -},O={className:"operator",begin:/[+\-!~*\/%<>&^|=]/ -},C=/[A-Za-z_$][A-Za-z_$0-9]*/,N={className:"params",begin:/\(/,end:/\)/, -excludeBegin:!0,excludeEnd:!0,lexemes:C,keywords:p, -contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,b,"self"]},f={ -begin:/\.\s*/,end:/[^A-Za-z0-9$_\.]/,excludeBegin:!0,excludeEnd:!0,keywords:{ -built_in:"gas value selector address length push pop send transfer call callcode delegatecall staticcall balance code codehash wrap unwrap name creationCode runtimeCode interfaceId min max" -},relevance:2},y=e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/, -lexemes:C,keywords:p}),w={className:"built_in", -begin:(E()?"(?`](https://holesky.etherscan.io/address/0x1Bd6ceB98Daf7FfEB590236b720F81b65213836A) | [``](https://holesky.etherscan.io/address/0x9a09A49870353867b0ce9901B44E84C32B2A47AC) | Proxy: [`UUPS@5.1.0`](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v5.1.0/contracts/proxy/utils/UUPSUpgradeable.sol) | - -  - -## :scroll: _License_ - -2025 SSV Network - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License, or any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the [GNU General Public License](LICENSE) -along with this program. If not, see . \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index 2a23b608..00000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -1,35 +0,0 @@ -# Summary -- [Home](README.md) -# src - - [❱ core](src/core/README.md) - - [❱ interfaces](src/core/interfaces/README.md) - - [IBasedAppManager](src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md) - - [ICore](src/core/interfaces/ICore.sol/interface.ICore.md) - - [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) - - [ProtocolStorageLib](src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md) - - [ValidationLib](src/core/libraries/ValidationLib.sol/library.ValidationLib.md) - - [ValidationLib constants](src/core/libraries/ValidationLib.sol/constants.ValidationLib.md) - - [❱ modules](src/core/modules/README.md) - - [BasedAppsManager](src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md) - - [ProtocolManager](src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md) - - [StrategyManager](src/core/modules/StrategyManager.sol/contract.StrategyManager.md) - - [SSVBasedApps](src/core/SSVBasedApps.sol/contract.SSVBasedApps.md) - - [❱ middleware](src/middleware/README.md) - - [❱ examples](src/middleware/examples/README.md) - - [WhitelistExample](src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md) - - [❱ interfaces](src/middleware/interfaces/README.md) - - [IBasedApp](src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md) - - [IBasedAppWhitelisted](src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md) - - [❱ modules](src/middleware/modules/README.md) - - [❱ core](src/middleware/modules/core/README.md) - - [BasedAppCore](src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md) - - [❱ core+roles](src/middleware/modules/core+roles/README.md) - - [AccessControlBasedApp](src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md) - - [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/README.md b/docs/src/src/README.md deleted file mode 100644 index 2c17bcbb..00000000 --- a/docs/src/src/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - -# Contents -- [core](/src/core) -- [middleware](/src/middleware) diff --git a/docs/src/src/core/README.md b/docs/src/src/core/README.md deleted file mode 100644 index 3d574836..00000000 --- a/docs/src/src/core/README.md +++ /dev/null @@ -1,7 +0,0 @@ - - -# Contents -- [interfaces](/src/core/interfaces) -- [libraries](/src/core/libraries) -- [modules](/src/core/modules) -- [SSVBasedApps](SSVBasedApps.sol/contract.SSVBasedApps.md) diff --git a/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md b/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md deleted file mode 100644 index 57b5d4f6..00000000 --- a/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md +++ /dev/null @@ -1,572 +0,0 @@ -# SSVBasedApps -[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 - -**Author:** - -Marco Tabasco -Riccardo Persiani - -The Core Contract to manage Based Applications, Validator Balance Delegations & Strategies for SSV Based Applications Platform. -GLOSSARY ** - -*The following terms are used throughout the contract: -- **Account**: An Ethereum address that can: -1. Delegate its balance to another address. -2. Create and manage a strategy. -3. Create and manage or be a bApp. -- **Based Application**: or bApp. -The entity that requests validation services from operators. On-chain is represented by an Ethereum address. -A bApp can be created by registering to this Core Contract, specifying the risk level. -The bApp can also specify one or many tokens as slashable capital to be provided by strategies. -During the bApp registration, the bApp owner can set the shared risk level and optionally a metadata URI, to be used in the SSV bApp marketplace. -- **Delegator**: An Ethereum address that has Ethereum Validator Balance of Staked ETH within the SSV platform. This capital delegated is non-slashable. -The delegator can decide to delegate its balance to itself or/and to a single or many receiver accounts. -The delegator has to set its address as the receiver account, when the delegator wants to delegate its balance to itself. -The delegated balance goes to an account and not to a strategy. This receiver account can manage only a single strategy. -- **Strategy**: The entity that manages the slashable assets bounded to based apps. -The strategy has its own balance, accounted in this core contract. -The strategy can be created by an account that becomes its owner. -The assets can be ERC20 tokens or Native ETH tokens, that can be deposited or withdrawn by the participants. -The strategy can manage its assets via s.obligations to one or many bApps. -- **Obligation**: A percentage of the strategy's balance of ERC20 (or Native ETH), that is reserved for securing a bApp. -The obligation is set exclusively by the strategy owner and can be updated by the strategy owner. -The tokens specified in an obligation needs to match the tokens specified in the bApp. -AUTHORS *** - - -## Functions -### initialize - - -```solidity -function initialize( - address owner_, - IBasedAppManager ssvBasedAppManger_, - IStrategyManager ssvStrategyManager_, - IProtocolManager protocolManager_, - ProtocolStorageLib.Data calldata config -) external override initializer onlyProxy; -``` - -### __SSVBasedApplications_init_unchained - - -```solidity -function __SSVBasedApplications_init_unchained( - IBasedAppManager ssvBasedAppManger_, - IStrategyManager ssvStrategyManager_, - IProtocolManager protocolManager_, - ProtocolStorageLib.Data calldata config -) internal onlyInitializing; -``` - -### constructor - -**Note:** -oz-upgrades-unsafe-allow: constructor - - -```solidity -constructor(); -``` - -### _authorizeUpgrade - - -```solidity -function _authorizeUpgrade(address) internal override onlyOwner; -``` - -### updateBAppMetadataURI - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external; -``` - -### registerBApp - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; -``` - -### updateBAppsTokens - - -```solidity -function updateBAppsTokens(ICore.TokenConfig[] calldata tokenConfigs) external; -``` - -### createObligation - - -```solidity -function createObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) external; -``` - -### createStrategy - - -```solidity -function createStrategy(uint32 fee, string calldata metadataURI) external returns (uint32 strategyId); -``` - -### delegateBalance - - -```solidity -function delegateBalance(address receiver, uint32 percentage) external; -``` - -### depositERC20 - - -```solidity -function depositERC20(uint32 strategyId, IERC20 token, uint256 amount) external; -``` - -### depositETH - - -```solidity -function depositETH(uint32 strategyId) external payable; -``` - -### finalizeFeeUpdate - - -```solidity -function finalizeFeeUpdate(uint32 strategyId) external; -``` - -### finalizeUpdateObligation - - -```solidity -function finalizeUpdateObligation(uint32 strategyId, address bApp, address token) external; -``` - -### finalizeWithdrawal - - -```solidity -function finalizeWithdrawal(uint32 strategyId, IERC20 token) external; -``` - -### finalizeWithdrawalETH - - -```solidity -function finalizeWithdrawalETH(uint32 strategyId) external; -``` - -### getSlashableBalance - - -```solidity -function getSlashableBalance(uint32 strategyId, address bApp, address token) public view returns (uint256 slashableBalance); -``` - -### proposeFeeUpdate - - -```solidity -function proposeFeeUpdate(uint32 strategyId, uint32 proposedFee) external; -``` - -### proposeUpdateObligation - - -```solidity -function proposeUpdateObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) external; -``` - -### proposeWithdrawal - - -```solidity -function proposeWithdrawal(uint32 strategyId, address token, uint256 amount) external; -``` - -### proposeWithdrawalETH - - -```solidity -function proposeWithdrawalETH(uint32 strategyId, uint256 amount) external; -``` - -### reduceFee - - -```solidity -function reduceFee(uint32 strategyId, uint32 proposedFee) external; -``` - -### removeDelegatedBalance - - -```solidity -function removeDelegatedBalance(address receiver) external; -``` - -### updateDelegatedBalance - - -```solidity -function updateDelegatedBalance(address receiver, uint32 percentage) external; -``` - -### updateStrategyMetadataURI - - -```solidity -function updateStrategyMetadataURI(uint32 strategyId, string calldata metadataURI) external; -``` - -### updateAccountMetadataURI - - -```solidity -function updateAccountMetadataURI(string calldata metadataURI) external; -``` - -### slash - - -```solidity -function slash(uint32 strategyId, address bApp, address token, uint32 percentage, bytes calldata data) external; -``` - -### withdrawSlashingFund - - -```solidity -function withdrawSlashingFund(address token, uint256 amount) external; -``` - -### withdrawETHSlashingFund - - -```solidity -function withdrawETHSlashingFund(uint256 amount) external; -``` - -### optInToBApp - - -```solidity -function optInToBApp(uint32 strategyId, address bApp, address[] calldata tokens, uint32[] calldata obligationPercentages, bytes calldata data) external; -``` - -### updateFeeTimelockPeriod - - -```solidity -function updateFeeTimelockPeriod(uint32 value) external onlyOwner; -``` - -### updateFeeExpireTime - - -```solidity -function updateFeeExpireTime(uint32 value) external onlyOwner; -``` - -### updateWithdrawalTimelockPeriod - - -```solidity -function updateWithdrawalTimelockPeriod(uint32 value) external onlyOwner; -``` - -### updateWithdrawalExpireTime - - -```solidity -function updateWithdrawalExpireTime(uint32 value) external onlyOwner; -``` - -### updateObligationTimelockPeriod - - -```solidity -function updateObligationTimelockPeriod(uint32 value) external onlyOwner; -``` - -### updateObligationExpireTime - - -```solidity -function updateObligationExpireTime(uint32 value) external onlyOwner; -``` - -### updateTokenUpdateTimelockPeriod - - -```solidity -function updateTokenUpdateTimelockPeriod(uint32 value) external onlyOwner; -``` - -### updateMaxShares - - -```solidity -function updateMaxShares(uint256 value) external onlyOwner; -``` - -### updateMaxFeeIncrement - - -```solidity -function updateMaxFeeIncrement(uint32 value) external onlyOwner; -``` - -### updateDisabledFeatures - - -```solidity -function updateDisabledFeatures(uint32 value) external onlyOwner; -``` - -### 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); -``` - -### getModuleAddress - -Retrieves the currently configured Module contract address. - - -```solidity -function getModuleAddress(SSVCoreModules moduleId) external view returns (address); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`moduleId`|`SSVCoreModules`|The ID of the SSV Module.| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`address`|The address of the SSV Module.| - - -### updateModule - - -```solidity -function updateModule(SSVCoreModules[] calldata moduleIds, address[] calldata moduleAddresses) external onlyOwner; -``` - -### _delegateTo - - -```solidity -function _delegateTo(SSVCoreModules moduleId) internal; -``` - diff --git a/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md b/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md deleted file mode 100644 index 4fb15889..00000000 --- a/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md +++ /dev/null @@ -1,76 +0,0 @@ -# IBasedAppManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IBasedAppManager.sol) - - -## Functions -### registerBApp - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; -``` - -### updateBAppMetadataURI - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external; -``` - -### updateBAppsTokens - - -```solidity -function updateBAppsTokens(ICore.TokenConfig[] calldata tokenConfigs) external; -``` - -## Events -### BAppMetadataURIUpdated - -```solidity -event BAppMetadataURIUpdated(address indexed bApp, string metadataURI); -``` - -### BAppRegistered - -```solidity -event BAppRegistered(address indexed bApp, ICore.TokenConfig[] tokenConfigs, string metadataURI); -``` - -### BAppTokensUpdated - -```solidity -event BAppTokensUpdated(address indexed bApp, ICore.TokenConfig[] tokenConfigs); -``` - -## Errors -### BAppAlreadyRegistered - -```solidity -error BAppAlreadyRegistered(); -``` - -### BAppDoesNotSupportInterface - -```solidity -error BAppDoesNotSupportInterface(); -``` - -### BAppNotRegistered - -```solidity -error BAppNotRegistered(); -``` - -### TokenAlreadyAddedToBApp - -```solidity -error TokenAlreadyAddedToBApp(address token); -``` - -### ZeroAddressNotAllowed - -```solidity -error ZeroAddressNotAllowed(); -``` - diff --git a/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md b/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md deleted file mode 100644 index 24ae6f6e..00000000 --- a/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md +++ /dev/null @@ -1,105 +0,0 @@ -# ICore -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/ICore.sol) - - -## Structs -### SharedRiskLevel -Represents a SharedRiskLevel - - -```solidity -struct SharedRiskLevel { - uint32 currentValue; - bool isSet; - uint32 pendingValue; - uint32 effectTime; -} -``` - -### Obligation -Represents an Obligation - - -```solidity -struct Obligation { - uint32 percentage; - bool isSet; -} -``` - -### Strategy -Represents a Strategy - - -```solidity -struct Strategy { - address owner; - uint32 fee; -} -``` - -### FeeUpdateRequest -Represents a FeeUpdateRequest - - -```solidity -struct FeeUpdateRequest { - uint32 percentage; - uint32 requestTime; -} -``` - -### WithdrawalRequest -Represents a request for a withdrawal from a participant of a strategy - - -```solidity -struct WithdrawalRequest { - uint256 shares; - uint32 requestTime; -} -``` - -### ObligationRequest -Represents a change in the obligation in a strategy. Only the owner can submit one. - - -```solidity -struct ObligationRequest { - uint32 percentage; - uint32 requestTime; -} -``` - -### Shares -Represents the shares system of a strategy - - -```solidity -struct Shares { - uint256 totalTokenBalance; - uint256 totalShareBalance; - uint256 currentGeneration; - mapping(address => uint256) accountShareBalance; - mapping(address => uint256) accountGeneration; -} -``` - -### TokenUpdateRequest - -```solidity -struct TokenUpdateRequest { - TokenConfig[] tokens; - uint32 requestTime; -} -``` - -### TokenConfig - -```solidity -struct TokenConfig { - address token; - uint32 sharedRiskLevel; -} -``` - diff --git a/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md b/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md deleted file mode 100644 index bb7fa5b1..00000000 --- a/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md +++ /dev/null @@ -1,129 +0,0 @@ -# IProtocolManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IProtocolManager.sol) - - -## Functions -### updateFeeExpireTime - - -```solidity -function updateFeeExpireTime(uint32 value) external; -``` - -### updateFeeTimelockPeriod - - -```solidity -function updateFeeTimelockPeriod(uint32 value) external; -``` - -### updateMaxFeeIncrement - - -```solidity -function updateMaxFeeIncrement(uint32 value) external; -``` - -### updateMaxShares - - -```solidity -function updateMaxShares(uint256 value) external; -``` - -### updateObligationExpireTime - - -```solidity -function updateObligationExpireTime(uint32 value) external; -``` - -### updateObligationTimelockPeriod - - -```solidity -function updateObligationTimelockPeriod(uint32 value) external; -``` - -### updateTokenUpdateTimelockPeriod - - -```solidity -function updateTokenUpdateTimelockPeriod(uint32 value) external; -``` - -### updateWithdrawalExpireTime - - -```solidity -function updateWithdrawalExpireTime(uint32 value) external; -``` - -### updateWithdrawalTimelockPeriod - - -```solidity -function updateWithdrawalTimelockPeriod(uint32 value) external; -``` - -## Events -### FeeExpireTimeUpdated - -```solidity -event FeeExpireTimeUpdated(uint32 feeExpireTime); -``` - -### FeeTimelockPeriodUpdated - -```solidity -event FeeTimelockPeriodUpdated(uint32 feeTimelockPeriod); -``` - -### ObligationExpireTimeUpdated - -```solidity -event ObligationExpireTimeUpdated(uint32 obligationExpireTime); -``` - -### ObligationTimelockPeriodUpdated - -```solidity -event ObligationTimelockPeriodUpdated(uint32 obligationTimelockPeriod); -``` - -### TokenUpdateTimelockPeriodUpdated - -```solidity -event TokenUpdateTimelockPeriodUpdated(uint32 tokenUpdateTimelockPeriod); -``` - -### StrategyMaxFeeIncrementUpdated - -```solidity -event StrategyMaxFeeIncrementUpdated(uint32 maxFeeIncrement); -``` - -### StrategyMaxSharesUpdated - -```solidity -event StrategyMaxSharesUpdated(uint256 maxShares); -``` - -### WithdrawalExpireTimeUpdated - -```solidity -event WithdrawalExpireTimeUpdated(uint32 withdrawalExpireTime); -``` - -### WithdrawalTimelockPeriodUpdated - -```solidity -event WithdrawalTimelockPeriodUpdated(uint32 withdrawalTimelockPeriod); -``` - -### DisabledFeaturesUpdated - -```solidity -event DisabledFeaturesUpdated(uint32 disabledFeatures); -``` - diff --git a/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md b/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md deleted file mode 100644 index ca7e186e..00000000 --- a/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md +++ /dev/null @@ -1,109 +0,0 @@ -# ISSVBasedApps -[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 -### getModuleAddress - - -```solidity -function getModuleAddress(SSVCoreModules moduleId) external view returns (address); -``` - -### initialize - - -```solidity -function initialize( - address owner_, - IBasedAppManager ssvBasedAppManger_, - IStrategyManager ssvStrategyManager_, - IProtocolManager protocolManager_, - ProtocolStorageLib.Data memory config -) external; -``` - -### updateModule - - -```solidity -function updateModule(SSVCoreModules[] calldata moduleIds, address[] calldata moduleAddresses) external; -``` - -## Events -### ModuleUpdated - -```solidity -event ModuleUpdated(SSVCoreModules indexed moduleId, address moduleAddress); -``` - -## Errors -### InvalidMaxFeeIncrement - -```solidity -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 -error TargetModuleDoesNotExist(uint8 moduleId); -``` - diff --git a/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md b/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md deleted file mode 100644 index e77b31e8..00000000 --- a/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md +++ /dev/null @@ -1,449 +0,0 @@ -# IStrategyManager -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/interfaces/IStrategyManager.sol) - - -## Functions -### createObligation - - -```solidity -function createObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) external; -``` - -### createStrategy - - -```solidity -function createStrategy(uint32 fee, string calldata metadataURI) external returns (uint32 strategyId); -``` - -### delegateBalance - - -```solidity -function delegateBalance(address receiver, uint32 percentage) external; -``` - -### depositERC20 - - -```solidity -function depositERC20(uint32 strategyId, IERC20 token, uint256 amount) external; -``` - -### depositETH - - -```solidity -function depositETH(uint32 strategyId) external payable; -``` - -### finalizeFeeUpdate - - -```solidity -function finalizeFeeUpdate(uint32 strategyId) external; -``` - -### finalizeUpdateObligation - - -```solidity -function finalizeUpdateObligation(uint32 strategyId, address bApp, address token) external; -``` - -### finalizeWithdrawal - - -```solidity -function finalizeWithdrawal(uint32 strategyId, IERC20 token) external; -``` - -### finalizeWithdrawalETH - - -```solidity -function finalizeWithdrawalETH(uint32 strategyId) external; -``` - -### optInToBApp - - -```solidity -function optInToBApp(uint32 strategyId, address bApp, address[] calldata tokens, uint32[] calldata obligationPercentages, bytes calldata data) external; -``` - -### proposeFeeUpdate - - -```solidity -function proposeFeeUpdate(uint32 strategyId, uint32 proposedFee) external; -``` - -### proposeUpdateObligation - - -```solidity -function proposeUpdateObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) external; -``` - -### proposeWithdrawal - - -```solidity -function proposeWithdrawal(uint32 strategyId, address token, uint256 amount) external; -``` - -### proposeWithdrawalETH - - -```solidity -function proposeWithdrawalETH(uint32 strategyId, uint256 amount) external; -``` - -### reduceFee - - -```solidity -function reduceFee(uint32 strategyId, uint32 proposedFee) external; -``` - -### removeDelegatedBalance - - -```solidity -function removeDelegatedBalance(address receiver) external; -``` - -### slash - - -```solidity -function slash(uint32 strategyId, address bApp, address token, uint32 percentage, bytes calldata data) external; -``` - -### updateAccountMetadataURI - - -```solidity -function updateAccountMetadataURI(string calldata metadataURI) external; -``` - -### updateDelegatedBalance - - -```solidity -function updateDelegatedBalance(address receiver, uint32 percentage) external; -``` - -### updateStrategyMetadataURI - - -```solidity -function updateStrategyMetadataURI(uint32 strategyId, string calldata metadataURI) external; -``` - -### withdrawETHSlashingFund - - -```solidity -function withdrawETHSlashingFund(uint256 amount) external; -``` - -### withdrawSlashingFund - - -```solidity -function withdrawSlashingFund(address token, uint256 amount) external; -``` - -## Events -### AccountMetadataURIUpdated - -```solidity -event AccountMetadataURIUpdated(address indexed account, string metadataURI); -``` - -### BAppOptedInByStrategy - -```solidity -event BAppOptedInByStrategy(uint32 indexed strategyId, address indexed bApp, bytes data, address[] tokens, uint32[] obligationPercentages); -``` - -### DelegationCreated - -```solidity -event DelegationCreated(address indexed delegator, address indexed receiver, uint32 percentage); -``` - -### DelegationRemoved - -```solidity -event DelegationRemoved(address indexed delegator, address indexed receiver); -``` - -### DelegationUpdated - -```solidity -event DelegationUpdated(address indexed delegator, address indexed receiver, uint32 percentage); -``` - -### MaxFeeIncrementSet - -```solidity -event MaxFeeIncrementSet(uint32 newMaxFeeIncrement); -``` - -### ObligationCreated - -```solidity -event ObligationCreated(uint32 indexed strategyId, address indexed bApp, address token, uint32 percentage); -``` - -### ObligationUpdated - -```solidity -event ObligationUpdated(uint32 indexed strategyId, address indexed bApp, address token, uint32 percentage); -``` - -### ObligationUpdateProposed - -```solidity -event ObligationUpdateProposed(uint32 indexed strategyId, address indexed bApp, address token, uint32 percentage); -``` - -### StrategyCreated - -```solidity -event StrategyCreated(uint32 indexed strategyId, address indexed owner, uint32 fee, string metadataURI); -``` - -### StrategyDeposit - -```solidity -event StrategyDeposit(uint32 indexed strategyId, address indexed account, address token, uint256 amount); -``` - -### StrategyFeeUpdated - -```solidity -event StrategyFeeUpdated(uint32 indexed strategyId, address owner, uint32 newFee, bool isFast); -``` - -### StrategyFeeUpdateProposed - -```solidity -event StrategyFeeUpdateProposed(uint32 indexed strategyId, address owner, uint32 proposedFee); -``` - -### StrategyMetadataURIUpdated - -```solidity -event StrategyMetadataURIUpdated(uint32 indexed strategyId, string metadataURI); -``` - -### StrategyWithdrawal - -```solidity -event StrategyWithdrawal(uint32 indexed strategyId, address indexed account, address token, uint256 amount, bool isFast); -``` - -### StrategyWithdrawalProposed - -```solidity -event StrategyWithdrawalProposed(uint32 indexed strategyId, address indexed account, address token, uint256 amount); -``` - -### SlashingFundWithdrawn - -```solidity -event SlashingFundWithdrawn(address token, uint256 amount); -``` - -### StrategySlashed - -```solidity -event StrategySlashed(uint32 indexed strategyId, address indexed bApp, address token, uint32 percentage, address receiver); -``` - -## Errors -### BAppAlreadyOptedIn - -```solidity -error BAppAlreadyOptedIn(); -``` - -### BAppNotOptedIn - -```solidity -error BAppNotOptedIn(); -``` - -### BAppOptInFailed - -```solidity -error BAppOptInFailed(); -``` - -### BAppSlashingFailed - -```solidity -error BAppSlashingFailed(); -``` - -### DelegationAlreadyExists - -```solidity -error DelegationAlreadyExists(); -``` - -### DelegationDoesNotExist - -```solidity -error DelegationDoesNotExist(); -``` - -### DelegationExistsWithSameValue - -```solidity -error DelegationExistsWithSameValue(); -``` - -### ExceedingMaxShares - -```solidity -error ExceedingMaxShares(); -``` - -### ExceedingPercentageUpdate - -```solidity -error ExceedingPercentageUpdate(); -``` - -### FeeAlreadySet - -```solidity -error FeeAlreadySet(); -``` - -### InsufficientBalance - -```solidity -error InsufficientBalance(); -``` - -### InsufficientLiquidity - -```solidity -error InsufficientLiquidity(); -``` - -### InvalidAccountGeneration - -```solidity -error InvalidAccountGeneration(); -``` - -### InvalidAmount - -```solidity -error InvalidAmount(); -``` - -### InvalidBAppOwner - -```solidity -error InvalidBAppOwner(address caller, address expectedOwner); -``` - -### InvalidPercentageIncrement - -```solidity -error InvalidPercentageIncrement(); -``` - -### InvalidStrategyFee - -```solidity -error InvalidStrategyFee(); -``` - -### InvalidStrategyOwner - -```solidity -error InvalidStrategyOwner(address caller, address expectedOwner); -``` - -### InvalidToken - -```solidity -error InvalidToken(); -``` - -### NoPendingFeeUpdate - -```solidity -error NoPendingFeeUpdate(); -``` - -### NoPendingObligationUpdate - -```solidity -error NoPendingObligationUpdate(); -``` - -### NoPendingWithdrawal - -```solidity -error NoPendingWithdrawal(); -``` - -### ObligationAlreadySet - -```solidity -error ObligationAlreadySet(); -``` - -### ObligationHasNotBeenCreated - -```solidity -error ObligationHasNotBeenCreated(); -``` - -### RequestTimeExpired - -```solidity -error RequestTimeExpired(); -``` - -### SlashingDisabled - -```solidity -error SlashingDisabled(); -``` - -### TimelockNotElapsed - -```solidity -error TimelockNotElapsed(); -``` - -### TokenNotSupportedByBApp - -```solidity -error TokenNotSupportedByBApp(address token); -``` - -### WithdrawTransferFailed - -```solidity -error WithdrawTransferFailed(); -``` - -### WithdrawalsDisabled - -```solidity -error WithdrawalsDisabled(); -``` - diff --git a/docs/src/src/core/interfaces/IViews.sol/interface.IViews.md b/docs/src/src/core/interfaces/IViews.sol/interface.IViews.md deleted file mode 100644 index a50a20aa..00000000 --- a/docs/src/src/core/interfaces/IViews.sol/interface.IViews.md +++ /dev/null @@ -1,208 +0,0 @@ -# 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 deleted file mode 100644 index 0593724a..00000000 --- a/docs/src/src/core/interfaces/README.md +++ /dev/null @@ -1,9 +0,0 @@ - - -# Contents -- [IBasedAppManager](IBasedAppManager.sol/interface.IBasedAppManager.md) -- [ICore](ICore.sol/interface.ICore.md) -- [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 deleted file mode 100644 index 68a026db..00000000 --- a/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md +++ /dev/null @@ -1,12 +0,0 @@ -# SSVCoreModules -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/CoreStorageLib.sol) - - -```solidity -enum SSVCoreModules { - SSV_PROTOCOL_MANAGER, - SSV_BAPPS_MANAGER, - SSV_STRATEGY_MANAGER -} -``` - diff --git a/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md b/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md deleted file mode 100644 index cfb1ab50..00000000 --- a/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md +++ /dev/null @@ -1,46 +0,0 @@ -# CoreStorageLib -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/CoreStorageLib.sol) - - -## State Variables -### SSV_BASED_APPS_STORAGE_POSITION - -```solidity -uint256 private constant SSV_BASED_APPS_STORAGE_POSITION = uint256(keccak256("ssv.based-apps.storage.main")) - 1; -``` - - -## Functions -### load - - -```solidity -function load() internal pure returns (Data storage sd); -``` - -## Structs -### Data -Represents all operational state required by the SSV Based Application platform. - - -```solidity -struct Data { - uint32 _strategyCounter; - mapping(SSVCoreModules => address) ssvContracts; - mapping(uint32 strategyId => ICore.Strategy) strategies; - mapping(address owner => uint32[] strategyId) strategyOwners; - mapping(address account => mapping(address bApp => uint32 strategyId)) accountBAppStrategy; - mapping(address delegator => mapping(address account => uint32 percentage)) delegations; - mapping(address delegator => uint32 totalPercentage) totalDelegatedPercentage; - mapping(uint32 strategyId => mapping(address token => ICore.Shares shares)) strategyTokenShares; - mapping(uint32 strategyId => mapping(address bApp => mapping(address token => ICore.Obligation))) obligations; - mapping(uint32 strategyId => mapping(address account => mapping(address token => ICore.WithdrawalRequest))) withdrawalRequests; - mapping(uint32 strategyId => mapping(address token => mapping(address bApp => ICore.ObligationRequest))) obligationRequests; - mapping(uint32 strategyId => ICore.FeeUpdateRequest) feeUpdateRequests; - mapping(address account => mapping(address token => uint256 amount)) slashingFund; - mapping(address bApp => bool isRegistered) registeredBApps; - mapping(address bApp => mapping(address token => ICore.SharedRiskLevel)) bAppTokens; - mapping(address bApp => ICore.TokenUpdateRequest) tokenUpdateRequests; -} -``` - diff --git a/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md b/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md deleted file mode 100644 index bce8a21b..00000000 --- a/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md +++ /dev/null @@ -1,40 +0,0 @@ -# ProtocolStorageLib -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/ProtocolStorageLib.sol) - - -## State Variables -### SSV_STORAGE_POSITION - -```solidity -uint256 private constant SSV_STORAGE_POSITION = uint256(keccak256("ssv.based-apps.storage.protocol")) - 1; -``` - - -## Functions -### load - - -```solidity -function load() internal pure returns (Data storage sd); -``` - -## Structs -### Data -Represents the operational settings and parameters required by the SSV Based Application Platform - - -```solidity -struct Data { - uint256 maxShares; - uint32 feeTimelockPeriod; - uint32 feeExpireTime; - uint32 withdrawalTimelockPeriod; - uint32 withdrawalExpireTime; - uint32 obligationTimelockPeriod; - uint32 obligationExpireTime; - uint32 tokenUpdateTimelockPeriod; - uint32 maxFeeIncrement; - uint32 disabledFeatures; -} -``` - diff --git a/docs/src/src/core/libraries/README.md b/docs/src/src/core/libraries/README.md deleted file mode 100644 index 3eba618a..00000000 --- a/docs/src/src/core/libraries/README.md +++ /dev/null @@ -1,8 +0,0 @@ - - -# Contents -- [SSVCoreModules](CoreStorageLib.sol/enum.SSVCoreModules.md) -- [CoreStorageLib](CoreStorageLib.sol/library.CoreStorageLib.md) -- [ProtocolStorageLib](ProtocolStorageLib.sol/library.ProtocolStorageLib.md) -- [ValidationLib](ValidationLib.sol/library.ValidationLib.md) -- [ValidationLib constants](ValidationLib.sol/constants.ValidationLib.md) diff --git a/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md b/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md deleted file mode 100644 index a39eaf59..00000000 --- a/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md +++ /dev/null @@ -1,27 +0,0 @@ -# Constants -[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 - -```solidity -uint32 constant MAX_PERCENTAGE = 1e4; -``` - -### ETH_ADDRESS - -```solidity -address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; -``` - diff --git a/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md b/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md deleted file mode 100644 index 0440a706..00000000 --- a/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md +++ /dev/null @@ -1,52 +0,0 @@ -# ValidationLib -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/core/libraries/ValidationLib.sol) - - -## Functions -### validatePercentage - - -```solidity -function validatePercentage(uint32 percentage) internal pure; -``` - -### validatePercentageAndNonZero - - -```solidity -function validatePercentageAndNonZero(uint32 percentage) internal pure; -``` - -### validateArrayLengths - - -```solidity -function validateArrayLengths(address[] calldata tokens, uint32[] memory values) internal pure; -``` - -### validateNonZeroAddress - - -```solidity -function validateNonZeroAddress(address addr) internal pure; -``` - -## Errors -### InvalidPercentage - -```solidity -error InvalidPercentage(); -``` - -### LengthsNotMatching - -```solidity -error LengthsNotMatching(); -``` - -### ZeroAddressNotAllowed - -```solidity -error ZeroAddressNotAllowed(); -``` - diff --git a/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md b/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md deleted file mode 100644 index e31ce82a..00000000 --- a/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md +++ /dev/null @@ -1,73 +0,0 @@ -# BasedAppsManager -[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) - - -## Functions -### _onlyRegisteredBApp - -Allow the function to be called only by a registered bApp - - -```solidity -function _onlyRegisteredBApp(CoreStorageLib.Data storage s) private view; -``` - -### registerBApp - -Registers a bApp. - -*Allows creating a bApp even with an empty token list.* - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`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.| - - -### updateBAppMetadataURI - -Function to update the metadata URI of the Based Application - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`metadataURI`|`string`|The new metadata URI| - - -### updateBAppsTokens - - -```solidity -function updateBAppsTokens(ICore.TokenConfig[] calldata tokenConfigs) external; -``` - -### _addNewTokens - -Function to add tokens to a bApp - - -```solidity -function _addNewTokens(address bApp, ICore.TokenConfig[] calldata tokenConfigs) internal; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`bApp`|`address`|The address of the bApp| -|`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 deleted file mode 100644 index 8d080770..00000000 --- a/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md +++ /dev/null @@ -1,93 +0,0 @@ -# ProtocolManager -[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) - - -## State Variables -### SLASHING_DISABLED - -```solidity -uint32 private constant SLASHING_DISABLED = 1 << 0; -``` - - -### WITHDRAWALS_DISABLED - -```solidity -uint32 private constant WITHDRAWALS_DISABLED = 1 << 1; -``` - - -## Functions -### updateFeeTimelockPeriod - - -```solidity -function updateFeeTimelockPeriod(uint32 feeTimelockPeriod) external; -``` - -### updateFeeExpireTime - - -```solidity -function updateFeeExpireTime(uint32 feeExpireTime) external; -``` - -### updateWithdrawalTimelockPeriod - - -```solidity -function updateWithdrawalTimelockPeriod(uint32 withdrawalTimelockPeriod) external; -``` - -### updateWithdrawalExpireTime - - -```solidity -function updateWithdrawalExpireTime(uint32 withdrawalExpireTime) external; -``` - -### updateObligationTimelockPeriod - - -```solidity -function updateObligationTimelockPeriod(uint32 obligationTimelockPeriod) external; -``` - -### updateObligationExpireTime - - -```solidity -function updateObligationExpireTime(uint32 obligationExpireTime) external; -``` - -### updateTokenUpdateTimelockPeriod - - -```solidity -function updateTokenUpdateTimelockPeriod(uint32 tokenUpdateTimelockPeriod) external; -``` - -### updateMaxShares - - -```solidity -function updateMaxShares(uint256 maxShares) external; -``` - -### updateMaxFeeIncrement - - -```solidity -function updateMaxFeeIncrement(uint32 maxFeeIncrement) external; -``` - -### updateDisabledFeatures - - -```solidity -function updateDisabledFeatures(uint32 disabledFeatures) external; -``` - diff --git a/docs/src/src/core/modules/README.md b/docs/src/src/core/modules/README.md deleted file mode 100644 index 532896ac..00000000 --- a/docs/src/src/core/modules/README.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Contents -- [BasedAppsManager](BasedAppsManager.sol/contract.BasedAppsManager.md) -- [ProtocolManager](ProtocolManager.sol/contract.ProtocolManager.md) -- [StrategyManager](StrategyManager.sol/contract.StrategyManager.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 deleted file mode 100644 index 15c37bd1..00000000 --- a/docs/src/src/core/modules/StrategyManager.sol/contract.StrategyManager.md +++ /dev/null @@ -1,611 +0,0 @@ -# StrategyManager -[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) - - -## State Variables -### SLASHING_DISABLED - -```solidity -uint32 private constant SLASHING_DISABLED = 1 << 0; -``` - - -### WITHDRAWALS_DISABLED - -```solidity -uint32 private constant WITHDRAWALS_DISABLED = 1 << 1; -``` - - -## Functions -### _onlyStrategyOwner - -Checks if the caller is the strategy owner - - -```solidity -function _onlyStrategyOwner(uint32 strategyId, CoreStorageLib.Data storage s) private view; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`s`|`CoreStorageLib.Data`|The CoreStorageLib data| - - -### updateAccountMetadataURI - -Function to update the metadata URI of the Account - - -```solidity -function updateAccountMetadataURI(string calldata metadataURI) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`metadataURI`|`string`|The new metadata URI| - - -### delegateBalance - -Function to delegate a percentage of the account's balance to another account - -*The percentage is scaled by 1e4 so the minimum unit is 0.01%* - - -```solidity -function delegateBalance(address account, uint32 percentage) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`account`|`address`|The address of the account to delegate to| -|`percentage`|`uint32`|The percentage of the account's balance to delegate| - - -### updateDelegatedBalance - -Function to update the delegated validator balance percentage to another account - -*The percentage is scaled by 1e4 so the minimum unit is 0.01%* - - -```solidity -function updateDelegatedBalance(address account, uint32 percentage) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`account`|`address`|The address of the account to delegate to| -|`percentage`|`uint32`|The updated percentage of the account's balance to delegate| - - -### removeDelegatedBalance - -Removes delegation from an account. - - -```solidity -function removeDelegatedBalance(address account) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`account`|`address`|The address of the account whose delegation is being removed.| - - -### createStrategy - -Function to create a new Strategy - - -```solidity -function createStrategy(uint32 fee, string calldata metadataURI) external returns (uint32 strategyId); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`fee`|`uint32`|| -|`metadataURI`|`string`|The metadata URI of the strategy| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the new Strategy| - - -### updateStrategyMetadataURI - -Function to update the metadata URI of the Strategy - - -```solidity -function updateStrategyMetadataURI(uint32 strategyId, string calldata metadataURI) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The id of the strategy| -|`metadataURI`|`string`|The new metadata URI| - - -### optInToBApp - -Opt-in to a bApp with a list of tokens and obligation percentages - -*checks that each token is supported by the bApp, but not that the obligation is > 0* - - -```solidity -function optInToBApp(uint32 strategyId, address bApp, address[] calldata tokens, uint32[] calldata obligationPercentages, bytes calldata data) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`tokens`|`address[]`|The list of tokens to opt-in with| -|`obligationPercentages`|`uint32[]`|The list of obligation percentages for each token| -|`data`|`bytes`|Optional parameter that could be required by the service| - - -### _isContract - -Function to check if an address is a contract - - -```solidity -function _isContract(address bApp) private view returns (bool); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`bApp`|`address`|The address of the bApp| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`bool`|True if the address is a contract| - - -### depositERC20 - -Deposit ERC20 tokens into the strategy - - -```solidity -function depositERC20(uint32 strategyId, IERC20 token, uint256 amount) external nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`token`|`IERC20`|The ERC20 token address| -|`amount`|`uint256`|The amount to deposit| - - -### depositETH - -Deposit ETH into the strategy - - -```solidity -function depositETH(uint32 strategyId) external payable nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| - - -### proposeWithdrawal - -Propose a withdrawal of ERC20 tokens from the strategy. - - -```solidity -function proposeWithdrawal(uint32 strategyId, address token, uint256 amount) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy.| -|`token`|`address`|The ERC20 token address.| -|`amount`|`uint256`|The amount to withdraw.| - - -### finalizeWithdrawal - -Finalize the ERC20 withdrawal after the timelock period has passed. - - -```solidity -function finalizeWithdrawal(uint32 strategyId, IERC20 token) external nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy.| -|`token`|`IERC20`|The ERC20 token address.| - - -### proposeWithdrawalETH - -Propose an ETH withdrawal from the strategy. - - -```solidity -function proposeWithdrawalETH(uint32 strategyId, uint256 amount) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy.| -|`amount`|`uint256`|The amount of ETH to withdraw.| - - -### finalizeWithdrawalETH - -Finalize the ETH withdrawal after the timelock period has passed. - - -```solidity -function finalizeWithdrawalETH(uint32 strategyId) external nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy.| - - -### createObligation - -Add a new obligation for a bApp - - -```solidity -function createObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`token`|`address`|The address of the token| -|`obligationPercentage`|`uint32`|The obligation percentage| - - -### proposeUpdateObligation - -Propose a withdrawal of ERC20 tokens from the strategy. - - -```solidity -function proposeUpdateObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy.| -|`bApp`|`address`|| -|`token`|`address`|The ERC20 token address.| -|`obligationPercentage`|`uint32`|The new percentage of the obligation| - - -### finalizeUpdateObligation - -Finalize the withdrawal after the timelock period has passed. - - -```solidity -function finalizeUpdateObligation(uint32 strategyId, address bApp, address token) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy.| -|`bApp`|`address`|The address of the bApp.| -|`token`|`address`|The ERC20 token address.| - - -### reduceFee - -Instantly lowers the fee for a strategy - - -```solidity -function reduceFee(uint32 strategyId, uint32 proposedFee) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`proposedFee`|`uint32`|The proposed fee| - - -### proposeFeeUpdate - -Propose a new fee for a strategy - - -```solidity -function proposeFeeUpdate(uint32 strategyId, uint32 proposedFee) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`proposedFee`|`uint32`|The proposed fee| - - -### finalizeFeeUpdate - -Finalize the fee update for a strategy - - -```solidity -function finalizeFeeUpdate(uint32 strategyId) external; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| - - -### _createOptInObligations - -Set the obligation percentages for a strategy - - -```solidity -function _createOptInObligations(uint32 strategyId, address bApp, address[] calldata tokens, uint32[] calldata obligationPercentages) private; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`tokens`|`address[]`|The list of tokens to set s.obligations for| -|`obligationPercentages`|`uint32[]`|The list of obligation percentages for each token| - - -### _createSingleObligation - -Set a single obligation for a strategy - - -```solidity -function _createSingleObligation(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) private; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`token`|`address`|The address of the token| -|`obligationPercentage`|`uint32`|The obligation percentage| - - -### _validateObligationUpdateInput - -Validate the input for the obligation creation or update - - -```solidity -function _validateObligationUpdateInput(uint32 strategyId, address bApp, address token, uint32 obligationPercentage) private view; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`token`|`address`|The address of the token| -|`obligationPercentage`|`uint32`|The obligation percentage| - - -### _checkTimelocks - -Check the timelocks - - -```solidity -function _checkTimelocks(uint256 requestTime, uint256 timelockPeriod, uint256 expireTime) internal view; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`requestTime`|`uint256`|The time of the request| -|`timelockPeriod`|`uint256`|The timelock period| -|`expireTime`|`uint256`|The expire time| - - -### _beforeDeposit - - -```solidity -function _beforeDeposit(uint32 strategyId, address token, uint256 amount) internal; -``` - -### _proposeWithdrawal - -*override the previous share balance* - - -```solidity -function _proposeWithdrawal(uint32 strategyId, address token, uint256 amount) internal; -``` - -### _finalizeWithdrawal - - -```solidity -function _finalizeWithdrawal(uint32 strategyId, address token) private returns (uint256 amount); -``` - -### getSlashableBalance - -Get the slashable balance for a strategy - - -```solidity -function getSlashableBalance(CoreStorageLib.Data storage s, uint32 strategyId, address bApp, address token, ICore.Shares storage strategyTokenShares) - internal - view - returns (uint256 slashableBalance); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`s`|`CoreStorageLib.Data`|| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`token`|`address`|The address of the token| -|`strategyTokenShares`|`ICore.Shares`|| - -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|`slashableBalance`|`uint256`|The slashable balance| - - -### _checkStrategyOptedIn - - -```solidity -function _checkStrategyOptedIn(CoreStorageLib.Data storage s, uint32 strategyId, address bApp) internal view; -``` - -### slash - -Slash a strategy - - -```solidity -function slash(uint32 strategyId, address bApp, address token, uint32 percentage, bytes calldata data) external nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`strategyId`|`uint32`|The ID of the strategy| -|`bApp`|`address`|The address of the bApp| -|`token`|`address`|The address of the token| -|`percentage`|`uint32`|The amount to slash| -|`data`|`bytes`|Optional parameter that could be required by the service| - - -### _exitStrategy - - -```solidity -function _exitStrategy(CoreStorageLib.Data storage s, uint32 strategyId, address bApp, address token) private; -``` - -### _adjustObligation - - -```solidity -function _adjustObligation( - CoreStorageLib.Data storage s, - uint32 strategyId, - address bApp, - address token, - uint256 amount, - ICore.Shares storage strategyTokenShares -) internal returns (uint32 obligationPercentage); -``` - -### withdrawSlashingFund - -Withdraw the slashing fund for a token - - -```solidity -function withdrawSlashingFund(address token, uint256 amount) external nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`token`|`address`|The address of the token| -|`amount`|`uint256`|The amount to withdraw| - - -### withdrawETHSlashingFund - -Withdraw the slashing fund for ETH - - -```solidity -function withdrawETHSlashingFund(uint256 amount) external nonReentrant; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`amount`|`uint256`|The amount to withdraw| - - -### _withdrawSlashingFund - -General withdraw code the slashing fund - - -```solidity -function _withdrawSlashingFund(address token, uint256 amount) internal; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`token`|`address`|The address of the token| -|`amount`|`uint256`|The amount to withdraw| - - -### _checkSlashingAllowed - - -```solidity -function _checkSlashingAllowed() internal view; -``` - -### _checkWithdrawalsAllowed - - -```solidity -function _checkWithdrawalsAllowed() internal view; -``` - diff --git a/docs/src/src/middleware/README.md b/docs/src/src/middleware/README.md deleted file mode 100644 index 5f178fe0..00000000 --- a/docs/src/src/middleware/README.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Contents -- [examples](/src/middleware/examples) -- [interfaces](/src/middleware/interfaces) -- [modules](/src/middleware/modules) diff --git a/docs/src/src/middleware/examples/README.md b/docs/src/src/middleware/examples/README.md deleted file mode 100644 index 0d309593..00000000 --- a/docs/src/src/middleware/examples/README.md +++ /dev/null @@ -1,4 +0,0 @@ - - -# Contents -- [WhitelistExample](WhitelistExample.sol/contract.WhitelistExample.md) diff --git a/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md b/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md deleted file mode 100644 index e1a56051..00000000 --- a/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md +++ /dev/null @@ -1,27 +0,0 @@ -# WhitelistExample -[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) - - -## Functions -### constructor - - -```solidity -constructor(address _basedAppManager, address _initOwner) OwnableBasedApp(_basedAppManager, _initOwner); -``` - -### optInToBApp - - -```solidity -function optInToBApp(uint32 strategyId, address[] calldata, uint32[] calldata, bytes calldata) - external - view - override - onlySSVBasedAppManager - returns (bool success); -``` - diff --git a/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md b/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md deleted file mode 100644 index 8ad4607b..00000000 --- a/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md +++ /dev/null @@ -1,49 +0,0 @@ -# IBasedApp -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/interfaces/IBasedApp.sol) - - -## Functions -### optInToBApp - - -```solidity -function optInToBApp(uint32 strategyId, address[] calldata tokens, uint32[] calldata obligationPercentages, bytes calldata data) external returns (bool); -``` - -### registerBApp - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external; -``` - -### slash - - -```solidity -function slash(uint32 strategyId, address token, uint32 percentage, address sender, bytes calldata data) - external - returns (bool success, address receiver, bool exit); -``` - -### updateBAppMetadataURI - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external; -``` - -### updateBAppTokens - - -```solidity -function updateBAppTokens(ICore.TokenConfig[] calldata tokenConfigs) external; -``` - -## Errors -### UnauthorizedCaller - -```solidity -error UnauthorizedCaller(); -``` - diff --git a/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md b/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md deleted file mode 100644 index 5db5cac9..00000000 --- a/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md +++ /dev/null @@ -1,44 +0,0 @@ -# IBasedAppWhitelisted -[Git Source](https://github.com/ssvlabs/based-applications/blob/3ee95af731e4fce61ac2b03f418aa4e9fb5f64bd/src/middleware/interfaces/IBasedAppWhitelisted.sol) - - -## Functions -### addWhitelisted - - -```solidity -function addWhitelisted(uint32 strategyId) external; -``` - -### removeWhitelisted - - -```solidity -function removeWhitelisted(uint32 strategyId) external; -``` - -## Errors -### AlreadyWhitelisted - -```solidity -error AlreadyWhitelisted(); -``` - -### NonWhitelistedCaller - -```solidity -error NonWhitelistedCaller(); -``` - -### NotWhitelisted - -```solidity -error NotWhitelisted(); -``` - -### ZeroID - -```solidity -error ZeroID(); -``` - diff --git a/docs/src/src/middleware/interfaces/README.md b/docs/src/src/middleware/interfaces/README.md deleted file mode 100644 index ae5a297b..00000000 --- a/docs/src/src/middleware/interfaces/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - -# Contents -- [IBasedApp](IBasedApp.sol/interface.IBasedApp.md) -- [IBasedAppWhitelisted](IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md) diff --git a/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md b/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md deleted file mode 100644 index 4a1798b8..00000000 --- a/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md +++ /dev/null @@ -1,30 +0,0 @@ -# BasedAppWhitelisted -[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) - - -## State Variables -### isWhitelisted - -```solidity -mapping(uint32 => bool) public isWhitelisted; -``` - - -## Functions -### addWhitelisted - - -```solidity -function addWhitelisted(uint32 strategyId) external virtual; -``` - -### removeWhitelisted - - -```solidity -function removeWhitelisted(uint32 strategyId) external virtual; -``` - diff --git a/docs/src/src/middleware/modules/README.md b/docs/src/src/middleware/modules/README.md deleted file mode 100644 index a80ba19f..00000000 --- a/docs/src/src/middleware/modules/README.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# Contents -- [core](/src/middleware/modules/core) -- [core+roles](/src/middleware/modules/core+roles) -- [BasedAppWhitelisted](BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.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 deleted file mode 100644 index 9103c220..00000000 --- a/docs/src/src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md +++ /dev/null @@ -1,84 +0,0 @@ -# AccessControlBasedApp -[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 - - -## State Variables -### MANAGER_ROLE - -```solidity -bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); -``` - - -### OWNER_ROLE - -```solidity -bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE"); -``` - - -## Functions -### constructor - - -```solidity -constructor(address _basedAppManager, address owner) AccessControl() BasedAppCore(_basedAppManager); -``` - -### grantManagerRole - - -```solidity -function grantManagerRole(address manager) external onlyRole(DEFAULT_ADMIN_ROLE); -``` - -### revokeManagerRole - - -```solidity -function revokeManagerRole(address manager) external onlyRole(DEFAULT_ADMIN_ROLE); -``` - -### registerBApp - -Registers a BApp calling the SSV SSVBasedApps - -*metadata should point to a json that respect template: -{ -"name": "SSV Based App", -"website": "https://www.ssvlabs.io/", -"description": "SSV Based App Core", -"logo": "https://link-to-your-logo.png", -"social": "https://x.com/ssv_network" -}* - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external override onlyRole(MANAGER_ROLE); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`tokenConfigs`|`ICore.TokenConfig[]`|array of token addresses and shared risk levels| -|`metadataURI`|`string`|URI of the metadata| - - -### updateBAppMetadataURI - -Updates the metadata URI of a BApp - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external override onlyRole(MANAGER_ROLE); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`metadataURI`|`string`|new metadata URI| - - 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 deleted file mode 100644 index fa6eec5b..00000000 --- a/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md +++ /dev/null @@ -1,55 +0,0 @@ -# OwnableBasedApp -[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) - - -## Functions -### constructor - - -```solidity -constructor(address _basedAppManager, address _initOwner) BasedAppCore(_basedAppManager) Ownable(_initOwner); -``` - -### registerBApp - -Registers a BApp calling the SSV SSVBasedApps - -*metadata should point to a json that respect template: -{ -"name": "SSV Based App", -"website": "https://www.ssvlabs.io/", -"description": "SSV Based App Core", -"logo": "https://link-to-your-logo.png", -"social": "https://x.com/ssv_network" -}* - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external override onlyOwner; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`tokenConfigs`|`ICore.TokenConfig[]`|array of token addresses and shared risk levels| -|`metadataURI`|`string`|URI of the metadata| - - -### updateBAppMetadataURI - -Updates the metadata URI of a BApp - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external override onlyOwner; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`metadataURI`|`string`|new metadata URI| - - diff --git a/docs/src/src/middleware/modules/core+roles/README.md b/docs/src/src/middleware/modules/core+roles/README.md deleted file mode 100644 index 1fbff46f..00000000 --- a/docs/src/src/middleware/modules/core+roles/README.md +++ /dev/null @@ -1,5 +0,0 @@ - - -# Contents -- [AccessControlBasedApp](AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md) -- [OwnableBasedApp](OwnableBasedApp.sol/abstract.OwnableBasedApp.md) 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 deleted file mode 100644 index 17d90255..00000000 --- a/docs/src/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md +++ /dev/null @@ -1,145 +0,0 @@ -# BasedAppCore -[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) - - -## State Variables -### SSV_BASED_APPS_NETWORK -Address of the SSV Based App Manager contract - - -```solidity -address public immutable SSV_BASED_APPS_NETWORK; -``` - - -## Functions -### onlySSVBasedAppManager - -*Allows only the SSV Based App Manager to call the function* - - -```solidity -modifier onlySSVBasedAppManager(); -``` - -### constructor - -constructor for the BasedAppCore contract, -initializes the contract with the SSVBasedApps address and the owner and disables the initializers. - - -```solidity -constructor(address _ssvBasedAppsNetwork); -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`_ssvBasedAppsNetwork`|`address`|address of the SSVBasedApps contract| - - -### registerBApp - -Registers a BApp calling the SSVBasedApps - -*metadata should point to a json that respect template: -{ -"name": "SSV Based App", -"website": "https://www.ssvlabs.io/", -"description": "SSV Based App Core", -"logo": "https://link-to-your-logo.png", -"social": "https://x.com/ssv_network" -}* - - -```solidity -function registerBApp(ICore.TokenConfig[] calldata tokenConfigs, string calldata metadataURI) external virtual; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`tokenConfigs`|`ICore.TokenConfig[]`|array of token configs (address, shared risk level)| -|`metadataURI`|`string`|URI of the metadata| - - -### updateBAppMetadataURI - -Updates the metadata URI of a BApp - - -```solidity -function updateBAppMetadataURI(string calldata metadataURI) external virtual; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`metadataURI`|`string`|new metadata URI| - - -### updateBAppTokens - -Updates the tokens of a BApp - - -```solidity -function updateBAppTokens(ICore.TokenConfig[] calldata tokenConfigs) external virtual; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`tokenConfigs`|`ICore.TokenConfig[]`|new list of tokens and their shared risk levels| - - -### withdrawSlashingFund - - -```solidity -function withdrawSlashingFund(address token, uint256 amount) external virtual; -``` - -### withdrawETHSlashingFund - - -```solidity -function withdrawETHSlashingFund(uint256 amount) external virtual; -``` - -### optInToBApp - -Allows a Strategy to Opt-in to a BApp, it can be called only by the SSV Based App Manager - - -```solidity -function optInToBApp(uint32, address[] calldata, uint32[] calldata, bytes calldata) external virtual onlySSVBasedAppManager returns (bool success); -``` - -### slash - -*--- CORE LOGIC (TO BE IMPLEMENTED) ---* - -*--- RETURN TRUE IF SUCCESS, FALSE OTHERWISE ---* - - -```solidity -function slash(uint32, address, uint32, address, bytes calldata) external virtual onlySSVBasedAppManager returns (bool, address, bool); -``` - -### receive - -*--- CORE LOGIC (TO BE IMPLEMENTED) ---* - -*--- RETURN TRUE IF SUCCESS, FALSE OTHERWISE ---* - -*--- RETURN RECEIVER ADDRESS FOR THE SLASHED FUNDS ---* - - -```solidity -receive() external payable virtual; -``` - diff --git a/docs/src/src/middleware/modules/core/README.md b/docs/src/src/middleware/modules/core/README.md deleted file mode 100644 index 4ac46fa6..00000000 --- a/docs/src/src/middleware/modules/core/README.md +++ /dev/null @@ -1,4 +0,0 @@ - - -# Contents -- [BasedAppCore](BasedAppCore.sol/abstract.BasedAppCore.md) diff --git a/guides/generations.md b/guides/generations.md index 0f16bc12..c7aef5fb 100644 --- a/guides/generations.md +++ b/guides/generations.md @@ -28,9 +28,8 @@ This cleanly tears down all outstanding share balances in one on‑chain operati - Add to `accountShareBalance[msg.sender]`. 2. **Slash & Generation bump** - - If `strategyTokenShares.totalTokenBalance` goes to zero after slashing: + - If `totalTokenBalance` goes to zero after slashing: ```solidity - delete strategyTokenShares.totalTokenBalance; delete strategyTokenShares.totalShareBalance; strategyTokenShares.currentGeneration += 1; ``` @@ -51,7 +50,6 @@ This cleanly tears down all outstanding share balances in one on‑chain operati ```solidity struct Shares { - uint256 totalTokenBalance; uint256 totalShareBalance; uint256 currentGeneration; mapping(address => uint256) accountShareBalance; diff --git a/package-lock.json b/package-lock.json index 8ee03362..7d1b199a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "GPL-3.0", "devDependencies": { "husky": "9.1.7", - "lint-staged": "16.1.0", + "lint-staged": "16.1.2", "prettier": "3.6.2", "prettier-plugin-solidity": "2.1.0", "solhint": "6.0.0" @@ -1010,9 +1010,9 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.0.tgz", - "integrity": "sha512-HkpQh69XHxgCjObjejBT3s2ILwNjFx8M3nw+tJ/ssBauDlIpkx2RpqWSi1fBgkXLSSXnbR3iEq1NkVtpvV+FLQ==", + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.1.2.tgz", + "integrity": "sha512-sQKw2Si2g9KUZNY3XNvRuDq4UJqpHwF0/FQzZR2M7I5MvtpWvibikCjUVJzZdGE0ByurEl3KQNvsGetd1ty1/Q==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f5bfdd0c..becb4a0f 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "husky": "9.1.7", - "lint-staged": "16.1.0", + "lint-staged": "16.1.2", "prettier": "3.6.2", "prettier-plugin-solidity": "2.1.0", "solhint": "6.0.0" diff --git a/src/core/SSVBasedApps.sol b/src/core/SSVBasedApps.sol index 550e34c3..a225fb2c 100644 --- a/src/core/SSVBasedApps.sol +++ b/src/core/SSVBasedApps.sol @@ -26,6 +26,9 @@ import { import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.sol"; import { CoreStorageLib, SSVCoreModules @@ -33,6 +36,7 @@ import { import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; /** * @title SSVBasedApps @@ -88,7 +92,8 @@ contract SSVBasedApps is address owner_, IBasedAppManager ssvBasedAppManger_, IStrategyManager ssvStrategyManager_, - IProtocolManager protocolManager_, + IProtocolManager ssvProtocolManager_, + IStrategyFactory ssvStrategyFactory_, ProtocolStorageLib.Data calldata config ) external override initializer onlyProxy { __UUPSUpgradeable_init(); @@ -96,7 +101,8 @@ contract SSVBasedApps is __SSVBasedApplications_init_unchained( ssvBasedAppManger_, ssvStrategyManager_, - protocolManager_, + ssvProtocolManager_, + ssvStrategyFactory_, config ); } @@ -105,11 +111,13 @@ contract SSVBasedApps is function __SSVBasedApplications_init_unchained( IBasedAppManager ssvBasedAppManger_, IStrategyManager ssvStrategyManager_, - IProtocolManager protocolManager_, + IProtocolManager ssvProtocolManager_, + IStrategyFactory ssvStrategyFactory_, ProtocolStorageLib.Data calldata config ) internal onlyInitializing { CoreStorageLib.Data storage s = CoreStorageLib.load(); ProtocolStorageLib.Data storage sp = ProtocolStorageLib.load(); + s.strategyFactory = address(ssvStrategyFactory_); s.ssvContracts[SSVCoreModules.SSV_STRATEGY_MANAGER] = address( ssvStrategyManager_ ); @@ -117,7 +125,7 @@ contract SSVBasedApps is ssvBasedAppManger_ ); s.ssvContracts[SSVCoreModules.SSV_PROTOCOL_MANAGER] = address( - protocolManager_ + ssvProtocolManager_ ); if ( @@ -269,12 +277,11 @@ contract SSVBasedApps is ) public view returns (uint256 slashableBalance) { CoreStorageLib.Data storage s = CoreStorageLib.load(); - ICore.Shares storage strategyTokenShares = s.strategyTokenShares[ - strategyId - ][token]; - uint32 percentage = s.obligations[strategyId][bApp][token].percentage; - uint256 balance = strategyTokenShares.totalTokenBalance; + + uint256 balance = token == ETH_ADDRESS + ? s.strategies[strategyId].strategyAddress.balance + : IERC20(token).balanceOf(s.strategies[strategyId].strategyAddress); return (balance * percentage) / MAX_PERCENTAGE; } @@ -330,13 +337,7 @@ contract SSVBasedApps is _delegateTo(SSVCoreModules.SSV_STRATEGY_MANAGER); } - function slash( - uint32 strategyId, - address bApp, - address token, - uint32 percentage, - bytes calldata data - ) external { + function slash(ICore.SlashContext memory s, bytes calldata data) external { _delegateTo(SSVCoreModules.SSV_STRATEGY_MANAGER); } @@ -406,6 +407,11 @@ contract SSVBasedApps is // ** Section: External Views ** // ***************************** + function strategyFactory() external view returns (address) { + CoreStorageLib.Data storage s = CoreStorageLib.load(); + return s.strategyFactory; + } + function delegations( address account, address receiver @@ -430,9 +436,17 @@ contract SSVBasedApps is function strategies( uint32 strategyId - ) external view returns (address strategyOwner, uint32 fee) { + ) + external + view + returns (address strategyAddress, address strategyOwner, uint32 fee) + { CoreStorageLib.Data storage s = CoreStorageLib.load(); - return (s.strategies[strategyId].owner, s.strategies[strategyId].fee); + return ( + s.strategies[strategyId].strategyAddress, + s.strategies[strategyId].owner, + s.strategies[strategyId].fee + ); } function ownedStrategies( @@ -467,7 +481,9 @@ contract SSVBasedApps is address token ) external view returns (uint256) { CoreStorageLib.Data storage s = CoreStorageLib.load(); - return s.strategyTokenShares[strategyId][token].totalTokenBalance; + address strategy = s.strategies[strategyId].strategyAddress; + if (token == ETH_ADDRESS) return strategy.balance; + else return IERC20(token).balanceOf(strategy); } function strategyTotalShares( @@ -695,4 +711,6 @@ contract SSVBasedApps is } } } + + receive() external payable {} } diff --git a/src/core/interfaces/ICore.sol b/src/core/interfaces/ICore.sol index 02f642df..808b6b60 100644 --- a/src/core/interfaces/ICore.sol +++ b/src/core/interfaces/ICore.sol @@ -26,6 +26,8 @@ interface ICore { /// @notice Represents a Strategy struct Strategy { + /// @dev The address of the deployed strategy vault + address strategyAddress; /// @dev The owner of the strategy address owner; /// @dev The fee in percentage @@ -58,8 +60,6 @@ interface ICore { /// @notice Represents the shares system of a strategy struct Shares { - /// @dev The total token balance - uint256 totalTokenBalance; /// @dev The total share balance uint256 totalShareBalance; /// @dev The current generation @@ -81,4 +81,16 @@ interface ICore { address token; uint32 sharedRiskLevel; } + + struct SlashContext { + uint256 amount; + uint32 strategyId; + uint32 percentage; + uint32 obligationPercentage; + address bApp; + bool exit; + bool success; + address token; + address receiver; + } } diff --git a/src/core/interfaces/ISSVBasedApps.sol b/src/core/interfaces/ISSVBasedApps.sol index bc67c424..b7c2423f 100644 --- a/src/core/interfaces/ISSVBasedApps.sol +++ b/src/core/interfaces/ISSVBasedApps.sol @@ -15,6 +15,9 @@ import { SSVCoreModules } from "@ssv/src/core/libraries/CoreStorageLib.sol"; import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.sol"; interface ISSVBasedApps is IStrategyManager, @@ -32,6 +35,7 @@ interface ISSVBasedApps is IBasedAppManager ssvBasedAppManger_, IStrategyManager ssvStrategyManager_, IProtocolManager protocolManager_, + IStrategyFactory ssvStrategyFactory_, ProtocolStorageLib.Data memory config ) external; function updateModule( diff --git a/src/core/interfaces/IStrategyFactory.sol b/src/core/interfaces/IStrategyFactory.sol new file mode 100644 index 00000000..ef9aaf6a --- /dev/null +++ b/src/core/interfaces/IStrategyFactory.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.30; +import { IStrategyVault } from "@ssv/src/core/interfaces/IStrategyVault.sol"; + +interface IStrategyFactory { + event NewStrategyDeployed(address owner, address strategy); + event StrategyBeaconModified(address previousBeacon, address newBeacon); + event SSVBasedAppsModified( + address previousSsvBasedApps, + address newSsvBasedApps + ); + + function createStrategy() external returns (address strategy); +} diff --git a/src/core/interfaces/IStrategyManager.sol b/src/core/interfaces/IStrategyManager.sol index 332d8d01..d88dd756 100644 --- a/src/core/interfaces/IStrategyManager.sol +++ b/src/core/interfaces/IStrategyManager.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.30; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; interface IStrategyManager { event AccountMetadataURIUpdated( @@ -51,6 +52,7 @@ interface IStrategyManager { event StrategyCreated( uint32 indexed strategyId, address indexed owner, + address indexed strategyAddress, uint32 fee, string metadataURI ); @@ -144,13 +146,7 @@ interface IStrategyManager { function proposeWithdrawalETH(uint32 strategyId, uint256 amount) external; function reduceFee(uint32 strategyId, uint32 proposedFee) external; function removeDelegatedBalance(address receiver) external; - function slash( - uint32 strategyId, - address bApp, - address token, - uint32 percentage, - bytes calldata data - ) external; + function slash(ICore.SlashContext memory c, bytes calldata data) external; function updateAccountMetadataURI(string calldata metadataURI) external; function updateDelegatedBalance( address receiver, @@ -175,6 +171,7 @@ interface IStrategyManager { error FeeAlreadySet(); error InsufficientBalance(); error InsufficientLiquidity(); + error InsufficientSlashAmount(); error InvalidAccountGeneration(); error InvalidAmount(); error InvalidBAppOwner(address caller, address expectedOwner); diff --git a/src/core/interfaces/IStrategyVault.sol b/src/core/interfaces/IStrategyVault.sol new file mode 100644 index 00000000..95f1b76c --- /dev/null +++ b/src/core/interfaces/IStrategyVault.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.30; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IStrategyVault { + event NewStrategyCreated(); + + function initialize(address _allowedSender) external; + function withdraw(IERC20 token, uint256 amount, address receiver) external; + function withdrawETH(uint256 amount, address receiver) external; + + error UnauthorizedCaller(); + error InvalidZeroAmount(); + error ETHTransferFailed(); +} diff --git a/src/core/interfaces/IViews.sol b/src/core/interfaces/IViews.sol index e8331199..9b1fe681 100644 --- a/src/core/interfaces/IViews.sol +++ b/src/core/interfaces/IViews.sol @@ -14,7 +14,10 @@ interface IViews { ) external view returns (bool isRegistered); function strategies( uint32 strategyId - ) external view returns (address strategyOwner, uint32 fee); + ) + external + view + returns (address strategyAddress, address strategyOwner, uint32 fee); function ownedStrategies( address owner ) external view returns (uint32[] memory strategyIds); diff --git a/src/core/libraries/CoreStorageLib.sol b/src/core/libraries/CoreStorageLib.sol index 0d24e68c..d6cd9e40 100644 --- a/src/core/libraries/CoreStorageLib.sol +++ b/src/core/libraries/CoreStorageLib.sol @@ -14,6 +14,7 @@ library CoreStorageLib { /// @notice Represents all operational state required by the SSV Based Application platform. struct Data { uint32 _strategyCounter; + address strategyFactory; /// @notice Maps each SSVCoreModules' module to its corresponding contract address mapping(SSVCoreModules => address) ssvContracts; /** @@ -92,6 +93,7 @@ library CoreStorageLib { * Submitting a new request will overwrite the previous one and reset the timer. */ mapping(address bApp => ICore.TokenUpdateRequest) tokenUpdateRequests; + uint256[50] __gap; } uint256 private constant SSV_BASED_APPS_STORAGE_POSITION = diff --git a/src/core/modules/StrategyManager.sol b/src/core/modules/StrategyManager.sol index 7e1c3531..0a41fe02 100644 --- a/src/core/modules/StrategyManager.sol +++ b/src/core/modules/StrategyManager.sol @@ -14,7 +14,11 @@ import { MAX_PERCENTAGE, ETH_ADDRESS } from "@ssv/src/core/libraries/ValidationLib.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.sol"; import { ICore } from "@ssv/src/core/interfaces/ICore.sol"; +import { IStrategyVault } from "@ssv/src/core/interfaces/IStrategyVault.sol"; import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; @@ -156,13 +160,23 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { strategyId = ++s._strategyCounter; } + address newStrategyAddress = IStrategyFactory(s.strategyFactory) + .createStrategy(); + ICore.Strategy storage newStrategy = s.strategies[strategyId]; + newStrategy.strategyAddress = newStrategyAddress; newStrategy.owner = msg.sender; newStrategy.fee = fee; s.strategyOwners[msg.sender].push(strategyId); - emit StrategyCreated(strategyId, msg.sender, fee, metadataURI); + emit StrategyCreated( + strategyId, + newStrategyAddress, + msg.sender, + fee, + metadataURI + ); } /// @notice Function to update the metadata URI of the Strategy @@ -245,14 +259,19 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { /// @param strategyId The ID of the strategy /// @param token The ERC20 token address /// @param amount The amount to deposit + function depositERC20( uint32 strategyId, IERC20 token, uint256 amount ) external nonReentrant { - _beforeDeposit(strategyId, address(token), amount); + address strategyAddress = _beforeDeposit( + strategyId, + address(token), + amount + ); - token.safeTransferFrom(msg.sender, address(this), amount); + token.safeTransferFrom(msg.sender, strategyAddress, amount); emit StrategyDeposit(strategyId, msg.sender, address(token), amount); } @@ -260,7 +279,13 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { /// @notice Deposit ETH into the strategy /// @param strategyId The ID of the strategy function depositETH(uint32 strategyId) external payable nonReentrant { - _beforeDeposit(strategyId, ETH_ADDRESS, msg.value); + address strategyAddress = _beforeDeposit( + strategyId, + ETH_ADDRESS, + msg.value + ); + + payable(strategyAddress).transfer(msg.value); emit StrategyDeposit(strategyId, msg.sender, ETH_ADDRESS, msg.value); } @@ -289,9 +314,12 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { ) external nonReentrant { _checkWithdrawalsAllowed(); - uint256 amount = _finalizeWithdrawal(strategyId, address(token)); + (uint256 amount, address strategyAddress) = _finalizeWithdrawal( + strategyId, + address(token) + ); - token.safeTransfer(msg.sender, amount); + IStrategyVault(strategyAddress).withdraw(token, amount, msg.sender); emit StrategyWithdrawal( strategyId, @@ -315,9 +343,12 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { function finalizeWithdrawalETH(uint32 strategyId) external nonReentrant { _checkWithdrawalsAllowed(); - uint256 amount = _finalizeWithdrawal(strategyId, ETH_ADDRESS); + (uint256 amount, address strategyAddress) = _finalizeWithdrawal( + strategyId, + ETH_ADDRESS + ); - payable(msg.sender).transfer(amount); + IStrategyVault(strategyAddress).withdrawETH(amount, msg.sender); emit StrategyWithdrawal( strategyId, @@ -606,7 +637,7 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { uint32 strategyId, address token, uint256 amount - ) internal { + ) internal returns (address strategyAddress) { if (amount == 0) revert InvalidAmount(); CoreStorageLib.Data storage s = CoreStorageLib.load(); @@ -614,7 +645,13 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { strategyId ][token]; - uint256 totalTokenBalance = strategyTokenShares.totalTokenBalance; + ICore.Strategy storage strategy = s.strategies[strategyId]; + strategyAddress = strategy.strategyAddress; + + uint256 totalTokenBalance = token == ETH_ADDRESS + ? strategyAddress.balance + : IERC20(token).balanceOf(strategyAddress); + uint256 totalShares = strategyTokenShares.totalShareBalance; uint256 shares; @@ -638,7 +675,6 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { } strategyTokenShares.totalShareBalance += shares; - strategyTokenShares.totalTokenBalance += amount; } function _proposeWithdrawal( @@ -657,7 +693,14 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { strategyTokenShares.currentGeneration != strategyTokenShares.accountGeneration[msg.sender] ) revert InvalidAccountGeneration(); - uint256 totalTokenBalance = strategyTokenShares.totalTokenBalance; + + ICore.Strategy storage strategy = s.strategies[strategyId]; + address strategyAddress = strategy.strategyAddress; + + uint256 totalTokenBalance = token == ETH_ADDRESS + ? strategyAddress.balance + : IERC20(token).balanceOf(strategyAddress); + uint256 totalShares = strategyTokenShares.totalShareBalance; if (totalTokenBalance == 0 || totalShares == 0) { @@ -686,7 +729,7 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { function _finalizeWithdrawal( uint32 strategyId, address token - ) private returns (uint256 amount) { + ) private returns (uint256 amount, address strategyAddress) { CoreStorageLib.Data storage s = CoreStorageLib.load(); ICore.WithdrawalRequest storage request = s.withdrawalRequests[ @@ -714,18 +757,21 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { strategyTokenShares.accountGeneration[msg.sender] ) revert InvalidAccountGeneration(); - uint256 totalTokenBalance = strategyTokenShares.totalTokenBalance; + ICore.Strategy storage strategy = s.strategies[strategyId]; + strategyAddress = strategy.strategyAddress; + + uint256 totalTokenBalance = token == ETH_ADDRESS + ? strategyAddress.balance + : IERC20(token).balanceOf(strategyAddress); + uint256 totalShares = strategyTokenShares.totalShareBalance; amount = (shares * totalTokenBalance) / totalShares; strategyTokenShares.accountShareBalance[msg.sender] -= shares; strategyTokenShares.totalShareBalance -= shares; - strategyTokenShares.totalTokenBalance -= amount; delete s.withdrawalRequests[strategyId][msg.sender][token]; - - return amount; } // *********************** @@ -742,14 +788,24 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { uint32 strategyId, address bApp, address token, - ICore.Shares storage strategyTokenShares + address strategyAddress ) internal view returns (uint256 slashableBalance) { uint32 percentage = s.obligations[strategyId][bApp][token].percentage; - uint256 balance = strategyTokenShares.totalTokenBalance; + + uint256 balance = _getTokenBalance(token, strategyAddress); return (balance * percentage) / MAX_PERCENTAGE; } + function _getTokenBalance( + address token, + address strategyAddress + ) internal view returns (uint256 balance) { + balance = token == ETH_ADDRESS + ? strategyAddress.balance + : IERC20(token).balanceOf(strategyAddress); + } + function _checkStrategyOptedIn( CoreStorageLib.Data storage s, uint32 strategyId, @@ -762,99 +818,119 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { } } - /// @notice Slash a strategy - /// @param strategyId The ID of the strategy - /// @param bApp The address of the bApp - /// @param token The address of the token - /// @param percentage The amount to slash - /// @param data Optional parameter that could be required by the service - function slash( + function _calculateSlashAmount( + CoreStorageLib.Data storage s, uint32 strategyId, address bApp, address token, uint32 percentage, + address strategyAddress + ) internal view returns (uint256 amount) { + uint256 slashableBalance = getSlashableBalance( + s, + strategyId, + bApp, + token, + strategyAddress + ); + if (slashableBalance == 0) revert InsufficientBalance(); + amount = (slashableBalance * percentage) / MAX_PERCENTAGE; + if (amount == 0) revert InsufficientSlashAmount(); + } + + /// @notice Slash a strategy + /// @param c The context to slash + /// @param data Optional parameter that could be required by the service + function slash( + ICore.SlashContext memory c, bytes calldata data ) external nonReentrant { _checkSlashingAllowed(); - ValidationLib.validatePercentageAndNonZero(percentage); + ValidationLib.validatePercentageAndNonZero(c.percentage); CoreStorageLib.Data storage s = CoreStorageLib.load(); - if (!s.registeredBApps[bApp]) { + if (!s.registeredBApps[c.bApp]) { revert IBasedAppManager.BAppNotRegistered(); } - _checkStrategyOptedIn(s, strategyId, bApp); + _checkStrategyOptedIn(s, c.strategyId, c.bApp); - ICore.Shares storage strategyTokenShares = s.strategyTokenShares[ - strategyId - ][token]; - uint256 slashableBalance = getSlashableBalance( + address strategyAddress = s.strategies[c.strategyId].strategyAddress; + + c.amount = _calculateSlashAmount( s, - strategyId, - bApp, - token, - strategyTokenShares + c.strategyId, + c.bApp, + c.token, + c.percentage, + strategyAddress ); - if (slashableBalance == 0) revert InsufficientBalance(); - uint256 amount = (slashableBalance * percentage) / MAX_PERCENTAGE; - address receiver; - bool exit; - bool success; - uint32 obligationPercentage; - if (_isContract(bApp)) { - (success, receiver, exit) = IBasedApp(bApp).slash( - strategyId, - token, - percentage, + if (_isContract(c.bApp)) { + (c.success, c.receiver, c.exit) = IBasedApp(c.bApp).slash( + c.strategyId, + c.token, + c.percentage, msg.sender, data ); - if (!success) revert IStrategyManager.BAppSlashingFailed(); + if (!c.success) revert IStrategyManager.BAppSlashingFailed(); - if (exit) { - _exitStrategy(s, strategyId, bApp, token); - obligationPercentage = 0; + if (c.exit) { + _exitStrategy(s, c.strategyId, c.bApp, c.token); + c.obligationPercentage = 0; } else - obligationPercentage = _adjustObligation( + c.obligationPercentage = _adjustObligation( s, - strategyId, - bApp, - token, - amount, - strategyTokenShares + c.strategyId, + c.bApp, + c.token, + c.amount, + s.strategyTokenShares[c.strategyId][c.token] ); } else { // Only the bApp EOA can slash - if (msg.sender != bApp) revert InvalidBAppOwner(msg.sender, bApp); - receiver = bApp; - _exitStrategy(s, strategyId, bApp, token); + if (msg.sender != c.bApp) + revert InvalidBAppOwner(msg.sender, c.bApp); + c.receiver = c.bApp; + _exitStrategy(s, c.strategyId, c.bApp, c.token); } - strategyTokenShares.totalTokenBalance -= amount; - s.slashingFund[receiver][token] += amount; + c.token == ETH_ADDRESS + ? IStrategyVault(strategyAddress).withdrawETH( + c.amount, + address(this) + ) + : IStrategyVault(strategyAddress).withdraw( + IERC20(c.token), + c.amount, + address(this) + ); - if (strategyTokenShares.totalTokenBalance == 0) { - delete strategyTokenShares.totalTokenBalance; - delete strategyTokenShares.totalShareBalance; - strategyTokenShares.currentGeneration += 1; + s.slashingFund[c.receiver][c.token] += c.amount; + + if (_getTokenBalance(c.token, strategyAddress) == 0) { + delete s + .strategyTokenShares[c.strategyId][c.token] + .totalShareBalance; + s.strategyTokenShares[c.strategyId][c.token].currentGeneration += 1; } emit IStrategyManager.StrategySlashed( - strategyId, - bApp, - token, - percentage, - receiver + c.strategyId, + c.bApp, + c.token, + c.percentage, + c.receiver ); emit IStrategyManager.ObligationUpdated( - strategyId, - bApp, - token, - obligationPercentage + c.strategyId, + c.bApp, + c.token, + c.obligationPercentage ); } @@ -878,9 +954,13 @@ contract StrategyManager is ReentrancyGuardTransient, IStrategyManager { ICore.Obligation storage obligation = s.obligations[strategyId][bApp][ token ]; - uint256 currentStrategyBalance = strategyTokenShares.totalTokenBalance; + uint256 currentStrategyBalance = _getTokenBalance( + token, + s.strategies[strategyId].strategyAddress + ); uint256 currentObligatedBalance = (obligation.percentage * currentStrategyBalance) / MAX_PERCENTAGE; + uint256 postSlashStrategyBalance = currentStrategyBalance - amount; uint256 postSlashObligatedBalance = currentObligatedBalance - amount; if (postSlashStrategyBalance == 0) { diff --git a/src/core/strategies/StrategyFactory.sol b/src/core/strategies/StrategyFactory.sol new file mode 100644 index 00000000..64d6e6c7 --- /dev/null +++ b/src/core/strategies/StrategyFactory.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.30; + +import { + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +import { + BeaconProxy +} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; +import { IBeacon } from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; +import { IStrategyVault } from "@ssv/src/core/interfaces/IStrategyVault.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.sol"; + +contract StrategyFactory is OwnableUpgradeable, IStrategyFactory { + ISSVBasedApps public ssvBasedApps; + IBeacon public strategyBeacon; + + constructor(string memory _version) { + //todo move based aoo there + _disableInitializers(); + } + + function initialize( + address _initialOwner, + IBeacon _strategyBeacon, + ISSVBasedApps _ssvBasedApps + ) public virtual initializer { + _transferOwnership(_initialOwner); + _setStrategyBeacon(_strategyBeacon); + _setSsvBasedApps(_ssvBasedApps); + } + + function createStrategy() external returns (address strategy) { + IStrategyVault strategyVault = IStrategyVault( + address( + new BeaconProxy( + address(strategyBeacon), + abi.encodeWithSelector( + IStrategyVault.initialize.selector, + address(ssvBasedApps) + ) + ) + ) + ); + strategy = address(strategyVault); + } + + function _setSsvBasedApps(ISSVBasedApps _ssvBasedApps) internal { + emit SSVBasedAppsModified( + address(ssvBasedApps), + address(_ssvBasedApps) + ); + ssvBasedApps = _ssvBasedApps; + } + + function _setStrategyBeacon(IBeacon _strategyBeacon) internal { + emit StrategyBeaconModified( + address(strategyBeacon), + address(_strategyBeacon) + ); + strategyBeacon = _strategyBeacon; + } +} diff --git a/src/core/strategies/StrategyVault.sol b/src/core/strategies/StrategyVault.sol new file mode 100644 index 00000000..5b8516cc --- /dev/null +++ b/src/core/strategies/StrategyVault.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.30; + +// there needs to be different shares per token... + +// shares[tokenAddress][ownerAddress] = 1; +// use balanceOf for balance. +import { IStrategyVault } from "@ssv/src/core/interfaces/IStrategyVault.sol"; +import { + Initializable +} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { + ReentrancyGuardTransient +} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; +import { + SafeERC20 +} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract StrategyVault is + IStrategyVault, + Initializable, + ReentrancyGuardTransient +{ + using SafeERC20 for IERC20; + + address public ssvBasedApps; + + /// @dev Allows only the SSV Based App Manager to call the function + modifier onlySSVBasedAppManager() { + if (msg.sender != ssvBasedApps) { + revert UnauthorizedCaller(); + } + _; + } + + function initialize(address _ssvBasedApps) public initializer { + ssvBasedApps = _ssvBasedApps; + emit NewStrategyCreated(); + } + + function withdraw( + IERC20 token, + uint256 amount, + address receiver + ) public onlySSVBasedAppManager nonReentrant { + _beforeWithdraw(amount, receiver); + token.safeTransfer(receiver, amount); + } + + function withdrawETH( + uint256 amount, + address receiver + ) public onlySSVBasedAppManager nonReentrant { + _beforeWithdraw(amount, receiver); + (bool success, ) = payable(receiver).call{ value: amount }(""); + if (!success) revert ETHTransferFailed(); + } + + function _beforeWithdraw(uint256 amount, address receiver) internal pure { + if (amount == 0) revert InvalidZeroAmount(); + } + + receive() external payable onlySSVBasedAppManager {} + + fallback() external payable onlySSVBasedAppManager {} +} diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 00000000..0e94ea93 Binary files /dev/null and b/test/.DS_Store differ diff --git a/test/Config.t.sol b/test/Config.t.sol index 94bfdc96..a2c05178 100644 --- a/test/Config.t.sol +++ b/test/Config.t.sol @@ -17,9 +17,13 @@ import { import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.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 { StrategyFactory } from "@ssv/src/core/strategies/StrategyFactory.sol"; import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; @@ -32,6 +36,7 @@ contract Config is Test { StrategyManager public strategyManagerMod; BasedAppsManager public basedAppsManagerMod; ProtocolManager public protocolManagerMod; + StrategyFactory public strategyFactory; // Proxies ERC1967Proxy public proxy; // UUPS Proxy contract @@ -53,6 +58,7 @@ contract Config is Test { basedAppsManagerMod = new BasedAppsManager(); strategyManagerMod = new StrategyManager(); protocolManagerMod = new ProtocolManager(); + strategyFactory = new StrategyFactory("0.0.1"); implementation = new SSVBasedApps(); vm.stopPrank(); } @@ -77,6 +83,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -107,6 +114,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -137,6 +145,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -167,6 +176,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -195,6 +205,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -225,6 +236,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -255,6 +267,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -285,6 +298,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -315,6 +329,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -345,6 +360,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( @@ -372,6 +388,7 @@ contract Config is Test { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactory), config ); vm.expectRevert( diff --git a/test/SSVBasedApps.t.sol b/test/SSVBasedApps.t.sol index 5d70ae98..a43c8bde 100644 --- a/test/SSVBasedApps.t.sol +++ b/test/SSVBasedApps.t.sol @@ -10,6 +10,7 @@ import { IStrategyManager, IBasedAppManager, IProtocolManager, + IStrategyFactory, SSVBasedApps } from "@ssv/test/helpers/Setup.t.sol"; import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; @@ -20,20 +21,19 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { assertEq(address(proxiedManager).balance, 0); } - function testRevertSendETHDirectly() public payable { + function testSendETHDirectly() public payable { vm.prank(USER1); vm.expectRevert(); payable(address(proxiedManager)).transfer(1 ether); - assertEq(address(proxiedManager).balance, 0); } - function testRevertSendETHViaFallback() public { + function testSendETHViaFallback() public { vm.prank(USER1); (bool success, ) = payable(address(proxiedManager)).call{ value: 1 ether }(""); - assertEq(success, false); - assertEq(address(proxiedManager).balance, 0); + assertEq(success, true); + assertEq(address(proxiedManager).balance, 1 ether); } function testRevertViaFallbackInvalidFunctionCall() public { @@ -41,7 +41,7 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { (bool success, ) = payable(address(proxiedManager)).call{ value: 0 ether }(""); - assertEq(success, false); + assertEq(success, true); assertEq(address(proxiedManager).balance, 0); } @@ -144,6 +144,7 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactoryProxy), config ); } @@ -156,6 +157,7 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactoryProxy), config ); } @@ -170,7 +172,7 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { vm.etch(newModuleAddress, new bytes(100)); moduleAddresses[0] = address(newModuleAddress); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit ISSVBasedApps.ModuleUpdated( SSVCoreModules.SSV_STRATEGY_MANAGER, address(newModuleAddress) @@ -196,7 +198,7 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { vm.etch(newModuleAddress, new bytes(100)); moduleAddresses[0] = address(newModuleAddress); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit ISSVBasedApps.ModuleUpdated( SSVCoreModules.SSV_BAPPS_MANAGER, address(newModuleAddress) @@ -215,7 +217,7 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { vm.etch(newModuleAddress, new bytes(100)); moduleAddresses[0] = address(newModuleAddress); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit ISSVBasedApps.ModuleUpdated( SSVCoreModules.SSV_PROTOCOL_MANAGER, address(newModuleAddress) @@ -255,4 +257,18 @@ contract SSVBasedAppsTest is Setup, Ownable2StepUpgradeable { vm.prank(OWNER); proxiedManager.updateModule(moduleIds, moduleAddresses); } + + function testStrategyFactory() public view { + address strategyFactory = proxiedManager.strategyFactory(); + assertNotEq( + strategyFactory, + address(0), + "Should not return empty strategy factory" + ); + assertEq( + strategyFactory, + address(strategyFactoryProxy), + "Should return the correct strategy factory address" + ); + } } diff --git a/test/helpers/Setup.t.sol b/test/helpers/Setup.t.sol index 8f9b3ff3..62a17031 100644 --- a/test/helpers/Setup.t.sol +++ b/test/helpers/Setup.t.sol @@ -22,10 +22,16 @@ import { import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.sol"; +import { ISSVBasedApps } from "@ssv/src/core/interfaces/ISSVBasedApps.sol"; import { NonCompliantBApp } from "@ssv/test/mocks/MockNonCompliantBApp.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 { StrategyFactory } from "@ssv/src/core/strategies/StrategyFactory.sol"; +import { StrategyVault } from "@ssv/src/core/strategies/StrategyVault.sol"; import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; @@ -35,6 +41,9 @@ import { } 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"; +import { + UpgradeableBeacon +} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; contract Setup is Test { // Main Contract @@ -43,9 +52,13 @@ contract Setup is Test { StrategyManager public strategyManagerMod; BasedAppsManager public basedAppsManagerMod; ProtocolManager public protocolManagerMod; - + // Factories + StrategyFactory public strategyFactoryProxy; + StrategyFactory public strategyFactoryImplementation; + StrategyVault public strategyVaultImplementation; // Proxies ERC1967Proxy public proxy; // UUPS Proxy contract + UpgradeableBeacon public strategyBeacon; SSVBasedApps public proxiedManager; // Proxy interface for interaction // BApps BasedAppMock public bApp1; @@ -128,12 +141,21 @@ contract Setup is Test { disabledFeatures: 0 }); + strategyFactoryImplementation = new StrategyFactory("0.0.1"); + + strategyFactoryProxy = StrategyFactory( + address( + new ERC1967Proxy(address(strategyFactoryImplementation), "") + ) + ); + bytes memory data = abi.encodeWithSelector( implementation.initialize.selector, address(OWNER), IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactoryProxy), config ); proxy = new ERC1967Proxy(address(implementation), data); @@ -144,6 +166,18 @@ contract Setup is Test { 500, "Initialization failed" ); + + //Strategies + strategyVaultImplementation = new StrategyVault(); + + strategyBeacon = new UpgradeableBeacon( + address(strategyVaultImplementation), + OWNER + ); + strategyBeacon.transferOwnership(OWNER); + + strategyFactoryProxy.initialize(OWNER, strategyBeacon, proxiedManager); + vm.stopPrank(); vm.startPrank(USER1); @@ -178,6 +212,12 @@ contract Setup is Test { vm.label(address(whitelistExample), "WhitelistExample"); vm.label(address(ecdsaVerifierExample), "ECDSAVerifierExample"); vm.label(address(proxiedManager), "BasedAppManagerProxy"); + vm.label(address(strategyFactoryProxy), "StrategyFactoryProxy"); + vm.label(address(strategyBeacon), "StrategyVaultBeaconProxy"); + vm.label( + address(strategyVaultImplementation), + "StrategyVaultImplementation" + ); vm.deal(USER1, INITIAL_USER1_BALANCE_ETH); vm.deal(USER2, INITIAL_USER2_BALANCE_ETH); diff --git a/test/helpers/Utils.t.sol b/test/helpers/Utils.t.sol index 9aeb0728..45ebe53f 100644 --- a/test/helpers/Utils.t.sol +++ b/test/helpers/Utils.t.sol @@ -87,7 +87,7 @@ contract UtilsTest is Setup { ); assertEq(isSet, expectedIsSet, "Obligation is set"); assertEq(obligationPercentage, percentage, "Obligation percentage"); - (address strategyOwner, ) = proxiedManager.strategies(strategyId); + (, address strategyOwner, ) = proxiedManager.strategies(strategyId); if (strategyOwner != address(0)) { assertEq(owner, strategyOwner, "Strategy owner"); } @@ -205,7 +205,7 @@ contract UtilsTest is Setup { uint32 expectedProposedFee, uint256 expectedUpdateTime ) internal view { - (address owner, uint32 fee) = proxiedManager.strategies(strategyId); + (, address owner, uint32 fee) = proxiedManager.strategies(strategyId); (uint32 feeProposed, uint256 feeUpdateTime) = proxiedManager .feeUpdateRequests(strategyId); assertEq( @@ -235,7 +235,7 @@ contract UtilsTest is Setup { address expectedOwner, uint32 expectedFee ) internal view { - (address owner, uint32 fee) = proxiedManager.strategies(strategyId); + (, address owner, uint32 fee) = proxiedManager.strategies(strategyId); assertEq( owner, expectedOwner, @@ -359,4 +359,17 @@ contract UtilsTest is Setup { return ((obligatedAmount * slashingPercentage) / proxiedManager.maxPercentage()); } + + function createSlashContext( + uint32 strategyId, + address bApp, + address token, + uint32 percentage + ) public pure returns (ICore.SlashContext memory c) { + c.strategyId = strategyId; + c.bApp = bApp; + c.token = token; + c.percentage = percentage; + return c; + } } diff --git a/test/mocks/MockBApp.sol b/test/mocks/MockBApp.sol index b9feeb45..b16ee5e1 100644 --- a/test/mocks/MockBApp.sol +++ b/test/mocks/MockBApp.sol @@ -33,4 +33,10 @@ contract BasedAppMock is OwnableBasedApp { if (counter % 2 == 0) return false; else return true; } + + event Received(address, uint256); + + receive() external payable override { + emit Received(msg.sender, msg.value); + } } diff --git a/test/mocks/MockERC20.sol b/test/mocks/MockERC20.sol index 488bd6ae..66996c7d 100644 --- a/test/mocks/MockERC20.sol +++ b/test/mocks/MockERC20.sol @@ -6,6 +6,10 @@ pragma solidity 0.8.30; import "@openzeppelin/contracts/interfaces/IERC20.sol"; import "@openzeppelin/contracts/utils/Context.sol"; +interface IRebase { + function rebase(address account, uint256 amount) external; +} + /** * @dev Implementation of the {IERC20} interface. * @@ -34,7 +38,7 @@ import "@openzeppelin/contracts/utils/Context.sol"; * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ -contract ERC20Mock is Context, IERC20 { +contract ERC20Mock is Context, IERC20, IRebase { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; @@ -169,7 +173,7 @@ contract ERC20Mock is Context, IERC20 { _beforeTokenTransfer(from, to, amount); - _mint(from, amount); + // _mint(from, amount); unchecked { _balances[from] = _balances[from] - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by @@ -203,6 +207,10 @@ contract ERC20Mock is Context, IERC20 { emit Transfer(address(0), account, amount); } + function rebase(address account, uint256 amount) external { + _mint(account, amount); + } + /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. diff --git a/test/middleware/examples/ECDSAVerifier/ECDSAVerifier.t.sol b/test/mocks/examples/ECDSAVerifier/ECDSAVerifier.t.sol similarity index 98% rename from test/middleware/examples/ECDSAVerifier/ECDSAVerifier.t.sol rename to test/mocks/examples/ECDSAVerifier/ECDSAVerifier.t.sol index 30f652e9..043fe835 100644 --- a/test/middleware/examples/ECDSAVerifier/ECDSAVerifier.t.sol +++ b/test/mocks/examples/ECDSAVerifier/ECDSAVerifier.t.sol @@ -19,7 +19,7 @@ contract WhitelistExampleTest is UtilsTest { "" ); assertEq(strategyId1, STRATEGY1, "Should set the correct strategy ID"); - (address owner, uint32 delegationFeeOnRewards) = proxiedManager + (, address owner, uint32 delegationFeeOnRewards) = proxiedManager .strategies(strategyId1); assertEq(owner, USER1, "Should set the correct strategy owner"); assertEq( diff --git a/test/middleware/examples/ECDSAVerifier/client/package-lock.json b/test/mocks/examples/ECDSAVerifier/client/package-lock.json similarity index 100% rename from test/middleware/examples/ECDSAVerifier/client/package-lock.json rename to test/mocks/examples/ECDSAVerifier/client/package-lock.json diff --git a/test/middleware/examples/ECDSAVerifier/client/package.json b/test/mocks/examples/ECDSAVerifier/client/package.json similarity index 100% rename from test/middleware/examples/ECDSAVerifier/client/package.json rename to test/mocks/examples/ECDSAVerifier/client/package.json diff --git a/test/middleware/examples/ECDSAVerifier/client/signature.js b/test/mocks/examples/ECDSAVerifier/client/signature.js similarity index 91% rename from test/middleware/examples/ECDSAVerifier/client/signature.js rename to test/mocks/examples/ECDSAVerifier/client/signature.js index c5387094..697704c0 100644 --- a/test/middleware/examples/ECDSAVerifier/client/signature.js +++ b/test/mocks/examples/ECDSAVerifier/client/signature.js @@ -17,4 +17,4 @@ console.log("✅ Generated test input:"); console.log("Signer Address :", signer); console.log("Message :", message); console.log("Message Hash :", messageHash); -console.log("Signature :", signature.serialized); +console.log("Signature :", signature.serialized); \ No newline at end of file diff --git a/test/middleware/examples/WhitelistExample.t.sol b/test/mocks/examples/WhitelistExample.t.sol similarity index 98% rename from test/middleware/examples/WhitelistExample.t.sol rename to test/mocks/examples/WhitelistExample.t.sol index 1c9a0e68..810d3d33 100644 --- a/test/middleware/examples/WhitelistExample.t.sol +++ b/test/mocks/examples/WhitelistExample.t.sol @@ -21,7 +21,7 @@ contract WhitelistExampleTest is UtilsTest { "" ); assertEq(strategyId1, STRATEGY1, "Should set the correct strategy ID"); - (address owner, uint32 delegationFeeOnRewards) = proxiedManager + (, address owner, uint32 delegationFeeOnRewards) = proxiedManager .strategies(strategyId1); assertEq(owner, USER1, "Should set the correct strategy owner"); assertEq( diff --git a/test/modules/BasedAppsManager.t.sol b/test/modules/BasedAppsManager.t.sol index 4504a778..3c4a90e2 100644 --- a/test/modules/BasedAppsManager.t.sol +++ b/test/modules/BasedAppsManager.t.sol @@ -99,7 +99,7 @@ contract BasedAppsManagerTest is UtilsTest { ); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IBasedAppManager.BAppRegistered( address(bApps[i]), tokenConfigsInput, @@ -275,7 +275,7 @@ contract BasedAppsManagerTest is UtilsTest { function testUpdateBAppMetadata() public { testRegisterBApp(); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, false, false, false); + vm.expectEmit(); emit IBasedAppManager.BAppMetadataURIUpdated( address(bApps[i]), metadataURI @@ -336,15 +336,6 @@ contract BasedAppsManagerTest is UtilsTest { testRegisterBApp(); 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); diff --git a/test/modules/DelegationManager.t.sol b/test/modules/DelegationManager.t.sol index 0a6f55e2..153132df 100644 --- a/test/modules/DelegationManager.t.sol +++ b/test/modules/DelegationManager.t.sol @@ -52,7 +52,7 @@ contract BasedAppManagerDelegateTest is Setup { function testDelegateMinimumBalance() public { uint32 delegatedAmount = 1; vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated( USER1, RECEIVER, @@ -68,7 +68,7 @@ contract BasedAppManagerDelegateTest is Setup { percentageAmount < proxiedManager.maxPercentage() ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated( USER1, RECEIVER, @@ -116,10 +116,10 @@ contract BasedAppManagerDelegateTest is Setup { vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated(USER1, RECEIVER, percentage1); proxiedManager.delegateBalance(RECEIVER, percentage1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); checkDelegation(USER1, RECEIVER, percentage1, percentage1); emit IStrategyManager.DelegationCreated(USER1, RECEIVER2, percentage2); @@ -163,7 +163,7 @@ contract BasedAppManagerDelegateTest is Setup { vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated(USER1, RECEIVER, percentage1); proxiedManager.delegateBalance(RECEIVER, percentage1); checkDelegation(USER1, RECEIVER, percentage1, percentage1); @@ -192,7 +192,7 @@ contract BasedAppManagerDelegateTest is Setup { uint32 updatePercentage = proxiedManager.maxPercentage(); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationUpdated( USER1, RECEIVER, @@ -248,7 +248,7 @@ contract BasedAppManagerDelegateTest is Setup { vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated( USER1, RECEIVER, @@ -257,7 +257,7 @@ contract BasedAppManagerDelegateTest is Setup { proxiedManager.delegateBalance(RECEIVER, delegatedAmount1); checkDelegation(USER1, RECEIVER, delegatedAmount1, delegatedAmount1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated( USER1, RECEIVER2, @@ -285,7 +285,7 @@ contract BasedAppManagerDelegateTest is Setup { testDelegateFullBalance(); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationRemoved(USER1, RECEIVER); proxiedManager.removeDelegatedBalance(RECEIVER); checkDelegationZero(USER1, RECEIVER, 0); @@ -299,14 +299,14 @@ contract BasedAppManagerDelegateTest is Setup { vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationRemoved(USER1, RECEIVER); proxiedManager.removeDelegatedBalance(RECEIVER); checkDelegationZero(USER1, RECEIVER, delegatedAmount2); checkDelegation(USER1, RECEIVER2, delegatedAmount2, delegatedAmount2); uint32 newDelegatedAmount1 = 1; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.DelegationCreated( USER1, RECEIVER, @@ -337,7 +337,7 @@ contract BasedAppManagerDelegateTest is Setup { function testUpdateAccountMetadata() public { string memory metadataURI = "https://account-metadata.com"; vm.startPrank(USER1); - vm.expectEmit(true, false, false, false); + vm.expectEmit(); emit IStrategyManager.AccountMetadataURIUpdated(USER1, metadataURI); proxiedManager.updateAccountMetadataURI(metadataURI); vm.stopPrank(); @@ -347,7 +347,7 @@ contract BasedAppManagerDelegateTest is Setup { testUpdateAccountMetadata(); string memory metadataURI2 = "https://account-metadata-2.com"; vm.startPrank(USER1); - vm.expectEmit(true, false, false, false); + vm.expectEmit(); emit IStrategyManager.AccountMetadataURIUpdated(USER1, metadataURI2); proxiedManager.updateAccountMetadataURI(metadataURI2); vm.stopPrank(); diff --git a/test/modules/ProtocolManager.t.sol b/test/modules/ProtocolManager.t.sol index 39da54f6..fcfe65ed 100644 --- a/test/modules/ProtocolManager.t.sol +++ b/test/modules/ProtocolManager.t.sol @@ -16,6 +16,9 @@ import { import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; +import { + IStrategyFactory +} from "@ssv/src/core/interfaces/IStrategyFactory.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; @@ -251,6 +254,7 @@ contract ProtocolManagerTest is Setup, Ownable2StepUpgradeable { IBasedAppManager(basedAppsManagerMod), IStrategyManager(strategyManagerMod), IProtocolManager(protocolManagerMod), + IStrategyFactory(strategyFactoryProxy), config ); ERC1967Proxy proxy = new ERC1967Proxy( @@ -281,7 +285,7 @@ contract ProtocolManagerTest is Setup, Ownable2StepUpgradeable { /// @notice Updating the flags should emit DisabledFeaturesUpdated function testEmitDisabledFeaturesUpdatedEvent() public { vm.prank(OWNER); - vm.expectEmit(true, false, false, true); + vm.expectEmit(); emit IProtocolManager.DisabledFeaturesUpdated(5); proxiedManager.updateDisabledFeatures(5); } diff --git a/test/modules/RebasingTokens.t.sol b/test/modules/RebasingTokens.t.sol new file mode 100644 index 00000000..48951688 --- /dev/null +++ b/test/modules/RebasingTokens.t.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +import { IERC20, BasedAppMock } from "@ssv/test/helpers/Setup.t.sol"; +import { StrategyManagerTest } from "@ssv/test/modules/StrategyManager.t.sol"; +import { + IStrategyManager +} from "@ssv/src/core/interfaces/IStrategyManager.sol"; +import { + 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"; +import { IRebase } from "@ssv/test/mocks/MockERC20.sol"; + +contract RebasingTokensTest is StrategyManagerTest { + function testBalanceAfterRebase() public { + uint256 depositAmount = 10000; + uint256 rebaseAmount = 10000; + testCreateStrategyAndSingleDeposit(depositAmount); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + IRebase(address(erc20mock)).rebase(strategyAddress, 10000); + checkTotalSharesAndTotalBalance( + STRATEGY1, + address(erc20mock), + depositAmount, + depositAmount + rebaseAmount + ); + checkAccountShares(STRATEGY1, USER1, address(erc20mock), depositAmount); + } + + function testWithdrawalAfterRebasingEvent() public { + ( + uint256 withdrawalAmount, + IERC20 token, + uint256 currentBalance + ) = testProposeWithdrawalFromStrategy(); + vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); + + uint256 oldUserBalance = token.balanceOf(USER1); + + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + // 120.000 + // withdraw 1000, withdraw 1000 shares + // rebase to 220000, 1000 shares now are worth 1833 tokens + uint256 oldStrategyBalance = token.balanceOf(strategyAddress); + + IRebase(address(token)).rebase(strategyAddress, 100000); + uint256 newStrategyBalance = token.balanceOf(strategyAddress); + uint256 newWithdrawalAmount = (newStrategyBalance * withdrawalAmount) / + oldStrategyBalance; + + vm.prank(USER1); + vm.expectEmit(); + emit IStrategyManager.StrategyWithdrawal( + STRATEGY1, + USER1, + address(token), + newWithdrawalAmount, + false + ); + proxiedManager.finalizeWithdrawal(STRATEGY1, token); + uint256 newShareBalance = currentBalance - withdrawalAmount; + uint256 newBalance = newStrategyBalance - newWithdrawalAmount; + uint256 newUserBalance = token.balanceOf(USER1); + + assertNotEq(newUserBalance, oldUserBalance); + + assertEq(newUserBalance, oldUserBalance + newWithdrawalAmount); + + checkAccountShares(STRATEGY1, USER1, address(token), newShareBalance); + checkTotalSharesAndTotalBalance( + STRATEGY1, + address(token), + newShareBalance, + newBalance + ); + checkProposedWithdrawal(STRATEGY1, USER1, address(token), 0, 0); + } +} diff --git a/test/modules/SlashingManager.bapp.t.sol b/test/modules/SlashingManager.bapp.t.sol index b5b544fc..b66ded4c 100644 --- a/test/modules/SlashingManager.bapp.t.sol +++ b/test/modules/SlashingManager.bapp.t.sol @@ -75,7 +75,7 @@ contract SlashingManagerTest is StrategyManagerTest { depositAmount ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp1), @@ -84,10 +84,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(bApp1) ); proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); @@ -122,7 +124,7 @@ contract SlashingManagerTest is StrategyManagerTest { vm.prank(USER2); proxiedManager.depositETH{ value: depositAmount }(STRATEGY1); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp1), @@ -131,10 +133,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(bApp1) ); proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -172,10 +176,12 @@ contract SlashingManagerTest is StrategyManagerTest { abi.encodeWithSelector(IStrategyManager.BAppSlashingFailed.selector) ); proxiedManager.slash( - STRATEGY1, - address(bApp2), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp2), + token, + slashPercentage + ), abi.encodePacked("0x00") ); checkTotalSharesAndTotalBalance( @@ -195,6 +201,8 @@ contract SlashingManagerTest is StrategyManagerTest { } function testRevertSlashBAppNotRegistered() public { + uint32 percentage = 9000; + testStrategyOptInToBApp(percentage); uint256 depositAmount = 100_000; vm.prank(USER2); proxiedManager.depositERC20( @@ -208,10 +216,12 @@ contract SlashingManagerTest is StrategyManagerTest { abi.encodeWithSelector(IBasedAppManager.BAppNotRegistered.selector) ); proxiedManager.slash( - STRATEGY1, - USER1, - address(erc20mock), - slashPercentage, + createSlashContext( + STRATEGY1, + USER1, + address(erc20mock), + slashPercentage + ), abi.encodePacked("0x00") ); } @@ -246,7 +256,7 @@ contract SlashingManagerTest is StrategyManagerTest { function testWithdrawSlashingFundErc20() public { uint32 slashPercentage = 9000; testSlashBApp(slashPercentage); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.SlashingFundWithdrawn(address(erc20mock), 1); vm.prank(USER1); bApp1.withdrawSlashingFund(address(erc20mock), 1); @@ -262,7 +272,7 @@ contract SlashingManagerTest is StrategyManagerTest { slashPercentage ); testSlashBAppWithEth(slashPercentage); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.SlashingFundWithdrawn(ETH_ADDRESS, slashAmount); vm.prank(USER1); bApp1.withdrawETHSlashingFund(slashAmount); @@ -322,15 +332,18 @@ contract SlashingManagerTest is StrategyManagerTest { uint32 slashPercentage = 100; uint32 percentage = 9000; uint256 depositAmount = 100_000; - uint256 withdrawalAmount = (depositAmount * 50) / 100; + uint256 withdrawalAmount = (depositAmount * 50) / 100; // 50000 address token = address(erc20mock); + uint256 slashAmount = calculateSlashAmount( depositAmount, percentage, slashPercentage - ); + ); // slashable amount = 90_000 + // slash Amount = 900 testStrategyOptInToBApp(percentage); + assertEq(slashAmount, 900, "Should match the expected slash amount"); vm.prank(USER2); proxiedManager.depositERC20( @@ -338,6 +351,14 @@ contract SlashingManagerTest is StrategyManagerTest { IERC20(erc20mock), depositAmount ); + assertEq( + proxiedManager.getSlashableBalance( + STRATEGY1, + address(bApp1), + token + ), + 90_000 + ); vm.prank(USER2); proxiedManager.proposeWithdrawal(STRATEGY1, token, withdrawalAmount); @@ -350,7 +371,7 @@ contract SlashingManagerTest is StrategyManagerTest { ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp1), @@ -358,11 +379,14 @@ contract SlashingManagerTest is StrategyManagerTest { slashPercentage, address(bApp1) ); + proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -416,7 +440,7 @@ contract SlashingManagerTest is StrategyManagerTest { ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp1), @@ -425,10 +449,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(bApp1) ); proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -475,7 +501,7 @@ contract SlashingManagerTest is StrategyManagerTest { depositAmount ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp1), @@ -485,10 +511,12 @@ contract SlashingManagerTest is StrategyManagerTest { ); checkGeneration(STRATEGY1, token, 0); proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -560,10 +588,12 @@ contract SlashingManagerTest is StrategyManagerTest { ) ); proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); checkTotalSharesAndTotalBalance( @@ -600,10 +630,12 @@ contract SlashingManagerTest is StrategyManagerTest { ); proxiedManager.proposeWithdrawal(STRATEGY1, token, withdrawalAmount); proxiedManager.slash( - STRATEGY1, - address(bApp1), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + token, + slashPercentage + ), abi.encodePacked("0x00") ); @@ -658,7 +690,7 @@ contract SlashingManagerTest is StrategyManagerTest { depositAmount ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp3), @@ -667,10 +699,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(0) ); proxiedManager.slash( - STRATEGY1, - address(bApp3), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp3), + token, + slashPercentage + ), abi.encodePacked("0x00") ); (uint32 adjustedPercentage, ) = proxiedManager.obligations( @@ -691,6 +725,48 @@ contract SlashingManagerTest is StrategyManagerTest { checkSlashingFund(address(0), token, slashAmount); } + function testRevertSlashBAppAdjustInvalidAmount( + uint32 slashPercentage + ) public { + uint32 percentage = 10_000; + address token = address(erc20mock); + uint256 depositAmount = 1; + vm.assume( + percentage > 0 && + percentage <= proxiedManager.maxPercentage() && + slashPercentage > 0 && + slashPercentage < proxiedManager.maxPercentage() + ); + uint256 slashAmount = calculateSlashAmount( + depositAmount, + percentage, + slashPercentage + ); + assertEq(slashAmount, 0, "Should return 0 as slash amount"); + testStrategyOptInToBApp(percentage); + vm.prank(USER2); + proxiedManager.depositERC20( + STRATEGY1, + IERC20(erc20mock), + depositAmount + ); + vm.prank(USER1); + vm.expectRevert( + abi.encodeWithSelector( + IStrategyManager.InsufficientSlashAmount.selector + ) + ); + proxiedManager.slash( + createSlashContext( + STRATEGY1, + address(bApp3), + token, + slashPercentage + ), + abi.encodePacked("0x00") + ); + } + function testSlashBAppAdjust( uint32 slashPercentage, uint256 depositAmount @@ -698,7 +774,7 @@ contract SlashingManagerTest is StrategyManagerTest { uint32 percentage = 10_000; address token = address(erc20mock); vm.assume( - depositAmount > 0 && + depositAmount > 10000 && depositAmount <= proxiedManager.maxShares() && percentage > 0 && percentage <= proxiedManager.maxPercentage() && @@ -718,7 +794,7 @@ contract SlashingManagerTest is StrategyManagerTest { depositAmount ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp3), @@ -727,10 +803,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(0) ); proxiedManager.slash( - STRATEGY1, - address(bApp3), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp3), + token, + slashPercentage + ), abi.encodePacked("0x00") ); uint32 adjustedPercentage = checkAdjustedPercentage( @@ -777,7 +855,7 @@ contract SlashingManagerTest is StrategyManagerTest { depositAmount ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp3), @@ -786,10 +864,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(0) ); proxiedManager.slash( - STRATEGY1, - address(bApp3), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp3), + token, + slashPercentage + ), abi.encodePacked("0x00") ); (uint32 adjustedPercentage, ) = proxiedManager.obligations( @@ -823,7 +903,7 @@ contract SlashingManagerTest is StrategyManagerTest { vm.prank(USER2); proxiedManager.depositETH{ value: depositAmount }(STRATEGY1); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp3), @@ -832,10 +912,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(0) ); proxiedManager.slash( - STRATEGY1, - address(bApp3), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp3), + token, + slashPercentage + ), abi.encodePacked("0x00") ); (uint32 adjustedPercentage, ) = proxiedManager.obligations( @@ -856,6 +938,10 @@ contract SlashingManagerTest is StrategyManagerTest { checkSlashingFund(address(0), token, slashAmount); } + function testAE() public { + testSlashBAppAdjustBasicETH(); + } + function testSlashBAppAdjustBasicETH() public { uint256 depositAmount = 100 ether; address token = ETH_ADDRESS; @@ -872,7 +958,7 @@ contract SlashingManagerTest is StrategyManagerTest { vm.prank(USER2); proxiedManager.depositETH{ value: depositAmount }(STRATEGY1); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, address(bApp4), @@ -881,10 +967,12 @@ contract SlashingManagerTest is StrategyManagerTest { address(bApp4) ); proxiedManager.slash( - STRATEGY1, - address(bApp4), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp4), + token, + slashPercentage + ), abi.encodePacked("0x00") ); (uint32 adjustedPercentage, ) = proxiedManager.obligations( @@ -937,10 +1025,12 @@ contract SlashingManagerTest is StrategyManagerTest { ) ); proxiedManager.slash( - STRATEGY1, - address(bApp1), - address(erc20mock), - slashPercentage, + createSlashContext( + 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 0dbc34cc..c5b9ecf5 100644 --- a/test/modules/SlashingManager.eoa.t.sol +++ b/test/modules/SlashingManager.eoa.t.sol @@ -60,10 +60,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { ); vm.prank(USER1); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -105,10 +102,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { checkAccountShares(STRATEGY1, USER2, token, depositAmount); vm.prank(USER1); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -147,10 +141,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { vm.prank(USER1); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -174,10 +165,12 @@ contract SlashingManagerEOATest is StrategyManagerTest { abi.encodeWithSelector(ValidationLib.InvalidPercentage.selector) ); proxiedManager.slash( - STRATEGY1, - USER1, - address(erc20mock), - slashPercentage, + createSlashContext( + STRATEGY1, + USER1, + address(erc20mock), + slashPercentage + ), abi.encodePacked("0x00") ); } @@ -195,10 +188,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { ) ); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); } @@ -219,10 +209,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { vm.prank(USER2); vm.expectRevert(); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); } @@ -231,7 +218,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { uint256 slashAmount = 100; uint32 slashPercentage = 9000; testSlashEOA(slashPercentage); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.SlashingFundWithdrawn( address(erc20mock), slashAmount @@ -244,7 +231,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { uint256 slashAmount = 0.2 ether; uint32 slashPercentage = 1000; testSlashEOAWithEth(slashPercentage); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.SlashingFundWithdrawn(ETH_ADDRESS, slashAmount); vm.prank(USER1); proxiedManager.withdrawETHSlashingFund(slashAmount); @@ -286,10 +273,12 @@ contract SlashingManagerEOATest is StrategyManagerTest { ); vm.expectRevert(); proxiedManager.slash( - STRATEGY1, - address(nonCompliantBApp), - token, - slashPercentage, + createSlashContext( + STRATEGY1, + address(nonCompliantBApp), + token, + slashPercentage + ), abi.encodePacked("0x00") ); vm.stopPrank(); @@ -298,7 +287,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { function testRevertSlashEOANotRegistered() public { uint256 depositAmount = 100_000; uint32 slashPercentage = 100; - + testStrategyOptInToBAppEOA(10_000); vm.prank(USER2); proxiedManager.depositERC20( STRATEGY1, @@ -310,10 +299,12 @@ contract SlashingManagerEOATest is StrategyManagerTest { abi.encodeWithSelector(IBasedAppManager.BAppNotRegistered.selector) ); proxiedManager.slash( - STRATEGY1, - USER1, - address(erc20mock), - slashPercentage, + createSlashContext( + STRATEGY1, + USER3, + address(erc20mock), + slashPercentage + ), abi.encodePacked("0x00") ); } @@ -339,10 +330,12 @@ contract SlashingManagerEOATest is StrategyManagerTest { ) ); proxiedManager.slash( - STRATEGY1, - USER1, - address(erc20mock), - slashPercentage, + createSlashContext( + STRATEGY1, + USER1, + address(erc20mock), + slashPercentage + ), abi.encodePacked("0x00") ); } @@ -425,7 +418,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, USER1, @@ -434,10 +427,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { USER1 ); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -493,7 +483,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategySlashed( STRATEGY1, USER1, @@ -502,10 +492,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { USER1 ); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; @@ -554,10 +541,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { checkGeneration(STRATEGY1, token, 0); vm.prank(USER1); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); checkGeneration(STRATEGY1, token, 1); @@ -657,10 +641,7 @@ contract SlashingManagerEOATest is StrategyManagerTest { ); proxiedManager.proposeWithdrawal(STRATEGY1, token, withdrawalAmount); proxiedManager.slash( - STRATEGY1, - USER1, - token, - slashPercentage, + createSlashContext(STRATEGY1, USER1, token, slashPercentage), abi.encodePacked("0x00") ); uint256 newStrategyBalance = depositAmount - slashAmount; // 700 diff --git a/test/modules/StrategyManager.t.sol b/test/modules/StrategyManager.t.sol index d96fe930..68b4d834 100644 --- a/test/modules/StrategyManager.t.sol +++ b/test/modules/StrategyManager.t.sol @@ -12,6 +12,8 @@ import { 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"; +import { IRebase } from "@ssv/test/mocks/MockERC20.sol"; +import { CoreStorageLib } from "@ssv/src/core/libraries/CoreStorageLib.sol"; contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function updateObligation( @@ -30,7 +32,6 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager.finalizeUpdateObligation(strategyId, bApp, token); } - // TESTS function testCreateStrategies() public { vm.startPrank(USER1); @@ -40,9 +41,10 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { INITIAL_USER1_BALANCE_ERC20 ); - vm.expectEmit(true, true, true, true); + vm.expectEmit(true, false, true, true); emit IStrategyManager.StrategyCreated( STRATEGY1, + address(0), USER1, STRATEGY1_INITIAL_FEE, "" @@ -86,7 +88,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { STRATEGY1, "Should have the correct ID for Strategy 1" ); - (address owner, uint32 delegationFeeOnRewards) = proxiedManager + (, address owner, uint32 delegationFeeOnRewards) = proxiedManager .strategies(strategyId1); assertEq(owner, USER1, "Should have the correct strategy owner"); assertEq( @@ -118,7 +120,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { STRATEGY4, "Should have the correct ID for Strategy 3" ); - (owner, delegationFeeOnRewards) = proxiedManager.strategies( + (, owner, delegationFeeOnRewards) = proxiedManager.strategies( strategyId4 ); assertEq(owner, USER2, "Should have the correct strategy owner"); @@ -143,10 +145,16 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function testCreateStrategyWithZeroFee() public { vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); - emit IStrategyManager.StrategyCreated(STRATEGY1, USER1, 0, ""); + vm.expectEmit(true, false, true, true); + emit IStrategyManager.StrategyCreated( + STRATEGY1, + address(0), // Placeholder, cause the address is not possible to predict + USER1, + 0, + "" + ); uint32 strategyId1 = proxiedManager.createStrategy(0, ""); - (, uint32 delegationFeeOnRewards) = proxiedManager.strategies( + (, , uint32 delegationFeeOnRewards) = proxiedManager.strategies( strategyId1 ); assertEq( @@ -182,7 +190,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { vm.assume(amount > 0 && amount < INITIAL_USER1_BALANCE_ERC20); testCreateStrategies(); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyDeposit( STRATEGY1, USER1, @@ -229,7 +237,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { vm.assume(amount > 0 && amount < INITIAL_USER1_BALANCE_ERC20); testCreateStrategies(); vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyDeposit( STRATEGY1, USER1, @@ -327,7 +335,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { uint256 depositAmount = 100_000; testCreateStrategies(); vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyDeposit( STRATEGY1, USER1, @@ -404,14 +412,14 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategies(); testRegisterBApp(); vm.startPrank(USER1); - (address owner, ) = proxiedManager.strategies(STRATEGY1); + (, address owner, ) = proxiedManager.strategies(STRATEGY1); assertEq(owner, USER1, "Should have the correct strategy owner"); address[] memory tokensInput = new address[](1); tokensInput[0] = address(erc20mock); uint32[] memory obligationPercentagesInput = new uint32[](1); obligationPercentagesInput[0] = percentage; for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -448,21 +456,21 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategies(); testRegisterBApp(); vm.startPrank(USER1); - (address owner, ) = proxiedManager.strategies(STRATEGY1); + (, address owner, ) = proxiedManager.strategies(STRATEGY1); assertEq(owner, USER1, "Should have set the correct strategy owner"); address[] memory tokensInput = new address[](1); tokensInput[0] = address(erc20mock); uint32[] memory obligationPercentagesInput = new uint32[](1); obligationPercentagesInput[0] = percentage; bytes memory data = abi.encodePacked("0x00"); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit BasedAppMock.OptInToBApp( STRATEGY1, tokensInput, obligationPercentagesInput, data ); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApp1), @@ -498,13 +506,13 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategies(); testRegisterBAppWithEOA(); vm.startPrank(USER1); - (address owner, ) = proxiedManager.strategies(STRATEGY1); + (, address owner, ) = proxiedManager.strategies(STRATEGY1); assertEq(owner, USER1, "Should have set the correct strategy owner"); address[] memory tokensInput = new address[](1); tokensInput[0] = address(erc20mock); uint32[] memory obligationPercentagesInput = new uint32[](1); obligationPercentagesInput[0] = percentage; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, USER1, @@ -538,13 +546,13 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategies(); testRegisterBAppWithEOAWithEth(); vm.startPrank(USER1); - (address owner, ) = proxiedManager.strategies(STRATEGY1); + (, address owner, ) = proxiedManager.strategies(STRATEGY1); assertEq(owner, USER1, "Should have set the correct strategy owner"); address[] memory tokensInput = new address[](1); tokensInput[0] = ETH_ADDRESS; uint32[] memory obligationPercentagesInput = new uint32[](1); obligationPercentagesInput[0] = percentage; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, USER1, @@ -578,13 +586,13 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategies(); testRegisterBAppFromNonBAppContract(); vm.startPrank(USER1); - (address owner, ) = proxiedManager.strategies(STRATEGY1); + (, address owner, ) = proxiedManager.strategies(STRATEGY1); assertEq(owner, USER1, "Should have set the correct strategy owner"); address[] memory tokensInput = new address[](1); tokensInput[0] = address(erc20mock); uint32[] memory obligationPercentagesInput = new uint32[](1); obligationPercentagesInput[0] = percentage; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(nonCompliantBApp), @@ -654,7 +662,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testRegisterBAppWithNoTokens(); vm.startPrank(USER1); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -702,7 +710,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { uint32[] memory obligationPercentagesInput = new uint32[](2); obligationPercentagesInput[0] = percentage; obligationPercentagesInput[1] = percentage; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApp1), @@ -787,7 +795,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { percentage ); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -858,7 +866,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { percentage ); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -899,7 +907,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { percentage ); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -1022,7 +1030,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { ); testStrategyOptInToBApp(9000); vm.startPrank(USER1); - (address owner, ) = proxiedManager.strategies(STRATEGY1); + (, address owner, ) = proxiedManager.strategies(STRATEGY1); assertEq(owner, USER1, "Strategy owner"); address[] memory tokensInput = new address[](1); tokensInput[0] = address(erc20mock); @@ -1080,8 +1088,8 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testStrategyOptInToBApp(9000); vm.startPrank(USER1); checkAccountShares(STRATEGY1, USER1, address(erc20mock2), 0); - checkTotalSharesAndTotalBalance(STRATEGY1, USER1, 0, 0); - vm.expectEmit(true, true, true, true); + checkTotalSharesAndTotalBalance(STRATEGY1, address(erc20mock2), 0, 0); + vm.expectEmit(); emit IStrategyManager.StrategyDeposit( STRATEGY1, USER1, @@ -1104,7 +1112,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { uint256 amount = 1_000_000 ether; testStrategyOptInToBApp(percentage); vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyDeposit( STRATEGY1, USER1, @@ -1152,7 +1160,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function testCreateStrategyETHAndDepositETH() public { testStrategyOptInToBAppWithETH(); vm.startPrank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyDeposit( STRATEGY1, USER1, @@ -1266,7 +1274,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { uint32 percentage = 9500; address token = address(erc20mock2); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationCreated( STRATEGY1, address(bApps[i]), @@ -1348,7 +1356,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { uint32 proposedFee ) public { testStrategyOptInToBApp(9000); - (, uint32 fee) = proxiedManager.strategies(STRATEGY1); + (, , uint32 fee) = proxiedManager.strategies(STRATEGY1); vm.assume( proposedFee < proxiedManager.maxPercentage() && proposedFee > fee + proxiedManager.maxFeeIncrement() @@ -1364,7 +1372,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function testRevertStrategyFeeUpdateFailsWithSameFeeValue() public { testStrategyOptInToBApp(9000); - (, uint32 fee) = proxiedManager.strategies(STRATEGY1); + (, , uint32 fee) = proxiedManager.strategies(STRATEGY1); vm.prank(USER1); vm.expectRevert( abi.encodeWithSelector(IStrategyManager.FeeAlreadySet.selector) @@ -1379,7 +1387,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testStrategyOptInToBApp(9000); proposedFee = 505; vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyFeeUpdateProposed( STRATEGY1, USER1, @@ -1405,7 +1413,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { timeBeforeLimit ); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyFeeUpdated( STRATEGY1, USER1, @@ -1450,7 +1458,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function testStrategyFastFeeUpdate() public { testStrategyOptInToBApp(9000); vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyFeeUpdated(STRATEGY1, USER1, 1, true); proxiedManager.reduceFee(STRATEGY1, 1); checkFee(STRATEGY1, USER1, 1); @@ -1537,7 +1545,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationUpdateProposed( STRATEGY1, address(bApps[i]), @@ -1572,7 +1580,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { vm.warp(block.timestamp + proxiedManager.obligationTimelockPeriod()); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationUpdated( STRATEGY1, address(bApps[i]), @@ -1610,7 +1618,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { ); for (uint256 i = 0; i < bApps.length; i++) { vm.prank(USER1); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationUpdated( STRATEGY1, address(bApps[i]), @@ -1650,7 +1658,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager.obligationExpireTime() ); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationUpdated( STRATEGY1, address(bApps[i]), @@ -1796,7 +1804,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { withdrawalAmount = 1000; token = erc20mock; currentBalance = 120_000; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyWithdrawalProposed( STRATEGY1, USER1, @@ -1832,7 +1840,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { uint256 currentBalance ) = testProposeWithdrawalFromStrategy(); vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyWithdrawal( STRATEGY1, USER1, @@ -1840,9 +1848,15 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { withdrawalAmount, false ); + uint256 oldUserBalance = token.balanceOf(USER1); vm.prank(USER1); proxiedManager.finalizeWithdrawal(STRATEGY1, token); uint256 newBalance = currentBalance - withdrawalAmount; + uint256 newUserBalance = token.balanceOf(USER1); + + assertNotEq(newUserBalance, oldUserBalance); + assertEq(newUserBalance, oldUserBalance + withdrawalAmount); + checkAccountShares(STRATEGY1, USER1, address(token), newBalance); checkTotalSharesAndTotalBalance( STRATEGY1, @@ -1870,7 +1884,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategyETHAndDepositETH(); vm.assume(withdrawalAmount > 0 && withdrawalAmount <= 1 ether); currentBalance = 1 ether; - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyWithdrawalProposed( STRATEGY1, USER1, @@ -1895,7 +1909,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { withdrawalAmount ); vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.StrategyWithdrawal( STRATEGY1, USER1, @@ -1930,11 +1944,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { function testRevertAsyncWithdrawETHFromStrategyWithMadeUpToken() public { testCreateStrategyAndMultipleDeposits(100_000, 20_000, 200_000); vm.prank(USER1); - vm.expectRevert( - abi.encodeWithSelector( - IStrategyManager.InsufficientLiquidity.selector - ) - ); + vm.expectRevert(); // It will fail cause the address has no balanceOf() proxiedManager.proposeWithdrawal(STRATEGY1, address(1), 1000); } @@ -2033,7 +2043,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { percentage ); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -2057,7 +2067,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager ); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationCreated( STRATEGY1, address(bApps[i]), @@ -2095,7 +2105,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { ); for (uint256 i = 0; i < bApps.length; i++) { - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.BAppOptedInByStrategy( STRATEGY1, address(bApps[i]), @@ -2118,8 +2128,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { true, proxiedManager ); - - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit IStrategyManager.ObligationCreated( STRATEGY1, address(bApps[i]), @@ -2233,7 +2242,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { testCreateStrategies(); string memory metadataURI = "https://metadata.com"; vm.startPrank(USER1); - vm.expectEmit(true, false, false, false); + vm.expectEmit(); emit IStrategyManager.StrategyMetadataURIUpdated( STRATEGY1, metadataURI @@ -2285,10 +2294,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager.depositERC20(STRATEGY1, erc20mock, 10_000); proxiedManager.proposeWithdrawal(STRATEGY1, address(erc20mock), 10_000); proxiedManager.slash( - STRATEGY1, - address(bApp1), - address(erc20mock), - 5000, + createSlashContext( + STRATEGY1, + address(bApp1), + address(erc20mock), + 5000 + ), abi.encode("0x00") ); vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); @@ -2304,10 +2315,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager.depositERC20(STRATEGY1, erc20mock, 10_000); proxiedManager.proposeWithdrawal(STRATEGY1, address(erc20mock), 10_000); proxiedManager.slash( - STRATEGY1, - address(bApp1), - address(erc20mock), - 10_000, + createSlashContext( + STRATEGY1, + address(bApp1), + address(erc20mock), + 10_000 + ), abi.encode("0x00") ); vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); @@ -2327,11 +2340,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager.proposeWithdrawalETH(STRATEGY1, 1 ether); uint32 slashPercentage = 5000; proxiedManager.slash( - STRATEGY1, - address(bApp1), - ETH_ADDRESS, - // 0.5 ether, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + ETH_ADDRESS, + slashPercentage + ), abi.encode("0x00") ); vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); @@ -2348,10 +2362,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { proxiedManager.proposeWithdrawalETH(STRATEGY1, 1 ether); uint32 slashPercentage = 10_000; proxiedManager.slash( - STRATEGY1, - address(bApp1), - ETH_ADDRESS, - slashPercentage, + createSlashContext( + STRATEGY1, + address(bApp1), + ETH_ADDRESS, + slashPercentage + ), abi.encode("0x00") ); vm.warp(block.timestamp + proxiedManager.withdrawalTimelockPeriod()); @@ -2392,7 +2408,7 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { ); } - function testSlashWhenEnabled() public { + function testSlashWhenEnabled(uint32 slashPercentage) public { uint32 pct = proxiedManager.maxPercentage(); // register & opt‐in testStrategyOptInToBApp(pct); @@ -2404,10 +2420,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { // slash from bApp1 vm.prank(address(bApp1)); proxiedManager.slash( - STRATEGY1, - address(bApp1), - address(erc20mock), - 10_000, + createSlashContext( + STRATEGY1, + address(bApp1), + address(erc20mock), + 10_000 + ), "" ); // after slash, strategy balance should be zero @@ -2428,10 +2446,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { abi.encodeWithSelector(IStrategyManager.SlashingDisabled.selector) ); proxiedManager.slash( - STRATEGY1, - address(bApp1), - address(erc20mock), - 10_000, + createSlashContext( + STRATEGY1, + address(bApp1), + address(erc20mock), + 10_000 + ), "" ); } @@ -2450,10 +2470,12 @@ contract StrategyManagerTest is UtilsTest, BasedAppsManagerTest { // should no longer revert vm.prank(address(bApp1)); proxiedManager.slash( - STRATEGY1, - address(bApp1), - address(erc20mock), - 10_000, + createSlashContext( + STRATEGY1, + address(bApp1), + address(erc20mock), + 10_000 + ), "" ); // confirm balance dropped diff --git a/test/strategies/StrategyVault.t.sol b/test/strategies/StrategyVault.t.sol new file mode 100644 index 00000000..7cf56568 --- /dev/null +++ b/test/strategies/StrategyVault.t.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.30; + +import { StrategyManagerTest } from "@ssv/test/modules/StrategyManager.t.sol"; + +import { IStrategyVault } from "@ssv/src/core/interfaces/IStrategyVault.sol"; + +contract StrategyVaultTest is StrategyManagerTest { + function testRevertAttackerTryingWithdrawal() public { + testCreateStrategyAndSingleDeposit(1 ether); + vm.startPrank(ATTACKER); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + vm.expectRevert( + abi.encodeWithSelector(IStrategyVault.UnauthorizedCaller.selector) + ); + IStrategyVault(strategyAddress).withdraw( + erc20mock, + 1, + address(ATTACKER) + ); + } + + function testRevertAttackerTryingWithdrawalETH() public { + testCreateStrategyAndSingleDeposit(1 ether); + vm.startPrank(ATTACKER); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + vm.expectRevert( + abi.encodeWithSelector(IStrategyVault.UnauthorizedCaller.selector) + ); + IStrategyVault(strategyAddress).withdrawETH(1, address(ATTACKER)); + } + + function testRevertFailTryingWithdrawalETH() public { + testCreateStrategyAndSingleDeposit(1 ether); + vm.startPrank(address(proxiedManager)); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + vm.expectRevert( + abi.encodeWithSelector(IStrategyVault.ETHTransferFailed.selector) + ); + // this results in EvmError: OutOfFunds + IStrategyVault(strategyAddress).withdrawETH(1, address(proxiedManager)); + } + + function testRevertZeroAmountWithdrawal() public { + testCreateStrategyAndSingleDeposit(1 ether); + vm.startPrank(address(proxiedManager)); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + vm.expectRevert( + abi.encodeWithSelector(IStrategyVault.InvalidZeroAmount.selector) + ); + IStrategyVault(strategyAddress).withdraw( + erc20mock, + 0, + address(proxiedManager) + ); + } + + function testRevertZeroAmountWithdrawalETH() public { + testCreateStrategyAndSingleDeposit(1 ether); + vm.startPrank(address(proxiedManager)); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + vm.expectRevert( + abi.encodeWithSelector(IStrategyVault.InvalidZeroAmount.selector) + ); + IStrategyVault(strategyAddress).withdrawETH(0, address(proxiedManager)); + } + + function testRevertSendEth() public { + testCreateStrategyAndSingleDeposit(1 ether); + vm.startPrank(ATTACKER); + (address strategyAddress, , ) = proxiedManager.strategies(STRATEGY1); + vm.expectRevert( + abi.encodeWithSelector(IStrategyVault.UnauthorizedCaller.selector) + ); + payable(strategyAddress).transfer(1 ether); + } +}