Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Implement ControlledToken, where contract owner can burn/mint tokens …
…at will.
  • Loading branch information
mjdietzx committed Mar 6, 2018
commit 3b39c8c6cf2a758ef34bb32ca460b4da76d8f428
12 changes: 12 additions & 0 deletions contracts/mocks/ControlledTokenMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.4.18;

import "../token/ERC20/ControlledToken.sol";


contract ControlledTokenMock is ControlledToken {

function ControlledTokenMock(address initialAccount, uint256 initialBalance) public {
balances[initialAccount] = initialBalance;
totalSupply_ = initialBalance;
}
}
51 changes: 51 additions & 0 deletions contracts/token/ERC20/ControlledToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
pragma solidity ^0.4.18;

import "./BasicToken.sol";
import "../../math/SafeMath.sol";
import "../../ownership/Ownable.sol";


/**
* @title Controlled Token
* @dev Token that can be irreversibly burned (destroyed) and minted (created) at will by the contract owner.
*/
contract ControlledToken is BasicToken, Ownable {
using SafeMath for uint256;

event Burn(address indexed from, uint256 amount);
event Mint(address indexed to, uint256 amount);

/**
* @dev Function to burn tokens
* @param _from The address that tokens will be burned from.
* @param _amount The amount of tokens to burn.
* @return A boolean that indicates if the operation was successful.
*/
function burn(address _from, uint256 _amount) onlyOwner public returns (bool) {
require(_amount <= super.balanceOf(_from));

totalSupply_ = totalSupply_.sub(_amount);
balances[_from] = balances[_from].sub(_amount);

Burn(_from, _amount);
Transfer(_from, address(0), _amount);

return true;
}

/**
* @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 public returns (bool) {
totalSupply_ = totalSupply_.add(_amount);
balances[_to] = balances[_to].add(_amount);

Mint(_to, _amount);
Transfer(address(0), _to, _amount);

return true;
}
}
129 changes: 129 additions & 0 deletions test/token/ControlledToken.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import assertRevert from '../helpers/assertRevert';
const ControlledTokenMock = artifacts.require('ControlledTokenMock');

contract('ControlledToken', function ([owner, anotherAccount]) {
const from = owner;
const totalSupply = 1000;
const amount = 100;

beforeEach(async function () {
this.token = await ControlledTokenMock.new(owner, totalSupply);
});

describe('burn', function () {
describe('when the given amount is not greater than balance of the burn address', function () {
it('burns the requested amount from the owner', async function () {
await this.token.burn(owner, amount, { from });

const balance = await this.token.balanceOf(from);
assert.equal(balance, totalSupply - amount);

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply - amount);
});

it('burns the requested amount from another account', async function () {
await this.token.transfer(anotherAccount, amount, { from: owner });
await this.token.burn(anotherAccount, 10, { from });
const balance = await this.token.balanceOf(anotherAccount);
assert.equal(balance, amount - 10);

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply - 10);
});

it('emits a burn event', async function () {
const { logs } = await this.token.burn(owner, amount, { from });
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
assert.equal(logs.length, 2);
assert.equal(logs[0].event, 'Burn');
assert.equal(logs[0].args.from, owner);
assert.equal(logs[0].args.amount, amount);

assert.equal(logs[1].event, 'Transfer');
assert.equal(logs[1].args.from, owner);
assert.equal(logs[1].args.to, ZERO_ADDRESS);
assert.equal(logs[1].args.value, amount);
});
});

describe('when the given amount is greater than the balance of the burn address', function () {
it('reverts', async function () {
await assertRevert(this.token.burn(owner, totalSupply + 1, { from }));

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply);
});

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

describe('when the caller is not the owner', function () {
it('reverts', async function () {
await assertRevert(this.token.burn(owner, amount, { from: anotherAccount }));
});

it('reverts', async function () {
await this.token.transfer(anotherAccount, amount, { from: owner });
await assertRevert(this.token.burn(anotherAccount, 10, { from: anotherAccount }));

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply);
});
});
});

describe('mint', function () {
describe('when the contract owner mints tokens', function () {
it('mints the requested amount to the owner', async function () {
await this.token.mint(owner, amount, { from });

const balance = await this.token.balanceOf(from);
assert.equal(balance, totalSupply + amount);

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply + amount);
});

it('mints the requested amount to another account', async function () {
await this.token.mint(anotherAccount, amount, { from });
const balance = await this.token.balanceOf(anotherAccount);
assert.equal(balance, amount);

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply + amount);
});

it('emits a mint event', async function () {
const { logs } = await this.token.mint(owner, amount, { from });
assert.equal(logs.length, 2);
assert.equal(logs[0].event, 'Mint');
assert.equal(logs[0].args.to, owner);
assert.equal(logs[0].args.amount, amount);

assert.equal(logs[1].event, 'Transfer');
assert.equal(logs[1].args.from, 0);
assert.equal(logs[1].args.to, owner);
assert.equal(logs[1].args.value, amount);
});
});

describe('when another account tries to mint tokens', function () {
it('reverts', async function () {
await assertRevert(this.token.mint(owner, amount, { from: anotherAccount }));

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply);
});

it('reverts', async function () {
await assertRevert(this.token.mint(anotherAccount, amount, { from: anotherAccount }));

const _totalSupply = await this.token.totalSupply();
assert.equal(_totalSupply, totalSupply);
});
});
});
});