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 <nicolas.venturo@gmail.com>

* Working around JS quirks

Co-Authored-By: nventuro <nicolas.venturo@gmail.com>

* Update PublicRole.behavior.js

* Renamed WhitelisteeRole to WhitelistedRole.

* Renamed WhitelistedCrowdsale to WhitelistCrowdsale.

* Now using the new test helper.

* Added basic documentation.
This commit is contained in:
Nicolás Venturo
2018-12-12 14:46:07 -03:00
committed by GitHub
parent dd2e947976
commit 7142e25e78
10 changed files with 276 additions and 32 deletions

View File

@ -8,7 +8,7 @@ function capitalize (str) {
return str.replace(/\b\w/g, l => l.toUpperCase());
}
function shouldBehaveLikePublicRole (authorized, otherAuthorized, [anyone], rolename) {
function shouldBehaveLikePublicRole (authorized, otherAuthorized, [anyone], rolename, manager) {
rolename = capitalize(rolename);
describe('should behave like public role', function () {
@ -18,11 +18,13 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [anyone], role
(await this.contract[`is${rolename}`](anyone)).should.equal(false);
});
it('emits events during construction', async function () {
await expectEvent.inConstruction(this.contract, `${rolename}Added`, {
account: authorized,
if (manager === undefined) { // Managed roles are only assigned by the manager, and none are set at construction
it('emits events during construction', async function () {
await expectEvent.inConstruction(this.contract, `${rolename}Added`, {
account: authorized,
});
});
});
}
it('reverts when querying roles for the null account', async function () {
await shouldFail.reverting(this.contract[`is${rolename}`](ZERO_ADDRESS));
@ -47,43 +49,52 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [anyone], role
});
describe('add', function () {
it('adds role to a new account', async function () {
await this.contract[`add${rolename}`](anyone, { from: authorized });
(await this.contract[`is${rolename}`](anyone)).should.equal(true);
});
const from = manager === undefined ? authorized : manager;
it(`emits a ${rolename}Added event`, async function () {
const { logs } = await this.contract[`add${rolename}`](anyone, { from: authorized });
expectEvent.inLogs(logs, `${rolename}Added`, { account: anyone });
});
context(`from ${manager ? 'the manager' : 'a role-haver'} account`, function () {
it('adds role to a new account', async function () {
await this.contract[`add${rolename}`](anyone, { from });
(await this.contract[`is${rolename}`](anyone)).should.equal(true);
});
it('reverts when adding role to an already assigned account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](authorized, { from: authorized }));
});
it(`emits a ${rolename}Added event`, async function () {
const { logs } = await this.contract[`add${rolename}`](anyone, { from });
expectEvent.inLogs(logs, `${rolename}Added`, { account: anyone });
});
it('reverts when adding role to the null account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](ZERO_ADDRESS, { from: authorized }));
it('reverts when adding role to an already assigned account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](authorized, { from }));
});
it('reverts when adding role to the null account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](ZERO_ADDRESS, { from }));
});
});
});
describe('remove', function () {
it('removes role from an already assigned account', async function () {
await this.contract[`remove${rolename}`](authorized);
(await this.contract[`is${rolename}`](authorized)).should.equal(false);
(await this.contract[`is${rolename}`](otherAuthorized)).should.equal(true);
});
// Non-managed roles have no restrictions on the mocked '_remove' function (exposed via 'remove').
const from = manager || anyone;
it(`emits a ${rolename}Removed event`, async function () {
const { logs } = await this.contract[`remove${rolename}`](authorized);
expectEvent.inLogs(logs, `${rolename}Removed`, { account: authorized });
});
context(`from ${manager ? 'the manager' : 'any'} account`, function () {
it('removes role from an already assigned account', async function () {
await this.contract[`remove${rolename}`](authorized, { from });
(await this.contract[`is${rolename}`](authorized)).should.equal(false);
(await this.contract[`is${rolename}`](otherAuthorized)).should.equal(true);
});
it('reverts when removing from an unassigned account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](anyone));
});
it(`emits a ${rolename}Removed event`, async function () {
const { logs } = await this.contract[`remove${rolename}`](authorized, { from });
expectEvent.inLogs(logs, `${rolename}Removed`, { account: authorized });
});
it('reverts when removing role from the null account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](ZERO_ADDRESS));
it('reverts when removing from an unassigned account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](anyone), { from });
});
it('reverts when removing role from the null account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](ZERO_ADDRESS), { from });
});
});
});

View File

@ -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);
});

View File

@ -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');
});

View File

@ -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);
});
});
});