Skip to content

Commit 583b7b7

Browse files
authored
Add BasicWhitelisting contract (#302)
* add BasicWhitelisting contract * add deployment task
1 parent 36e89f8 commit 583b7b7

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
pragma solidity 0.8.24;
3+
4+
import "@openzeppelin/contracts/access/Ownable.sol";
5+
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
6+
import {ISSVWhitelistingContract} from "../interfaces/external/ISSVWhitelistingContract.sol";
7+
8+
contract BasicWhitelisting is ISSVWhitelistingContract, ERC165, Ownable {
9+
mapping(address => bool) private whitelisted;
10+
11+
event AddressWhitelisted(address indexed account);
12+
event AddressRemovedFromWhitelist(address indexed account);
13+
14+
function addWhitelistedAddress(address account) external onlyOwner {
15+
whitelisted[account] = true;
16+
emit AddressWhitelisted(account);
17+
}
18+
19+
function removeWhitelistedAddress(address account) external onlyOwner {
20+
whitelisted[account] = false;
21+
emit AddressRemovedFromWhitelist(account);
22+
}
23+
24+
function isWhitelisted(address account, uint256) external view override returns (bool) {
25+
return whitelisted[account];
26+
}
27+
28+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
29+
return interfaceId == type(ISSVWhitelistingContract).interfaceId || super.supportsInterface(interfaceId);
30+
}
31+
}

tasks/deploy.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ task('deploy:main-impl', 'Deploys SSVNetwork / SSVNetworkViews implementation co
6161
await hre.run('deploy:impl', { contract });
6262
});
6363

64+
/**
65+
@title Hardhat task to deploy a basic whitelisting contract implementation.
66+
The deployment process involves running a subtask that handles the actual deployment.
67+
@returns {void} This function doesn't return anything. If the deployment process encounters an error,
68+
it will be printed to the console, and the process will exit with a non-zero status code.
69+
@example
70+
// Deploy BasicWhitelisting contract with the default deployer account
71+
npx hardhat --network holesky_testnet deploy:whitelisting-contract
72+
@remarks
73+
The deployer account used will be the first one returned by ethers.getSigners().
74+
Therefore, it should be appropriately configured in your Hardhat network configuration.
75+
This task uses the "deploy:impl" subtask for the actual deployment, specifying 'BasicWhitelisting' as the contract name.
76+
*/
77+
task('deploy:whitelisting-contract', 'Deploys a basic whitelisting contract').setAction(async (_, hre) => {
78+
await hre.run('deploy:impl', { contract: 'BasicWhitelisting' });
79+
});
80+
6481
/**
6582
@title Hardhat subtask to deploy an SSV module contract.
6683
The module parameter specifies the name of the SSV module to be deployed.

test/operators/external-whitelist.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import hre from 'hardhat';
2+
3+
import { assertEvent } from '../helpers/utils/test';
4+
const { expect } = require('chai');
5+
6+
describe('BasicWhitelisting', () => {
7+
let basicWhitelisting: any, owners: any;
8+
9+
beforeEach(async () => {
10+
owners = await hre.viem.getWalletClients();
11+
12+
basicWhitelisting = await hre.viem.deployContract('BasicWhitelisting');
13+
});
14+
15+
describe('Deployment', async () => {
16+
it('Should set the right owner', async () => {
17+
expect(await basicWhitelisting.read.owner()).to.deep.equal(owners[0].account.address);
18+
});
19+
});
20+
21+
describe('Whitelisting', async () => {
22+
it('Should whitelist an address', async () => {
23+
const addr1 = owners[2].account.address;
24+
25+
await basicWhitelisting.write.addWhitelistedAddress([addr1]);
26+
expect(await basicWhitelisting.read.isWhitelisted([addr1, 0])).to.be.true;
27+
});
28+
29+
it('Should remove an address from whitelist', async () => {
30+
const addr1 = owners[2].account.address;
31+
32+
await basicWhitelisting.write.addWhitelistedAddress([addr1]);
33+
await basicWhitelisting.write.removeWhitelistedAddress([addr1]);
34+
expect(await basicWhitelisting.read.isWhitelisted([addr1, 0])).to.be.false;
35+
});
36+
37+
it('Should emit AddressWhitelisted event', async () => {
38+
const addr1 = owners[2].account.address;
39+
40+
await assertEvent(basicWhitelisting.write.addWhitelistedAddress([addr1]), [
41+
{
42+
contract: basicWhitelisting,
43+
eventName: 'AddressWhitelisted',
44+
argNames: ['account'],
45+
argValuesList: [[addr1]],
46+
},
47+
]);
48+
});
49+
50+
it('Should emit AddressRemovedFromWhitelist event', async () => {
51+
const addr1 = owners[2].account.address;
52+
53+
await basicWhitelisting.write.addWhitelistedAddress([addr1]);
54+
55+
await assertEvent(basicWhitelisting.write.removeWhitelistedAddress([addr1]), [
56+
{
57+
contract: basicWhitelisting,
58+
eventName: 'AddressRemovedFromWhitelist',
59+
argNames: ['account'],
60+
argValuesList: [[addr1]],
61+
},
62+
]);
63+
});
64+
65+
it('Should only allow the owner to whitelist addresses', async () => {
66+
const addr1 = owners[2].account.address;
67+
68+
await expect(
69+
basicWhitelisting.write.addWhitelistedAddress([addr1], {
70+
account: owners[1].account,
71+
}),
72+
).to.be.rejectedWith('Ownable: caller is not the owner');
73+
});
74+
75+
it('Should only allow the owner to remove addresses from whitelist', async () => {
76+
const addr1 = owners[2].account.address;
77+
78+
await basicWhitelisting.write.addWhitelistedAddress([addr1]);
79+
await expect(
80+
basicWhitelisting.write.removeWhitelistedAddress([addr1], {
81+
account: owners[1].account,
82+
}),
83+
).to.be.rejectedWith('Ownable: caller is not the owner');
84+
});
85+
});
86+
});

0 commit comments

Comments
 (0)