From 5f23870cb6ef7a7e59d1f32204b54b7ea7ad6699 Mon Sep 17 00:00:00 2001 From: Marco Tabasco Date: Wed, 30 Aug 2023 11:49:53 +0200 Subject: [PATCH 1/6] Remove registerAuth implementation (#257) * remove registerAuth implementation * remove test * remove all references to registerAuth in tests --- contracts/SSVNetwork.sol | 19 ---- .../RegisterAuth.sol | 4 +- contracts/interfaces/ISSVNetwork.sol | 4 - contracts/test/interfaces/ISSVNetworkT.sol | 2 - test/account/withdraw.ts | 1 - test/dao/network-fee-withdraw.ts | 1 - test/deployment/deploy.ts | 37 -------- test/helpers/contract-helpers.ts | 8 -- test/liquidate/liquidate.ts | 1 - test/liquidate/liquidated-cluster.ts | 1 - test/liquidate/reactivate.ts | 1 - test/operators/others.ts | 3 - test/operators/register-auth.ts | 87 ------------------- test/operators/register.ts | 2 - test/operators/remove.ts | 1 - test/sanity/balances.ts | 49 ++++++----- test/validators/register.ts | 13 --- test/validators/remove.ts | 1 - 18 files changed, 28 insertions(+), 207 deletions(-) rename contracts/{libraries => deprecated}/RegisterAuth.sol (70%) delete mode 100644 test/operators/register-auth.ts diff --git a/contracts/SSVNetwork.sol b/contracts/SSVNetwork.sol index e445fee6..25f9d73a 100644 --- a/contracts/SSVNetwork.sol +++ b/contracts/SSVNetwork.sol @@ -12,7 +12,6 @@ import "./libraries/Types.sol"; import "./libraries/CoreLib.sol"; import "./libraries/SSVStorage.sol"; import "./libraries/SSVStorageProtocol.sol"; -import "./libraries/RegisterAuth.sol"; import "./SSVProxy.sol"; @@ -119,8 +118,6 @@ contract SSVNetwork is /*******************************/ function registerOperator(bytes calldata publicKey, uint256 fee) external override returns (uint64 id) { - if (!RegisterAuth.load().authorization[msg.sender].registerOperator) revert NotAuthorized(); - _delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_OPERATORS]); } @@ -175,8 +172,6 @@ contract SSVNetwork is uint256 amount, ISSVNetworkCore.Cluster memory cluster ) external override { - if (!RegisterAuth.load().authorization[msg.sender].registerValidator) revert NotAuthorized(); - _delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS]); } @@ -263,18 +258,4 @@ contract SSVNetwork is function updateModule(SSVModules moduleId, address moduleAddress) external onlyOwner { CoreLib.setModuleContract(moduleId, moduleAddress); } - - /*******************************/ - /* Register Authorization */ - /*******************************/ - function setRegisterAuth(address userAddress, bool authOperator, bool authValidator) external override onlyOwner { - RegisterAuth.load().authorization[userAddress] = Authorization(authOperator, authValidator); - } - - function getRegisterAuth( - address userAddress - ) external view override returns (bool authOperators, bool authValidators) { - Authorization memory auth = RegisterAuth.load().authorization[userAddress]; - return (auth.registerOperator, auth.registerValidator); - } } diff --git a/contracts/libraries/RegisterAuth.sol b/contracts/deprecated/RegisterAuth.sol similarity index 70% rename from contracts/libraries/RegisterAuth.sol rename to contracts/deprecated/RegisterAuth.sol index 351721b3..48cc836d 100644 --- a/contracts/libraries/RegisterAuth.sol +++ b/contracts/deprecated/RegisterAuth.sol @@ -6,8 +6,10 @@ struct Authorization { bool registerValidator; } +/// @notice Deprecated. Notice that if the library is used again, +/// all the values in AuthData.authorization will still be available. library RegisterAuth { - uint256 constant private SSV_STORAGE_POSITION = uint256(keccak256("ssv.network.storage.auth")) - 1; + uint256 private constant SSV_STORAGE_POSITION = uint256(keccak256("ssv.network.storage.auth")) - 1; struct AuthData { mapping(address => Authorization) authorization; diff --git a/contracts/interfaces/ISSVNetwork.sol b/contracts/interfaces/ISSVNetwork.sol index de6e5e73..e88a56a6 100644 --- a/contracts/interfaces/ISSVNetwork.sol +++ b/contracts/interfaces/ISSVNetwork.sol @@ -31,8 +31,4 @@ interface ISSVNetwork { function setFeeRecipientAddress(address feeRecipientAddress) external; function updateModule(SSVModules moduleId, address moduleAddress) external; - - function setRegisterAuth(address userAddress, bool authOperators, bool authValidators) external; - - function getRegisterAuth(address userAddress) external view returns (bool authOperators, bool authValidators); } diff --git a/contracts/test/interfaces/ISSVNetworkT.sol b/contracts/test/interfaces/ISSVNetworkT.sol index eaa3d0e3..84786b1b 100644 --- a/contracts/test/interfaces/ISSVNetworkT.sol +++ b/contracts/test/interfaces/ISSVNetworkT.sol @@ -8,8 +8,6 @@ import "../../interfaces/ISSVDAO.sol"; import "../../interfaces/ISSVViews.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "../../libraries/RegisterAuth.sol"; - interface ISSVNetworkT { function initialize( IERC20 token_, diff --git a/test/account/withdraw.ts b/test/account/withdraw.ts index 6b4d5419..0ab67d61 100644 --- a/test/account/withdraw.ts +++ b/test/account/withdraw.ts @@ -22,7 +22,6 @@ describe('Withdraw Tests', () => { // cold register await helpers.coldRegisterValidator(); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[4].address, false, true); await ssvToken.connect(helpers.DB.owners[4]).approve(ssvNetworkContract.address, minDepositAmount); const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[4]).registerValidator( helpers.DataGenerator.publicKey(1), diff --git a/test/dao/network-fee-withdraw.ts b/test/dao/network-fee-withdraw.ts index 7c40f9c6..79721816 100644 --- a/test/dao/network-fee-withdraw.ts +++ b/test/dao/network-fee-withdraw.ts @@ -26,7 +26,6 @@ describe('DAO Network Fee Withdraw Tests', () => { // Set network fee await ssvNetworkContract.updateNetworkFee(networkFee); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[0].address, false, true); // Register validators // cold register await helpers.coldRegisterValidator(); diff --git a/test/deployment/deploy.ts b/test/deployment/deploy.ts index fd03fa5f..502408c4 100644 --- a/test/deployment/deploy.ts +++ b/test/deployment/deploy.ts @@ -15,8 +15,6 @@ describe('Deployment tests', () => { }); it('Upgrade SSVNetwork contract. Check new function execution', async () => { - await ssvNetworkContract.setRegisterAuth(DB.owners[1].address, true, false); - await ssvNetworkContract.connect(DB.owners[1]).registerOperator( DataGenerator.publicKey(0), CONFIG.minimalOperatorFee); @@ -96,41 +94,6 @@ describe('Deployment tests', () => { expect(await ssvNetworkViews.getNetworkFee()).to.be.equals(0); }); - it('Remove registerAuth from SSVNetwork contract', async () => { - const publicKey = DataGenerator.publicKey(4); - await ssvNetworkContract.setRegisterAuth(DB.owners[1].address, true, false); - - await ssvNetworkContract.connect(DB.owners[1]).registerOperator( - publicKey, - CONFIG.minimalOperatorFee); - - const SSVNetworkUpgrade = await ethers.getContractFactory("SSVNetworkUpgrade"); - const ssvNetworkUpgrade = await upgrades.upgradeProxy(ssvNetworkContract.address, SSVNetworkUpgrade, { - kind: 'uups', - unsafeAllow: ['delegatecall'] - }); - await ssvNetworkUpgrade.deployed(); - - expect(await ssvNetworkViews.getOperatorById(1)).to.deep.equal( - [DB.owners[1].address, // owner - CONFIG.minimalOperatorFee, // fee - 0, // validatorCount - ethers.constants.AddressZero, // whitelisted - false, // isPrivate - true // active - ]); - - await expect(ssvNetworkContract.connect(DB.owners[4]).registerOperator( - publicKey, - CONFIG.minimalOperatorFee - )).to.be.revertedWithCustomError(ssvNetworkContract, 'OperatorAlreadyExists'); - - await expect(ssvNetworkContract.connect(DB.owners[1]).registerOperator( - DataGenerator.publicKey(2), - CONFIG.minimalOperatorFee - )).to.emit(ssvNetworkContract, 'OperatorAdded').withArgs(2, DB.owners[1].address, DataGenerator.publicKey(2), CONFIG.minimalOperatorFee); - }); - it('Update a module (SSVOperators)', async () => { const ssvNetworkFactory = await ethers.getContractFactory('SSVNetwork'); const ssvNetwork = await ssvNetworkFactory.attach(ssvNetworkContract.address); diff --git a/test/helpers/contract-helpers.ts b/test/helpers/contract-helpers.ts index 8bef455c..9d1f8de5 100644 --- a/test/helpers/contract-helpers.ts +++ b/test/helpers/contract-helpers.ts @@ -145,8 +145,6 @@ export const initializeContract = async () => { await DB.ssvNetwork.contract.deployed(); - await DB.ssvNetwork.contract.setRegisterAuth(DB.owners[0].address, true, true); - DB.ssvViews.contract = await upgrades.deployProxy(ssvViews, [ DB.ssvNetwork.contract.address ], @@ -170,7 +168,6 @@ export const initializeContract = async () => { }; export const registerOperators = async (ownerId: number, numberOfOperators: number, fee: string, gasGroups: GasGroup[] = [GasGroup.REGISTER_OPERATOR]) => { - await DB.ssvNetwork.contract.setRegisterAuth(DB.owners[ownerId].address, true, false); for (let i = 0; i < numberOfOperators; ++i) { const { eventsByName } = await trackGas( DB.ssvNetwork.contract.connect(DB.owners[ownerId]).registerOperator(DataGenerator.publicKey(i), fee), @@ -230,7 +227,6 @@ export const reactivate = async (ownerId: number, operatorIds: number[], amount: }; export const registerValidators = async (ownerId: number, numberOfValidators: number, amount: string, operatorIds: number[], gasGroups?: GasGroup[]) => { - await DB.ssvNetwork.contract.setRegisterAuth(DB.owners[ownerId].address, false, true); const validators: any = []; let args: any; // Register validators to contract @@ -260,8 +256,6 @@ export const registerValidators = async (ownerId: number, numberOfValidators: nu }; export const registerValidatorsRaw = async (ownerId: number, numberOfValidators: number, amount: string, operatorIds: number[], gasGroups?: GasGroup[]) => { - await DB.ssvNetwork.contract.setRegisterAuth(DB.owners[ownerId].address, false, true); - let cluster: any = { validatorCount: 0, networkFeeIndex: 0, @@ -294,8 +288,6 @@ export const getCluster = (payload: any) => ethers.utils.AbiCoder.prototype.enco ); export const coldRegisterValidator = async () => { - await DB.ssvNetwork.contract.setRegisterAuth(DB.owners[0].address, false, true); - await DB.ssvToken.approve(DB.ssvNetwork.contract.address, '1000000000000000'); await DB.ssvNetwork.contract.registerValidator( DataGenerator.publicKey(90), diff --git a/test/liquidate/liquidate.ts b/test/liquidate/liquidate.ts index e4346fc4..5691499f 100644 --- a/test/liquidate/liquidate.ts +++ b/test/liquidate/liquidate.ts @@ -22,7 +22,6 @@ describe('Liquidate Tests', () => { // cold register await helpers.coldRegisterValidator(); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, false, true); // first validator await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerValidator( diff --git a/test/liquidate/liquidated-cluster.ts b/test/liquidate/liquidated-cluster.ts index 4c33e168..3839edc5 100644 --- a/test/liquidate/liquidated-cluster.ts +++ b/test/liquidate/liquidated-cluster.ts @@ -23,7 +23,6 @@ describe('Liquidate Tests', () => { await ssvNetworkContract.updateNetworkFee(networkFee); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, false, true); // first validator await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount * 2); const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerValidator( diff --git a/test/liquidate/reactivate.ts b/test/liquidate/reactivate.ts index f19537d7..b11057f8 100644 --- a/test/liquidate/reactivate.ts +++ b/test/liquidate/reactivate.ts @@ -22,7 +22,6 @@ describe('Reactivate Tests', () => { // cold register await helpers.coldRegisterValidator(); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, false, true); // first validator await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerValidator( diff --git a/test/operators/others.ts b/test/operators/others.ts index 5de72c00..e40916da 100644 --- a/test/operators/others.ts +++ b/test/operators/others.ts @@ -14,7 +14,6 @@ describe('Others Operator Tests', () => { }); it('Add fee recipient address emits "FeeRecipientAddressUpdated"', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, true, false); await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).setFeeRecipientAddress( helpers.DB.owners[2].address )) @@ -37,7 +36,6 @@ describe('Others Operator Tests', () => { }); it('Non-owner remove operator whitelisted address reverts "CallerNotOwner"', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, true, false); const result = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerOperator( helpers.DataGenerator.publicKey(1), helpers.CONFIG.minimalOperatorFee @@ -63,7 +61,6 @@ describe('Others Operator Tests', () => { }); it('Non-owner update operator whitelisted address reverts "CallerNotOwner"', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, true, false); const result = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerOperator( helpers.DataGenerator.publicKey(1), helpers.CONFIG.minimalOperatorFee diff --git a/test/operators/register-auth.ts b/test/operators/register-auth.ts deleted file mode 100644 index fb2abd48..00000000 --- a/test/operators/register-auth.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Declare imports -import * as helpers from '../helpers/contract-helpers'; -import { expect } from 'chai'; - -// Declare globals -let ssvNetworkContract: any; - -describe('Register Auth Operator Tests', () => { - before(async () => { - const metadata = (await helpers.initializeContract()); - ssvNetworkContract = metadata.contract; - }); - - it('Register auth and get auth data', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[10].address, true, false); - expect(await ssvNetworkContract.getRegisterAuth(helpers.DB.owners[10].address)).to.deep.equal( - [true, false] - ); - - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[11].address, false, true); - expect(await ssvNetworkContract.getRegisterAuth(helpers.DB.owners[11].address)).to.deep.equal( - [false, true] - ) - - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[12].address, true, true); - expect(await ssvNetworkContract.getRegisterAuth(helpers.DB.owners[12].address)).to.deep.equal( - [true, true] - ) - - expect(await ssvNetworkContract.getRegisterAuth(helpers.DB.owners[5].address)).to.deep.equal( - [false, false] - ) - }); - - it('Register operator with unauthorized address reverts "NotAuthorized"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).registerOperator( - helpers.DataGenerator.publicKey(12), - helpers.CONFIG.minimalOperatorFee - )).to.be.revertedWithCustomError(ssvNetworkContract, 'NotAuthorized'); - - }); - - it('Register operator with unauthorized address reverts "NotAuthorized" (2)', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, false, true); - - await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).registerOperator( - helpers.DataGenerator.publicKey(12), - helpers.CONFIG.minimalOperatorFee - )).to.be.revertedWithCustomError(ssvNetworkContract, 'NotAuthorized'); - }); - - it('Register validator with unauthorized address reverts "NotAuthorized"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[3]).registerValidator( - helpers.DataGenerator.publicKey(12), - [1, 2, 3, 4], - helpers.DataGenerator.shares(4), - 10000000, - { - validatorCount: 0, - networkFeeIndex: 0, - index: 0, - balance: 0, - active: true - } - )).to.be.revertedWithCustomError(ssvNetworkContract, 'NotAuthorized'); - - }); - - it('Register validator with unauthorized address reverts "NotAuthorized" (2)', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[3].address, true, false); - - await expect(ssvNetworkContract.connect(helpers.DB.owners[3]).registerValidator( - helpers.DataGenerator.publicKey(12), - [1, 2, 3, 4], - helpers.DataGenerator.shares(4), - 10000000, - { - validatorCount: 0, - networkFeeIndex: 0, - index: 0, - balance: 0, - active: true - } - )).to.be.revertedWithCustomError(ssvNetworkContract, 'NotAuthorized'); - }); - -}); \ No newline at end of file diff --git a/test/operators/register.ts b/test/operators/register.ts index 480dea69..1e603f9c 100644 --- a/test/operators/register.ts +++ b/test/operators/register.ts @@ -11,8 +11,6 @@ describe('Register Operator Tests', () => { const metadata = (await helpers.initializeContract()); ssvNetworkContract = metadata.contract; ssvViews = metadata.ssvViews; - - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, true, false); }); it('Register operator emits "OperatorAdded"', async () => { diff --git a/test/operators/remove.ts b/test/operators/remove.ts index 56367e2d..47b59cb3 100644 --- a/test/operators/remove.ts +++ b/test/operators/remove.ts @@ -26,7 +26,6 @@ describe('Remove Operator Tests', () => { }); it('Remove private operator emits "OperatorRemoved"', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[0].address, true, false); const result = await trackGas(ssvNetworkContract.registerOperator( helpers.DataGenerator.publicKey(12), helpers.CONFIG.minimalOperatorFee diff --git a/test/sanity/balances.ts b/test/sanity/balances.ts index dfc9b883..e723405e 100644 --- a/test/sanity/balances.ts +++ b/test/sanity/balances.ts @@ -79,20 +79,20 @@ describe('Balance Tests', () => { it('Check operators earnings in three blocks, one after the other', async () => { await utils.progressBlocks(1); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(2)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(4)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 3); - await utils.progressBlocks(1); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(2)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(4)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 3); - await utils.progressBlocks(1); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(2)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 3); - expect(await ssvViews.getOperatorEarnings(4)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 3); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(2)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(4)).to.equal(helpers.CONFIG.minimalOperatorFee * 2 + helpers.CONFIG.minimalOperatorFee * 2); + await utils.progressBlocks(1); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(2)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(4)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee * 2); + await utils.progressBlocks(1); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(2)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(4)).to.equal(helpers.CONFIG.minimalOperatorFee * 6 + helpers.CONFIG.minimalOperatorFee * 2); }); it('Check cluster balance with removed operator', async () => { @@ -128,10 +128,11 @@ describe('Balance Tests', () => { // update network fee // register a new validator with some shared operators // update network fee + // progress blocks in the process await utils.progressBlocks(1); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 3 + helpers.CONFIG.minimalOperatorFee); expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.args.operatorIds, cluster1.args.cluster)).to.equal(minDepositAmount - burnPerBlock); expect(await ssvViews.getNetworkEarnings() - initNetworkFeeBalance).to.equal(networkFee * 2); @@ -148,28 +149,28 @@ describe('Balance Tests', () => { const cluster2 = await helpers.registerValidators(4, 1, minDep2.toString(), [3, 4, 5, 6]); await utils.progressBlocks(2); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 10 + helpers.CONFIG.minimalOperatorFee * 9); - expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 10 + helpers.CONFIG.minimalOperatorFee * 9 + helpers.CONFIG.minimalOperatorFee * 2); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 8 + helpers.CONFIG.minimalOperatorFee * 8); + expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 8 + helpers.CONFIG.minimalOperatorFee * 8 + helpers.CONFIG.minimalOperatorFee * 2); expect(await ssvViews.getOperatorEarnings(5)).to.equal(helpers.CONFIG.minimalOperatorFee * 2); - expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.args.operatorIds, cluster1.args.cluster)).to.equal(minDepositAmount - burnPerBlock * 2 - newBurnPerBlock * 6); + expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.args.operatorIds, cluster1.args.cluster)).to.equal(minDepositAmount - burnPerBlock * 2 - newBurnPerBlock * 5); expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster2.args.operatorIds, cluster2.args.cluster)).to.equal(minDep2 - newBurnPerBlock * 2); // cold cluster + cluster1 * networkFee (4) + (cold cluster + cluster1 * newNetworkFee (5 + 5)) + cluster2 * newNetworkFee (2) - expect(await ssvViews.getNetworkEarnings() - initNetworkFeeBalance).to.equal(networkFee * 4 + newNetworkFee * 5 + newNetworkFee * 5 + newNetworkFee * 4); + expect(await ssvViews.getNetworkEarnings() - initNetworkFeeBalance).to.equal(networkFee * 4 + newNetworkFee * 5 + newNetworkFee * 4 + newNetworkFee * 3); await ssvNetworkContract.updateNetworkFee(networkFee); await utils.progressBlocks(4); - expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.args.operatorIds, cluster1.args.cluster)).to.equal(minDepositAmount - burnPerBlock * 2 - newBurnPerBlock * 7 - burnPerBlock * 4); + expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.args.operatorIds, cluster1.args.cluster)).to.equal(minDepositAmount - burnPerBlock * 2 - newBurnPerBlock * 6 - burnPerBlock * 4); expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster2.args.operatorIds, cluster2.args.cluster)).to.equal(minDep2 - newBurnPerBlock * 3 - burnPerBlock * 4); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 15 + helpers.CONFIG.minimalOperatorFee * 14); - expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 15 + helpers.CONFIG.minimalOperatorFee * 14 + helpers.CONFIG.minimalOperatorFee * 7); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 14 + helpers.CONFIG.minimalOperatorFee * 12); + expect(await ssvViews.getOperatorEarnings(3)).to.equal(helpers.CONFIG.minimalOperatorFee * 14 + helpers.CONFIG.minimalOperatorFee * 12 + helpers.CONFIG.minimalOperatorFee * 7); expect(await ssvViews.getOperatorEarnings(5)).to.equal(helpers.CONFIG.minimalOperatorFee * 7); // cold cluster + cluster1 * networkFee (4) + (cold cluster + cluster1 * newNetworkFee (6 + 6)) + cluster2 * newNetworkFee (3) + (cold cluster + cluster1 + cluster2 * networkFee (4 + 4 + 4)) - expect(await ssvViews.getNetworkEarnings() - initNetworkFeeBalance).to.equal(networkFee * 4 + newNetworkFee * 7 + newNetworkFee * 7 + newNetworkFee * 3 + networkFee * 12); + expect(await ssvViews.getNetworkEarnings() - initNetworkFeeBalance).to.equal(networkFee * 4 + newNetworkFee * 6 + newNetworkFee * 6 + newNetworkFee * 3 + networkFee * 12); }); it('Check operator earnings and cluster balance when reducing operator fee"', async () => { @@ -178,7 +179,7 @@ describe('Balance Tests', () => { await utils.progressBlocks(2); - expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee + newFee * 4); + expect(await ssvViews.getOperatorEarnings(1)).to.equal(helpers.CONFIG.minimalOperatorFee * 4 + helpers.CONFIG.minimalOperatorFee + newFee * 2); expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.args.operatorIds, cluster1.args.cluster)).to.equal(minDepositAmount - burnPerBlock - ((helpers.CONFIG.minimalOperatorFee * 3 + networkFee) * 2) - newFee * 2); }); diff --git a/test/validators/register.ts b/test/validators/register.ts index 894f19cd..52f5f9c0 100644 --- a/test/validators/register.ts +++ b/test/validators/register.ts @@ -20,7 +20,6 @@ describe('Register Validator Tests', () => { minDepositAmount = (helpers.CONFIG.minimalBlocksBeforeLiquidation + 2) * helpers.CONFIG.minimalOperatorFee * 13; // cold register - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[6].address, false, true); await helpers.DB.ssvToken.connect(helpers.DB.owners[6]).approve(helpers.DB.ssvNetwork.contract.address, '1000000000000000'); cluster1 = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[6]).registerValidator( helpers.DataGenerator.publicKey(90), @@ -35,8 +34,6 @@ describe('Register Validator Tests', () => { active: true } )); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, true, true); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[0].address, true, true); }); it('Register validator with 4 operators emits "ValidatorAdded"', async () => { @@ -132,8 +129,6 @@ describe('Register Validator Tests', () => { args.cluster ), [GasGroup.REGISTER_VALIDATOR_EXISTING_CLUSTER]); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[2].address, false, true); - await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); await trackGas(ssvNetworkContract.connect(helpers.DB.owners[2]).registerValidator( helpers.DataGenerator.publicKey(4), @@ -250,8 +245,6 @@ describe('Register Validator Tests', () => { args.cluster ), [GasGroup.REGISTER_VALIDATOR_EXISTING_CLUSTER_7]); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[2].address, false, true); - await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); await trackGas(ssvNetworkContract.connect(helpers.DB.owners[2]).registerValidator( helpers.DataGenerator.publicKey(4), @@ -368,8 +361,6 @@ describe('Register Validator Tests', () => { args.cluster ), [GasGroup.REGISTER_VALIDATOR_EXISTING_CLUSTER_10]); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[2].address, false, true); - await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); await trackGas(ssvNetworkContract.connect(helpers.DB.owners[2]).registerValidator( helpers.DataGenerator.publicKey(4), @@ -486,8 +477,6 @@ describe('Register Validator Tests', () => { args.cluster ), [GasGroup.REGISTER_VALIDATOR_EXISTING_CLUSTER_13]); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[2].address, false, true); - await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); await trackGas(ssvNetworkContract.connect(helpers.DB.owners[2]).registerValidator( helpers.DataGenerator.publicKey(4), @@ -795,8 +784,6 @@ describe('Register Validator Tests', () => { await ssvNetworkContract.connect(helpers.DB.owners[1]).setOperatorWhitelist(operatorId, helpers.DB.owners[3].address); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[3].address, false, true); - await helpers.DB.ssvToken.connect(helpers.DB.owners[3]).approve(ssvNetworkContract.address, minDepositAmount); await expect(ssvNetworkContract.connect(helpers.DB.owners[3]).registerValidator( helpers.DataGenerator.publicKey(1), diff --git a/test/validators/remove.ts b/test/validators/remove.ts index 6cc23b45..84e69601 100644 --- a/test/validators/remove.ts +++ b/test/validators/remove.ts @@ -22,7 +22,6 @@ describe('Remove Validator Tests', () => { // cold register await helpers.coldRegisterValidator(); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[1].address, false, true); // first validator await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerValidator( From 530dd5dcd2ab162582c460cd50b4cb67529c86f5 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 30 Aug 2023 12:27:13 +0200 Subject: [PATCH 2/6] remove pending registerAuth --- test/liquidate/liquidated-cluster.ts | 2 -- test/operators/update-fee.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/test/liquidate/liquidated-cluster.ts b/test/liquidate/liquidated-cluster.ts index 3839edc5..31922daa 100644 --- a/test/liquidate/liquidated-cluster.ts +++ b/test/liquidate/liquidated-cluster.ts @@ -124,8 +124,6 @@ describe('Liquidate Tests', () => { }); it('Remove validator -> withdraw -> try liquidate reverts "ClusterNotLiquidatable"', async () => { - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[2].address, false, true); - await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[2]).registerValidator( helpers.DataGenerator.publicKey(2), diff --git a/test/operators/update-fee.ts b/test/operators/update-fee.ts index ed43f2ee..8d31054b 100644 --- a/test/operators/update-fee.ts +++ b/test/operators/update-fee.ts @@ -123,7 +123,6 @@ describe('Operator Fee Tests', () => { it('Declare fee too high reverts "FeeTooHigh" -> DAO updates limit -> declare fee emits "OperatorFeeDeclared"', async () => { const maxOperatorFee = 8e14; await ssvNetworkContract.updateMaximumOperatorFee(maxOperatorFee); - await ssvNetworkContract.setRegisterAuth(helpers.DB.owners[3].address, true, false); await ssvNetworkContract.connect(helpers.DB.owners[3]).registerOperator(helpers.DataGenerator.publicKey(10), maxOperatorFee); const newOperatorFee = maxOperatorFee + maxOperatorFee / 10; From 50db186abffe0e3115757df47ab72b67d6ff18bf Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 30 Aug 2023 14:04:03 +0200 Subject: [PATCH 3/6] deploy stage-dev 30082023 --- .openzeppelin/goerli.json | 241 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) diff --git a/.openzeppelin/goerli.json b/.openzeppelin/goerli.json index 80a1047a..43588e64 100644 --- a/.openzeppelin/goerli.json +++ b/.openzeppelin/goerli.json @@ -130,6 +130,11 @@ "address": "0xcDc4423E9ffa9542d4CdDf42a70859C84859d2A9", "txHash": "0x964e728e77bd4afa121c93bfd55076c36a5de0b764214dbb9ee574fa1976a9ad", "kind": "uups" + }, + { + "address": "0xFe35A31e57946E8aadd25158BdF303A36dEf3332", + "txHash": "0x73d4d1df08c7c3d95e8b34545aa55b9ceb7e7e07f7138cb524c7565c56b03e91", + "kind": "uups" } ], "impls": { @@ -6888,6 +6893,242 @@ } } } + }, + "0b47d2abd2279e76837ff94805e14a88d019593eed05755759cc40256af16f0e": { + "address": "0x296C821446f8756A6d30784C6CF63B65c2B82863", + "txHash": "0xc449b9dd299fe868cc5e284843897df96fb79db91b313450c96ee7e6c3b80a69", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_pendingOwner", + "offset": 0, + "slot": "201", + "type": "t_address", + "contract": "Ownable2StepUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:27" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "Ownable2StepUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:70" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + }, + "368acb5d5852996f01c66abefb8ca54bce00f1977c8aaed5efcf0c75f250303d": { + "address": "0xE20E557C5173D505a58eEBf3C4E6aD2672c57Fd1", + "txHash": "0xcde725501175a0ce2045f30df0fc6b3ed3fed0f2baab220f25163ec7c4f93933", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_pendingOwner", + "offset": 0, + "slot": "201", + "type": "t_address", + "contract": "Ownable2StepUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:27" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "Ownable2StepUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:70" + }, + { + "label": "ssvNetwork", + "offset": 0, + "slot": "251", + "type": "t_contract(ISSVViews)4189", + "contract": "SSVNetworkViews", + "src": "contracts/SSVNetworkViews.sol:19" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)50_storage", + "contract": "SSVNetworkViews", + "src": "contracts/SSVNetworkViews.sol:23" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(ISSVViews)4189": { + "label": "contract ISSVViews", + "numberOfBytes": "20" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } } } } From b8e03e7d2edf28a4631e16489f00052ed42a7e74 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 31 Aug 2023 14:06:34 +0200 Subject: [PATCH 4/6] deploy stage v1.0.0.rc4 --- .openzeppelin/goerli.json | 108 ++++++++++++++++++++++++++ contracts/SSVNetworkViews.sol | 2 +- contracts/libraries/CoreLib.sol | 2 +- contracts/test/libraries/CoreLibT.sol | 2 +- test/deployment/version.ts | 2 +- test/helpers/contract-helpers.ts | 2 +- 6 files changed, 113 insertions(+), 5 deletions(-) diff --git a/.openzeppelin/goerli.json b/.openzeppelin/goerli.json index 43588e64..a8fa54b4 100644 --- a/.openzeppelin/goerli.json +++ b/.openzeppelin/goerli.json @@ -7129,6 +7129,114 @@ } } } + }, + "1e550124c6e28f236ee8632b9abc9a68dac2f537aff421981b339520c87ad539": { + "address": "0x0097bBea812414d42D2AD6d76c7da1c794AA16A9", + "txHash": "0xf3a3e1c1742cd1d11b7271abf2768c6046122eb06a2f66971a931021b25763c5", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_pendingOwner", + "offset": 0, + "slot": "201", + "type": "t_address", + "contract": "Ownable2StepUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:27" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "Ownable2StepUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol:70" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } } } } diff --git a/contracts/SSVNetworkViews.sol b/contracts/SSVNetworkViews.sol index b731d7f8..babaa08d 100644 --- a/contracts/SSVNetworkViews.sol +++ b/contracts/SSVNetworkViews.sol @@ -20,7 +20,7 @@ contract SSVNetworkViews is UUPSUpgradeable, Ownable2StepUpgradeable, ISSVViews // @dev reserve storage space for future new state variables in base contract // slither-disable-next-line shadowing-state - uint256[50] private __gap; + uint256[50] private__gap; function _authorizeUpgrade(address) internal override onlyOwner {} diff --git a/contracts/libraries/CoreLib.sol b/contracts/libraries/CoreLib.sol index e8d31429..b68004a2 100644 --- a/contracts/libraries/CoreLib.sol +++ b/contracts/libraries/CoreLib.sol @@ -7,7 +7,7 @@ library CoreLib { event ModuleUpgraded(SSVModules indexed moduleId, address moduleAddress); function getVersion() internal pure returns (string memory) { - return "v1.0.0.rc3"; + return "v1.0.0.rc4"; } function transferBalance(address to, uint256 amount) internal { diff --git a/contracts/test/libraries/CoreLibT.sol b/contracts/test/libraries/CoreLibT.sol index 9125938f..83879dcf 100644 --- a/contracts/test/libraries/CoreLibT.sol +++ b/contracts/test/libraries/CoreLibT.sol @@ -6,7 +6,7 @@ import "../../libraries/SSVStorage.sol"; library CoreLibT { function getVersion() internal pure returns (string memory) { - return "v1.0.0.rc3"; + return "v1.0.0.rc4"; } function transfer(address to, uint256 amount) internal { diff --git a/test/deployment/version.ts b/test/deployment/version.ts index d13aa736..9d91518d 100644 --- a/test/deployment/version.ts +++ b/test/deployment/version.ts @@ -20,7 +20,7 @@ describe('Version upgrade tests', () => { await ssvNetworkContract.updateModule(helpers.SSV_MODULES.SSV_VIEWS, viewsContract.address) - expect(await ssvNetworkViews.getVersion()).to.equal("v1.0.0.rc3"); + expect(await ssvNetworkViews.getVersion()).to.equal("v1.0.0.rc4"); }); }); \ No newline at end of file diff --git a/test/helpers/contract-helpers.ts b/test/helpers/contract-helpers.ts index 9d1f8de5..e970e3b5 100644 --- a/test/helpers/contract-helpers.ts +++ b/test/helpers/contract-helpers.ts @@ -65,7 +65,7 @@ export const DataGenerator = { export const initializeContract = async () => { CONFIG = { - initialVersion: "v1.0.0.rc3", + initialVersion: "v1.0.0.rc4", operatorMaxFeeIncrease: 1000, declareOperatorFeePeriod: 3600, // HOUR executeOperatorFeePeriod: 86400, // DAY From a0c291495e0e7631c63fd87e0b31a683e5ae2676 Mon Sep 17 00:00:00 2001 From: Marco Tabasco Date: Fri, 29 Sep 2023 11:02:53 +0200 Subject: [PATCH 5/6] Fix network withdrawals (#262) * fix sequential network withdrawals issue --- CHANGELOG.md | 13 +++++++++ contracts/modules/SSVDAO.sol | 1 + test/account/withdraw.ts | 46 +++++++++++++++++++++++++------- test/dao/network-fee-withdraw.ts | 16 ++++++++--- 4 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..166b5261 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to SSV Network contracts will be documented in this file. + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [v1.0.1.rc4] - 2023-09-19 + +### Fixed + +- [22d2859](https://github.com/bloxapp/ssv-network/pull/262/commits/22d2859d8fe6267b09c7a1c9c645df19bdaa03ff) Fix bug in network earnings withdrawals. diff --git a/contracts/modules/SSVDAO.sol b/contracts/modules/SSVDAO.sol index cec7a6cf..e3e0a700 100644 --- a/contracts/modules/SSVDAO.sol +++ b/contracts/modules/SSVDAO.sol @@ -34,6 +34,7 @@ contract SSVDAO is ISSVDAO { } sp.daoBalance = networkBalance - shrunkAmount; + sp.daoIndexBlockNumber = uint32(block.number); CoreLib.transferBalance(msg.sender, amount); diff --git a/test/account/withdraw.ts b/test/account/withdraw.ts index 0ab67d61..5c5c6068 100644 --- a/test/account/withdraw.ts +++ b/test/account/withdraw.ts @@ -5,13 +5,15 @@ import { expect } from 'chai'; import { trackGas, GasGroup } from '../helpers/gas-usage'; // Declare globals -let ssvNetworkContract: any, ssvToken: any, cluster1: any, minDepositAmount: any; +let ssvNetworkContract: any, ssvViews: any, ssvToken: any, cluster1: any, minDepositAmount: any; describe('Withdraw Tests', () => { beforeEach(async () => { // Initialize contract const metadata = (await helpers.initializeContract()); ssvNetworkContract = metadata.contract; + ssvNetworkContract = metadata.contract; + ssvViews = metadata.ssvViews; ssvToken = metadata.ssvToken; // Register operators @@ -48,19 +50,19 @@ describe('Withdraw Tests', () => { }); it('Withdraw from operator balance emits "OperatorWithdrawn"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[0])['withdrawOperatorEarnings(uint64,uint256)'](1, helpers.CONFIG.minimalOperatorFee)).to.emit(ssvNetworkContract, 'OperatorWithdrawn'); + await expect(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawOperatorEarnings(1, helpers.CONFIG.minimalOperatorFee)).to.emit(ssvNetworkContract, 'OperatorWithdrawn'); }); it('Withdraw from operator balance gas limits', async () => { - await trackGas(ssvNetworkContract.connect(helpers.DB.owners[0])['withdrawOperatorEarnings(uint64,uint256)'](1, helpers.CONFIG.minimalOperatorFee), [GasGroup.WITHDRAW_OPERATOR_BALANCE]); + await trackGas(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawOperatorEarnings(1, helpers.CONFIG.minimalOperatorFee), [GasGroup.WITHDRAW_OPERATOR_BALANCE]); }); it('Withdraw the total operator balance emits "OperatorWithdrawn"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[0])['withdrawAllOperatorEarnings(uint64)'](1)).to.emit(ssvNetworkContract, 'OperatorWithdrawn'); + await expect(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawAllOperatorEarnings(1)).to.emit(ssvNetworkContract, 'OperatorWithdrawn'); }); it('Withdraw the total operator balance gas limits', async () => { - await trackGas(ssvNetworkContract.connect(helpers.DB.owners[0])['withdrawAllOperatorEarnings(uint64)'](1), [GasGroup.WITHDRAW_OPERATOR_BALANCE]); + await trackGas(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawAllOperatorEarnings(1), [GasGroup.WITHDRAW_OPERATOR_BALANCE]); }); it('Withdraw from a cluster that has a removed operator emits "ClusterWithdrawn"', async () => { @@ -72,6 +74,21 @@ describe('Withdraw Tests', () => { await expect(ssvNetworkContract.connect(helpers.DB.owners[4]).withdraw(cluster1.operatorIds, minDepositAmount, cluster1.cluster)).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); }); + it('Sequentially withdraw more than the cluster balance reverts "InsufficientBalance"', async () => { + const burnPerBlock = helpers.CONFIG.minimalOperatorFee * 4; + + cluster1 = await helpers.deposit(1, cluster1.owner, cluster1.operatorIds, (minDepositAmount * 2).toString(), cluster1.cluster); + expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.operatorIds, cluster1.cluster)).to.be.equals(minDepositAmount * 3 - (burnPerBlock * 2)); + + cluster1 = await helpers.withdraw(4, cluster1.operatorIds, minDepositAmount, cluster1.cluster); + expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.operatorIds, cluster1.cluster)).to.be.equals(minDepositAmount * 2 - (burnPerBlock * 3)); + + cluster1 = await helpers.withdraw(4, cluster1.operatorIds, minDepositAmount, cluster1.cluster); + expect(await ssvViews.getBalance(helpers.DB.owners[4].address, cluster1.operatorIds, cluster1.cluster)).to.be.equals(minDepositAmount - (burnPerBlock * 4)); + + await expect(ssvNetworkContract.connect(helpers.DB.owners[4]).withdraw(cluster1.operatorIds, minDepositAmount, cluster1.cluster)).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); + }); + it('Withdraw from a liquidatable cluster reverts "InsufficientBalance" (liquidation threshold)', async () => { await utils.progressBlocks(20); await expect(ssvNetworkContract.connect(helpers.DB.owners[4]).withdraw(cluster1.operatorIds, 4000000000, cluster1.cluster)).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); @@ -88,20 +105,31 @@ describe('Withdraw Tests', () => { }); it('Withdraw balance from an operator I do not own reverts "CallerNotOwner"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[2])['withdrawOperatorEarnings(uint64,uint256)'](1, minDepositAmount)).to.be.revertedWithCustomError(ssvNetworkContract, 'CallerNotOwner'); + await expect(ssvNetworkContract.connect(helpers.DB.owners[2]).withdrawOperatorEarnings(1, minDepositAmount)).to.be.revertedWithCustomError(ssvNetworkContract, 'CallerNotOwner'); }); it('Withdraw more than the operator balance reverts "InsufficientBalance"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[0])['withdrawOperatorEarnings(uint64,uint256)'](1, minDepositAmount + await expect(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawOperatorEarnings(1, minDepositAmount + )).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); + }); + + it('Sequentially withdraw more than the operator balance reverts "InsufficientBalance"', async () => { + await ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawOperatorEarnings(1, helpers.CONFIG.minimalOperatorFee * 3); + expect(await ssvViews.getOperatorEarnings(1)).to.be.equals(helpers.CONFIG.minimalOperatorFee * 4 - helpers.CONFIG.minimalOperatorFee * 3); + + await ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawOperatorEarnings(1, helpers.CONFIG.minimalOperatorFee * 3); + expect(await ssvViews.getOperatorEarnings(1)).to.be.equals(helpers.CONFIG.minimalOperatorFee * 6 - helpers.CONFIG.minimalOperatorFee * 6); + + await expect(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawOperatorEarnings(1, helpers.CONFIG.minimalOperatorFee * 3 )).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); }); it('Withdraw the total balance from an operator I do not own reverts "CallerNotOwner"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[2])['withdrawAllOperatorEarnings(uint64)'](12)).to.be.revertedWithCustomError(ssvNetworkContract, 'CallerNotOwner'); + await expect(ssvNetworkContract.connect(helpers.DB.owners[2]).withdrawAllOperatorEarnings(12)).to.be.revertedWithCustomError(ssvNetworkContract, 'CallerNotOwner'); }); it('Withdraw more than the operator total balance reverts "InsufficientBalance"', async () => { - await expect(ssvNetworkContract.connect(helpers.DB.owners[0])['withdrawAllOperatorEarnings(uint64)'](12)).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); + await expect(ssvNetworkContract.connect(helpers.DB.owners[0]).withdrawAllOperatorEarnings(12)).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); }); it('Withdraw from a cluster without validators', async () => { diff --git a/test/dao/network-fee-withdraw.ts b/test/dao/network-fee-withdraw.ts index 79721816..c09cdad1 100644 --- a/test/dao/network-fee-withdraw.ts +++ b/test/dao/network-fee-withdraw.ts @@ -33,9 +33,6 @@ describe('DAO Network Fee Withdraw Tests', () => { await helpers.registerValidators(4, 1, minDepositAmount, helpers.DataGenerator.cluster.new(), [GasGroup.REGISTER_VALIDATOR_NEW_STATE]); await utils.progressBlocks(10); - // Temporary till deposit logic not available - // Mint tokens - await helpers.DB.ssvToken.mint(ssvNetworkContract.address, minDepositAmount); }); it('Withdraw network earnings emits "NetworkEarningsWithdrawn"', async () => { @@ -68,4 +65,17 @@ describe('DAO Network Fee Withdraw Tests', () => { await expect(ssvNetworkContract.connect(helpers.DB.owners[3]).withdrawNetworkEarnings(amount )).to.be.revertedWith('Ownable: caller is not the owner'); }); + + it('Withdraw network earnings sequentially when not enough balance reverts "InsufficientBalance"', async () => { + const amount = await ssvViews.getNetworkEarnings() / 2; + + await ssvNetworkContract.withdrawNetworkEarnings(amount); + expect(await ssvViews.getNetworkEarnings()).to.be.equals(((networkFee * 13) + (networkFee * 11) - amount)); + + await ssvNetworkContract.withdrawNetworkEarnings(amount); + expect(await ssvViews.getNetworkEarnings()).to.be.equals(((networkFee * 14) + (networkFee * 12) - amount * 2)); + + await expect(ssvNetworkContract.withdrawNetworkEarnings(amount + )).to.be.revertedWithCustomError(ssvNetworkContract, 'InsufficientBalance'); + }); }); \ No newline at end of file From 8324ce818aca070ae7d4d089d78a7c194db543a1 Mon Sep 17 00:00:00 2001 From: Marco Tabasco Date: Fri, 29 Sep 2023 11:51:55 +0200 Subject: [PATCH 6/6] A validator can voluntarily exit (#263) * A validator can voluntarily exit --- CHANGELOG.md | 3 + contracts/SSVNetwork.sol | 4 + contracts/interfaces/ISSVClusters.sol | 7 ++ contracts/libraries/ValidatorLib.sol | 27 +++++ contracts/modules/SSVClusters.sol | 22 ++-- contracts/test/SSVNetworkUpgrade.sol | 9 +- test/helpers/gas-usage.ts | 3 + test/validators/others.ts | 154 ++++++++++++++++++++++++++ 8 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 contracts/libraries/ValidatorLib.sol create mode 100644 test/validators/others.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 166b5261..45173209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,3 +11,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed - [22d2859](https://github.com/bloxapp/ssv-network/pull/262/commits/22d2859d8fe6267b09c7a1c9c645df19bdaa03ff) Fix bug in network earnings withdrawals. + +### Added +- [bf0c51d](https://github.com/bloxapp/ssv-network/pull/263/commits/bf0c51d4df191018052d11425c9fcc252de61431) A validator can voluntarily exit. \ No newline at end of file diff --git a/contracts/SSVNetwork.sol b/contracts/SSVNetwork.sol index 25f9d73a..3380af0a 100644 --- a/contracts/SSVNetwork.sol +++ b/contracts/SSVNetwork.sol @@ -216,6 +216,10 @@ contract SSVNetwork is _delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS]); } + function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external override { + _delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS]); + } + function updateNetworkFee(uint256 fee) external override onlyOwner { _delegate(SSVStorage.load().ssvContracts[SSVModules.SSV_DAO]); } diff --git a/contracts/interfaces/ISSVClusters.sol b/contracts/interfaces/ISSVClusters.sol index 9910e9fd..88f3e36b 100644 --- a/contracts/interfaces/ISSVClusters.sol +++ b/contracts/interfaces/ISSVClusters.sol @@ -57,6 +57,11 @@ interface ISSVClusters is ISSVNetworkCore { /// @param cluster Cluster where the withdrawal will be made function withdraw(uint64[] memory operatorIds, uint256 tokenAmount, Cluster memory cluster) external; + /// @notice Fires the exit event for a validator + /// @param publicKey The public key of the validator to be exited + /// @param operatorIds Array of IDs of operators managing the validator + function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external; + /** * @dev Emitted when the validator has been added. * @param publicKey The public key of a validator. @@ -81,4 +86,6 @@ interface ISSVClusters is ISSVNetworkCore { event ClusterWithdrawn(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster); event ClusterDeposited(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster); + + event ValidatorExited(bytes indexed publicKey, uint64[] operatorIds); } diff --git a/contracts/libraries/ValidatorLib.sol b/contracts/libraries/ValidatorLib.sol new file mode 100644 index 00000000..ad98ee5e --- /dev/null +++ b/contracts/libraries/ValidatorLib.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import "../interfaces/ISSVNetworkCore.sol"; +import "./SSVStorage.sol"; + +library ValidatorLib { + function validateState( + bytes calldata publicKey, + uint64[] calldata operatorIds, + StorageData storage s + ) internal view returns (bytes32 hashedValidator) { + hashedValidator = keccak256(abi.encodePacked(publicKey, msg.sender)); + bytes32 validatorData = s.validatorPKs[hashedValidator]; + + if (validatorData == bytes32(0)) { + revert ISSVNetworkCore.ValidatorDoesNotExist(); + } + bytes32 mask = ~bytes32(uint256(1)); // All bits set to 1 except LSB + + bytes32 hashedOperatorIds = keccak256(abi.encodePacked(operatorIds)) & mask; // Clear LSB of provided operator ids + if ((validatorData & mask) != hashedOperatorIds) { + // Clear LSB of stored validator data and compare + revert ISSVNetworkCore.IncorrectValidatorState(); + } + } +} diff --git a/contracts/modules/SSVClusters.sol b/contracts/modules/SSVClusters.sol index 8d1df9aa..acef8f0e 100644 --- a/contracts/modules/SSVClusters.sol +++ b/contracts/modules/SSVClusters.sol @@ -6,6 +6,7 @@ import "../libraries/ClusterLib.sol"; import "../libraries/OperatorLib.sol"; import "../libraries/ProtocolLib.sol"; import "../libraries/CoreLib.sol"; +import "../libraries/ValidatorLib.sol"; import "../libraries/SSVStorage.sol"; import "../libraries/SSVStorageProtocol.sol"; @@ -145,20 +146,7 @@ contract SSVClusters is ISSVClusters { ) external override { StorageData storage s = SSVStorage.load(); - bytes32 hashedValidator = keccak256(abi.encodePacked(publicKey, msg.sender)); - - bytes32 mask = ~bytes32(uint256(1)); // All bits set to 1 except LSB - bytes32 validatorData = s.validatorPKs[hashedValidator]; - - if (validatorData == bytes32(0)) { - revert ValidatorDoesNotExist(); - } - - bytes32 hashedOperatorIds = keccak256(abi.encodePacked(operatorIds)) & mask; // Clear LSB of provided operator ids - if ((validatorData & mask) != hashedOperatorIds) { - // Clear LSB of stored validator data and compare - revert IncorrectValidatorState(); - } + bytes32 hashedValidator = ValidatorLib.validateState(publicKey, operatorIds, s); bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s); @@ -344,4 +332,10 @@ contract SSVClusters is ISSVClusters { emit ClusterWithdrawn(msg.sender, operatorIds, amount, cluster); } + + function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external override { + ValidatorLib.validateState(publicKey, operatorIds, SSVStorage.load()); + + emit ValidatorExited(publicKey, operatorIds); + } } diff --git a/contracts/test/SSVNetworkUpgrade.sol b/contracts/test/SSVNetworkUpgrade.sol index c859582f..c7288c17 100644 --- a/contracts/test/SSVNetworkUpgrade.sol +++ b/contracts/test/SSVNetworkUpgrade.sol @@ -286,6 +286,13 @@ contract SSVNetworkUpgrade is ); } + function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external override { + _delegateCall( + SSVStorage.load().ssvContracts[SSVModules.SSV_CLUSTERS], + abi.encodeWithSignature("exitValidator(bytes,uint64[]))", publicKey, operatorIds) + ); + } + function updateNetworkFee(uint256 fee) external override onlyOwner { _delegateCall( SSVStorage.load().ssvContracts[SSVModules.SSV_DAO], @@ -336,7 +343,7 @@ contract SSVNetworkUpgrade is } function updateMaximumOperatorFee(uint64 maxFee) external override { - _delegateCall( + _delegateCall( SSVStorage.load().ssvContracts[SSVModules.SSV_DAO], abi.encodeWithSignature("updateMaximumOperatorFee(uint64)", maxFee) ); diff --git a/test/helpers/gas-usage.ts b/test/helpers/gas-usage.ts index d67cd633..7b733092 100644 --- a/test/helpers/gas-usage.ts +++ b/test/helpers/gas-usage.ts @@ -31,6 +31,7 @@ export enum GasGroup { DEPOSIT, WITHDRAW_CLUSTER_BALANCE, WITHDRAW_OPERATOR_BALANCE, + VALIDATOR_EXIT, LIQUIDATE_CLUSTER_4, LIQUIDATE_CLUSTER_7, @@ -81,6 +82,8 @@ const MAX_GAS_PER_GROUP: any = { [GasGroup.DEPOSIT]: 77500, [GasGroup.WITHDRAW_CLUSTER_BALANCE]: 94500, [GasGroup.WITHDRAW_OPERATOR_BALANCE]: 64900, + [GasGroup.VALIDATOR_EXIT]: 41200, + [GasGroup.LIQUIDATE_CLUSTER_4]: 129300, [GasGroup.LIQUIDATE_CLUSTER_7]: 170500, [GasGroup.LIQUIDATE_CLUSTER_10]: 211600, diff --git a/test/validators/others.ts b/test/validators/others.ts new file mode 100644 index 00000000..6e5db6f5 --- /dev/null +++ b/test/validators/others.ts @@ -0,0 +1,154 @@ +// Declare imports +import * as helpers from '../helpers/contract-helpers'; +import { expect } from 'chai'; +import { trackGas, GasGroup } from '../helpers/gas-usage'; + +// Declare globals +let ssvNetworkContract: any, minDepositAmount: any, firstCluster: any; + +describe('Other Validator Tests', () => { + beforeEach(async () => { + // Initialize contract + const metadata = (await helpers.initializeContract()); + ssvNetworkContract = metadata.contract; + + minDepositAmount = (helpers.CONFIG.minimalBlocksBeforeLiquidation + 10) * helpers.CONFIG.minimalOperatorFee * 4; + + // Register operators + await helpers.registerOperators(0, 14, helpers.CONFIG.minimalOperatorFee); + + // Register a validator + // cold register + await helpers.coldRegisterValidator(); + + // first validator + await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); + const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerValidator( + helpers.DataGenerator.publicKey(1), + [1, 2, 3, 4], + helpers.DataGenerator.shares(4), + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true + } + ), [GasGroup.REGISTER_VALIDATOR_NEW_STATE]); + firstCluster = register.eventsByName.ValidatorAdded[0].args; + }); + + it('Exiting a validator emits "ValidatorExited"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(1), + firstCluster.operatorIds, + )).to.emit(ssvNetworkContract, 'ValidatorExited') + .withArgs(helpers.DataGenerator.publicKey(1), firstCluster.operatorIds); + }); + + it('Exiting a validator gas limit', async () => { + await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(1), + firstCluster.operatorIds, + ), [GasGroup.VALIDATOR_EXIT]); + }); + + it('Exiting one of the validators in a cluster emits "ValidatorExited"', async () => { + await helpers.DB.ssvToken.connect(helpers.DB.owners[1]).approve(ssvNetworkContract.address, minDepositAmount); + await trackGas(ssvNetworkContract.connect(helpers.DB.owners[1]).registerValidator( + helpers.DataGenerator.publicKey(2), + [1, 2, 3, 4], + helpers.DataGenerator.shares(4), + minDepositAmount, + firstCluster.cluster + )); + + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(2), + firstCluster.operatorIds, + )).to.emit(ssvNetworkContract, 'ValidatorExited') + .withArgs(helpers.DataGenerator.publicKey(2), firstCluster.operatorIds); + }); + + it('Exiting a removed validator reverts "ValidatorDoesNotExist"', async () => { + await ssvNetworkContract.connect(helpers.DB.owners[1]).removeValidator( + helpers.DataGenerator.publicKey(1), + firstCluster.operatorIds, + firstCluster.cluster + ); + + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(1), + firstCluster.operatorIds + )).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorDoesNotExist'); + }); + + it('Exiting a non-existing validator reverts "ValidatorDoesNotExist"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(12), + firstCluster.operatorIds + )).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorDoesNotExist'); + }); + + it('Exiting a validator with empty operator list reverts "IncorrectValidatorState"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(1), + [] + )).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorState'); + }); + + it('Exiting a validator with empty public key reverts "ValidatorDoesNotExist"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + '0x', + firstCluster.operatorIds + )).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorDoesNotExist'); + }); + + it('Exiting a validator using the wrong account reverts "ValidatorDoesNotExist"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[2]).exitValidator( + helpers.DataGenerator.publicKey(1), + firstCluster.operatorIds + )).to.be.revertedWithCustomError(ssvNetworkContract, 'ValidatorDoesNotExist'); + }); + + it('Exiting a validator with incorrect operators (unsorted list) reverts with "IncorrectValidatorState"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(1), + [4, 3, 2, 1] + )).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorState'); + }); + + it('Exiting a validator with incorrect operators (too many operators) reverts with "IncorrectValidatorState"', async () => { + minDepositAmount = (helpers.CONFIG.minimalBlocksBeforeLiquidation + 10) * helpers.CONFIG.minimalOperatorFee * 13; + + await helpers.DB.ssvToken.connect(helpers.DB.owners[2]).approve(ssvNetworkContract.address, minDepositAmount); + const register = await trackGas(ssvNetworkContract.connect(helpers.DB.owners[2]).registerValidator( + helpers.DataGenerator.publicKey(2), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + helpers.DataGenerator.shares(13), + minDepositAmount, + { + validatorCount: 0, + networkFeeIndex: 0, + index: 0, + balance: 0, + active: true + } + )); + const secondCluster = register.eventsByName.ValidatorAdded[0].args; + + await expect(ssvNetworkContract.connect(helpers.DB.owners[2]).exitValidator( + helpers.DataGenerator.publicKey(2), + secondCluster.operatorIds, + )).to.emit(ssvNetworkContract, 'ValidatorExited') + .withArgs(helpers.DataGenerator.publicKey(2), secondCluster.operatorIds); + }); + + it('Exiting a validator with incorrect operators reverts with "IncorrectValidatorState"', async () => { + await expect(ssvNetworkContract.connect(helpers.DB.owners[1]).exitValidator( + helpers.DataGenerator.publicKey(1), + [1, 2, 3, 5] + )).to.be.revertedWithCustomError(ssvNetworkContract, 'IncorrectValidatorState'); + }); +}); \ No newline at end of file