From f9ccd1212d52fdc9e762d82f919538de4ed3dbb5 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 11:01:14 +0100 Subject: [PATCH 01/33] fix: add events --- src/CredibleCommitmentCurationProvider.sol | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/CredibleCommitmentCurationProvider.sol b/src/CredibleCommitmentCurationProvider.sol index 854709a..0a98a5b 100644 --- a/src/CredibleCommitmentCurationProvider.sol +++ b/src/CredibleCommitmentCurationProvider.sol @@ -66,20 +66,19 @@ contract CredibleCommitmentCurationProvider is uint256 defaultBlockGasLimit ); event ResetForcedOptOut(uint256 indexed moduleId, uint256 indexed operatorId); + event OperatorManagerUpdated(uint256 indexed moduleId, uint256 indexed operatorId, address manager); error OperatorNotRegistered(); error ManagerNotRegistered(); error RewardAddressMismatch(); error OperatorNotActive(); error ModuleDisabled(); - error SameValue(); error OperatorAlreadyRegistered(); error OperatorOptInNotAllowed(); error OperatorOptOutNotAllowed(); error KeyIndexOutOfRange(); error KeysRangeExceedMaxValidators(); error KeyIndexMismatch(); - error InvalidOperatorId(); error InvalidModuleId(); error ZeroCommitteeAddress(); @@ -171,16 +170,18 @@ contract CredibleCommitmentCurationProvider is // save operator state /// @dev also checks if the proposed manager is already registered for different operator opKey._setOperatorManager(manager); + emit OperatorManagerUpdated(moduleId, operatorId, manager); + opKey._setOperatorOptInOutState( OperatorOptInOutState({optInBlock: uint64(block.number), optOutBlock: 0, isOptOutForced: false}) ); - emit OptInSucceeded(moduleId, operatorId, manager); - _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); /// @dev no checks on rpcUrl, so it can be rewritten on repeated opt-in opKey._setOperatorExtraData(OperatorExtraData({rpcURL: rpcURL})); - // emit RPCUrlUpdated(moduleId, operatorId, rpcURL); + emit RPCUrlUpdated(moduleId, operatorId, rpcURL); + + emit OptInSucceeded(moduleId, operatorId, manager); } /// @notice Opt-out operator on behalf of the operator manager @@ -254,6 +255,8 @@ contract CredibleCommitmentCurationProvider is uint256 opKey = DS.__encOpKey(moduleId, operatorId); /// @dev also checks if the proposed manager is already registered for different operator opKey._setOperatorManager(newManager); + + emit OperatorManagerUpdated(moduleId, operatorId, newManager); } function getOperator(address manager) From f1f7941ba6f51b64783b5e5b5778a76b04f8d7c5 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 11:05:00 +0100 Subject: [PATCH 02/33] fix: undef mainnet constants --- script/DeployMainnet.s.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/DeployMainnet.s.sol b/script/DeployMainnet.s.sol index adae97e..589f215 100644 --- a/script/DeployMainnet.s.sol +++ b/script/DeployMainnet.s.sol @@ -10,15 +10,15 @@ contract DeployMainnet is DeployBase { // implementation constants config.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb; config.csModuleType = "community-onchain-v1"; - config.defaultOperatorMaxValidators = 100; - config.defaultBlockGasLimit = 1000000; + config.defaultOperatorMaxValidators = 1000; + config.defaultBlockGasLimit = 3000000; // proxy - config.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + config.proxyAdmin = 0x0000000000000000000000000000000000000000; // Dev team EOA // initial parameters - config.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA - config.optInMinDurationBlocks = 32; + config.committeeAddress = 0x0000000000000000000000000000000000000000; // Dev team EOA + config.optInMinDurationBlocks = 0; config.optOutDelayDurationBlocks = 64; _setUp(); From 250bb61e99965df253d7d1dfd65aa5bda3dfddd4 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 14:14:48 +0100 Subject: [PATCH 03/33] fix: init on proxy deploy --- test/DataStorage.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/DataStorage.t.sol b/test/DataStorage.t.sol index 1e11244..f2489d0 100644 --- a/test/DataStorage.t.sol +++ b/test/DataStorage.t.sol @@ -27,7 +27,7 @@ contract ModulesDataStorageTest is Test { assertEq(newState.isDisabled, true); } - function test_OptInOutConfig() public { + function test_Config() public { uint64 optInMinDurationBlocks = 123; uint64 optOutDelayDurationBlocks = 234; uint64 defaultOperatorMaxValidators = 100; From b85fa0520e73f77a829403c0163118946766197f Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 14:21:56 +0100 Subject: [PATCH 04/33] fix: add version, check on manager update --- src/CredibleCommitmentCurationProvider.sol | 19 ++++++++++++------- test/CCCP.t.sol | 6 ++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/CredibleCommitmentCurationProvider.sol b/src/CredibleCommitmentCurationProvider.sol index 0a98a5b..983654a 100644 --- a/src/CredibleCommitmentCurationProvider.sol +++ b/src/CredibleCommitmentCurationProvider.sol @@ -127,10 +127,7 @@ contract CredibleCommitmentCurationProvider is } /// @notice Opt-in operator to the module - /// @dev prevent a repeated optin same operator with a different manager address, - /// or in case when operator changed reward address in the module, - /// the manager's address must first be changed to the new one - /// from operator's reward address (see `updateManagerAddress`) + /// @dev allows a repeated optin with different manager address function optIn( uint24 moduleId, uint64 operatorId, @@ -221,6 +218,8 @@ contract CredibleCommitmentCurationProvider is emit OptOutRequested(moduleId, operatorId, true); } + /// @notice Update the operator's keys range + /// @dev should be called by the operator manager address function updateKeysRange(uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd) external whenNotPaused { uint256 opKey = _getOpKeyByManager(msg.sender); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(opKey._getOperatorOptInOutState()); @@ -240,6 +239,8 @@ contract CredibleCommitmentCurationProvider is _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); } + /// @notice Update the operator's manager address + /// @dev should be called by the operator reward address function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external whenNotPaused { if (newManager == address(0)) revert ZeroOperatorManagerAddress(); @@ -252,7 +253,8 @@ contract CredibleCommitmentCurationProvider is revert RewardAddressMismatch(); } - uint256 opKey = DS.__encOpKey(moduleId, operatorId); + uint256 opKey = _getOpKeyById(moduleId, operatorId); + /// @dev also checks if the proposed manager is already registered for different operator opKey._setOperatorManager(newManager); @@ -296,6 +298,10 @@ contract CredibleCommitmentCurationProvider is ); } + function getContractVersion() external view returns (uint64) { + return _getInitializedVersion(); + } + function resetForcedOptOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { uint256 opKey = _getOpKeyById(moduleId, operatorId); OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); @@ -438,6 +444,7 @@ contract CredibleCommitmentCurationProvider is } } + /// @dev reverts if the operator not registered function _getOpKeyById(uint24 moduleId, uint64 operatorId) internal view returns (uint256 opKey) { opKey = DS.__encOpKey(moduleId, operatorId); if (opKey._getOperatorManager() == address(0)) { @@ -470,8 +477,6 @@ contract CredibleCommitmentCurationProvider is uint64 blockNumber = uint64(block.number); Config memory cfg = DS._getConfig(); - // bool optOutInProgress = - // optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + cfg.optOutDelayDurationBlocks >= blockNumber; bool isOptedOut = optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + cfg.optOutDelayDurationBlocks < blockNumber; bool isOptedIn = optInOutState.optInBlock > 0 && !isOptedOut; diff --git a/test/CCCP.t.sol b/test/CCCP.t.sol index a6c0452..a7016fa 100644 --- a/test/CCCP.t.sol +++ b/test/CCCP.t.sol @@ -71,6 +71,7 @@ contract CCCPInitialize is CCCPCommon { CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); assertEq(cccp.__test__getCSModuleType(), "csm-type"); assertEq(address(cccp.LIDO_LOCATOR()), address(locator)); + assertEq(cccp.getContractVersion(), type(uint64).max); } function test_constructor_RevertWhen_ZeroLocator() public { @@ -113,6 +114,7 @@ contract CCCPInitialize is CCCPCommon { assertEq(optOutDelayDurationBlocks, 64); assertEq(defaultOperatorMaxValidators, 10); assertEq(defaultBlockGasLimit, 1000000); + assertEq(cccp.getContractVersion(), 1); assertFalse(cccp.paused()); } } @@ -152,9 +154,9 @@ contract CCCPOptIn is CCCPCommon { // opt in on behalf of noCsm1 vm.broadcast(noCsm1); vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.OptInSucceeded(csmId, noCsm1Id, noCsm1Manager); - vm.expectEmit(true, true, true, false, address(cccp)); emit CCCP.KeyRangeUpdated(csmId, noCsm1Id, 2, 4); + vm.expectEmit(true, true, true, false, address(cccp)); + emit CCCP.OptInSucceeded(csmId, noCsm1Id, noCsm1Manager); cccp.optIn({ moduleId: csmId, From 039c914a9194da97c3bacf6fb09757aa6ffe4f80 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 14:22:27 +0100 Subject: [PATCH 05/33] fix: initialize during proxy deploy --- script/DeployBase.s.sol | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/script/DeployBase.s.sol b/script/DeployBase.s.sol index e82b42c..9944814 100644 --- a/script/DeployBase.s.sol +++ b/script/DeployBase.s.sol @@ -56,14 +56,15 @@ abstract contract DeployBase is Script { address(new CredibleCommitmentCurationProvider(config.lidoLocatorAddress, config.csModuleType)); console.log("CCCP impl address", address(cccpImpl)); - cccp = CredibleCommitmentCurationProvider(_deployProxy(config.proxyAdmin, address(cccpImpl))); - cccp.initialize({ - committeeAddress: config.committeeAddress, - optInMinDurationBlocks: config.optInMinDurationBlocks, - optOutDelayDurationBlocks: config.optOutDelayDurationBlocks, - defaultOperatorMaxValidators: config.defaultOperatorMaxValidators, - defaultBlockGasLimit: config.defaultBlockGasLimit - }); + cccp = CredibleCommitmentCurationProvider(_deployProxy(config.proxyAdmin, address(cccpImpl), + abi.encodeCall(CredibleCommitmentCurationProvider.initialize, ( + config.committeeAddress, + config.optInMinDurationBlocks, + config.optOutDelayDurationBlocks, + config.defaultOperatorMaxValidators, + config.defaultBlockGasLimit + )) + )); console.log("CCCP address", address(cccp)); JsonObj memory deployJson = Json.newObj(); @@ -78,9 +79,9 @@ abstract contract DeployBase is Script { vm.stopBroadcast(); } - function _deployProxy(address admin, address implementation) internal returns (address) { + function _deployProxy(address admin, address implementation, bytes memory data) internal returns (address) { OssifiableProxy proxy = - new OssifiableProxy({implementation_: implementation, data_: new bytes(0), admin_: admin}); + new OssifiableProxy({implementation_: implementation, data_: data, admin_: admin}); return address(proxy); } From 98e65863b960b46a57331ad2bddb11033879da0f Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 15:35:09 +0100 Subject: [PATCH 06/33] chore: linter tune --- .solhint.json | 6 +++-- .yarn/install-state.gz | Bin 95515 -> 100165 bytes package.json | 3 ++- yarn.lock | 49 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/.solhint.json b/.solhint.json index ff5e365..fd73670 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,7 +1,8 @@ { "extends": "solhint:recommended", - "plugins": [""], + "plugins": ["lido-csm"], "rules": { + "constructor-syntax": "error", "compiler-version": ["error", "0.8.28"], "no-inline-assembly": "off", "no-unused-import": "error", @@ -10,7 +11,8 @@ "reason-string": ["warn", { "maxLength": 64 }], "immutable-vars-naming": ["error", { "immutablesAsConstants": true }], "var-name-mixedcase": "error", - "func-name-mixedcase": "error", + "func-name-mixedcase": "warn", + "foundry-test-functions": ["warn", ["setUp"]], "no-global-import": "error", "ordering": "warn", "gas-calldata-parameters": "error", diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 09b5e8daab6fb2abf017b4f65edae3478da457c5..926cda90baf602525e887dd5aee5723fa87660a4 100644 GIT binary patch delta 100079 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 T@9V$^_~w5A4i`#N_d*Q7MFzf}V85tQF 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%0Dm+`> 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

~SP)1nJm?nfGpSg_6baeZ-3_}Au9U2 z@F~UuDt{*eq#Rilbz(1b7_kcR1fX2)s~g`LA!yk>#CjVwf&UyOyd6=AigX?YmC+U` z-z)?U75bd5A?p#$rb5y&LXMDnp}q2d_IF>RqVR?Ar{a+f^egXjCNdZ504C%<>VQ8o z0Q0Os&mM`7iND6dM^=KVqJ0A)0_Fg@X+s!ujc@?wY}%F0hl0_8WEl7`@YHO({XLgM z>0MZJM9Ely#3)J@PsE@D5C22ed@cEFK!AvA8!Ko_n_zjVvrMI#AV=>FBS{HA7@dA z#33Fs6(l(uDR#003&nFop|&YZwBh900XOh8)O!=_L?)KJ?uF zk!uGh11`UW)|uMO0+BMFx#e6*ZPhl-oXCSmEXG8V?Mc;T)lY;WF90a(0C@Szn4dm7 zMG^RGXJ2&St7RX)s}KfwX5e{=kjUob}q-z5=N=L7(u^E|xc_Pee( z_2SEtbr89N0)ag6BOS!52*$YxmoD3d@(}L;kSbga;Z<4z>Ga%!EM}#d%a%pY4p=5~ zW^KdZ4I1(|V-$mb2j>g@)$MQ-*X?)x;75P_@9)*^*F1240)72UznI^?eD(IX{!|0y zm;R7%zk+%Ai@kp9@B9rv`h_3;!tZm6`jczS55N0|Uz?zMI|saZI0yO$%Ac(WslA1X zB(>87op(hRr2_Q~@G!#WNM`{@rp5^ND#g_Wm^vg1A^n4fsvxav2z#tIk?M8802rA0D z?-)N?qJ3|F>Us6@&D+;}|FAuJ(VjeP{yBfHKlQM^`}t?@GmMT|bZP-<);suS$oe2) z<;zi9Ga6;Z8_@o}k=O$a2cJ`%Fw6BuGC|S9u=;?7Al;4~P)X|oas(6=CV*n)22&vt z6gIF@*XE`Uf&TQ(z`W}BxP?4Izu?mpKLGz7hWt2a>h`xa_zLUj~PPMsN+YfRX2 zFk4`N-EG)Gruw{^k0AF0)h(couROMphLd&>KgdkCfBcKeSa||5O{Uzq5≠<0D%Ui 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 z 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 diff --git a/package.json b/package.json index ece2d42..eec6636 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "lint-staged": "^15.3.0", "prettier": "^3.4.2", "prettier-plugin-solidity": "^1.4.2", - "solhint": "5.0.4" + "solhint": "5.0.4", + "solhint-plugin-lido-csm": "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3" }, "lint-staged": { "*": "prettier --ignore-unknown --write", diff --git a/yarn.lock b/yarn.lock index cd09b11..d14b954 100644 --- a/yarn.lock +++ b/yarn.lock @@ -57,6 +57,13 @@ __metadata: languageName: node linkType: hard +"@solidity-parser/parser@npm:^0.18.0": + version: 0.18.0 + resolution: "@solidity-parser/parser@npm:0.18.0" + checksum: 10c0/c54b4c9ba10e1fd1cd45894040135a39b9bc527f0ac40bec732d8628b0c0c7cb7ec2b7e816b408d613ab1d71c04f9555111ccc83b6dbaed2e39ff4ef7d000e25 + languageName: node + linkType: hard + "@solidity-parser/parser@npm:^0.19.0": version: 0.19.0 resolution: "@solidity-parser/parser@npm:0.19.0" @@ -334,6 +341,7 @@ __metadata: prettier: "npm:^3.4.2" prettier-plugin-solidity: "npm:^1.4.2" solhint: "npm:5.0.4" + solhint-plugin-lido-csm: "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3" languageName: unknown linkType: soft @@ -1221,6 +1229,47 @@ __metadata: languageName: node linkType: hard +"solhint-plugin-lido-csm@https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3": + version: 0.3.3 + resolution: "solhint-plugin-lido-csm@https://github.com/lidofinance/solhint-plugin-lido-csm.git#commit=db949adb893af939bfb1ca5ba191c26fd94aa69c" + dependencies: + solhint: "npm:4.5.2" + checksum: 10c0/3bbd7fcfd8adda38f8291586a0d29afcdd33bea20dc48b82cdd020696e6772f8c660bfa8468c8d9da4133b08d796bf08f7ef09f67b714c7a96a6193e7c3b6ce5 + languageName: node + linkType: hard + +"solhint@npm:4.5.2": + version: 4.5.2 + resolution: "solhint@npm:4.5.2" + dependencies: + "@solidity-parser/parser": "npm:^0.18.0" + ajv: "npm:^6.12.6" + antlr4: "npm:^4.13.1-patch-1" + ast-parents: "npm:^0.0.1" + chalk: "npm:^4.1.2" + commander: "npm:^10.0.0" + cosmiconfig: "npm:^8.0.0" + fast-diff: "npm:^1.2.0" + glob: "npm:^8.0.3" + ignore: "npm:^5.2.4" + js-yaml: "npm:^4.1.0" + latest-version: "npm:^7.0.0" + lodash: "npm:^4.17.21" + pluralize: "npm:^8.0.0" + prettier: "npm:^2.8.3" + semver: "npm:^7.5.2" + strip-ansi: "npm:^6.0.1" + table: "npm:^6.8.1" + text-table: "npm:^0.2.0" + dependenciesMeta: + prettier: + optional: true + bin: + solhint: solhint.js + checksum: 10c0/a54cc8df54ed711ca6be783b9291eb9e838f518f236d4df05c6ef412ead6a21f4a5bf3bace62ca7d2ed69f4566d5289db4c8c93e0c5e23ac91401904e1f1f312 + languageName: node + linkType: hard + "solhint@npm:5.0.4": version: 5.0.4 resolution: "solhint@npm:5.0.4" From d6038de78805f68cb5140e683e720d90ec58d765 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 15:35:55 +0100 Subject: [PATCH 07/33] fix: pause/resume roles --- src/CredibleCommitmentCurationProvider.sol | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CredibleCommitmentCurationProvider.sol b/src/CredibleCommitmentCurationProvider.sol index 983654a..27a1e5d 100644 --- a/src/CredibleCommitmentCurationProvider.sol +++ b/src/CredibleCommitmentCurationProvider.sol @@ -48,6 +48,9 @@ contract CredibleCommitmentCurationProvider is } bytes32 public constant COMMITTEE_ROLE = keccak256("COMMITTEE_ROLE"); + bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); + bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); + ILidoLocator public immutable LIDO_LOCATOR; // hardcoded CSModule type, used for the operator's state retrieval bytes32 internal immutable CS_MODULE_TYPE; @@ -110,6 +113,8 @@ contract CredibleCommitmentCurationProvider is _grantRole(DEFAULT_ADMIN_ROLE, committeeAddress); _grantRole(COMMITTEE_ROLE, committeeAddress); + _grantRole(PAUSE_ROLE, committeeAddress); + _grantRole(RESUME_ROLE, committeeAddress); _setConfig( optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit @@ -117,12 +122,12 @@ contract CredibleCommitmentCurationProvider is } /// @notice Resume all operations after a pause - function unpause() external onlyRole(COMMITTEE_ROLE) { + function unpause() external onlyRole(RESUME_ROLE) { _unpause(); } /// @notice Pause all operations - function pause() external onlyRole(COMMITTEE_ROLE) { + function pause() external onlyRole(PAUSE_ROLE) { _pause(); } From ac812a4275f598ecc8310c187a1739771ad90283 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 15:36:27 +0100 Subject: [PATCH 08/33] test: deploy state, upgrades --- test/fork/deployment/PostDeployment.t.sol | 70 +++++++++++++++++++++++ test/fork/deployment/Upgradability.sol | 42 ++++++++++++++ test/helpers/mocks/CCCPMock.sol | 4 ++ 3 files changed, 116 insertions(+) create mode 100644 test/fork/deployment/PostDeployment.t.sol create mode 100644 test/fork/deployment/Upgradability.sol diff --git a/test/fork/deployment/PostDeployment.t.sol b/test/fork/deployment/PostDeployment.t.sol new file mode 100644 index 0000000..fcef458 --- /dev/null +++ b/test/fork/deployment/PostDeployment.t.sol @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {Utilities} from "../../helpers/Utilities.sol"; +import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; +import {DeployParams} from "../../../script/DeployBase.s.sol"; +import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; +import {CredibleCommitmentCurationProvider as CCCP} from "../../../src/CredibleCommitmentCurationProvider.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract CSModuleDeploymentTest is Test, Utilities, DeploymentFixtures { + DeployParams private deployParams; + + function setUp() public { + Env memory env = envVars(); + vm.createSelectFork(env.RPC_URL); + initializeFromDeployment(env.DEPLOY_CONFIG); + deployParams = parseDeployParams(env.DEPLOY_CONFIG); + } + + function test_constructor() public view { + assertEq(address(cccp.LIDO_LOCATOR()), deployParams.lidoLocatorAddress); + } + + function test_initializer() public view { + ( + uint64 optInMinDurationBlocks, + uint64 optOutDelayDurationBlocks, + uint64 defaultOperatorMaxValidators, + uint64 defaultBlockGasLimit + ) = cccp.getConfig(); + + assertEq(optInMinDurationBlocks, deployParams.optInMinDurationBlocks); + assertEq(optOutDelayDurationBlocks, deployParams.optOutDelayDurationBlocks); + assertEq(defaultOperatorMaxValidators, deployParams.defaultOperatorMaxValidators); + assertEq(defaultBlockGasLimit, deployParams.defaultBlockGasLimit); + assertEq(cccp.getContractVersion(), 1); + assertFalse(cccp.paused()); + } + + function test_roles() public view { + assertTrue(cccp.hasRole(cccp.DEFAULT_ADMIN_ROLE(), deployParams.committeeAddress)); + assertTrue(cccp.getRoleMemberCount(cccp.DEFAULT_ADMIN_ROLE()) == 1); + assertTrue(cccp.hasRole(cccp.PAUSE_ROLE(), deployParams.committeeAddress)); + assertTrue(cccp.hasRole(cccp.RESUME_ROLE(), deployParams.committeeAddress)); + assertEq(cccp.getRoleMemberCount(cccp.PAUSE_ROLE()), 1); + assertEq(cccp.getRoleMemberCount(cccp.RESUME_ROLE()), 1); + } + + function test_proxy() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); + assertEq(proxy.proxy__getAdmin(), address(deployParams.proxyAdmin)); + assertFalse(proxy.proxy__getIsOssified()); + + CCCP cccpImpl = CCCP(proxy.proxy__getImplementation()); + assertEq(cccpImpl.getContractVersion(), type(uint64).max); + + vm.expectRevert(Initializable.InvalidInitialization.selector); + cccp.initialize({ + committeeAddress: deployParams.committeeAddress, + optInMinDurationBlocks: deployParams.optInMinDurationBlocks, + optOutDelayDurationBlocks: deployParams.optOutDelayDurationBlocks, + defaultOperatorMaxValidators: deployParams.defaultOperatorMaxValidators, + defaultBlockGasLimit: deployParams.defaultBlockGasLimit + }); + } +} diff --git a/test/fork/deployment/Upgradability.sol b/test/fork/deployment/Upgradability.sol new file mode 100644 index 0000000..6abc6bb --- /dev/null +++ b/test/fork/deployment/Upgradability.sol @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; +import {CCCPMock} from "../../helpers/mocks/CCCPMock.sol"; +import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; + +contract UpgradabilityTest is Test, DeploymentFixtures { + constructor() { + Env memory env = envVars(); + vm.createSelectFork(env.RPC_URL); + initializeFromDeployment(env.DEPLOY_CONFIG); + } + + function test_CCCPUpgradeTo() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); + CCCPMock newCccp = new CCCPMock(address(cccp.LIDO_LOCATOR()), "csm-type-new"); + + vm.prank(proxy.proxy__getAdmin()); + proxy.proxy__upgradeTo(address(newCccp)); + assertEq(CCCPMock(address(cccp)).__test__getCSModuleType(), "csm-type-new"); + } + + function test_CCCPUpgradeToAndCall() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); + CCCPMock newCccp = new CCCPMock(address(cccp.LIDO_LOCATOR()), "csm-type-new"); + + address contractAdmin = cccp.getRoleMember(cccp.DEFAULT_ADMIN_ROLE(), 0); + vm.prank(contractAdmin); + cccp.pause(); + assertTrue(cccp.paused()); + + vm.prank(proxy.proxy__getAdmin()); + proxy.proxy__upgradeToAndCall(address(newCccp), abi.encodeCall(newCccp.initialize_v2, ())); + assertEq(CCCPMock(address(cccp)).__test__getCSModuleType(), "csm-type-new"); + assertEq(cccp.getContractVersion(), 2); + assertFalse(cccp.paused()); + } +} diff --git a/test/helpers/mocks/CCCPMock.sol b/test/helpers/mocks/CCCPMock.sol index 7d7d237..1787a43 100644 --- a/test/helpers/mocks/CCCPMock.sol +++ b/test/helpers/mocks/CCCPMock.sol @@ -13,4 +13,8 @@ contract CCCPMock is CredibleCommitmentCurationProvider { function __test__getCSModuleType() external view returns (bytes32) { return CS_MODULE_TYPE; } + + function initialize_v2() external reinitializer(2) { + _unpause(); + } } From 50d419f16597451a0a5d86e23bd84e0211828dc8 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 17:14:20 +0100 Subject: [PATCH 09/33] test: add proxy test --- test/OssifiableProxy.t.sol | 160 +++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 test/OssifiableProxy.t.sol diff --git a/test/OssifiableProxy.t.sol b/test/OssifiableProxy.t.sol new file mode 100644 index 0000000..f119a7d --- /dev/null +++ b/test/OssifiableProxy.t.sol @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; +import {Utilities} from "./helpers/Utilities.sol"; +import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; + +contract InitializableImplementationStub { + uint256 public version; + + event Initialized(uint256 version); + event FallbackIsFired(); + + function initialize(uint256 _version) public { + version = _version; + emit Initialized(_version); + } + + fallback() external payable { + emit FallbackIsFired(); + } +} + +contract OssifiableProxyTest is Test, Utilities { + InitializableImplementationStub currentImpl; + InitializableImplementationStub nextImpl; + OssifiableProxy proxy; + address deployer; + address admin; + address stranger; + + function setUp() public { + deployer = address(this); + admin = nextAddress("admin"); + stranger = nextAddress("stranger"); + + currentImpl = new InitializableImplementationStub(); + nextImpl = new InitializableImplementationStub(); + + proxy = new OssifiableProxy(address(currentImpl), admin, "0x"); + } + + function test_constructor_revertWhenZeroAdmin() public { + vm.expectRevert(abi.encodeWithSelector(ERC1967Utils.ERC1967InvalidAdmin.selector, address(0))); + new OssifiableProxy(address(currentImpl), address(0), "0x"); + } + + function test_getAdmin() public view { + assertEq(proxy.proxy__getAdmin(), admin); + } + + function test_getImplementation() public view { + assertEq(proxy.proxy__getImplementation(), address(currentImpl)); + } + + function test_getIsOssified() public view { + assertFalse(proxy.proxy__getIsOssified()); + } + + function test_ossify_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__ossify(); + } + + function test_ossify() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + } + + function test_ossify_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__ossify(); + } + + function test_changeAdmin_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__changeAdmin(stranger); + } + + function test_changeAdmin_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__changeAdmin(stranger); + } + + function test_changeAdmin() public { + vm.prank(admin); + proxy.proxy__changeAdmin(stranger); + assertEq(proxy.proxy__getAdmin(), stranger); + } + + function test_upgradeTo_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__upgradeTo(address(nextImpl)); + } + + function test_upgradeTo_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__upgradeTo(address(nextImpl)); + } + + function test_upgradeTo() public { + vm.prank(admin); + proxy.proxy__upgradeTo(address(nextImpl)); + assertEq(proxy.proxy__getImplementation(), address(nextImpl)); + } + + function test_upgradeToAndCall_RevertWhenNotAdmin() public { + vm.prank(stranger); + vm.expectRevert(OssifiableProxy.NotAdmin.selector); + proxy.proxy__upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(nextImpl.initialize.selector, 1)); + } + + function test_upgradeToAndCall_RevertWhenOssified() public { + vm.prank(admin); + proxy.proxy__ossify(); + assertTrue(proxy.proxy__getIsOssified()); + + vm.expectRevert(OssifiableProxy.ProxyIsOssified.selector); + proxy.proxy__upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(nextImpl.initialize.selector, 1)); + } + + function test_upgradeToAndCall() public { + vm.prank(admin); + proxy.proxy__upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(nextImpl.initialize.selector, 1)); + assertEq(proxy.proxy__getImplementation(), address(nextImpl)); + assertEq(InitializableImplementationStub(payable(address(proxy))).version(), 1); + } + + function test_receive() public { + vm.deal(admin, 2 ether); + vm.prank(admin); + vm.expectEmit(true, true, true, true, address(proxy)); + emit InitializableImplementationStub.FallbackIsFired(); + payable(address(proxy)).call{value: 1 ether}(""); + assertEq(address(proxy).balance, 1 ether); + } + + function test_fallback() public { + vm.prank(admin); + uint256 version = InitializableImplementationStub(payable(address(proxy))).version(); + assertEq(version, 0); + } +} From 38cbce2f4ee178a984b113ac301050ef7f02eb1b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 17:15:24 +0100 Subject: [PATCH 10/33] chore: linter+tools --- .solhint.json | 2 +- .yarn/install-state.gz | Bin 100165 -> 12873 bytes foundry.toml | 10 ++++++++-- package.json | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.solhint.json b/.solhint.json index fd73670..cbd8d7a 100644 --- a/.solhint.json +++ b/.solhint.json @@ -10,7 +10,7 @@ "func-visibility": ["error", { "ignoreConstructors": true }], "reason-string": ["warn", { "maxLength": 64 }], "immutable-vars-naming": ["error", { "immutablesAsConstants": true }], - "var-name-mixedcase": "error", + "var-name-mixedcase": "warn", "func-name-mixedcase": "warn", "foundry-test-functions": ["warn", ["setUp"]], "no-global-import": "error", diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 926cda90baf602525e887dd5aee5723fa87660a4..90db52a8ac2a18ed1419ffb2e8059ea2963c2e16 100644 GIT binary patch literal 12873 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 literal 100165 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 Bl{WwY diff --git a/foundry.toml b/foundry.toml index f7e676f..fc0cdb2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,8 +17,12 @@ cache_path = "cache" block_gas_limit = 30_000_000 fuzz = { runs = 256 } -# gas_reports = [ -# ] +gas_reports = [ + "CredibleCommitmentCurationProvider", + "CCCPDataStorage", + "OssifiableProxy" +] + fs_permissions = [ { access = "read-write", path = "./out" }, @@ -26,6 +30,8 @@ fs_permissions = [ # { access = "read", path = "./test/fixtures" }, ] +ignored_warnings_from = ["test/OssifiableProxy.t.sol"] + [profile.ci] verbosity = 3 fuzz = { runs = 10_000, max_test_rejects = 2_000_000 } diff --git a/package.json b/package.json index eec6636..ff4acc5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "scripts": { "lint:solhint": "solhint './src/**/*.sol'", "lint:check": "prettier --check **.sol && yarn lint:solhint", - "lint:fix": "prettier --write **.sol" + "lint:fix": "prettier --write **.sol", + "generate:diffyscan": "node script/generateDiffyscanContracts.js" }, "devDependencies": { "husky": "^9.1.7", From 10048bcf961d16d45e336491d4d6699d6df82c03 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 17:15:44 +0100 Subject: [PATCH 11/33] build: env sample --- .env.sample | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index 9c9a754..e16d27a 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,7 @@ DEPLOYER_PRIVATE_KEY= # Testing purposes DEPLOY_CONFIG=./artifacts/holesky/deploy-holesky.json # Specify the chain to deploy or test on -CHAIN=mainnet | holesky +CHAIN=mainnet | holesky # Where to store the deployment artifacts. Default is ./artifacts/local ARTIFACTS_DIR=./artifacts/local +ETHERSCAN_API_KEY=key From d9ad35df67e2aeedf58fe69ed21cfa3b0b2468cf Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 17:16:31 +0100 Subject: [PATCH 12/33] fix: deploy script, optim. --- script/DeployBase.s.sol | 37 ++++++++++++---------- src/CredibleCommitmentCurationProvider.sol | 2 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/script/DeployBase.s.sol b/script/DeployBase.s.sol index 9944814..e383dcf 100644 --- a/script/DeployBase.s.sol +++ b/script/DeployBase.s.sol @@ -3,8 +3,7 @@ pragma solidity 0.8.28; -import {Script, console} from "forge-std/Script.sol"; -// import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {Script} from "forge-std/Script.sol"; import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; import {CredibleCommitmentCurationProvider} from "../src/CredibleCommitmentCurationProvider.sol"; @@ -45,7 +44,7 @@ abstract contract DeployBase is Script { revert ChainIdMismatch({actual: block.chainid, expected: chainId}); } - artifactDir = vm.envOr("ARTIFACTS_DIR", string("./artifacts/")); + artifactDir = vm.envOr("ARTIFACTS_DIR", string("./artifacts/local/")); pk = vm.envUint("DEPLOYER_PRIVATE_KEY"); deployer = vm.addr(pk); vm.label(deployer, "DEPLOYER"); @@ -54,18 +53,23 @@ abstract contract DeployBase is Script { { address cccpImpl = address(new CredibleCommitmentCurationProvider(config.lidoLocatorAddress, config.csModuleType)); - console.log("CCCP impl address", address(cccpImpl)); - - cccp = CredibleCommitmentCurationProvider(_deployProxy(config.proxyAdmin, address(cccpImpl), - abi.encodeCall(CredibleCommitmentCurationProvider.initialize, ( - config.committeeAddress, - config.optInMinDurationBlocks, - config.optOutDelayDurationBlocks, - config.defaultOperatorMaxValidators, - config.defaultBlockGasLimit - )) - )); - console.log("CCCP address", address(cccp)); + + cccp = CredibleCommitmentCurationProvider( + _deployProxy( + config.proxyAdmin, + address(cccpImpl), + abi.encodeCall( + CredibleCommitmentCurationProvider.initialize, + ( + config.committeeAddress, + config.optInMinDurationBlocks, + config.optOutDelayDurationBlocks, + config.defaultOperatorMaxValidators, + config.defaultBlockGasLimit + ) + ) + ) + ); JsonObj memory deployJson = Json.newObj(); deployJson.set("ChainId", chainId); @@ -80,8 +84,7 @@ abstract contract DeployBase is Script { } function _deployProxy(address admin, address implementation, bytes memory data) internal returns (address) { - OssifiableProxy proxy = - new OssifiableProxy({implementation_: implementation, data_: data, admin_: admin}); + OssifiableProxy proxy = new OssifiableProxy({implementation_: implementation, data_: data, admin_: admin}); return address(proxy); } diff --git a/src/CredibleCommitmentCurationProvider.sol b/src/CredibleCommitmentCurationProvider.sol index 27a1e5d..e814a2d 100644 --- a/src/CredibleCommitmentCurationProvider.sol +++ b/src/CredibleCommitmentCurationProvider.sol @@ -139,7 +139,7 @@ contract CredibleCommitmentCurationProvider is address manager, uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd, - string memory rpcURL + string calldata rpcURL ) external whenNotPaused { LidoOperatorCache memory _c; From f429bc934fc3caa1c1fb9da75d4b751db3ad8974 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 17:18:31 +0100 Subject: [PATCH 13/33] build: justfile + helpers script --- Justfile | 148 +++++++++++++++++++++++++++ artifacts/holesky/.gikeep | 0 artifacts/latest/.gitignore | 2 + artifacts/local/.gitignore | 2 + artifacts/mainnet/.gikeep | 0 script/generateDiffyscanContracts.js | 34 ++++++ 6 files changed, 186 insertions(+) create mode 100644 Justfile create mode 100644 artifacts/holesky/.gikeep create mode 100644 artifacts/latest/.gitignore create mode 100644 artifacts/local/.gitignore create mode 100644 artifacts/mainnet/.gikeep create mode 100644 script/generateDiffyscanContracts.js diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..32656e9 --- /dev/null +++ b/Justfile @@ -0,0 +1,148 @@ +set dotenv-load + +chain := env_var_or_default("CHAIN", "mainnet") +deploy_script_name := if chain == "mainnet" { + "DeployMainnet" +} else if chain == "holesky" { + "DeployHolesky" +} else { + error("Unsupported chain " + chain) +} + +deploy_script_path := "script" / deploy_script_name + ".s.sol:" + deploy_script_name + +anvil_host := env_var_or_default("ANVIL_IP_ADDR", "127.0.0.1") +anvil_port := "8545" +anvil_rpc_url := "http://" + anvil_host + ":" + anvil_port + +default: clean deps build test-all + +build *args: + forge build --force {{args}} + +clean: + forge clean + rm -rf cache broadcast out node_modules + +deps: + yarn workspaces focus --all --production + +deps-dev: + yarn workspaces focus --all && npx husky install + +lint-solhint: + yarn lint:solhint + +lint-fix: + yarn lint:fix + +lint: + yarn lint:check + +test-all: + just test-unit & + just test-local + +test-unit *args: + forge test --no-match-path 'test/fork/*' -vvv {{args}} + +test-deployment *args: + forge test --match-path 'test/fork/*' -vvv {{args}} + +gas-report: + #!/usr/bin/env python + + import subprocess + import re + + command = "just test-unit --nmt 'testFuzz.+' --gas-report" + output = subprocess.check_output(command, shell=True, text=True) + + lines = output.split('\n') + + filename = 'GAS.md' + to_print = False + skip_next = False + + with open(filename, 'w') as fh: + for line in lines: + if skip_next: + skip_next = False + continue + + if line.startswith('|'): + to_print = True + + if line.startswith('| Deployment Cost'): + to_print = False + skip_next = True + + if re.match(r"Ran \d+ test suites", line): + break + + if to_print: + fh.write(line + '\n') + + print(f"Done. Gas report saved to {filename}") + +coverage *args: + FOUNDRY_PROFILE=coverage forge coverage --no-match-path 'test/fork/*' {{args}} + +# Run coverage and save the report in LCOV file. +coverage-lcov *args: + FOUNDRY_PROFILE=coverage forge coverage --no-match-path 'test/fork/*' --report lcov {{args}} + +diffyscan-contracts *args: + yarn generate:diffyscan {{args}} + +make-fork *args: + @if pgrep -x "anvil" > /dev/null; \ + then just _warn "anvil process is already running in the background. Make sure it's connected to the right network and in the right state."; \ + else anvil -f ${RPC_URL} --host {{anvil_host}} --port {{anvil_port}} --config-out localhost.json {{args}}; \ + fi + +kill-fork: + @-pkill anvil && just _warn "anvil process is killed" + +deploy *args: + forge script {{deploy_script_path}} --rpc-url {{anvil_rpc_url}} --broadcast --slow {{args}} + +deploy-prod *args: + just _warn "The current `tput bold`chain={{chain}}`tput sgr0` with the following rpc url: $RPC_URL" + ARTIFACTS_DIR=./artifacts/latest/ just _deploy-prod-confirm {{args}} + + cp ./broadcast/{{deploy_script_name}}.s.sol/`cast chain-id --rpc-url=$RPC_URL`/run-latest.json \ + ./artifacts/latest/transactions.json + +[confirm("You are about to broadcast deployment transactions to the network. Are you sure?")] +_deploy-prod-confirm *args: + just _deploy-prod --broadcast --verify {{args}} + +deploy-prod-dry *args: + just _deploy-prod {{args}} + +verify-prod *args: + just _warn "Pass --chain=your_chain manually. e.g. --chain=holesky for testnet deployment" + forge script {{deploy_script_path}} --rpc-url ${RPC_URL} --verify {{args}} --unlocked + +_deploy-prod *args: + forge script {{deploy_script_path}} --force --rpc-url ${RPC_URL} {{args}} + +deploy-local: + just make-fork & + @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done + ARTIFACTS_DIR=./artifacts/local/ just deploy + just _warn "anvil is kept running in the background: {{anvil_rpc_url}}" + +test-local *args: + just make-fork --silent & + @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done + DEPLOYER_PRIVATE_KEY=`cat localhost.json | jq -r ".private_keys[0]"` \ + ARTIFACTS_DIR=./artifacts/local/ just deploy --silent + DEPLOY_CONFIG=./artifacts/local/deploy-{{chain}}.json \ + RPC_URL={{anvil_rpc_url}} \ + just test-deployment {{args}} + just kill-fork + +_warn message: + @tput setaf 3 && printf "[WARNING]" && tput sgr0 && echo " {{message}}" diff --git a/artifacts/holesky/.gikeep b/artifacts/holesky/.gikeep new file mode 100644 index 0000000..e69de29 diff --git a/artifacts/latest/.gitignore b/artifacts/latest/.gitignore new file mode 100644 index 0000000..9749182 --- /dev/null +++ b/artifacts/latest/.gitignore @@ -0,0 +1,2 @@ +# latest deployment configs after broadcasting on the network +*.json diff --git a/artifacts/local/.gitignore b/artifacts/local/.gitignore new file mode 100644 index 0000000..9749182 --- /dev/null +++ b/artifacts/local/.gitignore @@ -0,0 +1,2 @@ +# latest deployment configs after broadcasting on the network +*.json diff --git a/artifacts/mainnet/.gikeep b/artifacts/mainnet/.gikeep new file mode 100644 index 0000000..e69de29 diff --git a/script/generateDiffyscanContracts.js b/script/generateDiffyscanContracts.js new file mode 100644 index 0000000..d8a7037 --- /dev/null +++ b/script/generateDiffyscanContracts.js @@ -0,0 +1,34 @@ +const fs = require("fs"); +const path = require("path"); + +const ARTIFACTS_DIR = "artifacts"; + +function readJsonFile(path) { + return JSON.parse(fs.readFileSync(path)); +} + +function main() { + let result = {}; + const possibleArtifacts = fs.readdirSync(ARTIFACTS_DIR); + if (!possibleArtifacts.includes(process.argv[2])) { + throw new Error( + "Invalid arg. Possible values: " + possibleArtifacts.join(", "), + ); + } + const transactions = readJsonFile( + path.join(ARTIFACTS_DIR, process.argv[2], "transactions.json"), + ).transactions; + transactions + .filter((tx) => tx.transactionType === "CREATE") + .forEach((tx) => { + result[tx.contractAddress] = tx.contractName; + }); + console.log(JSON.stringify(result, null, 2)); +} + +try { + main(); +} catch (error) { + console.error(error); + process.exitCode = 1; +} From bc3d5bf0086e9eaa35992e62d199f3dfb399ed4b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 21 Jan 2025 17:19:04 +0100 Subject: [PATCH 14/33] deployed holesky artifacts --- artifacts/holesky/deploy-holesky.json | 7 + artifacts/holesky/transactions.json | 228 ++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 artifacts/holesky/deploy-holesky.json create mode 100644 artifacts/holesky/transactions.json diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json new file mode 100644 index 0000000..689e742 --- /dev/null +++ b/artifacts/holesky/deploy-holesky.json @@ -0,0 +1,7 @@ +{ + "CCCP": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", + "CCCPImpl": "0x73CCA4DD9E58Fa3aBCA22d55ee81AE613d958980", + "ChainId": 17000, + "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", + "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" +} \ No newline at end of file diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json new file mode 100644 index 0000000..0749a62 --- /dev/null +++ b/artifacts/holesky/transactions.json @@ -0,0 +1,228 @@ +{ + "transactions": [ + { + "hash": "0xca7a99583f33e0480dd1506d4616b0fa8770cf7d8a37cd9f258ad7f9aaa66582", + "transactionType": "CREATE", + "contractName": "CredibleCommitmentCurationProvider", + "contractAddress": "0x73cca4dd9e58fa3abca22d55ee81ae613d958980", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x37fa87", + "value": "0x0", + "input": "0x60c060405234801561000f575f5ffd5b5060405161330338038061330383398101604081905261002e91610128565b6001600160a01b03821661005557604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660805260a081905261006f610076565b505061015f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100c65760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101255780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610139575f5ffd5b82516001600160a01b038116811461014f575f5ffd5b6020939093015192949293505050565b60805160a05161317c6101875f395f611cba01525f81816104ab0152612370015261317c5ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c80638aa10435116100fe578063b10f63d81161009e578063d4eec5a61161006e578063d4eec5a614610478578063d547741f14610480578063d6dc6ae714610493578063dbba4b48146104a6575f5ffd5b8063b10f63d814610404578063ba6bc8d514610417578063c3f909d41461042a578063ca15c87314610465575f5ffd5b8063a217fddf116100d9578063a217fddf146103b6578063a3246ad3146103bd578063a3714e07146103dd578063ad32563d146103f0575f5ffd5b80638aa10435146103585780639010d07c1461037857806391d14854146103a3575f5ffd5b8063389ed267116101745780635c975abb116101445780635c975abb1461031357806370bc09f61461032a5780638456cb591461033d578063893d004e14610345575f5ffd5b8063389ed267146102ae5780633c074eb3146102d55780633f4ba83a146102e85780635865c60c146102f0575f5ffd5b80632de03aa1116101af5780632de03aa11461024e5780632de0920c146102755780632f2ff15d1461028857806336568abe1461029b575f5ffd5b806301ffc9a7146101d557806313e09453146101fd578063248a9ca314610212575b5f5ffd5b6101e86101e33660046125ce565b6104cd565b60405190151581526020015b60405180910390f35b61021061020b36600461261d565b6104f7565b005b61024061022036600461268a565b5f9081525f5160206131305f395f51905f52602052604090206001015490565b6040519081526020016101f4565b6102407f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b6102106102833660046126b1565b6106ba565b610210610296366004612779565b610942565b6102106102a9366004612779565b610978565b6102407f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b6102106102e33660046127a7565b6109b0565b610210610a3b565b6103036102fe3660046127d3565b610a70565b6040516101f49493929190612826565b5f5160206131505f395f51905f525460ff166101e8565b6102106103383660046128e3565b610aa0565b610210610aca565b61021061035336600461293c565b610afc565b610360610be1565b6040516001600160401b0390911681526020016101f4565b61038b610386366004612984565b610c18565b6040516001600160a01b0390911681526020016101f4565b6101e86103b1366004612779565b610c45565b6102405f81565b6103d06103cb36600461268a565b610c7b565b6040516101f491906129a4565b6103036103eb3660046127a7565b610cab565b6102405f5160206130f05f395f51905f5281565b6102106104123660046129fc565b610cdd565b610210610425366004612a39565b610de3565b610432610eea565b604080516001600160401b03958616815293851660208501529184169183019190915290911660608201526080016101f4565b61024061047336600461268a565b610f18565b610210610f3c565b61021061048e366004612779565b61100d565b6102106104a13660046127a7565b61103d565b61038b7f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806104f157506104f182611107565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561053b5750825b90505f826001600160401b031660011480156105565750303b155b905081158015610564575080155b156105825760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ac57845460ff60401b1916600160401b1785555b6001600160a01b038a166105d3576040516319467aa760e11b815260040160405180910390fd5b6105db61113b565b6105e361114d565b6105ed5f8b611155565b506106055f5160206130f05f395f51905f528b611155565b506106307f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b611155565b5061065b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b611155565b5061066889898989611197565b83156106ae57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106c26112ce565b6106ca612514565b6001600160a01b0386166106f157604051634434240960e11b815260040160405180910390fd5b6106fc8189896112fe565b80606001516001600160a01b0316336001600160a01b031614610732576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161075457604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f61077682611312565b90505f61078282611378565b8051909150156107a5576040516342ee68b560e01b815260040160405180910390fd5b80604001516107c7576040516328a5715760e21b815260040160405180910390fd5b6107d1838a611476565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3604080516060810182526001600160401b03431681525f602082018190529181019190915261084f90849061152a565b61085b84848a8a61158b565b604080516020601f88018190048102820183018352810187815261089e928291908a908a90819085018382808284375f920191909152505050915250849061162c565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef88886040516108df929190612a55565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206131305f395f51905f5260205260409020600101546109688161164b565b6109728383611155565b50505050565b6001600160a01b03811633146109a15760405163334bd91960e11b815260040160405180910390fd5b6109ab8282611655565b505050565b5f5160206130f05f395f51905f526109c78161164b565b5f6109d2848461168e565b90505f6109de82611312565b90508060400151156109fa575f60408201526109fa828261152a565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610a658161164b565b610a6d6116d7565b50565b5f5f5f610a7b612548565b5f610a8586611736565b9050610a9081611780565b9450945094509450509193509193565b5f5160206130f05f395f51905f52610ab78161164b565b610ac385858585611197565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610af48161164b565b610a6d611816565b610b046112ce565b6001600160a01b038116610b2b57604051634434240960e11b815260040160405180910390fd5b610b33612514565b610b3e8185856112fe565b80606001516001600160a01b0316336001600160a01b031614610b74576040516349ad9b0960e11b815260040160405180910390fd5b5f610b7f858561168e565b9050610b8b8184611476565b6040516001600160a01b03841681526001600160401b0385169062ffffff8716907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a35050505050565b5f610c137ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206130d05f395f51905f52602081905260408220610c3d908461185e565b949350505050565b5f9182525f5160206131305f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f8181525f5160206130d05f395f51905f526020819052604090912060609190610ca490611869565b9392505050565b5f5f5f610cb6612548565b5f610cc1878761168e565b9050610ccc81611780565b929a91995097509095509350505050565b5f5160206130f05f395f51905f52610cf48161164b565b610cfc612514565b610d068186611875565b5f610d108661190b565b85151581526001600160401b03858116602080840191825262ffffff8a165f9081527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd1029091526040902083518154925168ffffffffffffffffff1990931690151568ffffffffffffffff00191617610100929093169190910291909117905590506040805186151581526001600160401b038616602082015262ffffff8816917f9ea2c0fdca3eb79272af911e373f2b128f6f8ae84133e610e4798488facc243d910160405180910390a2505050505050565b610deb6112ce565b5f610df533611736565b90505f610e09610e0483611312565b611378565b8051909150610e2b57604051631a8660cb60e01b815260040160405180910390fd5b5f610e358361197a565b9050805f01516001600160401b0316856001600160401b03161180610e6f575080602001516001600160401b0316846001600160401b0316105b80610ea6575080516001600160401b038681169116148015610ea6575080602001516001600160401b0316846001600160401b0316145b15610ec45760405163c7f544bb60e01b815260040160405180910390fd5b610ecc612514565b610ed681856119c8565b610ee28185888861158b565b505050505050565b5f5f5f5f5f610ef76119d9565b80516020820151604083015160609093015191989097509195509350915050565b5f8181525f5160206130d05f395f51905f52602081905260408220610ca490611a6b565b610f446112ce565b5f610f4e33611736565b90505f610f5a82611312565b90505f610f6682611378565b8051909150610f8857604051631a8660cb60e01b815260040160405180910390fd5b8060600151610faa57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b0343166020830152610fc3838361152a565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d468490602001610bd2565b5f8281525f5160206131305f395f51905f5260205260409020600101546110338161164b565b6109728383611655565b5f5160206130f05f395f51905f526110548161164b565b5f61105f848461168e565b90505f61106b82611312565b90505f61107782611378565b805190915061109957604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b0343166020830152600160408301526110b9838361152a565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f6001600160e01b03198216637965db0b60e01b14806104f157506301ffc9a760e01b6001600160e01b03198316146104f1565b611143611a74565b61114b611abd565b565b61114b611a74565b5f5f5160206130d05f395f51905f528161116f8585611add565b90508015610c3d575f85815260208390526040902061118e9085611b7e565b50949350505050565b816001600160401b03165f036111c05760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f036111e857604051628faa1f60e61b815260040160405180910390fd5b60408051608080820183526001600160401b03878116808452878216602080860182905288841686880181905293881660609687018190527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10380546001600160801b0319168517600160401b8502176fffffffffffffffffffffffffffffffff16600160801b87026001600160c01b031617600160c01b8302179055875193845290830191909152818601929092529283015291517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd36929181900390910190a150505050565b5f5160206131505f395f51905f525460ff161561114b5760405163d93c066560e01b815260040160405180910390fd5b6113088383611875565b6109ab8382611b92565b604080516060810182525f808252602082018190529181019190915261133782611e9d565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b604080516080810182525f808252602082018190529181018290526060810191909152435f6113a56119d9565b90505f5f85602001516001600160401b03161180156113e85750826001600160401b0316826020015186602001516113dd9190612a97565b6001600160401b0316105b90505f5f865f01516001600160401b0316118015611404575081155b90505f8115801561141757508660400151155b90505f8280156114455750845188516001600160401b0388169161143a91612a97565b6001600160401b0316105b6040805160808101825294151585529415156020850152911515938301939093521515606082015295945050505050565b6114808282611ecc565b5f61148a83611e9d565b80549091506001600160a01b031680158015906114b95750826001600160a01b0316816001600160a01b031614155b156114e3576001600160a01b0381165f9081525f5160206131105f395f51905f5260205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691821790555f9081525f5160206131105f395f51905f526020526040902055565b8061153483611e9d565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b61159a84608001518383611f1d565b83516115a7908383611fa1565b604080518082019091526001600160401b038084168252821660208201526115d0908490612054565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f4758feb40e053085c8bfb6815d2061a4057e0820d0f681d7c19609673db27e19910160405180910390a350505050565b8061163683611e9d565b8151600391909101908190610ac39082612b46565b610a6d813361209b565b5f5f5160206130d05f395f51905f528161166f85856120dc565b90508015610c3d575f85815260208390526040902061118e9085612155565b6001600160401b038116604083901b62ffffff60401b16175f6116b082612169565b6001600160a01b0316036104f1576040516325ec6c1f60e01b815260040160405180910390fd5b6116df612183565b5f5160206131505f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f9081525f5160206131105f395f51905f5260205260408120549081900361177b5760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f61178b612548565b611793612514565b61179d81876119c8565b6117a6866121b2565b91505f6117b68360400151611378565b90505f6117c5835f015161190b565b825190915080156117d557508051155b80156117e257508260a001515b80156117fd57505f5160206131505f395f51905f525460ff16155b9450825f01518360400151965096505050509193509193565b61181e6112ce565b5f5160206131505f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611718565b5f610ca483836122ee565b60605f610ca483612314565b5f61187e61236d565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa1580156118c6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526118ed9190810190612d17565b62ffffff9092168352506020908101516001600160a01b0316910152565b6040805180820182525f808252602091820181905262ffffff9390931683527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10281529181902081518083019092525460ff81161515825261010090046001600160401b03169181019190915290565b604080518082019091525f808252602082015261199682611e9d565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b604081901c816109728483836112fe565b604080516080810182525f8082526020820181905291810182905260608101919091527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10060408051608081018252600392909201546001600160401b038082168452600160401b820481166020850152600160801b8204811692840192909252600160c01b9004166060820152919050565b5f6104f1825490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661114b57604051631afcd79f60e31b815260040160405180910390fd5b611ac5611a74565b5f5160206131505f395f51905f52805460ff19169055565b5f5f5160206131305f395f51905f52611af68484610c45565b611b75575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611b2b3390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506104f1565b5f9150506104f1565b5f610ca4836001600160a01b0384166123ee565b815162ffffff165f03611bb857604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bf9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c1d9190612e48565b9050806001600160401b0316826001600160401b031610611c515760405163b9aa612760e01b815260040160405180910390fd5b5f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c92573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cb69190612e48565b90507f00000000000000000000000000000000000000000000000000000000000000008103611df4576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa158015611d2f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d539190612e6a565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015611da8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dcc9190612e98565b6101808101516001600160a01b031660608801525163ffffffff166080870152506109729050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015611e49573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611e709190810190612fc1565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd1006020526040902090565b6001600160a01b0381165f9081525f5160206131105f395f51905f5260205260409020548015801590611eff5750828114155b156109ab5760405163324e8e5f60e01b815260040160405180910390fd5b806001600160401b0316826001600160401b03161115611f505760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b0316101580611f835750826001600160401b0316826001600160401b031610155b156109ab57604051637ba485c560e01b815260040160405180910390fd5b5f611faa6119d9565b90505f611fb68561190b565b805190915015611fd957604051630dbfe5fd60e31b815260040160405180910390fd5b5f611fe4858561305a565b611fef906001612a97565b90505f82602001516001600160401b03165f14612010578260200151612016565b83604001515b9050806001600160401b0316826001600160401b0316111561204b57604051631d1f75fb60e31b815260040160405180910390fd5b50505050505050565b8061205e83611e9d565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b03199094169216919091179190911790555050565b6120a58282610c45565b6120d85760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206131305f395f51905f526120f58484610c45565b15611b75575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506104f1565b5f610ca4836001600160a01b03841661243a565b5f61217382611e9d565b546001600160a01b031692915050565b5f5160206131505f395f51905f525460ff1661114b57604051638dfc202b60e01b815260040160405180910390fd5b6121ba612548565b6121c382611e9d565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061226490612aca565b80601f016020809104026020016040519081016040528092919081815260200182805461229090612aca565b80156122db5780601f106122b2576101008083540402835291602001916122db565b820191905f5260205f20905b8154815290600101906020018083116122be57829003601f168201915b5050509190925250505090525092915050565b5f825f01828154811061230357612303613079565b905f5260205f200154905092915050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561236157602002820191905f5260205f20905b81548152602001906001019080831161234d575b50505050509050919050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123ca573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c13919061308d565b5f81815260018301602052604081205461243357508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104f1565b505f6104f1565b5f8181526001830160205260408120548015611b75575f61245c6001836130a8565b85549091505f9061246f906001906130a8565b90508082146124ce575f865f01828154811061248d5761248d613079565b905f5260205f200154905080875f0184815481106124ad576124ad613079565b5f918252602080832090910192909255918252600188019052604090208390555b85548690806124df576124df6130bb565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104f1565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161258f60405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f80825260208281018290529282015291019081526020016125c96040518060200160405280606081525090565b905290565b5f602082840312156125de575f5ffd5b81356001600160e01b031981168114610ca4575f5ffd5b6001600160a01b0381168114610a6d575f5ffd5b6001600160401b0381168114610a6d575f5ffd5b5f5f5f5f5f60a08688031215612631575f5ffd5b853561263c816125f5565b9450602086013561264c81612609565b9350604086013561265c81612609565b9250606086013561266c81612609565b9150608086013561267c81612609565b809150509295509295909350565b5f6020828403121561269a575f5ffd5b5035919050565b62ffffff81168114610a6d575f5ffd5b5f5f5f5f5f5f5f60c0888a0312156126c7575f5ffd5b87356126d2816126a1565b965060208801356126e281612609565b955060408801356126f2816125f5565b9450606088013561270281612609565b9350608088013561271281612609565b925060a08801356001600160401b0381111561272c575f5ffd5b8801601f81018a1361273c575f5ffd5b80356001600160401b03811115612751575f5ffd5b8a6020828401011115612762575f5ffd5b602082019350809250505092959891949750929550565b5f5f6040838503121561278a575f5ffd5b82359150602083013561279c816125f5565b809150509250929050565b5f5f604083850312156127b8575f5ffd5b82356127c3816126a1565b9150602083013561279c81612609565b5f602082840312156127e3575f5ffd5b8135610ca4816125f5565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e06101408401526128d86101608401826127ee565b979650505050505050565b5f5f5f5f608085870312156128f6575f5ffd5b843561290181612609565b9350602085013561291181612609565b9250604085013561292181612609565b9150606085013561293181612609565b939692955090935050565b5f5f5f6060848603121561294e575f5ffd5b8335612959816126a1565b9250602084013561296981612609565b91506040840135612979816125f5565b809150509250925092565b5f5f60408385031215612995575f5ffd5b50508035926020909101359150565b602080825282518282018190525f918401906040840190835b818110156129e45783516001600160a01b03168352602093840193909201916001016129bd565b509095945050505050565b8015158114610a6d575f5ffd5b5f5f5f60608486031215612a0e575f5ffd5b8335612a19816126a1565b92506020840135612a29816129ef565b9150604084013561297981612609565b5f5f60408385031215612a4a575f5ffd5b82356127c381612609565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156104f1576104f1612a83565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612ade57607f821691505b602082108103612afc57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156109ab57805f5260205f20601f840160051c81016020851015612b275750805b601f840160051c820191505b81811015610ac3575f8155600101612b33565b81516001600160401b03811115612b5f57612b5f612ab6565b612b7381612b6d8454612aca565b84612b02565b6020601f821160018114612ba5575f8315612b8e5750848201515b5f19600385901b1c1916600184901b178455610ac3565b5f84815260208120601f198516915b82811015612bd45787850151825560209485019460019092019101612bb4565b5084821015612bf157868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b0381118282101715612c2357612c23612ab6565b60405290565b6040516101e081016001600160401b0381118282101715612c2357612c23612ab6565b805161177b816126a1565b805161177b816125f5565b805161ffff8116811461177b575f5ffd5b805160ff8116811461177b575f5ffd5b5f82601f830112612c92575f5ffd5b81516001600160401b03811115612cab57612cab612ab6565b604051601f8201601f19908116603f011681016001600160401b0381118282101715612cd957612cd9612ab6565b604052818152838201602001851015612cf0575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b805161177b81612609565b5f60208284031215612d27575f5ffd5b81516001600160401b03811115612d3c575f5ffd5b82016101a08185031215612d4e575f5ffd5b612d56612c00565b612d5f82612c4c565b8152612d6d60208301612c57565b6020820152612d7e60408301612c62565b6040820152612d8f60608301612c62565b6060820152612da060808301612c62565b6080820152612db160a08301612c73565b60a082015260c08201516001600160401b03811115612dce575f5ffd5b612dda86828501612c83565b60c083015250612dec60e08301612d0c565b60e082015261010082810151908201526101208083015190820152612e146101408301612c62565b610140820152612e276101608301612d0c565b610160820152612e3a6101808301612d0c565b610180820152949350505050565b5f60208284031215612e58575f5ffd5b5051919050565b805161177b816129ef565b5f60208284031215612e7a575f5ffd5b8151610ca4816129ef565b805163ffffffff8116811461177b575f5ffd5b5f6101e0828403128015612eaa575f5ffd5b50612eb3612c29565b612ebc83612e85565b8152612eca60208401612e85565b6020820152612edb60408401612e85565b6040820152612eec60608401612e85565b6060820152612efd60808401612e85565b6080820152612f0e60a08401612e85565b60a0820152612f1f60c08401612e85565b60c0820152612f3060e08401612c73565b60e0820152612f426101008401612e85565b610100820152612f556101208401612e85565b610120820152612f686101408401612c57565b610140820152612f7b6101608401612c57565b610160820152612f8e6101808401612c57565b610180820152612fa16101a08401612c57565b6101a0820152612fb46101c08401612e5f565b6101c08201529392505050565b5f5f5f5f5f5f60c08789031215612fd6575f5ffd5b8651612fe1816129ef565b60208801519096506001600160401b03811115612ffc575f5ffd5b61300889828a01612c83565b9550506040870151613019816125f5565b606088015190945061302a81612609565b608088015190935061303b81612609565b60a088015190925061304c81612609565b809150509295509295509295565b6001600160401b0382811682821603908111156104f1576104f1612a83565b634e487b7160e01b5f52603260045260245ffd5b5f6020828403121561309d575f5ffd5b8151610ca4816125f5565b818103818111156104f1576104f1612a83565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd10102dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x3d", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionType": "CREATE", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": null, + "arguments": [ + "0x73CCA4DD9E58Fa3aBCA22d55ee81AE613d958980", + "0x401FD888B5E41113B7c0C47725A742bbc3A083EF", + "0x13e09453000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x139d27", + "value": "0x0", + "input": "0x608060405234801561000f575f5ffd5b50604051610c46380380610c4683398101604081905261002e9161031f565b828161003a828261004e565b506100469050826100ac565b505050610406565b61005782610119565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100a05761009b8282610197565b505050565b6100a861020a565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6100eb5f516020610c265f395f51905f52546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a16101168161022b565b50565b806001600160a01b03163b5f0361015357604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f5f846001600160a01b0316846040516101b391906103f0565b5f60405180830381855af49150503d805f81146101eb576040519150601f19603f3d011682016040523d82523d5f602084013e6101f0565b606091505b509092509050610201858383610268565b95945050505050565b34156102295760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b03811661025457604051633173bdd160e11b81525f600482015260240161014a565b805f516020610c265f395f51905f52610176565b60608261027d57610278826102c7565b6102c0565b815115801561029457506001600160a01b0384163b155b156102bd57604051639996b31560e01b81526001600160a01b038516600482015260240161014a565b50805b9392505050565b8051156102d75780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80516001600160a01b0381168114610306575f5ffd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f5f5f60608486031215610331575f5ffd5b61033a846102f0565b9250610348602085016102f0565b60408501519092506001600160401b03811115610363575f5ffd5b8401601f81018613610373575f5ffd5b80516001600160401b0381111561038c5761038c61030b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156103ba576103ba61030b565b6040528181528282016020018810156103d1575f5ffd5b8160208401602083015e5f602083830101528093505050509250925092565b5f82518060208501845e5f920191825250919050565b610813806104135f395ff3fe608060405260043610610073575f3560e01c8063916f1fd71161004d578063916f1fd7146100f1578063aad188681461011d578063ad729a711461013c578063adcbc2371461015057610082565b8063133512581461008a5780633ebdd0eb146100b3578063773f5be8146100d257610082565b3661008257610080610164565b005b610080610164565b348015610095575f5ffd5b5061009e610176565b60405190151581526020015b60405180910390f35b3480156100be575f5ffd5b506100806100cd366004610759565b61018f565b3480156100dd575f5ffd5b506100806100ec366004610759565b610206565b3480156100fc575f5ffd5b5061010561026a565b6040516001600160a01b0390911681526020016100aa565b348015610128575f5ffd5b50610080610137366004610772565b610278565b348015610147575f5ffd5b50610105610318565b34801561015b575f5ffd5b50610080610321565b61017461016f610426565b61042f565b565b5f8061018061044d565b6001600160a01b031614905090565b5f61019861044d565b90506001600160a01b0381166101c15760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b03811633146101ea57604051637bfa4b9f60e01b815260040160405180910390fd5b6102028260405180602001604052805f81525061047f565b5050565b5f61020f61044d565b90506001600160a01b0381166102385760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b038116331461026157604051637bfa4b9f60e01b815260040160405180910390fd5b610202826104d9565b5f61027361044d565b905090565b5f61028161044d565b90506001600160a01b0381166102aa5760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b03811633146102d357604051637bfa4b9f60e01b815260040160405180910390fd5b6103128484848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061047f92505050565b50505050565b5f610273610426565b5f61032a61044d565b90506001600160a01b0381166103535760405163b83646a960e01b815260040160405180910390fd5b6001600160a01b038116331461037c57604051637bfa4b9f60e01b815260040160405180910390fd5b5f61038561044d565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610380546001600160a01b0319169055604080516001600160a01b03831681525f602082015281519293507f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f929081900390910190a16040517f158b204828f9326d9bb3c2be9336986c14911b4a72b93d1801f207aac3c68b9f905f90a15050565b5f610273610530565b365f5f375f5f365f845af43d5f5f3e808015610449573d5ff35b3d5ffd5b5f7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61048882610557565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156104d1576104cc82826105d5565b505050565b610202610647565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61050261044d565b604080516001600160a01b03928316815291841660208301520160405180910390a161052d81610666565b50565b5f7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610470565b806001600160a01b03163b5f0361059157604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f5f846001600160a01b0316846040516105f191906107f0565b5f60405180830381855af49150503d805f8114610629576040519150601f19603f3d011682016040523d82523d5f602084013e61062e565b606091505b509150915061063e8583836106b6565b95945050505050565b34156101745760405163b398979f60e01b815260040160405180910390fd5b6001600160a01b03811661068f57604051633173bdd160e11b81525f6004820152602401610588565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105b4565b6060826106cb576106c682610715565b61070e565b81511580156106e257506001600160a01b0384163b155b1561070b57604051639996b31560e01b81526001600160a01b0385166004820152602401610588565b50805b9392505050565b8051156107255780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80356001600160a01b0381168114610754575f5ffd5b919050565b5f60208284031215610769575f5ffd5b61070e8261073e565b5f5f5f60408486031215610784575f5ffd5b61078d8461073e565b9250602084013567ffffffffffffffff8111156107a8575f5ffd5b8401601f810186136107b8575f5ffd5b803567ffffffffffffffff8111156107ce575f5ffd5b8660208284010111156107df575f5ffd5b939660209190910195509293505050565b5f82518060208501845e5f92019182525091905056fea164736f6c634300081c000ab53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610300000000000000000000000073cca4dd9e58fa3abca22d55ee81ae613d958980000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a413e09453000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000", + "nonce": "0x3e", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x19fd698", + "logs": [ + { + "address": "0x73cca4dd9e58fa3abca22d55ee81ae613d958980", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0xca7a99583f33e0480dd1506d4616b0fa8770cf7d8a37cd9f258ad7f9aaa66582", + "transactionIndex": "0x9", + "logIndex": "0xf7", + "removed": false + } + ], + "logsBloom": "0x00000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xca7a99583f33e0480dd1506d4616b0fa8770cf7d8a37cd9f258ad7f9aaa66582", + "transactionIndex": "0x9", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "gasUsed": "0x2b0f7c", + "effectiveGasPrice": "0xf4afd", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x73cca4dd9e58fa3abca22d55ee81ae613d958980" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1aeed05", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000073cca4dd9e58fa3abca22d55ee81ae613d958980" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xf8", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xf9", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfa", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfb", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "0x000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef" + ], + "data": "0x", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfc", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd36" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfd", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xfe", + "removed": false + }, + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "blockTimestamp": "0x678fc69c", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "logIndex": "0xff", + "removed": false + } + ], + "logsBloom": "0x00000004000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000040000000000000000080000000000000200000000000000000000400002000000000000000000000000000200000000000000028400000000000000000800000000800000000000010000000000000000000000002000000800000000001000080000000080000000000000800000000000000000000060000000000000000000000000000200001000000000000020000020000000000000200000000000000000000404000100000000040020018000000000000000000000000000000000040000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x86e9deca47d1dd9f8bd745cc14a013c03f99a10a8cc28f92702bbdd66e688fc3", + "transactionIndex": "0xa", + "blockHash": "0x09a6dbe453836835cd7285caa1dc22b3d6c0acbf57b11405398d7ca36d7f4d44", + "blockNumber": "0x30b217", + "gasUsed": "0xf166d", + "effectiveGasPrice": "0xf4afd", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1737475792, + "chain": 17000, + "commit": "ac812a4" +} \ No newline at end of file From c35d4cc8442e51f19d79bd2a03470cdd4c001ee0 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 24 Jan 2025 00:04:49 +0100 Subject: [PATCH 15/33] feat: refactor storage contracts --- ...ommitmentCurationProvider.sol => CCCP.sol} | 181 ++++++------------ src/interfaces/ICCCP.sol | 34 ++++ src/interfaces/ICCCPConfigStorage.sol | 38 ++++ src/interfaces/ICCCPOperatorStatesStorage.sol | 59 ++++++ src/lib/CCCPConfigStorage.sol | 77 ++++++++ src/lib/CCCPDataStorage.sol | 171 ----------------- src/lib/CCCPOperatorStatesStorage.sol | 118 ++++++++++++ 7 files changed, 389 insertions(+), 289 deletions(-) rename src/{CredibleCommitmentCurationProvider.sol => CCCP.sol} (74%) create mode 100644 src/interfaces/ICCCP.sol create mode 100644 src/interfaces/ICCCPConfigStorage.sol create mode 100644 src/interfaces/ICCCPOperatorStatesStorage.sol create mode 100644 src/lib/CCCPConfigStorage.sol delete mode 100644 src/lib/CCCPDataStorage.sol create mode 100644 src/lib/CCCPOperatorStatesStorage.sol diff --git a/src/CredibleCommitmentCurationProvider.sol b/src/CCCP.sol similarity index 74% rename from src/CredibleCommitmentCurationProvider.sol rename to src/CCCP.sol index e814a2d..6b2d4c7 100644 --- a/src/CredibleCommitmentCurationProvider.sol +++ b/src/CCCP.sol @@ -8,15 +8,9 @@ import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import { - CCCPDataStorage as DS, - OperatorState, - OperatorOptInOutState, - OperatorKeysRangeState, - OperatorExtraData, - ModuleState, - Config -} from "./lib/CCCPDataStorage.sol"; +import {CCCPOperatorStatesStorage} from "./lib/CCCPOperatorStatesStorage.sol"; +import {CCCPConfigStorage} from "./lib/CCCPConfigStorage.sol"; +import {ICCCP} from "./interfaces/ICCCP.sol"; import {ILidoLocator} from "./interfaces/ILidoLocator.sol"; import {StakingModule, IStakingRouter} from "./interfaces/IStakingRouter.sol"; @@ -24,13 +18,18 @@ import {IStakingModule} from "./interfaces/IStakingModule.sol"; import {CSMNodeOperator, ICSModule} from "./interfaces/ICSModule.sol"; import {ICuratedModule} from "./interfaces/ICuratedModule.sol"; -contract CredibleCommitmentCurationProvider is +/** + * @title CCCP + * @notice CredibleCommitmentCurationProvider contract + */ +contract CCCP is + ICCCP, Initializable, + CCCPConfigStorage, + CCCPOperatorStatesStorage, AccessControlEnumerableUpgradeable, PausableUpgradeable { - using DS for uint256; - struct LidoOperatorCache { uint24 moduleId; address moduleAddress; @@ -52,27 +51,24 @@ contract CredibleCommitmentCurationProvider is bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); ILidoLocator public immutable LIDO_LOCATOR; - // hardcoded CSModule type, used for the operator's state retrieval + // CSModule type, used for the operator's state retrieval bytes32 internal immutable CS_MODULE_TYPE; event OptInSucceeded(uint256 indexed moduleId, uint256 indexed operatorId, address manager); event OptOutRequested(uint256 indexed moduleId, uint256 indexed operatorId, bool isForced); - event KeyRangeUpdated( - uint256 indexed moduleId, uint256 indexed operatorId, uint256 keysRangeStart, uint256 keysRangeEnd - ); + event ResetForcedOptOut(uint256 indexed moduleId, uint256 indexed operatorId); + + event KeysRangeUpdated(uint256 indexed moduleId, uint256 indexed operatorId, uint256 indexStart, uint256 indexEnd); + event OperatorManagerUpdated(uint256 indexed moduleId, uint256 indexed operatorId, address manager); event RPCUrlUpdated(uint256 indexed moduleId, uint256 indexed operatorId, string rpcURL); - event ModuleStateUpdated(uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxValidators); event ConfigUpdated( uint256 optInMinDurationBlocks, uint256 optOutDelayDurationBlocks, uint256 defaultOperatorMaxValidators, uint256 defaultBlockGasLimit ); - event ResetForcedOptOut(uint256 indexed moduleId, uint256 indexed operatorId); - event OperatorManagerUpdated(uint256 indexed moduleId, uint256 indexed operatorId, address manager); + event ModuleConfigUpdated(uint256 indexed moduleId, uint256 operatorMaxValidators, bool isDisabled); - error OperatorNotRegistered(); - error ManagerNotRegistered(); error RewardAddressMismatch(); error OperatorNotActive(); error ModuleDisabled(); @@ -87,13 +83,10 @@ contract CredibleCommitmentCurationProvider is error ZeroCommitteeAddress(); error ZeroOperatorManagerAddress(); error ZeroLocatorAddress(); - error ZeroDefaultOperatorMaxValidators(); - error ZeroDefaultBlockGasLimit(); constructor(address lidoLocator, bytes32 csModuleType) { if (lidoLocator == address(0)) revert ZeroLocatorAddress(); LIDO_LOCATOR = ILidoLocator(lidoLocator); - CS_MODULE_TYPE = csModuleType; _disableInitializers(); @@ -116,7 +109,7 @@ contract CredibleCommitmentCurationProvider is _grantRole(PAUSE_ROLE, committeeAddress); _grantRole(RESUME_ROLE, committeeAddress); - _setConfig( + _updateConfig( optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit ); } @@ -158,10 +151,10 @@ contract CredibleCommitmentCurationProvider is revert OperatorNotActive(); } - uint256 opKey = DS.__encOpKey(moduleId, operatorId); + uint256 opKey = __encOpKey(moduleId, operatorId); // check if the operator is already has the state - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); if (flags.isOptedIn) { revert OperatorAlreadyRegistered(); @@ -171,16 +164,16 @@ contract CredibleCommitmentCurationProvider is // save operator state /// @dev also checks if the proposed manager is already registered for different operator - opKey._setOperatorManager(manager); + _setOperatorManager(opKey, manager); emit OperatorManagerUpdated(moduleId, operatorId, manager); - opKey._setOperatorOptInOutState( - OperatorOptInOutState({optInBlock: uint64(block.number), optOutBlock: 0, isOptOutForced: false}) + _setOperatorOptInOutState( + opKey, OptInOutState({optInBlock: uint64(block.number), optOutBlock: 0, isOptOutForced: false}) ); _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); /// @dev no checks on rpcUrl, so it can be rewritten on repeated opt-in - opKey._setOperatorExtraData(OperatorExtraData({rpcURL: rpcURL})); + _setOperatorExtraData(opKey, ExtraData({rpcURL: rpcURL})); emit RPCUrlUpdated(moduleId, operatorId, rpcURL); emit OptInSucceeded(moduleId, operatorId, manager); @@ -190,7 +183,7 @@ contract CredibleCommitmentCurationProvider is /// @dev should be called by the operator manager address function optOut() external whenNotPaused { uint256 opKey = _getOpKeyByManager(msg.sender); - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); if (!flags.isOptedIn) { revert OperatorNotActive(); @@ -199,9 +192,9 @@ contract CredibleCommitmentCurationProvider is } optInOutState.optOutBlock = uint64(block.number); - opKey._setOperatorOptInOutState(optInOutState); + _setOperatorOptInOutState(opKey, optInOutState); - (uint24 moduleId, uint64 operatorId) = opKey.__decOpKey(); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); emit OptOutRequested(moduleId, operatorId, false); } @@ -209,7 +202,7 @@ contract CredibleCommitmentCurationProvider is function optOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { uint256 opKey = _getOpKeyById(moduleId, operatorId); - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); if (!flags.isOptedIn) { revert OperatorNotActive(); @@ -218,7 +211,7 @@ contract CredibleCommitmentCurationProvider is optInOutState.optOutBlock = uint64(block.number); optInOutState.isOptOutForced = true; - opKey._setOperatorOptInOutState(optInOutState); + _setOperatorOptInOutState(opKey, optInOutState); emit OptOutRequested(moduleId, operatorId, true); } @@ -227,20 +220,21 @@ contract CredibleCommitmentCurationProvider is /// @dev should be called by the operator manager address function updateKeysRange(uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd) external whenNotPaused { uint256 opKey = _getOpKeyByManager(msg.sender); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(opKey._getOperatorOptInOutState()); + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); if (!flags.isOptedIn) { revert OperatorNotActive(); } - OperatorKeysRangeState memory keysRangeState = opKey._getOperatorKeysRangeState(); + KeysRange memory keysRange = _getOperatorKeysRange(opKey); if ( - newKeyIndexRangeStart > keysRangeState.indexStart || newKeyIndexRangeEnd < keysRangeState.indexEnd - || (newKeyIndexRangeStart == keysRangeState.indexStart && newKeyIndexRangeEnd == keysRangeState.indexEnd) + newKeyIndexRangeStart > keysRange.indexStart || newKeyIndexRangeEnd < keysRange.indexEnd + || (newKeyIndexRangeStart == keysRange.indexStart && newKeyIndexRangeEnd == keysRange.indexEnd) ) { revert KeyIndexMismatch(); } LidoOperatorCache memory _c; - _loadLidoNodeOperator(_c, opKey); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + _loadLidoNodeOperator(_c, moduleId, operatorId); _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); } @@ -258,11 +252,8 @@ contract CredibleCommitmentCurationProvider is revert RewardAddressMismatch(); } - uint256 opKey = _getOpKeyById(moduleId, operatorId); - /// @dev also checks if the proposed manager is already registered for different operator - opKey._setOperatorManager(newManager); - + _setOperatorManager(__encOpKey(moduleId, operatorId), newManager); emit OperatorManagerUpdated(moduleId, operatorId, newManager); } @@ -294,13 +285,7 @@ contract CredibleCommitmentCurationProvider is uint64 defaultBlockGasLimit ) { - Config memory cfg = DS._getConfig(); - return ( - cfg.optInMinDurationBlocks, - cfg.optOutDelayDurationBlocks, - cfg.defaultOperatorMaxValidators, - cfg.defaultBlockGasLimit - ); + return _getConfig(); } function getContractVersion() external view returns (uint64) { @@ -309,10 +294,10 @@ contract CredibleCommitmentCurationProvider is function resetForcedOptOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { uint256 opKey = _getOpKeyById(moduleId, operatorId); - OperatorOptInOutState memory optInOutState = opKey._getOperatorOptInOutState(); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); if (optInOutState.isOptOutForced) { optInOutState.isOptOutForced = false; - opKey._setOperatorOptInOutState(optInOutState); + _setOperatorOptInOutState(opKey, optInOutState); } emit ResetForcedOptOut(moduleId, operatorId); } @@ -325,13 +310,13 @@ contract CredibleCommitmentCurationProvider is uint64 defaultOperatorMaxValidators, uint64 defaultBlockGasLimit ) external onlyRole(COMMITTEE_ROLE) { - _setConfig( + _updateConfig( optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit ); } /// @notice Update Disable/enable state and operator's max validators for the module - function setModuleState(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators) + function setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators) external onlyRole(COMMITTEE_ROLE) { @@ -339,35 +324,18 @@ contract CredibleCommitmentCurationProvider is LidoOperatorCache memory _c; _loadLidoModuleData(_c, moduleId); - ModuleState memory state = DS._getModuleState(moduleId); - /// @dev operators in disabled modules are automatically considered as opted-out - state.isDisabled = isDisabled; - /// @dev zero value means use default config - state.maxValidators = operatorMaxValidators; - DS._setModuleState(moduleId, state); - - emit ModuleStateUpdated(moduleId, isDisabled, operatorMaxValidators); + _setModuleConfig(moduleId, operatorMaxValidators, isDisabled); + emit ModuleConfigUpdated(moduleId, operatorMaxValidators, isDisabled); } - function _setConfig( + function _updateConfig( uint64 optInMinDurationBlocks, uint64 optOutDelayDurationBlocks, uint64 defaultOperatorMaxValidators, uint64 defaultBlockGasLimit ) internal { - if (defaultOperatorMaxValidators == 0) { - revert ZeroDefaultOperatorMaxValidators(); - } - if (defaultBlockGasLimit == 0) { - revert ZeroDefaultBlockGasLimit(); - } - DS._setConfig( - Config({ - optInMinDurationBlocks: optInMinDurationBlocks, - optOutDelayDurationBlocks: optOutDelayDurationBlocks, - defaultOperatorMaxValidators: defaultOperatorMaxValidators, - defaultBlockGasLimit: defaultBlockGasLimit - }) + _setConfig( + optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit ); emit ConfigUpdated( optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit @@ -384,11 +352,8 @@ contract CredibleCommitmentCurationProvider is _checkModuleParams(_c.moduleId, newKeyIndexRangeStart, newKeyIndexRangeEnd); // save operator state - opKey._setOperatorKeysRangeState( - OperatorKeysRangeState({indexStart: newKeyIndexRangeStart, indexEnd: newKeyIndexRangeEnd}) - ); - - emit KeyRangeUpdated(_c.moduleId, _c.operatorId, newKeyIndexRangeStart, newKeyIndexRangeEnd); + _setOperatorKeysRange(opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); + emit KeysRangeUpdated(_c.moduleId, _c.operatorId, newKeyIndexRangeStart, newKeyIndexRangeEnd); } function _getOperator(uint256 opKey) @@ -408,18 +373,18 @@ contract CredibleCommitmentCurationProvider is ) { LidoOperatorCache memory _c; - - _loadLidoNodeOperator(_c, opKey); - state = opKey._getOperatorState(); + (moduleId, operatorId) = __decOpKey(opKey); + _loadLidoNodeOperator(_c, moduleId, operatorId); + state = _getOperatorState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(state.optInOutState); - ModuleState memory moduleState = DS._getModuleState(_c.moduleId); + (, bool isDisabled) = _getModuleConfig(_c.moduleId); // operator is enabled: // - if it's s opted in // - if module not disabled // - if operator is active in Lido module // - if the contract is not paused - isEnabled = flags.isOptedIn && !moduleState.isDisabled && _c.isActive && !paused(); + isEnabled = flags.isOptedIn && !isDisabled && _c.isActive && !paused(); return ( _c.moduleId, @@ -428,42 +393,27 @@ contract CredibleCommitmentCurationProvider is // state.optInOutState.optInBlock, // state.optInOutState.optOutBlock, // state.optInOutState.isOptOutForced, - // state.keysRangeState.indexStart, - // state.keysRangeState.indexEnd, + // state.keysRange.indexStart, + // state.keysRange.indexEnd, // state.extraData.rpcURL state ); } function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { - Config memory cfg = DS._getConfig(); - ModuleState memory state = DS._getModuleState(moduleId); - if (state.isDisabled) { + (,, uint64 defaultOperatorMaxValidators,) = _getConfig(); + (uint64 moduleMaxValidators, bool isDisabled) = _getModuleConfig(moduleId); + if (isDisabled) { revert ModuleDisabled(); } uint64 totalKeys = endIndex - startIndex + 1; - uint64 maxValidators = state.maxValidators == 0 ? cfg.defaultOperatorMaxValidators : state.maxValidators; + uint64 maxValidators = moduleMaxValidators == 0 ? defaultOperatorMaxValidators : moduleMaxValidators; if (totalKeys > maxValidators) { revert KeysRangeExceedMaxValidators(); } } - /// @dev reverts if the operator not registered - function _getOpKeyById(uint24 moduleId, uint64 operatorId) internal view returns (uint256 opKey) { - opKey = DS.__encOpKey(moduleId, operatorId); - if (opKey._getOperatorManager() == address(0)) { - revert OperatorNotRegistered(); - } - } - - function _getOpKeyByManager(address manager) internal view returns (uint256 opKey) { - opKey = DS._getManagerOpKey(manager); - if (opKey == 0) { - revert ManagerNotRegistered(); - } - } - function _checkKeysRangeIsValid(uint64 totalKeys, uint64 startIndex, uint64 endIndex) internal pure { if (startIndex > endIndex) { revert KeyIndexMismatch(); @@ -474,19 +424,19 @@ contract CredibleCommitmentCurationProvider is } } - function _calcOptInOutFlags(OperatorOptInOutState memory optInOutState) + function _calcOptInOutFlags(OptInOutState memory optInOutState) internal view returns (OperatorOptInOutFlags memory flags) { uint64 blockNumber = uint64(block.number); - Config memory cfg = DS._getConfig(); + (uint64 optInMinDurationBlocks, uint64 optOutDelayDurationBlocks,,) = _getConfig(); bool isOptedOut = - optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + cfg.optOutDelayDurationBlocks < blockNumber; + optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + optOutDelayDurationBlocks < blockNumber; bool isOptedIn = optInOutState.optInBlock > 0 && !isOptedOut; bool optInAllowed = !isOptedIn && !optInOutState.isOptOutForced; - bool optOutAllowed = isOptedIn && optInOutState.optInBlock + cfg.optInMinDurationBlocks < blockNumber; + bool optOutAllowed = isOptedIn && optInOutState.optInBlock + optInMinDurationBlocks < blockNumber; return OperatorOptInOutFlags({ isOptedIn: isOptedIn, @@ -504,11 +454,6 @@ contract CredibleCommitmentCurationProvider is } /// @notice Prepare the cache for the staking module and node operator - function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint256 opKey) internal view { - (uint24 moduleId, uint64 operatorId) = opKey.__decOpKey(); - _loadLidoNodeOperator(_c, moduleId, operatorId); - } - function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint24 moduleId, uint64 operatorId) internal view { _loadLidoModuleData(_c, moduleId); _loadLidoNodeOperatorData(_c, operatorId); diff --git a/src/interfaces/ICCCP.sol b/src/interfaces/ICCCP.sol new file mode 100644 index 0000000..eed3dc9 --- /dev/null +++ b/src/interfaces/ICCCP.sol @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ICCCPOperatorStatesStorage} from "./ICCCPOperatorStatesStorage.sol"; + +/** + * @title ICCCP + * @notice Interface for CredibleCommitmentCurationProvider. + */ +interface ICCCP is ICCCPOperatorStatesStorage { + function optIn( + uint24 moduleId, + uint64 operatorId, + address manager, + uint64 newKeyIndexRangeStart, + uint64 newKeyIndexRangeEnd, + string calldata rpcURL + ) external; + function optOut() external; + function updateKeysRange(uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd) external; + function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external; + + function getOperator(address manager) + external + view + returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state); + + function getOperator(uint24 _moduleId, uint64 _operatorId) + external + view + returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state); +} diff --git a/src/interfaces/ICCCPConfigStorage.sol b/src/interfaces/ICCCPConfigStorage.sol new file mode 100644 index 0000000..02d1282 --- /dev/null +++ b/src/interfaces/ICCCPConfigStorage.sol @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +/** + * @title ICCCPConfigStorage + * @notice Interface for interacting with the storage and control config params. + */ +interface ICCCPConfigStorage { + struct ModuleConfig { + // hopefully, we won't need more than 2^64 validators + /// @dev zero value means use default config + uint64 maxValidators; + // is module disabled for pre-confs + /// @dev operators in disabled modules are automatically considered as opted-out + bool isDisabled; + } + + struct Config { + // minimum duration of the opt-in period in blocks + uint64 optInMinDurationBlocks; + // delay in blocks before the operator can opt-in again after opt-out + uint64 optOutDelayDurationBlocks; + uint64 defaultOperatorMaxValidators; //todo rename to per op + uint64 defaultBlockGasLimit; + } + + struct ConfigStorage { + // module configs + mapping(uint256 => ModuleConfig) _modules; + // config + Config _config; + } + + error ZeroDefaultOperatorMaxValidators(); + error ZeroDefaultBlockGasLimit(); +} diff --git a/src/interfaces/ICCCPOperatorStatesStorage.sol b/src/interfaces/ICCCPOperatorStatesStorage.sol new file mode 100644 index 0000000..9ef787d --- /dev/null +++ b/src/interfaces/ICCCPOperatorStatesStorage.sol @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +/** + * @title IOperatorStatesStorage + * @notice Interface for interacting with the storage and control states of operators. + */ +interface ICCCPOperatorStatesStorage { + /// @notice operator optin/optout state + /// operator can be in several statuses: + // 1. new, not registered: optInBlock = 0, optOutBlock = 0 + // 2. registered: optInBlock > 0, optOutBlock = 0 + // 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayDurationBlocksDelay >= block.r + // 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay >= block.r + // 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = false, optOutBlock + optOutDelayDurationBlocksDelay < block.r + // 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay < block.r + + // If isOptOutForced is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. + // If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isOptOutForced flag). + struct OptInOutState { + uint64 optInBlock; + uint64 optOutBlock; + bool isOptOutForced; // if the operator is forced to opt out by the committee + } + + struct KeysRange { + uint64 indexStart; + uint64 indexEnd; + } + + struct ExtraData { + string rpcURL; + } + + struct OperatorState { + address manager; + KeysRange keysRange; + OptInOutState optInOutState; + ExtraData extraData; + } + + /** + * @notice Storage structure for operator states data. + * @dev + * @param _operators Mapping opKey (module id + operator id) to operator state + * @param _managers Mapping manager address to opKey + */ + struct OperatorsStatesStorage { + mapping(uint256 => OperatorState) _operators; + mapping(address => uint256) _managers; + } + + + error ManagerBelongsToOtherOperator(); + error OperatorNotRegistered(); + error ManagerNotRegistered(); +} diff --git a/src/lib/CCCPConfigStorage.sol b/src/lib/CCCPConfigStorage.sol new file mode 100644 index 0000000..e6549e2 --- /dev/null +++ b/src/lib/CCCPConfigStorage.sol @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ICCCPConfigStorage} from "../interfaces/ICCCPConfigStorage.sol"; + +abstract contract CCCPConfigStorage is ICCCPConfigStorage { + bytes32 private immutable STORAGE_SLOT_REF; + + constructor() { + STORAGE_SLOT_REF = keccak256( + abi.encode(uint256(keccak256(abi.encodePacked("lido.cccp.storage.ConfigStorage"))) - 1) + ) & ~bytes32(uint256(0xff)); + } + + function _setConfig( + uint64 optInMinDurationBlocks, + uint64 optOutDelayDurationBlocks, + uint64 defaultOperatorMaxValidators, + uint64 defaultBlockGasLimit + ) internal { + if (defaultOperatorMaxValidators == 0) { + revert ZeroDefaultOperatorMaxValidators(); + } + if (defaultBlockGasLimit == 0) { + revert ZeroDefaultBlockGasLimit(); + } + _getConfigStorage()._config = Config({ + optInMinDurationBlocks: optInMinDurationBlocks, + optOutDelayDurationBlocks: optOutDelayDurationBlocks, + defaultOperatorMaxValidators: defaultOperatorMaxValidators, + defaultBlockGasLimit: defaultBlockGasLimit + }); + } + + function _setModuleConfig(uint24 moduleId, uint64 maxValidators, bool isDisabled) internal { + _getConfigStorage()._modules[moduleId] = ModuleConfig({maxValidators: maxValidators, isDisabled: isDisabled}); + } + + function _getConfig() + internal + view + returns ( + uint64 optInMinDurationBlocks, + uint64 optOutDelayDurationBlocks, + uint64 defaultOperatorMaxValidators, + uint64 defaultBlockGasLimit + ) + { + Config memory config = _getConfigStorage()._config; + return ( + config.optInMinDurationBlocks, + config.optOutDelayDurationBlocks, + config.defaultOperatorMaxValidators, + config.defaultBlockGasLimit + ); + } + + function _getModuleConfig(uint24 moduleId) internal view returns (uint64 maxValidators, bool isDisabled) { + ModuleConfig memory moduleConfig = _getConfigStorage()._modules[moduleId]; + return (moduleConfig.maxValidators, moduleConfig.isDisabled); + } + + /** + * @notice Accesses the storage slot for the config's data. + * @return $ A reference to the `ConfigStorage` struct. + * + * @dev This function uses inline assembly to access a predefined storage slot. + */ + function _getConfigStorage() private view returns (ConfigStorage storage $) { + bytes32 slot = STORAGE_SLOT_REF; + assembly { + $.slot := slot + } + } +} diff --git a/src/lib/CCCPDataStorage.sol b/src/lib/CCCPDataStorage.sol deleted file mode 100644 index b505f2b..0000000 --- a/src/lib/CCCPDataStorage.sol +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -/// @notice operator optin/optout state -/// operator can be in several statuses: -// 1. new, not registered: optInBlock = 0, optOutBlock = 0 -// 2. registered: optInBlock > 0, optOutBlock = 0 -// 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayDurationBlocksDelay >= block.r -// 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay >= block.r -// 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = false, optOutBlock + optOutDelayDurationBlocksDelay < block.r -// 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay < block.r - -// If isOptOutForced is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. -// If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isOptOutForced flag). -struct OperatorOptInOutState { - uint64 optInBlock; - uint64 optOutBlock; - bool isOptOutForced; // if the operator is forced to opt out by the committee -} - -struct OperatorKeysRangeState { - uint64 indexStart; - uint64 indexEnd; -} - -struct OperatorExtraData { - string rpcURL; -} - -struct OperatorState { - address manager; - OperatorKeysRangeState keysRangeState; - OperatorOptInOutState optInOutState; - OperatorExtraData extraData; -} - -struct ModuleState { - // is module disabled for pre-confs - bool isDisabled; - // hopefully, we won't need more than 2^64 validators - uint64 maxValidators; -} - -struct Config { - // minimum duration of the opt-in period in blocks - uint64 optInMinDurationBlocks; - // delay in blocks before the operator can opt-in again after opt-out - uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; //todo rename to per op - uint64 defaultBlockGasLimit; -} - -library CCCPDataStorage { - struct CCCPData { - // opKey (module id + operator id) => operator state - mapping(uint256 => OperatorState) _operators; - // manager address to opKey - mapping(address => uint256) _managers; - // modules state - mapping(uint256 => ModuleState) _modules; - // config - Config _config; - } - - // keccak256(abi.encode(uint256(keccak256("lido.cccp.CCCPData")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 internal constant CCCP_DATA_LOCATION = 0x250c379b4df7db4aa0cebfe63c44e477918a4a35c66c19b68448ebd5517bd100; - - error ManagerBelongsToOtherOperator(); - - function _getStorage() private pure returns (CCCPData storage $) { - assembly { - $.slot := CCCP_DATA_LOCATION - } - } - - function _getOperatorStateStorage(uint256 opKey) private view returns (OperatorState storage) { - return _getStorage()._operators[opKey]; - } - - /// @notice get operator' full state - function _getOperatorState(uint256 opKey) internal view returns (OperatorState memory) { - return _getOperatorStateStorage(opKey); - } - - /// @notice get operator's opt-in/opt-out state - function _getOperatorOptInOutState(uint256 opKey) internal view returns (OperatorOptInOutState memory) { - return _getOperatorStateStorage(opKey).optInOutState; - } - - function _setOperatorOptInOutState(uint256 opKey, OperatorOptInOutState memory state) internal { - _getOperatorStateStorage(opKey).optInOutState = state; - } - - /// @notice get operator's keys range state - function _getOperatorKeysRangeState(uint256 opKey) internal view returns (OperatorKeysRangeState memory) { - return _getOperatorStateStorage(opKey).keysRangeState; - } - - function _setOperatorKeysRangeState(uint256 opKey, OperatorKeysRangeState memory state) internal { - _getOperatorStateStorage(opKey).keysRangeState = state; - } - - /// @notice get operator's extra data - function _getOperatorExtraData(uint256 opKey) internal view returns (OperatorExtraData memory) { - return _getOperatorStateStorage(opKey).extraData; - } - - function _setOperatorExtraData(uint256 opKey, OperatorExtraData memory data) internal { - _getOperatorStateStorage(opKey).extraData = data; - } - - /// @notice get manager address linked to the operator's reward address - function _getOperatorManager(uint256 opKey) internal view returns (address managerAddress) { - return _getOperatorStateStorage(opKey).manager; - } - - /// @dev safe manager address update - function _setOperatorManager(uint256 opKey, address manager) internal { - _checkManager(opKey, manager); - OperatorState storage $ = _getOperatorStateStorage(opKey); - - address oldManager = $.manager; - if (oldManager != address(0) && oldManager != manager) { - delete _getStorage()._managers[oldManager]; - } - $.manager = manager; - _getStorage()._managers[manager] = opKey; - } - - function _getManagerOpKey(address manager) internal view returns (uint256) { - return _getStorage()._managers[manager]; - } - - function _checkManager(uint256 opKey, address manager) internal view { - uint256 managerOpKey = _getManagerOpKey(manager); - // revert if the manager address is linked to the other operator - if (managerOpKey != 0 && managerOpKey != opKey) { - revert ManagerBelongsToOtherOperator(); - } - } - - function __encOpKey(uint24 moduleId, uint64 operatorId) internal pure returns (uint256) { - return uint256(moduleId) << 64 | operatorId; - } - - function __decOpKey(uint256 opKey) internal pure returns (uint24 moduleId, uint64 operatorId) { - return (uint24(opKey >> 64), uint64(opKey)); - } - - /// MODULES DATA - - function _getModuleState(uint24 moduleId) internal view returns (ModuleState memory) { - return _getStorage()._modules[moduleId]; - } - - function _setModuleState(uint24 moduleId, ModuleState memory state) internal { - _getStorage()._modules[moduleId] = state; - } - - /// CONFIG DATA - - function _getConfig() internal view returns (Config memory) { - return _getStorage()._config; - } - - function _setConfig(Config memory config) internal { - _getStorage()._config = config; - } -} diff --git a/src/lib/CCCPOperatorStatesStorage.sol b/src/lib/CCCPOperatorStatesStorage.sol new file mode 100644 index 0000000..1159b5e --- /dev/null +++ b/src/lib/CCCPOperatorStatesStorage.sol @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {ICCCPOperatorStatesStorage} from "../interfaces/ICCCPOperatorStatesStorage.sol"; + +abstract contract CCCPOperatorStatesStorage is ICCCPOperatorStatesStorage, Initializable { + bytes32 private immutable STORAGE_SLOT_REF; + + constructor() { + STORAGE_SLOT_REF = keccak256( + abi.encode(uint256(keccak256(abi.encodePacked("lido.cccp.storage.OperatorStatesStorage"))) - 1) + ) & ~bytes32(uint256(0xff)); + } + + function __initializeOperatorStatesStorage() internal onlyInitializing {} + + function _setOperatorOptInOutState(uint256 opKey, OptInOutState memory state) internal { + _getOperatorStateStorage(opKey).optInOutState = state; + } + + function _setOperatorKeysRange(uint256 opKey, uint64 indexStart, uint64 indexEnd) internal { + _getOperatorStateStorage(opKey).keysRange = KeysRange(indexStart, indexEnd); + } + + function _setOperatorExtraData(uint256 opKey, ExtraData memory data) internal { + _getOperatorStateStorage(opKey).extraData = data; + } + + /// @dev safe manager's address update + function _setOperatorManager(uint256 opKey, address manager) internal { + // _checkManagerFree(opKey, manager); + uint256 managerOpKey = _getManagerOpKey(manager); + // revert if the manager address is linked to the other operator + if (managerOpKey != 0 && managerOpKey != opKey) { + revert ManagerBelongsToOtherOperator(); + } + + OperatorState storage $ = _getOperatorStateStorage(opKey); + address oldManager = $.manager; + if (oldManager != address(0) && oldManager != manager) { + delete _getOperatorsStatesStorage()._managers[oldManager]; + } + $.manager = manager; + _getOperatorsStatesStorage()._managers[manager] = opKey; + } + + /// @notice get operator' full state + function _getOperatorState(uint256 opKey) internal view returns (OperatorState memory) { + return _getOperatorStateStorage(opKey); + } + + /// @notice get operator's opt-in/opt-out state + function _getOperatorOptInOutState(uint256 opKey) internal view returns (OptInOutState memory) { + return _getOperatorStateStorage(opKey).optInOutState; + } + + /// @notice get operator's keys range state + function _getOperatorKeysRange(uint256 opKey) internal view returns (KeysRange memory) { + return _getOperatorStateStorage(opKey).keysRange; + } + + /// @notice get operator's extra data + function _getOperatorExtraData(uint256 opKey) internal view returns (ExtraData memory) { + return _getOperatorStateStorage(opKey).extraData; + } + + /// @notice get manager address linked to the operator's reward address + function _getOperatorManager(uint256 opKey) internal view returns (address managerAddress) { + return _getOperatorStateStorage(opKey).manager; + } + + function _getManagerOpKey(address manager) internal view returns (uint256) { + return _getOperatorsStatesStorage()._managers[manager]; + } + + /// @dev reverts if the operator not registered + function _getOpKeyById(uint24 moduleId, uint64 operatorId) internal view returns (uint256 opKey) { + opKey = __encOpKey(moduleId, operatorId); + if (_getOperatorManager(opKey) == address(0)) { + revert OperatorNotRegistered(); + } + } + + function _getOpKeyByManager(address manager) internal view returns (uint256 opKey) { + opKey = _getManagerOpKey(manager); + if (opKey == 0) { + revert ManagerNotRegistered(); + } + } + + function __encOpKey(uint24 moduleId, uint64 operatorId) internal pure returns (uint256) { + return (uint256(moduleId) << 64) | operatorId; + } + + function __decOpKey(uint256 opKey) internal pure returns (uint24 moduleId, uint64 operatorId) { + return (uint24(opKey >> 64), uint64(opKey)); + } + + /** + * @notice Accesses the storage slot for the OperatorState's data. + * @return $ A reference to the `OperatorsStatesStorage` struct. + * + * @dev This function uses inline assembly to access a predefined storage slot. + */ + function _getOperatorsStatesStorage() private view returns (OperatorsStatesStorage storage $) { + bytes32 slot = STORAGE_SLOT_REF; + assembly { + $.slot := slot + } + } + + function _getOperatorStateStorage(uint256 opKey) private view returns (OperatorState storage) { + return _getOperatorsStatesStorage()._operators[opKey]; + } +} From 67e6e75062c97c1c2c4ec8639292fb62ecd2b79c Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 24 Jan 2025 00:06:58 +0100 Subject: [PATCH 16/33] test: fix tests --- script/DeployBase.s.sol | 11 ++--- src/interfaces/ICCCPOperatorStatesStorage.sol | 1 - test/CCCP.t.sol | 24 +++++---- test/DataStorage.t.sol | 49 +++++++++---------- test/fork/deployment/PostDeployment.t.sol | 2 +- test/helpers/Fixtures.sol | 6 +-- test/helpers/mocks/CCCPMock.sol | 8 ++- 7 files changed, 45 insertions(+), 56 deletions(-) diff --git a/script/DeployBase.s.sol b/script/DeployBase.s.sol index e383dcf..cee4679 100644 --- a/script/DeployBase.s.sol +++ b/script/DeployBase.s.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.28; import {Script} from "forge-std/Script.sol"; import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; -import {CredibleCommitmentCurationProvider} from "../src/CredibleCommitmentCurationProvider.sol"; +import {CCCP} from "../src/CCCP.sol"; import {JsonObj, Json} from "./utils/Json.sol"; @@ -28,7 +28,7 @@ abstract contract DeployBase is Script { address internal deployer; uint256 internal pk; - CredibleCommitmentCurationProvider public cccp; + CCCP public cccp; error ChainIdMismatch(uint256 actual, uint256 expected); @@ -51,15 +51,14 @@ abstract contract DeployBase is Script { vm.startBroadcast(pk); { - address cccpImpl = - address(new CredibleCommitmentCurationProvider(config.lidoLocatorAddress, config.csModuleType)); + address cccpImpl = address(new CCCP(config.lidoLocatorAddress, config.csModuleType)); - cccp = CredibleCommitmentCurationProvider( + cccp = CCCP( _deployProxy( config.proxyAdmin, address(cccpImpl), abi.encodeCall( - CredibleCommitmentCurationProvider.initialize, + CCCP.initialize, ( config.committeeAddress, config.optInMinDurationBlocks, diff --git a/src/interfaces/ICCCPOperatorStatesStorage.sol b/src/interfaces/ICCCPOperatorStatesStorage.sol index 9ef787d..38f2b2a 100644 --- a/src/interfaces/ICCCPOperatorStatesStorage.sol +++ b/src/interfaces/ICCCPOperatorStatesStorage.sol @@ -52,7 +52,6 @@ interface ICCCPOperatorStatesStorage { mapping(address => uint256) _managers; } - error ManagerBelongsToOtherOperator(); error OperatorNotRegistered(); error ManagerNotRegistered(); diff --git a/test/CCCP.t.sol b/test/CCCP.t.sol index a7016fa..ccd5dc0 100644 --- a/test/CCCP.t.sol +++ b/test/CCCP.t.sol @@ -2,18 +2,16 @@ pragma solidity 0.8.28; import {Test, console} from "forge-std/Test.sol"; -import {CCCPDataStorage as DS, ModuleState, Config} from "../src/lib/CCCPDataStorage.sol"; -import { - CredibleCommitmentCurationProvider as CCCP, OperatorState -} from "../src/CredibleCommitmentCurationProvider.sol"; +import {CCCPConfigStorage, ICCCPConfigStorage} from "../src/lib/CCCPConfigStorage.sol"; +import {CCCPOperatorStatesStorage, ICCCPOperatorStatesStorage} from "../src/lib/CCCPOperatorStatesStorage.sol"; +import {CCCP} from "../src/CCCP.sol"; import {CCCPMock} from "./helpers/mocks/CCCPMock.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -import "./helpers/Fixtures.sol"; -import "./helpers/Utilities.sol"; +import {Fixtures} from "./helpers/Fixtures.sol"; +import {Utilities} from "./helpers/Utilities.sol"; import {LidoLocatorMock} from "test/helpers/mocks/LidoLocatorMock.sol"; import {StakingModuleMock} from "test/helpers/mocks/StakingModuleMock.sol"; -import {StakingRouterMock} from "test/helpers/mocks/StakingRouterMock.sol"; +import {StakingRouterMock, IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; import {CuratedModuleMock} from "test/helpers/mocks/CuratedModuleMock.sol"; import {CSModuleMock} from "test/helpers/mocks/CSModuleMock.sol"; @@ -154,7 +152,7 @@ contract CCCPOptIn is CCCPCommon { // opt in on behalf of noCsm1 vm.broadcast(noCsm1); vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.KeyRangeUpdated(csmId, noCsm1Id, 2, 4); + emit CCCP.KeysRangeUpdated(csmId, noCsm1Id, 2, 4); vm.expectEmit(true, true, true, false, address(cccp)); emit CCCP.OptInSucceeded(csmId, noCsm1Id, noCsm1Manager); @@ -167,15 +165,15 @@ contract CCCPOptIn is CCCPCommon { rpcURL: "" }); - (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) = + (uint24 moduleId, uint64 operatorId, bool isEnabled, CCCP.OperatorState memory state) = cccp.getOperator(noCsm1Manager); assertEq(moduleId, csmId); assertEq(operatorId, noCsm1Id); assertEq(isEnabled, true); - assertEq(state.keysRangeState.indexStart, 2); - assertEq(state.keysRangeState.indexEnd, 4); + assertEq(state.keysRange.indexStart, 2); + assertEq(state.keysRange.indexEnd, 4); assertEq(state.manager, noCsm1Manager); assertEq(state.optInOutState.optInBlock, block.number); assertEq(state.optInOutState.optOutBlock, 0); @@ -307,7 +305,7 @@ contract CCCPOptIn is CCCPCommon { // optin with same manager vm.broadcast(noCsm1); - vm.expectRevert(DS.ManagerBelongsToOtherOperator.selector); + vm.expectRevert(ICCCPOperatorStatesStorage.ManagerBelongsToOtherOperator.selector); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, diff --git a/test/DataStorage.t.sol b/test/DataStorage.t.sol index f2489d0..161bedc 100644 --- a/test/DataStorage.t.sol +++ b/test/DataStorage.t.sol @@ -1,30 +1,21 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.28; -import {Test, console} from "forge-std/Test.sol"; -import {CCCPDataStorage as DS, ModuleState, Config} from "../src/lib/CCCPDataStorage.sol"; +import {Test} from "forge-std/Test.sol"; +import {CCCPConfigStorage} from "../src/lib/CCCPConfigStorage.sol"; -contract ModulesDataStorageTest is Test { - function setUp() public { - // counter.setNumber(0); - } - - function test_StorageLocationConstant() public pure { - bytes32 location = keccak256(abi.encode(uint256(keccak256("lido.cccp.CCCPData")) - 1)) & ~bytes32(uint256(0xff)); - - assertEq(DS.CCCP_DATA_LOCATION, location); - } +contract ModulesDataStorageTest is Test, CCCPConfigStorage { + function setUp() public {} - function test_ModuleState() public { + function test_ModuleConfig() public { uint24 moduleId = 111; uint64 maxValidators = 1111; - ModuleState memory state = ModuleState({isDisabled: true, maxValidators: maxValidators}); - DS._setModuleState(moduleId, state); - ModuleState memory newState = DS._getModuleState(moduleId); + _setModuleConfig(moduleId, maxValidators, true); + (uint64 newMaxValidators, bool newIsDisabled) = _getModuleConfig(moduleId); - assertEq(newState.maxValidators, maxValidators); - assertEq(newState.isDisabled, true); + assertEq(newMaxValidators, maxValidators); + assertEq(newIsDisabled, true); } function test_Config() public { @@ -32,16 +23,20 @@ contract ModulesDataStorageTest is Test { uint64 optOutDelayDurationBlocks = 234; uint64 defaultOperatorMaxValidators = 100; uint64 defaultBlockGasLimit = 1000000; - Config memory cfg = Config( + + _setConfig( optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit ); - - DS._setConfig(cfg); - Config memory newCfg = DS._getConfig(); - - assertEq(newCfg.optInMinDurationBlocks, optInMinDurationBlocks); - assertEq(newCfg.optOutDelayDurationBlocks, optOutDelayDurationBlocks); - assertEq(newCfg.defaultOperatorMaxValidators, defaultOperatorMaxValidators); - assertEq(newCfg.defaultBlockGasLimit, defaultBlockGasLimit); + ( + uint64 newOptInMinDurationBlocks, + uint64 newOptOutDelayDurationBlocks, + uint64 newDefaultOperatorMaxValidators, + uint64 newDefaultBlockGasLimit + ) = _getConfig(); + + assertEq(newOptInMinDurationBlocks, optInMinDurationBlocks); + assertEq(newOptOutDelayDurationBlocks, optOutDelayDurationBlocks); + assertEq(newDefaultOperatorMaxValidators, defaultOperatorMaxValidators); + assertEq(newDefaultBlockGasLimit, defaultBlockGasLimit); } } diff --git a/test/fork/deployment/PostDeployment.t.sol b/test/fork/deployment/PostDeployment.t.sol index fcef458..adbb8e8 100644 --- a/test/fork/deployment/PostDeployment.t.sol +++ b/test/fork/deployment/PostDeployment.t.sol @@ -8,7 +8,7 @@ import {Utilities} from "../../helpers/Utilities.sol"; import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; import {DeployParams} from "../../../script/DeployBase.s.sol"; import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; -import {CredibleCommitmentCurationProvider as CCCP} from "../../../src/CredibleCommitmentCurationProvider.sol"; +import {CCCP} from "../../../src/CCCP.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract CSModuleDeploymentTest is Test, Utilities, DeploymentFixtures { diff --git a/test/helpers/Fixtures.sol b/test/helpers/Fixtures.sol index 7c39bf9..986ff1a 100644 --- a/test/helpers/Fixtures.sol +++ b/test/helpers/Fixtures.sol @@ -6,7 +6,7 @@ import {StdCheats} from "forge-std/StdCheats.sol"; import {Test} from "forge-std/Test.sol"; import {DeployParams} from "../../script/DeployBase.s.sol"; -import {CredibleCommitmentCurationProvider} from "../../src/CredibleCommitmentCurationProvider.sol"; +import {CCCP} from "../../src/CCCP.sol"; import {ILidoLocator} from "../../src/interfaces/ILidoLocator.sol"; import {IStakingRouter} from "../../src/interfaces/IStakingRouter.sol"; @@ -57,7 +57,7 @@ contract DeploymentFixtures is StdCheats, Test { address lidoLocator; } - CredibleCommitmentCurationProvider public cccp; + CCCP public cccp; ILidoLocator public locator; IStakingRouter public stakingRouter; @@ -73,7 +73,7 @@ contract DeploymentFixtures is StdCheats, Test { DeploymentConfig memory deploymentConfig = parseDeploymentConfig(config); assertEq(deploymentConfig.chainId, block.chainid, "ChainId mismatch"); - cccp = CredibleCommitmentCurationProvider(deploymentConfig.cccp); + cccp = CCCP(deploymentConfig.cccp); locator = ILidoLocator(deploymentConfig.lidoLocator); stakingRouter = IStakingRouter(locator.stakingRouter()); } diff --git a/test/helpers/mocks/CCCPMock.sol b/test/helpers/mocks/CCCPMock.sol index 1787a43..0c4696f 100644 --- a/test/helpers/mocks/CCCPMock.sol +++ b/test/helpers/mocks/CCCPMock.sol @@ -3,12 +3,10 @@ pragma solidity 0.8.28; -import {CredibleCommitmentCurationProvider} from "../../../src/CredibleCommitmentCurationProvider.sol"; +import {CCCP} from "../../../src/CCCP.sol"; -contract CCCPMock is CredibleCommitmentCurationProvider { - constructor(address lidoLocator, bytes32 csModuleType) - CredibleCommitmentCurationProvider(lidoLocator, csModuleType) - {} +contract CCCPMock is CCCP { + constructor(address lidoLocator, bytes32 csModuleType) CCCP(lidoLocator, csModuleType) {} function __test__getCSModuleType() external view returns (bytes32) { return CS_MODULE_TYPE; From 4dc5cd2daaad9e19ac349548f92fdda6569613c7 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 24 Jan 2025 00:10:05 +0100 Subject: [PATCH 17/33] chore: code metrics tools --- .gitignore | 3 + .solhint.json | 4 +- .yarn/install-state.gz | Bin 12873 -> 139114 bytes foundry.toml | 2 +- package.json | 15 +- yarn.lock | 503 +++++++++++++++++++++++++++++++++++++---- 6 files changed, 475 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 1cb5839..12aa7a2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ lcov.info !.yarn/versions lib/* + +scc-report.* +scm-report.* diff --git a/.solhint.json b/.solhint.json index cbd8d7a..8e53812 100644 --- a/.solhint.json +++ b/.solhint.json @@ -6,13 +6,13 @@ "compiler-version": ["error", "0.8.28"], "no-inline-assembly": "off", "no-unused-import": "error", - "func-named-parameters": "error", + "func-named-parameters": ["warn", 5], "func-visibility": ["error", { "ignoreConstructors": true }], "reason-string": ["warn", { "maxLength": 64 }], "immutable-vars-naming": ["error", { "immutablesAsConstants": true }], "var-name-mixedcase": "warn", "func-name-mixedcase": "warn", - "foundry-test-functions": ["warn", ["setUp"]], + "foundry-test-functions": ["off", ["setUp"]], "no-global-import": "error", "ordering": "warn", "gas-calldata-parameters": "error", diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 90db52a8ac2a18ed1419ffb2e8059ea2963c2e16..d2fd5800217be7394cfb192f10179ae2e616c04f 100644 GIT binary patch literal 139114 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 NQ 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%0AIW)!>ljb}_~lI+Y_o5;Z^jtH@EVl` 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 M12;Y)KT?4P0L&HDqyPW_ literal 12873 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 diff --git a/foundry.toml b/foundry.toml index fc0cdb2..c11bd55 100644 --- a/foundry.toml +++ b/foundry.toml @@ -18,7 +18,7 @@ block_gas_limit = 30_000_000 fuzz = { runs = 256 } gas_reports = [ - "CredibleCommitmentCurationProvider", + "CCCP", "CCCPDataStorage", "OssifiableProxy" ] diff --git a/package.json b/package.json index ff4acc5..856ef9d 100644 --- a/package.json +++ b/package.json @@ -8,17 +8,20 @@ "private": true, "scripts": { "lint:solhint": "solhint './src/**/*.sol'", - "lint:check": "prettier --check **.sol && yarn lint:solhint", - "lint:fix": "prettier --write **.sol", - "generate:diffyscan": "node script/generateDiffyscanContracts.js" + "lint:check": "forge fmt --check && yarn lint:solhint", + "lint:fix": "forge fmt", + "generate:diffyscan": "node script/generateDiffyscanContracts.js", + "scc:report": "scc src --sort names --no-cocomo --exclude-dir interfaces --by-file --format wide > scc-report.txt", + "scm:report": "solidity-code-metrics $(tree -f -i -I '*interfaces*' | grep '^./src.*sol' | xargs ls -d 2>/dev/null) > scm-report.md" }, "devDependencies": { "husky": "^9.1.7", - "lint-staged": "^15.3.0", + "lint-staged": "^15.4.2", "prettier": "^3.4.2", "prettier-plugin-solidity": "^1.4.2", - "solhint": "5.0.4", - "solhint-plugin-lido-csm": "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3" + "solhint": "5.0.5", + "solhint-plugin-lido-csm": "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3", + "solidity-code-metrics": "^0.0.28" }, "lint-staged": { "*": "prettier --ignore-unknown --write", diff --git a/yarn.lock b/yarn.lock index d14b954..d59a7ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,6 +23,27 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + "@pnpm/config.env-replace@npm:^1.1.0": version: 1.1.0 resolution: "@pnpm/config.env-replace@npm:1.1.0" @@ -57,6 +78,15 @@ __metadata: languageName: node linkType: hard +"@solidity-parser/parser@npm:^0.16.1": + version: 0.16.2 + resolution: "@solidity-parser/parser@npm:0.16.2" + dependencies: + antlr4ts: "npm:^0.5.0-alpha.4" + checksum: 10c0/f0612b36f9a25def75188b44ce06d7cb286b4f843c54b3f0e8836bdd48438663aafea7839890d54f9ccdbc6fa2c1e1247cae2ab734713463e21e4bd656e526a7 + languageName: node + linkType: hard + "@solidity-parser/parser@npm:^0.18.0": version: 0.18.0 resolution: "@solidity-parser/parser@npm:0.18.0" @@ -143,7 +173,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.2.1": +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c @@ -157,6 +187,15 @@ __metadata: languageName: node linkType: hard +"antlr4ts@npm:^0.5.0-alpha.4": + version: 0.5.0-dev + resolution: "antlr4ts@npm:0.5.0-dev" + dependencies: + source-map-support: "npm:^0.5.16" + checksum: 10c0/948d95d02497a5751105cc61e9931d03a9bf0566b33a28ea8f2c72484a47ec4c5148670e1a525bfbc0069b1b86ab820417ec3fad120081211ff55f542fb4a835 + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -178,6 +217,13 @@ __metadata: languageName: node linkType: hard +"async@npm:^3.2.4": + version: 3.2.6 + resolution: "async@npm:3.2.6" + checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70 + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -203,6 +249,20 @@ __metadata: languageName: node linkType: hard +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"c3-linearization@npm:^0.3.0": + version: 0.3.0 + resolution: "c3-linearization@npm:0.3.0" + checksum: 10c0/7d07af10d6cc861e75f07fb55b7db3e97efc4b3e27921d047bb18a96dc18a147a78d6ab6e4195b6d25cab457d45158914942e7d5dc8d7cb150d13b16672adb03 + languageName: node + linkType: hard + "cacheable-lookup@npm:^7.0.0": version: 7.0.0 resolution: "cacheable-lookup@npm:7.0.0" @@ -242,7 +302,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:~5.4.1": +"chalk@npm:^5.4.1": version: 5.4.1 resolution: "chalk@npm:5.4.1" checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef @@ -258,6 +318,15 @@ __metadata: languageName: node linkType: hard +"cli-table@npm:^0.3.11": + version: 0.3.11 + resolution: "cli-table@npm:0.3.11" + dependencies: + colors: "npm:1.0.3" + checksum: 10c0/6e31da4e19e942bf01749ff78d7988b01e0101955ce2b1e413eecdc115d4bb9271396464761491256a7d3feeedb5f37ae505f4314c4f8044b5d0f4b579c18f29 + languageName: node + linkType: hard + "cli-truncate@npm:^4.0.0": version: 4.0.0 resolution: "cli-truncate@npm:4.0.0" @@ -268,6 +337,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -291,6 +371,20 @@ __metadata: languageName: node linkType: hard +"colors@npm:1.0.3": + version: 1.0.3 + resolution: "colors@npm:1.0.3" + checksum: 10c0/f9e40dd8b3e1a65378a7ced3fced15ddfd60aaf38e99a7521a7fdb25056b15e092f651cd0f5aa1e9b04fa8ce3616d094e07fc6c2bb261e24098db1ddd3d09a1d + languageName: node + linkType: hard + +"colors@npm:^1.4.0": + version: 1.4.0 + resolution: "colors@npm:1.4.0" + checksum: 10c0/9af357c019da3c5a098a301cf64e3799d27549d8f185d86f79af23069e4f4303110d115da98483519331f6fb71c8568d5688fa1c6523600044fd4a54e97c4efb + languageName: node + linkType: hard + "commander@npm:^10.0.0": version: 10.0.1 resolution: "commander@npm:10.0.1" @@ -298,10 +392,17 @@ __metadata: languageName: node linkType: hard -"commander@npm:~12.1.0": - version: 12.1.0 - resolution: "commander@npm:12.1.0" - checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 +"commander@npm:^11.0.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: 10c0/13cc6ac875e48780250f723fb81c1c1178d35c5decb1abb1b628b3177af08a8554e76b2c0f29de72d69eef7c864d12613272a71fabef8047922bc622ab75a179 + languageName: node + linkType: hard + +"commander@npm:^13.1.0": + version: 13.1.0 + resolution: "commander@npm:13.1.0" + checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164 languageName: node linkType: hard @@ -337,15 +438,16 @@ __metadata: resolution: "credible-commitment-curation-provider@workspace:." dependencies: husky: "npm:^9.1.7" - lint-staged: "npm:^15.3.0" + lint-staged: "npm:^15.4.2" prettier: "npm:^3.4.2" prettier-plugin-solidity: "npm:^1.4.2" - solhint: "npm:5.0.4" + solhint: "npm:5.0.5" solhint-plugin-lido-csm: "https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3" + solidity-code-metrics: "npm:^0.0.28" languageName: unknown linkType: soft -"cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -356,7 +458,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:~4.4.0": +"debug@npm:^4.4.0": version: 4.4.0 resolution: "debug@npm:4.4.0" dependencies: @@ -391,6 +493,13 @@ __metadata: languageName: node linkType: hard +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + "emoji-regex@npm:^10.3.0": version: 10.4.0 resolution: "emoji-regex@npm:10.4.0" @@ -405,6 +514,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + "environment@npm:^1.0.0": version: 1.1.0 resolution: "environment@npm:1.1.0" @@ -421,6 +537,13 @@ __metadata: languageName: node linkType: hard +"escalade@npm:^3.1.1": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + "eventemitter3@npm:^5.0.1": version: 5.0.1 resolution: "eventemitter3@npm:5.0.1" @@ -428,7 +551,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:~8.0.1": +"execa@npm:^8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" dependencies: @@ -482,6 +605,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.3.0 + resolution: "foreground-child@npm:3.3.0" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10c0/028f1d41000553fcfa6c4bb5c372963bf3d9bf0b1f25a87d1a6253014343fb69dfb1b42d9625d7cf44c8ba429940f3d0ff718b62105d4d4a4f6ef8ca0a53faa2 + languageName: node + linkType: hard + "form-data-encoder@npm:^2.1.2": version: 2.1.4 resolution: "form-data-encoder@npm:2.1.4" @@ -496,6 +629,13 @@ __metadata: languageName: node linkType: hard +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + "get-east-asian-width@npm:^1.0.0": version: 1.3.0 resolution: "get-east-asian-width@npm:1.3.0" @@ -517,6 +657,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.3.15": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + languageName: node + linkType: hard + "glob@npm:^8.0.3": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -556,6 +712,15 @@ __metadata: languageName: node linkType: hard +"graphviz@npm:0.0.9": + version: 0.0.9 + resolution: "graphviz@npm:0.0.9" + dependencies: + temp: "npm:~0.4.0" + checksum: 10c0/d9e7ea3d74b00db43ae96fe465f988dccc2e62b48471bdfb134ea914f4ba552e5ec5817da350993e68b11452392d2fe9bfd79ad3d06ccaac6724301778c32870 + languageName: node + linkType: hard + "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -563,6 +728,16 @@ __metadata: languageName: node linkType: hard +"hasha@npm:^5.2.0": + version: 5.2.2 + resolution: "hasha@npm:5.2.2" + dependencies: + is-stream: "npm:^2.0.0" + type-fest: "npm:^0.8.0" + checksum: 10c0/9d10d4e665a37beea6e18ba3a0c0399a05b26e505c5ff2fe9115b64fedb3ca95f68c89cf15b08ee4d09fd3064b5e1bfc8e8247353c7aa6b7388471d0f86dca74 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -674,6 +849,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + "is-stream@npm:^3.0.0": version: 3.0.0 resolution: "is-stream@npm:3.0.0" @@ -688,6 +870,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -752,7 +947,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:~3.1.3": +"lilconfig@npm:^3.1.3": version: 3.1.3 resolution: "lilconfig@npm:3.1.3" checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc @@ -766,27 +961,27 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:^15.3.0": - version: 15.3.0 - resolution: "lint-staged@npm:15.3.0" +"lint-staged@npm:^15.4.2": + version: 15.4.2 + resolution: "lint-staged@npm:15.4.2" dependencies: - chalk: "npm:~5.4.1" - commander: "npm:~12.1.0" - debug: "npm:~4.4.0" - execa: "npm:~8.0.1" - lilconfig: "npm:~3.1.3" - listr2: "npm:~8.2.5" - micromatch: "npm:~4.0.8" - pidtree: "npm:~0.6.0" - string-argv: "npm:~0.3.2" - yaml: "npm:~2.6.1" + chalk: "npm:^5.4.1" + commander: "npm:^13.1.0" + debug: "npm:^4.4.0" + execa: "npm:^8.0.1" + lilconfig: "npm:^3.1.3" + listr2: "npm:^8.2.5" + micromatch: "npm:^4.0.8" + pidtree: "npm:^0.6.0" + string-argv: "npm:^0.3.2" + yaml: "npm:^2.7.0" bin: lint-staged: bin/lint-staged.js - checksum: 10c0/1ddf9488c523c0b65c85b755428d4ad74fac3aa6ccb2e28e9bff5b8d86503158fe241d20d5433a11146872050b43580644901a5ef4c924b1ad7017c224a07339 + checksum: 10c0/08dd28149241788f7ca628a64c9c1817a9dfbe19517ba0317fdf96a1109f6d624948864edfeaf2936561bb49c65aeb32d5ddc75fb15afa2b6527024ef01a546b languageName: node linkType: hard -"listr2@npm:~8.2.5": +"listr2@npm:^8.2.5": version: 8.2.5 resolution: "listr2@npm:8.2.5" dependencies: @@ -834,6 +1029,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -841,7 +1043,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:~4.0.8": +"micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -888,6 +1090,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + "minimist@npm:^1.2.0": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -895,6 +1106,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + "ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" @@ -952,6 +1170,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + "package-json@npm:^8.1.0": version: 8.1.1 resolution: "package-json@npm:8.1.1" @@ -999,6 +1224,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -1013,14 +1248,14 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.3.1": +"picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be languageName: node linkType: hard -"pidtree@npm:~0.6.0": +"pidtree@npm:^0.6.0": version: 0.6.0 resolution: "pidtree@npm:0.6.0" bin: @@ -1101,6 +1336,15 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^3.3.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b + languageName: node + linkType: hard + "registry-auth-token@npm:^5.0.1": version: 5.0.3 resolution: "registry-auth-token@npm:5.0.3" @@ -1119,6 +1363,13 @@ __metadata: languageName: node linkType: hard +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + "require-from-string@npm:^2.0.2": version: 2.0.2 resolution: "require-from-string@npm:2.0.2" @@ -1175,6 +1426,15 @@ __metadata: languageName: node linkType: hard +"sha1-file@npm:^2.0.0": + version: 2.0.1 + resolution: "sha1-file@npm:2.0.1" + dependencies: + hasha: "npm:^5.2.0" + checksum: 10c0/aa8a3ae2f64360163190adc75eebd2bf22a3ad8223069af3aaa88b6b1832fb71ce18df57cd2c67e1f7653fdc3011661d2ca90974b29e0d9d7b2f1844a14aba51 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -1191,7 +1451,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.1.0": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -1229,6 +1489,20 @@ __metadata: languageName: node linkType: hard +"sloc@npm:^0.3.2": + version: 0.3.2 + resolution: "sloc@npm:0.3.2" + dependencies: + async: "npm:^3.2.4" + cli-table: "npm:^0.3.11" + commander: "npm:^11.0.0" + readdirp: "npm:^3.3.0" + bin: + sloc: bin/sloc + checksum: 10c0/6287e561407c668f73139c2931db35c605aa471d5dade6dac81fb5d1659aed43f8464c629497556768df0fd4f692a5a8322283c9bb42b078433de6b021f8998d + languageName: node + linkType: hard + "solhint-plugin-lido-csm@https://github.com/lidofinance/solhint-plugin-lido-csm.git#0.3.3": version: 0.3.3 resolution: "solhint-plugin-lido-csm@https://github.com/lidofinance/solhint-plugin-lido-csm.git#commit=db949adb893af939bfb1ca5ba191c26fd94aa69c" @@ -1270,9 +1544,9 @@ __metadata: languageName: node linkType: hard -"solhint@npm:5.0.4": - version: 5.0.4 - resolution: "solhint@npm:5.0.4" +"solhint@npm:5.0.5": + version: 5.0.5 + resolution: "solhint@npm:5.0.5" dependencies: "@solidity-parser/parser": "npm:^0.19.0" ajv: "npm:^6.12.6" @@ -1298,18 +1572,61 @@ __metadata: optional: true bin: solhint: solhint.js - checksum: 10c0/70058b23c8746762fc88d48b571c4571719913ca7f3c582a55c123ad9ba38976a2338782025fbb9643bb75bfad18bf3dce1b71e500df6d99589e9814fbcce1d7 + checksum: 10c0/becf018ff57f6b3579a7001179dcf941814bbdbc9fed8e4bb6502d35a8b5adc4fc42d0fa7f800e3003471768f9e17d2c458fb9f21c65c067160573f16ff12769 + languageName: node + linkType: hard + +"solidity-code-metrics@npm:^0.0.28": + version: 0.0.28 + resolution: "solidity-code-metrics@npm:0.0.28" + dependencies: + "@solidity-parser/parser": "npm:^0.18.0" + glob: "npm:^10.3.15" + sloc: "npm:^0.3.2" + solidity-doppelganger: "npm:^0.0.11" + surya: "npm:^0.4.12" + bin: + solidity-code-metrics: src/cli.js + checksum: 10c0/cbd0de8b37ee342cf943975ab8963859f6b13e0f1a1df8635e37882bc0f085d506cc35e20413684ce36acfbbb116b35be99afe7bb42e39391f880e885cf09f9d + languageName: node + linkType: hard + +"solidity-doppelganger@npm:^0.0.11": + version: 0.0.11 + resolution: "solidity-doppelganger@npm:0.0.11" + dependencies: + "@solidity-parser/parser": "npm:^0.16.1" + glob: "npm:^8.0.3" + yargs: "npm:^17.0.1" + checksum: 10c0/43bc2ce9a2c5ab39d7a09ddd72f8939fe1f24cd353833ebc2fdba77bcb2d374c7fffee1d8cff74a9665cbdfc331feadab9e518722913772c2163c1b37163c21a + languageName: node + linkType: hard + +"source-map-support@npm:^0.5.16": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 languageName: node linkType: hard -"string-argv@npm:~0.3.2": +"string-argv@npm:^0.3.2": version: 0.3.2 resolution: "string-argv@npm:0.3.2" checksum: 10c0/75c02a83759ad1722e040b86823909d9a2fc75d15dd71ec4b537c3560746e33b5f5a07f7332d1e3f88319909f82190843aa2f0a0d8c8d591ec08e93d5b8dec82 languageName: node linkType: hard -"string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -1320,6 +1637,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + "string-width@npm:^7.0.0": version: 7.2.0 resolution: "string-width@npm:7.2.0" @@ -1331,7 +1659,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: @@ -1340,7 +1668,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.1.0": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -1372,6 +1700,23 @@ __metadata: languageName: node linkType: hard +"surya@npm:^0.4.12": + version: 0.4.12 + resolution: "surya@npm:0.4.12" + dependencies: + "@solidity-parser/parser": "npm:^0.16.1" + c3-linearization: "npm:^0.3.0" + colors: "npm:^1.4.0" + graphviz: "npm:0.0.9" + sha1-file: "npm:^2.0.0" + treeify: "npm:^1.1.0" + yargs: "npm:^17.0.0" + bin: + surya: bin/surya + checksum: 10c0/ed90d4ba50390e33df65590c5e8f2bfcc444822202431baabe39abbac6486946770007d0aa4b2441e906ac1aa6fda26ac8efbab07bf4b83b1410587625641490 + languageName: node + linkType: hard + "table@npm:^6.8.1": version: 6.9.0 resolution: "table@npm:6.9.0" @@ -1385,6 +1730,13 @@ __metadata: languageName: node linkType: hard +"temp@npm:~0.4.0": + version: 0.4.0 + resolution: "temp@npm:0.4.0" + checksum: 10c0/cf25c604d7509c3a41d2893c9779d4130a127ac2a9bea23ec1a5ff7da0f82e0014b5dcd77a4ce7227e63bc667ad4bd5ec312441e01e39ca5dcee6e146fbdaefb + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -1401,6 +1753,20 @@ __metadata: languageName: node linkType: hard +"treeify@npm:^1.1.0": + version: 1.1.0 + resolution: "treeify@npm:1.1.0" + checksum: 10c0/2f0dea9e89328b8a42296a3963d341ab19897a05b723d6b0bced6b28701a340d2a7b03241aef807844198e46009aaf3755139274eb082cfce6fdc1935cbd69dd + languageName: node + linkType: hard + +"type-fest@npm:^0.8.0": + version: 0.8.1 + resolution: "type-fest@npm:0.8.1" + checksum: 10c0/dffbb99329da2aa840f506d376c863bd55f5636f4741ad6e65e82f5ce47e6914108f44f340a0b74009b0cb5d09d6752ae83203e53e98b1192cf80ecee5651636 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -1421,6 +1787,28 @@ __metadata: languageName: node linkType: hard +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + "wrap-ansi@npm:^9.0.0": version: 9.0.0 resolution: "wrap-ansi@npm:9.0.0" @@ -1439,11 +1827,40 @@ __metadata: languageName: node linkType: hard -"yaml@npm:~2.6.1": - version: 2.6.1 - resolution: "yaml@npm:2.6.1" +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + +"yaml@npm:^2.7.0": + version: 2.7.0 + resolution: "yaml@npm:2.7.0" bin: yaml: bin.mjs - checksum: 10c0/aebf07f61c72b38c74d2b60c3a3ccf89ee4da45bcd94b2bfb7899ba07a5257625a7c9f717c65a6fc511563d48001e01deb1d9e55f0133f3e2edf86039c8c1be7 + checksum: 10c0/886a7d2abbd70704b79f1d2d05fe9fb0aa63aefb86e1cb9991837dced65193d300f5554747a872b4b10ae9a12bc5d5327e4d04205f70336e863e35e89d8f4ea9 + languageName: node + linkType: hard + +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^17.0.0, yargs@npm:^17.0.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 languageName: node linkType: hard From 9942a44c8af89e767aeb483db7cea1068b8fdb8c Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 24 Jan 2025 00:10:40 +0100 Subject: [PATCH 18/33] chore: upd holesky deployed --- artifacts/holesky/deploy-holesky.json | 4 +- artifacts/holesky/transactions.json | 107 +++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json index 689e742..83b7286 100644 --- a/artifacts/holesky/deploy-holesky.json +++ b/artifacts/holesky/deploy-holesky.json @@ -1,7 +1,7 @@ { "CCCP": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", - "CCCPImpl": "0x73CCA4DD9E58Fa3aBCA22d55ee81AE613d958980", + "CCCPImpl": "0x7c178B9B797C6ea6776A784C22A0f95a79385c9b", "ChainId": 17000, "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" -} \ No newline at end of file +} diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json index 0749a62..3445f4a 100644 --- a/artifacts/holesky/transactions.json +++ b/artifacts/holesky/transactions.json @@ -42,6 +42,48 @@ }, "additionalContracts": [], "isFixedGasLimit": false + }, + { + "hash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", + "transactionType": "CREATE", + "contractName": "CCCP", + "contractAddress": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x386e1c", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b506040516134e03803806134e083398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516131d461030c5f395f611d0c01525f818161049801526123e801525f81816113b401528181611459015281816114b4015281816116d70152611ef501525f81816110670152818161188201528181611b42015261230f01526131d45ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c80638aa10435116100fe578063ba6bc8d51161009e578063d547741f1161006e578063d547741f1461046d578063d6dc6ae714610480578063dbba4b4814610493578063fc3372f5146104ba575f5ffd5b8063ba6bc8d514610404578063c3f909d414610417578063ca15c87314610452578063d4eec5a614610465575f5ffd5b8063a217fddf116100d9578063a217fddf146103b6578063a3246ad3146103bd578063a3714e07146103dd578063ad32563d146103f0575f5ffd5b80638aa10435146103585780639010d07c1461037857806391d14854146103a3575f5ffd5b8063389ed267116101745780635c975abb116101445780635c975abb1461031357806370bc09f61461032a5780638456cb591461033d578063893d004e14610345575f5ffd5b8063389ed267146102ae5780633c074eb3146102d55780633f4ba83a146102e85780635865c60c146102f0575f5ffd5b80632de03aa1116101af5780632de03aa11461024e5780632de0920c146102755780632f2ff15d1461028857806336568abe1461029b575f5ffd5b806301ffc9a7146101d557806313e09453146101fd578063248a9ca314610212575b5f5ffd5b6101e86101e3366004612646565b6104cd565b60405190151581526020015b60405180910390f35b61021061020b366004612695565b6104f7565b005b610240610220366004612702565b5f9081525f5160206131885f395f51905f52602052604090206001015490565b6040519081526020016101f4565b6102407f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610210610283366004612729565b6106ba565b6102106102963660046127f1565b61094a565b6102106102a93660046127f1565b610980565b6102407f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b6102106102e336600461281f565b6109b8565b610210610a43565b6103036102fe36600461284b565b610a78565b6040516101f4949392919061289e565b5f5160206131a85f395f51905f525460ff166101e8565b61021061033836600461295b565b610aa8565b610210610ad2565b6102106103533660046129b4565b610b04565b610360610bf2565b6040516001600160401b0390911681526020016101f4565b61038b6103863660046129fc565b610c29565b6040516001600160a01b0390911681526020016101f4565b6101e86103b13660046127f1565b610c56565b6102405f81565b6103d06103cb366004612702565b610c8c565b6040516101f49190612a1c565b6103036103eb36600461281f565b610cbc565b6102405f5160206131685f395f51905f5281565b610210610412366004612a67565b610cee565b61041f610dfe565b604080516001600160401b03958616815293851660208501529184169183019190915290911660608201526080016101f4565b610240610460366004612702565b610e18565b610210610e3c565b61021061047b3660046127f1565b610f17565b61021061048e36600461281f565b610f47565b61038b7f000000000000000000000000000000000000000000000000000000000000000081565b6102106104c8366004612a90565b611011565b5f6001600160e01b03198216635a05180f60e01b14806104f157506104f182611103565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561053b5750825b90505f826001600160401b031660011480156105565750303b155b905081158015610564575080155b156105825760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ac57845460ff60401b1916600160401b1785555b6001600160a01b038a166105d3576040516319467aa760e11b815260040160405180910390fd5b6105db611137565b6105e3611149565b6105ed5f8b611151565b506106055f5160206131685f395f51905f528b611151565b506106307f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b611151565b5061065b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b611151565b5061066889898989611193565b83156106ae57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106c26111f8565b6106ca61258c565b6001600160a01b0386166106f157604051634434240960e11b815260040160405180910390fd5b6106fc818989611228565b80606001516001600160a01b0316336001600160a01b031614610732576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161075457604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6107768261123c565b90505f610782826112a2565b8051909150156107a5576040516342ee68b560e01b815260040160405180910390fd5b80604001516107c7576040516328a5715760e21b815260040160405180910390fd5b6107d1838a6113a2565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3610857836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f15158152506114e0565b61086384848a8a611541565b604080516020601f8801819004810282018301835281018781526108a69286929182918b908b90819085018382808284375f9201919091525050509152506115bb565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef88886040516108e7929190612acd565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206131885f395f51905f526020526040902060010154610970816115da565b61097a8383611151565b50505050565b6001600160a01b03811633146109a95760405163334bd91960e11b815260040160405180910390fd5b6109b382826115e4565b505050565b5f5160206131685f395f51905f526109cf816115da565b5f6109da848461161d565b90505f6109e68261123c565b9050806040015115610a02575f6040820152610a0282826114e0565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610a6d816115da565b610a75611666565b50565b5f5f5f610a836125c0565b5f610a8d866116c5565b9050610a9881611725565b9450945094509450509193509193565b5f5160206131685f395f51905f52610abf816115da565b610acb85858585611193565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610afc816115da565b610a756117c7565b610b0c6111f8565b6001600160a01b038116610b3357604051634434240960e11b815260040160405180910390fd5b610b3b61258c565b610b46818585611228565b80606001516001600160a01b0316336001600160a01b031614610b7c576040516349ad9b0960e11b815260040160405180910390fd5b610b9d62ffffff60401b604086901b166001600160401b03851617836113a2565b6040516001600160a01b03831681526001600160401b0384169062ffffff8616907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a350505050565b5f610c247ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206131485f395f51905f52602081905260408220610c4e908461180f565b949350505050565b5f9182525f5160206131885f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f8181525f5160206131485f395f51905f526020819052604090912060609190610cb59061181a565b9392505050565b5f5f5f610cc76125c0565b5f610cd2878761161d565b9050610cdd81611725565b929a91995097509095509350505050565b610cf66111f8565b5f610d00336116c5565b90505f610d14610d0f8361123c565b6112a2565b8051909150610d3657604051631a8660cb60e01b815260040160405180910390fd5b5f610d4083611826565b9050805f01516001600160401b0316856001600160401b03161180610d7a575080602001516001600160401b0316846001600160401b0316105b80610db1575080516001600160401b038681169116148015610db1575080602001516001600160401b0316846001600160401b0316145b15610dcf5760405163c7f544bb60e01b815260040160405180910390fd5b610dd761258c565b604084901c84610de8838383611228565b610df483878a8a611541565b5050505050505050565b5f5f5f5f610e0a611874565b935093509350935090919293565b5f8181525f5160206131485f395f51905f52602081905260408220610cb5906118ea565b610e446111f8565b5f610e4e336116c5565b90505f610e5a8261123c565b90505f610e66826112a2565b8051909150610e8857604051631a8660cb60e01b815260040160405180910390fd5b8060600151610eaa57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b0343166020830152610ec383836114e0565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206131885f395f51905f526020526040902060010154610f3d816115da565b61097a83836115e4565b5f5160206131685f395f51905f52610f5e816115da565b5f610f69848461161d565b90505f610f758261123c565b90505f610f81826112a2565b8051909150610fa357604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b034316602083015260016040830152610fc383836114e0565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f5160206131685f395f51905f52611028816115da565b61103061258c565b61103a81866118f3565b6040805180820182526001600160401b038086168252861515602080840191825262ffffff8a165f9081527f0000000000000000000000000000000000000000000000000000000000000000909152939093209151825493511515600160401b0268ffffffffffffffffff19909416911617919091179055604080516001600160401b0385168152851515602082015262ffffff8716917f55bd0114bd34303292838dd2bc70c974c9eea7858dc4fb3716662d439ec3ae12910160405180910390a25050505050565b5f6001600160e01b03198216637965db0b60e01b14806104f157506301ffc9a760e01b6001600160e01b03198316146104f1565b61113f611989565b6111476119d2565b565b611147611989565b5f5f5160206131485f395f51905f528161116b85856119f2565b90508015610c4e575f85815260208390526040902061118a9085611a93565b50949350505050565b61119f84848484611aa7565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b5f5160206131a85f395f51905f525460ff16156111475760405163d93c066560e01b815260040160405180910390fd5b61123283836118f3565b6109b38382611be4565b604080516060810182525f808252602082018190529181019190915261126182611eef565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b604080516080810182525f808252602082018190529181018290526060810191909152435f806112d0611874565b5050915091505f5f86602001516001600160401b03161180156113135750836001600160401b03168287602001516113089190612b0f565b6001600160401b0316105b90505f5f875f01516001600160401b031611801561132f575081155b90505f8115801561134257508760400151155b90505f828015611370575088516001600160401b03881690611365908890612b0f565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906113eb5750828114155b156114095760405163324e8e5f60e01b815260040160405180910390fd5b5f61141384611eef565b80549091506001600160a01b031680158015906114425750836001600160a01b0316816001600160a01b031614155b15611482576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b806114ea83611eef565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b61155084608001518383611f1e565b835161155d908383611fa2565b611568838383612043565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c9101610be4565b806115c583611eef565b8151600391909101908190610acb9082612bbe565b610a7581336120b1565b5f5f5160206131485f395f51905f52816115fe85856120f2565b90508015610c4e575f85815260208390526040902061118a908561216b565b6001600160401b038116604083901b62ffffff60401b16175f61163f8261217f565b6001600160a01b0316036104f1576040516325ec6c1f60e01b815260040160405180910390fd5b61166e612199565b5f5160206131a85f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f0000000000000000000000000000000000000000000000000000000000000000016020526040812054908190036117205760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f6117306125c0565b61173861258c565b604086901c945085935061174d818686611228565b611756866121c8565b91505f61176683604001516112a2565b90505f611775835f0151612304565b915050815f01518015611786575080155b801561179357508260a001515b80156117ae57505f5160206131a85f395f51905f525460ff16155b9450825f01518360400151965096505050509193509193565b6117cf6111f8565b5f5160206131a85f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336116a7565b5f610cb58383612366565b60605f610cb58361238c565b604080518082019091525f808252602082015261184282611eef565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6104f1825490565b5f6118fc6123e5565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611944573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261196b9190810190612d8f565b62ffffff9092168352506020908101516001600160a01b0316910152565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661114757604051631afcd79f60e31b815260040160405180910390fd5b6119da611989565b5f5160206131a85f395f51905f52805460ff19169055565b5f5f5160206131885f395f51905f52611a0b8484610c56565b611a8a575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611a403390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506104f1565b5f9150506104f1565b5f610cb5836001600160a01b038416612466565b816001600160401b03165f03611ad05760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611af857604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b0316815250611b647f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f03611c0a57604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c4b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c6f9190612ec0565b9050806001600160401b0316826001600160401b031610611ca35760405163b9aa612760e01b815260040160405180910390fd5b5f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ce4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d089190612ec0565b90507f00000000000000000000000000000000000000000000000000000000000000008103611e46576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa158015611d81573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da59190612ee2565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015611dfa573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e1e9190612f10565b6101808101516001600160a01b031660608801525163ffffffff1660808701525061097a9050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015611e9b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611ec29190810190613039565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b03161115611f515760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b0316101580611f845750826001600160401b0316826001600160401b031610155b156109b357604051637ba485c560e01b815260040160405180910390fd5b5f611fab611874565b50925050505f5f611fbb86612304565b915091508015611fde57604051630dbfe5fd60e31b815260040160405180910390fd5b5f611fe986866130d2565b611ff4906001612b0f565b90505f6001600160401b0384161561200c578361200e565b845b9050806001600160401b0316826001600160401b03161115610df457604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061207384611eef565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6120bb8282610c56565b6120ee5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206131885f395f51905f5261210b8484610c56565b15611a8a575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506104f1565b5f610cb5836001600160a01b0384166124b2565b5f61218982611eef565b546001600160a01b031692915050565b5f5160206131a85f395f51905f525460ff1661114757604051638dfc202b60e01b815260040160405180910390fd5b6121d06125c0565b6121d982611eef565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061227a90612b42565b80601f01602080910402602001604051908101604052809291908181526020018280546122a690612b42565b80156122f15780601f106122c8576101008083540402835291602001916122f1565b820191905f5260205f20905b8154815290600101906020018083116122d457829003601f168201915b5050509190925250505090525092915050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251808401909352546001600160401b038116808452600160401b90910460ff1615159290910182905291565b5f825f01828154811061237b5761237b6130f1565b905f5260205f200154905092915050565b6060815f018054806020026020016040519081016040528092919081815260200182805480156123d957602002820191905f5260205f20905b8154815260200190600101908083116123c5575b50505050509050919050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612442573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c249190613105565b5f8181526001830160205260408120546124ab57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104f1565b505f6104f1565b5f8181526001830160205260408120548015611a8a575f6124d4600183613120565b85549091505f906124e790600190613120565b9050808214612546575f865f018281548110612505576125056130f1565b905f5260205f200154905080875f018481548110612525576125256130f1565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061255757612557613133565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104f1565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161260760405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f80825260208281018290529282015291019081526020016126416040518060200160405280606081525090565b905290565b5f60208284031215612656575f5ffd5b81356001600160e01b031981168114610cb5575f5ffd5b6001600160a01b0381168114610a75575f5ffd5b6001600160401b0381168114610a75575f5ffd5b5f5f5f5f5f60a086880312156126a9575f5ffd5b85356126b48161266d565b945060208601356126c481612681565b935060408601356126d481612681565b925060608601356126e481612681565b915060808601356126f481612681565b809150509295509295909350565b5f60208284031215612712575f5ffd5b5035919050565b62ffffff81168114610a75575f5ffd5b5f5f5f5f5f5f5f60c0888a03121561273f575f5ffd5b873561274a81612719565b9650602088013561275a81612681565b9550604088013561276a8161266d565b9450606088013561277a81612681565b9350608088013561278a81612681565b925060a08801356001600160401b038111156127a4575f5ffd5b8801601f81018a136127b4575f5ffd5b80356001600160401b038111156127c9575f5ffd5b8a60208284010111156127da575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612802575f5ffd5b8235915060208301356128148161266d565b809150509250929050565b5f5f60408385031215612830575f5ffd5b823561283b81612719565b9150602083013561281481612681565b5f6020828403121561285b575f5ffd5b8135610cb58161266d565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e0610140840152612950610160840182612866565b979650505050505050565b5f5f5f5f6080858703121561296e575f5ffd5b843561297981612681565b9350602085013561298981612681565b9250604085013561299981612681565b915060608501356129a981612681565b939692955090935050565b5f5f5f606084860312156129c6575f5ffd5b83356129d181612719565b925060208401356129e181612681565b915060408401356129f18161266d565b809150509250925092565b5f5f60408385031215612a0d575f5ffd5b50508035926020909101359150565b602080825282518282018190525f918401906040840190835b81811015612a5c5783516001600160a01b0316835260209384019390920191600101612a35565b509095945050505050565b5f5f60408385031215612a78575f5ffd5b823561283b81612681565b8015158114610a75575f5ffd5b5f5f5f60608486031215612aa2575f5ffd5b8335612aad81612719565b92506020840135612abd81612a83565b915060408401356129f181612681565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156104f1576104f1612afb565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612b5657607f821691505b602082108103612b7457634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156109b357805f5260205f20601f840160051c81016020851015612b9f5750805b601f840160051c820191505b81811015610acb575f8155600101612bab565b81516001600160401b03811115612bd757612bd7612b2e565b612beb81612be58454612b42565b84612b7a565b6020601f821160018114612c1d575f8315612c065750848201515b5f19600385901b1c1916600184901b178455610acb565b5f84815260208120601f198516915b82811015612c4c5787850151825560209485019460019092019101612c2c565b5084821015612c6957868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b0381118282101715612c9b57612c9b612b2e565b60405290565b6040516101e081016001600160401b0381118282101715612c9b57612c9b612b2e565b805161172081612719565b80516117208161266d565b805161ffff81168114611720575f5ffd5b805160ff81168114611720575f5ffd5b5f82601f830112612d0a575f5ffd5b81516001600160401b03811115612d2357612d23612b2e565b604051601f8201601f19908116603f011681016001600160401b0381118282101715612d5157612d51612b2e565b604052818152838201602001851015612d68575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b805161172081612681565b5f60208284031215612d9f575f5ffd5b81516001600160401b03811115612db4575f5ffd5b82016101a08185031215612dc6575f5ffd5b612dce612c78565b612dd782612cc4565b8152612de560208301612ccf565b6020820152612df660408301612cda565b6040820152612e0760608301612cda565b6060820152612e1860808301612cda565b6080820152612e2960a08301612ceb565b60a082015260c08201516001600160401b03811115612e46575f5ffd5b612e5286828501612cfb565b60c083015250612e6460e08301612d84565b60e082015261010082810151908201526101208083015190820152612e8c6101408301612cda565b610140820152612e9f6101608301612d84565b610160820152612eb26101808301612d84565b610180820152949350505050565b5f60208284031215612ed0575f5ffd5b5051919050565b805161172081612a83565b5f60208284031215612ef2575f5ffd5b8151610cb581612a83565b805163ffffffff81168114611720575f5ffd5b5f6101e0828403128015612f22575f5ffd5b50612f2b612ca1565b612f3483612efd565b8152612f4260208401612efd565b6020820152612f5360408401612efd565b6040820152612f6460608401612efd565b6060820152612f7560808401612efd565b6080820152612f8660a08401612efd565b60a0820152612f9760c08401612efd565b60c0820152612fa860e08401612ceb565b60e0820152612fba6101008401612efd565b610100820152612fcd6101208401612efd565b610120820152612fe06101408401612ccf565b610140820152612ff36101608401612ccf565b6101608201526130066101808401612ccf565b6101808201526130196101a08401612ccf565b6101a082015261302c6101c08401612ed7565b6101c08201529392505050565b5f5f5f5f5f5f60c0878903121561304e575f5ffd5b865161305981612a83565b60208801519096506001600160401b03811115613074575f5ffd5b61308089828a01612cfb565b95505060408701516130918161266d565b60608801519094506130a281612681565b60808801519093506130b381612681565b60a08801519092506130c481612681565b809150509295509295509295565b6001600160401b0382811682821603908111156104f1576104f1612afb565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613115575f5ffd5b8151610cb58161266d565b818103818111156104f1576104f1612afb565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x3f", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xde2a6f7ad4ba810948e3c1e417e3e6520a8828267a594d759ad12d0802fde233", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x7c178B9B797C6ea6776A784C22A0f95a79385c9b" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb0000000000000000000000007c178b9b797c6ea6776a784c22a0f95a79385c9b", + "nonce": "0x42", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false } ], "receipts": [ @@ -217,6 +259,69 @@ "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", "to": null, "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1af28aa", + "logs": [ + { + "address": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "blockTimestamp": "0x6792c828", + "transactionHash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", + "transactionIndex": "0x9", + "logIndex": "0x10d", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", + "transactionIndex": "0x9", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "gasUsed": "0x2b6865", + "effectiveGasPrice": "0xf424f", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1e030d3", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000007c178b9b797c6ea6776a784c22a0f95a79385c9b" + ], + "data": "0x", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "blockTimestamp": "0x6792c828", + "transactionHash": "0xde2a6f7ad4ba810948e3c1e417e3e6520a8828267a594d759ad12d0802fde233", + "transactionIndex": "0xd", + "logIndex": "0x114", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000020000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xde2a6f7ad4ba810948e3c1e417e3e6520a8828267a594d759ad12d0802fde233", + "transactionIndex": "0xd", + "blockHash": "0x3dfa399aa60c13bbfbe8f1cc9cd9cde07700ee525ec96fb788a6419a17c6ec28", + "blockNumber": "0x30ece5", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf424f", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null } ], "libraries": [], @@ -225,4 +330,4 @@ "timestamp": 1737475792, "chain": 17000, "commit": "ac812a4" -} \ No newline at end of file +} From ebacce09599c95c906021939b7c2462946862983 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 24 Jan 2025 00:24:21 +0100 Subject: [PATCH 19/33] fix: minor cleanup --- src/CCCP.sol | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index 6b2d4c7..60ad64c 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -134,10 +134,9 @@ contract CCCP is uint64 newKeyIndexRangeEnd, string calldata rpcURL ) external whenNotPaused { - LidoOperatorCache memory _c; - if (manager == address(0)) revert ZeroOperatorManagerAddress(); + LidoOperatorCache memory _c; /// @dev correctness of moduleId and operatorId are checked inside _loadLidoNodeOperator(_c, moduleId, operatorId); @@ -363,12 +362,6 @@ contract CCCP is uint24 moduleId, uint64 operatorId, bool isEnabled, - // uint64 optInBlock, - // uint64 optOutBlock, - // bool isOptOutForced, // if the operator is forced to opt out by the committee - // uint64 keyIndexRangeStart, - // uint64 keyIndexRangeEnd, - // string memory rpcURL OperatorState memory state ) { @@ -390,12 +383,6 @@ contract CCCP is _c.moduleId, _c.operatorId, isEnabled, - // state.optInOutState.optInBlock, - // state.optInOutState.optOutBlock, - // state.optInOutState.isOptOutForced, - // state.keysRange.indexStart, - // state.keysRange.indexEnd, - // state.extraData.rpcURL state ); } From bbb994c9ec3c96a528b8a8fafb793e8aa6d114f9 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Fri, 24 Jan 2025 00:45:03 +0100 Subject: [PATCH 20/33] style: fix formatting --- src/CCCP.sol | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index 60ad64c..ac760d5 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -358,12 +358,7 @@ contract CCCP is function _getOperator(uint256 opKey) internal view - returns ( - uint24 moduleId, - uint64 operatorId, - bool isEnabled, - OperatorState memory state - ) + returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) { LidoOperatorCache memory _c; (moduleId, operatorId) = __decOpKey(opKey); @@ -379,12 +374,7 @@ contract CCCP is // - if the contract is not paused isEnabled = flags.isOptedIn && !isDisabled && _c.isActive && !paused(); - return ( - _c.moduleId, - _c.operatorId, - isEnabled, - state - ); + return (_c.moduleId, _c.operatorId, isEnabled, state); } function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { From 47ffa9d0bee5315f09f0274a2a847699d5b5b2c7 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Mon, 27 Jan 2025 16:56:42 +0100 Subject: [PATCH 21/33] chore: fix comments --- src/CCCP.sol | 2 ++ src/interfaces/ICCCPConfigStorage.sol | 2 +- src/interfaces/ICCCPOperatorStatesStorage.sol | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index ac760d5..ffbb47b 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -112,6 +112,8 @@ contract CCCP is _updateConfig( optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit ); + + ///todo: pass module Ids to disable them at the start } /// @notice Resume all operations after a pause diff --git a/src/interfaces/ICCCPConfigStorage.sol b/src/interfaces/ICCCPConfigStorage.sol index 02d1282..445810d 100644 --- a/src/interfaces/ICCCPConfigStorage.sol +++ b/src/interfaces/ICCCPConfigStorage.sol @@ -22,7 +22,7 @@ interface ICCCPConfigStorage { uint64 optInMinDurationBlocks; // delay in blocks before the operator can opt-in again after opt-out uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; //todo rename to per op + uint64 defaultOperatorMaxValidators; uint64 defaultBlockGasLimit; } diff --git a/src/interfaces/ICCCPOperatorStatesStorage.sol b/src/interfaces/ICCCPOperatorStatesStorage.sol index 38f2b2a..60dca15 100644 --- a/src/interfaces/ICCCPOperatorStatesStorage.sol +++ b/src/interfaces/ICCCPOperatorStatesStorage.sol @@ -12,10 +12,10 @@ interface ICCCPOperatorStatesStorage { /// operator can be in several statuses: // 1. new, not registered: optInBlock = 0, optOutBlock = 0 // 2. registered: optInBlock > 0, optOutBlock = 0 - // 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayDurationBlocksDelay >= block.r - // 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay >= block.r - // 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = false, optOutBlock + optOutDelayDurationBlocksDelay < block.r - // 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay < block.r + // 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayDurationBlocksDelay >= block.number + // 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay >= block.number + // 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = false, optOutBlock + optOutDelayDurationBlocksDelay < block.number + // 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay < block.number // If isOptOutForced is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. // If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isOptOutForced flag). From 191aa23e3e0f0c6289ff2e722f4db8c3c7c284f7 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 28 Jan 2025 20:29:24 +0100 Subject: [PATCH 22/33] fix: deploy scripts refactor+upgrade script --- script/DeployBase.s.sol | 94 ----------------------- script/DeployBase.sol | 76 ++++++++++++++++++ script/DeployHolesky.s.sol | 18 ++--- script/DeployMainnet.s.sol | 18 ++--- script/ScriptInit.sol | 38 +++++++++ script/UpgradeBase.sol | 77 +++++++++++++++++++ script/UpgradeHolesky.s.sol | 26 +++++++ test/fork/deployment/PostDeployment.t.sol | 2 +- test/helpers/Fixtures.sol | 2 +- 9 files changed, 237 insertions(+), 114 deletions(-) delete mode 100644 script/DeployBase.s.sol create mode 100644 script/DeployBase.sol create mode 100644 script/ScriptInit.sol create mode 100644 script/UpgradeBase.sol create mode 100644 script/UpgradeHolesky.s.sol diff --git a/script/DeployBase.s.sol b/script/DeployBase.s.sol deleted file mode 100644 index cee4679..0000000 --- a/script/DeployBase.s.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -import {Script} from "forge-std/Script.sol"; -import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; -import {CCCP} from "../src/CCCP.sol"; - -import {JsonObj, Json} from "./utils/Json.sol"; - -struct DeployParams { - address lidoLocatorAddress; - bytes32 csModuleType; - address proxyAdmin; - address committeeAddress; - uint64 optInMinDurationBlocks; - uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; - uint64 defaultBlockGasLimit; -} - -abstract contract DeployBase is Script { - DeployParams internal config; - string internal artifactDir; - string internal chainName; - uint256 internal chainId; - - address internal deployer; - uint256 internal pk; - CCCP public cccp; - - error ChainIdMismatch(uint256 actual, uint256 expected); - - constructor(string memory _chainName, uint256 _chainId) { - chainName = _chainName; - chainId = _chainId; - } - - function _setUp() internal {} - - function run() external virtual { - if (chainId != block.chainid) { - revert ChainIdMismatch({actual: block.chainid, expected: chainId}); - } - - artifactDir = vm.envOr("ARTIFACTS_DIR", string("./artifacts/local/")); - pk = vm.envUint("DEPLOYER_PRIVATE_KEY"); - deployer = vm.addr(pk); - vm.label(deployer, "DEPLOYER"); - - vm.startBroadcast(pk); - { - address cccpImpl = address(new CCCP(config.lidoLocatorAddress, config.csModuleType)); - - cccp = CCCP( - _deployProxy( - config.proxyAdmin, - address(cccpImpl), - abi.encodeCall( - CCCP.initialize, - ( - config.committeeAddress, - config.optInMinDurationBlocks, - config.optOutDelayDurationBlocks, - config.defaultOperatorMaxValidators, - config.defaultBlockGasLimit - ) - ) - ) - ); - - JsonObj memory deployJson = Json.newObj(); - deployJson.set("ChainId", chainId); - deployJson.set("CCCPImpl", cccpImpl); - deployJson.set("CCCP", address(cccp)); - deployJson.set("LidoLocator", config.lidoLocatorAddress); - deployJson.set("DeployParams", abi.encode(config)); - vm.writeJson(deployJson.str, _deployJsonFilename()); - } - - vm.stopBroadcast(); - } - - function _deployProxy(address admin, address implementation, bytes memory data) internal returns (address) { - OssifiableProxy proxy = new OssifiableProxy({implementation_: implementation, data_: data, admin_: admin}); - - return address(proxy); - } - - function _deployJsonFilename() internal view returns (string memory) { - return string(abi.encodePacked(artifactDir, "deploy-", chainName, ".json")); - } -} diff --git a/script/DeployBase.sol b/script/DeployBase.sol new file mode 100644 index 0000000..3e3c1a7 --- /dev/null +++ b/script/DeployBase.sol @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ScriptInit} from "./ScriptInit.sol"; +import {JsonObj, Json} from "./utils/Json.sol"; + +import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; +import {CCCP} from "../src/CCCP.sol"; + +struct DeployParams { + address lidoLocatorAddress; + bytes32 csModuleType; + address proxyAdmin; + address committeeAddress; + uint64 optInMinDurationBlocks; + uint64 optOutDelayDurationBlocks; + uint64 defaultOperatorMaxValidators; + uint64 defaultBlockGasLimit; +} + +abstract contract DeployBase is ScriptInit { + DeployParams public params; + CCCP public cccp; + + constructor(string memory _chainName, uint256 _chainId) ScriptInit(_chainName, _chainId) {} + + function run() external virtual { + init(); + + vm.startBroadcast(pk); + { + // deploy new CCCP implementation + CCCP cccpImpl = _deployImplementation(params); + + cccp = CCCP( + _deployProxy( + params.proxyAdmin, + address(cccpImpl), + abi.encodeCall( + CCCP.initialize, + ( + params.committeeAddress, + params.optInMinDurationBlocks, + params.optOutDelayDurationBlocks, + params.defaultOperatorMaxValidators, + params.defaultBlockGasLimit + ) + ) + ) + ); + + JsonObj memory deployJson = Json.newObj(); + deployJson.set("ChainId", chainId); + deployJson.set("CCCPImpl", address(cccpImpl)); + deployJson.set("CCCP", address(cccp)); + deployJson.set("LidoLocator", params.lidoLocatorAddress); + deployJson.set("DeployParams", abi.encode(params)); + vm.writeJson(deployJson.str, _deployJsonFilename()); + } + + vm.stopBroadcast(); + } + + /// @dev can be overridden to customize the upgrade process + function _deployImplementation(DeployParams memory _params) internal virtual returns (CCCP) { + return new CCCP(_params.lidoLocatorAddress, _params.csModuleType); + } + + function _deployProxy(address _admin, address _impl, bytes memory _data) internal returns (address) { + OssifiableProxy proxy = new OssifiableProxy({implementation_: _impl, data_: _data, admin_: _admin}); + + return address(proxy); + } +} diff --git a/script/DeployHolesky.s.sol b/script/DeployHolesky.s.sol index 966ac0d..40b298a 100644 --- a/script/DeployHolesky.s.sol +++ b/script/DeployHolesky.s.sol @@ -3,23 +3,23 @@ pragma solidity 0.8.28; -import {DeployBase} from "./DeployBase.s.sol"; +import {DeployBase} from "./DeployBase.sol"; contract DeployHolesky is DeployBase { constructor() DeployBase("holesky", 17000) { // implementation constants - config.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; - config.csModuleType = "community-onchain-v1"; - config.defaultOperatorMaxValidators = 100; - config.defaultBlockGasLimit = 1000000; + params.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; + params.csModuleType = "community-onchain-v1"; + params.defaultOperatorMaxValidators = 100; + params.defaultBlockGasLimit = 1000000; // proxy - config.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA // initial parameters - config.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA - config.optInMinDurationBlocks = 32; - config.optOutDelayDurationBlocks = 64; + params.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.optInMinDurationBlocks = 32; + params.optOutDelayDurationBlocks = 64; _setUp(); } diff --git a/script/DeployMainnet.s.sol b/script/DeployMainnet.s.sol index 589f215..b9ef10f 100644 --- a/script/DeployMainnet.s.sol +++ b/script/DeployMainnet.s.sol @@ -3,23 +3,23 @@ pragma solidity 0.8.28; -import {DeployBase} from "./DeployBase.s.sol"; +import {DeployBase} from "./DeployBase.sol"; contract DeployMainnet is DeployBase { constructor() DeployBase("mainnet", 1) { // implementation constants - config.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb; - config.csModuleType = "community-onchain-v1"; - config.defaultOperatorMaxValidators = 1000; - config.defaultBlockGasLimit = 3000000; + params.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb; + params.csModuleType = "community-onchain-v1"; + params.defaultOperatorMaxValidators = 1000; + params.defaultBlockGasLimit = 3000000; // proxy - config.proxyAdmin = 0x0000000000000000000000000000000000000000; // Dev team EOA + params.proxyAdmin = 0x0000000000000000000000000000000000000000; // Dev team EOA // initial parameters - config.committeeAddress = 0x0000000000000000000000000000000000000000; // Dev team EOA - config.optInMinDurationBlocks = 0; - config.optOutDelayDurationBlocks = 64; + params.committeeAddress = 0x0000000000000000000000000000000000000000; // Dev team EOA + params.optInMinDurationBlocks = 0; + params.optOutDelayDurationBlocks = 64; _setUp(); } diff --git a/script/ScriptInit.sol b/script/ScriptInit.sol new file mode 100644 index 0000000..93c6d83 --- /dev/null +++ b/script/ScriptInit.sol @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Script} from "forge-std/Script.sol"; + +abstract contract ScriptInit is Script { + string internal artifactDir; + string internal chainName; + uint256 internal chainId; + address internal deployer; + uint256 internal pk; + + error ChainIdMismatch(uint256 actual, uint256 expected); + + constructor(string memory _chainName, uint256 _chainId) { + chainName = _chainName; + chainId = _chainId; + } + + function _setUp() internal virtual {} + + function init() internal virtual { + if (chainId != block.chainid) { + revert ChainIdMismatch({actual: block.chainid, expected: chainId}); + } + + artifactDir = vm.envOr("ARTIFACTS_DIR", string("./artifacts/local/")); + pk = vm.envUint("DEPLOYER_PRIVATE_KEY"); + deployer = vm.addr(pk); + vm.label(deployer, "DEPLOYER"); + } + + function _deployJsonFilename() internal view returns (string memory) { + return string(abi.encodePacked(artifactDir, "deploy-", chainName, ".json")); + } +} diff --git a/script/UpgradeBase.sol b/script/UpgradeBase.sol new file mode 100644 index 0000000..1a23db7 --- /dev/null +++ b/script/UpgradeBase.sol @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ScriptInit} from "./ScriptInit.sol"; +import {JsonObj, Json} from "./utils/Json.sol"; + +import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; +import {CCCP} from "../src/CCCP.sol"; +import {DeployParams} from "./DeployBase.sol"; + +abstract contract UpgradeBase is ScriptInit { + DeployParams internal params; + CCCP public cccp; + + error ArtifactsChainIdMismatch(uint256 actual, uint256 expected); + + constructor(string memory _chainName, uint256 _chainId) ScriptInit(_chainName, _chainId) {} + + function run() external { + init(); + + string memory artifactsPath = vm.envOr("DEPLOY_CONFIG", string("")); + uint256 artifactsChainId; + (artifactsChainId, cccp, params) = parseArtifacts(artifactsPath); + + if (chainId != artifactsChainId) { + revert ArtifactsChainIdMismatch({actual: artifactsChainId, expected: chainId}); + } + + vm.startBroadcast(pk); + { + OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); + + // deploy new CCCP implementation with the same LidoLocator and CSModuleType + CCCP cccpImpl = _deployImplementation(params); + _upgradeProxy(proxy, cccpImpl, params); + + JsonObj memory deployJson = Json.newObj(); + deployJson.set("ChainId", chainId); + deployJson.set("CCCPImpl", address(cccpImpl)); + deployJson.set("CCCP", address(cccp)); + deployJson.set("LidoLocator", params.lidoLocatorAddress); + deployJson.set("DeployParams", abi.encode(params)); + vm.writeJson(deployJson.str, _deployJsonFilename()); + } + + vm.stopBroadcast(); + } + + function parseArtifacts(string memory artifactsPath) internal view returns (uint256, CCCP, DeployParams memory) { + string memory artifactsJson = vm.readFile(artifactsPath); + + return ( + vm.parseJsonUint(artifactsJson, ".ChainId"), + CCCP(vm.parseJsonAddress(artifactsJson, ".CCCP")), + abi.decode(vm.parseJsonBytes(artifactsJson, ".DeployParams"), (DeployParams)) + ); + } + + /// @dev can be overridden to customize the upgrade process + function _deployImplementation(DeployParams memory _params) internal virtual returns (CCCP) { + return new CCCP(_params.lidoLocatorAddress, _params.csModuleType); + } + + /// @dev can be overridden to customize the upgrade process + function _upgradeProxy(OssifiableProxy _proxy, CCCP _impl, DeployParams memory _params) internal virtual { + // upgrade proxy to new CCCP implementation + _proxy.proxy__upgradeTo(address(_impl)); + // silent warning: unused variable + _params; + + // example of calling a function on the new implementation + // proxy.proxy__upgradeToAndCall(address(impl), abi.encodeCall(impl.initialize_v2, ())); + } +} diff --git a/script/UpgradeHolesky.s.sol b/script/UpgradeHolesky.s.sol new file mode 100644 index 0000000..10ee6bc --- /dev/null +++ b/script/UpgradeHolesky.s.sol @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2024 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {UpgradeBase} from "./UpgradeBase.sol"; + +contract UpgradeHolesky is UpgradeBase { + constructor() UpgradeBase("holesky", 17000) { + // implementation constants + params.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; + params.csModuleType = "community-onchain-v1"; + params.defaultOperatorMaxValidators = 100; + params.defaultBlockGasLimit = 1000000; + + // proxy + params.proxyAdmin = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + + // initial parameters + params.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA + params.optInMinDurationBlocks = 32; + params.optOutDelayDurationBlocks = 64; + + _setUp(); + } +} diff --git a/test/fork/deployment/PostDeployment.t.sol b/test/fork/deployment/PostDeployment.t.sol index adbb8e8..1c62b12 100644 --- a/test/fork/deployment/PostDeployment.t.sol +++ b/test/fork/deployment/PostDeployment.t.sol @@ -6,7 +6,7 @@ pragma solidity 0.8.28; import {Test} from "forge-std/Test.sol"; import {Utilities} from "../../helpers/Utilities.sol"; import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; -import {DeployParams} from "../../../script/DeployBase.s.sol"; +import {DeployParams} from "../../../script/DeployBase.sol"; import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; import {CCCP} from "../../../src/CCCP.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; diff --git a/test/helpers/Fixtures.sol b/test/helpers/Fixtures.sol index 986ff1a..7c7c4d7 100644 --- a/test/helpers/Fixtures.sol +++ b/test/helpers/Fixtures.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.28; import {StdCheats} from "forge-std/StdCheats.sol"; import {Test} from "forge-std/Test.sol"; -import {DeployParams} from "../../script/DeployBase.s.sol"; +import {DeployParams} from "../../script/DeployBase.sol"; import {CCCP} from "../../src/CCCP.sol"; import {ILidoLocator} from "../../src/interfaces/ILidoLocator.sol"; From b99604d5bcd1c96b067e83532a3547da00c4bc6d Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 28 Jan 2025 20:29:46 +0100 Subject: [PATCH 23/33] fix: add just commands --- Justfile | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Justfile b/Justfile index 32656e9..b4bfebf 100644 --- a/Justfile +++ b/Justfile @@ -9,7 +9,16 @@ deploy_script_name := if chain == "mainnet" { error("Unsupported chain " + chain) } +upgrade_script_name := if chain == "mainnet" { + "UpgradeMainnet" +} else if chain == "holesky" { + "UpgradeHolesky" +} else { + error("Unsupported chain " + chain) +} + deploy_script_path := "script" / deploy_script_name + ".s.sol:" + deploy_script_name +upgrade_script_path := "script" / upgrade_script_name + ".s.sol:" + upgrade_script_name anvil_host := env_var_or_default("ANVIL_IP_ADDR", "127.0.0.1") anvil_port := "8545" @@ -104,6 +113,7 @@ make-fork *args: kill-fork: @-pkill anvil && just _warn "anvil process is killed" +# deploy production deploy *args: forge script {{deploy_script_path}} --rpc-url {{anvil_rpc_url}} --broadcast --slow {{args}} @@ -128,12 +138,40 @@ verify-prod *args: _deploy-prod *args: forge script {{deploy_script_path}} --force --rpc-url ${RPC_URL} {{args}} +# upgrade production +upgrade *args: + forge script {{upgrade_script_path}} --rpc-url {{anvil_rpc_url}} --broadcast --slow {{args}} + +upgrade-prod *args: + just _warn "The current `tput bold`chain={{chain}}`tput sgr0` with the following rpc url: $RPC_URL" + ARTIFACTS_DIR=./artifacts/latest/ just _upgrade-prod-confirm {{args}} + + cp ./broadcast/{{upgrade_script_path}}.s.sol/`cast chain-id --rpc-url=$RPC_URL`/run-latest.json \ + ./artifacts/latest/transactions.json + +[confirm("You are about to broadcast upgrade transactions to the network. Are you sure?")] +_upgrade-prod-confirm *args: + just _upgrade-prod --broadcast --verify {{args}} + +upgrade-prod-dry *args: + just _upgrade-prod {{args}} + +_upgrade-prod *args: + forge script {{upgrade_script_path}} --force --rpc-url ${RPC_URL} {{args}} + +# local deployment deploy-local: just make-fork & @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done ARTIFACTS_DIR=./artifacts/local/ just deploy just _warn "anvil is kept running in the background: {{anvil_rpc_url}}" +upgrade-local: + just make-fork & + @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done + ARTIFACTS_DIR=./artifacts/local/ just upgrade + just _warn "anvil is kept running in the background: {{anvil_rpc_url}}" + test-local *args: just make-fork --silent & @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done From 7a90f27751cec696b4960f4ed8f147c7d97af84c Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 28 Jan 2025 20:31:54 +0100 Subject: [PATCH 24/33] fix: missing operatorId in getOperator --- src/CCCP.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index ffbb47b..f24492f 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -367,7 +367,7 @@ contract CCCP is _loadLidoNodeOperator(_c, moduleId, operatorId); state = _getOperatorState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(state.optInOutState); - (, bool isDisabled) = _getModuleConfig(_c.moduleId); + (, bool isDisabled) = _getModuleConfig(moduleId); // operator is enabled: // - if it's s opted in @@ -376,7 +376,7 @@ contract CCCP is // - if the contract is not paused isEnabled = flags.isOptedIn && !isDisabled && _c.isActive && !paused(); - return (_c.moduleId, _c.operatorId, isEnabled, state); + return (moduleId, operatorId, isEnabled, state); } function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { @@ -456,6 +456,7 @@ contract CCCP is if (operatorId >= totalOperatorsCount) { revert InvalidOperatorId(); } + _c.operatorId = operatorId; /// @dev check for the CSModule type bytes32 moduleType = IStakingModule(_c.moduleAddress).getType(); From ac629cf463ecc0facd5a7e3f7c10587f8dcfa9ce Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 28 Jan 2025 20:32:59 +0100 Subject: [PATCH 25/33] chore: upd deployed holesky artifacts --- artifacts/holesky/deploy-holesky.json | 2 +- artifacts/holesky/transactions.json | 105 ++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json index 83b7286..0db0925 100644 --- a/artifacts/holesky/deploy-holesky.json +++ b/artifacts/holesky/deploy-holesky.json @@ -1,6 +1,6 @@ { "CCCP": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", - "CCCPImpl": "0x7c178B9B797C6ea6776A784C22A0f95a79385c9b", + "CCCPImpl": "0x0442fB2C8607D04D6Ba496Ea8990dA7bd7Ec25fc", "ChainId": 17000, "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json index 3445f4a..47aacae 100644 --- a/artifacts/holesky/transactions.json +++ b/artifacts/holesky/transactions.json @@ -84,6 +84,48 @@ }, "additionalContracts": [], "isFixedGasLimit": false + }, + { + "hash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", + "transactionType": "CREATE", + "contractName": "CCCP", + "contractAddress": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x387d65", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b506040516134ee3803806134ee83398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516131e261030c5f395f611d1a01525f818161049801526123f601525f81816113b401528181611459015281816114b4015281816116d70152611f0301525f81816110670152818161187201528181611b32015261231d01526131e25ff3fe608060405234801561000f575f5ffd5b50600436106101d1575f3560e01c80638aa10435116100fe578063ba6bc8d51161009e578063d547741f1161006e578063d547741f1461046d578063d6dc6ae714610480578063dbba4b4814610493578063fc3372f5146104ba575f5ffd5b8063ba6bc8d514610404578063c3f909d414610417578063ca15c87314610452578063d4eec5a614610465575f5ffd5b8063a217fddf116100d9578063a217fddf146103b6578063a3246ad3146103bd578063a3714e07146103dd578063ad32563d146103f0575f5ffd5b80638aa10435146103585780639010d07c1461037857806391d14854146103a3575f5ffd5b8063389ed267116101745780635c975abb116101445780635c975abb1461031357806370bc09f61461032a5780638456cb591461033d578063893d004e14610345575f5ffd5b8063389ed267146102ae5780633c074eb3146102d55780633f4ba83a146102e85780635865c60c146102f0575f5ffd5b80632de03aa1116101af5780632de03aa11461024e5780632de0920c146102755780632f2ff15d1461028857806336568abe1461029b575f5ffd5b806301ffc9a7146101d557806313e09453146101fd578063248a9ca314610212575b5f5ffd5b6101e86101e3366004612654565b6104cd565b60405190151581526020015b60405180910390f35b61021061020b3660046126a3565b6104f7565b005b610240610220366004612710565b5f9081525f5160206131965f395f51905f52602052604090206001015490565b6040519081526020016101f4565b6102407f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610210610283366004612737565b6106ba565b6102106102963660046127ff565b61094a565b6102106102a93660046127ff565b610980565b6102407f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b6102106102e336600461282d565b6109b8565b610210610a43565b6103036102fe366004612859565b610a78565b6040516101f494939291906128ac565b5f5160206131b65f395f51905f525460ff166101e8565b610210610338366004612969565b610aa8565b610210610ad2565b6102106103533660046129c2565b610b04565b610360610bf2565b6040516001600160401b0390911681526020016101f4565b61038b610386366004612a0a565b610c29565b6040516001600160a01b0390911681526020016101f4565b6101e86103b13660046127ff565b610c56565b6102405f81565b6103d06103cb366004612710565b610c8c565b6040516101f49190612a2a565b6103036103eb36600461282d565b610cbc565b6102405f5160206131765f395f51905f5281565b610210610412366004612a75565b610cee565b61041f610dfe565b604080516001600160401b03958616815293851660208501529184169183019190915290911660608201526080016101f4565b610240610460366004612710565b610e18565b610210610e3c565b61021061047b3660046127ff565b610f17565b61021061048e36600461282d565b610f47565b61038b7f000000000000000000000000000000000000000000000000000000000000000081565b6102106104c8366004612a9e565b611011565b5f6001600160e01b03198216635a05180f60e01b14806104f157506104f182611103565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561053b5750825b90505f826001600160401b031660011480156105565750303b155b905081158015610564575080155b156105825760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ac57845460ff60401b1916600160401b1785555b6001600160a01b038a166105d3576040516319467aa760e11b815260040160405180910390fd5b6105db611137565b6105e3611149565b6105ed5f8b611151565b506106055f5160206131765f395f51905f528b611151565b506106307f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b611151565b5061065b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b611151565b5061066889898989611193565b83156106ae57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b6106c26111f8565b6001600160a01b0385166106e957604051634434240960e11b815260040160405180910390fd5b6106f161259a565b6106fc818989611228565b80606001516001600160a01b0316336001600160a01b031614610732576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161075457604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6107768261123c565b90505f610782826112a2565b8051909150156107a5576040516342ee68b560e01b815260040160405180910390fd5b80604001516107c7576040516328a5715760e21b815260040160405180910390fd5b6107d1838a6113a2565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3610857836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f15158152506114e0565b61086384848a8a611541565b604080516020601f8801819004810282018301835281018781526108a69286929182918b908b90819085018382808284375f9201919091525050509152506115bb565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef88886040516108e7929190612adb565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206131965f395f51905f526020526040902060010154610970816115da565b61097a8383611151565b50505050565b6001600160a01b03811633146109a95760405163334bd91960e11b815260040160405180910390fd5b6109b382826115e4565b505050565b5f5160206131765f395f51905f526109cf816115da565b5f6109da848461161d565b90505f6109e68261123c565b9050806040015115610a02575f6040820152610a0282826114e0565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610a6d816115da565b610a75611666565b50565b5f5f5f610a836125ce565b5f610a8d866116c5565b9050610a9881611725565b9450945094509450509193509193565b5f5160206131765f395f51905f52610abf816115da565b610acb85858585611193565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610afc816115da565b610a756117b7565b610b0c6111f8565b6001600160a01b038116610b3357604051634434240960e11b815260040160405180910390fd5b610b3b61259a565b610b46818585611228565b80606001516001600160a01b0316336001600160a01b031614610b7c576040516349ad9b0960e11b815260040160405180910390fd5b610b9d62ffffff60401b604086901b166001600160401b03851617836113a2565b6040516001600160a01b03831681526001600160401b0384169062ffffff8616907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a350505050565b5f610c247ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206131565f395f51905f52602081905260408220610c4e90846117ff565b949350505050565b5f9182525f5160206131965f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f8181525f5160206131565f395f51905f526020819052604090912060609190610cb59061180a565b9392505050565b5f5f5f610cc76125ce565b5f610cd2878761161d565b9050610cdd81611725565b929a91995097509095509350505050565b610cf66111f8565b5f610d00336116c5565b90505f610d14610d0f8361123c565b6112a2565b8051909150610d3657604051631a8660cb60e01b815260040160405180910390fd5b5f610d4083611816565b9050805f01516001600160401b0316856001600160401b03161180610d7a575080602001516001600160401b0316846001600160401b0316105b80610db1575080516001600160401b038681169116148015610db1575080602001516001600160401b0316846001600160401b0316145b15610dcf5760405163c7f544bb60e01b815260040160405180910390fd5b610dd761259a565b604084901c84610de8838383611228565b610df483878a8a611541565b5050505050505050565b5f5f5f5f610e0a611864565b935093509350935090919293565b5f8181525f5160206131565f395f51905f52602081905260408220610cb5906118da565b610e446111f8565b5f610e4e336116c5565b90505f610e5a8261123c565b90505f610e66826112a2565b8051909150610e8857604051631a8660cb60e01b815260040160405180910390fd5b8060600151610eaa57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b0343166020830152610ec383836114e0565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206131965f395f51905f526020526040902060010154610f3d816115da565b61097a83836115e4565b5f5160206131765f395f51905f52610f5e816115da565b5f610f69848461161d565b90505f610f758261123c565b90505f610f81826112a2565b8051909150610fa357604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b034316602083015260016040830152610fc383836114e0565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f5160206131765f395f51905f52611028816115da565b61103061259a565b61103a81866118e3565b6040805180820182526001600160401b038086168252861515602080840191825262ffffff8a165f9081527f0000000000000000000000000000000000000000000000000000000000000000909152939093209151825493511515600160401b0268ffffffffffffffffff19909416911617919091179055604080516001600160401b0385168152851515602082015262ffffff8716917f55bd0114bd34303292838dd2bc70c974c9eea7858dc4fb3716662d439ec3ae12910160405180910390a25050505050565b5f6001600160e01b03198216637965db0b60e01b14806104f157506301ffc9a760e01b6001600160e01b03198316146104f1565b61113f611979565b6111476119c2565b565b611147611979565b5f5f5160206131565f395f51905f528161116b85856119e2565b90508015610c4e575f85815260208390526040902061118a9085611a83565b50949350505050565b61119f84848484611a97565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b5f5160206131b65f395f51905f525460ff16156111475760405163d93c066560e01b815260040160405180910390fd5b61123283836118e3565b6109b38382611bd4565b604080516060810182525f808252602082018190529181019190915261126182611efd565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b604080516080810182525f808252602082018190529181018290526060810191909152435f806112d0611864565b5050915091505f5f86602001516001600160401b03161180156113135750836001600160401b03168287602001516113089190612b1d565b6001600160401b0316105b90505f5f875f01516001600160401b031611801561132f575081155b90505f8115801561134257508760400151155b90505f828015611370575088516001600160401b03881690611365908890612b1d565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906113eb5750828114155b156114095760405163324e8e5f60e01b815260040160405180910390fd5b5f61141384611efd565b80549091506001600160a01b031680158015906114425750836001600160a01b0316816001600160a01b031614155b15611482576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b806114ea83611efd565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b61155084608001518383611f2c565b835161155d908383611fb0565b611568838383612051565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c9101610be4565b806115c583611efd565b8151600391909101908190610acb9082612bcc565b610a7581336120bf565b5f5f5160206131565f395f51905f52816115fe8585612100565b90508015610c4e575f85815260208390526040902061118a9085612179565b6001600160401b038116604083901b62ffffff60401b16175f61163f8261218d565b6001600160a01b0316036104f1576040516325ec6c1f60e01b815260040160405180910390fd5b61166e6121a7565b5f5160206131b65f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f0000000000000000000000000000000000000000000000000000000000000000016020526040812054908190036117205760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f6117306125ce565b61173861259a565b604086901c945085935061174d818686611228565b611756866121d6565b91505f61176683604001516112a2565b90505f61177287612312565b915050815f01518015611783575080155b801561179057508260a001515b80156117ab57505f5160206131b65f395f51905f525460ff16155b94505050509193509193565b6117bf6111f8565b5f5160206131b65f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336116a7565b5f610cb58383612374565b60605f610cb58361239a565b604080518082019091525f808252602082015261183282611efd565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6104f1825490565b5f6118ec6123f3565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611934573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261195b9190810190612d9d565b62ffffff9092168352506020908101516001600160a01b0316910152565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661114757604051631afcd79f60e31b815260040160405180910390fd5b6119ca611979565b5f5160206131b65f395f51905f52805460ff19169055565b5f5f5160206131965f395f51905f526119fb8484610c56565b611a7a575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611a303390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506104f1565b5f9150506104f1565b5f610cb5836001600160a01b038416612474565b816001600160401b03165f03611ac05760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611ae857604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b0316815250611b547f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f03611bfa57604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c5f9190612ece565b9050806001600160401b0316826001600160401b031610611c935760405163b9aa612760e01b815260040160405180910390fd5b8183604001906001600160401b031690816001600160401b0316815250505f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cf2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d169190612ece565b90507f00000000000000000000000000000000000000000000000000000000000000008103611e54576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa158015611d8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611db39190612ef0565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015611e08573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2c9190612f1e565b6101808101516001600160a01b031660608801525163ffffffff1660808701525061097a9050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015611ea9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611ed09190810190613047565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b03161115611f5f5760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b0316101580611f925750826001600160401b0316826001600160401b031610155b156109b357604051637ba485c560e01b815260040160405180910390fd5b5f611fb9611864565b50925050505f5f611fc986612312565b915091508015611fec57604051630dbfe5fd60e31b815260040160405180910390fd5b5f611ff786866130e0565b612002906001612b1d565b90505f6001600160401b0384161561201a578361201c565b845b9050806001600160401b0316826001600160401b03161115610df457604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061208184611efd565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6120c98282610c56565b6120fc5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206131965f395f51905f526121198484610c56565b15611a7a575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506104f1565b5f610cb5836001600160a01b0384166124c0565b5f61219782611efd565b546001600160a01b031692915050565b5f5160206131b65f395f51905f525460ff1661114757604051638dfc202b60e01b815260040160405180910390fd5b6121de6125ce565b6121e782611efd565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061228890612b50565b80601f01602080910402602001604051908101604052809291908181526020018280546122b490612b50565b80156122ff5780601f106122d6576101008083540402835291602001916122ff565b820191905f5260205f20905b8154815290600101906020018083116122e257829003601f168201915b5050509190925250505090525092915050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251808401909352546001600160401b038116808452600160401b90910460ff1615159290910182905291565b5f825f018281548110612389576123896130ff565b905f5260205f200154905092915050565b6060815f018054806020026020016040519081016040528092919081815260200182805480156123e757602002820191905f5260205f20905b8154815260200190600101908083116123d3575b50505050509050919050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612450573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c249190613113565b5f8181526001830160205260408120546124b957508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556104f1565b505f6104f1565b5f8181526001830160205260408120548015611a7a575f6124e260018361312e565b85549091505f906124f59060019061312e565b9050808214612554575f865f018281548110612513576125136130ff565b905f5260205f200154905080875f018481548110612533576125336130ff565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061256557612565613141565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506104f1565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161261560405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f808252602082810182905292820152910190815260200161264f6040518060200160405280606081525090565b905290565b5f60208284031215612664575f5ffd5b81356001600160e01b031981168114610cb5575f5ffd5b6001600160a01b0381168114610a75575f5ffd5b6001600160401b0381168114610a75575f5ffd5b5f5f5f5f5f60a086880312156126b7575f5ffd5b85356126c28161267b565b945060208601356126d28161268f565b935060408601356126e28161268f565b925060608601356126f28161268f565b915060808601356127028161268f565b809150509295509295909350565b5f60208284031215612720575f5ffd5b5035919050565b62ffffff81168114610a75575f5ffd5b5f5f5f5f5f5f5f60c0888a03121561274d575f5ffd5b873561275881612727565b965060208801356127688161268f565b955060408801356127788161267b565b945060608801356127888161268f565b935060808801356127988161268f565b925060a08801356001600160401b038111156127b2575f5ffd5b8801601f81018a136127c2575f5ffd5b80356001600160401b038111156127d7575f5ffd5b8a60208284010111156127e8575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612810575f5ffd5b8235915060208301356128228161267b565b809150509250929050565b5f5f6040838503121561283e575f5ffd5b823561284981612727565b915060208301356128228161268f565b5f60208284031215612869575f5ffd5b8135610cb58161267b565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e061014084015261295e610160840182612874565b979650505050505050565b5f5f5f5f6080858703121561297c575f5ffd5b84356129878161268f565b935060208501356129978161268f565b925060408501356129a78161268f565b915060608501356129b78161268f565b939692955090935050565b5f5f5f606084860312156129d4575f5ffd5b83356129df81612727565b925060208401356129ef8161268f565b915060408401356129ff8161267b565b809150509250925092565b5f5f60408385031215612a1b575f5ffd5b50508035926020909101359150565b602080825282518282018190525f918401906040840190835b81811015612a6a5783516001600160a01b0316835260209384019390920191600101612a43565b509095945050505050565b5f5f60408385031215612a86575f5ffd5b82356128498161268f565b8015158114610a75575f5ffd5b5f5f5f60608486031215612ab0575f5ffd5b8335612abb81612727565b92506020840135612acb81612a91565b915060408401356129ff8161268f565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156104f1576104f1612b09565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612b6457607f821691505b602082108103612b8257634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156109b357805f5260205f20601f840160051c81016020851015612bad5750805b601f840160051c820191505b81811015610acb575f8155600101612bb9565b81516001600160401b03811115612be557612be5612b3c565b612bf981612bf38454612b50565b84612b88565b6020601f821160018114612c2b575f8315612c145750848201515b5f19600385901b1c1916600184901b178455610acb565b5f84815260208120601f198516915b82811015612c5a5787850151825560209485019460019092019101612c3a565b5084821015612c7757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b0381118282101715612ca957612ca9612b3c565b60405290565b6040516101e081016001600160401b0381118282101715612ca957612ca9612b3c565b805161172081612727565b80516117208161267b565b805161ffff81168114611720575f5ffd5b805160ff81168114611720575f5ffd5b5f82601f830112612d18575f5ffd5b81516001600160401b03811115612d3157612d31612b3c565b604051601f8201601f19908116603f011681016001600160401b0381118282101715612d5f57612d5f612b3c565b604052818152838201602001851015612d76575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80516117208161268f565b5f60208284031215612dad575f5ffd5b81516001600160401b03811115612dc2575f5ffd5b82016101a08185031215612dd4575f5ffd5b612ddc612c86565b612de582612cd2565b8152612df360208301612cdd565b6020820152612e0460408301612ce8565b6040820152612e1560608301612ce8565b6060820152612e2660808301612ce8565b6080820152612e3760a08301612cf9565b60a082015260c08201516001600160401b03811115612e54575f5ffd5b612e6086828501612d09565b60c083015250612e7260e08301612d92565b60e082015261010082810151908201526101208083015190820152612e9a6101408301612ce8565b610140820152612ead6101608301612d92565b610160820152612ec06101808301612d92565b610180820152949350505050565b5f60208284031215612ede575f5ffd5b5051919050565b805161172081612a91565b5f60208284031215612f00575f5ffd5b8151610cb581612a91565b805163ffffffff81168114611720575f5ffd5b5f6101e0828403128015612f30575f5ffd5b50612f39612caf565b612f4283612f0b565b8152612f5060208401612f0b565b6020820152612f6160408401612f0b565b6040820152612f7260608401612f0b565b6060820152612f8360808401612f0b565b6080820152612f9460a08401612f0b565b60a0820152612fa560c08401612f0b565b60c0820152612fb660e08401612cf9565b60e0820152612fc86101008401612f0b565b610100820152612fdb6101208401612f0b565b610120820152612fee6101408401612cdd565b6101408201526130016101608401612cdd565b6101608201526130146101808401612cdd565b6101808201526130276101a08401612cdd565b6101a082015261303a6101c08401612ee5565b6101c08201529392505050565b5f5f5f5f5f5f60c0878903121561305c575f5ffd5b865161306781612a91565b60208801519096506001600160401b03811115613082575f5ffd5b61308e89828a01612d09565b955050604087015161309f8161267b565b60608801519094506130b08161268f565b60808801519093506130c18161268f565b60a08801519092506130d28161268f565b809150509295509295509295565b6001600160401b0382811682821603908111156104f1576104f1612b09565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613123575f5ffd5b8151610cb58161267b565b818103818111156104f1576104f1612b09565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x45", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xbf3baa41f166f615fc2370f47f338b6b5e56d27f70d9fdeb51efdf8cd61aa2be", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x0442fB2C8607D04D6Ba496Ea8990dA7bd7Ec25fc" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb0000000000000000000000000442fb2c8607d04d6ba496ea8990da7bd7ec25fc", + "nonce": "0x46", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false } ], "receipts": [ @@ -322,6 +364,69 @@ "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1b9df0d", + "logs": [ + { + "address": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "blockTimestamp": "0x6798d2a8", + "transactionHash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", + "transactionIndex": "0x9", + "logIndex": "0xff", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", + "transactionIndex": "0x9", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "gasUsed": "0x2b7427", + "effectiveGasPrice": "0xf424b", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1ba601f", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000000442fb2c8607d04d6ba496ea8990da7bd7ec25fc" + ], + "data": "0x", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "blockTimestamp": "0x6798d2a8", + "transactionHash": "0xbf3baa41f166f615fc2370f47f338b6b5e56d27f70d9fdeb51efdf8cd61aa2be", + "transactionIndex": "0xa", + "logIndex": "0x100", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000020000000000000000000000000000000000000000010000000000000000000000000000000001000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xbf3baa41f166f615fc2370f47f338b6b5e56d27f70d9fdeb51efdf8cd61aa2be", + "transactionIndex": "0xa", + "blockHash": "0x961d98bea6b208d1fd92cd1951541acbbef1728f836c520ee543065e993dd20d", + "blockNumber": "0x31650f", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf424b", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null } ], "libraries": [], From 31bcc1d62e315e00de846436df7b4b720e2ecdf1 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 29 Jan 2025 01:11:16 +0100 Subject: [PATCH 26/33] fix: module config refactor --- src/CCCP.sol | 62 +++++++++++++++++---------- src/interfaces/ICCCPConfigStorage.sol | 19 +++++--- src/lib/CCCPConfigStorage.sol | 9 ++-- 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index f24492f..f3b424a 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -67,7 +67,7 @@ contract CCCP is uint256 defaultOperatorMaxValidators, uint256 defaultBlockGasLimit ); - event ModuleConfigUpdated(uint256 indexed moduleId, uint256 operatorMaxValidators, bool isDisabled); + event ModuleConfigUpdated(uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxValidators); error RewardAddressMismatch(); error OperatorNotActive(); @@ -132,8 +132,8 @@ contract CCCP is uint24 moduleId, uint64 operatorId, address manager, - uint64 newKeyIndexRangeStart, - uint64 newKeyIndexRangeEnd, + uint64 keyIndexStart, + uint64 keyIndexEnd, string calldata rpcURL ) external whenNotPaused { if (manager == address(0)) revert ZeroOperatorManagerAddress(); @@ -171,7 +171,7 @@ contract CCCP is _setOperatorOptInOutState( opKey, OptInOutState({optInBlock: uint64(block.number), optOutBlock: 0, isOptOutForced: false}) ); - _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); + _checkAndUpdateKeysRange(_c, opKey, keyIndexStart, keyIndexEnd); /// @dev no checks on rpcUrl, so it can be rewritten on repeated opt-in _setOperatorExtraData(opKey, ExtraData({rpcURL: rpcURL})); @@ -219,7 +219,7 @@ contract CCCP is /// @notice Update the operator's keys range /// @dev should be called by the operator manager address - function updateKeysRange(uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd) external whenNotPaused { + function updateKeysRange(uint64 keyIndexStart, uint64 keyIndexEnd) external whenNotPaused { uint256 opKey = _getOpKeyByManager(msg.sender); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); if (!flags.isOptedIn) { @@ -228,15 +228,15 @@ contract CCCP is KeysRange memory keysRange = _getOperatorKeysRange(opKey); if ( - newKeyIndexRangeStart > keysRange.indexStart || newKeyIndexRangeEnd < keysRange.indexEnd - || (newKeyIndexRangeStart == keysRange.indexStart && newKeyIndexRangeEnd == keysRange.indexEnd) + keyIndexStart > keysRange.indexStart || keyIndexEnd < keysRange.indexEnd + || (keyIndexStart == keysRange.indexStart && keyIndexEnd == keysRange.indexEnd) ) { revert KeyIndexMismatch(); } LidoOperatorCache memory _c; (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); _loadLidoNodeOperator(_c, moduleId, operatorId); - _checkAndUpdateKeysRange(_c, opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); + _checkAndUpdateKeysRange(_c, opKey, keyIndexStart, keyIndexEnd); } /// @notice Update the operator's manager address @@ -276,6 +276,15 @@ contract CCCP is return _getOperator(opKey); } + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) internal view returns (bool) { + LidoOperatorCache memory _c; + _loadLidoNodeOperator(_c, moduleId, operatorId); + uint256 opKey = _getOpKeyById(moduleId, operatorId); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); + + return _isOperatorIsEnabledForPreconf(optInOutState, moduleId, _c.isActive); + } + function getConfig() external view @@ -325,8 +334,8 @@ contract CCCP is LidoOperatorCache memory _c; _loadLidoModuleData(_c, moduleId); - _setModuleConfig(moduleId, operatorMaxValidators, isDisabled); - emit ModuleConfigUpdated(moduleId, operatorMaxValidators, isDisabled); + _setModuleConfig(moduleId, isDisabled, operatorMaxValidators); + emit ModuleConfigUpdated(moduleId, isDisabled, operatorMaxValidators); } function _updateConfig( @@ -346,15 +355,15 @@ contract CCCP is function _checkAndUpdateKeysRange( LidoOperatorCache memory _c, uint256 opKey, - uint64 newKeyIndexRangeStart, - uint64 newKeyIndexRangeEnd + uint64 keyIndexStart, + uint64 keyIndexEnd ) internal { - _checkKeysRangeIsValid(_c.totalKeys, newKeyIndexRangeStart, newKeyIndexRangeEnd); - _checkModuleParams(_c.moduleId, newKeyIndexRangeStart, newKeyIndexRangeEnd); + _checkKeysRangeIsValid(_c.totalKeys, keyIndexStart, keyIndexEnd); + _checkModuleParams(_c.moduleId, keyIndexStart, keyIndexEnd); // save operator state - _setOperatorKeysRange(opKey, newKeyIndexRangeStart, newKeyIndexRangeEnd); - emit KeysRangeUpdated(_c.moduleId, _c.operatorId, newKeyIndexRangeStart, newKeyIndexRangeEnd); + _setOperatorKeysRange(opKey, keyIndexStart, keyIndexEnd); + emit KeysRangeUpdated(_c.moduleId, _c.operatorId, keyIndexStart, keyIndexEnd); } function _getOperator(uint256 opKey) @@ -366,27 +375,34 @@ contract CCCP is (moduleId, operatorId) = __decOpKey(opKey); _loadLidoNodeOperator(_c, moduleId, operatorId); state = _getOperatorState(opKey); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(state.optInOutState); - (, bool isDisabled) = _getModuleConfig(moduleId); + isEnabled = _isOperatorIsEnabledForPreconf(state.optInOutState, moduleId, _c.isActive); + + return (moduleId, operatorId, isEnabled, state); + } + function _isOperatorIsEnabledForPreconf(OptInOutState memory optInOutState, uint24 moduleId, bool isOperatorActive) + internal + view + returns (bool) + { + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); + (bool isModuleDisabled,,) = _getModuleConfig(moduleId); // operator is enabled: // - if it's s opted in // - if module not disabled // - if operator is active in Lido module // - if the contract is not paused - isEnabled = flags.isOptedIn && !isDisabled && _c.isActive && !paused(); - - return (moduleId, operatorId, isEnabled, state); + return flags.isOptedIn && !isModuleDisabled && isOperatorActive && !paused(); } function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { (,, uint64 defaultOperatorMaxValidators,) = _getConfig(); - (uint64 moduleMaxValidators, bool isDisabled) = _getModuleConfig(moduleId); + (bool isDisabled, uint64 operatorMaxValidators) = _getModuleConfig(moduleId); if (isDisabled) { revert ModuleDisabled(); } uint64 totalKeys = endIndex - startIndex + 1; - uint64 maxValidators = moduleMaxValidators == 0 ? defaultOperatorMaxValidators : moduleMaxValidators; + uint64 maxValidators = operatorMaxValidators == 0 ? defaultOperatorMaxValidators : operatorMaxValidators; if (totalKeys > maxValidators) { revert KeysRangeExceedMaxValidators(); diff --git a/src/interfaces/ICCCPConfigStorage.sol b/src/interfaces/ICCCPConfigStorage.sol index 445810d..a3c402b 100644 --- a/src/interfaces/ICCCPConfigStorage.sol +++ b/src/interfaces/ICCCPConfigStorage.sol @@ -8,19 +8,24 @@ pragma solidity 0.8.28; * @notice Interface for interacting with the storage and control config params. */ interface ICCCPConfigStorage { + + /// @notice steaking module parameters + /// @dev override global default values, zero values means use default config + /// @param isDisabled is module disabled for pre-confs + /// operators in disabled modules are automatically considered as opted-out + /// @param operatorMaxValidators maximum number of validators per operator struct ModuleConfig { - // hopefully, we won't need more than 2^64 validators - /// @dev zero value means use default config - uint64 maxValidators; - // is module disabled for pre-confs - /// @dev operators in disabled modules are automatically considered as opted-out bool isDisabled; + uint64 operatorMaxValidators; } + /// @notice global config parameters + /// @param optInMinDurationBlocks minimum duration of the opt-in period in blocks + /// @param optOutDelayDurationBlocks delay in blocks before the operator can opt-in again after opt-out + /// @param defaultOperatorMaxValidators default maximum number of validators per operator + /// @param defaultBlockGasLimit default block gas limit struct Config { - // minimum duration of the opt-in period in blocks uint64 optInMinDurationBlocks; - // delay in blocks before the operator can opt-in again after opt-out uint64 optOutDelayDurationBlocks; uint64 defaultOperatorMaxValidators; uint64 defaultBlockGasLimit; diff --git a/src/lib/CCCPConfigStorage.sol b/src/lib/CCCPConfigStorage.sol index e6549e2..d810787 100644 --- a/src/lib/CCCPConfigStorage.sol +++ b/src/lib/CCCPConfigStorage.sol @@ -34,8 +34,9 @@ abstract contract CCCPConfigStorage is ICCCPConfigStorage { }); } - function _setModuleConfig(uint24 moduleId, uint64 maxValidators, bool isDisabled) internal { - _getConfigStorage()._modules[moduleId] = ModuleConfig({maxValidators: maxValidators, isDisabled: isDisabled}); + function _setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators) internal { + _getConfigStorage()._modules[moduleId] = + ModuleConfig({isDisabled: isDisabled, operatorMaxValidators: operatorMaxValidators}); } function _getConfig() @@ -57,9 +58,9 @@ abstract contract CCCPConfigStorage is ICCCPConfigStorage { ); } - function _getModuleConfig(uint24 moduleId) internal view returns (uint64 maxValidators, bool isDisabled) { + function _getModuleConfig(uint24 moduleId) internal view returns (bool isDisabled, uint64 operatorMaxValidators) { ModuleConfig memory moduleConfig = _getConfigStorage()._modules[moduleId]; - return (moduleConfig.maxValidators, moduleConfig.isDisabled); + return (moduleConfig.isDisabled, moduleConfig.operatorMaxValidators); } /** From 7eb3da9ecfbcc1970e8b8ac88a86d56af6deebd1 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 29 Jan 2025 03:12:38 +0100 Subject: [PATCH 27/33] feat: refactor module config, add view methods --- src/CCCP.sol | 83 +++++++++++++++++++++++---- src/interfaces/ICCCP.sol | 11 +++- src/interfaces/ICCCPConfigStorage.sol | 3 +- src/lib/CCCPConfigStorage.sol | 19 ++++-- 4 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index f3b424a..a3db0e5 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -67,7 +67,9 @@ contract CCCP is uint256 defaultOperatorMaxValidators, uint256 defaultBlockGasLimit ); - event ModuleConfigUpdated(uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxValidators); + event ModuleConfigUpdated( + uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxValidators, uint64 blockGasLimit + ); error RewardAddressMismatch(); error OperatorNotActive(); @@ -276,7 +278,12 @@ contract CCCP is return _getOperator(opKey); } - function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) internal view returns (bool) { + function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address) { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + return _getOperatorManager(opKey); + } + + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool) { LidoOperatorCache memory _c; _loadLidoNodeOperator(_c, moduleId, operatorId); uint256 opKey = _getOpKeyById(moduleId, operatorId); @@ -285,6 +292,45 @@ contract CCCP is return _isOperatorIsEnabledForPreconf(optInOutState, moduleId, _c.isActive); } + function getOperatorAllowedValidators(uint24 moduleId, uint64 operatorId) + external + view + returns (uint64 allowedValidators) + { + // check if the module has max validators limit + uint64 maxValidators = getModuleOperatorMaxValidators(moduleId); + if (maxValidators == 0) { + return 0; + } + // check if the operator is active in Lido module + LidoOperatorCache memory _c; + _loadLidoNodeOperator(_c, moduleId, operatorId); + if (!_c.isActive) { + return 0; + } + uint256 opKey = _getOpKeyById(moduleId, operatorId); + // check if the operator is already has the state + if (_getOperatorManager(opKey) == address(0)) { + return 0; + } + + KeysRange memory keysRange = _getOperatorKeysRange(opKey); + uint64 totalKeys = keysRange.indexEnd - keysRange.indexStart + 1; + // check if the operator has already reached the max validators limit + if (totalKeys >= maxValidators) { + return 0; + } + + unchecked { + allowedValidators = maxValidators - totalKeys; + } + // check if the operator has enough keys to reach the max validators limit + uint64 restKeys = _c.totalKeys - totalKeys; + if (restKeys < allowedValidators) { + allowedValidators = restKeys; + } + } + function getConfig() external view @@ -298,6 +344,26 @@ contract CCCP is return _getConfig(); } + function getModuleConfig(uint24 moduleId) + external + view + returns (bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) + { + return _getModuleConfig(moduleId); + } + + function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64) { + (,,, uint64 defaultBlockGasLimit) = _getConfig(); + (bool isDisabled,, uint64 blockGasLimit) = _getModuleConfig(moduleId); + return isDisabled ? 0 : blockGasLimit == 0 ? defaultBlockGasLimit : blockGasLimit; + } + + function getModuleOperatorMaxValidators(uint24 moduleId) public view returns (uint64) { + (,, uint64 defaultOperatorMaxValidators,) = _getConfig(); + (bool isDisabled, uint64 operatorMaxValidators,) = _getModuleConfig(moduleId); + return isDisabled ? 0 : operatorMaxValidators == 0 ? defaultOperatorMaxValidators : operatorMaxValidators; + } + function getContractVersion() external view returns (uint64) { return _getInitializedVersion(); } @@ -326,7 +392,7 @@ contract CCCP is } /// @notice Update Disable/enable state and operator's max validators for the module - function setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators) + function setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) external onlyRole(COMMITTEE_ROLE) { @@ -334,8 +400,8 @@ contract CCCP is LidoOperatorCache memory _c; _loadLidoModuleData(_c, moduleId); - _setModuleConfig(moduleId, isDisabled, operatorMaxValidators); - emit ModuleConfigUpdated(moduleId, isDisabled, operatorMaxValidators); + _setModuleConfig(moduleId, isDisabled, operatorMaxValidators, blockGasLimit); + emit ModuleConfigUpdated(moduleId, isDisabled, operatorMaxValidators, blockGasLimit); } function _updateConfig( @@ -396,14 +462,11 @@ contract CCCP is } function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { - (,, uint64 defaultOperatorMaxValidators,) = _getConfig(); - (bool isDisabled, uint64 operatorMaxValidators) = _getModuleConfig(moduleId); - if (isDisabled) { + uint64 maxValidators = getModuleOperatorMaxValidators(moduleId); + if (maxValidators == 0) { revert ModuleDisabled(); } uint64 totalKeys = endIndex - startIndex + 1; - uint64 maxValidators = operatorMaxValidators == 0 ? defaultOperatorMaxValidators : operatorMaxValidators; - if (totalKeys > maxValidators) { revert KeysRangeExceedMaxValidators(); } diff --git a/src/interfaces/ICCCP.sol b/src/interfaces/ICCCP.sol index eed3dc9..644cc5b 100644 --- a/src/interfaces/ICCCP.sol +++ b/src/interfaces/ICCCP.sol @@ -14,12 +14,12 @@ interface ICCCP is ICCCPOperatorStatesStorage { uint24 moduleId, uint64 operatorId, address manager, - uint64 newKeyIndexRangeStart, - uint64 newKeyIndexRangeEnd, + uint64 keyIndexStart, + uint64 keyIndexEnd, string calldata rpcURL ) external; function optOut() external; - function updateKeysRange(uint64 newKeyIndexRangeStart, uint64 newKeyIndexRangeEnd) external; + function updateKeysRange(uint64 keyIndexStart, uint64 keyIndexEnd) external; function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external; function getOperator(address manager) @@ -31,4 +31,9 @@ interface ICCCP is ICCCPOperatorStatesStorage { external view returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state); + function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64); + function getModuleOperatorMaxValidators(uint24 moduleId) external view returns (uint64); + function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address); + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool); + function getOperatorAllowedValidators(uint24 moduleId, uint64 operatorId) external view returns (uint64); } diff --git a/src/interfaces/ICCCPConfigStorage.sol b/src/interfaces/ICCCPConfigStorage.sol index a3c402b..4fca02c 100644 --- a/src/interfaces/ICCCPConfigStorage.sol +++ b/src/interfaces/ICCCPConfigStorage.sol @@ -8,15 +8,16 @@ pragma solidity 0.8.28; * @notice Interface for interacting with the storage and control config params. */ interface ICCCPConfigStorage { - /// @notice steaking module parameters /// @dev override global default values, zero values means use default config /// @param isDisabled is module disabled for pre-confs /// operators in disabled modules are automatically considered as opted-out /// @param operatorMaxValidators maximum number of validators per operator + /// @param blockGasLimit block gas limit struct ModuleConfig { bool isDisabled; uint64 operatorMaxValidators; + uint64 blockGasLimit; } /// @notice global config parameters diff --git a/src/lib/CCCPConfigStorage.sol b/src/lib/CCCPConfigStorage.sol index d810787..2e9af5e 100644 --- a/src/lib/CCCPConfigStorage.sol +++ b/src/lib/CCCPConfigStorage.sol @@ -34,9 +34,14 @@ abstract contract CCCPConfigStorage is ICCCPConfigStorage { }); } - function _setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators) internal { - _getConfigStorage()._modules[moduleId] = - ModuleConfig({isDisabled: isDisabled, operatorMaxValidators: operatorMaxValidators}); + function _setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) + internal + { + _getConfigStorage()._modules[moduleId] = ModuleConfig({ + isDisabled: isDisabled, + operatorMaxValidators: operatorMaxValidators, + blockGasLimit: blockGasLimit + }); } function _getConfig() @@ -58,9 +63,13 @@ abstract contract CCCPConfigStorage is ICCCPConfigStorage { ); } - function _getModuleConfig(uint24 moduleId) internal view returns (bool isDisabled, uint64 operatorMaxValidators) { + function _getModuleConfig(uint24 moduleId) + internal + view + returns (bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) + { ModuleConfig memory moduleConfig = _getConfigStorage()._modules[moduleId]; - return (moduleConfig.isDisabled, moduleConfig.operatorMaxValidators); + return (moduleConfig.isDisabled, moduleConfig.operatorMaxValidators, moduleConfig.blockGasLimit); } /** From aaa0e97551c49b61bae3f5af0a105c69576d122b Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 29 Jan 2025 03:13:20 +0100 Subject: [PATCH 28/33] test: new tests, refactor --- test/CCCP.t.sol | 226 ++++++++++++++---------------------- test/CCCPConfig.t.sol | 182 +++++++++++++++++++++++++++++ test/CCCPInit.t.sol | 60 ++++++++++ test/DataStorage.t.sol | 42 ------- test/helpers/CCCPCommon.sol | 60 ++++++++++ test/helpers/Utilities.sol | 7 ++ 6 files changed, 393 insertions(+), 184 deletions(-) create mode 100644 test/CCCPConfig.t.sol create mode 100644 test/CCCPInit.t.sol delete mode 100644 test/DataStorage.t.sol create mode 100644 test/helpers/CCCPCommon.sol diff --git a/test/CCCP.t.sol b/test/CCCP.t.sol index ccd5dc0..0764907 100644 --- a/test/CCCP.t.sol +++ b/test/CCCP.t.sol @@ -1,124 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.28; -import {Test, console} from "forge-std/Test.sol"; -import {CCCPConfigStorage, ICCCPConfigStorage} from "../src/lib/CCCPConfigStorage.sol"; -import {CCCPOperatorStatesStorage, ICCCPOperatorStatesStorage} from "../src/lib/CCCPOperatorStatesStorage.sol"; +import {CCCPCommon} from "./helpers/CCCPCommon.sol"; + import {CCCP} from "../src/CCCP.sol"; import {CCCPMock} from "./helpers/mocks/CCCPMock.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {Fixtures} from "./helpers/Fixtures.sol"; -import {Utilities} from "./helpers/Utilities.sol"; -import {LidoLocatorMock} from "test/helpers/mocks/LidoLocatorMock.sol"; -import {StakingModuleMock} from "test/helpers/mocks/StakingModuleMock.sol"; -import {StakingRouterMock, IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; -import {CuratedModuleMock} from "test/helpers/mocks/CuratedModuleMock.sol"; -import {CSModuleMock} from "test/helpers/mocks/CSModuleMock.sol"; - -abstract contract CCCPFixtures is Test, Fixtures, Utilities { - LidoLocatorMock public locator; - StakingRouterMock public sr; - CuratedModuleMock public nor; - CSModuleMock public csm; - - // address internal admin; - address internal stranger1; - address internal stranger2; - address internal noCsm1; - address internal noCsm1Manager; - address internal noCurated1; - address internal noCurated1Manager; - address internal committee; - - // uint64 optInMinDurationBlocks = 100; - // uint64 optOutDelayDurationBlocks = 200; - // uint64 defaultOperatorMaxValidators = 100; - // uint64 defaultBlockGasLimit = 1000000; - - // function createNo(uint256 modId, address rewAddr) internal returns (uint256) { - // StakingModuleMock m = StakingModuleMock(sr.getStakingModule(modId).stakingModuleAddress); - // return createNo(csm, rewAddr, 1); - // } - - function createNo(StakingModuleMock m, address rewAddr, uint32 keysCount) internal returns (uint64) { - m.addNo(true, rewAddr, keysCount); - return uint64(m.getNodeOperatorsCount() - 1); - } - - function updateNoKeys(StakingModuleMock m, uint256 noId, uint32 keysCount) internal { - m.updNoKeys(noId, keysCount); - } - - function updateNoActive(StakingModuleMock m, uint256 noId, bool active) internal { - m.updNoActive(noId, active); - } -} - -contract CCCPCommon is CCCPFixtures { - function setUp() public virtual { - committee = nextAddress("COMMITTEE"); - stranger1 = nextAddress("STRANGER1"); - stranger2 = nextAddress("STRANGER2"); - - (locator, sr, nor, csm) = initLidoMock(); - } -} - -contract CCCPInitialize is CCCPCommon { - function test_constructor() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - assertEq(cccp.__test__getCSModuleType(), "csm-type"); - assertEq(address(cccp.LIDO_LOCATOR()), address(locator)); - assertEq(cccp.getContractVersion(), type(uint64).max); - } - - function test_constructor_RevertWhen_ZeroLocator() public { - vm.expectRevert(CCCP.ZeroLocatorAddress.selector); - new CCCPMock({lidoLocator: address(0), csModuleType: "csm-type"}); - } - - function test_constructor_RevertWhen_InitOnImpl() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - - vm.expectRevert(Initializable.InvalidInitialization.selector); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 0, - optOutDelayDurationBlocks: 0, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - } - - function test_initialize() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - _enableInitializers(address(cccp)); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 32, - optOutDelayDurationBlocks: 64, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - - ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) = cccp.getConfig(); - - assertEq(optInMinDurationBlocks, 32); - assertEq(optOutDelayDurationBlocks, 64); - assertEq(defaultOperatorMaxValidators, 10); - assertEq(defaultBlockGasLimit, 1000000); - assertEq(cccp.getContractVersion(), 1); - assertFalse(cccp.paused()); - } -} +import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; +import {ICCCPOperatorStatesStorage} from "../src/interfaces/ICCCPOperatorStatesStorage.sol"; contract CCCPOptIn is CCCPCommon { - CCCPMock public cccp; + CCCP public cccp; uint64 public noCsm1Id; uint64 public noCurated1Id; @@ -126,6 +17,9 @@ contract CCCPOptIn is CCCPCommon { uint24 public constant norId = 1; uint24 public constant csmId = 2; + string public constant rpcUrl1 = "some-url-1"; + string public constant rpcUrl2 = "some-url-2"; + function setUp() public virtual override { super.setUp(); @@ -152,17 +46,37 @@ contract CCCPOptIn is CCCPCommon { // opt in on behalf of noCsm1 vm.broadcast(noCsm1); vm.expectEmit(true, true, true, false, address(cccp)); + emit CCCP.OperatorManagerUpdated(csmId, noCsm1Id, noCsm1Manager); + vm.expectEmit(true, true, true, false, address(cccp)); emit CCCP.KeysRangeUpdated(csmId, noCsm1Id, 2, 4); vm.expectEmit(true, true, true, false, address(cccp)); + emit CCCP.RPCUrlUpdated(csmId, noCsm1Id, rpcUrl1); + vm.expectEmit(true, true, true, false, address(cccp)); emit CCCP.OptInSucceeded(csmId, noCsm1Id, noCsm1Manager); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, - rpcURL: "" + keyIndexStart: 2, + keyIndexEnd: 4, + rpcURL: rpcUrl1 + }); + + assertEq(cccp.getOperatorIsEnabledForPreconf(csmId, noCsm1Id), true); + assertEq(cccp.getOperatorManager(csmId, noCsm1Id), noCsm1Manager); + } + + function test_GetOperatorByManager() public { + // opt in on behalf of noCsm1 + vm.broadcast(noCsm1); + cccp.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + keyIndexStart: 2, + keyIndexEnd: 4, + rpcURL: rpcUrl1 }); (uint24 moduleId, uint64 operatorId, bool isEnabled, CCCP.OperatorState memory state) = @@ -178,7 +92,35 @@ contract CCCPOptIn is CCCPCommon { assertEq(state.optInOutState.optInBlock, block.number); assertEq(state.optInOutState.optOutBlock, 0); assertEq(state.optInOutState.isOptOutForced, false); - assertEq(state.extraData.rpcURL, ""); + assertEq(state.extraData.rpcURL, rpcUrl1); + } + + function test_GetOperatorById() public { + // opt in on behalf of noCsm1 + vm.broadcast(noCsm1); + cccp.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + keyIndexStart: 2, + keyIndexEnd: 4, + rpcURL: rpcUrl1 + }); + + (uint24 moduleId, uint64 operatorId, bool isEnabled, CCCP.OperatorState memory state) = + cccp.getOperator(csmId, noCsm1Id); + + assertEq(moduleId, csmId); + assertEq(operatorId, noCsm1Id); + assertEq(isEnabled, true); + + assertEq(state.keysRange.indexStart, 2); + assertEq(state.keysRange.indexEnd, 4); + assertEq(state.manager, noCsm1Manager); + assertEq(state.optInOutState.optInBlock, block.number); + assertEq(state.optInOutState.optOutBlock, 0); + assertEq(state.optInOutState.isOptOutForced, false); + assertEq(state.extraData.rpcURL, rpcUrl1); } function test_OptIn_RevertWhen_ZeroManagerAddress() public { @@ -187,8 +129,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: address(0), - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -200,8 +142,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -213,8 +155,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: 999, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -229,8 +171,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -242,8 +184,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); @@ -254,8 +196,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -267,8 +209,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); @@ -285,8 +227,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -298,8 +240,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: norId, operatorId: noCurated1Id, manager: noCurated1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); @@ -310,8 +252,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCurated1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 4, + keyIndexStart: 2, + keyIndexEnd: 4, rpcURL: "" }); } @@ -324,8 +266,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 4, - newKeyIndexRangeEnd: 2, + keyIndexStart: 4, + keyIndexEnd: 2, rpcURL: "" }); } @@ -338,8 +280,8 @@ contract CCCPOptIn is CCCPCommon { moduleId: csmId, operatorId: noCsm1Id, manager: noCsm1Manager, - newKeyIndexRangeStart: 2, - newKeyIndexRangeEnd: 100, + keyIndexStart: 2, + keyIndexEnd: 100, rpcURL: "" }); } diff --git a/test/CCCPConfig.t.sol b/test/CCCPConfig.t.sol new file mode 100644 index 0000000..c2e02eb --- /dev/null +++ b/test/CCCPConfig.t.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCCPCommon} from "./helpers/CCCPCommon.sol"; + +import {CCCP} from "../src/CCCP.sol"; +import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; +import {ICCCPConfigStorage} from "../src/interfaces/ICCCPConfigStorage.sol"; + +contract CCCPConfig is CCCPCommon { + CCCP public cccp; + uint24 public moduleId = 1; + uint64 public maxValidators = 1111; + uint64 public blockGasLimit = 1111111; + + uint64 optInMinDurationBlocks = 32; + uint64 optOutDelayDurationBlocks = 64; + uint64 defaultOperatorMaxValidators = 100; + uint64 defaultBlockGasLimit = 1000000; + + function setUp() public virtual override { + super.setUp(); + + cccp = new CCCP(address(locator), "community-onchain-v1"); + _enableInitializers(address(cccp)); + cccp.initialize({ + committeeAddress: committee, + optInMinDurationBlocks: optInMinDurationBlocks, + optOutDelayDurationBlocks: optOutDelayDurationBlocks, + defaultOperatorMaxValidators: defaultOperatorMaxValidators, + defaultBlockGasLimit: defaultBlockGasLimit + }); + } + + function test_GetInitialConfig() public view { + ( + uint64 newOptInMinDurationBlocks, + uint64 newOptOutDelayDurationBlocks, + uint64 newDefaultOperatorMaxValidators, + uint64 newDefaultBlockGasLimit + ) = cccp.getConfig(); + + assertEq(newOptInMinDurationBlocks, optInMinDurationBlocks); + assertEq(newOptOutDelayDurationBlocks, optOutDelayDurationBlocks); + assertEq(newDefaultOperatorMaxValidators, defaultOperatorMaxValidators); + assertEq(newDefaultBlockGasLimit, defaultBlockGasLimit); + } + + function test_SetConfig() public { + vm.prank(committee); + cccp.setConfig(10, 20, 30, 40); + ( + uint64 newOptInMinDurationBlocks, + uint64 newOptOutDelayDurationBlocks, + uint64 newDefaultOperatorMaxValidators, + uint64 newDefaultBlockGasLimit + ) = cccp.getConfig(); + + assertEq(newOptInMinDurationBlocks, 10); + assertEq(newOptOutDelayDurationBlocks, 20); + assertEq(newDefaultOperatorMaxValidators, 30); + assertEq(newDefaultBlockGasLimit, 40); + } + + function test_SetConfig_RevertWhen_CallerNotCommittee() public { + bytes32 role = cccp.COMMITTEE_ROLE(); + + vm.prank(stranger1); + expectRoleRevert(stranger1, role); + cccp.setConfig( + optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit + ); + } + + function test_SetConfig_RevertWhen_ZeroDefaultOperatorMaxValidators() public { + vm.prank(committee); + vm.expectRevert(ICCCPConfigStorage.ZeroDefaultOperatorMaxValidators.selector); + cccp.setConfig(optInMinDurationBlocks, optOutDelayDurationBlocks, 0, defaultBlockGasLimit); + } + + function test_SetConfig_RevertWhen_ZeroDefaultBlockGasLimit() public { + vm.prank(committee); + vm.expectRevert(ICCCPConfigStorage.ZeroDefaultBlockGasLimit.selector); + cccp.setConfig(optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, 0); + } + + function test_SetModuleConfig() public { + vm.prank(committee); + cccp.setModuleConfig(moduleId, true, maxValidators, blockGasLimit); + (bool newIsDisabled, uint64 newMaxValidators, uint64 newblockGasLimit) = cccp.getModuleConfig(moduleId); + + assertEq(newIsDisabled, true); + assertEq(newMaxValidators, maxValidators); + assertEq(newblockGasLimit, blockGasLimit); + } + + function test_SetModuleConfig_RevertWhen_CallerNotCommittee() public { + bytes32 role = cccp.COMMITTEE_ROLE(); + + vm.prank(stranger1); + expectRoleRevert(stranger1, role); + cccp.setModuleConfig(moduleId, true, maxValidators, blockGasLimit); + } + + function test_SetModuleConfig_RevertWhen_WrongModuleId() public { + vm.prank(committee); + vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); + cccp.setModuleConfig(999, true, maxValidators, blockGasLimit); + } + + function test_ModuleConfig_OverrideBlockGasLimit() public { + // module config not yet set + assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + + vm.prank(committee); + cccp.setModuleConfig(moduleId, false, 0, blockGasLimit); + assertEq(cccp.getModuleBlockGasLimit(moduleId), blockGasLimit); + + // set block gas limit to 0 + vm.prank(committee); + cccp.setModuleConfig(moduleId, false, 0, 0); + assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + } + + function test_ModuleConfig_ZeroBlockGasLimitWhenModuleDisabled() public { + // module config not yet set + assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + + // disable module + vm.prank(committee); + cccp.setModuleConfig(moduleId, true, 0, 0); + assertEq(cccp.getModuleBlockGasLimit(moduleId), 0); + + // enable module + vm.prank(committee); + cccp.setModuleConfig(moduleId, false, 0, 0); + assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + } + + function test_ModuleConfig_OverrideOperatorMaxValidators() public { + // module config not yet set + assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); + + vm.prank(committee); + cccp.setModuleConfig(moduleId, false, maxValidators, 0); + assertEq(cccp.getModuleOperatorMaxValidators(moduleId), maxValidators); + + // set block gas limit to 0 + vm.prank(committee); + cccp.setModuleConfig(moduleId, false, 0, 0); + assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); + } + + function test_ModuleConfig_ZeroOperatorMaxValidatorsWhenModuleDisabled() public { + // module config not yet set + assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); + + // disable module + vm.prank(committee); + cccp.setModuleConfig(moduleId, true, 0, 0); + assertEq(cccp.getModuleOperatorMaxValidators(moduleId), 0); + + // enable module + vm.prank(committee); + cccp.setModuleConfig(moduleId, false, 0, 0); + assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); + } + + // function test_GetInitialConfig() public { + // ( + // uint64 newOptInMinDurationBlocks, + // uint64 newOptOutDelayDurationBlocks, + // uint64 newDefaultOperatorMaxValidators, + // uint64 newDefaultBlockGasLimit + // ) = cccp.getConfig(); + + // assertEq(newOptInMinDurationBlocks, 32); + // assertEq(newOptOutDelayDurationBlocks, 64); + // assertEq(newDefaultOperatorMaxValidators, 100); + // assertEq(newDefaultBlockGasLimit, 1000000); + // } +} diff --git a/test/CCCPInit.t.sol b/test/CCCPInit.t.sol new file mode 100644 index 0000000..b2693ff --- /dev/null +++ b/test/CCCPInit.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCCP} from "../src/CCCP.sol"; +import {CCCPMock} from "./helpers/mocks/CCCPMock.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {CCCPCommon} from "./helpers/CCCPCommon.sol"; + +contract CCCPInitialize is CCCPCommon { + function test_constructor() public { + CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); + assertEq(cccp.__test__getCSModuleType(), "csm-type"); + assertEq(address(cccp.LIDO_LOCATOR()), address(locator)); + assertEq(cccp.getContractVersion(), type(uint64).max); + } + + function test_constructor_RevertWhen_ZeroLocator() public { + vm.expectRevert(CCCP.ZeroLocatorAddress.selector); + new CCCP({lidoLocator: address(0), csModuleType: "csm-type"}); + } + + function test_constructor_RevertWhen_InitOnImpl() public { + CCCP cccp = new CCCP({lidoLocator: address(locator), csModuleType: "csm-type"}); + + vm.expectRevert(Initializable.InvalidInitialization.selector); + cccp.initialize({ + committeeAddress: committee, + optInMinDurationBlocks: 0, + optOutDelayDurationBlocks: 0, + defaultOperatorMaxValidators: 10, + defaultBlockGasLimit: 1000000 + }); + } + + function test_initialize() public { + CCCP cccp = new CCCP({lidoLocator: address(locator), csModuleType: "csm-type"}); + _enableInitializers(address(cccp)); + cccp.initialize({ + committeeAddress: committee, + optInMinDurationBlocks: 32, + optOutDelayDurationBlocks: 64, + defaultOperatorMaxValidators: 10, + defaultBlockGasLimit: 1000000 + }); + + ( + uint64 optInMinDurationBlocks, + uint64 optOutDelayDurationBlocks, + uint64 defaultOperatorMaxValidators, + uint64 defaultBlockGasLimit + ) = cccp.getConfig(); + + assertEq(optInMinDurationBlocks, 32); + assertEq(optOutDelayDurationBlocks, 64); + assertEq(defaultOperatorMaxValidators, 10); + assertEq(defaultBlockGasLimit, 1000000); + assertEq(cccp.getContractVersion(), 1); + assertFalse(cccp.paused()); + } +} diff --git a/test/DataStorage.t.sol b/test/DataStorage.t.sol deleted file mode 100644 index 161bedc..0000000 --- a/test/DataStorage.t.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.28; - -import {Test} from "forge-std/Test.sol"; -import {CCCPConfigStorage} from "../src/lib/CCCPConfigStorage.sol"; - -contract ModulesDataStorageTest is Test, CCCPConfigStorage { - function setUp() public {} - - function test_ModuleConfig() public { - uint24 moduleId = 111; - uint64 maxValidators = 1111; - - _setModuleConfig(moduleId, maxValidators, true); - (uint64 newMaxValidators, bool newIsDisabled) = _getModuleConfig(moduleId); - - assertEq(newMaxValidators, maxValidators); - assertEq(newIsDisabled, true); - } - - function test_Config() public { - uint64 optInMinDurationBlocks = 123; - uint64 optOutDelayDurationBlocks = 234; - uint64 defaultOperatorMaxValidators = 100; - uint64 defaultBlockGasLimit = 1000000; - - _setConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - ( - uint64 newOptInMinDurationBlocks, - uint64 newOptOutDelayDurationBlocks, - uint64 newDefaultOperatorMaxValidators, - uint64 newDefaultBlockGasLimit - ) = _getConfig(); - - assertEq(newOptInMinDurationBlocks, optInMinDurationBlocks); - assertEq(newOptOutDelayDurationBlocks, optOutDelayDurationBlocks); - assertEq(newDefaultOperatorMaxValidators, defaultOperatorMaxValidators); - assertEq(newDefaultBlockGasLimit, defaultBlockGasLimit); - } -} diff --git a/test/helpers/CCCPCommon.sol b/test/helpers/CCCPCommon.sol new file mode 100644 index 0000000..3c6444c --- /dev/null +++ b/test/helpers/CCCPCommon.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {Test} from "forge-std/Test.sol"; +import {Fixtures} from "./Fixtures.sol"; +import {Utilities} from "./Utilities.sol"; +import {LidoLocatorMock} from "test/helpers/mocks/LidoLocatorMock.sol"; +import {StakingModuleMock} from "test/helpers/mocks/StakingModuleMock.sol"; +import {StakingRouterMock} from "test/helpers/mocks/StakingRouterMock.sol"; +import {CuratedModuleMock} from "test/helpers/mocks/CuratedModuleMock.sol"; +import {CSModuleMock} from "test/helpers/mocks/CSModuleMock.sol"; + +abstract contract CCCPFixtures is Test, Fixtures, Utilities { + LidoLocatorMock public locator; + StakingRouterMock public sr; + CuratedModuleMock public nor; + CSModuleMock public csm; + + // address internal admin; + address internal stranger1; + address internal stranger2; + address internal noCsm1; + address internal noCsm1Manager; + address internal noCurated1; + address internal noCurated1Manager; + address internal committee; + + // uint64 optInMinDurationBlocks = 100; + // uint64 optOutDelayDurationBlocks = 200; + // uint64 defaultOperatorMaxValidators = 100; + // uint64 defaultBlockGasLimit = 1000000; + + // function createNo(uint256 modId, address rewAddr) internal returns (uint256) { + // StakingModuleMock m = StakingModuleMock(sr.getStakingModule(modId).stakingModuleAddress); + // return createNo(csm, rewAddr, 1); + // } + + function createNo(StakingModuleMock m, address rewAddr, uint32 keysCount) internal returns (uint64) { + m.addNo(true, rewAddr, keysCount); + return uint64(m.getNodeOperatorsCount() - 1); + } + + function updateNoKeys(StakingModuleMock m, uint256 noId, uint32 keysCount) internal { + m.updNoKeys(noId, keysCount); + } + + function updateNoActive(StakingModuleMock m, uint256 noId, bool active) internal { + m.updNoActive(noId, active); + } +} + +contract CCCPCommon is CCCPFixtures { + function setUp() public virtual { + committee = nextAddress("COMMITTEE"); + stranger1 = nextAddress("STRANGER1"); + stranger2 = nextAddress("STRANGER2"); + + (locator, sr, nor, csm) = initLidoMock(); + } +} diff --git a/test/helpers/Utilities.sol b/test/helpers/Utilities.sol index d305f01..76bf1a7 100644 --- a/test/helpers/Utilities.sol +++ b/test/helpers/Utilities.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.28; import {CommonBase, Vm} from "forge-std/Base.sol"; +import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; /// @author madlabman contract Utilities is CommonBase { @@ -20,4 +21,10 @@ contract Utilities is CommonBase { vm.label(a, label); return a; } + + function expectRoleRevert(address account, bytes32 neededRole) internal { + vm.expectRevert( + abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, account, neededRole) + ); + } } From 1365a5fbdc4b4fd7630df79e32b17f9ca0454c60 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 29 Jan 2025 03:20:43 +0100 Subject: [PATCH 29/33] fix: deployed holesky artifacts --- Justfile | 2 +- artifacts/holesky/deploy-holesky.json | 2 +- artifacts/holesky/transactions.json | 109 +++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/Justfile b/Justfile index b4bfebf..62a2a5c 100644 --- a/Justfile +++ b/Justfile @@ -146,7 +146,7 @@ upgrade-prod *args: just _warn "The current `tput bold`chain={{chain}}`tput sgr0` with the following rpc url: $RPC_URL" ARTIFACTS_DIR=./artifacts/latest/ just _upgrade-prod-confirm {{args}} - cp ./broadcast/{{upgrade_script_path}}.s.sol/`cast chain-id --rpc-url=$RPC_URL`/run-latest.json \ + cp ./broadcast/{{upgrade_script_name}}.s.sol/`cast chain-id --rpc-url=$RPC_URL`/run-latest.json \ ./artifacts/latest/transactions.json [confirm("You are about to broadcast upgrade transactions to the network. Are you sure?")] diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json index 0db0925..6bda25f 100644 --- a/artifacts/holesky/deploy-holesky.json +++ b/artifacts/holesky/deploy-holesky.json @@ -1,6 +1,6 @@ { "CCCP": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", - "CCCPImpl": "0x0442fB2C8607D04D6Ba496Ea8990dA7bd7Ec25fc", + "CCCPImpl": "0x82CFAE30820b4249acD6315C90A4D0a9fEF5A99A", "ChainId": 17000, "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json index 47aacae..fd75138 100644 --- a/artifacts/holesky/transactions.json +++ b/artifacts/holesky/transactions.json @@ -126,6 +126,48 @@ }, "additionalContracts": [], "isFixedGasLimit": false + }, + { + "hash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", + "transactionType": "CREATE", + "contractName": "CCCP", + "contractAddress": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x3c4001", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b5060405161385b38038061385b83398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161354f61030c5f395f61211801525f818161058601526126fe01525f818161174b015281816117f00152818161184b01528181611a8d015261230101525f818161159e01528181611c4801528181611d060152611f30015261354f5ff3fe608060405234801561000f575f5ffd5b5060043610610213575f3560e01c8063893d004e1161011f578063b70ddca0116100a9578063cede01ca11610079578063cede01ca14610540578063d4eec5a614610553578063d547741f1461055b578063d6dc6ae71461056e578063dbba4b4814610581575f5ffd5b8063b70ddca0146104cc578063ba6bc8d5146104df578063c3f909d4146104f2578063ca15c8731461052d575f5ffd5b8063a217fddf116100ef578063a217fddf1461046b578063a264756914610472578063a3246ad314610485578063a3714e07146104a5578063ad32563d146104b8575f5ffd5b8063893d004e1461042a5780638aa104351461043d5780639010d07c1461044557806391d1485414610458575f5ffd5b8063389ed267116101a05780635865c60c116101705780635865c60c146103aa57806358c5be18146103cd5780635c975abb146103f857806370bc09f61461040f5780638456cb5914610422575f5ffd5b8063389ed2671461033d5780633c074eb3146103645780633dcd5765146103775780633f4ba83a146103a2575f5ffd5b8063248a9ca3116101e6578063248a9ca3146102a15780632de03aa1146102dd5780632de0920c146103045780632f2ff15d1461031757806336568abe1461032a575f5ffd5b806301ffc9a71461021757806313e094531461023f5780631a6357e5146102545780631d8394bb14610267575b5f5ffd5b61022a6102253660046129b5565b6105a8565b60405190151581526020015b60405180910390f35b61025261024d366004612a04565b6105d2565b005b61022a610262366004612a81565b610795565b61027a610275366004612ab8565b6107db565b6040805193151584526001600160401b039283166020850152911690820152606001610236565b6102cf6102af366004612ad3565b5f9081525f5160206135035f395f51905f52602052604090206001015490565b604051908152602001610236565b6102cf7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610252610312366004612aea565b6107f4565b610252610325366004612bb2565b610a84565b610252610338366004612bb2565b610aba565b6102cf7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b610252610372366004612a81565b610af2565b61038a610385366004612a81565b610b7d565b6040516001600160401b039091168152602001610236565b610252610c8e565b6103bd6103b8366004612bd5565b610cc3565b6040516102369493929190612c28565b6103e06103db366004612a81565b610cf3565b6040516001600160a01b039091168152602001610236565b5f5160206135235f395f51905f525460ff1661022a565b61025261041d366004612ce5565b610d12565b610252610d3c565b610252610438366004612d3e565b610d6e565b61038a610e5c565b6103e0610453366004612d86565b610e93565b61022a610466366004612bb2565b610eb8565b6102cf5f81565b610252610480366004612db3565b610eee565b610498610493366004612ad3565b610f7e565b6040516102369190612de1565b6103bd6104b3366004612a81565b610fae565b6102cf5f5160206134e35f395f51905f5281565b61038a6104da366004612ab8565b610fe0565b6102526104ed366004612e2c565b61102a565b6104fa61113a565b604080516001600160401b0395861681529385166020850152918416918301919091529091166060820152608001610236565b6102cf61053b366004612ad3565b611154565b61038a61054e366004612ab8565b611178565b6102526111b1565b610252610569366004612bb2565b61128c565b61025261057c366004612a81565b6112bc565b6103e07f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806105cc57506105cc82611386565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156106165750825b90505f826001600160401b031660011480156106315750303b155b90508115801561063f575080155b1561065d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561068757845460ff60401b1916600160401b1785555b6001600160a01b038a166106ae576040516319467aa760e11b815260040160405180910390fd5b6106b66113ba565b6106be6113cc565b6106c85f8b6113d4565b506106e05f5160206134e35f395f51905f528b6113d4565b5061070b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b6113d4565b506107367f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b6113d4565b5061074389898989611416565b831561078957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b5f61079e6128fb565b6107a981858561147b565b5f6107b4858561148f565b90505f6107c0826114d8565b90506107d181878560a0015161153e565b9695505050505050565b5f5f5f6107e784611593565b9250925092509193909250565b6107fc611609565b6001600160a01b03851661082357604051634434240960e11b815260040160405180910390fd5b61082b6128fb565b61083681898961147b565b80606001516001600160a01b0316336001600160a01b03161461086c576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161088e57604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6108b0826114d8565b90505f6108bc82611639565b8051909150156108df576040516342ee68b560e01b815260040160405180910390fd5b8060400151610901576040516328a5715760e21b815260040160405180910390fd5b61090b838a611739565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a3610991836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f1515815250611877565b61099d84848a8a6118d8565b604080516020601f8801819004810282018301835281018781526109e09286929182918b908b90819085018382808284375f920191909152505050915250611952565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef8888604051610a21929190612e48565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206135035f395f51905f526020526040902060010154610aaa81611971565b610ab483836113d4565b50505050565b6001600160a01b0381163314610ae35760405163334bd91960e11b815260040160405180910390fd5b610aed828261197b565b505050565b5f5160206134e35f395f51905f52610b0981611971565b5f610b14848461148f565b90505f610b20826114d8565b9050806040015115610b3c575f6040820152610b3c8282611877565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b5f5f610b8884610fe0565b9050806001600160401b03165f03610ba3575f9150506105cc565b610bab6128fb565b610bb681868661147b565b8060a00151610bc9575f925050506105cc565b5f610bd4868661148f565b90505f610be0826119b4565b6001600160a01b031603610bf9575f93505050506105cc565b5f610c03826119ce565b90505f815f01518260200151610c199190612e8a565b610c24906001612ea9565b9050846001600160401b0316816001600160401b031610610c4c575f955050505050506105cc565b80850395505f818560800151610c629190612e8a565b9050866001600160401b0316816001600160401b03161015610c82578096505b50505050505092915050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610cb881611971565b610cc0611a1c565b50565b5f5f5f610cce61292f565b5f610cd886611a7b565b9050610ce381611adb565b9450945094509450509193509193565b5f5f610cff848461148f565b9050610d0a816119b4565b949350505050565b5f5160206134e35f395f51905f52610d2981611971565b610d3585858585611416565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610d6681611971565b610cc0611b2b565b610d76611609565b6001600160a01b038116610d9d57604051634434240960e11b815260040160405180910390fd5b610da56128fb565b610db081858561147b565b80606001516001600160a01b0316336001600160a01b031614610de6576040516349ad9b0960e11b815260040160405180910390fd5b610e0762ffffff60401b604086901b166001600160401b0385161783611739565b6040516001600160a01b03831681526001600160401b0384169062ffffff8616907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc906020015b60405180910390a350505050565b5f610e8e7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206134c35f395f51905f52602081905260408220610d0a9084611b73565b5f9182525f5160206135035f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f5160206134e35f395f51905f52610f0581611971565b610f0d6128fb565b610f178187611b7e565b610f2386868686611c14565b6040805186151581526001600160401b038681166020830152851681830152905162ffffff8816917f37874944470fa7fc10b8f30fa6686dc6b2f96522f58cddd220c8a5856400652b919081900360600190a2505050505050565b5f8181525f5160206134c35f395f51905f526020819052604090912060609190610fa790611cec565b9392505050565b5f5f5f610fb961292f565b5f610fc4878761148f565b9050610fcf81611adb565b929a91995097509095509350505050565b5f5f610fea611cf8565b50925050505f5f610ffa85611593565b50915091508161101f576001600160401b038116156110195780611021565b82611021565b5f5b95945050505050565b611032611609565b5f61103c33611a7b565b90505f61105061104b836114d8565b611639565b805190915061107257604051631a8660cb60e01b815260040160405180910390fd5b5f61107c836119ce565b9050805f01516001600160401b0316856001600160401b031611806110b6575080602001516001600160401b0316846001600160401b0316105b806110ed575080516001600160401b0386811691161480156110ed575080602001516001600160401b0316846001600160401b0316145b1561110b5760405163c7f544bb60e01b815260040160405180910390fd5b6111136128fb565b604084901c8461112483838361147b565b61113083878a8a6118d8565b5050505050505050565b5f5f5f5f611146611cf8565b935093509350935090919293565b5f8181525f5160206134c35f395f51905f52602081905260408220610fa790611d6e565b5f5f611182611cf8565b93505050505f5f61119285611593565b92505091508161101f576001600160401b038116156110195780611021565b6111b9611609565b5f6111c333611a7b565b90505f6111cf826114d8565b90505f6111db82611639565b80519091506111fd57604051631a8660cb60e01b815260040160405180910390fd5b806060015161121f57604051630eedec1160e31b815260040160405180910390fd5b6001600160401b03431660208301526112388383611877565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206135035f395f51905f5260205260409020600101546112b281611971565b610ab4838361197b565b5f5160206134e35f395f51905f526112d381611971565b5f6112de848461148f565b90505f6112ea826114d8565b90505f6112f682611639565b805190915061131857604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b0343166020830152600160408301526113388383611877565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f6001600160e01b03198216637965db0b60e01b14806105cc57506301ffc9a760e01b6001600160e01b03198316146105cc565b6113c2611d77565b6113ca611dc0565b565b6113ca611d77565b5f5f5160206134c35f395f51905f52816113ee8585611de0565b90508015610d0a575f85815260208390526040902061140d9085611e81565b50949350505050565b61142284848484611e95565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b6114858383611b7e565b610aed8382611fd2565b6001600160401b038116604083901b62ffffff60401b16175f6114b1826119b4565b6001600160a01b0316036105cc576040516325ec6c1f60e01b815260040160405180910390fd5b604080516060810182525f80825260208201819052918101919091526114fd826122fb565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b5f5f61154985611639565b90505f61155585611593565b505082519091508015611566575080155b801561156f5750835b80156107d157505f5160206135235f395f51905f525460ff16159695505050505050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251606081018452905460ff811615158083526001600160401b0361010083048116948401859052600160481b909204909116919093018190529192909190565b5f5160206135235f395f51905f525460ff16156113ca5760405163d93c066560e01b815260040160405180910390fd5b604080516080810182525f808252602082018190529181018290526060810191909152435f80611667611cf8565b5050915091505f5f86602001516001600160401b03161180156116aa5750836001600160401b031682876020015161169f9190612ea9565b6001600160401b0316105b90505f5f875f01516001600160401b03161180156116c6575081155b90505f811580156116d957508760400151155b90505f828015611707575088516001600160401b038816906116fc908890612ea9565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906117825750828114155b156117a05760405163324e8e5f60e01b815260040160405180910390fd5b5f6117aa846122fb565b80549091506001600160a01b031680158015906117d95750836001600160a01b0316816001600160a01b031614155b15611819576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b80611881836122fb565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b6118e78460800151838361232a565b83516118f49083836123ae565b6118ff83838361242e565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c9101610e4e565b8061195c836122fb565b8151600391909101908190610d359082612f58565b610cc0813361249c565b5f5f5160206134c35f395f51905f528161199585856124dd565b90508015610d0a575f85815260208390526040902061140d9085612556565b5f6119be826122fb565b546001600160a01b031692915050565b604080518082019091525f80825260208201526119ea826122fb565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b611a2461256a565b5f5160206135235f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604081205490819003611ad65760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f611ae661292f565b611aee6128fb565b604086901c9450859350611b0381868661147b565b611b0c86612599565b9150611b218260400151868360a0015161153e565b9250509193509193565b611b33611609565b5f5160206135235f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611a5d565b5f610fa783836126d5565b5f611b876126fb565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611bcf573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611bf69190810190613129565b62ffffff9092168352506020908101516001600160a01b0316910152565b60405180606001604052808415158152602001836001600160401b03168152602001826001600160401b0316815250611c6a7f000000000000000000000000000000000000000000000000000000000000000090565b62ffffff959095165f90815260209586526040908190208251815497840151939092015168ffffffffffffffffff1990971691151568ffffffffffffffff001916919091176101006001600160401b03938416021770ffffffffffffffff0000000000000000001916600160481b929096169190910294909417909355505050565b60605f610fa78361277c565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6105cc825490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166113ca57604051631afcd79f60e31b815260040160405180910390fd5b611dc8611d77565b5f5160206135235f395f51905f52805460ff19169055565b5f5f5160206135035f395f51905f52611df98484610eb8565b611e78575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611e2e3390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506105cc565b5f9150506105cc565b5f610fa7836001600160a01b0384166127d5565b816001600160401b03165f03611ebe5760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611ee657604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b0316815250611f527f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f03611ff857604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612039573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061205d919061325a565b9050806001600160401b0316826001600160401b0316106120915760405163b9aa612760e01b815260040160405180910390fd5b8183604001906001600160401b031690816001600160401b0316815250505f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120f0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612114919061325a565b90507f00000000000000000000000000000000000000000000000000000000000000008103612252576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa15801561218d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121b1919061327c565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa158015612206573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061222a91906132aa565b6101808101516001600160a01b031660608801525163ffffffff16608087015250610ab49050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa1580156122a7573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526122ce91908101906133d3565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b0316111561235d5760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b03161015806123905750826001600160401b0316826001600160401b031610155b15610aed57604051637ba485c560e01b815260040160405180910390fd5b5f6123b884610fe0565b9050806001600160401b03165f036123e357604051630dbfe5fd60e31b815260040160405180910390fd5b5f6123ee8484612e8a565b6123f9906001612ea9565b9050816001600160401b0316816001600160401b03161115610d3557604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061245e846122fb565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6124a68282610eb8565b6124d95760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206135035f395f51905f526124f68484610eb8565b15611e78575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506105cc565b5f610fa7836001600160a01b038416612821565b5f5160206135235f395f51905f525460ff166113ca57604051638dfc202b60e01b815260040160405180910390fd5b6125a161292f565b6125aa826122fb565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061264b90612edc565b80601f016020809104026020016040519081016040528092919081815260200182805461267790612edc565b80156126c25780601f10612699576101008083540402835291602001916126c2565b820191905f5260205f20905b8154815290600101906020018083116126a557829003601f168201915b5050509190925250505090525092915050565b5f825f0182815481106126ea576126ea61346c565b905f5260205f200154905092915050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612758573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e8e9190613480565b6060815f018054806020026020016040519081016040528092919081815260200182805480156127c957602002820191905f5260205f20905b8154815260200190600101908083116127b5575b50505050509050919050565b5f81815260018301602052604081205461281a57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556105cc565b505f6105cc565b5f8181526001830160205260408120548015611e78575f61284360018361349b565b85549091505f906128569060019061349b565b90508082146128b5575f865f0182815481106128745761287461346c565b905f5260205f200154905080875f0184815481106128945761289461346c565b5f918252602080832090910192909255918252600188019052604090208390555b85548690806128c6576128c66134ae565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506105cc565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b0316815260200161297660405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f80825260208281018290529282015291019081526020016129b06040518060200160405280606081525090565b905290565b5f602082840312156129c5575f5ffd5b81356001600160e01b031981168114610fa7575f5ffd5b6001600160a01b0381168114610cc0575f5ffd5b6001600160401b0381168114610cc0575f5ffd5b5f5f5f5f5f60a08688031215612a18575f5ffd5b8535612a23816129dc565b94506020860135612a33816129f0565b93506040860135612a43816129f0565b92506060860135612a53816129f0565b91506080860135612a63816129f0565b809150509295509295909350565b62ffffff81168114610cc0575f5ffd5b5f5f60408385031215612a92575f5ffd5b8235612a9d81612a71565b91506020830135612aad816129f0565b809150509250929050565b5f60208284031215612ac8575f5ffd5b8135610fa781612a71565b5f60208284031215612ae3575f5ffd5b5035919050565b5f5f5f5f5f5f5f60c0888a031215612b00575f5ffd5b8735612b0b81612a71565b96506020880135612b1b816129f0565b95506040880135612b2b816129dc565b94506060880135612b3b816129f0565b93506080880135612b4b816129f0565b925060a08801356001600160401b03811115612b65575f5ffd5b8801601f81018a13612b75575f5ffd5b80356001600160401b03811115612b8a575f5ffd5b8a6020828401011115612b9b575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612bc3575f5ffd5b823591506020830135612aad816129dc565b5f60208284031215612be5575f5ffd5b8135610fa7816129dc565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e0610140840152612cda610160840182612bf0565b979650505050505050565b5f5f5f5f60808587031215612cf8575f5ffd5b8435612d03816129f0565b93506020850135612d13816129f0565b92506040850135612d23816129f0565b91506060850135612d33816129f0565b939692955090935050565b5f5f5f60608486031215612d50575f5ffd5b8335612d5b81612a71565b92506020840135612d6b816129f0565b91506040840135612d7b816129dc565b809150509250925092565b5f5f60408385031215612d97575f5ffd5b50508035926020909101359150565b8015158114610cc0575f5ffd5b5f5f5f5f60808587031215612dc6575f5ffd5b8435612dd181612a71565b93506020850135612d1381612da6565b602080825282518282018190525f918401906040840190835b81811015612e215783516001600160a01b0316835260209384019390920191600101612dfa565b509095945050505050565b5f5f60408385031215612e3d575f5ffd5b8235612a9d816129f0565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0382811682821603908111156105cc576105cc612e76565b6001600160401b0381811683821601908111156105cc576105cc612e76565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612ef057607f821691505b602082108103612f0e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610aed57805f5260205f20601f840160051c81016020851015612f395750805b601f840160051c820191505b81811015610d35575f8155600101612f45565b81516001600160401b03811115612f7157612f71612ec8565b612f8581612f7f8454612edc565b84612f14565b6020601f821160018114612fb7575f8315612fa05750848201515b5f19600385901b1c1916600184901b178455610d35565b5f84815260208120601f198516915b82811015612fe65787850151825560209485019460019092019101612fc6565b508482101561300357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b038111828210171561303557613035612ec8565b60405290565b6040516101e081016001600160401b038111828210171561303557613035612ec8565b8051611ad681612a71565b8051611ad6816129dc565b805161ffff81168114611ad6575f5ffd5b805160ff81168114611ad6575f5ffd5b5f82601f8301126130a4575f5ffd5b81516001600160401b038111156130bd576130bd612ec8565b604051601f8201601f19908116603f011681016001600160401b03811182821017156130eb576130eb612ec8565b604052818152838201602001851015613102575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051611ad6816129f0565b5f60208284031215613139575f5ffd5b81516001600160401b0381111561314e575f5ffd5b82016101a08185031215613160575f5ffd5b613168613012565b6131718261305e565b815261317f60208301613069565b602082015261319060408301613074565b60408201526131a160608301613074565b60608201526131b260808301613074565b60808201526131c360a08301613085565b60a082015260c08201516001600160401b038111156131e0575f5ffd5b6131ec86828501613095565b60c0830152506131fe60e0830161311e565b60e0820152610100828101519082015261012080830151908201526132266101408301613074565b610140820152613239610160830161311e565b61016082015261324c610180830161311e565b610180820152949350505050565b5f6020828403121561326a575f5ffd5b5051919050565b8051611ad681612da6565b5f6020828403121561328c575f5ffd5b8151610fa781612da6565b805163ffffffff81168114611ad6575f5ffd5b5f6101e08284031280156132bc575f5ffd5b506132c561303b565b6132ce83613297565b81526132dc60208401613297565b60208201526132ed60408401613297565b60408201526132fe60608401613297565b606082015261330f60808401613297565b608082015261332060a08401613297565b60a082015261333160c08401613297565b60c082015261334260e08401613085565b60e08201526133546101008401613297565b6101008201526133676101208401613297565b61012082015261337a6101408401613069565b61014082015261338d6101608401613069565b6101608201526133a06101808401613069565b6101808201526133b36101a08401613069565b6101a08201526133c66101c08401613271565b6101c08201529392505050565b5f5f5f5f5f5f60c087890312156133e8575f5ffd5b86516133f381612da6565b60208801519096506001600160401b0381111561340e575f5ffd5b61341a89828a01613095565b955050604087015161342b816129dc565b606088015190945061343c816129f0565b608088015190935061344d816129f0565b60a088015190925061345e816129f0565b809150509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613490575f5ffd5b8151610fa7816129dc565b818103818111156105cc576105cc612e76565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x47", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x867f5b6a3545347409a33493a0b80ba7e75ed96b54d7b7b56eff08ea9609a54f", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x82CFAE30820b4249acD6315C90A4D0a9fEF5A99A" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb00000000000000000000000082cfae30820b4249acd6315c90a4d0a9fef5a99a", + "nonce": "0x48", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false } ], "receipts": [ @@ -427,12 +469,75 @@ "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xa94462", + "logs": [ + { + "address": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "blockTimestamp": "0x67998f30", + "transactionHash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", + "transactionIndex": "0x11", + "logIndex": "0x25", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000080000000000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", + "transactionIndex": "0x11", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "gasUsed": "0x2e589f", + "effectiveGasPrice": "0xf4251", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xa9c574", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000082cfae30820b4249acd6315c90a4d0a9fef5a99a" + ], + "data": "0x", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "blockTimestamp": "0x67998f30", + "transactionHash": "0x867f5b6a3545347409a33493a0b80ba7e75ed96b54d7b7b56eff08ea9609a54f", + "transactionIndex": "0x12", + "logIndex": "0x26", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000010000000000000002000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x867f5b6a3545347409a33493a0b80ba7e75ed96b54d7b7b56eff08ea9609a54f", + "transactionIndex": "0x12", + "blockHash": "0xa2b7ec82cf05335ee76b5a5f424adbfbd6f019ff8dd5b0ef90cb5771556073bb", + "blockNumber": "0x317382", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf4251", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null } ], "libraries": [], "pending": [], "returns": {}, - "timestamp": 1737475792, + "timestamp": 1738116938, "chain": 17000, - "commit": "ac812a4" + "commit": "aaa0e97" } From df5cb1f8effcdf7ccf2406cadfb0e644aad29473 Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 29 Jan 2025 13:14:16 +0100 Subject: [PATCH 30/33] fix: preconfEnabled logic refactor,fixes, tests --- src/CCCP.sol | 80 +++++++++++++--------- test/CCCP.t.sol | 156 ++++++++++++++++++++++++++++++++++++------ test/CCCPConfig.t.sol | 22 ++---- 3 files changed, 186 insertions(+), 72 deletions(-) diff --git a/src/CCCP.sol b/src/CCCP.sol index a3db0e5..6bfff42 100644 --- a/src/CCCP.sol +++ b/src/CCCP.sol @@ -41,7 +41,7 @@ contract CCCP is struct OperatorOptInOutFlags { bool isOptedIn; - bool isOptedOut; + bool optOutInProgress; bool optInAllowed; bool optOutAllowed; } @@ -74,9 +74,11 @@ contract CCCP is error RewardAddressMismatch(); error OperatorNotActive(); error ModuleDisabled(); - error OperatorAlreadyRegistered(); + error OperatorOptedIn(); + error OperatorOptedOut(); error OperatorOptInNotAllowed(); error OperatorOptOutNotAllowed(); + error OperatorOptOutInProgress(); error KeyIndexOutOfRange(); error KeysRangeExceedMaxValidators(); error KeyIndexMismatch(); @@ -160,7 +162,7 @@ contract CCCP is OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); if (flags.isOptedIn) { - revert OperatorAlreadyRegistered(); + revert OperatorOptedIn(); } else if (!flags.optInAllowed) { revert OperatorOptInNotAllowed(); } @@ -189,7 +191,7 @@ contract CCCP is OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); if (!flags.isOptedIn) { - revert OperatorNotActive(); + revert OperatorOptedOut(); } else if (!flags.optOutAllowed) { revert OperatorOptOutNotAllowed(); } @@ -208,7 +210,9 @@ contract CCCP is OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); if (!flags.isOptedIn) { - revert OperatorNotActive(); + revert OperatorOptedOut(); + } else if (flags.optOutInProgress) { + revert OperatorOptOutInProgress(); } /// @dev ignore optOutAllowed, as the committee can force opt-out @@ -225,7 +229,9 @@ contract CCCP is uint256 opKey = _getOpKeyByManager(msg.sender); OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); if (!flags.isOptedIn) { - revert OperatorNotActive(); + revert OperatorOptedOut(); + } else if (flags.optOutInProgress) { + revert OperatorOptOutInProgress(); } KeysRange memory keysRange = _getOperatorKeysRange(opKey); @@ -255,8 +261,17 @@ contract CCCP is revert RewardAddressMismatch(); } + uint256 opKey = _getOpKeyById(moduleId, operatorId); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); + if (!flags.isOptedIn) { + revert OperatorOptedOut(); + } else if (flags.optOutInProgress) { + revert OperatorOptOutInProgress(); + } + /// @dev also checks if the proposed manager is already registered for different operator - _setOperatorManager(__encOpKey(moduleId, operatorId), newManager); + _setOperatorManager(opKey, newManager); emit OperatorManagerUpdated(moduleId, operatorId, newManager); } @@ -286,7 +301,7 @@ contract CCCP is function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool) { LidoOperatorCache memory _c; _loadLidoNodeOperator(_c, moduleId, operatorId); - uint256 opKey = _getOpKeyById(moduleId, operatorId); + uint256 opKey = __encOpKey(moduleId, operatorId); OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); return _isOperatorIsEnabledForPreconf(optInOutState, moduleId, _c.isActive); @@ -298,8 +313,8 @@ contract CCCP is returns (uint64 allowedValidators) { // check if the module has max validators limit - uint64 maxValidators = getModuleOperatorMaxValidators(moduleId); - if (maxValidators == 0) { + allowedValidators = getModuleOperatorMaxValidators(moduleId); + if (allowedValidators == 0) { return 0; } // check if the operator is active in Lido module @@ -308,27 +323,26 @@ contract CCCP is if (!_c.isActive) { return 0; } - uint256 opKey = _getOpKeyById(moduleId, operatorId); - // check if the operator is already has the state - if (_getOperatorManager(opKey) == address(0)) { - return 0; + // min(_c.totalKeys, maxValidators) + if (allowedValidators > _c.totalKeys) { + allowedValidators = _c.totalKeys; } - KeysRange memory keysRange = _getOperatorKeysRange(opKey); - uint64 totalKeys = keysRange.indexEnd - keysRange.indexStart + 1; - // check if the operator has already reached the max validators limit - if (totalKeys >= maxValidators) { + uint256 opKey = __encOpKey(moduleId, operatorId); + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); + if (flags.isOptedIn && !flags.optOutInProgress) { + KeysRange memory keysRange = _getOperatorKeysRange(opKey); + uint64 totalKeys = keysRange.indexEnd - keysRange.indexStart + 1; + // check if the operator has already reached the max validators limit + if (totalKeys >= allowedValidators) { + return 0; + } + unchecked { + allowedValidators -= totalKeys; + } + } else if (!flags.optInAllowed) { return 0; } - - unchecked { - allowedValidators = maxValidators - totalKeys; - } - // check if the operator has enough keys to reach the max validators limit - uint64 restKeys = _c.totalKeys - totalKeys; - if (restKeys < allowedValidators) { - allowedValidators = restKeys; - } } function getConfig() @@ -490,15 +504,17 @@ contract CCCP is uint64 blockNumber = uint64(block.number); (uint64 optInMinDurationBlocks, uint64 optOutDelayDurationBlocks,,) = _getConfig(); - bool isOptedOut = - optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + optOutDelayDurationBlocks < blockNumber; - bool isOptedIn = optInOutState.optInBlock > 0 && !isOptedOut; + bool optOutInProgress = + optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + optOutDelayDurationBlocks >= blockNumber; + + bool isOptedIn = optInOutState.optInBlock > 0 && (optInOutState.optOutBlock == 0 || optOutInProgress); bool optInAllowed = !isOptedIn && !optInOutState.isOptOutForced; - bool optOutAllowed = isOptedIn && optInOutState.optInBlock + optInMinDurationBlocks < blockNumber; + bool optOutAllowed = + isOptedIn && !optOutInProgress && optInOutState.optInBlock + optInMinDurationBlocks < blockNumber; return OperatorOptInOutFlags({ isOptedIn: isOptedIn, - isOptedOut: isOptedOut, + optOutInProgress: optOutInProgress, optInAllowed: optInAllowed, optOutAllowed: optOutAllowed }); diff --git a/test/CCCP.t.sol b/test/CCCP.t.sol index 0764907..b96a097 100644 --- a/test/CCCP.t.sol +++ b/test/CCCP.t.sol @@ -20,6 +20,13 @@ contract CCCPOptIn is CCCPCommon { string public constant rpcUrl1 = "some-url-1"; string public constant rpcUrl2 = "some-url-2"; + uint32 constant opKeysCount = 10; + + uint64 constant optInMinDurationBlocks = 0; + uint64 constant optOutDelayDurationBlocks = 0; + uint64 constant defaultOperatorMaxValidators = 100; + uint64 constant defaultBlockGasLimit = 1000000; + function setUp() public virtual override { super.setUp(); @@ -32,19 +39,19 @@ contract CCCPOptIn is CCCPCommon { _enableInitializers(address(cccp)); cccp.initialize({ committeeAddress: committee, - optInMinDurationBlocks: 0, - optOutDelayDurationBlocks: 0, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 + optInMinDurationBlocks: optInMinDurationBlocks, + optOutDelayDurationBlocks: optOutDelayDurationBlocks, + defaultOperatorMaxValidators: defaultOperatorMaxValidators, + defaultBlockGasLimit: defaultBlockGasLimit }); - noCsm1Id = createNo(csm, noCsm1, 10); - noCurated1Id = createNo(nor, noCurated1, 10); + noCsm1Id = createNo(csm, noCsm1, opKeysCount); + noCurated1Id = createNo(nor, noCurated1, opKeysCount); } function test_OptIn() public { // opt in on behalf of noCsm1 - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectEmit(true, true, true, false, address(cccp)); emit CCCP.OperatorManagerUpdated(csmId, noCsm1Id, noCsm1Manager); vm.expectEmit(true, true, true, false, address(cccp)); @@ -69,7 +76,7 @@ contract CCCPOptIn is CCCPCommon { function test_GetOperatorByManager() public { // opt in on behalf of noCsm1 - vm.broadcast(noCsm1); + vm.prank(noCsm1); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, @@ -97,7 +104,7 @@ contract CCCPOptIn is CCCPCommon { function test_GetOperatorById() public { // opt in on behalf of noCsm1 - vm.broadcast(noCsm1); + vm.prank(noCsm1); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, @@ -136,7 +143,7 @@ contract CCCPOptIn is CCCPCommon { } function test_OptIn_RevertWhen_WrongRewardAddress() public { - vm.broadcast(stranger1); + vm.prank(stranger1); vm.expectRevert(CCCP.RewardAddressMismatch.selector); cccp.optIn({ moduleId: csmId, @@ -149,7 +156,7 @@ contract CCCPOptIn is CCCPCommon { } function test_OptIn_RevertWhen_WrongModuleId() public { - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); cccp.optIn({ moduleId: 999, @@ -165,7 +172,7 @@ contract CCCPOptIn is CCCPCommon { // set noCsm1 to inactive updateNoActive(csm, noCsm1Id, false); - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectRevert(CCCP.OperatorNotActive.selector); cccp.optIn({ moduleId: csmId, @@ -179,7 +186,7 @@ contract CCCPOptIn is CCCPCommon { function test_OptIn_RevertWhen_OperatorAlreadyOptedIn() public { // optin - vm.broadcast(noCsm1); + vm.prank(noCsm1); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, @@ -190,8 +197,8 @@ contract CCCPOptIn is CCCPCommon { }); // repeat optin - vm.broadcast(noCsm1); - vm.expectRevert(CCCP.OperatorAlreadyRegistered.selector); + vm.prank(noCsm1); + vm.expectRevert(CCCP.OperatorOptedIn.selector); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, @@ -204,7 +211,7 @@ contract CCCPOptIn is CCCPCommon { function test_OptIn_RevertWhen_OperatorForceOptedOut() public { // optin - vm.broadcast(noCsm1); + vm.prank(noCsm1); cccp.optIn({ moduleId: csmId, operatorId: noCsm1Id, @@ -216,12 +223,12 @@ contract CCCPOptIn is CCCPCommon { // force optout vm.roll(block.number + 100); - vm.broadcast(committee); + vm.prank(committee); cccp.optOut({moduleId: csmId, operatorId: noCsm1Id}); // repeat optin vm.roll(block.number + 100); - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectRevert(CCCP.OperatorOptInNotAllowed.selector); cccp.optIn({ moduleId: csmId, @@ -235,7 +242,7 @@ contract CCCPOptIn is CCCPCommon { function test_OptIn_RevertWhen_ManagerBelongsOtherOperator() public { // optin - vm.broadcast(noCurated1); + vm.prank(noCurated1); cccp.optIn({ moduleId: norId, operatorId: noCurated1Id, @@ -246,7 +253,7 @@ contract CCCPOptIn is CCCPCommon { }); // optin with same manager - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectRevert(ICCCPOperatorStatesStorage.ManagerBelongsToOtherOperator.selector); cccp.optIn({ moduleId: csmId, @@ -260,7 +267,7 @@ contract CCCPOptIn is CCCPCommon { function test_OptIn_RevertWhen_KeyIndexWrongOrder() public { // optin - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectRevert(CCCP.KeyIndexMismatch.selector); cccp.optIn({ moduleId: csmId, @@ -274,7 +281,7 @@ contract CCCPOptIn is CCCPCommon { function test_OptIn_RevertWhen_KeyIndexOutOfRange() public { // optin - vm.broadcast(noCsm1); + vm.prank(noCsm1); vm.expectRevert(CCCP.KeyIndexOutOfRange.selector); cccp.optIn({ moduleId: csmId, @@ -285,4 +292,109 @@ contract CCCPOptIn is CCCPCommon { rpcURL: "" }); } + + function test_getOperatorIsEnabledForPreconf() public { + // wrong op id + // assertFalse(cccp.getOperatorIsEnabledForPreconf(csmId, 999)); + + // not yet opted in + assertFalse(cccp.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + cccp.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + keyIndexStart: 2, + keyIndexEnd: 4, + rpcURL: rpcUrl1 + }); + // opted in + assertTrue(cccp.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); + } + + function test_GetOperatorAllowedValidators() public { + vm.prank(committee); + cccp.setModuleConfig(csmId, true, 0, 0); + // 0 for disabled module + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); + + vm.prank(committee); + cccp.setModuleConfig(csmId, false, 0, 0); + + // set noCsm1 to inactive + updateNoActive(csm, noCsm1Id, false); + // 0 for inactive operator + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); + updateNoActive(csm, noCsm1Id, true); + + /// operator NOT yet opted in + /// + vm.prank(committee); + cccp.setConfig(0, 0, opKeysCount - 1, defaultBlockGasLimit); + + // operatorMaxValidators when operatorMaxValidators < operatorTotalAddedKeys + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount - 1); + + vm.prank(committee); + cccp.setConfig(0, 99, defaultOperatorMaxValidators, defaultBlockGasLimit); + + // operatorTotalAddedKeys when operatorMaxValidators > operatorTotalAddedKeys + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount); + + // opt in on behalf of noCsm1 with 3 keys + vm.prank(noCsm1); + cccp.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + keyIndexStart: 2, + keyIndexEnd: 4, + rpcURL: rpcUrl1 + }); + + // operatorMaxValidators > operatorTotalAddedKeys + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount - 3); + + // voluntary opt out + vm.roll(block.number + 100); + vm.prank(noCsm1Manager); + cccp.optOut(); + // optOut in progress + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); + + // optOut finished + vm.roll(block.number + 100); + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount); + + // opt in on behalf of noCsm1 with 9 keys + vm.prank(noCsm1); + cccp.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + keyIndexStart: 0, + keyIndexEnd: 8, + rpcURL: rpcUrl1 + }); + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount - 9); + + // reduce max operator validators in module config to 5 + vm.prank(committee); + cccp.setModuleConfig(csmId, false, 5, 0); + // operator opted in totalKeys > operatorMaxValidators + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); + + // force optout + vm.roll(block.number + 100); + vm.prank(committee); + cccp.optOut({moduleId: csmId, operatorId: noCsm1Id}); + + // optOut in progress + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); + + // optOut finished, but forced optout + vm.roll(block.number + 100); + assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); + } } diff --git a/test/CCCPConfig.t.sol b/test/CCCPConfig.t.sol index c2e02eb..5fbef7a 100644 --- a/test/CCCPConfig.t.sol +++ b/test/CCCPConfig.t.sol @@ -13,10 +13,10 @@ contract CCCPConfig is CCCPCommon { uint64 public maxValidators = 1111; uint64 public blockGasLimit = 1111111; - uint64 optInMinDurationBlocks = 32; - uint64 optOutDelayDurationBlocks = 64; - uint64 defaultOperatorMaxValidators = 100; - uint64 defaultBlockGasLimit = 1000000; + uint64 constant optInMinDurationBlocks = 32; + uint64 constant optOutDelayDurationBlocks = 64; + uint64 constant defaultOperatorMaxValidators = 100; + uint64 constant defaultBlockGasLimit = 1000000; function setUp() public virtual override { super.setUp(); @@ -165,18 +165,4 @@ contract CCCPConfig is CCCPCommon { cccp.setModuleConfig(moduleId, false, 0, 0); assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); } - - // function test_GetInitialConfig() public { - // ( - // uint64 newOptInMinDurationBlocks, - // uint64 newOptOutDelayDurationBlocks, - // uint64 newDefaultOperatorMaxValidators, - // uint64 newDefaultBlockGasLimit - // ) = cccp.getConfig(); - - // assertEq(newOptInMinDurationBlocks, 32); - // assertEq(newOptOutDelayDurationBlocks, 64); - // assertEq(newDefaultOperatorMaxValidators, 100); - // assertEq(newDefaultBlockGasLimit, 1000000); - // } } From 0527fc985ec140f8bd56f137de272e3d93b1d85a Mon Sep 17 00:00:00 2001 From: KRogLA Date: Wed, 29 Jan 2025 13:25:41 +0100 Subject: [PATCH 31/33] chore: upd deployed artifacts --- artifacts/holesky/deploy-holesky.json | 2 +- artifacts/holesky/transactions.json | 105 ++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json index 6bda25f..cc81c34 100644 --- a/artifacts/holesky/deploy-holesky.json +++ b/artifacts/holesky/deploy-holesky.json @@ -1,6 +1,6 @@ { "CCCP": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", - "CCCPImpl": "0x82CFAE30820b4249acD6315C90A4D0a9fEF5A99A", + "CCCPImpl": "0x76773E5A432113238be489F9A7Bf716e8dC4a015", "ChainId": 17000, "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json index fd75138..3d2842d 100644 --- a/artifacts/holesky/transactions.json +++ b/artifacts/holesky/transactions.json @@ -168,6 +168,48 @@ }, "additionalContracts": [], "isFixedGasLimit": false + }, + { + "hash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", + "transactionType": "CREATE", + "contractName": "CCCP", + "contractAddress": "0x76773e5a432113238be489f9a7bf716e8dc4a015", + "function": null, + "arguments": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "gas": "0x3d360c", + "value": "0x0", + "input": "0x610100604052348015610010575f5ffd5b5060405161393b38038061393b83398101604081905261002f91610245565b6040517f6c69646f2e636363702e73746f726167652e436f6e66696753746f7261676500602082015260ff1990600190603f01604051602081830303815290604052805190602001205f1c610084919061027c565b60405160200161009691815260200190565b60408051601f198184030181529082905280516020918201209290921660805260ff19916001916100fb91017f6c69646f2e636363702e73746f726167652e4f70657261746f7253746174657381526653746f7261676560c81b602082015260270190565b604051602081830303815290604052805190602001205f1c61011d919061027c565b60405160200161012f91815260200190565b60408051601f1981840301815291905280516020909101201660a0526001600160a01b03821661017257604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b03821660c05260e081905261018c610193565b50506102a1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156101e35760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102425780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610256575f5ffd5b82516001600160a01b038116811461026c575f5ffd5b6020939093015192949293505050565b8181038181111561029b57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161362f61030c5f395f6121f801525f818161058601526127de01525f81816117d90152818161187e015281816118d901528181611b5301526123e101525f818161160d01528181611d2801528181611de60152612010015261362f5ff3fe608060405234801561000f575f5ffd5b5060043610610213575f3560e01c8063893d004e1161011f578063b70ddca0116100a9578063cede01ca11610079578063cede01ca14610540578063d4eec5a614610553578063d547741f1461055b578063d6dc6ae71461056e578063dbba4b4814610581575f5ffd5b8063b70ddca0146104cc578063ba6bc8d5146104df578063c3f909d4146104f2578063ca15c8731461052d575f5ffd5b8063a217fddf116100ef578063a217fddf1461046b578063a264756914610472578063a3246ad314610485578063a3714e07146104a5578063ad32563d146104b8575f5ffd5b8063893d004e1461042a5780638aa104351461043d5780639010d07c1461044557806391d1485414610458575f5ffd5b8063389ed267116101a05780635865c60c116101705780635865c60c146103aa57806358c5be18146103cd5780635c975abb146103f857806370bc09f61461040f5780638456cb5914610422575f5ffd5b8063389ed2671461033d5780633c074eb3146103645780633dcd5765146103775780633f4ba83a146103a2575f5ffd5b8063248a9ca3116101e6578063248a9ca3146102a15780632de03aa1146102dd5780632de0920c146103045780632f2ff15d1461031757806336568abe1461032a575f5ffd5b806301ffc9a71461021757806313e094531461023f5780631a6357e5146102545780631d8394bb14610267575b5f5ffd5b61022a610225366004612a95565b6105a8565b60405190151581526020015b60405180910390f35b61025261024d366004612ae4565b6105d2565b005b61022a610262366004612b61565b610795565b61027a610275366004612b98565b6107e6565b6040805193151584526001600160401b039283166020850152911690820152606001610236565b6102cf6102af366004612bb3565b5f9081525f5160206135e35f395f51905f52602052604090206001015490565b604051908152602001610236565b6102cf7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b610252610312366004612bca565b6107ff565b610252610325366004612c92565b610a8f565b610252610338366004612c92565b610ac5565b6102cf7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b610252610372366004612b61565b610afd565b61038a610385366004612b61565b610b88565b6040516001600160401b039091168152602001610236565b610252610cb2565b6103bd6103b8366004612cb5565b610ce7565b6040516102369493929190612d08565b6103e06103db366004612b61565b610d17565b6040516001600160a01b039091168152602001610236565b5f5160206136035f395f51905f525460ff1661022a565b61025261041d366004612dc5565b610d36565b610252610d60565b610252610438366004612e1e565b610d92565b61038a610ed3565b6103e0610453366004612e66565b610f0a565b61022a610466366004612c92565b610f2f565b6102cf5f81565b610252610480366004612e93565b610f65565b610498610493366004612bb3565b610ff5565b6040516102369190612ec1565b6103bd6104b3366004612b61565b611025565b6102cf5f5160206135c35f395f51905f5281565b61038a6104da366004612b98565b611057565b6102526104ed366004612f0c565b6110a1565b6104fa6111cf565b604080516001600160401b0395861681529385166020850152918416918301919091529091166060820152608001610236565b6102cf61053b366004612bb3565b6111e9565b61038a61054e366004612b98565b61120d565b610252611246565b610252610569366004612c92565b611321565b61025261057c366004612b61565b611351565b6103e07f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806105cc57506105cc8261143e565b92915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156106165750825b90505f826001600160401b031660011480156106315750303b155b90508115801561063f575080155b1561065d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561068757845460ff60401b1916600160401b1785555b6001600160a01b038a166106ae576040516319467aa760e11b815260040160405180910390fd5b6106b6611472565b6106be611484565b6106c85f8b61148c565b506106e05f5160206135c35f395f51905f528b61148c565b5061070b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d8b61148c565b506107367f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c78b61148c565b50610743898989896114ce565b831561078957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b5f61079e6129db565b6107a9818585611533565b6001600160401b038316604085901b62ffffff60401b16175f6107cb82611547565b90506107dc81878560a001516115ad565b9695505050505050565b5f5f5f6107f284611602565b9250925092509193909250565b610807611678565b6001600160a01b03851661082e57604051634434240960e11b815260040160405180910390fd5b6108366129db565b610841818989611533565b80606001516001600160a01b0316336001600160a01b031614610877576040516349ad9b0960e11b815260040160405180910390fd5b8060a0015161089957604051631a8660cb60e01b815260040160405180910390fd5b6001600160401b038716604089901b62ffffff60401b16175f6108bb82611547565b90505f6108c7826116a8565b8051909150156108ea576040516348a1843960e01b815260040160405180910390fd5b806040015161090c576040516328a5715760e21b815260040160405180910390fd5b610916838a6117c7565b6040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a361099c836040518060600160405280436001600160401b031681526020015f6001600160401b031681526020015f1515815250611905565b6109a884848a8a611966565b604080516020601f8801819004810282018301835281018781526109eb9286929182918b908b90819085018382808284375f9201919091525050509152506119e9565b896001600160401b03168b62ffffff167f8ae0affaa3c6c49aee34fd262b42a1ed56bf92a700857bddf013a00a99bff4ef8888604051610a2c929190612f28565b60405180910390a36040516001600160a01b038a1681526001600160401b038b169062ffffff8d16907f742aa8302eca1011d41495047d2d195954b0f91a24ac18e221a1788507e42d7d9060200160405180910390a35050505050505050505050565b5f8281525f5160206135e35f395f51905f526020526040902060010154610ab581611a08565b610abf838361148c565b50505050565b6001600160a01b0381163314610aee5760405163334bd91960e11b815260040160405180910390fd5b610af88282611a12565b505050565b5f5160206135c35f395f51905f52610b1481611a08565b5f610b1f8484611a4b565b90505f610b2b82611547565b9050806040015115610b47575f6040820152610b478282611905565b6040516001600160401b0385169062ffffff8716907f2b140fd22a9c33bfd560c6a50d8f4f235e6d85feeb1629b97b62b488f3c95004905f90a35050505050565b5f610b9283611057565b9050806001600160401b03165f03610bab57505f6105cc565b610bb36129db565b610bbe818585611533565b8060a00151610bd0575f9150506105cc565b80608001516001600160401b0316826001600160401b03161115610bf657806080015191505b6001600160401b038316604085901b62ffffff60401b16175f610c20610c1b83611547565b6116a8565b80519091508015610c3357508060200151155b15610c95575f610c4283611a94565b90505f815f01518260200151610c589190612f6a565b610c63906001612f89565b9050856001600160401b0316816001600160401b031610610c8b575f955050505050506105cc565b9094039350610ca9565b8060400151610ca9575f93505050506105cc565b50505092915050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7610cdc81611a08565b610ce4611ae2565b50565b5f5f5f610cf2612a0f565b5f610cfc86611b41565b9050610d0781611ba1565b9450945094509450509193509193565b5f5f610d238484611a4b565b9050610d2e81611bf1565b949350505050565b5f5160206135c35f395f51905f52610d4d81611a08565b610d59858585856114ce565b5050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d610d8a81611a08565b610ce4611c0b565b610d9a611678565b6001600160a01b038116610dc157604051634434240960e11b815260040160405180910390fd5b610dc96129db565b610dd4818585611533565b80606001516001600160a01b0316336001600160a01b031614610e0a576040516349ad9b0960e11b815260040160405180910390fd5b5f610e158585611a4b565b90505f610e2182611547565b90505f610e2d826116a8565b8051909150610e4f57604051632acf1f2560e11b815260040160405180910390fd5b806020015115610e7257604051630a93f65160e11b815260040160405180910390fd5b610e7c83866117c7565b6040516001600160a01b03861681526001600160401b0387169062ffffff8916907fb5a535addaaf9c7dbe0b8728def4b0e19391fe6471ac14e0a2c22d8ee83a4ebc9060200160405180910390a350505050505050565b5f610f057ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00546001600160401b031690565b905090565b5f8281525f5160206135a35f395f51905f52602081905260408220610d2e9084611c53565b5f9182525f5160206135e35f395f51905f52602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f5160206135c35f395f51905f52610f7c81611a08565b610f846129db565b610f8e8187611c5e565b610f9a86868686611cf4565b6040805186151581526001600160401b038681166020830152851681830152905162ffffff8816917f37874944470fa7fc10b8f30fa6686dc6b2f96522f58cddd220c8a5856400652b919081900360600190a2505050505050565b5f8181525f5160206135a35f395f51905f52602081905260409091206060919061101e90611dcc565b9392505050565b5f5f5f611030612a0f565b5f61103b8787611a4b565b905061104681611ba1565b929a91995097509095509350505050565b5f5f611061611dd8565b50925050505f5f61107185611602565b509150915081611096576001600160401b038116156110905780611098565b82611098565b5f5b95945050505050565b6110a9611678565b5f6110b333611b41565b90505f6110c2610c1b83611547565b80519091506110e457604051632acf1f2560e11b815260040160405180910390fd5b80602001511561110757604051630a93f65160e11b815260040160405180910390fd5b5f61111183611a94565b9050805f01516001600160401b0316856001600160401b0316118061114b575080602001516001600160401b0316846001600160401b0316105b80611182575080516001600160401b038681169116148015611182575080602001516001600160401b0316846001600160401b0316145b156111a05760405163c7f544bb60e01b815260040160405180910390fd5b6111a86129db565b604084901c846111b9838383611533565b6111c583878a8a611966565b5050505050505050565b5f5f5f5f6111db611dd8565b935093509350935090919293565b5f8181525f5160206135a35f395f51905f5260208190526040822061101e90611e4e565b5f5f611217611dd8565b93505050505f5f61122785611602565b925050915081611096576001600160401b038116156110905780611098565b61124e611678565b5f61125833611b41565b90505f61126482611547565b90505f611270826116a8565b805190915061129257604051632acf1f2560e11b815260040160405180910390fd5b80606001516112b457604051630eedec1160e31b815260040160405180910390fd5b6001600160401b03431660208301526112cd8383611905565b604080515f81529084901c9084906001600160401b0382169062ffffff8416907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a35050505050565b5f8281525f5160206135e35f395f51905f52602052604090206001015461134781611a08565b610abf8383611a12565b5f5160206135c35f395f51905f5261136881611a08565b5f6113738484611a4b565b90505f61137f82611547565b90505f61138b826116a8565b80519091506113ad57604051632acf1f2560e11b815260040160405180910390fd5b8060200151156113d057604051630a93f65160e11b815260040160405180910390fd5b6001600160401b0343166020830152600160408301526113f08383611905565b604051600181526001600160401b0386169062ffffff8816907fd6b972cb40d3e29a33d9ccde1b9db78d12306458943f95974c2856d9093d46849060200160405180910390a3505050505050565b5f6001600160e01b03198216637965db0b60e01b14806105cc57506301ffc9a760e01b6001600160e01b03198316146105cc565b61147a611e57565b611482611ea0565b565b611482611e57565b5f5f5160206135a35f395f51905f52816114a68585611ec0565b90508015610d2e575f8581526020839052604090206114c59085611f61565b50949350505050565b6114da84848484611f75565b604080516001600160401b0386811682528581166020830152848116828401528316606082015290517f84d25455db084cf777a0a4a00fcc28946b3a2ebc5cc866ef8820ab8571dbdd369181900360800190a150505050565b61153d8383611c5e565b610af883826120b2565b604080516060810182525f808252602082018190529181019190915261156c826123db565b60408051606081018252600292909201546001600160401b038082168452600160401b8204166020840152600160801b900460ff1615159082015292915050565b5f5f6115b8856116a8565b90505f6115c485611602565b5050825190915080156115d5575080155b80156115de5750835b80156107dc57505f5160206136035f395f51905f525460ff16159695505050505050565b62ffffff165f9081527f000000000000000000000000000000000000000000000000000000000000000060209081526040918290208251606081018452905460ff811615158083526001600160401b0361010083048116948401859052600160481b909204909116919093018190529192909190565b5f5160206136035f395f51905f525460ff16156114825760405163d93c066560e01b815260040160405180910390fd5b604080516080810182525f808252602082018190529181018290526060810191909152435f806116d6611dd8565b5050915091505f5f86602001516001600160401b031611801561171a5750836001600160401b031682876020015161170e9190612f89565b6001600160401b031610155b90505f5f875f01516001600160401b031611801561174a575060208701516001600160401b0316158061174a5750815b90505f8115801561175d57508760400151155b90505f82801561176b575083155b8015611795575088516001600160401b0388169061178a908890612f89565b6001600160401b0316105b604080516080810182529415158552941515602085015291151593830193909352151560608201529695505050505050565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604090205480158015906118105750828114155b1561182e5760405163324e8e5f60e01b815260040160405180910390fd5b5f611838846123db565b80549091506001600160a01b031680158015906118675750836001600160a01b0316816001600160a01b031614155b156118a7576001600160a01b0381165f90815260017f00000000000000000000000000000000000000000000000000000000000000000160205260408120555b50805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03939093169283179055505f9081527f00000000000000000000000000000000000000000000000000000000000000006001016020526040902055565b8061190f836123db565b8151600291909101805460208401516040909401511515600160801b0260ff60801b196001600160401b03958616600160401b026001600160801b0319909316959094169490941717919091169190911790555050565b6119758460800151838361240a565b835161198290838361248e565b61198d83838361250e565b604080850151855182516001600160401b03868116825285811660208301529092169262ffffff909116917f9841e87ed1a3c39f013566d8bc2c2119007cfb8c591ccde84c6e012e8891410c910160405180910390a350505050565b806119f3836123db565b8151600391909101908190610d599082613038565b610ce4813361257c565b5f5f5160206135a35f395f51905f5281611a2c85856125bd565b90508015610d2e575f8581526020839052604090206114c59085612636565b6001600160401b038116604083901b62ffffff60401b16175f611a6d82611bf1565b6001600160a01b0316036105cc576040516325ec6c1f60e01b815260040160405180910390fd5b604080518082019091525f8082526020820152611ab0826123db565b60408051808201909152600191909101546001600160401b038082168352600160401b90910416602082015292915050565b611aea61264a565b5f5160206136035f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b6001600160a01b0381165f90815260017f000000000000000000000000000000000000000000000000000000000000000001602052604081205490819003611b9c5760405163fe0a2bb160e01b815260040160405180910390fd5b919050565b5f5f5f611bac612a0f565b611bb46129db565b604086901c9450859350611bc9818686611533565b611bd286612679565b9150611be78260400151868360a001516115ad565b9250509193509193565b5f611bfb826123db565b546001600160a01b031692915050565b611c13611678565b5f5160206136035f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611b23565b5f61101e83836127b5565b5f611c676127db565b604051630bc1bb1960e41b815262ffffff841660048201526001600160a01b03919091169063bc1bb190906024015f60405180830381865afa158015611caf573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611cd69190810190613209565b62ffffff9092168352506020908101516001600160a01b0316910152565b60405180606001604052808415158152602001836001600160401b03168152602001826001600160401b0316815250611d4a7f000000000000000000000000000000000000000000000000000000000000000090565b62ffffff959095165f90815260209586526040908190208251815497840151939092015168ffffffffffffffffff1990971691151568ffffffffffffffff001916919091176101006001600160401b03938416021770ffffffffffffffff0000000000000000001916600160481b929096169190910294909417909355505050565b60605f61101e8361285c565b6040805160808101825260017f000000000000000000000000000000000000000000000000000000000000000001546001600160401b03808216808452600160401b8304821660208501819052600160801b84048316958501869052600160c01b90930490911660609093018390529390929190565b5f6105cc825490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661148257604051631afcd79f60e31b815260040160405180910390fd5b611ea8611e57565b5f5160206136035f395f51905f52805460ff19169055565b5f5f5160206135e35f395f51905f52611ed98484610f2f565b611f58575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055611f0e3390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506105cc565b5f9150506105cc565b5f61101e836001600160a01b0384166128b5565b816001600160401b03165f03611f9e5760405163ad617a0b60e01b815260040160405180910390fd5b806001600160401b03165f03611fc657604051628faa1f60e61b815260040160405180910390fd5b6040518060800160405280856001600160401b03168152602001846001600160401b03168152602001836001600160401b03168152602001826001600160401b03168152506120327f000000000000000000000000000000000000000000000000000000000000000090565b81516001919091018054602084015160408501516060909501516001600160401b03908116600160c01b026001600160c01b03968216600160801b02969096166fffffffffffffffffffffffffffffffff928216600160401b026001600160801b0319909416919095161791909117169190911791909117905550505050565b815162ffffff165f036120d857604051634632571560e01b815260040160405180910390fd5b5f82602001516001600160a01b031663a70c70e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612119573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061213d919061333a565b9050806001600160401b0316826001600160401b0316106121715760405163b9aa612760e01b815260040160405180910390fd5b8183604001906001600160401b031690816001600160401b0316815250505f83602001516001600160a01b03166315dae03e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121f4919061333a565b90507f00000000000000000000000000000000000000000000000000000000000000008103612332576020840151604051630bc5f72160e31b81526001600160401b03851660048201526001600160a01b03821690635e2fb90890602401602060405180830381865afa15801561226d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612291919061335c565b151560a08601526040516365c14dc760e01b81526001600160401b03851660048201525f906001600160a01b038316906365c14dc7906024016101e060405180830381865afa1580156122e6573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061230a919061338a565b6101808101516001600160a01b031660608801525163ffffffff16608087015250610abf9050565b6020840151604051632695a60f60e21b81526001600160401b03851660048201525f60248201526001600160a01b03821690639a56983c906044015f60405180830381865afa158015612387573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526123ae91908101906134b3565b6001600160401b031660808b015250506001600160a01b0316606088015250151560a08601525050505050565b5f9081527f00000000000000000000000000000000000000000000000000000000000000006020526040902090565b806001600160401b0316826001600160401b0316111561243d5760405163c7f544bb60e01b815260040160405180910390fd5b826001600160401b0316816001600160401b03161015806124705750826001600160401b0316826001600160401b031610155b15610af857604051637ba485c560e01b815260040160405180910390fd5b5f61249884611057565b9050806001600160401b03165f036124c357604051630dbfe5fd60e31b815260040160405180910390fd5b5f6124ce8484612f6a565b6124d9906001612f89565b9050816001600160401b0316816001600160401b03161115610d5957604051631d1f75fb60e31b815260040160405180910390fd5b6040518060400160405280836001600160401b03168152602001826001600160401b031681525061253e846123db565b815160019190910180546020909301516001600160401b03908116600160401b026001600160801b0319909416921691909117919091179055505050565b6125868282610f2f565b6125b95760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b5050565b5f5f5160206135e35f395f51905f526125d68484610f2f565b15611f58575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506105cc565b5f61101e836001600160a01b038416612901565b5f5160206136035f395f51905f525460ff1661148257604051638dfc202b60e01b815260040160405180910390fd5b612681612a0f565b61268a826123db565b6040805160808101825282546001600160a01b031681528151808301835260018401546001600160401b038082168352600160401b918290048116602080850191909152808501939093528451606080820187526002880154808416835293840490921681850152600160801b90920460ff161515828601528385019190915283519182019093526003840180549294938501928290829061272b90612fbc565b80601f016020809104026020016040519081016040528092919081815260200182805461275790612fbc565b80156127a25780601f10612779576101008083540402835291602001916127a2565b820191905f5260205f20905b81548152906001019060200180831161278557829003601f168201915b5050509190925250505090525092915050565b5f825f0182815481106127ca576127ca61354c565b905f5260205f200154905092915050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612838573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f059190613560565b6060815f018054806020026020016040519081016040528092919081815260200182805480156128a957602002820191905f5260205f20905b815481526020019060010190808311612895575b50505050509050919050565b5f8181526001830160205260408120546128fa57508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556105cc565b505f6105cc565b5f8181526001830160205260408120548015611f58575f61292360018361357b565b85549091505f906129369060019061357b565b9050808214612995575f865f0182815481106129545761295461354c565b905f5260205f200154905080875f0184815481106129745761297461354c565b5f918252602080832090910192909255918252600188019052604090208390555b85548690806129a6576129a661358e565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506105cc565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180608001604052805f6001600160a01b03168152602001612a5660405180604001604052805f6001600160401b031681526020015f6001600160401b031681525090565b8152604080516060810182525f8082526020828101829052928201529101908152602001612a906040518060200160405280606081525090565b905290565b5f60208284031215612aa5575f5ffd5b81356001600160e01b03198116811461101e575f5ffd5b6001600160a01b0381168114610ce4575f5ffd5b6001600160401b0381168114610ce4575f5ffd5b5f5f5f5f5f60a08688031215612af8575f5ffd5b8535612b0381612abc565b94506020860135612b1381612ad0565b93506040860135612b2381612ad0565b92506060860135612b3381612ad0565b91506080860135612b4381612ad0565b809150509295509295909350565b62ffffff81168114610ce4575f5ffd5b5f5f60408385031215612b72575f5ffd5b8235612b7d81612b51565b91506020830135612b8d81612ad0565b809150509250929050565b5f60208284031215612ba8575f5ffd5b813561101e81612b51565b5f60208284031215612bc3575f5ffd5b5035919050565b5f5f5f5f5f5f5f60c0888a031215612be0575f5ffd5b8735612beb81612b51565b96506020880135612bfb81612ad0565b95506040880135612c0b81612abc565b94506060880135612c1b81612ad0565b93506080880135612c2b81612ad0565b925060a08801356001600160401b03811115612c45575f5ffd5b8801601f81018a13612c55575f5ffd5b80356001600160401b03811115612c6a575f5ffd5b8a6020828401011115612c7b575f5ffd5b602082019350809250505092959891949750929550565b5f5f60408385031215612ca3575f5ffd5b823591506020830135612b8d81612abc565b5f60208284031215612cc5575f5ffd5b813561101e81612abc565b5f81516020845280518060208601528060208301604087015e5f604082870101526040601f19601f8301168601019250505092915050565b62ffffff851681526001600160401b03841660208201528215156040820152608060608201526001600160a01b0382511660808201525f60208301516001600160401b0381511660a08401526001600160401b0360208201511660c08401525060408301516001600160401b0381511660e08401526001600160401b036020820151166101008401526040810151151561012084015250606083015160e0610140840152612dba610160840182612cd0565b979650505050505050565b5f5f5f5f60808587031215612dd8575f5ffd5b8435612de381612ad0565b93506020850135612df381612ad0565b92506040850135612e0381612ad0565b91506060850135612e1381612ad0565b939692955090935050565b5f5f5f60608486031215612e30575f5ffd5b8335612e3b81612b51565b92506020840135612e4b81612ad0565b91506040840135612e5b81612abc565b809150509250925092565b5f5f60408385031215612e77575f5ffd5b50508035926020909101359150565b8015158114610ce4575f5ffd5b5f5f5f5f60808587031215612ea6575f5ffd5b8435612eb181612b51565b93506020850135612df381612e86565b602080825282518282018190525f918401906040840190835b81811015612f015783516001600160a01b0316835260209384019390920191600101612eda565b509095945050505050565b5f5f60408385031215612f1d575f5ffd5b8235612b7d81612ad0565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0382811682821603908111156105cc576105cc612f56565b6001600160401b0381811683821601908111156105cc576105cc612f56565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680612fd057607f821691505b602082108103612fee57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610af857805f5260205f20601f840160051c810160208510156130195750805b601f840160051c820191505b81811015610d59575f8155600101613025565b81516001600160401b0381111561305157613051612fa8565b6130658161305f8454612fbc565b84612ff4565b6020601f821160018114613097575f83156130805750848201515b5f19600385901b1c1916600184901b178455610d59565b5f84815260208120601f198516915b828110156130c657878501518255602094850194600190920191016130a6565b50848210156130e357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6040516101a081016001600160401b038111828210171561311557613115612fa8565b60405290565b6040516101e081016001600160401b038111828210171561311557613115612fa8565b8051611b9c81612b51565b8051611b9c81612abc565b805161ffff81168114611b9c575f5ffd5b805160ff81168114611b9c575f5ffd5b5f82601f830112613184575f5ffd5b81516001600160401b0381111561319d5761319d612fa8565b604051601f8201601f19908116603f011681016001600160401b03811182821017156131cb576131cb612fa8565b6040528181528382016020018510156131e2575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051611b9c81612ad0565b5f60208284031215613219575f5ffd5b81516001600160401b0381111561322e575f5ffd5b82016101a08185031215613240575f5ffd5b6132486130f2565b6132518261313e565b815261325f60208301613149565b602082015261327060408301613154565b604082015261328160608301613154565b606082015261329260808301613154565b60808201526132a360a08301613165565b60a082015260c08201516001600160401b038111156132c0575f5ffd5b6132cc86828501613175565b60c0830152506132de60e083016131fe565b60e0820152610100828101519082015261012080830151908201526133066101408301613154565b61014082015261331961016083016131fe565b61016082015261332c61018083016131fe565b610180820152949350505050565b5f6020828403121561334a575f5ffd5b5051919050565b8051611b9c81612e86565b5f6020828403121561336c575f5ffd5b815161101e81612e86565b805163ffffffff81168114611b9c575f5ffd5b5f6101e082840312801561339c575f5ffd5b506133a561311b565b6133ae83613377565b81526133bc60208401613377565b60208201526133cd60408401613377565b60408201526133de60608401613377565b60608201526133ef60808401613377565b608082015261340060a08401613377565b60a082015261341160c08401613377565b60c082015261342260e08401613165565b60e08201526134346101008401613377565b6101008201526134476101208401613377565b61012082015261345a6101408401613149565b61014082015261346d6101608401613149565b6101608201526134806101808401613149565b6101808201526134936101a08401613149565b6101a08201526134a66101c08401613351565b6101c08201529392505050565b5f5f5f5f5f5f60c087890312156134c8575f5ffd5b86516134d381612e86565b60208801519096506001600160401b038111156134ee575f5ffd5b6134fa89828a01613175565b955050604087015161350b81612abc565b606088015190945061351c81612ad0565b608088015190935061352d81612ad0565b60a088015190925061353e81612ad0565b809150509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613570575f5ffd5b815161101e81612abc565b818103818111156105cc576105cc612f56565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000794daa56950487582951e8db2fdbcbee68c2223c65641d0aa02a3afc64f9a86f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a164736f6c634300081c000a00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000", + "nonce": "0x49", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xc6a0779a406a82c2d3df8d61d264958464f943189959040a72636e11a79c6725", + "transactionType": "CALL", + "contractName": "OssifiableProxy", + "contractAddress": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "function": "proxy__upgradeTo(address)", + "arguments": [ + "0x76773E5A432113238be489F9A7Bf716e8dC4a015" + ], + "transaction": { + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "gas": "0xbcc3", + "value": "0x0", + "input": "0x3ebdd0eb00000000000000000000000076773e5a432113238be489f9a7bf716e8dc4a015", + "nonce": "0x4a", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false } ], "receipts": [ @@ -532,6 +574,69 @@ "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x6323fc", + "logs": [ + { + "address": "0x76773e5a432113238be489f9a7bf716e8dc4a015", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "blockTimestamp": "0x679a1bdc", + "transactionHash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", + "transactionIndex": "0x11", + "logIndex": "0x2d", + "removed": false + } + ], + "logsBloom": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000080000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", + "transactionIndex": "0x11", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "gasUsed": "0x2f15e2", + "effectiveGasPrice": "0xf4250", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": null, + "contractAddress": "0x76773e5a432113238be489f9a7bf716e8dc4a015" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x63a50e", + "logs": [ + { + "address": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000076773e5a432113238be489f9a7bf716e8dc4a015" + ], + "data": "0x", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "blockTimestamp": "0x679a1bdc", + "transactionHash": "0xc6a0779a406a82c2d3df8d61d264958464f943189959040a72636e11a79c6725", + "transactionIndex": "0x12", + "logIndex": "0x2e", + "removed": false + } + ], + "logsBloom": "0x00000000000000000040000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000010010000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xc6a0779a406a82c2d3df8d61d264958464f943189959040a72636e11a79c6725", + "transactionIndex": "0x12", + "blockHash": "0x57c3d6218f235bc92540419428d3c6be61a1166a2dfa83fb8422ec00f53420bb", + "blockNumber": "0x317e50", + "gasUsed": "0x8112", + "effectiveGasPrice": "0xf4250", + "from": "0x401fd888b5e41113b7c0c47725a742bbc3a083ef", + "to": "0x5e30e8958a4361ac7a7c4fce978ff18f70e915a2", + "contractAddress": null } ], "libraries": [], From 79d5e4513b2fccae3b8356c19aedaa760af5c1cf Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 11 Feb 2025 13:58:44 +0100 Subject: [PATCH 32/33] feat: delegates+multi ranges --- foundry.toml | 4 +- script/DeployBase.sol | 34 +- script/DeployHolesky.s.sol | 6 +- script/DeployMainnet.s.sol | 6 +- script/UpgradeBase.sol | 30 +- script/UpgradeHolesky.s.sol | 6 +- src/CCCP.sol | 569 -------------- src/CCR.sol | 728 ++++++++++++++++++ src/interfaces/ICCCP.sol | 39 - src/interfaces/ICCR.sol | 37 + ...onfigStorage.sol => ICCRConfigStorage.sol} | 22 +- ...rage.sol => ICCROperatorStatesStorage.sol} | 42 +- src/interfaces/ICSModule.sol | 4 +- src/interfaces/ICuratedModule.sol | 6 +- src/interfaces/IStakingRouter.sol | 2 +- ...ConfigStorage.sol => CCRConfigStorage.sol} | 47 +- ...orage.sol => CCROperatorStatesStorage.sol} | 101 ++- test/CCCP.t.sol | 400 ---------- test/CCCPConfig.t.sol | 168 ---- test/CCCPInit.t.sol | 60 -- test/CCR.t.sol | 351 +++++++++ test/CCRConfig.t.sol | 166 ++++ test/CCRInit.t.sol | 56 ++ test/fork/deployment/PostDeployment.t.sol | 48 +- test/fork/deployment/Upgradability.sol | 32 +- .../helpers/{CCCPCommon.sol => CCRCommon.sol} | 10 +- test/helpers/Fixtures.sol | 12 +- .../mocks/{CCCPMock.sol => CCRMock.sol} | 6 +- test/helpers/mocks/CSModuleMock.sol | 2 +- test/helpers/mocks/CuratedModuleMock.sol | 12 +- test/helpers/mocks/StakingModuleMock.sol | 6 +- 31 files changed, 1599 insertions(+), 1413 deletions(-) delete mode 100644 src/CCCP.sol create mode 100644 src/CCR.sol delete mode 100644 src/interfaces/ICCCP.sol create mode 100644 src/interfaces/ICCR.sol rename src/interfaces/{ICCCPConfigStorage.sol => ICCRConfigStorage.sol} (60%) rename src/interfaces/{ICCCPOperatorStatesStorage.sol => ICCROperatorStatesStorage.sol} (61%) rename src/lib/{CCCPConfigStorage.sol => CCRConfigStorage.sol} (56%) rename src/lib/{CCCPOperatorStatesStorage.sol => CCROperatorStatesStorage.sol} (50%) delete mode 100644 test/CCCP.t.sol delete mode 100644 test/CCCPConfig.t.sol delete mode 100644 test/CCCPInit.t.sol create mode 100644 test/CCR.t.sol create mode 100644 test/CCRConfig.t.sol create mode 100644 test/CCRInit.t.sol rename test/helpers/{CCCPCommon.sol => CCRCommon.sol} (89%) rename test/helpers/mocks/{CCCPMock.sol => CCRMock.sol} (65%) diff --git a/foundry.toml b/foundry.toml index c11bd55..47cafcf 100644 --- a/foundry.toml +++ b/foundry.toml @@ -18,8 +18,8 @@ block_gas_limit = 30_000_000 fuzz = { runs = 256 } gas_reports = [ - "CCCP", - "CCCPDataStorage", + "CCR", + "CCRDataStorage", "OssifiableProxy" ] diff --git a/script/DeployBase.sol b/script/DeployBase.sol index 3e3c1a7..cfbddcb 100644 --- a/script/DeployBase.sol +++ b/script/DeployBase.sol @@ -7,22 +7,22 @@ import {ScriptInit} from "./ScriptInit.sol"; import {JsonObj, Json} from "./utils/Json.sol"; import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; -import {CCCP} from "../src/CCCP.sol"; +import {CCR} from "../src/CCR.sol"; struct DeployParams { address lidoLocatorAddress; bytes32 csModuleType; address proxyAdmin; address committeeAddress; - uint64 optInMinDurationBlocks; - uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; + uint64 optInDelayBlocks; + uint64 optOutDelayBlocks; + uint64 defaultOperatorMaxKeys; uint64 defaultBlockGasLimit; } abstract contract DeployBase is ScriptInit { DeployParams public params; - CCCP public cccp; + CCR public ccr; constructor(string memory _chainName, uint256 _chainId) ScriptInit(_chainName, _chainId) {} @@ -31,20 +31,20 @@ abstract contract DeployBase is ScriptInit { vm.startBroadcast(pk); { - // deploy new CCCP implementation - CCCP cccpImpl = _deployImplementation(params); + // deploy new CCR implementation + CCR ccrImpl = _deployImplementation(params); - cccp = CCCP( + ccr = CCR( _deployProxy( params.proxyAdmin, - address(cccpImpl), + address(ccrImpl), abi.encodeCall( - CCCP.initialize, + CCR.initialize, ( params.committeeAddress, - params.optInMinDurationBlocks, - params.optOutDelayDurationBlocks, - params.defaultOperatorMaxValidators, + params.optInDelayBlocks, + params.optOutDelayBlocks, + params.defaultOperatorMaxKeys, params.defaultBlockGasLimit ) ) @@ -53,8 +53,8 @@ abstract contract DeployBase is ScriptInit { JsonObj memory deployJson = Json.newObj(); deployJson.set("ChainId", chainId); - deployJson.set("CCCPImpl", address(cccpImpl)); - deployJson.set("CCCP", address(cccp)); + deployJson.set("CCRImpl", address(ccrImpl)); + deployJson.set("CCR", address(ccr)); deployJson.set("LidoLocator", params.lidoLocatorAddress); deployJson.set("DeployParams", abi.encode(params)); vm.writeJson(deployJson.str, _deployJsonFilename()); @@ -64,8 +64,8 @@ abstract contract DeployBase is ScriptInit { } /// @dev can be overridden to customize the upgrade process - function _deployImplementation(DeployParams memory _params) internal virtual returns (CCCP) { - return new CCCP(_params.lidoLocatorAddress, _params.csModuleType); + function _deployImplementation(DeployParams memory _params) internal virtual returns (CCR) { + return new CCR(_params.lidoLocatorAddress, _params.csModuleType); } function _deployProxy(address _admin, address _impl, bytes memory _data) internal returns (address) { diff --git a/script/DeployHolesky.s.sol b/script/DeployHolesky.s.sol index 40b298a..620b9ed 100644 --- a/script/DeployHolesky.s.sol +++ b/script/DeployHolesky.s.sol @@ -10,7 +10,7 @@ contract DeployHolesky is DeployBase { // implementation constants params.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; params.csModuleType = "community-onchain-v1"; - params.defaultOperatorMaxValidators = 100; + params.defaultOperatorMaxKeys = 100; params.defaultBlockGasLimit = 1000000; // proxy @@ -18,8 +18,8 @@ contract DeployHolesky is DeployBase { // initial parameters params.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA - params.optInMinDurationBlocks = 32; - params.optOutDelayDurationBlocks = 64; + params.optInDelayBlocks = 32; + params.optOutDelayBlocks = 64; _setUp(); } diff --git a/script/DeployMainnet.s.sol b/script/DeployMainnet.s.sol index b9ef10f..d03100f 100644 --- a/script/DeployMainnet.s.sol +++ b/script/DeployMainnet.s.sol @@ -10,7 +10,7 @@ contract DeployMainnet is DeployBase { // implementation constants params.lidoLocatorAddress = 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb; params.csModuleType = "community-onchain-v1"; - params.defaultOperatorMaxValidators = 1000; + params.defaultOperatorMaxKeys = 1000; params.defaultBlockGasLimit = 3000000; // proxy @@ -18,8 +18,8 @@ contract DeployMainnet is DeployBase { // initial parameters params.committeeAddress = 0x0000000000000000000000000000000000000000; // Dev team EOA - params.optInMinDurationBlocks = 0; - params.optOutDelayDurationBlocks = 64; + params.optInDelayBlocks = 0; + params.optOutDelayBlocks = 64; _setUp(); } diff --git a/script/UpgradeBase.sol b/script/UpgradeBase.sol index 1a23db7..99ab857 100644 --- a/script/UpgradeBase.sol +++ b/script/UpgradeBase.sol @@ -7,12 +7,12 @@ import {ScriptInit} from "./ScriptInit.sol"; import {JsonObj, Json} from "./utils/Json.sol"; import {OssifiableProxy} from "../src/lib/proxy/OssifiableProxy.sol"; -import {CCCP} from "../src/CCCP.sol"; +import {CCR} from "../src/CCR.sol"; import {DeployParams} from "./DeployBase.sol"; abstract contract UpgradeBase is ScriptInit { DeployParams internal params; - CCCP public cccp; + CCR public ccr; error ArtifactsChainIdMismatch(uint256 actual, uint256 expected); @@ -23,7 +23,7 @@ abstract contract UpgradeBase is ScriptInit { string memory artifactsPath = vm.envOr("DEPLOY_CONFIG", string("")); uint256 artifactsChainId; - (artifactsChainId, cccp, params) = parseArtifacts(artifactsPath); + (artifactsChainId, ccr, params) = parseArtifacts(artifactsPath); if (chainId != artifactsChainId) { revert ArtifactsChainIdMismatch({actual: artifactsChainId, expected: chainId}); @@ -31,16 +31,16 @@ abstract contract UpgradeBase is ScriptInit { vm.startBroadcast(pk); { - OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); - // deploy new CCCP implementation with the same LidoLocator and CSModuleType - CCCP cccpImpl = _deployImplementation(params); - _upgradeProxy(proxy, cccpImpl, params); + // deploy new CCR implementation with the same LidoLocator and CSModuleType + CCR ccrImpl = _deployImplementation(params); + _upgradeProxy(proxy, ccrImpl, params); JsonObj memory deployJson = Json.newObj(); deployJson.set("ChainId", chainId); - deployJson.set("CCCPImpl", address(cccpImpl)); - deployJson.set("CCCP", address(cccp)); + deployJson.set("CCRImpl", address(ccrImpl)); + deployJson.set("CCR", address(ccr)); deployJson.set("LidoLocator", params.lidoLocatorAddress); deployJson.set("DeployParams", abi.encode(params)); vm.writeJson(deployJson.str, _deployJsonFilename()); @@ -49,24 +49,24 @@ abstract contract UpgradeBase is ScriptInit { vm.stopBroadcast(); } - function parseArtifacts(string memory artifactsPath) internal view returns (uint256, CCCP, DeployParams memory) { + function parseArtifacts(string memory artifactsPath) internal view returns (uint256, CCR, DeployParams memory) { string memory artifactsJson = vm.readFile(artifactsPath); return ( vm.parseJsonUint(artifactsJson, ".ChainId"), - CCCP(vm.parseJsonAddress(artifactsJson, ".CCCP")), + CCR(vm.parseJsonAddress(artifactsJson, ".CCR")), abi.decode(vm.parseJsonBytes(artifactsJson, ".DeployParams"), (DeployParams)) ); } /// @dev can be overridden to customize the upgrade process - function _deployImplementation(DeployParams memory _params) internal virtual returns (CCCP) { - return new CCCP(_params.lidoLocatorAddress, _params.csModuleType); + function _deployImplementation(DeployParams memory _params) internal virtual returns (CCR) { + return new CCR(_params.lidoLocatorAddress, _params.csModuleType); } /// @dev can be overridden to customize the upgrade process - function _upgradeProxy(OssifiableProxy _proxy, CCCP _impl, DeployParams memory _params) internal virtual { - // upgrade proxy to new CCCP implementation + function _upgradeProxy(OssifiableProxy _proxy, CCR _impl, DeployParams memory _params) internal virtual { + // upgrade proxy to new CCR implementation _proxy.proxy__upgradeTo(address(_impl)); // silent warning: unused variable _params; diff --git a/script/UpgradeHolesky.s.sol b/script/UpgradeHolesky.s.sol index 10ee6bc..0d1fc6d 100644 --- a/script/UpgradeHolesky.s.sol +++ b/script/UpgradeHolesky.s.sol @@ -10,7 +10,7 @@ contract UpgradeHolesky is UpgradeBase { // implementation constants params.lidoLocatorAddress = 0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8; params.csModuleType = "community-onchain-v1"; - params.defaultOperatorMaxValidators = 100; + params.defaultOperatorMaxKeys = 100; params.defaultBlockGasLimit = 1000000; // proxy @@ -18,8 +18,8 @@ contract UpgradeHolesky is UpgradeBase { // initial parameters params.committeeAddress = 0x401FD888B5E41113B7c0C47725A742bbc3A083EF; // Dev team EOA - params.optInMinDurationBlocks = 32; - params.optOutDelayDurationBlocks = 64; + params.optInDelayBlocks = 32; + params.optOutDelayBlocks = 64; _setUp(); } diff --git a/src/CCCP.sol b/src/CCCP.sol deleted file mode 100644 index 6bfff42..0000000 --- a/src/CCCP.sol +++ /dev/null @@ -1,569 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {AccessControlEnumerableUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; - -import {CCCPOperatorStatesStorage} from "./lib/CCCPOperatorStatesStorage.sol"; -import {CCCPConfigStorage} from "./lib/CCCPConfigStorage.sol"; -import {ICCCP} from "./interfaces/ICCCP.sol"; - -import {ILidoLocator} from "./interfaces/ILidoLocator.sol"; -import {StakingModule, IStakingRouter} from "./interfaces/IStakingRouter.sol"; -import {IStakingModule} from "./interfaces/IStakingModule.sol"; -import {CSMNodeOperator, ICSModule} from "./interfaces/ICSModule.sol"; -import {ICuratedModule} from "./interfaces/ICuratedModule.sol"; - -/** - * @title CCCP - * @notice CredibleCommitmentCurationProvider contract - */ -contract CCCP is - ICCCP, - Initializable, - CCCPConfigStorage, - CCCPOperatorStatesStorage, - AccessControlEnumerableUpgradeable, - PausableUpgradeable -{ - struct LidoOperatorCache { - uint24 moduleId; - address moduleAddress; - uint64 operatorId; - address rewardAddress; - uint64 totalKeys; - bool isActive; - } - - struct OperatorOptInOutFlags { - bool isOptedIn; - bool optOutInProgress; - bool optInAllowed; - bool optOutAllowed; - } - - bytes32 public constant COMMITTEE_ROLE = keccak256("COMMITTEE_ROLE"); - bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); - bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); - - ILidoLocator public immutable LIDO_LOCATOR; - // CSModule type, used for the operator's state retrieval - bytes32 internal immutable CS_MODULE_TYPE; - - event OptInSucceeded(uint256 indexed moduleId, uint256 indexed operatorId, address manager); - event OptOutRequested(uint256 indexed moduleId, uint256 indexed operatorId, bool isForced); - event ResetForcedOptOut(uint256 indexed moduleId, uint256 indexed operatorId); - - event KeysRangeUpdated(uint256 indexed moduleId, uint256 indexed operatorId, uint256 indexStart, uint256 indexEnd); - event OperatorManagerUpdated(uint256 indexed moduleId, uint256 indexed operatorId, address manager); - event RPCUrlUpdated(uint256 indexed moduleId, uint256 indexed operatorId, string rpcURL); - event ConfigUpdated( - uint256 optInMinDurationBlocks, - uint256 optOutDelayDurationBlocks, - uint256 defaultOperatorMaxValidators, - uint256 defaultBlockGasLimit - ); - event ModuleConfigUpdated( - uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxValidators, uint64 blockGasLimit - ); - - error RewardAddressMismatch(); - error OperatorNotActive(); - error ModuleDisabled(); - error OperatorOptedIn(); - error OperatorOptedOut(); - error OperatorOptInNotAllowed(); - error OperatorOptOutNotAllowed(); - error OperatorOptOutInProgress(); - error KeyIndexOutOfRange(); - error KeysRangeExceedMaxValidators(); - error KeyIndexMismatch(); - error InvalidOperatorId(); - error InvalidModuleId(); - error ZeroCommitteeAddress(); - error ZeroOperatorManagerAddress(); - error ZeroLocatorAddress(); - - constructor(address lidoLocator, bytes32 csModuleType) { - if (lidoLocator == address(0)) revert ZeroLocatorAddress(); - LIDO_LOCATOR = ILidoLocator(lidoLocator); - CS_MODULE_TYPE = csModuleType; - - _disableInitializers(); - } - - function initialize( - address committeeAddress, - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) external initializer { - if (committeeAddress == address(0)) revert ZeroCommitteeAddress(); - - __Pausable_init(); - __AccessControlEnumerable_init(); - - _grantRole(DEFAULT_ADMIN_ROLE, committeeAddress); - _grantRole(COMMITTEE_ROLE, committeeAddress); - _grantRole(PAUSE_ROLE, committeeAddress); - _grantRole(RESUME_ROLE, committeeAddress); - - _updateConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - - ///todo: pass module Ids to disable them at the start - } - - /// @notice Resume all operations after a pause - function unpause() external onlyRole(RESUME_ROLE) { - _unpause(); - } - - /// @notice Pause all operations - function pause() external onlyRole(PAUSE_ROLE) { - _pause(); - } - - /// @notice Opt-in operator to the module - /// @dev allows a repeated optin with different manager address - function optIn( - uint24 moduleId, - uint64 operatorId, - address manager, - uint64 keyIndexStart, - uint64 keyIndexEnd, - string calldata rpcURL - ) external whenNotPaused { - if (manager == address(0)) revert ZeroOperatorManagerAddress(); - - LidoOperatorCache memory _c; - /// @dev correctness of moduleId and operatorId are checked inside - _loadLidoNodeOperator(_c, moduleId, operatorId); - - // check if the caller is the operator's reward address - if (msg.sender != _c.rewardAddress) { - revert RewardAddressMismatch(); - } - - // check if the operator is active in Lido module - if (!_c.isActive) { - revert OperatorNotActive(); - } - - uint256 opKey = __encOpKey(moduleId, operatorId); - - // check if the operator is already has the state - OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (flags.isOptedIn) { - revert OperatorOptedIn(); - } else if (!flags.optInAllowed) { - revert OperatorOptInNotAllowed(); - } - - // save operator state - /// @dev also checks if the proposed manager is already registered for different operator - _setOperatorManager(opKey, manager); - emit OperatorManagerUpdated(moduleId, operatorId, manager); - - _setOperatorOptInOutState( - opKey, OptInOutState({optInBlock: uint64(block.number), optOutBlock: 0, isOptOutForced: false}) - ); - _checkAndUpdateKeysRange(_c, opKey, keyIndexStart, keyIndexEnd); - - /// @dev no checks on rpcUrl, so it can be rewritten on repeated opt-in - _setOperatorExtraData(opKey, ExtraData({rpcURL: rpcURL})); - emit RPCUrlUpdated(moduleId, operatorId, rpcURL); - - emit OptInSucceeded(moduleId, operatorId, manager); - } - - /// @notice Opt-out operator on behalf of the operator manager - /// @dev should be called by the operator manager address - function optOut() external whenNotPaused { - uint256 opKey = _getOpKeyByManager(msg.sender); - OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (!flags.isOptedIn) { - revert OperatorOptedOut(); - } else if (!flags.optOutAllowed) { - revert OperatorOptOutNotAllowed(); - } - - optInOutState.optOutBlock = uint64(block.number); - _setOperatorOptInOutState(opKey, optInOutState); - - (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); - emit OptOutRequested(moduleId, operatorId, false); - } - - /// @notice Opt-out operator on behalf of the committee - function optOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { - uint256 opKey = _getOpKeyById(moduleId, operatorId); - - OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (!flags.isOptedIn) { - revert OperatorOptedOut(); - } else if (flags.optOutInProgress) { - revert OperatorOptOutInProgress(); - } - /// @dev ignore optOutAllowed, as the committee can force opt-out - - optInOutState.optOutBlock = uint64(block.number); - optInOutState.isOptOutForced = true; - _setOperatorOptInOutState(opKey, optInOutState); - - emit OptOutRequested(moduleId, operatorId, true); - } - - /// @notice Update the operator's keys range - /// @dev should be called by the operator manager address - function updateKeysRange(uint64 keyIndexStart, uint64 keyIndexEnd) external whenNotPaused { - uint256 opKey = _getOpKeyByManager(msg.sender); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); - if (!flags.isOptedIn) { - revert OperatorOptedOut(); - } else if (flags.optOutInProgress) { - revert OperatorOptOutInProgress(); - } - - KeysRange memory keysRange = _getOperatorKeysRange(opKey); - if ( - keyIndexStart > keysRange.indexStart || keyIndexEnd < keysRange.indexEnd - || (keyIndexStart == keysRange.indexStart && keyIndexEnd == keysRange.indexEnd) - ) { - revert KeyIndexMismatch(); - } - LidoOperatorCache memory _c; - (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); - _loadLidoNodeOperator(_c, moduleId, operatorId); - _checkAndUpdateKeysRange(_c, opKey, keyIndexStart, keyIndexEnd); - } - - /// @notice Update the operator's manager address - /// @dev should be called by the operator reward address - function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external whenNotPaused { - if (newManager == address(0)) revert ZeroOperatorManagerAddress(); - - LidoOperatorCache memory _c; - /// @dev correctness of moduleId and operatorId are checked inside - _loadLidoNodeOperator(_c, moduleId, operatorId); - - // check if the caller is the operator's reward address - if (msg.sender != _c.rewardAddress) { - revert RewardAddressMismatch(); - } - - uint256 opKey = _getOpKeyById(moduleId, operatorId); - OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - if (!flags.isOptedIn) { - revert OperatorOptedOut(); - } else if (flags.optOutInProgress) { - revert OperatorOptOutInProgress(); - } - - /// @dev also checks if the proposed manager is already registered for different operator - _setOperatorManager(opKey, newManager); - emit OperatorManagerUpdated(moduleId, operatorId, newManager); - } - - function getOperator(address manager) - external - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) - { - uint256 opKey = _getOpKeyByManager(manager); - return _getOperator(opKey); - } - - function getOperator(uint24 _moduleId, uint64 _operatorId) - external - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) - { - uint256 opKey = _getOpKeyById(_moduleId, _operatorId); - return _getOperator(opKey); - } - - function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address) { - uint256 opKey = _getOpKeyById(moduleId, operatorId); - return _getOperatorManager(opKey); - } - - function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool) { - LidoOperatorCache memory _c; - _loadLidoNodeOperator(_c, moduleId, operatorId); - uint256 opKey = __encOpKey(moduleId, operatorId); - OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); - - return _isOperatorIsEnabledForPreconf(optInOutState, moduleId, _c.isActive); - } - - function getOperatorAllowedValidators(uint24 moduleId, uint64 operatorId) - external - view - returns (uint64 allowedValidators) - { - // check if the module has max validators limit - allowedValidators = getModuleOperatorMaxValidators(moduleId); - if (allowedValidators == 0) { - return 0; - } - // check if the operator is active in Lido module - LidoOperatorCache memory _c; - _loadLidoNodeOperator(_c, moduleId, operatorId); - if (!_c.isActive) { - return 0; - } - // min(_c.totalKeys, maxValidators) - if (allowedValidators > _c.totalKeys) { - allowedValidators = _c.totalKeys; - } - - uint256 opKey = __encOpKey(moduleId, operatorId); - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); - if (flags.isOptedIn && !flags.optOutInProgress) { - KeysRange memory keysRange = _getOperatorKeysRange(opKey); - uint64 totalKeys = keysRange.indexEnd - keysRange.indexStart + 1; - // check if the operator has already reached the max validators limit - if (totalKeys >= allowedValidators) { - return 0; - } - unchecked { - allowedValidators -= totalKeys; - } - } else if (!flags.optInAllowed) { - return 0; - } - } - - function getConfig() - external - view - returns ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) - { - return _getConfig(); - } - - function getModuleConfig(uint24 moduleId) - external - view - returns (bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) - { - return _getModuleConfig(moduleId); - } - - function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64) { - (,,, uint64 defaultBlockGasLimit) = _getConfig(); - (bool isDisabled,, uint64 blockGasLimit) = _getModuleConfig(moduleId); - return isDisabled ? 0 : blockGasLimit == 0 ? defaultBlockGasLimit : blockGasLimit; - } - - function getModuleOperatorMaxValidators(uint24 moduleId) public view returns (uint64) { - (,, uint64 defaultOperatorMaxValidators,) = _getConfig(); - (bool isDisabled, uint64 operatorMaxValidators,) = _getModuleConfig(moduleId); - return isDisabled ? 0 : operatorMaxValidators == 0 ? defaultOperatorMaxValidators : operatorMaxValidators; - } - - function getContractVersion() external view returns (uint64) { - return _getInitializedVersion(); - } - - function resetForcedOptOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { - uint256 opKey = _getOpKeyById(moduleId, operatorId); - OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); - if (optInOutState.isOptOutForced) { - optInOutState.isOptOutForced = false; - _setOperatorOptInOutState(opKey, optInOutState); - } - emit ResetForcedOptOut(moduleId, operatorId); - } - - /// @notice update min opt-in and opt-out delay durations, - /// default operator's max validators for the module and block gas limit - function setConfig( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) external onlyRole(COMMITTEE_ROLE) { - _updateConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - } - - /// @notice Update Disable/enable state and operator's max validators for the module - function setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) - external - onlyRole(COMMITTEE_ROLE) - { - /// @dev check moduleId via staking router - LidoOperatorCache memory _c; - _loadLidoModuleData(_c, moduleId); - - _setModuleConfig(moduleId, isDisabled, operatorMaxValidators, blockGasLimit); - emit ModuleConfigUpdated(moduleId, isDisabled, operatorMaxValidators, blockGasLimit); - } - - function _updateConfig( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) internal { - _setConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - emit ConfigUpdated( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - } - - function _checkAndUpdateKeysRange( - LidoOperatorCache memory _c, - uint256 opKey, - uint64 keyIndexStart, - uint64 keyIndexEnd - ) internal { - _checkKeysRangeIsValid(_c.totalKeys, keyIndexStart, keyIndexEnd); - _checkModuleParams(_c.moduleId, keyIndexStart, keyIndexEnd); - - // save operator state - _setOperatorKeysRange(opKey, keyIndexStart, keyIndexEnd); - emit KeysRangeUpdated(_c.moduleId, _c.operatorId, keyIndexStart, keyIndexEnd); - } - - function _getOperator(uint256 opKey) - internal - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state) - { - LidoOperatorCache memory _c; - (moduleId, operatorId) = __decOpKey(opKey); - _loadLidoNodeOperator(_c, moduleId, operatorId); - state = _getOperatorState(opKey); - isEnabled = _isOperatorIsEnabledForPreconf(state.optInOutState, moduleId, _c.isActive); - - return (moduleId, operatorId, isEnabled, state); - } - - function _isOperatorIsEnabledForPreconf(OptInOutState memory optInOutState, uint24 moduleId, bool isOperatorActive) - internal - view - returns (bool) - { - OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); - (bool isModuleDisabled,,) = _getModuleConfig(moduleId); - // operator is enabled: - // - if it's s opted in - // - if module not disabled - // - if operator is active in Lido module - // - if the contract is not paused - return flags.isOptedIn && !isModuleDisabled && isOperatorActive && !paused(); - } - - function _checkModuleParams(uint24 moduleId, uint64 startIndex, uint64 endIndex) internal view { - uint64 maxValidators = getModuleOperatorMaxValidators(moduleId); - if (maxValidators == 0) { - revert ModuleDisabled(); - } - uint64 totalKeys = endIndex - startIndex + 1; - if (totalKeys > maxValidators) { - revert KeysRangeExceedMaxValidators(); - } - } - - function _checkKeysRangeIsValid(uint64 totalKeys, uint64 startIndex, uint64 endIndex) internal pure { - if (startIndex > endIndex) { - revert KeyIndexMismatch(); - } - - if (endIndex >= totalKeys || startIndex >= totalKeys) { - revert KeyIndexOutOfRange(); - } - } - - function _calcOptInOutFlags(OptInOutState memory optInOutState) - internal - view - returns (OperatorOptInOutFlags memory flags) - { - uint64 blockNumber = uint64(block.number); - (uint64 optInMinDurationBlocks, uint64 optOutDelayDurationBlocks,,) = _getConfig(); - - bool optOutInProgress = - optInOutState.optOutBlock > 0 && optInOutState.optOutBlock + optOutDelayDurationBlocks >= blockNumber; - - bool isOptedIn = optInOutState.optInBlock > 0 && (optInOutState.optOutBlock == 0 || optOutInProgress); - bool optInAllowed = !isOptedIn && !optInOutState.isOptOutForced; - bool optOutAllowed = - isOptedIn && !optOutInProgress && optInOutState.optInBlock + optInMinDurationBlocks < blockNumber; - - return OperatorOptInOutFlags({ - isOptedIn: isOptedIn, - optOutInProgress: optOutInProgress, - optInAllowed: optInAllowed, - optOutAllowed: optOutAllowed - }); - } - - /// CACHE - - /// @notice Get the staking router address from LidoLocator - function _getStakingRouter() internal view returns (IStakingRouter) { - return IStakingRouter(LIDO_LOCATOR.stakingRouter()); - } - - /// @notice Prepare the cache for the staking module and node operator - function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint24 moduleId, uint64 operatorId) internal view { - _loadLidoModuleData(_c, moduleId); - _loadLidoNodeOperatorData(_c, operatorId); - } - - function _loadLidoModuleData(LidoOperatorCache memory _c, uint24 moduleId) internal view { - /// @dev module id validity check is done in the staking router - StakingModule memory module = _getStakingRouter().getStakingModule(moduleId); - - _c.moduleId = moduleId; - _c.moduleAddress = module.stakingModuleAddress; - } - - function _loadLidoNodeOperatorData(LidoOperatorCache memory _c, uint64 operatorId) internal view { - if (_c.moduleId == 0) { - revert InvalidModuleId(); - } - - /// @dev check if the operatorId is valid - uint64 totalOperatorsCount = uint64(IStakingModule(_c.moduleAddress).getNodeOperatorsCount()); - if (operatorId >= totalOperatorsCount) { - revert InvalidOperatorId(); - } - _c.operatorId = operatorId; - - /// @dev check for the CSModule type - bytes32 moduleType = IStakingModule(_c.moduleAddress).getType(); - if (moduleType == CS_MODULE_TYPE) { - ICSModule module = ICSModule(_c.moduleAddress); - _c.isActive = module.getNodeOperatorIsActive(operatorId); - CSMNodeOperator memory operator = module.getNodeOperator(operatorId); - _c.rewardAddress = operator.rewardAddress; - _c.totalKeys = operator.totalAddedKeys; - } else { - ICuratedModule module = ICuratedModule(_c.moduleAddress); - (_c.isActive,, _c.rewardAddress,,, _c.totalKeys) = module.getNodeOperator(operatorId, false); - } - } -} diff --git a/src/CCR.sol b/src/CCR.sol new file mode 100644 index 0000000..d79cb7a --- /dev/null +++ b/src/CCR.sol @@ -0,0 +1,728 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {AccessControlEnumerableUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; + +import {CCROperatorStatesStorage} from "./lib/CCROperatorStatesStorage.sol"; +import {CCRConfigStorage} from "./lib/CCRConfigStorage.sol"; +import {ICCR} from "./interfaces/ICCR.sol"; + +import {ILidoLocator} from "./interfaces/ILidoLocator.sol"; +import {StakingModule, IStakingRouter} from "./interfaces/IStakingRouter.sol"; +import {IStakingModule} from "./interfaces/IStakingModule.sol"; +import {CSMNodeOperator, ICSModule} from "./interfaces/ICSModule.sol"; +import {ICuratedModule} from "./interfaces/ICuratedModule.sol"; + +/** + * @title CCR + * @notice CredibleCommitmentCurationProvider contract + */ +contract CCR is + ICCR, + Initializable, + CCRConfigStorage, + CCROperatorStatesStorage, + AccessControlEnumerableUpgradeable, + PausableUpgradeable +{ + struct LidoOperatorCache { + uint24 moduleId; + address moduleAddress; + uint64 operatorId; + address rewardAddress; + uint64 totalKeys; + bool isActive; + } + + struct OperatorOptInOutFlags { + bool isOptedIn; + // bool isOptedOut; + bool isOptInDelayed; + bool isOptOutDelayed; + } + + bytes32 public constant COMMITTEE_ROLE = keccak256("COMMITTEE_ROLE"); + bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); + bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); + uint64 public constant MAX_COMMITMENTS = 64; + + ILidoLocator public immutable LIDO_LOCATOR; + // CSModule type, used for the operator's state retrieval + bytes32 internal immutable CS_MODULE_TYPE; + + event OptInSucceeded(uint256 indexed moduleId, uint256 indexed operatorId); + event OptOutRequested(uint256 indexed moduleId, uint256 indexed operatorId, bool isForced); + event UnblockOperator(uint256 indexed moduleId, uint256 indexed operatorId); + + // event KeyRangeUpdated(uint256 indexed moduleId, uint256 indexed operatorId, uint256 indexStart, uint256 indexEnd); + event OperatorCommitmentAdded( + uint256 indexed moduleId, uint256 indexed operatorId, uint256 keyIndexStart, uint256 keyIndexEnd, string rpcURL + ); + event OperatorCommitmentDeleted( + uint256 indexed moduleId, uint256 indexed operatorId, uint256 keyIndexStart, uint256 keyIndexEnd + ); + event OperatorManagerUpdated(uint256 indexed moduleId, uint256 indexed operatorId, address manager); + event OperatorDelegateAdded(uint256 indexed moduleId, uint256 indexed operatorId, bytes key); + event OperatorDelegateDeleted(uint256 indexed moduleId, uint256 indexed operatorId, bytes key); + // event RPCUrlUpdated(uint256 indexed moduleId, uint256 indexed operatorId, string rpcURL); + event ConfigUpdated( + uint256 optInDelayBlocks, + uint256 optOutDelayBlocks, + uint256 defaultOperatorMaxKeys, + uint256 defaultBlockGasLimit + ); + event ModuleConfigUpdated(uint256 indexed moduleId, bool isDisabled, uint256 operatorMaxKeys, uint64 blockGasLimit); + + error RewardAddressMismatch(); + error OperatorNotActive(); + error OperatorBlocked(); + error ModuleDisabled(); + error OperatorOptedIn(); + error OperatorOptedOut(); + error OperatorOptInNotAllowed(); + error OperatorOptOutNotAllowed(); + error OperatorOptOutInProgress(); + error KeyIndexOutOfRange(); + error KeyRangeExceedMaxKeys(); + error KeyIndexOverlapExisting(); + error KeyIndexMismatch(); + error InvalidOperatorId(); + error InvalidModuleId(); + error ZeroCommitteeAddress(); + error ZeroOperatorManagerAddress(); + error ZeroLocatorAddress(); + error CommitmentsLimitReached(); + error OptInActionDelayed(); + error OptOutActionDelayed(); + + constructor(address lidoLocator, bytes32 csModuleType) { + if (lidoLocator == address(0)) revert ZeroLocatorAddress(); + LIDO_LOCATOR = ILidoLocator(lidoLocator); + CS_MODULE_TYPE = csModuleType; + + _disableInitializers(); + } + + function initialize( + address committeeAddress, + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) external initializer { + if (committeeAddress == address(0)) revert ZeroCommitteeAddress(); + + __Pausable_init(); + __AccessControlEnumerable_init(); + + _grantRole(DEFAULT_ADMIN_ROLE, committeeAddress); + _grantRole(COMMITTEE_ROLE, committeeAddress); + _grantRole(PAUSE_ROLE, committeeAddress); + _grantRole(RESUME_ROLE, committeeAddress); + + _updateConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + + ///todo: pass module Ids to disable them at the start + } + + /// @notice Resume all operations after a pause + function unpause() external onlyRole(RESUME_ROLE) { + _unpause(); + } + + /// @notice Pause all operations + function pause() external onlyRole(PAUSE_ROLE) { + _pause(); + } + + /// @notice Opt-in operator to the module + /// @dev allows a repeated optin with different manager address + function optIn( + uint24 moduleId, + uint64 operatorId, + address manager, + uint64 indexStart, + uint64 indexEnd, + string calldata rpcURL + ) external whenNotPaused { + if (manager == address(0)) revert ZeroOperatorManagerAddress(); + + LidoOperatorCache memory _c; + /// @dev correctness of moduleId and operatorId are checked inside + _loadLidoNodeOperator(_c, moduleId, operatorId); + + // check if the caller is the operator's reward address + if (msg.sender != _c.rewardAddress) { + revert RewardAddressMismatch(); + } + + // check if the operator is active in Lido module + if (!_c.isActive) { + revert OperatorNotActive(); + } + + uint256 opKey = __encOpKey(moduleId, operatorId); + if (_getIsOperatorBlocked(opKey)) { + revert OperatorBlocked(); + } + + // check if the operator is already has the state + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); + if (flags.isOptedIn) { + revert OperatorOptedIn(); + } + _checkOptInDelayed(flags); + + // save operator state + /// @dev also checks if the proposed manager is already registered for different operator + _setOperatorManager(opKey, manager); + emit OperatorManagerUpdated(moduleId, operatorId, manager); + + /// @dev clear the previous commitments (could be expensive!) + _delOperatorAllCommitments(opKey); + + _checkAndAddCommitment(_c, opKey, indexStart, indexEnd, rpcURL); + emit OperatorCommitmentAdded(moduleId, operatorId, indexStart, indexEnd, rpcURL); + + emit OptInSucceeded(moduleId, operatorId); + + optInOutState.optInBlock = uint64(block.number); + optInOutState.optOutBlock = 0; + _setOptInOutStateWithOptInDelay(opKey, optInOutState); + } + + /// @notice Opt-out operator on behalf of the operator manager + /// @dev should be called by the operator manager address + function optOut() external whenNotPaused { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + _checkOptInDelayed(flags); + + optInOutState.optOutBlock = uint64(block.number); + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OptOutRequested(moduleId, operatorId, false); + } + + /// @notice Opt-out operator on behalf of the committee + function optOut(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + (OptInOutState memory optInOutState,) = _checkOptInAndLoadState(opKey); + /// @dev ignore isOptOutDelayed, as the committee can force opt-out + + optInOutState.optOutBlock = uint64(block.number); + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + _setIsOperatorBlocked(opKey, true); + + emit OptOutRequested(moduleId, operatorId, true); + } + + function getOperatorDelegates(uint24 moduleId, uint64 operatorId) + external + view + returns (Delegate[] memory delegates) + { + uint256 opKey = __encOpKey(moduleId, operatorId); + delegates = _getOperatorDelegatesStorage(opKey); + } + + function addOperatorDelegate(bytes memory key) external whenNotPaused { + (uint256 opKey,,) = _checkOptInAndLoadStateByManager(msg.sender); + + /// @dev no checks on delegate key + _addOperatorDelegate(opKey, key); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorDelegateAdded(moduleId, operatorId, key); + } + + function delOperatorDelegate(uint256 dIdx) external whenNotPaused { + (uint256 opKey,,) = _checkOptInAndLoadStateByManager(msg.sender); + + bytes memory key = _getOperatorDelegateStorage(opKey, dIdx).key; + + _delOperatorDelegate(opKey, dIdx); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorDelegateDeleted(moduleId, operatorId, key); + } + + function getOperatorCommitments(uint24 moduleId, uint64 operatorId) + external + view + returns (Commitment[] memory commitments) + { + uint256 opKey = __encOpKey(moduleId, operatorId); + commitments = _getOperatorCommitmentsStorage(opKey); + } + + /// @notice Update the operator's keys kr + /// @dev should be called by the operator manager address + /// @dev `opt-in` action + function addOperatorCommitment(uint64 indexStart, uint64 indexEnd, string calldata rpcUrl) external whenNotPaused { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + _checkOptInDelayed(flags); + + LidoOperatorCache memory _c; + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + _loadLidoNodeOperator(_c, moduleId, operatorId); + _checkAndAddCommitment(_c, opKey, indexStart, indexEnd, rpcUrl); + emit OperatorCommitmentAdded(moduleId, operatorId, indexStart, indexEnd, rpcUrl); + + /// @dev update the opt-in delay off block and save state + _setOptInOutStateWithOptInDelay(opKey, optInOutState); + } + + /// @notice Delete the operator's commitment (keys kr) + /// @dev should be called by the operator manager address + /// @dev `opt-out` action + function delOperatorCommitment(uint256 cIdx) external whenNotPaused { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + _checkOptOutDelayed(flags); + + KeyRange memory kr = _getOperatorCommitmentStorage(opKey, cIdx).keyRange; + _delOperatorCommitment(opKey, cIdx); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorCommitmentDeleted(moduleId, operatorId, kr.indexStart, kr.indexEnd); + + /// @dev update the opt-out delay off block and save state + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + } + + /// @dev if the new kr just extends existing one - it's considered as an `opt-in` action, otherwise - `opt-out` action + function updateOperatorCommitment(uint256 cIdx, uint64 indexStart, uint64 indexEnd, string calldata rpcUrl) + external + whenNotPaused + { + (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) = + _checkOptInAndLoadStateByManager(msg.sender); + + bool isExtend = true; + KeyRange memory kr = _getOperatorCommitmentStorage(opKey, cIdx).keyRange; + if (indexStart > kr.indexStart || indexEnd < kr.indexEnd) { + isExtend = false; + } + + if (isExtend) { + _checkOptInDelayed(flags); + } else { + _checkOptOutDelayed(flags); + } + + // delete the old commitment + _delOperatorCommitment(opKey, cIdx); + + // add the new commitment + LidoOperatorCache memory _c; + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + _loadLidoNodeOperator(_c, moduleId, operatorId); + _checkAndAddCommitment(_c, opKey, indexStart, indexEnd, rpcUrl); + emit OperatorCommitmentDeleted(moduleId, operatorId, kr.indexStart, kr.indexEnd); + emit OperatorCommitmentAdded(moduleId, operatorId, indexStart, indexEnd, rpcUrl); + + if (isExtend) { + /// @dev update the opt-in delay off block and save state + _setOptInOutStateWithOptInDelay(opKey, optInOutState); + } else { + /// @dev update the opt-out delay off block and save state + _setOptInOutStateWithOptOutDelay(opKey, optInOutState); + } + } + + function updateOperatorCommitmentExtraData(uint256 cIdx, string calldata rpcUrl) external whenNotPaused { + (uint256 opKey,,) = _checkOptInAndLoadStateByManager(msg.sender); + + KeyRange memory kr = _getOperatorCommitmentStorage(opKey, cIdx).keyRange; + _setOperatorCommitmentRPCUrl(opKey, cIdx, rpcUrl); + (uint24 moduleId, uint64 operatorId) = __decOpKey(opKey); + emit OperatorCommitmentAdded(moduleId, operatorId, kr.indexStart, kr.indexEnd, rpcUrl); + } + + function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address) { + uint256 opKey = __encOpKey(moduleId, operatorId); + return _getOperatorManager(opKey); + } + + /// @notice Update the operator's manager address + /// @dev should be called by the operator reward address + function updateOperatorManager(uint24 moduleId, uint64 operatorId, address newManager) external whenNotPaused { + if (newManager == address(0)) revert ZeroOperatorManagerAddress(); + + LidoOperatorCache memory _c; + /// @dev correctness of moduleId and operatorId are checked inside + _loadLidoNodeOperator(_c, moduleId, operatorId); + + // check if the caller is the operator's reward address + if (msg.sender != _c.rewardAddress) { + revert RewardAddressMismatch(); + } + + // check if the operator is active in Lido module + if (!_c.isActive) { + revert OperatorNotActive(); + } + + uint256 opKey = _getOpKeyById(moduleId, operatorId); + if (_getIsOperatorBlocked(opKey)) { + revert OperatorBlocked(); + } + + _checkOptInAndLoadState(opKey); + + /// @dev also checks if the proposed manager is already registered for different operator + _setOperatorManager(opKey, newManager); + emit OperatorManagerUpdated(moduleId, operatorId, newManager); + } + + function getOperator(uint24 moduleId, uint64 operatorId) + external + view + returns (address manager, bool isBlocked, bool isEnabled, OptInOutState memory optInOutState) + { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + manager = _getOperatorManager(opKey); + isBlocked = _getIsOperatorBlocked(opKey); + optInOutState = _getOperatorOptInOutState(opKey); + + isEnabled = _isOperatorIsEnabledForPreconf(moduleId, operatorId, optInOutState); + } + + // + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool) { + uint256 opKey = __encOpKey(moduleId, operatorId); + OptInOutState memory optInOutState = _getOperatorOptInOutState(opKey); + + return _isOperatorIsEnabledForPreconf(moduleId, operatorId, optInOutState); + } + + function getOperatorAllowedKeys(uint24 moduleId, uint64 operatorId) external view returns (uint64 allowedKeys) { + uint256 opKey = __encOpKey(moduleId, operatorId); + if (_getIsOperatorBlocked(opKey)) { + return 0; + } + + // check if the module has max keys limit + allowedKeys = getModuleOperatorMaxKeys(moduleId); + if (allowedKeys == 0) { + return 0; + } + // check if the operator is active in Lido module + LidoOperatorCache memory _c; + _loadLidoNodeOperator(_c, moduleId, operatorId); + if (!_c.isActive) { + return 0; + } + // min(operator totalAddedKeys, moduleMaxKeys) + if (allowedKeys > _c.totalKeys) { + allowedKeys = _c.totalKeys; + } + + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(_getOperatorOptInOutState(opKey)); + if (flags.isOptedIn) { + (uint64 committedKeys,) = _loadCommitmentKeyRanges(opKey); + // check if the operator has already reached the max keys limit + if (committedKeys >= allowedKeys) { + return 0; + } + unchecked { + allowedKeys -= committedKeys; + } + } + } + + function getConfig() + external + view + returns ( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) + { + return _getConfig(); + } + + function getModuleConfig(uint24 moduleId) + external + view + returns (bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) + { + return _getModuleConfig(moduleId); + } + + function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64) { + (,,, uint64 defaultBlockGasLimit) = _getConfig(); + (bool isDisabled,, uint64 blockGasLimit) = _getModuleConfig(moduleId); + return isDisabled ? 0 : blockGasLimit == 0 ? defaultBlockGasLimit : blockGasLimit; + } + + function getModuleOperatorMaxKeys(uint24 moduleId) public view returns (uint64) { + (,, uint64 defaultOperatorMaxKeys,) = _getConfig(); + (bool isDisabled, uint64 operatorMaxKeys,) = _getModuleConfig(moduleId); + return isDisabled ? 0 : operatorMaxKeys == 0 ? defaultOperatorMaxKeys : operatorMaxKeys; + } + + function getContractVersion() external view returns (uint64) { + return _getInitializedVersion(); + } + + function unblockOperator(uint24 moduleId, uint64 operatorId) external onlyRole(COMMITTEE_ROLE) { + uint256 opKey = _getOpKeyById(moduleId, operatorId); + bool isBlocked = _getIsOperatorBlocked(opKey); + if (isBlocked) { + _setIsOperatorBlocked(opKey, false); + } + emit UnblockOperator(moduleId, operatorId); + } + + /// @notice update min opt-in and opt-out delay durations, + /// default operator's max keys for the module and block gas limit + function setConfig( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) external onlyRole(COMMITTEE_ROLE) { + _updateConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + } + + /// @notice Update Disable/enable state and operator's max keys for the module + function setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) + external + onlyRole(COMMITTEE_ROLE) + { + /// @dev check moduleId via staking router + LidoOperatorCache memory _c; + _loadLidoModuleData(_c, moduleId); + + _setModuleConfig(moduleId, isDisabled, operatorMaxKeys, blockGasLimit); + emit ModuleConfigUpdated(moduleId, isDisabled, operatorMaxKeys, blockGasLimit); + } + + function _updateConfig( + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, + uint64 defaultBlockGasLimit + ) internal { + _setConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + emit ConfigUpdated(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + } + + function _checkAndAddCommitment( + LidoOperatorCache memory _c, + uint256 opKey, + uint64 indexStart, + uint64 indexEnd, + string calldata rpcUrl + ) internal { + if (indexStart > indexEnd) { + revert KeyIndexMismatch(); + } + + if (indexEnd >= _c.totalKeys) { + revert KeyIndexOutOfRange(); + } + + (uint64 committedKeys, KeyRange[] memory ranges) = _loadCommitmentKeyRanges(opKey); + + if (ranges.length >= MAX_COMMITMENTS) { + revert CommitmentsLimitReached(); + } + + // check for overlapping with existing ranges + for (uint256 i = 0; i < ranges.length;) { + // condition for overlapping two ranges [s1,e1] and [s2,e2]: + // they overlap if s1 <= e2 and s2 <= e1. + if ((indexStart <= ranges[i].indexEnd) && (ranges[i].indexStart <= indexEnd)) { + revert KeyIndexOverlapExisting(); + } + + unchecked { + ++i; + } + } + + // new total committed keys + committedKeys += indexEnd - indexStart + 1; + _checkModuleParams(_c.moduleId, committedKeys); + + /// @dev no checks on rpcUrl + _addOperatorCommitment(opKey, indexStart, indexEnd, rpcUrl); + } + + function _loadCommitmentKeyRanges(uint256 opKey) + internal + view + returns (uint64 committedKeys, KeyRange[] memory ranges) + { + Commitment[] storage commitments = _getOperatorCommitmentsStorage(opKey); + uint256 commitmentsLen = commitments.length; + uint64 rangeLen; + + ranges = new KeyRange[](commitmentsLen); + + for (uint256 i = 0; i < commitmentsLen;) { + ranges[i] = commitments[i].keyRange; + unchecked { + rangeLen = ranges[i].indexEnd - ranges[i].indexStart + 1; + ++i; + } + committedKeys += rangeLen; + } + } + + function _checkOptInAndLoadStateByManager(address manager) + internal + view + returns (uint256 opKey, OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) + { + opKey = _getOpKeyByManager(manager); + (optInOutState, flags) = _checkOptInAndLoadState(opKey); + } + + function _checkOptInAndLoadState(uint256 opKey) + internal + view + returns (OptInOutState memory optInOutState, OperatorOptInOutFlags memory flags) + { + optInOutState = _getOperatorOptInOutState(opKey); + flags = _calcOptInOutFlags(optInOutState); + + if (!flags.isOptedIn) { + revert OperatorOptedOut(); + } + } + + function _isOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId, OptInOutState memory optInOutState) + internal + view + returns (bool) + { + OperatorOptInOutFlags memory flags = _calcOptInOutFlags(optInOutState); + (bool isModuleDisabled,,) = _getModuleConfig(moduleId); + + LidoOperatorCache memory _c; + _loadLidoNodeOperator(_c, moduleId, operatorId); + + // operator is enabled: + // - if it's s opted in + // - if module not disabled + // - if operator is active in Lido module + // - if the contract is not paused + return flags.isOptedIn && !isModuleDisabled && _c.isActive && !paused(); + } + + function _checkModuleParams(uint24 moduleId, uint64 committedKeys) internal view { + uint64 maxKeys = getModuleOperatorMaxKeys(moduleId); + if (maxKeys == 0) { + revert ModuleDisabled(); + } + if (committedKeys > maxKeys) { + revert KeyRangeExceedMaxKeys(); + } + } + + function _calcOptInOutFlags(OptInOutState memory optInOutState) + internal + view + returns (OperatorOptInOutFlags memory flags) + { + bool isOptedOut = optInOutState.optOutBlock > 0; // && blockNumber >= optInOutState.optOutBlock; + bool isOptedIn = optInOutState.optInBlock > 0 && !isOptedOut; + // any opt-in action is delayed after any opt-out action is made + bool isOptInDelayed = _isDelay(optInOutState.optOutBlock); + // any opt-out action is delayed after any opt-in action is made + bool isOptOutDelayed = _isDelay(optInOutState.optInBlock); + + return OperatorOptInOutFlags({ + isOptedIn: isOptedIn, + // isOptedOut: isOptedOut, + isOptInDelayed: isOptInDelayed, + isOptOutDelayed: isOptOutDelayed + }); + } + + /// CACHE + + /// @notice Get the staking router address from LidoLocator + function _getStakingRouter() internal view returns (IStakingRouter) { + return IStakingRouter(LIDO_LOCATOR.stakingRouter()); + } + + /// @notice Prepare the cache for the staking module and node operator + function _loadLidoNodeOperator(LidoOperatorCache memory _c, uint24 moduleId, uint64 operatorId) internal view { + _loadLidoModuleData(_c, moduleId); + _loadLidoNodeOperatorData(_c, operatorId); + } + + function _loadLidoModuleData(LidoOperatorCache memory _c, uint24 moduleId) internal view { + /// @dev module id validity check is done in the staking router + StakingModule memory module = _getStakingRouter().getStakingModule(moduleId); + + _c.moduleId = moduleId; + _c.moduleAddress = module.stakingModuleAddress; + } + + function _loadLidoNodeOperatorData(LidoOperatorCache memory _c, uint64 operatorId) internal view { + if (_c.moduleId == 0) { + revert InvalidModuleId(); + } + + /// @dev check if the operatorId is valid + uint64 totalOperatorsCount = uint64(IStakingModule(_c.moduleAddress).getNodeOperatorsCount()); + if (operatorId >= totalOperatorsCount) { + revert InvalidOperatorId(); + } + _c.operatorId = operatorId; + + /// @dev check for the CSModule type + bytes32 moduleType = IStakingModule(_c.moduleAddress).getType(); + if (moduleType == CS_MODULE_TYPE) { + ICSModule module = ICSModule(_c.moduleAddress); + _c.isActive = module.getNodeOperatorIsActive(operatorId); + CSMNodeOperator memory operator = module.getNodeOperator(operatorId); + _c.rewardAddress = operator.rewardAddress; + _c.totalKeys = operator.totalAddedKeys; + } else { + ICuratedModule module = ICuratedModule(_c.moduleAddress); + (_c.isActive,, _c.rewardAddress,,, _c.totalKeys) = module.getNodeOperator(operatorId, false); + } + } + + function _isDelay(uint64 offBlock) internal view returns (bool) { + return offBlock >= block.number; + } + + function _checkOptInDelayed(OperatorOptInOutFlags memory flags) internal pure { + if (flags.isOptInDelayed) { + revert OptInActionDelayed(); + } + } + + function _checkOptOutDelayed(OperatorOptInOutFlags memory flags) internal pure { + if (flags.isOptOutDelayed) { + revert OptOutActionDelayed(); + } + } + + function _setOptInOutStateWithOptOutDelay(uint256 opKey, OptInOutState memory state) internal { + (, uint64 optOutDelayBlocks,,) = _getConfig(); + state.optOutDelayOffBlock = uint64(block.number + optOutDelayBlocks); + _setOperatorOptInOutState(opKey, state); + } + + function _setOptInOutStateWithOptInDelay(uint256 opKey, OptInOutState memory state) internal { + (uint64 optInDelayBlocks,,,) = _getConfig(); + state.optInDelayOffBlock = uint64(block.number + optInDelayBlocks); + _setOperatorOptInOutState(opKey, state); + } +} diff --git a/src/interfaces/ICCCP.sol b/src/interfaces/ICCCP.sol deleted file mode 100644 index 644cc5b..0000000 --- a/src/interfaces/ICCCP.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.28; - -import {ICCCPOperatorStatesStorage} from "./ICCCPOperatorStatesStorage.sol"; - -/** - * @title ICCCP - * @notice Interface for CredibleCommitmentCurationProvider. - */ -interface ICCCP is ICCCPOperatorStatesStorage { - function optIn( - uint24 moduleId, - uint64 operatorId, - address manager, - uint64 keyIndexStart, - uint64 keyIndexEnd, - string calldata rpcURL - ) external; - function optOut() external; - function updateKeysRange(uint64 keyIndexStart, uint64 keyIndexEnd) external; - function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external; - - function getOperator(address manager) - external - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state); - - function getOperator(uint24 _moduleId, uint64 _operatorId) - external - view - returns (uint24 moduleId, uint64 operatorId, bool isEnabled, OperatorState memory state); - function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64); - function getModuleOperatorMaxValidators(uint24 moduleId) external view returns (uint64); - function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address); - function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool); - function getOperatorAllowedValidators(uint24 moduleId, uint64 operatorId) external view returns (uint64); -} diff --git a/src/interfaces/ICCR.sol b/src/interfaces/ICCR.sol new file mode 100644 index 0000000..a3e11f6 --- /dev/null +++ b/src/interfaces/ICCR.sol @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 Lido +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.28; + +import {ICCROperatorStatesStorage} from "./ICCROperatorStatesStorage.sol"; + +/** + * @title ICCR + * @notice Interface for CredibleCommitmentCurationProvider. + */ +interface ICCR is ICCROperatorStatesStorage { + function optIn( + uint24 moduleId, + uint64 operatorId, + address manager, + uint64 keyIndexStart, + uint64 keyIndexEnd, + string calldata rpcURL + ) external; + function optOut() external; + // function updateKeyRange(uint64 keyIndexStart, uint64 keyIndexEnd) external; + // function updateManager(uint24 moduleId, uint64 operatorId, address newManager) external; + + function getOperatorManager(uint24 moduleId, uint64 operatorId) external view returns (address); + function getOperatorDelegates(uint24 moduleId, uint64 operatorId) external view returns (Delegate[] memory); + function getOperatorCommitments(uint24 moduleId, uint64 operatorId) external view returns (Commitment[] memory); + + function getOperator(uint24 moduleId, uint64 operatorId) + external + view + returns (address manager, bool isBlocked, bool isEnabled, OptInOutState memory optInOutState); + function getModuleBlockGasLimit(uint24 moduleId) external view returns (uint64); + function getModuleOperatorMaxKeys(uint24 moduleId) external view returns (uint64); + function getOperatorIsEnabledForPreconf(uint24 moduleId, uint64 operatorId) external view returns (bool); + function getOperatorAllowedKeys(uint24 moduleId, uint64 operatorId) external view returns (uint64); +} diff --git a/src/interfaces/ICCCPConfigStorage.sol b/src/interfaces/ICCRConfigStorage.sol similarity index 60% rename from src/interfaces/ICCCPConfigStorage.sol rename to src/interfaces/ICCRConfigStorage.sol index 4fca02c..88d39c3 100644 --- a/src/interfaces/ICCCPConfigStorage.sol +++ b/src/interfaces/ICCRConfigStorage.sol @@ -4,31 +4,31 @@ pragma solidity 0.8.28; /** - * @title ICCCPConfigStorage + * @title ICCRConfigStorage * @notice Interface for interacting with the storage and control config params. */ -interface ICCCPConfigStorage { +interface ICCRConfigStorage { /// @notice steaking module parameters /// @dev override global default values, zero values means use default config /// @param isDisabled is module disabled for pre-confs /// operators in disabled modules are automatically considered as opted-out - /// @param operatorMaxValidators maximum number of validators per operator + /// @param operatorMaxKeys maximum number of keys per operator /// @param blockGasLimit block gas limit struct ModuleConfig { bool isDisabled; - uint64 operatorMaxValidators; + uint64 operatorMaxKeys; uint64 blockGasLimit; } /// @notice global config parameters - /// @param optInMinDurationBlocks minimum duration of the opt-in period in blocks - /// @param optOutDelayDurationBlocks delay in blocks before the operator can opt-in again after opt-out - /// @param defaultOperatorMaxValidators default maximum number of validators per operator + /// @param optInDelayBlocks minimum duration of the opt-in period in blocks + /// @param optOutDelayBlocks delay in blocks before the operator can opt-in again after opt-out + /// @param defaultOperatorMaxKeys default maximum number of keys per operator /// @param defaultBlockGasLimit default block gas limit struct Config { - uint64 optInMinDurationBlocks; - uint64 optOutDelayDurationBlocks; - uint64 defaultOperatorMaxValidators; + uint64 optInDelayBlocks; + uint64 optOutDelayBlocks; + uint64 defaultOperatorMaxKeys; uint64 defaultBlockGasLimit; } @@ -39,6 +39,6 @@ interface ICCCPConfigStorage { Config _config; } - error ZeroDefaultOperatorMaxValidators(); + error ZeroDefaultOperatorMaxKeys(); error ZeroDefaultBlockGasLimit(); } diff --git a/src/interfaces/ICCCPOperatorStatesStorage.sol b/src/interfaces/ICCROperatorStatesStorage.sol similarity index 61% rename from src/interfaces/ICCCPOperatorStatesStorage.sol rename to src/interfaces/ICCROperatorStatesStorage.sol index 60dca15..84325b6 100644 --- a/src/interfaces/ICCCPOperatorStatesStorage.sol +++ b/src/interfaces/ICCROperatorStatesStorage.sol @@ -7,25 +7,26 @@ pragma solidity 0.8.28; * @title IOperatorStatesStorage * @notice Interface for interacting with the storage and control states of operators. */ -interface ICCCPOperatorStatesStorage { +interface ICCROperatorStatesStorage { /// @notice operator optin/optout state /// operator can be in several statuses: // 1. new, not registered: optInBlock = 0, optOutBlock = 0 // 2. registered: optInBlock > 0, optOutBlock = 0 - // 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayDurationBlocksDelay >= block.number - // 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay >= block.number - // 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = false, optOutBlock + optOutDelayDurationBlocksDelay < block.number - // 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isOptOutForced = true, optOutBlock + optOutDelayDurationBlocksDelay < block.number + // 3. opt-out in progress: optInBlock > 0, optOutBlock > 0, optOutBlock + optOutDelayBlocksDelay >= block.number + // 4. forded opt-out in progress: optInBlock > 0, optOutBlock > 0, isBlocked = true, optOutBlock + optOutDelayBlocksDelay >= block.number + // 5. opt-out completed: optInBlock > 0, optOutBlock > 0, isBlocked = false, optOutBlock + optOutDelayBlocksDelay < block.number + // 6. forced opt-out completed: optInBlock > 0, optOutBlock > 0, isBlocked = true, optOutBlock + optOutDelayBlocksDelay < block.number - // If isOptOutForced is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. - // If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isOptOutForced flag). + // If isBlocked is set, optOutBlock has non-zero value of the block number when the operator was forced to opt out. + // If operator has "forced opt-out completed" status, it can't opt in again until the committee decides to allow it (clear isBlocked flag). struct OptInOutState { uint64 optInBlock; uint64 optOutBlock; - bool isOptOutForced; // if the operator is forced to opt out by the committee + uint64 optInDelayOffBlock; + uint64 optOutDelayOffBlock; } - struct KeysRange { + struct KeyRange { uint64 indexStart; uint64 indexEnd; } @@ -34,13 +35,31 @@ interface ICCCPOperatorStatesStorage { string rpcURL; } - struct OperatorState { + struct Delegate { + bytes key; + } + + struct Commitment { + bytes32 id; // reserved for future use + KeyRange keyRange; + ExtraData extraData; + } + + struct OperatorStateOld { address manager; - KeysRange keysRange; + KeyRange keyRange; OptInOutState optInOutState; ExtraData extraData; } + struct OperatorState { + bool isBlocked; // if the operator is blocked (i.e. forced to opt out by the committee) + address manager; + OptInOutState optInOutState; + Commitment[] commitments; + Delegate[] delegates; + } + /** * @notice Storage structure for operator states data. * @dev @@ -55,4 +74,5 @@ interface ICCCPOperatorStatesStorage { error ManagerBelongsToOtherOperator(); error OperatorNotRegistered(); error ManagerNotRegistered(); + error IndexOutOfRange(); } diff --git a/src/interfaces/ICSModule.sol b/src/interfaces/ICSModule.sol index 7840775..26a27dd 100644 --- a/src/interfaces/ICSModule.sol +++ b/src/interfaces/ICSModule.sol @@ -10,8 +10,8 @@ struct CSMNodeOperator { uint32 totalWithdrawnKeys; uint32 totalDepositedKeys; uint32 totalVettedKeys; - uint32 stuckValidatorsCount; - uint32 depositableValidatorsCount; + uint32 stuckKeysCount; + uint32 depositableKeysCount; uint32 targetLimit; uint8 targetLimitMode; uint32 totalExitedKeys; diff --git a/src/interfaces/ICuratedModule.sol b/src/interfaces/ICuratedModule.sol index 95d7f19..0a7ae08 100644 --- a/src/interfaces/ICuratedModule.sol +++ b/src/interfaces/ICuratedModule.sol @@ -13,8 +13,8 @@ interface ICuratedModule is IStakingModule { bool active, string memory name, address rewardAddress, - uint64 totalVettedValidators, - uint64 totalExitedValidators, - uint64 totalAddedValidators + uint64 totalVettedKeys, + uint64 totalExitedKeys, + uint64 totalAddedKeys ); } diff --git a/src/interfaces/IStakingRouter.sol b/src/interfaces/IStakingRouter.sol index 42e7b09..9e29749 100644 --- a/src/interfaces/IStakingRouter.sol +++ b/src/interfaces/IStakingRouter.sol @@ -13,7 +13,7 @@ struct StakingModule { string name; uint64 lastDepositAt; uint256 lastDepositBlock; - uint256 exitedValidatorsCount; + uint256 exitedKeysCount; uint16 priorityExitShareThreshold; uint64 maxDepositsPerBlock; uint64 minDepositBlockDistance; diff --git a/src/lib/CCCPConfigStorage.sol b/src/lib/CCRConfigStorage.sol similarity index 56% rename from src/lib/CCCPConfigStorage.sol rename to src/lib/CCRConfigStorage.sol index 2e9af5e..d8c4741 100644 --- a/src/lib/CCCPConfigStorage.sol +++ b/src/lib/CCRConfigStorage.sol @@ -3,62 +3,59 @@ pragma solidity 0.8.28; -import {ICCCPConfigStorage} from "../interfaces/ICCCPConfigStorage.sol"; +import {ICCRConfigStorage} from "../interfaces/ICCRConfigStorage.sol"; -abstract contract CCCPConfigStorage is ICCCPConfigStorage { +abstract contract CCRConfigStorage is ICCRConfigStorage { bytes32 private immutable STORAGE_SLOT_REF; constructor() { STORAGE_SLOT_REF = keccak256( - abi.encode(uint256(keccak256(abi.encodePacked("lido.cccp.storage.ConfigStorage"))) - 1) + abi.encode(uint256(keccak256(abi.encodePacked("lido.ccr.storage.ConfigStorage"))) - 1) ) & ~bytes32(uint256(0xff)); } function _setConfig( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, uint64 defaultBlockGasLimit ) internal { - if (defaultOperatorMaxValidators == 0) { - revert ZeroDefaultOperatorMaxValidators(); + if (defaultOperatorMaxKeys == 0) { + revert ZeroDefaultOperatorMaxKeys(); } if (defaultBlockGasLimit == 0) { revert ZeroDefaultBlockGasLimit(); } _getConfigStorage()._config = Config({ - optInMinDurationBlocks: optInMinDurationBlocks, - optOutDelayDurationBlocks: optOutDelayDurationBlocks, - defaultOperatorMaxValidators: defaultOperatorMaxValidators, + optInDelayBlocks: optInDelayBlocks, + optOutDelayBlocks: optOutDelayBlocks, + defaultOperatorMaxKeys: defaultOperatorMaxKeys, defaultBlockGasLimit: defaultBlockGasLimit }); } - function _setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) + function _setModuleConfig(uint24 moduleId, bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) internal { - _getConfigStorage()._modules[moduleId] = ModuleConfig({ - isDisabled: isDisabled, - operatorMaxValidators: operatorMaxValidators, - blockGasLimit: blockGasLimit - }); + _getConfigStorage()._modules[moduleId] = + ModuleConfig({isDisabled: isDisabled, operatorMaxKeys: operatorMaxKeys, blockGasLimit: blockGasLimit}); } function _getConfig() internal view returns ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, + uint64 optInDelayBlocks, + uint64 optOutDelayBlocks, + uint64 defaultOperatorMaxKeys, uint64 defaultBlockGasLimit ) { Config memory config = _getConfigStorage()._config; return ( - config.optInMinDurationBlocks, - config.optOutDelayDurationBlocks, - config.defaultOperatorMaxValidators, + config.optInDelayBlocks, + config.optOutDelayBlocks, + config.defaultOperatorMaxKeys, config.defaultBlockGasLimit ); } @@ -66,10 +63,10 @@ abstract contract CCCPConfigStorage is ICCCPConfigStorage { function _getModuleConfig(uint24 moduleId) internal view - returns (bool isDisabled, uint64 operatorMaxValidators, uint64 blockGasLimit) + returns (bool isDisabled, uint64 operatorMaxKeys, uint64 blockGasLimit) { ModuleConfig memory moduleConfig = _getConfigStorage()._modules[moduleId]; - return (moduleConfig.isDisabled, moduleConfig.operatorMaxValidators, moduleConfig.blockGasLimit); + return (moduleConfig.isDisabled, moduleConfig.operatorMaxKeys, moduleConfig.blockGasLimit); } /** diff --git a/src/lib/CCCPOperatorStatesStorage.sol b/src/lib/CCROperatorStatesStorage.sol similarity index 50% rename from src/lib/CCCPOperatorStatesStorage.sol rename to src/lib/CCROperatorStatesStorage.sol index 1159b5e..bcc2c74 100644 --- a/src/lib/CCCPOperatorStatesStorage.sol +++ b/src/lib/CCROperatorStatesStorage.sol @@ -4,29 +4,71 @@ pragma solidity 0.8.28; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {ICCCPOperatorStatesStorage} from "../interfaces/ICCCPOperatorStatesStorage.sol"; +import {ICCROperatorStatesStorage} from "../interfaces/ICCROperatorStatesStorage.sol"; -abstract contract CCCPOperatorStatesStorage is ICCCPOperatorStatesStorage, Initializable { +abstract contract CCROperatorStatesStorage is ICCROperatorStatesStorage, Initializable { bytes32 private immutable STORAGE_SLOT_REF; constructor() { STORAGE_SLOT_REF = keccak256( - abi.encode(uint256(keccak256(abi.encodePacked("lido.cccp.storage.OperatorStatesStorage"))) - 1) + abi.encode(uint256(keccak256(abi.encodePacked("lido.ccr.storage.OperatorStatesStorage"))) - 1) ) & ~bytes32(uint256(0xff)); } function __initializeOperatorStatesStorage() internal onlyInitializing {} - function _setOperatorOptInOutState(uint256 opKey, OptInOutState memory state) internal { - _getOperatorStateStorage(opKey).optInOutState = state; + function _addOperatorCommitment(uint256 opKey, uint64 indexStart, uint64 indexEnd, string memory rpcUrl) internal { + Commitment[] storage commitments = _getOperatorCommitmentsStorage(opKey); + commitments.push( + Commitment({id: bytes32(0), keyRange: KeyRange(indexStart, indexEnd), extraData: ExtraData(rpcUrl)}) + ); + } + + function _delOperatorCommitment(uint256 opKey, uint256 cIdx) internal { + Commitment[] storage commitments = _getOperatorCommitmentsStorage(opKey); + uint256 length = commitments.length; + if (cIdx >= length) { + revert IndexOutOfRange(); + } + + // del element in O(1), by replacing it with the last one + unchecked { + if (cIdx < length - 1) { + commitments[cIdx] = commitments[length - 1]; + } + } + commitments.pop(); + } + + function _delOperatorAllCommitments(uint256 opKey) internal { + if (_getOperatorCommitmentsStorage(opKey).length > 0) { + delete _getOperatorStateStorage(opKey).commitments; + } + } + + function _addOperatorDelegate(uint256 opKey, bytes memory key) internal { + Delegate[] storage delegates = _getOperatorDelegatesStorage(opKey); + delegates.push(Delegate({key: key})); } - function _setOperatorKeysRange(uint256 opKey, uint64 indexStart, uint64 indexEnd) internal { - _getOperatorStateStorage(opKey).keysRange = KeysRange(indexStart, indexEnd); + function _delOperatorDelegate(uint256 opKey, uint256 dIdx) internal { + Delegate[] storage delegates = _getOperatorDelegatesStorage(opKey); + uint256 length = delegates.length; + if (dIdx >= length) { + revert IndexOutOfRange(); + } + + // del element in O(1), by replacing it with the last one + unchecked { + if (dIdx < length - 1) { + delegates[dIdx] = delegates[length - 1]; + } + } + delegates.pop(); } - function _setOperatorExtraData(uint256 opKey, ExtraData memory data) internal { - _getOperatorStateStorage(opKey).extraData = data; + function _setOperatorCommitmentRPCUrl(uint256 opKey, uint256 cIdx, string memory rpcUrl) internal { + _getOperatorCommitmentStorage(opKey, cIdx).extraData = ExtraData({rpcURL: rpcUrl}); } /// @dev safe manager's address update @@ -57,21 +99,50 @@ abstract contract CCCPOperatorStatesStorage is ICCCPOperatorStatesStorage, Initi return _getOperatorStateStorage(opKey).optInOutState; } - /// @notice get operator's keys range state - function _getOperatorKeysRange(uint256 opKey) internal view returns (KeysRange memory) { - return _getOperatorStateStorage(opKey).keysRange; + function _getOperatorOptInOutStateStorage(uint256 opKey) internal view returns (OptInOutState storage) { + return _getOperatorStateStorage(opKey).optInOutState; + } + + function _setOperatorOptInOutState(uint256 opKey, OptInOutState memory state) internal { + _getOperatorStateStorage(opKey).optInOutState = state; } /// @notice get operator's extra data - function _getOperatorExtraData(uint256 opKey) internal view returns (ExtraData memory) { - return _getOperatorStateStorage(opKey).extraData; + function _getOperatorCommitmentsStorage(uint256 opKey) internal view returns (Commitment[] storage) { + return _getOperatorStateStorage(opKey).commitments; + } + + function _getOperatorCommitmentStorage(uint256 opKey, uint256 cIdx) internal view returns (Commitment storage) { + // return _getOperatorStateStorage(opKey).commitments[cIdx]; + return _getOperatorCommitmentsStorage(opKey)[cIdx]; + } + + function _getOperatorDelegatesStorage(uint256 opKey) internal view returns (Delegate[] storage) { + return _getOperatorStateStorage(opKey).delegates; } + function _getOperatorDelegateStorage(uint256 opKey, uint256 dIdx) internal view returns (Delegate storage) { + return _getOperatorDelegatesStorage(opKey)[dIdx]; + } + + // function _getOperatorCommitmentExtraData(uint256 opKey, uint cIdx) internal view returns (ExtraData memory) { + // Commitment[] storage commitments = _getOperatorStateStorage(opKey).commitments; + // return commitments[cIdx].extraData; + // } + /// @notice get manager address linked to the operator's reward address - function _getOperatorManager(uint256 opKey) internal view returns (address managerAddress) { + function _getOperatorManager(uint256 opKey) internal view returns (address) { return _getOperatorStateStorage(opKey).manager; } + function _getIsOperatorBlocked(uint256 opKey) internal view returns (bool) { + return _getOperatorStateStorage(opKey).isBlocked; + } + + function _setIsOperatorBlocked(uint256 opKey, bool isBlocked) internal { + _getOperatorStateStorage(opKey).isBlocked = isBlocked; + } + function _getManagerOpKey(address manager) internal view returns (uint256) { return _getOperatorsStatesStorage()._managers[manager]; } diff --git a/test/CCCP.t.sol b/test/CCCP.t.sol deleted file mode 100644 index b96a097..0000000 --- a/test/CCCP.t.sol +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.28; - -import {CCCPCommon} from "./helpers/CCCPCommon.sol"; - -import {CCCP} from "../src/CCCP.sol"; -import {CCCPMock} from "./helpers/mocks/CCCPMock.sol"; -import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; -import {ICCCPOperatorStatesStorage} from "../src/interfaces/ICCCPOperatorStatesStorage.sol"; - -contract CCCPOptIn is CCCPCommon { - CCCP public cccp; - - uint64 public noCsm1Id; - uint64 public noCurated1Id; - - uint24 public constant norId = 1; - uint24 public constant csmId = 2; - - string public constant rpcUrl1 = "some-url-1"; - string public constant rpcUrl2 = "some-url-2"; - - uint32 constant opKeysCount = 10; - - uint64 constant optInMinDurationBlocks = 0; - uint64 constant optOutDelayDurationBlocks = 0; - uint64 constant defaultOperatorMaxValidators = 100; - uint64 constant defaultBlockGasLimit = 1000000; - - function setUp() public virtual override { - super.setUp(); - - noCsm1 = nextAddress("NO_CSM1"); - noCurated1 = nextAddress("NO_CURATED1"); - noCsm1Manager = nextAddress("NO_CSM1_MANAGER"); - noCurated1Manager = nextAddress("NO_CURATED1_MANAGER"); - - cccp = new CCCPMock(address(locator), "community-onchain-v1"); - _enableInitializers(address(cccp)); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: optInMinDurationBlocks, - optOutDelayDurationBlocks: optOutDelayDurationBlocks, - defaultOperatorMaxValidators: defaultOperatorMaxValidators, - defaultBlockGasLimit: defaultBlockGasLimit - }); - - noCsm1Id = createNo(csm, noCsm1, opKeysCount); - noCurated1Id = createNo(nor, noCurated1, opKeysCount); - } - - function test_OptIn() public { - // opt in on behalf of noCsm1 - vm.prank(noCsm1); - vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.OperatorManagerUpdated(csmId, noCsm1Id, noCsm1Manager); - vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.KeysRangeUpdated(csmId, noCsm1Id, 2, 4); - vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.RPCUrlUpdated(csmId, noCsm1Id, rpcUrl1); - vm.expectEmit(true, true, true, false, address(cccp)); - emit CCCP.OptInSucceeded(csmId, noCsm1Id, noCsm1Manager); - - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: rpcUrl1 - }); - - assertEq(cccp.getOperatorIsEnabledForPreconf(csmId, noCsm1Id), true); - assertEq(cccp.getOperatorManager(csmId, noCsm1Id), noCsm1Manager); - } - - function test_GetOperatorByManager() public { - // opt in on behalf of noCsm1 - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: rpcUrl1 - }); - - (uint24 moduleId, uint64 operatorId, bool isEnabled, CCCP.OperatorState memory state) = - cccp.getOperator(noCsm1Manager); - - assertEq(moduleId, csmId); - assertEq(operatorId, noCsm1Id); - assertEq(isEnabled, true); - - assertEq(state.keysRange.indexStart, 2); - assertEq(state.keysRange.indexEnd, 4); - assertEq(state.manager, noCsm1Manager); - assertEq(state.optInOutState.optInBlock, block.number); - assertEq(state.optInOutState.optOutBlock, 0); - assertEq(state.optInOutState.isOptOutForced, false); - assertEq(state.extraData.rpcURL, rpcUrl1); - } - - function test_GetOperatorById() public { - // opt in on behalf of noCsm1 - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: rpcUrl1 - }); - - (uint24 moduleId, uint64 operatorId, bool isEnabled, CCCP.OperatorState memory state) = - cccp.getOperator(csmId, noCsm1Id); - - assertEq(moduleId, csmId); - assertEq(operatorId, noCsm1Id); - assertEq(isEnabled, true); - - assertEq(state.keysRange.indexStart, 2); - assertEq(state.keysRange.indexEnd, 4); - assertEq(state.manager, noCsm1Manager); - assertEq(state.optInOutState.optInBlock, block.number); - assertEq(state.optInOutState.optOutBlock, 0); - assertEq(state.optInOutState.isOptOutForced, false); - assertEq(state.extraData.rpcURL, rpcUrl1); - } - - function test_OptIn_RevertWhen_ZeroManagerAddress() public { - vm.expectRevert(CCCP.ZeroOperatorManagerAddress.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: address(0), - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_WrongRewardAddress() public { - vm.prank(stranger1); - vm.expectRevert(CCCP.RewardAddressMismatch.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_WrongModuleId() public { - vm.prank(noCsm1); - vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); - cccp.optIn({ - moduleId: 999, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_LidoOperatorNotActive() public { - // set noCsm1 to inactive - updateNoActive(csm, noCsm1Id, false); - - vm.prank(noCsm1); - vm.expectRevert(CCCP.OperatorNotActive.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_OperatorAlreadyOptedIn() public { - // optin - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - - // repeat optin - vm.prank(noCsm1); - vm.expectRevert(CCCP.OperatorOptedIn.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_OperatorForceOptedOut() public { - // optin - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - - // force optout - vm.roll(block.number + 100); - vm.prank(committee); - cccp.optOut({moduleId: csmId, operatorId: noCsm1Id}); - - // repeat optin - vm.roll(block.number + 100); - vm.prank(noCsm1); - vm.expectRevert(CCCP.OperatorOptInNotAllowed.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_ManagerBelongsOtherOperator() public { - // optin - vm.prank(noCurated1); - cccp.optIn({ - moduleId: norId, - operatorId: noCurated1Id, - manager: noCurated1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - - // optin with same manager - vm.prank(noCsm1); - vm.expectRevert(ICCCPOperatorStatesStorage.ManagerBelongsToOtherOperator.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCurated1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_KeyIndexWrongOrder() public { - // optin - vm.prank(noCsm1); - vm.expectRevert(CCCP.KeyIndexMismatch.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 4, - keyIndexEnd: 2, - rpcURL: "" - }); - } - - function test_OptIn_RevertWhen_KeyIndexOutOfRange() public { - // optin - vm.prank(noCsm1); - vm.expectRevert(CCCP.KeyIndexOutOfRange.selector); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 100, - rpcURL: "" - }); - } - - function test_getOperatorIsEnabledForPreconf() public { - // wrong op id - // assertFalse(cccp.getOperatorIsEnabledForPreconf(csmId, 999)); - - // not yet opted in - assertFalse(cccp.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); - // opt in on behalf of noCsm1 - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: rpcUrl1 - }); - // opted in - assertTrue(cccp.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); - } - - function test_GetOperatorAllowedValidators() public { - vm.prank(committee); - cccp.setModuleConfig(csmId, true, 0, 0); - // 0 for disabled module - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); - - vm.prank(committee); - cccp.setModuleConfig(csmId, false, 0, 0); - - // set noCsm1 to inactive - updateNoActive(csm, noCsm1Id, false); - // 0 for inactive operator - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); - updateNoActive(csm, noCsm1Id, true); - - /// operator NOT yet opted in - /// - vm.prank(committee); - cccp.setConfig(0, 0, opKeysCount - 1, defaultBlockGasLimit); - - // operatorMaxValidators when operatorMaxValidators < operatorTotalAddedKeys - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount - 1); - - vm.prank(committee); - cccp.setConfig(0, 99, defaultOperatorMaxValidators, defaultBlockGasLimit); - - // operatorTotalAddedKeys when operatorMaxValidators > operatorTotalAddedKeys - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount); - - // opt in on behalf of noCsm1 with 3 keys - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 2, - keyIndexEnd: 4, - rpcURL: rpcUrl1 - }); - - // operatorMaxValidators > operatorTotalAddedKeys - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount - 3); - - // voluntary opt out - vm.roll(block.number + 100); - vm.prank(noCsm1Manager); - cccp.optOut(); - // optOut in progress - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); - - // optOut finished - vm.roll(block.number + 100); - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount); - - // opt in on behalf of noCsm1 with 9 keys - vm.prank(noCsm1); - cccp.optIn({ - moduleId: csmId, - operatorId: noCsm1Id, - manager: noCsm1Manager, - keyIndexStart: 0, - keyIndexEnd: 8, - rpcURL: rpcUrl1 - }); - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), opKeysCount - 9); - - // reduce max operator validators in module config to 5 - vm.prank(committee); - cccp.setModuleConfig(csmId, false, 5, 0); - // operator opted in totalKeys > operatorMaxValidators - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); - - // force optout - vm.roll(block.number + 100); - vm.prank(committee); - cccp.optOut({moduleId: csmId, operatorId: noCsm1Id}); - - // optOut in progress - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); - - // optOut finished, but forced optout - vm.roll(block.number + 100); - assertEq(cccp.getOperatorAllowedValidators(csmId, noCsm1Id), 0); - } -} diff --git a/test/CCCPConfig.t.sol b/test/CCCPConfig.t.sol deleted file mode 100644 index 5fbef7a..0000000 --- a/test/CCCPConfig.t.sol +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.28; - -import {CCCPCommon} from "./helpers/CCCPCommon.sol"; - -import {CCCP} from "../src/CCCP.sol"; -import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; -import {ICCCPConfigStorage} from "../src/interfaces/ICCCPConfigStorage.sol"; - -contract CCCPConfig is CCCPCommon { - CCCP public cccp; - uint24 public moduleId = 1; - uint64 public maxValidators = 1111; - uint64 public blockGasLimit = 1111111; - - uint64 constant optInMinDurationBlocks = 32; - uint64 constant optOutDelayDurationBlocks = 64; - uint64 constant defaultOperatorMaxValidators = 100; - uint64 constant defaultBlockGasLimit = 1000000; - - function setUp() public virtual override { - super.setUp(); - - cccp = new CCCP(address(locator), "community-onchain-v1"); - _enableInitializers(address(cccp)); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: optInMinDurationBlocks, - optOutDelayDurationBlocks: optOutDelayDurationBlocks, - defaultOperatorMaxValidators: defaultOperatorMaxValidators, - defaultBlockGasLimit: defaultBlockGasLimit - }); - } - - function test_GetInitialConfig() public view { - ( - uint64 newOptInMinDurationBlocks, - uint64 newOptOutDelayDurationBlocks, - uint64 newDefaultOperatorMaxValidators, - uint64 newDefaultBlockGasLimit - ) = cccp.getConfig(); - - assertEq(newOptInMinDurationBlocks, optInMinDurationBlocks); - assertEq(newOptOutDelayDurationBlocks, optOutDelayDurationBlocks); - assertEq(newDefaultOperatorMaxValidators, defaultOperatorMaxValidators); - assertEq(newDefaultBlockGasLimit, defaultBlockGasLimit); - } - - function test_SetConfig() public { - vm.prank(committee); - cccp.setConfig(10, 20, 30, 40); - ( - uint64 newOptInMinDurationBlocks, - uint64 newOptOutDelayDurationBlocks, - uint64 newDefaultOperatorMaxValidators, - uint64 newDefaultBlockGasLimit - ) = cccp.getConfig(); - - assertEq(newOptInMinDurationBlocks, 10); - assertEq(newOptOutDelayDurationBlocks, 20); - assertEq(newDefaultOperatorMaxValidators, 30); - assertEq(newDefaultBlockGasLimit, 40); - } - - function test_SetConfig_RevertWhen_CallerNotCommittee() public { - bytes32 role = cccp.COMMITTEE_ROLE(); - - vm.prank(stranger1); - expectRoleRevert(stranger1, role); - cccp.setConfig( - optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, defaultBlockGasLimit - ); - } - - function test_SetConfig_RevertWhen_ZeroDefaultOperatorMaxValidators() public { - vm.prank(committee); - vm.expectRevert(ICCCPConfigStorage.ZeroDefaultOperatorMaxValidators.selector); - cccp.setConfig(optInMinDurationBlocks, optOutDelayDurationBlocks, 0, defaultBlockGasLimit); - } - - function test_SetConfig_RevertWhen_ZeroDefaultBlockGasLimit() public { - vm.prank(committee); - vm.expectRevert(ICCCPConfigStorage.ZeroDefaultBlockGasLimit.selector); - cccp.setConfig(optInMinDurationBlocks, optOutDelayDurationBlocks, defaultOperatorMaxValidators, 0); - } - - function test_SetModuleConfig() public { - vm.prank(committee); - cccp.setModuleConfig(moduleId, true, maxValidators, blockGasLimit); - (bool newIsDisabled, uint64 newMaxValidators, uint64 newblockGasLimit) = cccp.getModuleConfig(moduleId); - - assertEq(newIsDisabled, true); - assertEq(newMaxValidators, maxValidators); - assertEq(newblockGasLimit, blockGasLimit); - } - - function test_SetModuleConfig_RevertWhen_CallerNotCommittee() public { - bytes32 role = cccp.COMMITTEE_ROLE(); - - vm.prank(stranger1); - expectRoleRevert(stranger1, role); - cccp.setModuleConfig(moduleId, true, maxValidators, blockGasLimit); - } - - function test_SetModuleConfig_RevertWhen_WrongModuleId() public { - vm.prank(committee); - vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); - cccp.setModuleConfig(999, true, maxValidators, blockGasLimit); - } - - function test_ModuleConfig_OverrideBlockGasLimit() public { - // module config not yet set - assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); - - vm.prank(committee); - cccp.setModuleConfig(moduleId, false, 0, blockGasLimit); - assertEq(cccp.getModuleBlockGasLimit(moduleId), blockGasLimit); - - // set block gas limit to 0 - vm.prank(committee); - cccp.setModuleConfig(moduleId, false, 0, 0); - assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); - } - - function test_ModuleConfig_ZeroBlockGasLimitWhenModuleDisabled() public { - // module config not yet set - assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); - - // disable module - vm.prank(committee); - cccp.setModuleConfig(moduleId, true, 0, 0); - assertEq(cccp.getModuleBlockGasLimit(moduleId), 0); - - // enable module - vm.prank(committee); - cccp.setModuleConfig(moduleId, false, 0, 0); - assertEq(cccp.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); - } - - function test_ModuleConfig_OverrideOperatorMaxValidators() public { - // module config not yet set - assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); - - vm.prank(committee); - cccp.setModuleConfig(moduleId, false, maxValidators, 0); - assertEq(cccp.getModuleOperatorMaxValidators(moduleId), maxValidators); - - // set block gas limit to 0 - vm.prank(committee); - cccp.setModuleConfig(moduleId, false, 0, 0); - assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); - } - - function test_ModuleConfig_ZeroOperatorMaxValidatorsWhenModuleDisabled() public { - // module config not yet set - assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); - - // disable module - vm.prank(committee); - cccp.setModuleConfig(moduleId, true, 0, 0); - assertEq(cccp.getModuleOperatorMaxValidators(moduleId), 0); - - // enable module - vm.prank(committee); - cccp.setModuleConfig(moduleId, false, 0, 0); - assertEq(cccp.getModuleOperatorMaxValidators(moduleId), defaultOperatorMaxValidators); - } -} diff --git a/test/CCCPInit.t.sol b/test/CCCPInit.t.sol deleted file mode 100644 index b2693ff..0000000 --- a/test/CCCPInit.t.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.28; - -import {CCCP} from "../src/CCCP.sol"; -import {CCCPMock} from "./helpers/mocks/CCCPMock.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import {CCCPCommon} from "./helpers/CCCPCommon.sol"; - -contract CCCPInitialize is CCCPCommon { - function test_constructor() public { - CCCPMock cccp = new CCCPMock({lidoLocator: address(locator), csModuleType: "csm-type"}); - assertEq(cccp.__test__getCSModuleType(), "csm-type"); - assertEq(address(cccp.LIDO_LOCATOR()), address(locator)); - assertEq(cccp.getContractVersion(), type(uint64).max); - } - - function test_constructor_RevertWhen_ZeroLocator() public { - vm.expectRevert(CCCP.ZeroLocatorAddress.selector); - new CCCP({lidoLocator: address(0), csModuleType: "csm-type"}); - } - - function test_constructor_RevertWhen_InitOnImpl() public { - CCCP cccp = new CCCP({lidoLocator: address(locator), csModuleType: "csm-type"}); - - vm.expectRevert(Initializable.InvalidInitialization.selector); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 0, - optOutDelayDurationBlocks: 0, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - } - - function test_initialize() public { - CCCP cccp = new CCCP({lidoLocator: address(locator), csModuleType: "csm-type"}); - _enableInitializers(address(cccp)); - cccp.initialize({ - committeeAddress: committee, - optInMinDurationBlocks: 32, - optOutDelayDurationBlocks: 64, - defaultOperatorMaxValidators: 10, - defaultBlockGasLimit: 1000000 - }); - - ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) = cccp.getConfig(); - - assertEq(optInMinDurationBlocks, 32); - assertEq(optOutDelayDurationBlocks, 64); - assertEq(defaultOperatorMaxValidators, 10); - assertEq(defaultBlockGasLimit, 1000000); - assertEq(cccp.getContractVersion(), 1); - assertFalse(cccp.paused()); - } -} diff --git a/test/CCR.t.sol b/test/CCR.t.sol new file mode 100644 index 0000000..edb6b35 --- /dev/null +++ b/test/CCR.t.sol @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCRCommon} from "./helpers/CCRCommon.sol"; + +import {CCR} from "../src/CCR.sol"; +import {CCRMock} from "./helpers/mocks/CCRMock.sol"; +import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; +import {ICCROperatorStatesStorage} from "../src/interfaces/ICCROperatorStatesStorage.sol"; + +contract CCROptIn is CCRCommon { + CCR public ccr; + + uint64 public noCsm1Id; + uint64 public noCurated1Id; + + uint24 public constant norId = 1; + uint24 public constant csmId = 2; + + string public constant rpcUrl1 = "some-url-1"; + string public constant rpcUrl2 = "some-url-2"; + + uint32 public constant opKeysCount = 10; + + uint64 public constant optInDelayBlocks = 0; + uint64 public constant optOutDelayBlocks = 0; + uint64 public constant defaultOperatorMaxKeys = 100; + uint64 public constant defaultBlockGasLimit = 1000000; + + function setUp() public virtual override { + super.setUp(); + + noCsm1 = nextAddress("NO_CSM1"); + noCurated1 = nextAddress("NO_CURATED1"); + noCsm1Manager = nextAddress("NO_CSM1_MANAGER"); + noCurated1Manager = nextAddress("NO_CURATED1_MANAGER"); + + ccr = new CCRMock(address(locator), "community-onchain-v1"); + _enableInitializers(address(ccr)); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: optInDelayBlocks, + optOutDelayBlocks: optOutDelayBlocks, + defaultOperatorMaxKeys: defaultOperatorMaxKeys, + defaultBlockGasLimit: defaultBlockGasLimit + }); + + noCsm1Id = createNo(csm, noCsm1, opKeysCount); + noCurated1Id = createNo(nor, noCurated1, opKeysCount); + } + + function test_OptIn() public { + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + vm.expectEmit(true, true, true, false, address(ccr)); + emit CCR.OperatorManagerUpdated(csmId, noCsm1Id, noCsm1Manager); + vm.expectEmit(true, true, true, false, address(ccr)); + emit CCR.OperatorCommitmentAdded(csmId, noCsm1Id, 2, 4, rpcUrl1); + vm.expectEmit(true, true, true, false, address(ccr)); + emit CCR.OptInSucceeded(csmId, noCsm1Id); + + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + + assertEq(ccr.getOperatorIsEnabledForPreconf(csmId, noCsm1Id), true); + assertEq(ccr.getOperatorManager(csmId, noCsm1Id), noCsm1Manager); + } + + function test_GetOperator() public { + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + + (address manager, bool isBlocked, bool isEnabled, CCR.OptInOutState memory optInOutState) = + ccr.getOperator(csmId, noCsm1Id); + + assertEq(manager, noCsm1Manager); + assertEq(isBlocked, false); + assertEq(isEnabled, true); + assertEq(optInOutState.optInBlock, block.number); + assertEq(optInOutState.optOutBlock, 0); + + CCR.Commitment[] memory commitments = ccr.getOperatorCommitments(csmId, noCsm1Id); + + assertEq(commitments.length, 1); + assertEq(commitments[0].keyRange.indexStart, 2); + assertEq(commitments[0].keyRange.indexEnd, 4); + assertEq(commitments[0].extraData.rpcURL, rpcUrl1); + } + + function test_OptIn_RevertWhen_ZeroManagerAddress() public { + vm.expectRevert(CCR.ZeroOperatorManagerAddress.selector); + ccr.optIn({moduleId: csmId, operatorId: noCsm1Id, manager: address(0), indexStart: 2, indexEnd: 4, rpcURL: ""}); + } + + function test_OptIn_RevertWhen_WrongRewardAddress() public { + vm.prank(stranger1); + vm.expectRevert(CCR.RewardAddressMismatch.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_WrongModuleId() public { + vm.prank(noCsm1); + vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); + ccr.optIn({moduleId: 999, operatorId: noCsm1Id, manager: noCsm1Manager, indexStart: 2, indexEnd: 4, rpcURL: ""}); + } + + function test_OptIn_RevertWhen_LidoOperatorNotActive() public { + // set noCsm1 to inactive + updateNoActive(csm, noCsm1Id, false); + + vm.prank(noCsm1); + vm.expectRevert(CCR.OperatorNotActive.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_OperatorAlreadyOptedIn() public { + // optin + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + + // repeat optin + vm.prank(noCsm1); + vm.expectRevert(CCR.OperatorOptedIn.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_OperatorForceOptedOut() public { + // optin + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + + // force optout + vm.roll(block.number + 100); + vm.prank(committee); + ccr.optOut({moduleId: csmId, operatorId: noCsm1Id}); + + // repeat optin + vm.roll(block.number + 100); + vm.prank(noCsm1); + vm.expectRevert(CCR.OperatorBlocked.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_ManagerBelongsOtherOperator() public { + // optin + vm.prank(noCurated1); + ccr.optIn({ + moduleId: norId, + operatorId: noCurated1Id, + manager: noCurated1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + + // optin with same manager + vm.prank(noCsm1); + vm.expectRevert(ICCROperatorStatesStorage.ManagerBelongsToOtherOperator.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCurated1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_KeyIndexWrongOrder() public { + // optin + vm.prank(noCsm1); + vm.expectRevert(CCR.KeyIndexMismatch.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 4, + indexEnd: 2, + rpcURL: "" + }); + } + + function test_OptIn_RevertWhen_KeyIndexOutOfRange() public { + // optin + vm.prank(noCsm1); + vm.expectRevert(CCR.KeyIndexOutOfRange.selector); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 100, + rpcURL: "" + }); + } + + function test_getOperatorIsEnabledForPreconf() public { + // wrong op id + // assertFalse(ccr.getOperatorIsEnabledForPreconf(csmId, 999)); + + // not yet opted in + assertFalse(ccr.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); + // opt in on behalf of noCsm1 + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + // opted in + assertTrue(ccr.getOperatorIsEnabledForPreconf(csmId, noCsm1Id)); + } + + function test_GetOperatorAllowedKeys() public { + vm.prank(committee); + ccr.setModuleConfig(csmId, true, 0, 0); + // 0 for disabled module + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + + vm.prank(committee); + ccr.setModuleConfig(csmId, false, 0, 0); + + // set noCsm1 to inactive + updateNoActive(csm, noCsm1Id, false); + // 0 for inactive operator + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + updateNoActive(csm, noCsm1Id, true); + + /// operator NOT yet opted in + /// + vm.prank(committee); + ccr.setConfig(0, 0, opKeysCount - 1, defaultBlockGasLimit); + + // operatorMaxKeys when operatorMaxKeys < operatorTotalAddedKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount - 1); + + vm.prank(committee); + ccr.setConfig(0, 99, defaultOperatorMaxKeys, defaultBlockGasLimit); + + // operatorTotalAddedKeys when operatorMaxKeys > operatorTotalAddedKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount); + + // opt in on behalf of noCsm1 with 3 keys + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 2, + indexEnd: 4, + rpcURL: rpcUrl1 + }); + + // operatorMaxKeys > operatorTotalAddedKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount - 3); + + // voluntary opt out + vm.roll(block.number + 100); + vm.prank(noCsm1Manager); + ccr.optOut(); + + vm.roll(block.number + 100); + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount); + + // opt in on behalf of noCsm1 with 9 keys + vm.prank(noCsm1); + ccr.optIn({ + moduleId: csmId, + operatorId: noCsm1Id, + manager: noCsm1Manager, + indexStart: 0, + indexEnd: 8, + rpcURL: rpcUrl1 + }); + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), opKeysCount - 9); + + // reduce max operator keys in module config to 5 + vm.prank(committee); + ccr.setModuleConfig(csmId, false, 5, 0); + // operator opted in totalKeys > operatorMaxKeys + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + + // force optout + vm.roll(block.number + 100); + vm.prank(committee); + ccr.optOut({moduleId: csmId, operatorId: noCsm1Id}); + + // optOut finished, but forced optout + vm.roll(block.number + 100); + assertEq(ccr.getOperatorAllowedKeys(csmId, noCsm1Id), 0); + } +} diff --git a/test/CCRConfig.t.sol b/test/CCRConfig.t.sol new file mode 100644 index 0000000..0b1335e --- /dev/null +++ b/test/CCRConfig.t.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCRCommon} from "./helpers/CCRCommon.sol"; + +import {CCR} from "../src/CCR.sol"; +import {IStakingRouter} from "test/helpers/mocks/StakingRouterMock.sol"; +import {ICCRConfigStorage} from "../src/interfaces/ICCRConfigStorage.sol"; + +contract CCRConfig is CCRCommon { + CCR public ccr; + uint24 public moduleId = 1; + uint64 public maxKeys = 1111; + uint64 public blockGasLimit = 1111111; + + uint64 constant optInDelayBlocks = 32; + uint64 constant optOutDelayBlocks = 64; + uint64 constant defaultOperatorMaxKeys = 100; + uint64 constant defaultBlockGasLimit = 1000000; + + function setUp() public virtual override { + super.setUp(); + + ccr = new CCR(address(locator), "community-onchain-v1"); + _enableInitializers(address(ccr)); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: optInDelayBlocks, + optOutDelayBlocks: optOutDelayBlocks, + defaultOperatorMaxKeys: defaultOperatorMaxKeys, + defaultBlockGasLimit: defaultBlockGasLimit + }); + } + + function test_GetInitialConfig() public view { + ( + uint64 newoptInDelayBlocks, + uint64 newoptOutDelayBlocks, + uint64 newDefaultOperatorMaxKeys, + uint64 newDefaultBlockGasLimit + ) = ccr.getConfig(); + + assertEq(newoptInDelayBlocks, optInDelayBlocks); + assertEq(newoptOutDelayBlocks, optOutDelayBlocks); + assertEq(newDefaultOperatorMaxKeys, defaultOperatorMaxKeys); + assertEq(newDefaultBlockGasLimit, defaultBlockGasLimit); + } + + function test_SetConfig() public { + vm.prank(committee); + ccr.setConfig(10, 20, 30, 40); + ( + uint64 newoptInDelayBlocks, + uint64 newoptOutDelayBlocks, + uint64 newDefaultOperatorMaxKeys, + uint64 newDefaultBlockGasLimit + ) = ccr.getConfig(); + + assertEq(newoptInDelayBlocks, 10); + assertEq(newoptOutDelayBlocks, 20); + assertEq(newDefaultOperatorMaxKeys, 30); + assertEq(newDefaultBlockGasLimit, 40); + } + + function test_SetConfig_RevertWhen_CallerNotCommittee() public { + bytes32 role = ccr.COMMITTEE_ROLE(); + + vm.prank(stranger1); + expectRoleRevert(stranger1, role); + ccr.setConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, defaultBlockGasLimit); + } + + function test_SetConfig_RevertWhen_ZeroDefaultOperatorMaxKeys() public { + vm.prank(committee); + vm.expectRevert(ICCRConfigStorage.ZeroDefaultOperatorMaxKeys.selector); + ccr.setConfig(optInDelayBlocks, optOutDelayBlocks, 0, defaultBlockGasLimit); + } + + function test_SetConfig_RevertWhen_ZeroDefaultBlockGasLimit() public { + vm.prank(committee); + vm.expectRevert(ICCRConfigStorage.ZeroDefaultBlockGasLimit.selector); + ccr.setConfig(optInDelayBlocks, optOutDelayBlocks, defaultOperatorMaxKeys, 0); + } + + function test_SetModuleConfig() public { + vm.prank(committee); + ccr.setModuleConfig(moduleId, true, maxKeys, blockGasLimit); + (bool newIsDisabled, uint64 newMaxKeys, uint64 newblockGasLimit) = ccr.getModuleConfig(moduleId); + + assertEq(newIsDisabled, true); + assertEq(newMaxKeys, maxKeys); + assertEq(newblockGasLimit, blockGasLimit); + } + + function test_SetModuleConfig_RevertWhen_CallerNotCommittee() public { + bytes32 role = ccr.COMMITTEE_ROLE(); + + vm.prank(stranger1); + expectRoleRevert(stranger1, role); + ccr.setModuleConfig(moduleId, true, maxKeys, blockGasLimit); + } + + function test_SetModuleConfig_RevertWhen_WrongModuleId() public { + vm.prank(committee); + vm.expectRevert(IStakingRouter.StakingModuleUnregistered.selector); + ccr.setModuleConfig(999, true, maxKeys, blockGasLimit); + } + + function test_ModuleConfig_OverrideBlockGasLimit() public { + // module config not yet set + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, blockGasLimit); + assertEq(ccr.getModuleBlockGasLimit(moduleId), blockGasLimit); + + // set block gas limit to 0 + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + } + + function test_ModuleConfig_ZeroBlockGasLimitWhenModuleDisabled() public { + // module config not yet set + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + + // disable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, true, 0, 0); + assertEq(ccr.getModuleBlockGasLimit(moduleId), 0); + + // enable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleBlockGasLimit(moduleId), defaultBlockGasLimit); + } + + function test_ModuleConfig_OverrideOperatorMaxKeys() public { + // module config not yet set + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, maxKeys, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), maxKeys); + + // set block gas limit to 0 + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + } + + function test_ModuleConfig_ZeroOperatorMaxKeysWhenModuleDisabled() public { + // module config not yet set + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + + // disable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, true, 0, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), 0); + + // enable module + vm.prank(committee); + ccr.setModuleConfig(moduleId, false, 0, 0); + assertEq(ccr.getModuleOperatorMaxKeys(moduleId), defaultOperatorMaxKeys); + } +} diff --git a/test/CCRInit.t.sol b/test/CCRInit.t.sol new file mode 100644 index 0000000..db48d3c --- /dev/null +++ b/test/CCRInit.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.28; + +import {CCR} from "../src/CCR.sol"; +import {CCRMock} from "./helpers/mocks/CCRMock.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {CCRCommon} from "./helpers/CCRCommon.sol"; + +contract CCRInitialize is CCRCommon { + function test_constructor() public { + CCRMock ccr = new CCRMock({lidoLocator: address(locator), csModuleType: "csm-type"}); + assertEq(ccr.__test__getCSModuleType(), "csm-type"); + assertEq(address(ccr.LIDO_LOCATOR()), address(locator)); + assertEq(ccr.getContractVersion(), type(uint64).max); + } + + function test_constructor_RevertWhen_ZeroLocator() public { + vm.expectRevert(CCR.ZeroLocatorAddress.selector); + new CCR({lidoLocator: address(0), csModuleType: "csm-type"}); + } + + function test_constructor_RevertWhen_InitOnImpl() public { + CCR ccr = new CCR({lidoLocator: address(locator), csModuleType: "csm-type"}); + + vm.expectRevert(Initializable.InvalidInitialization.selector); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: 0, + optOutDelayBlocks: 0, + defaultOperatorMaxKeys: 10, + defaultBlockGasLimit: 1000000 + }); + } + + function test_initialize() public { + CCR ccr = new CCR({lidoLocator: address(locator), csModuleType: "csm-type"}); + _enableInitializers(address(ccr)); + ccr.initialize({ + committeeAddress: committee, + optInDelayBlocks: 32, + optOutDelayBlocks: 64, + defaultOperatorMaxKeys: 10, + defaultBlockGasLimit: 1000000 + }); + + (uint64 optInDelayBlocks, uint64 optOutDelayBlocks, uint64 defaultOperatorMaxKeys, uint64 defaultBlockGasLimit) + = ccr.getConfig(); + + assertEq(optInDelayBlocks, 32); + assertEq(optOutDelayBlocks, 64); + assertEq(defaultOperatorMaxKeys, 10); + assertEq(defaultBlockGasLimit, 1000000); + assertEq(ccr.getContractVersion(), 1); + assertFalse(ccr.paused()); + } +} diff --git a/test/fork/deployment/PostDeployment.t.sol b/test/fork/deployment/PostDeployment.t.sol index 1c62b12..45407b3 100644 --- a/test/fork/deployment/PostDeployment.t.sol +++ b/test/fork/deployment/PostDeployment.t.sol @@ -8,7 +8,7 @@ import {Utilities} from "../../helpers/Utilities.sol"; import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; import {DeployParams} from "../../../script/DeployBase.sol"; import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; -import {CCCP} from "../../../src/CCCP.sol"; +import {CCR} from "../../../src/CCR.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract CSModuleDeploymentTest is Test, Utilities, DeploymentFixtures { @@ -22,48 +22,44 @@ contract CSModuleDeploymentTest is Test, Utilities, DeploymentFixtures { } function test_constructor() public view { - assertEq(address(cccp.LIDO_LOCATOR()), deployParams.lidoLocatorAddress); + assertEq(address(ccr.LIDO_LOCATOR()), deployParams.lidoLocatorAddress); } function test_initializer() public view { - ( - uint64 optInMinDurationBlocks, - uint64 optOutDelayDurationBlocks, - uint64 defaultOperatorMaxValidators, - uint64 defaultBlockGasLimit - ) = cccp.getConfig(); + (uint64 optInDelayBlocks, uint64 optOutDelayBlocks, uint64 defaultOperatorMaxKeys, uint64 defaultBlockGasLimit) + = ccr.getConfig(); - assertEq(optInMinDurationBlocks, deployParams.optInMinDurationBlocks); - assertEq(optOutDelayDurationBlocks, deployParams.optOutDelayDurationBlocks); - assertEq(defaultOperatorMaxValidators, deployParams.defaultOperatorMaxValidators); + assertEq(optInDelayBlocks, deployParams.optInDelayBlocks); + assertEq(optOutDelayBlocks, deployParams.optOutDelayBlocks); + assertEq(defaultOperatorMaxKeys, deployParams.defaultOperatorMaxKeys); assertEq(defaultBlockGasLimit, deployParams.defaultBlockGasLimit); - assertEq(cccp.getContractVersion(), 1); - assertFalse(cccp.paused()); + assertEq(ccr.getContractVersion(), 1); + assertFalse(ccr.paused()); } function test_roles() public view { - assertTrue(cccp.hasRole(cccp.DEFAULT_ADMIN_ROLE(), deployParams.committeeAddress)); - assertTrue(cccp.getRoleMemberCount(cccp.DEFAULT_ADMIN_ROLE()) == 1); - assertTrue(cccp.hasRole(cccp.PAUSE_ROLE(), deployParams.committeeAddress)); - assertTrue(cccp.hasRole(cccp.RESUME_ROLE(), deployParams.committeeAddress)); - assertEq(cccp.getRoleMemberCount(cccp.PAUSE_ROLE()), 1); - assertEq(cccp.getRoleMemberCount(cccp.RESUME_ROLE()), 1); + assertTrue(ccr.hasRole(ccr.DEFAULT_ADMIN_ROLE(), deployParams.committeeAddress)); + assertTrue(ccr.getRoleMemberCount(ccr.DEFAULT_ADMIN_ROLE()) == 1); + assertTrue(ccr.hasRole(ccr.PAUSE_ROLE(), deployParams.committeeAddress)); + assertTrue(ccr.hasRole(ccr.RESUME_ROLE(), deployParams.committeeAddress)); + assertEq(ccr.getRoleMemberCount(ccr.PAUSE_ROLE()), 1); + assertEq(ccr.getRoleMemberCount(ccr.RESUME_ROLE()), 1); } function test_proxy() public { - OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); assertEq(proxy.proxy__getAdmin(), address(deployParams.proxyAdmin)); assertFalse(proxy.proxy__getIsOssified()); - CCCP cccpImpl = CCCP(proxy.proxy__getImplementation()); - assertEq(cccpImpl.getContractVersion(), type(uint64).max); + CCR ccrImpl = CCR(proxy.proxy__getImplementation()); + assertEq(ccrImpl.getContractVersion(), type(uint64).max); vm.expectRevert(Initializable.InvalidInitialization.selector); - cccp.initialize({ + ccr.initialize({ committeeAddress: deployParams.committeeAddress, - optInMinDurationBlocks: deployParams.optInMinDurationBlocks, - optOutDelayDurationBlocks: deployParams.optOutDelayDurationBlocks, - defaultOperatorMaxValidators: deployParams.defaultOperatorMaxValidators, + optInDelayBlocks: deployParams.optInDelayBlocks, + optOutDelayBlocks: deployParams.optOutDelayBlocks, + defaultOperatorMaxKeys: deployParams.defaultOperatorMaxKeys, defaultBlockGasLimit: deployParams.defaultBlockGasLimit }); } diff --git a/test/fork/deployment/Upgradability.sol b/test/fork/deployment/Upgradability.sol index 6abc6bb..2db6efa 100644 --- a/test/fork/deployment/Upgradability.sol +++ b/test/fork/deployment/Upgradability.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.28; import {Test} from "forge-std/Test.sol"; import {OssifiableProxy} from "../../../src/lib/proxy/OssifiableProxy.sol"; -import {CCCPMock} from "../../helpers/mocks/CCCPMock.sol"; +import {CCRMock} from "../../helpers/mocks/CCRMock.sol"; import {DeploymentFixtures} from "../../helpers/Fixtures.sol"; contract UpgradabilityTest is Test, DeploymentFixtures { @@ -15,28 +15,28 @@ contract UpgradabilityTest is Test, DeploymentFixtures { initializeFromDeployment(env.DEPLOY_CONFIG); } - function test_CCCPUpgradeTo() public { - OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); - CCCPMock newCccp = new CCCPMock(address(cccp.LIDO_LOCATOR()), "csm-type-new"); + function test_CCRUpgradeTo() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); + CCRMock newCcr = new CCRMock(address(ccr.LIDO_LOCATOR()), "csm-type-new"); vm.prank(proxy.proxy__getAdmin()); - proxy.proxy__upgradeTo(address(newCccp)); - assertEq(CCCPMock(address(cccp)).__test__getCSModuleType(), "csm-type-new"); + proxy.proxy__upgradeTo(address(newCcr)); + assertEq(CCRMock(address(ccr)).__test__getCSModuleType(), "csm-type-new"); } - function test_CCCPUpgradeToAndCall() public { - OssifiableProxy proxy = OssifiableProxy(payable(address(cccp))); - CCCPMock newCccp = new CCCPMock(address(cccp.LIDO_LOCATOR()), "csm-type-new"); + function test_CCRUpgradeToAndCall() public { + OssifiableProxy proxy = OssifiableProxy(payable(address(ccr))); + CCRMock newCcr = new CCRMock(address(ccr.LIDO_LOCATOR()), "csm-type-new"); - address contractAdmin = cccp.getRoleMember(cccp.DEFAULT_ADMIN_ROLE(), 0); + address contractAdmin = ccr.getRoleMember(ccr.DEFAULT_ADMIN_ROLE(), 0); vm.prank(contractAdmin); - cccp.pause(); - assertTrue(cccp.paused()); + ccr.pause(); + assertTrue(ccr.paused()); vm.prank(proxy.proxy__getAdmin()); - proxy.proxy__upgradeToAndCall(address(newCccp), abi.encodeCall(newCccp.initialize_v2, ())); - assertEq(CCCPMock(address(cccp)).__test__getCSModuleType(), "csm-type-new"); - assertEq(cccp.getContractVersion(), 2); - assertFalse(cccp.paused()); + proxy.proxy__upgradeToAndCall(address(newCcr), abi.encodeCall(newCcr.initialize_v2, ())); + assertEq(CCRMock(address(ccr)).__test__getCSModuleType(), "csm-type-new"); + assertEq(ccr.getContractVersion(), 2); + assertFalse(ccr.paused()); } } diff --git a/test/helpers/CCCPCommon.sol b/test/helpers/CCRCommon.sol similarity index 89% rename from test/helpers/CCCPCommon.sol rename to test/helpers/CCRCommon.sol index 3c6444c..04123d0 100644 --- a/test/helpers/CCCPCommon.sol +++ b/test/helpers/CCRCommon.sol @@ -10,7 +10,7 @@ import {StakingRouterMock} from "test/helpers/mocks/StakingRouterMock.sol"; import {CuratedModuleMock} from "test/helpers/mocks/CuratedModuleMock.sol"; import {CSModuleMock} from "test/helpers/mocks/CSModuleMock.sol"; -abstract contract CCCPFixtures is Test, Fixtures, Utilities { +abstract contract CCRFixtures is Test, Fixtures, Utilities { LidoLocatorMock public locator; StakingRouterMock public sr; CuratedModuleMock public nor; @@ -25,9 +25,9 @@ abstract contract CCCPFixtures is Test, Fixtures, Utilities { address internal noCurated1Manager; address internal committee; - // uint64 optInMinDurationBlocks = 100; - // uint64 optOutDelayDurationBlocks = 200; - // uint64 defaultOperatorMaxValidators = 100; + // uint64 optInDelayBlocks = 100; + // uint64 optOutDelayBlocks = 200; + // uint64 defaultOperatorMaxKeys = 100; // uint64 defaultBlockGasLimit = 1000000; // function createNo(uint256 modId, address rewAddr) internal returns (uint256) { @@ -49,7 +49,7 @@ abstract contract CCCPFixtures is Test, Fixtures, Utilities { } } -contract CCCPCommon is CCCPFixtures { +contract CCRCommon is CCRFixtures { function setUp() public virtual { committee = nextAddress("COMMITTEE"); stranger1 = nextAddress("STRANGER1"); diff --git a/test/helpers/Fixtures.sol b/test/helpers/Fixtures.sol index 7c7c4d7..efe0ca9 100644 --- a/test/helpers/Fixtures.sol +++ b/test/helpers/Fixtures.sol @@ -6,7 +6,7 @@ import {StdCheats} from "forge-std/StdCheats.sol"; import {Test} from "forge-std/Test.sol"; import {DeployParams} from "../../script/DeployBase.sol"; -import {CCCP} from "../../src/CCCP.sol"; +import {CCR} from "../../src/CCR.sol"; import {ILidoLocator} from "../../src/interfaces/ILidoLocator.sol"; import {IStakingRouter} from "../../src/interfaces/IStakingRouter.sol"; @@ -53,11 +53,11 @@ contract DeploymentFixtures is StdCheats, Test { struct DeploymentConfig { uint256 chainId; - address cccp; + address ccr; address lidoLocator; } - CCCP public cccp; + CCR public ccr; ILidoLocator public locator; IStakingRouter public stakingRouter; @@ -73,7 +73,7 @@ contract DeploymentFixtures is StdCheats, Test { DeploymentConfig memory deploymentConfig = parseDeploymentConfig(config); assertEq(deploymentConfig.chainId, block.chainid, "ChainId mismatch"); - cccp = CCCP(deploymentConfig.cccp); + ccr = CCR(deploymentConfig.ccr); locator = ILidoLocator(deploymentConfig.lidoLocator); stakingRouter = IStakingRouter(locator.stakingRouter()); } @@ -81,8 +81,8 @@ contract DeploymentFixtures is StdCheats, Test { function parseDeploymentConfig(string memory config) public returns (DeploymentConfig memory deploymentConfig) { deploymentConfig.chainId = vm.parseJsonUint(config, ".ChainId"); - deploymentConfig.cccp = vm.parseJsonAddress(config, ".CCCP"); - vm.label(deploymentConfig.cccp, "csm"); + deploymentConfig.ccr = vm.parseJsonAddress(config, ".CCR"); + vm.label(deploymentConfig.ccr, "csm"); deploymentConfig.lidoLocator = vm.parseJsonAddress(config, ".LidoLocator"); vm.label(deploymentConfig.lidoLocator, "LidoLocator"); diff --git a/test/helpers/mocks/CCCPMock.sol b/test/helpers/mocks/CCRMock.sol similarity index 65% rename from test/helpers/mocks/CCCPMock.sol rename to test/helpers/mocks/CCRMock.sol index 0c4696f..b2df96b 100644 --- a/test/helpers/mocks/CCCPMock.sol +++ b/test/helpers/mocks/CCRMock.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.28; -import {CCCP} from "../../../src/CCCP.sol"; +import {CCR} from "../../../src/CCR.sol"; -contract CCCPMock is CCCP { - constructor(address lidoLocator, bytes32 csModuleType) CCCP(lidoLocator, csModuleType) {} +contract CCRMock is CCR { + constructor(address lidoLocator, bytes32 csModuleType) CCR(lidoLocator, csModuleType) {} function __test__getCSModuleType() external view returns (bytes32) { return CS_MODULE_TYPE; diff --git a/test/helpers/mocks/CSModuleMock.sol b/test/helpers/mocks/CSModuleMock.sol index 43c8ad3..d1068e6 100644 --- a/test/helpers/mocks/CSModuleMock.sol +++ b/test/helpers/mocks/CSModuleMock.sol @@ -8,7 +8,7 @@ import {StakingModuleMock} from "./StakingModuleMock.sol"; contract CSModuleMock is StakingModuleMock, ICSModule { function getNodeOperator(uint256 id) public view returns (CSMNodeOperator memory no) { - no.totalAddedKeys = ops[id].totalAddedValidators; + no.totalAddedKeys = ops[id].totalAddedKeys; no.rewardAddress = ops[id].rewardAddress; } diff --git a/test/helpers/mocks/CuratedModuleMock.sol b/test/helpers/mocks/CuratedModuleMock.sol index 1371e96..a399fd0 100644 --- a/test/helpers/mocks/CuratedModuleMock.sol +++ b/test/helpers/mocks/CuratedModuleMock.sol @@ -15,19 +15,19 @@ contract CuratedModuleMock is StakingModuleMock, ICuratedModule { bool active, string memory name, address rewardAddress, - uint64 totalVettedValidators, - uint64 totalExitedValidators, - uint64 totalAddedValidators + uint64 totalVettedKeys, + uint64 totalExitedKeys, + uint64 totalAddedKeys ) { active = ops[id].active; rewardAddress = ops[id].rewardAddress; - totalAddedValidators = ops[id].totalAddedValidators; + totalAddedKeys = ops[id].totalAddedKeys; // silence warnings name; - totalExitedValidators; - totalVettedValidators; + totalExitedKeys; + totalVettedKeys; } function getType() external pure returns (bytes32 moduleType) { diff --git a/test/helpers/mocks/StakingModuleMock.sol b/test/helpers/mocks/StakingModuleMock.sol index 5e182cb..d8d98de 100644 --- a/test/helpers/mocks/StakingModuleMock.sol +++ b/test/helpers/mocks/StakingModuleMock.sol @@ -9,7 +9,7 @@ abstract contract StakingModuleMock is IStakingModule { struct NO { bool active; address rewardAddress; - uint32 totalAddedValidators; + uint32 totalAddedKeys; } NO[] public ops; @@ -23,11 +23,11 @@ abstract contract StakingModuleMock is IStakingModule { function updNo(uint256 id, bool active, address rewAddr, uint32 keys) public { ops[id].active = active; ops[id].rewardAddress = rewAddr; - ops[id].totalAddedValidators = keys; + ops[id].totalAddedKeys = keys; } function updNoKeys(uint256 id, uint32 keys) public { - ops[id].totalAddedValidators = keys; + ops[id].totalAddedKeys = keys; } function updNoActive(uint256 id, bool active) public { From aa264e41802dab6ba19b1534900e71b4959c1dbf Mon Sep 17 00:00:00 2001 From: KRogLA Date: Tue, 11 Feb 2025 13:59:20 +0100 Subject: [PATCH 33/33] fix: holesky deployed artifacts --- artifacts/holesky/deploy-holesky.json | 4 ++-- artifacts/holesky/transactions.json | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/artifacts/holesky/deploy-holesky.json b/artifacts/holesky/deploy-holesky.json index cc81c34..12c34da 100644 --- a/artifacts/holesky/deploy-holesky.json +++ b/artifacts/holesky/deploy-holesky.json @@ -1,6 +1,6 @@ { - "CCCP": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", - "CCCPImpl": "0x76773E5A432113238be489F9A7Bf716e8dC4a015", + "CCR": "0x5e30e8958a4361ac7a7C4FcE978FF18f70E915a2", + "CCRImpl": "0x76773E5A432113238be489F9A7Bf716e8dC4a015", "ChainId": 17000, "DeployParams": "0x00000000000000000000000028fab2059c713a7f9d8c86db49f9bb0e96af1ef8636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef000000000000000000000000401fd888b5e41113b7c0c47725a742bbc3a083ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000f4240", "LidoLocator": "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" diff --git a/artifacts/holesky/transactions.json b/artifacts/holesky/transactions.json index 3d2842d..dcfa80e 100644 --- a/artifacts/holesky/transactions.json +++ b/artifacts/holesky/transactions.json @@ -46,7 +46,7 @@ { "hash": "0x138cad03fbe5a1d4f10a6765b4328249858670248d32bb3f8fbbde49ac17a339", "transactionType": "CREATE", - "contractName": "CCCP", + "contractName": "CCR", "contractAddress": "0x7c178b9b797c6ea6776a784c22a0f95a79385c9b", "function": null, "arguments": [ @@ -88,7 +88,7 @@ { "hash": "0xfb7ba8b9366bd8189498e164cabf5317c199115d94919cbf432de63e185af960", "transactionType": "CREATE", - "contractName": "CCCP", + "contractName": "CCR", "contractAddress": "0x0442fb2c8607d04d6ba496ea8990da7bd7ec25fc", "function": null, "arguments": [ @@ -130,7 +130,7 @@ { "hash": "0xfa237667dbb4f06775db358bbb699b0170861becbb005617df009b5138c46164", "transactionType": "CREATE", - "contractName": "CCCP", + "contractName": "CCR", "contractAddress": "0x82cfae30820b4249acd6315c90a4d0a9fef5a99a", "function": null, "arguments": [ @@ -172,7 +172,7 @@ { "hash": "0x7bec86c8525d9afe2a2ae2cce7526d114800521780f991aad20a53b06e363dee", "transactionType": "CREATE", - "contractName": "CCCP", + "contractName": "CCR", "contractAddress": "0x76773e5a432113238be489f9a7bf716e8dc4a015", "function": null, "arguments": [