Skip to content

Commit

Permalink
added fork tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mtabasco committed Apr 29, 2024
1 parent 65347ee commit c61c701
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 43 deletions.
2 changes: 1 addition & 1 deletion contracts/interfaces/ISSVNetworkCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface ISSVNetworkCore {
uint64 fee;
/// @dev The address of the operator's owner
address owner;
/// @dev Whitelisted flag for this operator
/// @dev private flag for this operator
bool whitelisted;
/// @dev The state snapshot of the operator
Snapshot snapshot;
Expand Down
4 changes: 2 additions & 2 deletions contracts/interfaces/ISSVViews.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface ISSVViews is ISSVNetworkCore {
/// @return owner The owner of the operator
/// @return fee The fee associated with the operator (SSV)
/// @return validatorCount The count of validators associated with the operator
/// @return whitelistedContract The whitelisted contract address of the operator, if any
/// @return whitelistedAddress The whitelisted address of the operator. It can be and EOA or generic contract (legacy) or a whitelisting contract
/// @return isPrivate A boolean indicating if the operator is private (uses whitelisting contract or SSV Whitelisting module)
/// @return active A boolean indicating if the operator is active
function getOperatorById(
Expand All @@ -42,7 +42,7 @@ interface ISSVViews is ISSVNetworkCore {
address owner,
uint256 fee,
uint32 validatorCount,
address whitelistedContract,
address whitelistedAddress,
bool isPrivate,
bool active
);
Expand Down
4 changes: 2 additions & 2 deletions contracts/modules/SSVViews.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ contract SSVViews is ISSVViews {
address owner,
uint256 fee,
uint32 validatorCount,
address whitelistedContract,
address whitelistedAddress,
bool isPrivate,
bool isActive
)
Expand All @@ -69,7 +69,7 @@ contract SSVViews is ISSVViews {
owner = operator.owner;
fee = operator.fee.expand();
validatorCount = operator.validatorCount;
whitelistedContract = SSVStorage.load().operatorsWhitelist[operatorId];
whitelistedAddress = SSVStorage.load().operatorsWhitelist[operatorId];
isPrivate = operator.whitelisted;
isActive = operator.snapshot.block != 0;
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"scripts": {
"build": "npx hardhat compile",
"test": "npx hardhat test",
"fork-test": "FORK_TESTING_ENABLED=true npx hardhat test",
"test-forked": "FORK_TESTING_ENABLED=true npx hardhat test test-forked/*.ts",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint --fix . --ext .ts",
"solidity-coverage": "SOLIDITY_COVERAGE=true NO_GAS_ENFORCE=1 npx hardhat coverage",
Expand Down
190 changes: 190 additions & 0 deletions test-forked/operators-whitelist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Declare imports
import hre from 'hardhat';

import { setBalance } from '@nomicfoundation/hardhat-toolbox-viem/network-helpers';

import { expect } from 'chai';
import { ethers } from 'hardhat';

import { DataGenerator } from '../test/helpers/contract-helpers';
import { assertPostTxEvent } from '../test/helpers/utils/test';

import { Address, TestClient } from 'viem';

// Declare globals
let ssvNetwork: any, ssvViews: any, ssvToken: any, owners: any[], client: TestClient;

const ssvNetworkAddress = '0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1';
const ssvNetworkViewsAddress = '0xafE830B6Ee262ba11cce5F32fDCd760FFE6a66e4';
const ssvTokenAddress = '0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54';

const MOCK_SHARES =
'0x8f5f07aac10b2113fc18c178080e34665931f4bcefb743a888acdee7799fbf70c2db26c7dd9d102161aad3751b1db06c177659c3fe9af62ddae8e70dd1461267e97fe751974e09d4a8e1176e73567e29b2329566dd759ba464eab259111d3cb7b2843fe292c8b8d295c3b30b98ee77cd5d4aaf14dbfa3f63fb092b44d002a851fa9c16f4c1f1356fbff61757fff5d1c493826db56c37b7487bb50e0cd5cbb3f4698797f31f2c22373e6b601cae505233b5fa4e1dc583f0b7203372a807684990a7dfd9c775f6f3105f4d0200b63779b575bc709b4a55461f6597e506c9be2d253c8732a4acacc8292983218a516eced8a176064fad697ecd82b17adcee4d10a57f3fe9b539b467cb66fdf36e3f0b0591f1f161fcaf7c4bbb1ed000d50c8cc4e1960cc20b17c3c55c7b7a1f6b3c530f40f7b74225d38aff51cfdbfcacb78a9bdf6f31a5cf21b99c4d1055416935280b9b8f38850139a31263d625b7d4098f521225ae8941131c54fcf5a1b2589a01b96759ec92e869ad6c80ba73085a37034c291cd2716b6b1e9e8633b6037cf9312a7de72c9d04db5c52801e84b636f6c51762b63e48b454dba198fc604d326a3249370de6851a3f7ed4cbe2d3ff92de780ea7c9708df9012be9c115849c44533d574a35bce635e98e8835818052155bedd8aade6d27e480a6c497c804815b740489bd0790be851f97947fbbaf2526d84a05c9aebd0ad2161e117a2878a24e49932c7a2dff4b20725d20c1600c2103fcbadae04f5cb95ec413923e710b332be42ebc0264128b8f250063fa346be2f55108e917a9dd0c64a5159411ff4f99801f546c77891c88e4f6db6dddb8f18bd87be91a9ba0eaa076994448f97b4f3273ccc5aa51c0c6cbf6cf69f446862a0a5bbc0ca590a961871f8ef8f38f446db9cadd76675f53df8f41350d76a4015fa5b700b3caf07f9b2014778bf3400d9a43962c3ace37f1dadaa7afb546825acbf6081f2168e8496468f25fefac1b884f434f3884eb12a3e9536914a9c87b9a3dee92e83be856dc29718ae387f0066ca04ff0c8f07855a295b568358c4cf7de4964ecc9d69e0efd7102f915d0a343c85f3519e4ac0ba32b4912b2016389e3ebb9d1412b1118a428fc036b8a599614d033d89f764446b6486103bf98f782dcbdda5cfb415ff18a1fbe16d2cd448227b63bb6e2989cd54170cd4ca4400802910d77adf0bc670eba3c8c8a1571fbb73040b2f5fb391f5bbfa4f3e8b62cf8f516ce9ed6726516c19a956df1b7a20ab4fb82a0a3b0235518cc0c52fff7a59ca52a49c4b15f652466048933f6651a66abfa8eb4c8836ccc1db2a5fa7cba133923fc7ebbdda2f3db26c9e1a194dcb51543df4e06d401ec24d17bc42db2d822abad9e6a775ef6c33bfb54760839adb02cf5bd9740a59aca9a6dd20b0b90a68a940626094df638fb0a3405b1324508492a9549a316d0c8c7ab27303668fe6d61f3c75a27fc4a6008cbf948084881e9b34cdbfe2773d595d637b2ab1444219a9aad51e8f6d4a4905a5845e58cbc8ef743f30a9c17869dd5ce8adef13fe4e43fce4f380fe5a3e502e7699868ad8baa5aeb52d8c9f498a665365fac845fe6df6949a653af825e20e1e9966363ffbb0c3babe48165f72643cfded8cc451553edc1c2fd6e513533c9c51cc3ce6c12930f17fc27cec2ead93b095dc452dc3f988bb72e730e1f4c67b4852c4d20f9d8bc198d2b09962de51518d2c93f3f33a49b5d64a3ab20a4f1bbc0a972d075ab3f482c060f46d3b31c124ed8f8d89e056e3f40853f15cf92a34796c5435f2e44a1a3a941aa3afe9333b83f2a23617b715442ea13a256f7575cea9cce1c07e485';

describe('Whitelisting Tests (fork)', () => {
beforeEach(async () => {
owners = await hre.viem.getWalletClients();

client = await hre.viem.getTestClient();
await client.impersonateAccount({ address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' });

await setBalance('0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6', 500000000000000000n);

ssvNetwork = await hre.viem.getContractAt('SSVNetwork', ssvNetworkAddress);
ssvViews = await hre.viem.getContractAt('SSVNetworkViews', ssvNetworkViewsAddress as Address);
ssvToken = await hre.viem.getContractAt('SSVToken', ssvTokenAddress as Address);

const ssvNetworkUpgrade = await hre.viem.deployContract('SSVNetwork', [], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});
await ssvNetwork.write.upgradeTo([await ssvNetworkUpgrade.address], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});

const ssvViewsUpgrade = await hre.viem.deployContract('SSVNetworkViews', [], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});
await ssvViews.write.upgradeTo([await ssvViewsUpgrade.address], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});

await upgradeModule('SSVOperators', 0);
await upgradeModule('SSVClusters', 1);
await upgradeModule('SSVViews', 3);
await upgradeModule('SSVOperatorsWhitelist', 4);

await client.stopImpersonatingAccount({
address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6',
});
});

it('Check an existing whitelisted operator is whitelisted but not using an external contract', async () => {
const operatorData = await ssvViews.read.getOperatorById([314]);

expect(operatorData[3]).to.not.equal(ethers.ZeroAddress);
expect(operatorData[4]).to.equal(true);
expect(operatorData[5]).to.equal(true);

expect(await ssvViews.read.isWhitelistingContract([operatorData[3]])).to.equal(false);
});

it('Register using legacy whitelisted operators in 4 operators cluster events/logic', async () => {
// get the current number of validators for these operators
const operatorsValidatorsCount = {
'314': (await ssvViews.read.getOperatorById([314]))[2],
'315': (await ssvViews.read.getOperatorById([315]))[2],
'316': (await ssvViews.read.getOperatorById([316]))[2],
'317': (await ssvViews.read.getOperatorById([317]))[2],
};

// SSV contracts owner
await client.impersonateAccount({ address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' });

// 0xB4084F25DfCb2c1bf6636b420b59eda807953769 -> whitelisted address for operators 314, 315, 316, 317
await client.impersonateAccount({ address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' });
await setBalance('0xB4084F25DfCb2c1bf6636b420b59eda807953769', 500000000000000000n);

const liquidationCollateral = await ssvViews.read.getMinimumLiquidationCollateral();
const minDepositAmount = liquidationCollateral * 2n;

// give the sender enough SSV tokens
await ssvToken.write.mint(['0xB4084F25DfCb2c1bf6636b420b59eda807953769', minDepositAmount], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});

await ssvToken.write.approve([ssvNetwork.address, minDepositAmount], {
account: { address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' },
});

await ssvNetwork.write.registerValidator(
[
DataGenerator.publicKey(1),
[314, 315, 316, 317],
MOCK_SHARES,
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0n,
active: true,
},
],
{ account: { address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' } },
);

// event confirms full execution
await assertPostTxEvent([
{
contract: ssvNetwork,
eventName: 'ValidatorAdded',
argNames: ['owner'],
argValuesList: [['0xB4084F25DfCb2c1bf6636b420b59eda807953769']],
},
]);

// check the operators increased the number of validators by one
for (let i = 314; i < 318; i++) {
expect((await ssvViews.read.getOperatorById([i]))[2]).to.equal(operatorsValidatorsCount[i] + 1);
}
});

it('Replace a whitelisted address by an external whitelisting contract', async () => {
// owner of the operator 314
await client.impersonateAccount({ address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' });
await setBalance('0xB4084F25DfCb2c1bf6636b420b59eda807953769', 500000000000000000n);

// get the current whitelisted address
const prevWhitelistedAddress = (await ssvViews.read.getOperatorById([314]))[3];

const whitelistingContract = await hre.viem.deployContract(
'MockWhitelistingContract',
[['0xB4084F25DfCb2c1bf6636b420b59eda807953769']],
{
client: owners[0].client,
},
);
const whitelistingContractAddress = await whitelistingContract.address;
// Set the whitelisting contract for operators 1,2,3,4
await ssvNetwork.write.setOperatorsWhitelistingContract([[314], whitelistingContractAddress], {
account: { address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' },
});

// the operator now uses the whitelisting contract
expect((await ssvViews.read.getOperatorById([314]))[3]).to.deep.equal(whitelistingContractAddress);

// and the previous whitelisted address was passed to the SSV whitelisting module
expect(await ssvViews.read.getWhitelistedOperators([[314], prevWhitelistedAddress])).to.deep.equal([314n]);
});

it('Whitelist multiple operators for an already whitelisted operator', async () => {
// owner of the operator 314
await client.impersonateAccount({ address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' });
await setBalance('0xB4084F25DfCb2c1bf6636b420b59eda807953769', 500000000000000000n);

// get the current whitelisted address
const prevWhitelistedAddress = (await ssvViews.read.getOperatorById([314]))[3];

await ssvNetwork.write.setOperatorMultipleWhitelists([[314, 315, 316, 317], [owners[2].account.address]], {
account: { address: '0xB4084F25DfCb2c1bf6636b420b59eda807953769' },
});

expect(
await ssvViews.read.getWhitelistedOperators([[314, 315, 316, 317], owners[2].account.address]),
).to.deep.equal([314n, 315n, 316n, 317n]);

// the operator uses the previous whitelisting main address
expect((await ssvViews.read.getOperatorById([314]))[3]).to.deep.equal(prevWhitelistedAddress);
});
});

//* HELPERS */

const upgradeModule = async function (contractName: string, id: number) {
const ssvModule = await hre.viem.deployContract(contractName, [], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});
await ssvNetwork.write.updateModule([id, await ssvModule.address], {
account: { address: '0xb35096b074fdb9bBac63E3AdaE0Bbde512B2E6b6' },
});
};
24 changes: 1 addition & 23 deletions test/deployment/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
// Imports
import {
owners,
initializeContract,
DataGenerator,
CONFIG,
publicClient,
} from '../helpers/contract-helpers';
import { owners, initializeContract, DataGenerator, CONFIG, publicClient } from '../helpers/contract-helpers';
import { ethers, upgrades } from 'hardhat';
import { expect } from 'chai';

Expand Down Expand Up @@ -126,22 +120,6 @@ describe('Deployment tests', () => {
expect(await ssvViews.read.getNetworkFee()).to.be.equals(0);
});

it('Update a module (SSVOperators)', async () => {
const deployedSSVNetwork = await hre.viem.getContractAt('SSVNetwork', ssvNetwork.address as Address);

const operatorsImpl = await hre.viem.deployContract('SSVOperatorsUpdate', [], {
client: owners[1].client,
});

await expect(deployedSSVNetwork.write.updateModule([0, ethers.ZeroAddress])).to.be.rejectedWith(
'TargetModuleDoesNotExist',
);

await deployedSSVNetwork.write.updateModule([0, await operatorsImpl.address]);

await expect(ssvNetwork.write.declareOperatorFee([0, 0])).to.be.rejectedWith('NoFeeDeclared');
});

it('ETH can not be transferred to SSVNetwork / SSVNetwork views', async () => {
const amount = 10000000n;

Expand Down
46 changes: 32 additions & 14 deletions test/validators/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1249,13 +1249,22 @@ describe('Register Validator Tests', () => {
const shares = await Promise.all(Array.from({ length: 10 }, (_, index) => DataGenerator.shares(1, index, 4)));

await expect(
ssvNetwork.write.bulkRegisterValidator([pks, [1, 2, 3, 4, 5], shares, minDepositAmount, {
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0n,
active: true,
}], { account: owners[1].account },),
ssvNetwork.write.bulkRegisterValidator(
[
pks,
[1, 2, 3, 4, 5],
shares,
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0n,
active: true,
},
],
{ account: owners[1].account },
),
).to.be.rejectedWith('InvalidOperatorIdsLength');
});

Expand All @@ -1264,13 +1273,22 @@ describe('Register Validator Tests', () => {
const shares = await Promise.all(Array.from({ length: 10 }, (_, index) => DataGenerator.shares(1, index, 4)));

await expect(
ssvNetwork.write.bulkRegisterValidator([pks, [], shares, minDepositAmount, {
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0n,
active: true,
}], { account: owners[1].account },),
ssvNetwork.write.bulkRegisterValidator(
[
pks,
[],
shares,
minDepositAmount,
{
validatorCount: 0,
networkFeeIndex: 0,
index: 0,
balance: 0n,
active: true,
},
],
{ account: owners[1].account },
),
).to.be.rejectedWith('InvalidOperatorIdsLength');
});

Expand Down

0 comments on commit c61c701

Please sign in to comment.