Commit ef347ffc by Doug Crescenzi Committed by Francisco Giordano

Consolidated ERC20 Interface and Implementation Files (#1125)

* Consolidted ERC20 Interface and Implementation Files

* Fixed CanReclaimToken's tests to use StandardTokenMock instead of BasicTokenMock

* Changed token's variable type in TokenTimelock to ERC20

* Merged the StandardBurnableToken with BurnableToken since it now inherits from StandardToken; Fixed TokenTimelock so it uses SafeERC20 for ERC20

* Fixed variable type for _token in TokenTimelock constructor

* Fixed linting warning in BurnableToken

* Added back burnFrom tests.
parent 3d86c58d
...@@ -2,7 +2,6 @@ pragma solidity ^0.4.24; ...@@ -2,7 +2,6 @@ pragma solidity ^0.4.24;
import "../Crowdsale.sol"; import "../Crowdsale.sol";
import "../../token/ERC20/ERC20.sol"; import "../../token/ERC20/ERC20.sol";
import "../../token/ERC20/ERC20Basic.sol";
import "../../token/ERC20/SafeERC20.sol"; import "../../token/ERC20/SafeERC20.sol";
import "../../math/SafeMath.sol"; import "../../math/SafeMath.sol";
......
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "../ownership/Ownable.sol"; import "../ownership/Ownable.sol";
import "../token/ERC20/ERC20Basic.sol"; import "../token/ERC20/ERC20.sol";
/** /**
...@@ -16,7 +16,7 @@ contract TokenDestructible is Ownable { ...@@ -16,7 +16,7 @@ contract TokenDestructible is Ownable {
/** /**
* @notice Terminate contract and refund to owner * @notice Terminate contract and refund to owner
* @param _tokens List of addresses of ERC20 or ERC20Basic token contracts to * @param _tokens List of addresses of ERC20 token contracts to
refund. refund.
* @notice The called token contracts could try to re-enter this contract. Only * @notice The called token contracts could try to re-enter this contract. Only
supply token contracts you trust. supply token contracts you trust.
...@@ -25,7 +25,7 @@ contract TokenDestructible is Ownable { ...@@ -25,7 +25,7 @@ contract TokenDestructible is Ownable {
// Transfer tokens to owner // Transfer tokens to owner
for (uint256 i = 0; i < _tokens.length; i++) { for (uint256 i = 0; i < _tokens.length; i++) {
ERC20Basic token = ERC20Basic(_tokens[i]); ERC20 token = ERC20(_tokens[i]);
uint256 balance = token.balanceOf(this); uint256 balance = token.balanceOf(this);
token.transfer(owner, balance); token.transfer(owner, balance);
} }
......
pragma solidity ^0.4.24;
import "../token/ERC20/BasicToken.sol";
// mock class using BasicToken
contract BasicTokenMock is BasicToken {
constructor(address _initialAccount, uint256 _initialBalance) public {
balances[_initialAccount] = _initialBalance;
totalSupply_ = _initialBalance;
}
}
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "../token/ERC20/BasicToken.sol"; import "../token/ERC20/StandardToken.sol";
contract ERC223ContractInterface { contract ERC223ContractInterface {
...@@ -8,7 +8,7 @@ contract ERC223ContractInterface { ...@@ -8,7 +8,7 @@ contract ERC223ContractInterface {
} }
contract ERC223TokenMock is BasicToken { contract ERC223TokenMock is StandardToken {
constructor(address _initialAccount, uint256 _initialBalance) public { constructor(address _initialAccount, uint256 _initialBalance) public {
balances[_initialAccount] = _initialBalance; balances[_initialAccount] = _initialBalance;
......
pragma solidity ^0.4.24;
import "../token/ERC20/StandardBurnableToken.sol";
contract StandardBurnableTokenMock is StandardBurnableToken {
constructor(address _initialAccount, uint _initialBalance) public {
balances[_initialAccount] = _initialBalance;
totalSupply_ = _initialBalance;
}
}
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "./Ownable.sol"; import "./Ownable.sol";
import "../token/ERC20/ERC20Basic.sol"; import "../token/ERC20/ERC20.sol";
import "../token/ERC20/SafeERC20.sol"; import "../token/ERC20/SafeERC20.sol";
...@@ -12,13 +12,13 @@ import "../token/ERC20/SafeERC20.sol"; ...@@ -12,13 +12,13 @@ import "../token/ERC20/SafeERC20.sol";
* This will prevent any accidental loss of tokens. * This will prevent any accidental loss of tokens.
*/ */
contract CanReclaimToken is Ownable { contract CanReclaimToken is Ownable {
using SafeERC20 for ERC20Basic; using SafeERC20 for ERC20;
/** /**
* @dev Reclaim all ERC20Basic compatible tokens * @dev Reclaim all ERC20 compatible tokens
* @param _token ERC20Basic The address of the token contract * @param _token ERC20 The address of the token contract
*/ */
function reclaimToken(ERC20Basic _token) external onlyOwner { function reclaimToken(ERC20 _token) external onlyOwner {
uint256 balance = _token.balanceOf(this); uint256 balance = _token.balanceOf(this);
_token.safeTransfer(owner, balance); _token.safeTransfer(owner, balance);
} }
......
...@@ -7,7 +7,7 @@ import "./CanReclaimToken.sol"; ...@@ -7,7 +7,7 @@ import "./CanReclaimToken.sol";
* @title Contracts that should not own Tokens * @title Contracts that should not own Tokens
* @author Remco Bloemen <remco@2π.com> * @author Remco Bloemen <remco@2π.com>
* @dev This blocks incoming ERC223 tokens to prevent accidental loss of tokens. * @dev This blocks incoming ERC223 tokens to prevent accidental loss of tokens.
* Should tokens (any ERC20Basic compatible) end up in the contract, it allows the * Should tokens (any ERC20 compatible) end up in the contract, it allows the
* owner to reclaim the tokens. * owner to reclaim the tokens.
*/ */
contract HasNoTokens is CanReclaimToken { contract HasNoTokens is CanReclaimToken {
......
pragma solidity ^0.4.24;
import "./ERC20Basic.sol";
import "../../math/SafeMath.sol";
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) internal balances;
uint256 internal totalSupply_;
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
/**
* @dev Transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_value <= balances[msg.sender]);
require(_to != address(0));
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}
}
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "./BasicToken.sol"; import "./StandardToken.sol";
/** /**
* @title Burnable Token * @title Burnable Token
* @dev Token that can be irreversibly burned (destroyed). * @dev Token that can be irreversibly burned (destroyed).
*/ */
contract BurnableToken is BasicToken { contract BurnableToken is StandardToken {
event Burn(address indexed burner, uint256 value); event Burn(address indexed burner, uint256 value);
...@@ -19,6 +19,19 @@ contract BurnableToken is BasicToken { ...@@ -19,6 +19,19 @@ contract BurnableToken is BasicToken {
_burn(msg.sender, _value); _burn(msg.sender, _value);
} }
/**
* @dev Burns a specific amount of tokens from the target address and decrements allowance
* @param _from address The address which you want to send tokens from
* @param _value uint256 The amount of token to be burned
*/
function burnFrom(address _from, uint256 _value) public {
require(_value <= allowed[_from][msg.sender]);
// Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
// this function needs to emit an event with the updated approval.
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
_burn(_from, _value);
}
function _burn(address _who, uint256 _value) internal { function _burn(address _who, uint256 _value) internal {
require(_value <= balances[_who]); require(_value <= balances[_who]);
// no need to require value <= totalSupply, since that would imply the // no need to require value <= totalSupply, since that would imply the
...@@ -29,4 +42,4 @@ contract BurnableToken is BasicToken { ...@@ -29,4 +42,4 @@ contract BurnableToken is BasicToken {
emit Burn(_who, _value); emit Burn(_who, _value);
emit Transfer(_who, address(0), _value); emit Transfer(_who, address(0), _value);
} }
} }
\ No newline at end of file
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "./ERC20Basic.sol";
/** /**
* @title ERC20 interface * @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20 * @dev see https://github.com/ethereum/EIPs/issues/20
*/ */
contract ERC20 is ERC20Basic { contract ERC20 {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function allowance(address _owner, address _spender) function allowance(address _owner, address _spender)
public view returns (uint256); public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
function approve(address _spender, uint256 _value)
public returns (bool);
function transferFrom(address _from, address _to, uint256 _value) function transferFrom(address _from, address _to, uint256 _value)
public returns (bool); public returns (bool);
function approve(address _spender, uint256 _value) public returns (bool); event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval( event Approval(
address indexed owner, address indexed owner,
address indexed spender, address indexed spender,
......
pragma solidity ^0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "./ERC20Basic.sol"; import "./StandardToken.sol";
import "./ERC20.sol"; import "./ERC20.sol";
...@@ -12,7 +12,7 @@ import "./ERC20.sol"; ...@@ -12,7 +12,7 @@ import "./ERC20.sol";
*/ */
library SafeERC20 { library SafeERC20 {
function safeTransfer( function safeTransfer(
ERC20Basic _token, ERC20 _token,
address _to, address _to,
uint256 _value uint256 _value
) )
......
pragma solidity ^0.4.24;
import "./BurnableToken.sol";
import "./StandardToken.sol";
/**
* @title Standard Burnable Token
* @dev Adds burnFrom method to ERC20 implementations
*/
contract StandardBurnableToken is BurnableToken, StandardToken {
/**
* @dev Burns a specific amount of tokens from the target address and decrements allowance
* @param _from address The address which you want to send tokens from
* @param _value uint256 The amount of token to be burned
*/
function burnFrom(address _from, uint256 _value) public {
require(_value <= allowed[_from][msg.sender]);
// Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
// this function needs to emit an event with the updated approval.
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
_burn(_from, _value);
}
}
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "./BasicToken.sol";
import "./ERC20.sol"; import "./ERC20.sol";
import "../../math/SafeMath.sol";
/** /**
...@@ -11,33 +11,60 @@ import "./ERC20.sol"; ...@@ -11,33 +11,60 @@ import "./ERC20.sol";
* https://github.com/ethereum/EIPs/issues/20 * https://github.com/ethereum/EIPs/issues/20
* Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/ */
contract StandardToken is ERC20, BasicToken { contract StandardToken is ERC20 {
using SafeMath for uint256;
mapping(address => uint256) balances;
mapping (address => mapping (address => uint256)) internal allowed; mapping (address => mapping (address => uint256)) internal allowed;
uint256 totalSupply_;
/** /**
* @dev Transfer tokens from one address to another * @dev Total number of tokens in existence
* @param _from address The address which you want to send tokens from */
* @param _to address The address which you want to transfer to function totalSupply() public view returns (uint256) {
* @param _value uint256 the amount of tokens to be transferred return totalSupply_;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/ */
function transferFrom( function allowance(
address _from, address _owner,
address _to, address _spender
uint256 _value )
)
public public
returns (bool) view
returns (uint256)
{ {
require(_value <= balances[_from]); return allowed[_owner][_spender];
require(_value <= allowed[_from][msg.sender]); }
/**
* @dev Transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_value <= balances[msg.sender]);
require(_to != address(0)); require(_to != address(0));
balances[_from] = balances[_from].sub(_value); balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value); balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); emit Transfer(msg.sender, _to, _value);
emit Transfer(_from, _to, _value);
return true; return true;
} }
...@@ -57,20 +84,28 @@ contract StandardToken is ERC20, BasicToken { ...@@ -57,20 +84,28 @@ contract StandardToken is ERC20, BasicToken {
} }
/** /**
* @dev Function to check the amount of tokens that an owner allowed to a spender. * @dev Transfer tokens from one address to another
* @param _owner address The address which owns the funds. * @param _from address The address which you want to send tokens from
* @param _spender address The address which will spend the funds. * @param _to address The address which you want to transfer to
* @return A uint256 specifying the amount of tokens still available for the spender. * @param _value uint256 the amount of tokens to be transferred
*/ */
function allowance( function transferFrom(
address _owner, address _from,
address _spender address _to,
) uint256 _value
)
public public
view returns (bool)
returns (uint256)
{ {
return allowed[_owner][_spender]; require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
require(_to != address(0));
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Transfer(_from, _to, _value);
return true;
} }
/** /**
......
...@@ -9,10 +9,10 @@ import "./SafeERC20.sol"; ...@@ -9,10 +9,10 @@ import "./SafeERC20.sol";
* beneficiary to extract the tokens after a given release time * beneficiary to extract the tokens after a given release time
*/ */
contract TokenTimelock { contract TokenTimelock {
using SafeERC20 for ERC20Basic; using SafeERC20 for ERC20;
// ERC20 basic token contract being held // ERC20 basic token contract being held
ERC20Basic public token; ERC20 public token;
// beneficiary of tokens after they are released // beneficiary of tokens after they are released
address public beneficiary; address public beneficiary;
...@@ -21,7 +21,7 @@ contract TokenTimelock { ...@@ -21,7 +21,7 @@ contract TokenTimelock {
uint256 public releaseTime; uint256 public releaseTime;
constructor( constructor(
ERC20Basic _token, ERC20 _token,
address _beneficiary, address _beneficiary,
uint256 _releaseTime uint256 _releaseTime
) )
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
import "./ERC20Basic.sol";
import "./SafeERC20.sol"; import "./SafeERC20.sol";
import "../../ownership/Ownable.sol"; import "../../ownership/Ownable.sol";
import "../../math/SafeMath.sol"; import "../../math/SafeMath.sol";
...@@ -16,7 +15,7 @@ import "../../math/SafeMath.sol"; ...@@ -16,7 +15,7 @@ import "../../math/SafeMath.sol";
*/ */
contract TokenVesting is Ownable { contract TokenVesting is Ownable {
using SafeMath for uint256; using SafeMath for uint256;
using SafeERC20 for ERC20Basic; using SafeERC20 for ERC20;
event Released(uint256 amount); event Released(uint256 amount);
event Revoked(); event Revoked();
...@@ -66,7 +65,7 @@ contract TokenVesting is Ownable { ...@@ -66,7 +65,7 @@ contract TokenVesting is Ownable {
* @notice Transfers vested tokens to beneficiary. * @notice Transfers vested tokens to beneficiary.
* @param _token ERC20 token which is being vested * @param _token ERC20 token which is being vested
*/ */
function release(ERC20Basic _token) public { function release(ERC20 _token) public {
uint256 unreleased = releasableAmount(_token); uint256 unreleased = releasableAmount(_token);
require(unreleased > 0); require(unreleased > 0);
...@@ -83,7 +82,7 @@ contract TokenVesting is Ownable { ...@@ -83,7 +82,7 @@ contract TokenVesting is Ownable {
* remain in the contract, the rest are returned to the owner. * remain in the contract, the rest are returned to the owner.
* @param _token ERC20 token which is being vested * @param _token ERC20 token which is being vested
*/ */
function revoke(ERC20Basic _token) public onlyOwner { function revoke(ERC20 _token) public onlyOwner {
require(revocable); require(revocable);
require(!revoked[_token]); require(!revoked[_token]);
...@@ -103,7 +102,7 @@ contract TokenVesting is Ownable { ...@@ -103,7 +102,7 @@ contract TokenVesting is Ownable {
* @dev Calculates the amount that has already vested but hasn't been released yet. * @dev Calculates the amount that has already vested but hasn't been released yet.
* @param _token ERC20 token which is being vested * @param _token ERC20 token which is being vested
*/ */
function releasableAmount(ERC20Basic _token) public view returns (uint256) { function releasableAmount(ERC20 _token) public view returns (uint256) {
return vestedAmount(_token).sub(released[_token]); return vestedAmount(_token).sub(released[_token]);
} }
...@@ -111,8 +110,8 @@ contract TokenVesting is Ownable { ...@@ -111,8 +110,8 @@ contract TokenVesting is Ownable {
* @dev Calculates the amount that has already vested. * @dev Calculates the amount that has already vested.
* @param _token ERC20 token which is being vested * @param _token ERC20 token which is being vested
*/ */
function vestedAmount(ERC20Basic _token) public view returns (uint256) { function vestedAmount(ERC20 _token) public view returns (uint256) {
uint256 currentBalance = _token.balanceOf(address(this)); uint256 currentBalance = _token.balanceOf(this);
uint256 totalBalance = currentBalance.add(released[_token]); uint256 totalBalance = currentBalance.add(released[_token]);
if (block.timestamp < cliff) { if (block.timestamp < cliff) {
......
const { expectThrow } = require('../helpers/expectThrow'); const { expectThrow } = require('../helpers/expectThrow');
const CanReclaimToken = artifacts.require('CanReclaimToken'); const CanReclaimToken = artifacts.require('CanReclaimToken');
const BasicTokenMock = artifacts.require('BasicTokenMock'); const StandardTokenMock = artifacts.require('StandardTokenMock');
contract('CanReclaimToken', function ([_, owner, anyone]) { contract('CanReclaimToken', function ([_, owner, anyone]) {
let token = null; let token = null;
...@@ -9,8 +9,9 @@ contract('CanReclaimToken', function ([_, owner, anyone]) { ...@@ -9,8 +9,9 @@ contract('CanReclaimToken', function ([_, owner, anyone]) {
beforeEach(async function () { beforeEach(async function () {
// Create contract and token // Create contract and token
token = await BasicTokenMock.new(owner, 100, { from: owner }); token = await StandardTokenMock.new(owner, 100, { from: owner });
canReclaimToken = await CanReclaimToken.new({ from: owner }); canReclaimToken = await CanReclaimToken.new({ from: owner });
// Force token into contract // Force token into contract
await token.transfer(canReclaimToken.address, 10, { from: owner }); await token.transfer(canReclaimToken.address, 10, { from: owner });
const startBalance = await token.balanceOf(canReclaimToken.address); const startBalance = await token.balanceOf(canReclaimToken.address);
......
const { assertRevert } = require('../../helpers/assertRevert');
const BasicToken = artifacts.require('BasicTokenMock');
contract('StandardToken', function ([_, owner, recipient, anotherAccount]) {
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
beforeEach(async function () {
this.token = await BasicToken.new(owner, 100);
});
describe('total supply', function () {
it('returns the total amount of tokens', async function () {
const totalSupply = await this.token.totalSupply();
assert.equal(totalSupply, 100);
});
});
describe('balanceOf', function () {
describe('when the requested account has no tokens', function () {
it('returns zero', async function () {
const balance = await this.token.balanceOf(anotherAccount);
assert.equal(balance, 0);
});
});
describe('when the requested account has some tokens', function () {
it('returns the total amount of tokens', async function () {
const balance = await this.token.balanceOf(owner);
assert.equal(balance, 100);
});
});
});
describe('transfer', function () {
describe('when the recipient is not the zero address', function () {
const to = recipient;
describe('when the sender does not have enough balance', function () {
const amount = 101;
it('reverts', async function () {
await assertRevert(this.token.transfer(to, amount, { from: owner }));
});
});
describe('when the sender has enough balance', function () {
const amount = 100;
it('transfers the requested amount', async function () {
await this.token.transfer(to, amount, { from: owner });
const senderBalance = await this.token.balanceOf(owner);
assert.equal(senderBalance, 0);
const recipientBalance = await this.token.balanceOf(to);
assert.equal(recipientBalance, amount);
});
it('emits a transfer event', async function () {
const { logs } = await this.token.transfer(to, amount, { from: owner });
assert.equal(logs.length, 1);
assert.equal(logs[0].event, 'Transfer');
assert.equal(logs[0].args.from, owner);
assert.equal(logs[0].args.to, to);
assert(logs[0].args.value.eq(amount));
});
});
});
describe('when the recipient is the zero address', function () {
const to = ZERO_ADDRESS;
it('reverts', async function () {
await assertRevert(this.token.transfer(to, 100, { from: owner }));
});
});
});
});
...@@ -8,8 +8,8 @@ require('chai') ...@@ -8,8 +8,8 @@ require('chai')
.use(require('chai-bignumber')(BigNumber)) .use(require('chai-bignumber')(BigNumber))
.should(); .should();
function shouldBehaveLikeBurnableToken (owner, initialBalance) { function shouldBehaveLikeBurnableToken (owner, initialBalance, [burner]) {
describe('as a basic burnable token', function () { describe('burn', function () {
describe('when the given amount is not greater than balance of the sender', function () { describe('when the given amount is not greater than balance of the sender', function () {
const amount = 100; const amount = 100;
...@@ -44,6 +44,57 @@ function shouldBehaveLikeBurnableToken (owner, initialBalance) { ...@@ -44,6 +44,57 @@ function shouldBehaveLikeBurnableToken (owner, initialBalance) {
}); });
}); });
}); });
describe('burnFrom', function () {
describe('on success', function () {
const amount = 100;
beforeEach(async function () {
await this.token.approve(burner, 300, { from: owner });
const { logs } = await this.token.burnFrom(owner, amount, { from: burner });
this.logs = logs;
});
it('burns the requested amount', async function () {
const balance = await this.token.balanceOf(owner);
balance.should.be.bignumber.equal(initialBalance - amount);
});
it('decrements allowance', async function () {
const allowance = await this.token.allowance(owner, burner);
allowance.should.be.bignumber.equal(200);
});
it('emits a burn event', async function () {
const event = await inLogs(this.logs, 'Burn');
event.args.burner.should.eq(owner);
event.args.value.should.be.bignumber.equal(amount);
});
it('emits a transfer event', async function () {
const event = await inLogs(this.logs, 'Transfer');
event.args.from.should.eq(owner);
event.args.to.should.eq(ZERO_ADDRESS);
event.args.value.should.be.bignumber.equal(amount);
});
});
describe('when the given amount is greater than the balance of the sender', function () {
const amount = initialBalance + 1;
it('reverts', async function () {
await this.token.approve(burner, amount, { from: owner });
await assertRevert(this.token.burnFrom(owner, amount, { from: burner }));
});
});
describe('when the given amount is greater than the allowance', function () {
const amount = 100;
it('reverts', async function () {
await this.token.approve(burner, amount - 1, { from: owner });
await assertRevert(this.token.burnFrom(owner, amount, { from: burner }));
});
});
});
} }
module.exports = { module.exports = {
......
const { shouldBehaveLikeBurnableToken } = require('./BurnableToken.behaviour'); const { shouldBehaveLikeBurnableToken } = require('./BurnableToken.behaviour');
const BurnableTokenMock = artifacts.require('BurnableTokenMock'); const BurnableTokenMock = artifacts.require('BurnableTokenMock');
contract('BurnableToken', function ([_, owner]) { contract('BurnableToken', function ([_, owner, ...otherAccounts]) {
const initialBalance = 1000; const initialBalance = 1000;
beforeEach(async function () { beforeEach(async function () {
this.token = await BurnableTokenMock.new(owner, initialBalance, { from: owner }); this.token = await BurnableTokenMock.new(owner, initialBalance, { from: owner });
}); });
shouldBehaveLikeBurnableToken(owner, initialBalance); shouldBehaveLikeBurnableToken(owner, initialBalance, otherAccounts);
}); });
const { assertRevert } = require('../../helpers/assertRevert');
const { inLogs } = require('../../helpers/expectEvent');
const { shouldBehaveLikeBurnableToken } = require('./BurnableToken.behaviour');
const StandardBurnableTokenMock = artifacts.require('StandardBurnableTokenMock');
const BigNumber = web3.BigNumber;
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
require('chai')
.use(require('chai-bignumber')(BigNumber))
.should();
contract('StandardBurnableToken', function ([_, owner, burner]) {
const initialBalance = 1000;
beforeEach(async function () {
this.token = await StandardBurnableTokenMock.new(owner, initialBalance);
});
shouldBehaveLikeBurnableToken(owner, initialBalance);
describe('burnFrom', function () {
describe('on success', function () {
const amount = 100;
beforeEach(async function () {
await this.token.approve(burner, 300, { from: owner });
const { logs } = await this.token.burnFrom(owner, amount, { from: burner });
this.logs = logs;
});
it('burns the requested amount', async function () {
const balance = await this.token.balanceOf(owner);
balance.should.be.bignumber.equal(initialBalance - amount);
});
it('decrements allowance', async function () {
const allowance = await this.token.allowance(owner, burner);
allowance.should.be.bignumber.equal(200);
});
it('emits a burn event', async function () {
const event = await inLogs(this.logs, 'Burn');
event.args.burner.should.eq(owner);
event.args.value.should.be.bignumber.equal(amount);
});
it('emits a transfer event', async function () {
const event = await inLogs(this.logs, 'Transfer');
event.args.from.should.eq(owner);
event.args.to.should.eq(ZERO_ADDRESS);
event.args.value.should.be.bignumber.equal(amount);
});
});
describe('when the given amount is greater than the balance of the sender', function () {
const amount = initialBalance + 1;
it('reverts', async function () {
await this.token.approve(burner, amount, { from: owner });
await assertRevert(this.token.burnFrom(owner, amount, { from: burner }));
});
});
describe('when the given amount is greater than the allowance', function () {
const amount = 100;
it('reverts', async function () {
await this.token.approve(burner, amount - 1, { from: owner });
await assertRevert(this.token.burnFrom(owner, amount, { from: burner }));
});
});
});
});
const { assertRevert } = require('../../helpers/assertRevert'); const { assertRevert } = require('../../helpers/assertRevert');
const StandardTokenMock = artifacts.require('StandardTokenMock'); const StandardToken = artifacts.require('StandardTokenMock');
contract('StandardToken', function ([_, owner, recipient, anotherAccount]) { contract('StandardToken', function ([_, owner, recipient, anotherAccount]) {
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
beforeEach(async function () { beforeEach(async function () {
this.token = await StandardTokenMock.new(owner, 100); this.token = await StandardToken.new(owner, 100);
}); });
describe('total supply', function () { describe('total supply', function () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment