diff --git a/.gitignore b/.gitignore index 2ab6307d..68a6866c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,6 @@ out/ /broadcast/**/dry-run/ /broadcast -# Docs -docs/ - # Dotenv file .env @@ -21,6 +18,4 @@ coverage/ report/ lcov.info -.gas-snapshot - -test.txt \ No newline at end of file +.gas-snapshot \ No newline at end of file diff --git a/README.md b/README.md index 2f625457..dda8eab2 100644 --- a/README.md +++ b/README.md @@ -61,82 +61,25 @@ __`❍ forge test`__   -## 🔨 _Slashing Mechanism_ +## 🔨 _Slashing & Withdrawals Mechanisms_ -The `slash` function allows for the reduction of a strategy’s token balance under specific conditions, either as a penalty or to enforce protocol-defined behavior. Slashing can happen in two distinct modes, depending on whether: +[Slashing & Withdrawals](./guides/slashing-and-withdrawals.md) -**1)** The bApp is a compliant smart contract; +[Generation pattern](./guides/generations.md) -**2)** The bApp is a non-compliant smart contract or an EOA. +## :gear: _Feature Activation_ -### 🧠 Compliant BApp - -If the bApp is a compliant contract implementing the required interface `IBasedApp`, - -The slash function of the bApp is called: `(success, receiver, exit) = IBasedApp(bApp).slash(...)` - -* `data` parameter is forwarded and may act as a proof or auxiliary input. - -* The bApp decides: - - * Who receives the slashed funds by setting the `receiver` fund, it can burn by setting the receiver as `address(0)`; - - * Whether to exit the strategy or adjust obligations; - - * If `exit == true`, the strategy is exited and the obligation value is set to 0; - - * Otherwise, obligations are adjusted proportionally based on remaining balances, the new obligated amount is set to the previous one less the slashed amount; - - * Funds are credited to the receiver in the slashing fund. - -### 🔐 Non-compliant bApp (EOA or Non-compliant Contract) - -If the bApp is an EOA or does not comply with the required interface: - -* Only the bApp itself can invoke slashing; - -* The receiver of slashed funds is forcibly set to the bApp itself; - -* The strategy is always exited (no obligation adjustment); - -* Funds are added to the bApp’s slashing fund. - -### ⏳ Post Slashing - -⚠️ Important: After an obligation has been exited, it can be updated again to a value greater than 0, but only after a 14-day obligation timelock. - -This acts as a safeguard to prevent immediate re-entry and encourages more deliberate strategy participation. - -### 💸 Slashing Fund - -Slashed tokens are not immediately transferred. They are deposited into an internal slashing fund. - -The `receiver` (set during slashing) can later withdraw them using: - -``` -function withdrawSlashingFund(address token, uint256 amount) external -function withdrawETHSlashingFund(uint256 amount) external -``` - -These functions verify balances and authorize the caller to retrieve their accumulated slashed tokens. - -  - -## :gear: _Feature activation_ - -[Feature activation](./specs/feature_activation.md) +[Feature Activation](./guides/feature-activation.md) ## :page_facing_up: _Whitepaper_ [Whitepaper](https://ssv.network/wp-content/uploads/2025/01/SSV2.0-Based-Applications-Protocol-1.pdf) -  ## :books: _More Resources_ -[Based Apps Onboarding Guide](./doc/bAppOnBoarding.md) +[Based Apps Onboarding Guide](./guides/bApp-onboarding.md) -  ## :rocket: _Deployments_ @@ -164,13 +107,12 @@ __`❍ updateModules`__: specifying the correct module id and the new module add __`❍ upgradeToAndCall`__: specifying the new implementation address. The data field can be left empty in this case. -There is also the `UpgradeProxy.s.sol` script file that can be run easily from the CLI. -### Public Testnet Holesky +### Public Testnet Hoodi | Name | Proxy | Implementation | Notes | | -------- | -------- | -------- | -------- | -| [`BasedApplications`](https://github.com/ssvlabs/based-applications/blob/main/src/BasedAppManager.sol) | [`0x1Bd6ceB98Daf7FfEB590236b720F81b65213836A`](https://holesky.etherscan.io/address/0x1Bd6ceB98Daf7FfEB590236b720F81b65213836A) | [`0x9a09A49870353867b0ce9901B44E84C32B2A47AC`](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) | +| [`SSVBasedApps`](https://github.com/ssvlabs/based-applications/blob/main/src/BasedAppManager.sol) | [``](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) |   diff --git a/artifacts/deploy-hoodi-prod.json b/artifacts/deploy-hoodi-prod.json new file mode 100644 index 00000000..d9a64146 --- /dev/null +++ b/artifacts/deploy-hoodi-prod.json @@ -0,0 +1,25 @@ +{ + "addresses": { + "BAppsModule": "0xCf9894B93C34E9Af8E912EcE7089DCE879740141", + "ProtocolModule": "0xA11022BB79cE25F9bAdfDB426746A4a47a1FD768", + "SSVBasedAppsImpl": "0xe5a75122b158D405518C6752aEE5D40b8FB1C364", + "SSVBasedAppsProxy": "0xc7fCFeEc5FB9962bDC2234A7a25dCec739e27f9f", + "StrategyModule": "0x0B81DcaF34f4455221A7d446eA41A70B8Baf91FF" + }, + "chainInfo": { + "chainId": 560048, + "deploymentBlock": 260728 + }, + "parameters": { + "disabledFeatures": 0, + "feeExpireTime": 3600, + "feeTimelockPeriod": 86400, + "maxFeeIncrement": 500, + "maxShares": 100000000000000000000000000000000000000000000000000, + "obligationExpireTime": 86400, + "obligationTimelockPeriod": 172800, + "tokenUpdateTimelockPeriod": 3600, + "withdrawalExpireTime": 86400, + "withdrawalTimelockPeriod": 172800 + } +} \ No newline at end of file diff --git a/artifacts/deploy-hoodi-stage.json b/artifacts/deploy-hoodi-stage.json index 8923d47c..3e35ccf0 100644 --- a/artifacts/deploy-hoodi-stage.json +++ b/artifacts/deploy-hoodi-stage.json @@ -1,13 +1,25 @@ { "addresses": { - "BAppsModule": "0xF2b0c91b5256aD75585438C2722d5E7119666288", - "ProtocolModule": "0x5C08fb73Ba1E8Fd2894e0A3706C1e4CfBEA3F833", - "SSVBasedAppsImpl": "0x7E438E9264B431Fb62fbE2BFa350b9860cf7E8AA", - "SSVBasedAppsProxy": "0x1F3d4e4aEeA5406e3A7119d8daa31C67Ed3B13de", - "StrategyModule": "0x4b4638B067946c395C32CD0DFE5399E0899C7542" + "BAppsModule": "0xdC97f7935Ed6FDcA407dDdE95EB26f7807E52Dc9", + "ProtocolModule": "0x7AadaE0f159eECCe6a4Faad78FFF94e1eab4C540", + "SSVBasedAppsImpl": "0xfCD7243D8fBb441497fEd67e19B7681C27BB20aD", + "SSVBasedAppsProxy": "0x40d959B95e7c56962D6d388d87921c03734b9C2C", + "StrategyModule": "0x7cF08Af55c6939BB748D4c4D2d219791EDe9dBC1" }, "chainInfo": { "chainId": 560048, - "deploymentBlock": 253405 + "deploymentBlock": 259312 + }, + "parameters": { + "disabledFeatures": 0, + "feeExpireTime": 3600, + "feeTimelockPeriod": 300, + "maxFeeIncrement": 500, + "maxShares": 100000000000000000000000000000000000000000000000000, + "obligationExpireTime": 3600, + "obligationTimelockPeriod": 300, + "tokenUpdateTimelockPeriod": 300, + "withdrawalExpireTime": 3600, + "withdrawalTimelockPeriod": 300 } } \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..4e42a1bc --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book/ \ No newline at end of file diff --git a/docs/book.css b/docs/book.css new file mode 100644 index 00000000..b5ce903f --- /dev/null +++ b/docs/book.css @@ -0,0 +1,13 @@ +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 new file mode 100644 index 00000000..a2e2683a --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,13 @@ +[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 new file mode 100644 index 00000000..19249329 --- /dev/null +++ b/docs/solidity.min.js @@ -0,0 +1,74 @@ +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 new file mode 100644 index 00000000..2a0eba86 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,34 @@ +# 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) + - [❱ 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 new file mode 100644 index 00000000..2c17bcbb --- /dev/null +++ b/docs/src/src/README.md @@ -0,0 +1,5 @@ + + +# Contents +- [core](/src/core) +- [middleware](/src/middleware) diff --git a/docs/src/src/core/README.md b/docs/src/src/core/README.md new file mode 100644 index 00000000..3d574836 --- /dev/null +++ b/docs/src/src/core/README.md @@ -0,0 +1,7 @@ + + +# 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 new file mode 100644 index 00000000..879f1405 --- /dev/null +++ b/docs/src/src/core/SSVBasedApps.sol/contract.SSVBasedApps.md @@ -0,0 +1,572 @@ +# SSVBasedApps +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/SSVBasedApps.sol) + +**Inherits:** +[ISSVBasedApps](/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md), UUPSUpgradeable, Ownable2StepUpgradeable, [IBasedAppManager](/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md), [IStrategyManager](/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md), [IProtocolManager](/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md) + +**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(address[] calldata tokens, uint32[] calldata sharedRiskLevels, 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 disabledFeatures) 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 new file mode 100644 index 00000000..0f5a03d7 --- /dev/null +++ b/docs/src/src/core/interfaces/IBasedAppManager.sol/interface.IBasedAppManager.md @@ -0,0 +1,76 @@ +# IBasedAppManager +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/IBasedAppManager.sol) + + +## Functions +### registerBApp + + +```solidity +function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, 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, address[] tokens, uint32[] sharedRiskLevel, 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 new file mode 100644 index 00000000..c14f6948 --- /dev/null +++ b/docs/src/src/core/interfaces/ICore.sol/interface.ICore.md @@ -0,0 +1,105 @@ +# ICore +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..4672c707 --- /dev/null +++ b/docs/src/src/core/interfaces/IProtocolManager.sol/interface.IProtocolManager.md @@ -0,0 +1,129 @@ +# IProtocolManager +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..cdffb5cb --- /dev/null +++ b/docs/src/src/core/interfaces/ISSVBasedApps.sol/interface.ISSVBasedApps.md @@ -0,0 +1,59 @@ +# ISSVBasedApps +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/interfaces/ISSVBasedApps.sol) + + +## Functions +### getModuleAddress + + +```solidity +function getModuleAddress(SSVCoreModules moduleId) external view returns (address); +``` + +### getVersion + + +```solidity +function getVersion() external pure returns (string memory version); +``` + +### 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(); +``` + +### 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 new file mode 100644 index 00000000..27fcf9dc --- /dev/null +++ b/docs/src/src/core/interfaces/IStrategyManager.sol/interface.IStrategyManager.md @@ -0,0 +1,449 @@ +# IStrategyManager +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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/README.md b/docs/src/src/core/interfaces/README.md new file mode 100644 index 00000000..6997ead1 --- /dev/null +++ b/docs/src/src/core/interfaces/README.md @@ -0,0 +1,8 @@ + + +# 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) diff --git a/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md b/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md new file mode 100644 index 00000000..b20a0c76 --- /dev/null +++ b/docs/src/src/core/libraries/CoreStorageLib.sol/enum.SSVCoreModules.md @@ -0,0 +1,12 @@ +# SSVCoreModules +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..aba2eba0 --- /dev/null +++ b/docs/src/src/core/libraries/CoreStorageLib.sol/library.CoreStorageLib.md @@ -0,0 +1,46 @@ +# CoreStorageLib +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..aa331895 --- /dev/null +++ b/docs/src/src/core/libraries/ProtocolStorageLib.sol/library.ProtocolStorageLib.md @@ -0,0 +1,40 @@ +# ProtocolStorageLib +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..3eba618a --- /dev/null +++ b/docs/src/src/core/libraries/README.md @@ -0,0 +1,8 @@ + + +# 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 new file mode 100644 index 00000000..08c55976 --- /dev/null +++ b/docs/src/src/core/libraries/ValidationLib.sol/constants.ValidationLib.md @@ -0,0 +1,15 @@ +# Constants +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/core/libraries/ValidationLib.sol) + +### 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 new file mode 100644 index 00000000..b62afb71 --- /dev/null +++ b/docs/src/src/core/libraries/ValidationLib.sol/library.ValidationLib.md @@ -0,0 +1,52 @@ +# ValidationLib +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..84eb2c0a --- /dev/null +++ b/docs/src/src/core/modules/BasedAppsManager.sol/contract.BasedAppsManager.md @@ -0,0 +1,75 @@ +# BasedAppsManager +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`tokens`|`address[]`|The list of tokens the bApp accepts; can be empty.| +|`sharedRiskLevels`|`uint32[]`|The shared risk level of the bApp.| +|`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, address[] calldata tokens, uint32[] calldata sharedRiskLevels) internal; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`bApp`|`address`|The address of the bApp| +|`tokens`|`address[]`|The list of tokens to add| +|`sharedRiskLevels`|`uint32[]`|The shared risk levels of the tokens| + + diff --git a/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md b/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md new file mode 100644 index 00000000..605925a0 --- /dev/null +++ b/docs/src/src/core/modules/ProtocolManager.sol/contract.ProtocolManager.md @@ -0,0 +1,93 @@ +# ProtocolManager +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..532896ac --- /dev/null +++ b/docs/src/src/core/modules/README.md @@ -0,0 +1,6 @@ + + +# 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 new file mode 100644 index 00000000..b8697d3c --- /dev/null +++ b/docs/src/src/core/modules/StrategyManager.sol/contract.StrategyManager.md @@ -0,0 +1,604 @@ +# StrategyManager +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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| + + +### _isBApp + +Function to check if an address uses the correct bApp interface + + +```solidity +function _isBApp(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 uses the correct bApp interface| + + +### 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| + + +### 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; +``` + +### 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 new file mode 100644 index 00000000..5f178fe0 --- /dev/null +++ b/docs/src/src/middleware/README.md @@ -0,0 +1,6 @@ + + +# 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 new file mode 100644 index 00000000..0d309593 --- /dev/null +++ b/docs/src/src/middleware/examples/README.md @@ -0,0 +1,4 @@ + + +# 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 new file mode 100644 index 00000000..4df60b4f --- /dev/null +++ b/docs/src/src/middleware/examples/WhitelistExample.sol/contract.WhitelistExample.md @@ -0,0 +1,27 @@ +# WhitelistExample +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..0573fdac --- /dev/null +++ b/docs/src/src/middleware/interfaces/IBasedApp.sol/interface.IBasedApp.md @@ -0,0 +1,59 @@ +# IBasedApp +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/src/middleware/interfaces/IBasedApp.sol) + +**Inherits:** +IERC165 + + +## Functions +### optInToBApp + + +```solidity +function optInToBApp(uint32 strategyId, address[] calldata tokens, uint32[] calldata obligationPercentages, bytes calldata data) external returns (bool); +``` + +### registerBApp + + +```solidity +function registerBApp(address[] calldata tokens, uint32[] calldata sharedRiskLevels, 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); +``` + +### supportsInterface + + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool); +``` + +### 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 new file mode 100644 index 00000000..0c9bda76 --- /dev/null +++ b/docs/src/src/middleware/interfaces/IBasedAppWhitelisted.sol/interface.IBasedAppWhitelisted.md @@ -0,0 +1,44 @@ +# IBasedAppWhitelisted +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..ae5a297b --- /dev/null +++ b/docs/src/src/middleware/interfaces/README.md @@ -0,0 +1,5 @@ + + +# 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 new file mode 100644 index 00000000..d373b492 --- /dev/null +++ b/docs/src/src/middleware/modules/BasedAppWhitelisted.sol/abstract.BasedAppWhitelisted.md @@ -0,0 +1,30 @@ +# BasedAppWhitelisted +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 new file mode 100644 index 00000000..a80ba19f --- /dev/null +++ b/docs/src/src/middleware/modules/README.md @@ -0,0 +1,6 @@ + + +# 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 new file mode 100644 index 00000000..10043154 --- /dev/null +++ b/docs/src/src/middleware/modules/core+roles/AccessControlBasedApp.sol/abstract.AccessControlBasedApp.md @@ -0,0 +1,106 @@ +# AccessControlBasedApp +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external override onlyRole(MANAGER_ROLE); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`tokens`|`address[]`|array of token addresses| +|`sharedRiskLevels`|`uint32[]`|array of shared risk levels| +|`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| + + +### supportsInterface + +Checks if the contract supports the interface + + +```solidity +function supportsInterface(bytes4 interfaceId) public pure override(AccessControl, BasedAppCore) returns (bool isSupported); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`interfaceId`|`bytes4`|interface id| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`isSupported`|`bool`|if the contract supports the interface| + + diff --git a/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md b/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md new file mode 100644 index 00000000..45ec53a6 --- /dev/null +++ b/docs/src/src/middleware/modules/core+roles/OwnableBasedApp.sol/abstract.OwnableBasedApp.md @@ -0,0 +1,77 @@ +# OwnableBasedApp +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external override onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`tokens`|`address[]`|array of token addresses| +|`sharedRiskLevels`|`uint32[]`|array of 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| + + +### supportsInterface + +Checks if the contract supports the interface + + +```solidity +function supportsInterface(bytes4 interfaceId) public pure override(BasedAppCore) returns (bool isSupported); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`interfaceId`|`bytes4`|interface id| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`isSupported`|`bool`|if the contract supports the interface| + + diff --git a/docs/src/src/middleware/modules/core+roles/README.md b/docs/src/src/middleware/modules/core+roles/README.md new file mode 100644 index 00000000..1fbff46f --- /dev/null +++ b/docs/src/src/middleware/modules/core+roles/README.md @@ -0,0 +1,5 @@ + + +# 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 new file mode 100644 index 00000000..6dae71ec --- /dev/null +++ b/docs/src/src/middleware/modules/core/BasedAppCore.sol/abstract.BasedAppCore.md @@ -0,0 +1,167 @@ +# BasedAppCore +[Git Source](https://github.com/ssvlabs/based-applications/blob/f462573124548b82b6a002d4ef069bdfacf5c637/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 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(address[] calldata tokens, uint32[] calldata sharedRiskLevels, string calldata metadataURI) external virtual; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`tokens`|`address[]`|array of token addresses| +|`sharedRiskLevels`|`uint32[]`|array of shared risk levels| +|`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); +``` + +### supportsInterface + +Checks if the contract supports the interface + +*--- CORE LOGIC (TO BE IMPLEMENTED) ---* + +*--- RETURN TRUE IF SUCCESS, FALSE OTHERWISE ---* + +*--- RETURN RECEIVER ADDRESS FOR THE SLASHED FUNDS ---* + + +```solidity +function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`interfaceId`|`bytes4`|interface id| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`bool`|true if the contract supports the interface| + + +### receive + + +```solidity +receive() external payable virtual; +``` + diff --git a/docs/src/src/middleware/modules/core/README.md b/docs/src/src/middleware/modules/core/README.md new file mode 100644 index 00000000..4ac46fa6 --- /dev/null +++ b/docs/src/src/middleware/modules/core/README.md @@ -0,0 +1,4 @@ + + +# Contents +- [BasedAppCore](BasedAppCore.sol/abstract.BasedAppCore.md) diff --git a/doc/bAppOnBoarding.md b/guides/bApp-onboarding.md similarity index 100% rename from doc/bAppOnBoarding.md rename to guides/bApp-onboarding.md diff --git a/doc/feature_activation.md b/guides/feature-activation.md similarity index 100% rename from doc/feature_activation.md rename to guides/feature-activation.md diff --git a/guides/generations.md b/guides/generations.md new file mode 100644 index 00000000..0f16bc12 --- /dev/null +++ b/guides/generations.md @@ -0,0 +1,115 @@ +# Generation Versioning in Strategy Shares + +## Purpose + +When a slashing event fully drains a strategy’s token balance, we need to ensure that any _old_ share‑balances and pending withdrawals become invalidated. The “generation” mechanism provides a simple versioning layer: bumping the `currentGeneration` counter makes all previous per‑account shares stale, preventing them from being redeemed after a full slash. + +## Rationale + +Without generation/versioning: + +1. A user deposits → receives `N` shares. +2. A slash drains the strategy → token balance is zero. +3. If we didn’t invalidate old shares, a user could still call `withdraw`, burn “old” shares, and withdraw zero tokens—potentially confusing or even exploitable in edge flows. + +By grouping every deposit/withdraw epoch under a “generation” number: + +- **Deposits** record both a share count _and_ the current generation at that time. +- On a full‑balance slash, we bump `currentGeneration`. +- Any subsequent withdrawal attempts carry an out‑of‑date generation tag and revert. + +This cleanly tears down all outstanding share balances in one on‑chain operation. + +## High-Level Flow + +1. **Deposit** + - If `totalShares == 0` (fresh strategy), `shares = amount`; + - Store `accountGeneration[msg.sender] = currentGeneration`; + - Add to `accountShareBalance[msg.sender]`. + +2. **Slash & Generation bump** + - If `strategyTokenShares.totalTokenBalance` goes to zero after slashing: + ```solidity + delete strategyTokenShares.totalTokenBalance; + delete strategyTokenShares.totalShareBalance; + strategyTokenShares.currentGeneration += 1; + ``` + - Now `currentGeneration` > any `accountGeneration[...]` recorded so far. + +3. **Withdraw** + - On `proposeWithdrawal` and `finalizeWithdrawal`, we check + ```solidity + if (strategyTokenShares.currentGeneration != + strategyTokenShares.accountGeneration[msg.sender] + ) revert InvalidAccountGeneration(); + ``` + - Out‑of‑date generations cannot proceed. + +## Technical Explanation + +### Storage Layout (excerpt) + +```solidity +struct Shares { + uint256 totalTokenBalance; + uint256 totalShareBalance; + uint256 currentGeneration; + mapping(address => uint256) accountShareBalance; + mapping(address => uint256) accountGeneration; +} +mapping(uint32 strategyId => mapping(address token => Shares)) public strategyTokenShares; +``` + +- `currentGeneration` is a global counter per `(strategyId,token)` pair. +- `accountGeneration[addr]` records which generation that account last deposited in. +- When generations mismatch, the user’s local `accountShareBalance` is considered invalid. + +### Deposit (`_beforeDeposit`) + +```solidity +if (strategyTokenShares.currentGeneration + != strategyTokenShares.accountGeneration[msg.sender] +) { + // brand-new generation for this account + strategyTokenShares.accountGeneration[msg.sender] + = strategyTokenShares.currentGeneration; + strategyTokenShares.accountShareBalance[msg.sender] + = computedShares; +} else { + // same generation: accumulate shares + strategyTokenShares.accountShareBalance[msg.sender] += computedShares; +} +``` + +### Full Slash & Bump + +```solidity +if (strategyTokenShares.totalTokenBalance == 0) { + // clear balances + delete strategyTokenShares.totalTokenBalance; + delete strategyTokenShares.totalShareBalance; + // bump to invalidate everyone else + strategyTokenShares.currentGeneration += 1; +} +``` + +### Withdrawal Check + +```solidity +// in _proposeWithdrawal or finalize: +if (strategyTokenShares.currentGeneration + != strategyTokenShares.accountGeneration[msg.sender] +) revert InvalidAccountGeneration(); +``` + +## References + +- **CoreStorageLib** + Defines `strategyTokenShares` and where `Shares` lives: + [`src/core/libraries/CoreStorageLib.sol`](src/core/libraries/CoreStorageLib.sol) +- **ERC-20 share math + generation logic** + In `StrategyManager._beforeDeposit`, `StrategyManager._proposeWithdrawal`, and the slash handlers: + [`src/core/modules/StrategyManager.sol`](src/core/modules/StrategyManager.sol) +- **IStrategyManager.Shares** + Describes the struct and intent: + [`src/core/interfaces/ICore.sol`](src/core/interfaces/ICore.sol) diff --git a/guides/img/sw_correlated.png b/guides/img/sw_correlated.png new file mode 100644 index 00000000..93ba38ae Binary files /dev/null and b/guides/img/sw_correlated.png differ diff --git a/guides/img/sw_early_attack.png b/guides/img/sw_early_attack.png new file mode 100644 index 00000000..4ed86edb Binary files /dev/null and b/guides/img/sw_early_attack.png differ diff --git a/guides/img/sw_example.png b/guides/img/sw_example.png new file mode 100644 index 00000000..a65e0e04 Binary files /dev/null and b/guides/img/sw_example.png differ diff --git a/guides/img/sw_late_attack.png b/guides/img/sw_late_attack.png new file mode 100644 index 00000000..ca1574b4 Binary files /dev/null and b/guides/img/sw_late_attack.png differ diff --git a/guides/slashing-and-withdrawals.md b/guides/slashing-and-withdrawals.md new file mode 100644 index 00000000..89fa1b48 --- /dev/null +++ b/guides/slashing-and-withdrawals.md @@ -0,0 +1,221 @@ +# Slashing & Withdrawals + +> _This document describes the rationale and technical implementation of slashing and withdrawal mechanisms in the SSV Based Applications platform._ + +--- + +## 1. Rationale & Security Considerations + +Slashing is particularly sensitive to withdrawal operations as they are the gateway for malicious strategies to attempt risk-free attacks — when a strategy commits slashable offenses and then withdraws its funds before suffering any penalty. + +As a security mechanism, withdrawals are implemented as a two-step process: + +1. **Withdrawal Request** (`proposeWithdrawal`) — strategy submits a request and records a share-based amount and timestamp. +2. **Execution** (`finalizeWithdrawal`) — after a configurable time window, the requested amount is released. + +However, to fully prevent risk‑free attacks, bApps should **track pending withdrawals** and **immediately adjust the strategy’s voting weight** to reflect its _future_ obligated balance upon the request. + +--- + +## 2. Illustrative Example + +![alt text](./img/sw_example.png "Example") + +For simplicity, assume **X** obligated tokens correspond to **X%** of voting weight. + +- At **t0**, the strategy’s voting weight is aligned with its obligated balance: 10 tokens ⇒ **10%** voting weight. +- At **t1**, the strategy calls `proposeWithdrawal(6)`. The bApp should immediately align the voting weight to reflect the _future_ obligated balance (10 − 6 = 4 tokens), reducing weight to **4%** even though the tokens remain until finalization. +- At **t1 + Δwindow**, executing the withdrawal releases the 6 tokens, leaving 4 tokens and **4%** weight. + +| Time | Obligated Balance | Voting Weight | +|----------------------------|-------------------|---------------| +| **t0** | 10 tokens | 10% | +| **t1** (_after request_) | 10 tokens | **4%** | +| **t1 + Δwindow** (finalize)| 4 tokens | 4% | + +--- + +## 3. Late‑Attack Scenario + +![alt text](./img/sw_late_attack.png "Late attack") + +Therefore, if the strategy attacks **just before** `finalizeWithdrawal`, its attack power is already capped to **4%**. The bApp can still call `slash(...)` on the remaining obligated tokens, seizing those 4 tokens. + +--- + +## 4. Early‑Attack Scenario + +![alt text](./img/sw_early_attack.png "Early attack") + +A strategy may attack at full **10%** voting power **before** the bApp detects its withdrawal request. In this case, although more powerful, the bApp can still fully slash up to **100%** of the obligation (10 tokens) at any point _during_ the withdrawal waiting period, preventing a risk‑free exit. + +--- + +## 5. Correlated Slashing & Withdrawal Timing + +![alt text](./img/sw_correlated.png "Correlated attack") + +When using correlated slashing, bApps often wait a delay Δ_slash after detecting a slashable offense before executing the penalty. To prevent a strategy from escaping in the gap: + +```text +Δ_slashing < Δ_withdrawal +``` + +That is, the correlated slashing window must be **shorter** than the withdrawal timelock window, ensuring the slash occurs _before_ funds can be withdrawn. + +--- + +## 6. Core On‑Chain Data Structures + +Refer to `libraries/CoreStorageLib.sol` and `libraries/ProtocolStorageLib.sol` for complete definitions. + +```solidity +// CoreStorageLib.Data +mapping(uint32 => mapping(address => mapping(address => ICore.WithdrawalRequest))) withdrawalRequests; +mapping(uint32 => mapping(address => mapping(address => ICore.Obligation))) obligations; +mapping(address => mapping(address => uint256)) slashingFund; + +// ProtocolStorageLib.Data +uint32 withdrawalTimelockPeriod; +uint32 withdrawalExpireTime; +uint32 disabledFeatures; // bitmask: slashingDisabled, withdrawalsDisabled, ... +``` + +--- + +## 7. Key Contract Snippets + +### Propose Withdrawal + +```solidity +function proposeWithdrawal( + uint32 strategyId, + address token, + uint256 amount +) external { + _checkWithdrawalsAllowed(); + _proposeWithdrawal(strategyId, token, amount); +} +``` + +### Finalize Withdrawal + +```solidity +function finalizeWithdrawal( + uint32 strategyId, + IERC20 token +) external nonReentrant { + _checkWithdrawalsAllowed(); + uint256 amount = _finalizeWithdrawal(strategyId, address(token)); + token.safeTransfer(msg.sender, amount); +} +``` + +### Slash Strategy + +```solidity +function slash( + uint32 strategyId, + address bApp, + address token, + uint32 percentage, + bytes calldata data +) external nonReentrant { + _checkSlashingAllowed(); + uint256 slashable = getSlashableBalance(strategyId, bApp, token); + uint256 amount = (slashable * percentage) / MAX_PERCENTAGE; + // delegate to bApp callback, adjust obligations or exit + s.slashingFund[receiver][token] += amount; +} +``` + + +## 8. Consequences for bApps + +When a slashing event or withdrawal occurs, bApps face several technical and economic consequences: + +### 8.1 Reduced Security Collateral +- **Obligated capital depletion** + Slashing permanently removes tokens from the strategy’s collateral. If the strategy was the only provider for that token, the bApp’s total secured stake decreases, potentially exposing it to higher risk. +- **Voting-weight impact** + Reduced obligations also lower the strategy’s voting weight, which may affect governance quorum and the bApp’s ability to influence protocol parameters. + +### 8.2 Financial Flows +- **Slashing fund accrual** + Upon a slash, the bApp (or designated receiver) accumulates funds in its `slashingFund` balance. These can be withdrawn via `withdrawSlashingFund(...)`: + ```solidity + function withdrawSlashingFund(address token, uint256 amount) external nonReentrant; + ``` +- **Capital rebalancing** + The bApp must decide whether to: + 1. **Replenish** obligations by incentivizing strategies to deposit more. + 2. **Adjust risk levels** or reduce service capacity to match the new collateral. + +### 8.3 Governance and Reputation +- **Risk-level adjustment** + Repeated slashing may trigger bApp marketplace logic (off-chain or on-chain) to flag higher shared risk levels for delegators and integrators. +- **Strategy exit** + If a strategy’s obligations drop to zero (full exit), the bApp loses that collateral entirely and must opt-in a new strategy or await new deposits. + +## 9. Slashing Mechanism Details + +This section consolidates the detailed behavior of the `slash(...)` function for both compliant and non‑compliant bApps, as well as post‑slashing rules and the slashing fund lifecycle. + +### 9.1 Modes of Invocation + +**1) Compliant bApp (Smart Contract implementing `IBasedApp`)** + +- The platform calls: + ```solidity + (success, receiver, exit) = IBasedApp(bApp).slash( + strategyId, + token, + percentage, + msg.sender, + data + ); + ``` +- `data` is forwarded, often containing proofs or auxiliary input. +- The bApp contract decides: + - **Receiver** of slashed funds (e.g. burn with `address(0)` or send to a treasury). + - **Exit or Adjust**: + - If `exit == true`, the strategy’s obligation percentage is set to 0 (full exit). + - Otherwise, obligations are reduced proportionally: + ```solidity + newObligated = oldObligated - amount; + newTotal = oldTotal - amount; + obligation.pct = newObligated * MAX_PERCENTAGE / newTotal; + ``` +- Finally, the slashed `amount` is credited to `slashingFund[receiver][token]`. + +**2) Non‑Compliant bApp (EOA or contract not supporting `IBasedApp`)** + +- Only the bApp address itself can invoke `slash(...)`. +- Receiver is forcibly `bApp` address. +- Strategy always **exits**: obligation percentage → 0. +- Slashed tokens accumulate in `slashingFund[bApp][token]`. + +### 9.2 Post‑Slashing Rules + +- **Re‑opt‑in Timelock** + After a strategy obligation is exited (percentage == 0), a new obligation can only be created again after the protocol’s **obligation timelock** (default 14 days). This prevents immediate re‑entry and enforces deliberate participation. + +### 9.3 Slashing Fund Lifecycle + +- **Accrual** + Slashed tokens are held in an internal fund (`slashingFund` mapping) until withdrawal. + +- **Withdrawal** + The designated receiver calls: + ```solidity + function withdrawSlashingFund(address token, uint256 amount) external nonReentrant; + function withdrawETHSlashingFund(uint256 amount) external nonReentrant; + ``` + under non‑zero and balance‑checked preconditions. + +- **Economic Impact** + Withdrawn funds are immediately available to the receiver, while the strategy’s capital and voting power remain permanently reduced. + + + + diff --git a/package-lock.json b/package-lock.json index b60352aa..400094dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "based-applications", - "version": "0.0.1", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "based-applications", - "version": "0.0.1", + "version": "0.1.0", "license": "GPL-3.0", "devDependencies": { "@openzeppelin/contracts-upgradeable": "^5.3.0", diff --git a/package.json b/package.json index 89870459..cd7cf2e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "based-applications", - "version": "0.0.1", + "version": "0.1.0", "description": "SSV Based Applications", "author": "SSV.Network", "repository": { diff --git a/script/DeployAll.sol b/script/DeployAll.sol index 673c0df4..f014367c 100644 --- a/script/DeployAll.sol +++ b/script/DeployAll.sol @@ -38,7 +38,7 @@ contract DeployAll is Script { console.log("ProtocolModule: ", address(protocolMod)); console.log("SSVBasedApps Proxy: ", address(proxy)); - return saveToJson(impl, proxy, strategyMod, bAppsMod, protocolMod); + return saveToJson(impl, proxy, strategyMod, bAppsMod, protocolMod, raw); } function saveToJson( @@ -46,7 +46,8 @@ contract DeployAll is Script { ERC1967Proxy proxy, StrategyManager strategyMod, BasedAppsManager bAppsMod, - ProtocolManager protocolMod + ProtocolManager protocolMod, + string memory raw ) internal returns (string memory) { string memory parent = "parent"; @@ -78,6 +79,54 @@ contract DeployAll is Script { address(protocolMod) ); + string memory parameters = "parameters"; + vm.serializeUint( + parameters, + "feeTimelockPeriod", + raw.readUint(".feeTimelockPeriod") + ); + vm.serializeUint( + parameters, + "feeExpireTime", + raw.readUint(".feeExpireTime") + ); + vm.serializeUint( + parameters, + "withdrawalTimelockPeriod", + raw.readUint(".withdrawalTimelockPeriod") + ); + vm.serializeUint( + parameters, + "withdrawalExpireTime", + raw.readUint(".withdrawalExpireTime") + ); + vm.serializeUint( + parameters, + "obligationTimelockPeriod", + raw.readUint(".obligationTimelockPeriod") + ); + vm.serializeUint( + parameters, + "obligationExpireTime", + raw.readUint(".obligationExpireTime") + ); + vm.serializeUint( + parameters, + "tokenUpdateTimelockPeriod", + raw.readUint(".tokenUpdateTimelockPeriod") + ); + vm.serializeUint(parameters, "maxShares", raw.readUint(".maxShares")); + vm.serializeUint( + parameters, + "maxFeeIncrement", + raw.readUint(".maxFeeIncrement") + ); + string memory parameters_output = vm.serializeUint( + parameters, + "disabledFeatures", + raw.readUint(".disabledFeatures") + ); + string memory chain_info = "chainInfo"; vm.serializeUint(chain_info, "deploymentBlock", block.number); string memory chain_info_output = vm.serializeUint( @@ -92,8 +141,8 @@ contract DeployAll is Script { deployed_addresses, deployed_addresses_output ); - - return vm.serializeString(parent, chain_info, chain_info_output); + vm.serializeString(parent, chain_info, chain_info_output); + return vm.serializeString(parent, parameters, parameters_output); } function deployProxy( diff --git a/script/config/hoodi-stage.json b/script/config/hoodi-stage.json index 9b0f9b60..b7dc49ca 100644 --- a/script/config/hoodi-stage.json +++ b/script/config/hoodi-stage.json @@ -1,11 +1,11 @@ { - "feeTimelockPeriod": 86400, + "feeTimelockPeriod": 300, "feeExpireTime": 3600, - "withdrawalTimelockPeriod": 172800, - "withdrawalExpireTime": 86400, - "obligationTimelockPeriod": 172800, - "obligationExpireTime": 86400, - "tokenUpdateTimelockPeriod": 3600, + "withdrawalTimelockPeriod": 300, + "withdrawalExpireTime": 3600, + "obligationTimelockPeriod": 300, + "obligationExpireTime": 3600, + "tokenUpdateTimelockPeriod": 300, "maxShares": "1e50", "maxFeeIncrement": 500, "disabledFeatures": 0 diff --git a/src/core/SSVBasedApps.sol b/src/core/SSVBasedApps.sol index f17cdcef..31b81362 100644 --- a/src/core/SSVBasedApps.sol +++ b/src/core/SSVBasedApps.sol @@ -570,7 +570,7 @@ contract SSVBasedApps is } function getVersion() external pure returns (string memory) { - return "0.0.1"; + return "0.1.0"; } // ********************************* diff --git a/src/core/libraries/ProtocolStorageLib.sol b/src/core/libraries/ProtocolStorageLib.sol index 579968ef..2f277342 100644 --- a/src/core/libraries/ProtocolStorageLib.sol +++ b/src/core/libraries/ProtocolStorageLib.sol @@ -5,6 +5,7 @@ library ProtocolStorageLib { /// @title SSV Based Apps Storage Protocol /// @notice Represents the operational settings and parameters required by the SSV Based Application Platform struct Data { + uint256 maxShares; uint32 feeTimelockPeriod; uint32 feeExpireTime; uint32 withdrawalTimelockPeriod; @@ -13,7 +14,6 @@ library ProtocolStorageLib { uint32 obligationExpireTime; uint32 tokenUpdateTimelockPeriod; uint32 maxFeeIncrement; - uint256 maxShares; // each bit, starting from the LSB, represents a DISABLED feature // bit 0 = slashingDisabled // bit 1 = withdrawalsDisabled diff --git a/src/core/modules/BasedAppsManager.sol b/src/core/modules/BasedAppsManager.sol index b0ad9980..024ced56 100644 --- a/src/core/modules/BasedAppsManager.sol +++ b/src/core/modules/BasedAppsManager.sol @@ -47,7 +47,7 @@ contract BasedAppsManager is IBasedAppManager { } function updateBAppsTokens( - ICore.TokenConfig[] memory tokenConfigs + ICore.TokenConfig[] calldata tokenConfigs ) external { CoreStorageLib.Data storage s = CoreStorageLib.load(); diff --git a/test/helpers/Setup.t.sol b/test/helpers/Setup.t.sol index 53eb9fcc..d6824a41 100644 --- a/test/helpers/Setup.t.sol +++ b/test/helpers/Setup.t.sol @@ -124,7 +124,7 @@ contract Setup is Test { ); proxy = new ERC1967Proxy(address(implementation), data); proxiedManager = SSVBasedApps(payable(address(proxy))); - assertEq(proxiedManager.getVersion(), "0.0.1", "Version mismatch"); + assertEq(proxiedManager.getVersion(), "0.1.0", "Version mismatch"); assertEq( proxiedManager.maxFeeIncrement(), 500,