diff --git a/.env.sample b/.env.sample new file mode 100644 index 00000000..de8c0bf4 --- /dev/null +++ b/.env.sample @@ -0,0 +1,4 @@ +HOODI_RPC_URL= +MAINNET_RPC_URL= +ETHERSCAN_API_KEY= +DEPLOYER_PRIVATE_KEY= \ No newline at end of file diff --git a/README.md b/README.md index 8fab390b..2f625457 100644 --- a/README.md +++ b/README.md @@ -142,11 +142,11 @@ These functions verify balances and authorize the caller to retrieve their accum ### How to Deploy -**1)** Run the deployment script `DeployProxy.s.sol` defined in `scripts/`: +**2)** Set the environment variables in the `.env` file. -__`❍ npm run deploy:holesky`__: verification is done automatically. +**1)** Run the deployment script `DeployAllHoodi.s.sol` defined in `script/`: -__`❍ npm run deploy:hoodi`__: verification needs to be done manually for now. +__`❍ npm run deploy:hoodi-stage`__: verification is done automatically. ### How to Update Module Contracts diff --git a/artifacts/deploy-hoodi-stage.json b/artifacts/deploy-hoodi-stage.json new file mode 100644 index 00000000..8923d47c --- /dev/null +++ b/artifacts/deploy-hoodi-stage.json @@ -0,0 +1,13 @@ +{ + "addresses": { + "BAppsModule": "0xF2b0c91b5256aD75585438C2722d5E7119666288", + "ProtocolModule": "0x5C08fb73Ba1E8Fd2894e0A3706C1e4CfBEA3F833", + "SSVBasedAppsImpl": "0x7E438E9264B431Fb62fbE2BFa350b9860cf7E8AA", + "SSVBasedAppsProxy": "0x1F3d4e4aEeA5406e3A7119d8daa31C67Ed3B13de", + "StrategyModule": "0x4b4638B067946c395C32CD0DFE5399E0899C7542" + }, + "chainInfo": { + "chainId": 560048, + "deploymentBlock": 253405 + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index f7122ba6..b2904e3f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,6 +7,15 @@ optimizer = true optimizer_runs = 10_000 gas_reports = ["*"] gas_reports_ignore = ["ERC20Mock", "BasedAppMock", "BasedAppMock2", "BasedAppMock3", "ERC1967Proxy"] +fs_permissions = [{ access = "read", path = "./script/config/"}, { access = "write", path = "./artifacts/"}] + +[rpc_endpoints] +hoodi = "${HOODI_RPC_URL}" +mainnet = "${MAINNET_RPC_URL}" + +[etherscan] +hoodi = { key = "${ETHERSCAN_API_KEY}" } +mainnet = { key = "${ETHERSCAN_API_KEY}" } [fmt] bracket_spacing = false diff --git a/lib/forge-std b/lib/forge-std index bf909b22..9530d9ec 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bf909b22fa55e244796dfa920c9639fdffa1c545 +Subproject commit 9530d9ec702df1b27b7f8f50c0a63a11b1b5fba9 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 9586aaf3..da32fb3b 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 9586aaf35241daf4b17e4858bf7c86edbb4b7247 +Subproject commit da32fb3bd89be5ad3c70dd329a34c9f150fa552f diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 773e11a8..50740658 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 773e11a869622361ac63dc1da992d1156f440464 +Subproject commit 50740658e6f1798de061de16155ee196a68e4956 diff --git a/package-lock.json b/package-lock.json index c28090d3..b60352aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "based-applications", - "version": "0.0.0", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "based-applications", - "version": "0.0.0", + "version": "0.0.1", "license": "GPL-3.0", "devDependencies": { "@openzeppelin/contracts-upgradeable": "^5.3.0", diff --git a/package.json b/package.json index 3cccce85..89870459 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "scripts": { "compile": "forge compile", "build": "npm run lint:fix && forge clean && forge build", - "deploy:holesky": "npm run build && forge script scripts/DeployProxy.s.sol --rpc-url $HOLESKY_RPC_URL --private-key $PRIVATE_KEY --verify -vvv --broadcast", - "deploy:hoodi": "npm run build && forge script scripts/DeployProxy.s.sol --rpc-url $HOODI_RPC_URL --private-key $PRIVATE_KEY --etherscan-api-key $ETHERSCAN_API_KEY --verify -vvv --broadcast", - "verify:hoodi": "forge verify-contract --rpc-url https://hoodi.cloud.blockscout.com/api/eth-rpc --verifier blockscout --verifier-url 'https://hoodi.cloud.blockscout.com/api/' $IMPLEMENTATION_ADDRESS src/SSVBasedApps.sol:SSVBasedApps", + "deploy:hoodi-stage": "source .env && forge script script/DeployAllHoodi.s.sol false --sig 'run(bool)' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", + "deploy:hoodi-prod": "source .env && forge script script/DeployAllHoodi.s.sol true --sig 'run(bool)' --rpc-url $HOODI_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", + "deploy:mainnet": "source .env && forge script script/DeployAllMainnet.s.sol --rpc-url $MAINNET_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY --verify -vvv --broadcast", "gas-report": "forge test --gas-report", "generate-docs": "solc --include-path node_modules --base-path . --combined-json userdoc,devdoc src/SSVBasedApps.sol --output-dir ./docs --overwrite", "prepare": "husky", @@ -30,8 +30,8 @@ "lint:check": "prettier --check **.sol", "lint:fix": "prettier --write **.sol", "test": "forge test", - "test-coverage": "forge coverage --no-match-coverage \"(scripts|test)\"", - "test-coverage-report": "forge coverage --no-match-coverage \"(scripts|test)\" --report debug > test.txt" + "test-coverage": "forge coverage --no-match-coverage \"(script|test)\"", + "test-coverage-report": "forge coverage --no-match-coverage \"(script|test)\" --report debug > test.txt" }, "devDependencies": { "@openzeppelin/contracts-upgradeable": "^5.3.0", diff --git a/remappings.txt b/remappings.txt index d79616a2..6d581d02 100644 --- a/remappings.txt +++ b/remappings.txt @@ -3,10 +3,8 @@ @ssv/src/middleware=src/middleware @ssv/lib/=lib/ @ssv/test/=test/ -@ssv/scripts/=scripts/ -@ssv/forge-std/=lib/forge-std/src/ +@ssv/script/=script/ @ssv/src/core=src/core @ssv/src/core/interfaces=src/core/interfaces @ssv/src/core/libraries=src/core/libraries -@ssv/src/core/modules=src/core/modules - +@ssv/src/core/modules=src/core/modules \ No newline at end of file diff --git a/script/DeployAll.sol b/script/DeployAll.sol new file mode 100644 index 00000000..673c0df4 --- /dev/null +++ b/script/DeployAll.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.29; + +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { Script, console } from "forge-std/Script.sol"; +import { stdJson } from "forge-std/StdJson.sol"; + +import { StrategyManager } from "src/core/modules/StrategyManager.sol"; +import { BasedAppsManager } from "src/core/modules/BasedAppsManager.sol"; +import { ProtocolManager } from "src/core/modules/ProtocolManager.sol"; +import { SSVBasedApps } from "src/core/SSVBasedApps.sol"; +import { ProtocolStorageLib } from "src/core/libraries/ProtocolStorageLib.sol"; + +contract DeployAll is Script { + using stdJson for string; + + function _deployAll(string memory raw) internal returns (string memory) { + vm.startBroadcast(); + + SSVBasedApps impl = new SSVBasedApps(); + StrategyManager strategyMod = new StrategyManager(); + BasedAppsManager bAppsMod = new BasedAppsManager(); + ProtocolManager protocolMod = new ProtocolManager(); + + ERC1967Proxy proxy = deployProxy( + impl, + strategyMod, + bAppsMod, + protocolMod, + raw + ); + + vm.stopBroadcast(); + + console.log("SSVBasedApps Impl: ", address(impl)); + console.log("StrategyModule: ", address(strategyMod)); + console.log("BAppsModule: ", address(bAppsMod)); + console.log("ProtocolModule: ", address(protocolMod)); + console.log("SSVBasedApps Proxy: ", address(proxy)); + + return saveToJson(impl, proxy, strategyMod, bAppsMod, protocolMod); + } + + function saveToJson( + SSVBasedApps impl, + ERC1967Proxy proxy, + StrategyManager strategyMod, + BasedAppsManager bAppsMod, + ProtocolManager protocolMod + ) internal returns (string memory) { + string memory parent = "parent"; + + string memory deployed_addresses = "addresses"; + vm.serializeAddress( + deployed_addresses, + "SSVBasedAppsProxy", + address(proxy) + ); + vm.serializeAddress( + deployed_addresses, + "SSVBasedAppsImpl", + address(impl) + ); + vm.serializeAddress( + deployed_addresses, + "StrategyModule", + address(strategyMod) + ); + vm.serializeAddress( + deployed_addresses, + "BAppsModule", + address(bAppsMod) + ); + + string memory deployed_addresses_output = vm.serializeAddress( + deployed_addresses, + "ProtocolModule", + address(protocolMod) + ); + + string memory chain_info = "chainInfo"; + vm.serializeUint(chain_info, "deploymentBlock", block.number); + string memory chain_info_output = vm.serializeUint( + chain_info, + "chainId", + block.chainid + ); + + // serialize all the data + vm.serializeString( + parent, + deployed_addresses, + deployed_addresses_output + ); + + return vm.serializeString(parent, chain_info, chain_info_output); + } + + function deployProxy( + SSVBasedApps impl, + StrategyManager strategyMod, + BasedAppsManager bAppsMod, + ProtocolManager protocolMod, + string memory raw + ) internal returns (ERC1967Proxy proxy) { + return + new ERC1967Proxy( + address(impl), + abi.encodeWithSelector( + impl.initialize.selector, + msg.sender, + address(bAppsMod), + address(strategyMod), + address(protocolMod), + ProtocolStorageLib.Data({ + feeTimelockPeriod: uint32( + raw.readUint(".feeTimelockPeriod") + ), + feeExpireTime: uint32(raw.readUint(".feeExpireTime")), + withdrawalTimelockPeriod: uint32( + raw.readUint(".withdrawalTimelockPeriod") + ), + withdrawalExpireTime: uint32( + raw.readUint(".withdrawalExpireTime") + ), + obligationTimelockPeriod: uint32( + raw.readUint(".obligationTimelockPeriod") + ), + obligationExpireTime: uint32( + raw.readUint(".obligationExpireTime") + ), + tokenUpdateTimelockPeriod: uint32( + raw.readUint(".tokenUpdateTimelockPeriod") + ), + maxShares: raw.readUint(".maxShares"), + maxFeeIncrement: uint32( + raw.readUint(".maxFeeIncrement") + ), + disabledFeatures: uint32( + raw.readUint(".disabledFeatures") + ) + }) + ) + ); + } +} diff --git a/script/DeployAllHoodi.s.sol b/script/DeployAllHoodi.s.sol new file mode 100644 index 00000000..95432c9a --- /dev/null +++ b/script/DeployAllHoodi.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.29; + +import { Script } from "forge-std/Script.sol"; +import { DeployAll } from "./DeployAll.sol"; + +contract DeployAllHoodi is Script, DeployAll { + function run(bool isProd) external { + if (block.chainid != 560_048) { + revert("This script is only for the Hoodi prod"); + } + + string memory cfgPath; + string memory outPath; + if (isProd) { + cfgPath = "script/config/hoodi-prod.json"; + outPath = "artifacts/deploy-hoodi-prod.json"; + } else { + cfgPath = "script/config/hoodi-stage.json"; + outPath = "artifacts/deploy-hoodi-stage.json"; + } + + string memory finalJson = _deployAll(vm.readFile(cfgPath)); + + vm.writeJson(finalJson, outPath); + } +} diff --git a/script/DeployAllMainnet.s.sol b/script/DeployAllMainnet.s.sol new file mode 100644 index 00000000..4da956cc --- /dev/null +++ b/script/DeployAllMainnet.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.29; + +import { Script, console } from "forge-std/Script.sol"; +import { DeployAll } from "./DeployAll.sol"; + +contract DeployAllHoodi is Script, DeployAll { + function run() external { + if (block.chainid != 1) { + revert("This script is only for Mainnet"); + } + + string memory finalJson = _deployAll( + vm.readFile("script/config/mainnet.json") + ); + + vm.writeJson(finalJson, "artifacts/deploy-mainnet.json"); + } +} diff --git a/script/config/hoodi-prod.json b/script/config/hoodi-prod.json new file mode 100644 index 00000000..9b0f9b60 --- /dev/null +++ b/script/config/hoodi-prod.json @@ -0,0 +1,12 @@ +{ + "feeTimelockPeriod": 86400, + "feeExpireTime": 3600, + "withdrawalTimelockPeriod": 172800, + "withdrawalExpireTime": 86400, + "obligationTimelockPeriod": 172800, + "obligationExpireTime": 86400, + "tokenUpdateTimelockPeriod": 3600, + "maxShares": "1e50", + "maxFeeIncrement": 500, + "disabledFeatures": 0 +} diff --git a/script/config/hoodi-stage.json b/script/config/hoodi-stage.json new file mode 100644 index 00000000..9b0f9b60 --- /dev/null +++ b/script/config/hoodi-stage.json @@ -0,0 +1,12 @@ +{ + "feeTimelockPeriod": 86400, + "feeExpireTime": 3600, + "withdrawalTimelockPeriod": 172800, + "withdrawalExpireTime": 86400, + "obligationTimelockPeriod": 172800, + "obligationExpireTime": 86400, + "tokenUpdateTimelockPeriod": 3600, + "maxShares": "1e50", + "maxFeeIncrement": 500, + "disabledFeatures": 0 +} diff --git a/script/config/mainnet.json b/script/config/mainnet.json new file mode 100644 index 00000000..60fe7331 --- /dev/null +++ b/script/config/mainnet.json @@ -0,0 +1,12 @@ +{ + "feeTimelockPeriod": 604800, + "feeExpireTime": 86400, + "withdrawalTimelockPeriod": 1209600, + "withdrawalExpireTime": 259200, + "obligationTimelockPeriod": 1209600, + "obligationExpireTime": 259200, + "tokenUpdateTimelockPeriod": 1209600, + "maxShares": "1e50", + "maxFeeIncrement": 500, + "disabledFeatures": 3 +} diff --git a/scripts/DeployProxy.s.sol b/scripts/DeployProxy.s.sol deleted file mode 100644 index 99384295..00000000 --- a/scripts/DeployProxy.s.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; - -import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -import { Script, console } from "@ssv/forge-std/Script.sol"; -import { StrategyManager } from "@ssv/src/core/modules/StrategyManager.sol"; -import { BasedAppsManager } from "@ssv/src/core/modules/BasedAppsManager.sol"; -import { ProtocolManager } from "@ssv/src/core/modules/ProtocolManager.sol"; -import { IBasedAppManager } from "@ssv/src/core/interfaces/IBasedAppManager.sol"; -import { IStrategyManager } from "@ssv/src/core/interfaces/IStrategyManager.sol"; -import { IProtocolManager } from "@ssv/src/core/interfaces/IProtocolManager.sol"; -import { SSVBasedApps } from "src/core/SSVBasedApps.sol"; -import { ProtocolStorageLib } from "@ssv/src/core/libraries/ProtocolStorageLib.sol"; - -// solhint-disable no-console -contract DeployProxy is Script { - function run() external { - vm.startBroadcast(); - - SSVBasedApps implementation = new SSVBasedApps(); - - StrategyManager strategyManagerMod = new StrategyManager(); - BasedAppsManager basedAppsManagerMod = new BasedAppsManager(); - ProtocolManager protocolManagerMod = new ProtocolManager(); - - ProtocolStorageLib.Data memory config = ProtocolStorageLib.Data({ - feeTimelockPeriod: 7 days, - feeExpireTime: 1 days, - withdrawalTimelockPeriod: 14 days, - maxShares: 1e50, - withdrawalExpireTime: 3 days, - obligationTimelockPeriod: 14 days, - obligationExpireTime: 3 days, - tokenUpdateTimelockPeriod: 14 days, - maxFeeIncrement: 500, - disabledFeatures: 0 - }); - - bytes memory initData = abi.encodeWithSelector( - implementation.initialize.selector, - msg.sender, - IBasedAppManager(basedAppsManagerMod), - IStrategyManager(strategyManagerMod), - IProtocolManager(protocolManagerMod), - config - ); - - ERC1967Proxy proxy = new ERC1967Proxy( - address(implementation), - initData - ); - - console.log("Implementation deployed at:", address(implementation)); - console.log( - "Module StrategyManager deployed at:", - address(strategyManagerMod) - ); - console.log( - "Module BasedAppsManager deployed at:", - address(basedAppsManagerMod) - ); - console.log( - "Module ProtocolManager deployed at:", - address(protocolManagerMod) - ); - console.log("Proxy deployed at:", address(proxy)); - - console.log("Fee Timelock Period:", config.feeTimelockPeriod); - console.log("Fee Expire Time:", config.feeExpireTime); - console.log( - "Withdrawal Timelock Period:", - config.withdrawalTimelockPeriod - ); - console.log("Withdrawal Expire Time:", config.withdrawalExpireTime); - console.log( - "Obligation Timelock Period:", - config.obligationTimelockPeriod - ); - console.log("Obligation Expire Time:", config.obligationExpireTime); - console.log( - "Token Update Timelock Period:", - config.tokenUpdateTimelockPeriod - ); - console.log("Max Shares:", config.maxShares); - console.log("Max Fee Increment:", config.maxFeeIncrement); - vm.stopBroadcast(); - } -} diff --git a/scripts/UpgradeProxy.s.sol b/scripts/UpgradeProxy.s.sol deleted file mode 100644 index 2ac113e5..00000000 --- a/scripts/UpgradeProxy.s.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.29; - -import { Script, console } from "@ssv/forge-std/Script.sol"; - -interface IUUPSUpgradeable { - function upgradeToAndCall( - address newImplementation, - bytes calldata data - ) external; -} - -// solhint-disable no-console -contract UpgradeScript is Script { - function run() external { - vm.startBroadcast(); - - address proxyAddress = vm.envAddress("PROXY_ADDRESS"); - address newImplementation = vm.envAddress("IMPLEMENTATION_ADDRESS"); - - IUUPSUpgradeable(proxyAddress).upgradeToAndCall(newImplementation, ""); - - console.log( - "Proxy ", - proxyAddress, - " upgraded to new implementation: ", - newImplementation - ); - - vm.stopBroadcast(); - } -} diff --git a/src/core/libraries/ProtocolStorageLib.sol b/src/core/libraries/ProtocolStorageLib.sol index 50136616..579968ef 100644 --- a/src/core/libraries/ProtocolStorageLib.sol +++ b/src/core/libraries/ProtocolStorageLib.sol @@ -13,12 +13,12 @@ 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 // ... uint32 disabledFeatures; - uint256 maxShares; } uint256 private constant SSV_STORAGE_POSITION = diff --git a/test/modules/SlashingManager.bapp.t.sol b/test/modules/SlashingManager.bapp.t.sol index df1a36d2..9f8a0255 100644 --- a/test/modules/SlashingManager.bapp.t.sol +++ b/test/modules/SlashingManager.bapp.t.sol @@ -851,7 +851,6 @@ contract SlashingManagerTest is StrategyManagerTest { function testSlashBAppAdjustBasicETH() public { uint256 depositAmount = 100 ether; address token = ETH_ADDRESS; - // uint256 slashAmount = 10 ether; uint32 slashPercentage = 100; uint32 percentage = 10_000; uint256 slashAmount = calculateSlashAmount(