forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Whitelist contract (OpenZeppelin#746)
* Add Whitelist contract
- Loading branch information
1 parent
5a0c104
commit e090a8f
Showing
3 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import "../ownership/Whitelist.sol"; | ||
|
||
|
||
contract WhitelistMock is Whitelist { | ||
|
||
function onlyWhitelistedCanDoThis() | ||
onlyWhitelisted | ||
view | ||
external | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
|
||
import "./Ownable.sol"; | ||
|
||
|
||
/** | ||
* @title Whitelist | ||
* @dev The Whitelist contract has a whitelist of addresses, and provides basic authorization control functions. | ||
* @dev This simplifies the implementation of "user permissions". | ||
*/ | ||
contract Whitelist is Ownable { | ||
mapping(address => bool) public whitelist; | ||
|
||
event WhitelistedAddressAdded(address addr); | ||
event WhitelistedAddressRemoved(address addr); | ||
|
||
/** | ||
* @dev Throws if called by any account that's not whitelisted. | ||
*/ | ||
modifier onlyWhitelisted() { | ||
require(whitelist[msg.sender]); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev add an address to the whitelist | ||
* @param addr address | ||
* @return true if the address was added to the whitelist, false if the address was already in the whitelist | ||
*/ | ||
function addAddressToWhitelist(address addr) onlyOwner public returns(bool success) { | ||
if (!whitelist[addr]) { | ||
whitelist[addr] = true; | ||
WhitelistedAddressAdded(addr); | ||
success = true; | ||
} | ||
} | ||
|
||
/** | ||
* @dev add addresses to the whitelist | ||
* @param addrs addresses | ||
* @return true if at least one address was added to the whitelist, | ||
* false if all addresses were already in the whitelist | ||
*/ | ||
function addAddressesToWhitelist(address[] addrs) onlyOwner public returns(bool success) { | ||
for (uint256 i = 0; i < addrs.length; i++) { | ||
if (addAddressToWhitelist(addrs[i])) { | ||
success = true; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @dev remove an address from the whitelist | ||
* @param addr address | ||
* @return true if the address was removed from the whitelist, | ||
* false if the address wasn't in the whitelist in the first place | ||
*/ | ||
function removeAddressFromWhitelist(address addr) onlyOwner public returns(bool success) { | ||
if (whitelist[addr]) { | ||
whitelist[addr] = false; | ||
WhitelistedAddressRemoved(addr); | ||
success = true; | ||
} | ||
} | ||
|
||
/** | ||
* @dev remove addresses from the whitelist | ||
* @param addrs addresses | ||
* @return true if at least one address was removed from the whitelist, | ||
* false if all addresses weren't in the whitelist in the first place | ||
*/ | ||
function removeAddressesFromWhitelist(address[] addrs) onlyOwner public returns(bool success) { | ||
for (uint256 i = 0; i < addrs.length; i++) { | ||
if (removeAddressFromWhitelist(addrs[i])) { | ||
success = true; | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import expectThrow from '../helpers/expectThrow'; | ||
import expectEvent from '../helpers/expectEvent'; | ||
|
||
const WhitelistMock = artifacts.require('WhitelistMock'); | ||
|
||
require('chai') | ||
.use(require('chai-as-promised')) | ||
.should(); | ||
|
||
contract('Whitelist', function (accounts) { | ||
let mock; | ||
|
||
const [ | ||
owner, | ||
whitelistedAddress1, | ||
whitelistedAddress2, | ||
anyone, | ||
] = accounts; | ||
|
||
const whitelistedAddresses = [whitelistedAddress1, whitelistedAddress2]; | ||
|
||
before(async function () { | ||
mock = await WhitelistMock.new(); | ||
}); | ||
|
||
context('in normal conditions', () => { | ||
it('should add address to the whitelist', async function () { | ||
await expectEvent.inTransaction( | ||
mock.addAddressToWhitelist(whitelistedAddress1, { from: owner }), | ||
'WhitelistedAddressAdded' | ||
); | ||
const isWhitelisted = await mock.whitelist(whitelistedAddress1); | ||
isWhitelisted.should.be.equal(true); | ||
}); | ||
|
||
it('should add addresses to the whitelist', async function () { | ||
await expectEvent.inTransaction( | ||
mock.addAddressesToWhitelist(whitelistedAddresses, { from: owner }), | ||
'WhitelistedAddressAdded' | ||
); | ||
for (let addr of whitelistedAddresses) { | ||
const isWhitelisted = await mock.whitelist(addr); | ||
isWhitelisted.should.be.equal(true); | ||
} | ||
}); | ||
|
||
it('should not announce WhitelistedAddressAdded event if address is already in the whitelist', async function () { | ||
const { logs } = await mock.addAddressToWhitelist(whitelistedAddress1, { from: owner }); | ||
logs.should.be.empty; | ||
}); | ||
|
||
it('should remove address from the whitelist', async function () { | ||
await expectEvent.inTransaction( | ||
mock.removeAddressFromWhitelist(whitelistedAddress1, { from: owner }), | ||
'WhitelistedAddressRemoved' | ||
); | ||
let isWhitelisted = await mock.whitelist(whitelistedAddress1); | ||
isWhitelisted.should.be.equal(false); | ||
}); | ||
|
||
it('should remove addresses from the the whitelist', async function () { | ||
await expectEvent.inTransaction( | ||
mock.removeAddressesFromWhitelist(whitelistedAddresses, { from: owner }), | ||
'WhitelistedAddressRemoved' | ||
); | ||
for (let addr of whitelistedAddresses) { | ||
const isWhitelisted = await mock.whitelist(addr); | ||
isWhitelisted.should.be.equal(false); | ||
} | ||
}); | ||
|
||
it('should not announce WhitelistedAddressRemoved event if address is not in the whitelist', async function () { | ||
const { logs } = await mock.removeAddressFromWhitelist(whitelistedAddress1, { from: owner }); | ||
logs.should.be.empty; | ||
}); | ||
|
||
it('should allow whitelisted address to call #onlyWhitelistedCanDoThis', async () => { | ||
await mock.addAddressToWhitelist(whitelistedAddress1, { from: owner }); | ||
await mock.onlyWhitelistedCanDoThis({ from: whitelistedAddress1 }) | ||
.should.be.fulfilled; | ||
}); | ||
}); | ||
|
||
context('in adversarial conditions', () => { | ||
it('should not allow "anyone" to add to the whitelist', async () => { | ||
await expectThrow( | ||
mock.addAddressToWhitelist(whitelistedAddress1, { from: anyone }) | ||
); | ||
}); | ||
|
||
it('should not allow "anyone" to remove from the whitelist', async () => { | ||
await expectThrow( | ||
mock.removeAddressFromWhitelist(whitelistedAddress1, { from: anyone }) | ||
); | ||
}); | ||
|
||
it('should not allow "anyone" to call #onlyWhitelistedCanDoThis', async () => { | ||
await expectThrow( | ||
mock.onlyWhitelistedCanDoThis({ from: anyone }) | ||
); | ||
}); | ||
}); | ||
}); |