Skip to content

Commit

Permalink
Add BasicWhitelisting contract (#302)
Browse files Browse the repository at this point in the history
* add BasicWhitelisting contract

* add deployment task
  • Loading branch information
mtabasco authored Aug 7, 2024
1 parent 36e89f8 commit 583b7b7
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
31 changes: 31 additions & 0 deletions contracts/whitelisting/BasicWhitelisting.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {ISSVWhitelistingContract} from "../interfaces/external/ISSVWhitelistingContract.sol";

contract BasicWhitelisting is ISSVWhitelistingContract, ERC165, Ownable {
mapping(address => bool) private whitelisted;

event AddressWhitelisted(address indexed account);
event AddressRemovedFromWhitelist(address indexed account);

function addWhitelistedAddress(address account) external onlyOwner {
whitelisted[account] = true;
emit AddressWhitelisted(account);
}

function removeWhitelistedAddress(address account) external onlyOwner {
whitelisted[account] = false;
emit AddressRemovedFromWhitelist(account);
}

function isWhitelisted(address account, uint256) external view override returns (bool) {
return whitelisted[account];
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(ISSVWhitelistingContract).interfaceId || super.supportsInterface(interfaceId);
}
}
17 changes: 17 additions & 0 deletions tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ task('deploy:main-impl', 'Deploys SSVNetwork / SSVNetworkViews implementation co
await hre.run('deploy:impl', { contract });
});

/**
@title Hardhat task to deploy a basic whitelisting contract implementation.
The deployment process involves running a subtask that handles the actual deployment.
@returns {void} This function doesn't return anything. If the deployment process encounters an error,
it will be printed to the console, and the process will exit with a non-zero status code.
@example
// Deploy BasicWhitelisting contract with the default deployer account
npx hardhat --network holesky_testnet deploy:whitelisting-contract
@remarks
The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
This task uses the "deploy:impl" subtask for the actual deployment, specifying 'BasicWhitelisting' as the contract name.
*/
task('deploy:whitelisting-contract', 'Deploys a basic whitelisting contract').setAction(async (_, hre) => {
await hre.run('deploy:impl', { contract: 'BasicWhitelisting' });
});

/**
@title Hardhat subtask to deploy an SSV module contract.
The module parameter specifies the name of the SSV module to be deployed.
Expand Down
86 changes: 86 additions & 0 deletions test/operators/external-whitelist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import hre from 'hardhat';

import { assertEvent } from '../helpers/utils/test';
const { expect } = require('chai');

describe('BasicWhitelisting', () => {
let basicWhitelisting: any, owners: any;

beforeEach(async () => {
owners = await hre.viem.getWalletClients();

basicWhitelisting = await hre.viem.deployContract('BasicWhitelisting');
});

describe('Deployment', async () => {
it('Should set the right owner', async () => {
expect(await basicWhitelisting.read.owner()).to.deep.equal(owners[0].account.address);
});
});

describe('Whitelisting', async () => {
it('Should whitelist an address', async () => {
const addr1 = owners[2].account.address;

await basicWhitelisting.write.addWhitelistedAddress([addr1]);
expect(await basicWhitelisting.read.isWhitelisted([addr1, 0])).to.be.true;
});

it('Should remove an address from whitelist', async () => {
const addr1 = owners[2].account.address;

await basicWhitelisting.write.addWhitelistedAddress([addr1]);
await basicWhitelisting.write.removeWhitelistedAddress([addr1]);
expect(await basicWhitelisting.read.isWhitelisted([addr1, 0])).to.be.false;
});

it('Should emit AddressWhitelisted event', async () => {
const addr1 = owners[2].account.address;

await assertEvent(basicWhitelisting.write.addWhitelistedAddress([addr1]), [
{
contract: basicWhitelisting,
eventName: 'AddressWhitelisted',
argNames: ['account'],
argValuesList: [[addr1]],
},
]);
});

it('Should emit AddressRemovedFromWhitelist event', async () => {
const addr1 = owners[2].account.address;

await basicWhitelisting.write.addWhitelistedAddress([addr1]);

await assertEvent(basicWhitelisting.write.removeWhitelistedAddress([addr1]), [
{
contract: basicWhitelisting,
eventName: 'AddressRemovedFromWhitelist',
argNames: ['account'],
argValuesList: [[addr1]],
},
]);
});

it('Should only allow the owner to whitelist addresses', async () => {
const addr1 = owners[2].account.address;

await expect(
basicWhitelisting.write.addWhitelistedAddress([addr1], {
account: owners[1].account,
}),
).to.be.rejectedWith('Ownable: caller is not the owner');
});

it('Should only allow the owner to remove addresses from whitelist', async () => {
const addr1 = owners[2].account.address;

await basicWhitelisting.write.addWhitelistedAddress([addr1]);
await expect(
basicWhitelisting.write.removeWhitelistedAddress([addr1], {
account: owners[1].account,
}),
).to.be.rejectedWith('Ownable: caller is not the owner');
});
});
});

0 comments on commit 583b7b7

Please sign in to comment.