Skip to content

Commit

Permalink
Merge fc5c0e7 into f0eea31
Browse files Browse the repository at this point in the history
  • Loading branch information
vittominacori committed May 8, 2018
2 parents f0eea31 + fc5c0e7 commit f0de68a
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 77 deletions.
14 changes: 12 additions & 2 deletions contracts/token/ERC20/MintableToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,23 @@ contract MintableToken is StandardToken, Ownable {
_;
}

modifier hasMintPermission() {
require(msg.sender == owner);
_;
}

modifier hasFinishMintingPermission() {
require(msg.sender == owner);
_;
}

/**
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
function mint(address _to, uint256 _amount) hasMintPermission canMint public returns (bool) {
totalSupply_ = totalSupply_.add(_amount);
balances[_to] = balances[_to].add(_amount);
emit Mint(_to, _amount);
Expand All @@ -40,7 +50,7 @@ contract MintableToken is StandardToken, Ownable {
* @dev Function to stop minting new tokens.
* @return True if the operation was successful.
*/
function finishMinting() onlyOwner canMint public returns (bool) {
function finishMinting() hasFinishMintingPermission canMint public returns (bool) {
mintingFinished = true;
emit MintFinished();
return true;
Expand Down
27 changes: 27 additions & 0 deletions contracts/token/ERC20/RBACMintableToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pragma solidity ^0.4.21;

import "./MintableToken.sol";
import "../../ownership/rbac/RBACWithAdmin.sol";


/**
* @title RBACMintableToken
* @author Vittorio Minacori (@vittominacori)
* @dev Simple ERC20 Mintable Token, with RBAC minter permissions
*/
contract RBACMintableToken is MintableToken, RBACWithAdmin {
/**
* A constant role name for indicating minters.
*/
string public constant ROLE_MINTER = "minter";

modifier hasMintPermission() {
checkRole(msg.sender, ROLE_MINTER);
_;
}

modifier hasFinishMintingPermission() {
checkRole(msg.sender, ROLE_ADMIN);
_;
}
}
63 changes: 63 additions & 0 deletions test/crowdsale/RBACMintedCrowdsale.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import ether from '../helpers/ether';

const BigNumber = web3.BigNumber;

const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();

const MintedCrowdsale = artifacts.require('MintedCrowdsaleImpl');
const RBACMintableToken = artifacts.require('RBACMintableToken');

const ROLE_MINTER = 'minter';

contract('MintedCrowdsale using RBACMintableToken', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1000);
const value = ether(5);

const expectedTokenAmount = rate.mul(value);

beforeEach(async function () {
this.token = await RBACMintableToken.new();
this.crowdsale = await MintedCrowdsale.new(rate, wallet, this.token.address);
await this.token.adminAddRole(this.crowdsale.address, ROLE_MINTER);
});

describe('accepting payments', function () {
it('should have minter role on token', async function () {
const isMinter = await this.token.hasRole(this.crowdsale.address, ROLE_MINTER);
isMinter.should.equal(true);
});

it('should accept payments', async function () {
await this.crowdsale.send(value).should.be.fulfilled;
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser }).should.be.fulfilled;
});
});

describe('high-level purchase', function () {
it('should log purchase', async function () {
const { logs } = await this.crowdsale.sendTransaction({ value: value, from: investor });
const event = logs.find(e => e.event === 'TokenPurchase');
should.exist(event);
event.args.purchaser.should.equal(investor);
event.args.beneficiary.should.equal(investor);
event.args.value.should.be.bignumber.equal(value);
event.args.amount.should.be.bignumber.equal(expectedTokenAmount);
});

it('should assign tokens to sender', async function () {
await this.crowdsale.sendTransaction({ value: value, from: investor });
let balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(expectedTokenAmount);
});

it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet);
await this.crowdsale.sendTransaction({ value, from: investor });
const post = web3.eth.getBalance(wallet);
post.minus(pre).should.be.bignumber.equal(value);
});
});
});
84 changes: 84 additions & 0 deletions test/token/ERC20/MintableToken.behaviour.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import assertRevert from '../../helpers/assertRevert';

const BigNumber = web3.BigNumber;

require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should();

export default function ([owner, anotherAccount]) {
describe('as a basic mintable token', function () {
describe('minting finished', function () {
describe('when the token minting is not finished', function () {
it('returns false', async function () {
const mintingFinished = await this.token.mintingFinished();
assert.equal(mintingFinished, false);
});
});

describe('when the token is minting finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from: owner });
});

it('returns true', async function () {
const mintingFinished = await this.token.mintingFinished();
assert.equal(mintingFinished, true);
});
});
});

describe('finish minting', function () {
describe('when the sender has the finishMinting permission', function () {
const from = owner;

describe('when the token minting was not finished', function () {
it('finishes token minting', async function () {
await this.token.finishMinting({ from });

const mintingFinished = await this.token.mintingFinished();
assert.equal(mintingFinished, true);
});

it('emits a mint finished event', async function () {
const { logs } = await this.token.finishMinting({ from });

assert.equal(logs.length, 1);
assert.equal(logs[0].event, 'MintFinished');
});
});

describe('when the token minting was already finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from });
});

it('reverts', async function () {
await assertRevert(this.token.finishMinting({ from }));
});
});
});

describe('when the sender has not the finishMinting permission', function () {
const from = anotherAccount;

describe('when the token minting was not finished', function () {
it('reverts', async function () {
await assertRevert(this.token.finishMinting({ from }));
});
});

describe('when the token was already finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from: owner });
});

it('reverts', async function () {
await assertRevert(this.token.finishMinting({ from }));
});
});
});
});
});
};
81 changes: 6 additions & 75 deletions test/token/ERC20/MintableToken.test.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,29 @@
import assertRevert from '../../helpers/assertRevert';
import shouldBehaveLikeMintableToken from './MintableToken.behaviour';
const MintableToken = artifacts.require('MintableToken');

contract('Mintable', function ([owner, anotherAccount]) {
beforeEach(async function () {
this.token = await MintableToken.new({ from: owner });
});

describe('minting finished', function () {
describe('when the token is not finished', function () {
it('returns false', async function () {
const mintingFinished = await this.token.mintingFinished();
assert.equal(mintingFinished, false);
});
});

describe('when the token is finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from: owner });
});

it('returns true', async function () {
const mintingFinished = await this.token.mintingFinished.call();
assert.equal(mintingFinished, true);
});
});
});

describe('finish minting', function () {
describe('when the sender is the token owner', function () {
const from = owner;

describe('when the token was not finished', function () {
it('finishes token minting', async function () {
await this.token.finishMinting({ from });

const mintingFinished = await this.token.mintingFinished();
assert.equal(mintingFinished, true);
});

it('emits a mint finished event', async function () {
const { logs } = await this.token.finishMinting({ from });

assert.equal(logs.length, 1);
assert.equal(logs[0].event, 'MintFinished');
});
});

describe('when the token was already finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from });
});

it('reverts', async function () {
await assertRevert(this.token.finishMinting({ from }));
});
});
});

describe('when the sender is not the token owner', function () {
const from = anotherAccount;

describe('when the token was not finished', function () {
it('reverts', async function () {
await assertRevert(this.token.finishMinting({ from }));
});
});

describe('when the token was already finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from: owner });
});

it('reverts', async function () {
await assertRevert(this.token.finishMinting({ from }));
});
});
});
});
shouldBehaveLikeMintableToken([owner, anotherAccount]);

describe('mint', function () {
const amount = 100;

describe('when the sender is the token owner', function () {
const from = owner;

describe('when the token was not finished', function () {
describe('when the token minting is not finished', function () {
it('mints the requested amount', async function () {
await this.token.mint(owner, amount, { from });

const balance = await this.token.balanceOf(owner);
assert.equal(balance, amount);
});

it('emits a mint finished event', async function () {
it('emits a mint and a transfer event', async function () {
const { logs } = await this.token.mint(owner, amount, { from });

assert.equal(logs.length, 2);
Expand All @@ -117,13 +48,13 @@ contract('Mintable', function ([owner, anotherAccount]) {
describe('when the sender is not the token owner', function () {
const from = anotherAccount;

describe('when the token was not finished', function () {
describe('when the token minting is not finished', function () {
it('reverts', async function () {
await assertRevert(this.token.mint(owner, amount, { from }));
});
});

describe('when the token was already finished', function () {
describe('when the token minting is already finished', function () {
beforeEach(async function () {
await this.token.finishMinting({ from: owner });
});
Expand Down

0 comments on commit f0de68a

Please sign in to comment.