-
Notifications
You must be signed in to change notification settings - Fork 11.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add back WhitelistedCrowdsale (#1525)
* Added WhitelisterRole. * Added WhitelisteeRole and WhitelistedCrowdsale. * Added WhitelistedCrowdsale tests. * Whitelisters can now remove Whitelistees. * PublicRole.behavior now supports a manager account, added Whitelistee tests. * Rephrased tests, added test for whitelistees doing invalid purchases. * Fixed linter error. * Fixed typos Co-Authored-By: nventuro <[email protected]> * Working around JS quirks Co-Authored-By: nventuro <[email protected]> * Update PublicRole.behavior.js * Renamed WhitelisteeRole to WhitelistedRole. * Renamed WhitelistedCrowdsale to WhitelistCrowdsale. * Now using the new test helper. * Added basic documentation.
- Loading branch information
Showing
10 changed files
with
276 additions
and
32 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,50 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
import "../Roles.sol"; | ||
import "./WhitelisterRole.sol"; | ||
|
||
/** | ||
* @title WhitelistedRole | ||
* @dev Whitelisted accounts have been approved by a Whitelister to perform certain actions (e.g. participate in a | ||
* crowdsale). This role is special in that the only accounts that can add it are Whitelisters (who can also remove it), | ||
* and not Whitelisteds themselves. | ||
*/ | ||
contract WhitelistedRole is WhitelisterRole { | ||
using Roles for Roles.Role; | ||
|
||
event WhitelistedAdded(address indexed account); | ||
event WhitelistedRemoved(address indexed account); | ||
|
||
Roles.Role private _whitelisteds; | ||
|
||
modifier onlyWhitelisted() { | ||
require(isWhitelisted(msg.sender)); | ||
_; | ||
} | ||
|
||
function isWhitelisted(address account) public view returns (bool) { | ||
return _whitelisteds.has(account); | ||
} | ||
|
||
function addWhitelisted(address account) public onlyWhitelister { | ||
_addWhitelisted(account); | ||
} | ||
|
||
function removeWhitelisted(address account) public onlyWhitelister { | ||
_removeWhitelisted(account); | ||
} | ||
|
||
function renounceWhitelisted() public { | ||
_removeWhitelisted(msg.sender); | ||
} | ||
|
||
function _addWhitelisted(address account) internal { | ||
_whitelisteds.add(account); | ||
emit WhitelistedAdded(account); | ||
} | ||
|
||
function _removeWhitelisted(address account) internal { | ||
_whitelisteds.remove(account); | ||
emit WhitelistedRemoved(account); | ||
} | ||
} |
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,47 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
import "../Roles.sol"; | ||
|
||
/** | ||
* @title WhitelisterRole | ||
* @dev Whitelisters are responsible for assigning and removing Whitelisted accounts. | ||
*/ | ||
contract WhitelisterRole { | ||
using Roles for Roles.Role; | ||
|
||
event WhitelisterAdded(address indexed account); | ||
event WhitelisterRemoved(address indexed account); | ||
|
||
Roles.Role private _whitelisters; | ||
|
||
constructor () internal { | ||
_addWhitelister(msg.sender); | ||
} | ||
|
||
modifier onlyWhitelister() { | ||
require(isWhitelister(msg.sender)); | ||
_; | ||
} | ||
|
||
function isWhitelister(address account) public view returns (bool) { | ||
return _whitelisters.has(account); | ||
} | ||
|
||
function addWhitelister(address account) public onlyWhitelister { | ||
_addWhitelister(account); | ||
} | ||
|
||
function renounceWhitelister() public { | ||
_removeWhitelister(msg.sender); | ||
} | ||
|
||
function _addWhitelister(address account) internal { | ||
_whitelisters.add(account); | ||
emit WhitelisterAdded(account); | ||
} | ||
|
||
function _removeWhitelister(address account) internal { | ||
_whitelisters.remove(account); | ||
emit WhitelisterRemoved(account); | ||
} | ||
} |
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,21 @@ | ||
pragma solidity ^0.4.24; | ||
import "../Crowdsale.sol"; | ||
import "../../access/roles/WhitelistedRole.sol"; | ||
|
||
|
||
/** | ||
* @title WhitelistCrowdsale | ||
* @dev Crowdsale in which only whitelisted users can contribute. | ||
*/ | ||
contract WhitelistCrowdsale is WhitelistedRole, Crowdsale { | ||
/** | ||
* @dev Extend parent behavior requiring beneficiary to be whitelisted. Note that no | ||
* restriction is imposed on the account sending the transaction. | ||
* @param _beneficiary Token beneficiary | ||
* @param _weiAmount Amount of wei contributed | ||
*/ | ||
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal view { | ||
require(isWhitelisted(_beneficiary)); | ||
super._preValidatePurchase(_beneficiary, _weiAmount); | ||
} | ||
} |
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,10 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
import "../token/ERC20/IERC20.sol"; | ||
import "../crowdsale/validation/WhitelistCrowdsale.sol"; | ||
import "../crowdsale/Crowdsale.sol"; | ||
|
||
|
||
contract WhitelistCrowdsaleImpl is Crowdsale, WhitelistCrowdsale { | ||
constructor (uint256 _rate, address _wallet, IERC20 _token) Crowdsale(_rate, _wallet, _token) public {} | ||
} |
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,8 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
import "../access/roles/WhitelistedRole.sol"; | ||
|
||
contract WhitelistedRoleMock is WhitelistedRole { | ||
function onlyWhitelistedMock() public view onlyWhitelisted { | ||
} | ||
} |
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,17 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
import "../access/roles/WhitelisterRole.sol"; | ||
|
||
contract WhitelisterRoleMock is WhitelisterRole { | ||
function removeWhitelister(address account) public { | ||
_removeWhitelister(account); | ||
} | ||
|
||
function onlyWhitelisterMock() public view onlyWhitelister { | ||
} | ||
|
||
// Causes a compilation error if super._removeWhitelister is not internal | ||
function _removeWhitelister(address account) internal { | ||
super._removeWhitelister(account); | ||
} | ||
} |
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
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,12 @@ | ||
const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); | ||
const WhitelistedRoleMock = artifacts.require('WhitelistedRoleMock'); | ||
|
||
contract('WhitelistedRole', function ([_, whitelisted, otherWhitelisted, whitelister, ...otherAccounts]) { | ||
beforeEach(async function () { | ||
this.contract = await WhitelistedRoleMock.new({ from: whitelister }); | ||
await this.contract.addWhitelisted(whitelisted, { from: whitelister }); | ||
await this.contract.addWhitelisted(otherWhitelisted, { from: whitelister }); | ||
}); | ||
|
||
shouldBehaveLikePublicRole(whitelisted, otherWhitelisted, otherAccounts, 'whitelisted', whitelister); | ||
}); |
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,11 @@ | ||
const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); | ||
const WhitelisterRoleMock = artifacts.require('WhitelisterRoleMock'); | ||
|
||
contract('WhitelisterRole', function ([_, whitelister, otherWhitelister, ...otherAccounts]) { | ||
beforeEach(async function () { | ||
this.contract = await WhitelisterRoleMock.new({ from: whitelister }); | ||
await this.contract.addWhitelister(otherWhitelister, { from: whitelister }); | ||
}); | ||
|
||
shouldBehaveLikePublicRole(whitelister, otherWhitelister, otherAccounts, 'whitelister'); | ||
}); |
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,57 @@ | ||
require('../helpers/setup'); | ||
const { ether } = require('../helpers/ether'); | ||
const shouldFail = require('../helpers/shouldFail'); | ||
|
||
const BigNumber = web3.BigNumber; | ||
|
||
const WhitelistCrowdsale = artifacts.require('WhitelistCrowdsaleImpl'); | ||
const SimpleToken = artifacts.require('SimpleToken'); | ||
|
||
contract('WhitelistCrowdsale', function ([_, wallet, whitelister, whitelisted, otherWhitelisted, anyone]) { | ||
const rate = 1; | ||
const value = ether(42); | ||
const tokenSupply = new BigNumber('1e22'); | ||
|
||
beforeEach(async function () { | ||
this.token = await SimpleToken.new({ from: whitelister }); | ||
this.crowdsale = await WhitelistCrowdsale.new(rate, wallet, this.token.address, { from: whitelister }); | ||
await this.token.transfer(this.crowdsale.address, tokenSupply, { from: whitelister }); | ||
}); | ||
|
||
async function purchaseShouldSucceed (crowdsale, beneficiary, value) { | ||
await crowdsale.buyTokens(beneficiary, { from: beneficiary, value }); | ||
await crowdsale.sendTransaction({ from: beneficiary, value }); | ||
} | ||
|
||
async function purchaseShouldFail (crowdsale, beneficiary, value) { | ||
await shouldFail.reverting(crowdsale.buyTokens(beneficiary, { from: beneficiary, value })); | ||
await shouldFail.reverting(crowdsale.sendTransaction({ from: beneficiary, value })); | ||
} | ||
|
||
context('with no whitelisted addresses', function () { | ||
it('rejects all purchases', async function () { | ||
await purchaseShouldFail(this.crowdsale, anyone, value); | ||
await purchaseShouldFail(this.crowdsale, whitelisted, value); | ||
}); | ||
}); | ||
|
||
context('with whitelisted addresses', function () { | ||
beforeEach(async function () { | ||
await this.crowdsale.addWhitelisted(whitelisted, { from: whitelister }); | ||
await this.crowdsale.addWhitelisted(otherWhitelisted, { from: whitelister }); | ||
}); | ||
|
||
it('accepts purchases with whitelisted beneficiaries', async function () { | ||
await purchaseShouldSucceed(this.crowdsale, whitelisted, value); | ||
await purchaseShouldSucceed(this.crowdsale, otherWhitelisted, value); | ||
}); | ||
|
||
it('rejects purchases from whitelisted addresses with non-whitelisted beneficiaries', async function () { | ||
await shouldFail(this.crowdsale.buyTokens(anyone, { from: whitelisted, value })); | ||
}); | ||
|
||
it('rejects purchases with non-whitelisted beneficiaries', async function () { | ||
await purchaseShouldFail(this.crowdsale, anyone, value); | ||
}); | ||
}); | ||
}); |