Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create and test PausableToken Contract #206

Merged
merged 1 commit into from
May 3, 2017
Merged
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
40 changes: 21 additions & 19 deletions contracts/lifecycle/Pausable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,36 @@ import "../ownership/Ownable.sol";

/*
* Pausable
* Abstract contract that allows children to implement an
* emergency stop mechanism.
* Abstract contract that allows children to implement a
* pause mechanism.
*/
contract Pausable is Ownable {
bool public stopped;
event Pause();
event Unpause();

modifier stopInEmergency {
if (stopped) {
throw;
}
bool public paused = false;

modifier whenNotPaused() {
if (paused) throw;
_;
}

modifier onlyInEmergency {
if (!stopped) {
throw;
}

modifier whenPaused {
if (!paused) throw;
_;
}

// called by the owner on emergency, triggers stopped state
function emergencyStop() external onlyOwner {
stopped = true;
// called by the owner to pause, triggers stopped state
function pause() onlyOwner whenNotPaused returns (bool) {
paused = true;
Pause();
return true;
}

// called by the owner on end of emergency, returns to normal state
function release() external onlyOwner onlyInEmergency {
stopped = false;
// called by the owner to unpause, returns to normal state
function unpause() onlyOwner whenPaused returns (bool) {
paused = false;
Unpause();
return true;
}

}
25 changes: 25 additions & 0 deletions contracts/token/PausableToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pragma solidity ^0.4.8;

import './StandardToken.sol';
import '../lifecycle/Pausable.sol';

/**
* Pausable token
*
* Simple ERC20 Token example, with pausable token creation
* Issue:
* https://github.com/OpenZeppelin/zeppelin-solidity/issues/194
* Based on code by BCAPtoken:
* https://github.com/BCAPtoken/BCAPToken/blob/5cb5e76338cc47343ba9268663a915337c8b268e/sol/BCAPToken.sol#L27
**/

contract PausableToken is Pausable, StandardToken {

function transfer(address _to, uint _value) whenNotPaused {
return super.transfer(_to, _value);
}

function transferFrom(address _from, address _to, uint _value) whenNotPaused {
return super.transferFrom(_from, _to, _value);
}
}
19 changes: 10 additions & 9 deletions docs/source/pausable.rst
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
Pausable
=============================================

Base contract that provides an emergency stop mechanism.
Base contract that provides a pause mechanism.

Inherits from contract Ownable.

emergencyStop( ) external onlyOwner
pause() onlyOwner whenNotPaused returns (bool)
"""""""""""""""""""""""""""""""""""""

Triggers the stop mechanism on the contract. After this function is called (by the owner of the contract), any function with modifier stopInEmergency will not run.
Triggers pause mechanism on the contract. After this function is called (by the owner of the contract), any function with modifier whenNotPaused will not run.

modifier stopInEmergency

modifier whenNotPaused()
"""""""""""""""""""""""""""""""""""""

Prevents function from running if stop mechanism is activated.
Prevents function from running if pause mechanism is activated.

modifier onlyInEmergency
modifier whenPaused()
"""""""""""""""""""""""""""""""""""""

Only runs if stop mechanism is activated.
Only runs if pause mechanism is activated.

release( ) external onlyOwner onlyInEmergency
unpause() onlyOwner whenPaused returns (bool)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

Deactivates the stop mechanism.
Deactivates the pause mechanism.
18 changes: 9 additions & 9 deletions test/Pausable.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const PausableMock = artifacts.require('helpers/PausableMock.sol');

contract('Pausable', function(accounts) {

it('can perform normal process in non-emergency', async function() {
it('can perform normal process in non-pause', async function() {
let Pausable = await PausableMock.new();
let count0 = await Pausable.count();
assert.equal(count0, 0);
Expand All @@ -15,9 +15,9 @@ contract('Pausable', function(accounts) {
assert.equal(count1, 1);
});

it('can not perform normal process in emergency', async function() {
it('can not perform normal process in pause', async function() {
let Pausable = await PausableMock.new();
await Pausable.emergencyStop();
await Pausable.pause();
let count0 = await Pausable.count();
assert.equal(count0, 0);

Expand All @@ -31,7 +31,7 @@ contract('Pausable', function(accounts) {
});


it('can not take drastic measure in non-emergency', async function() {
it('can not take drastic measure in non-pause', async function() {
let Pausable = await PausableMock.new();
try {
await Pausable.drasticMeasure();
Expand All @@ -43,19 +43,19 @@ contract('Pausable', function(accounts) {
assert.isFalse(drasticMeasureTaken);
});

it('can take a drastic measure in an emergency', async function() {
it('can take a drastic measure in a pause', async function() {
let Pausable = await PausableMock.new();
await Pausable.emergencyStop();
await Pausable.pause();
await Pausable.drasticMeasure();
let drasticMeasureTaken = await Pausable.drasticMeasureTaken();

assert.isTrue(drasticMeasureTaken);
});

it('should resume allowing normal process after emergency is over', async function() {
it('should resume allowing normal process after pause is over', async function() {
let Pausable = await PausableMock.new();
await Pausable.emergencyStop();
await Pausable.release();
await Pausable.pause();
await Pausable.unpause();
await Pausable.normalProcess();
let count0 = await Pausable.count();

Expand Down
73 changes: 73 additions & 0 deletions test/PausableToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'user strict';

const assertJump = require('./helpers/assertJump');
var PausableTokenMock = artifacts.require('./helpers/PausableTokenMock.sol');

contract('PausableToken', function(accounts) {
let token;

beforeEach(async function() {
token = await PausableTokenMock.new(accounts[0], 100);
});

it('should return paused false after construction', async function() {
let paused = await token.paused();

assert.equal(paused, false);
});

it('should return paused true after pause', async function() {
await token.pause();
let paused = await token.paused();

assert.equal(paused, true);
});

it('should return paused false after pause and unpause', async function() {
await token.pause();
await token.unpause();
let paused = await token.paused();

assert.equal(paused, false);
});

it('should be able to transfer if transfers are unpaused', async function() {
await token.transfer(accounts[1], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);

let balance1 = await token.balanceOf(accounts[1]);
assert.equal(balance1, 100);
});

it('should be able to transfer after transfers are paused and unpaused', async function() {
await token.pause();
await token.unpause();
await token.transfer(accounts[1], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);

let balance1 = await token.balanceOf(accounts[1]);
assert.equal(balance1, 100);
});

it('should throw an error trying to transfer while transactions are paused', async function() {
await token.pause();
try {
await token.transfer(accounts[1], 100);
} catch (error) {
return assertJump(error);
}
assert.fail('should have thrown before');
});

it('should throw an error trying to transfer from another account while transactions are paused', async function() {
await token.pause();
try {
await token.transferFrom(accounts[0], accounts[1], 100);
} catch (error) {
return assertJump(error);
}
assert.fail('should have thrown before');
});
})
4 changes: 2 additions & 2 deletions test/helpers/PausableMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ contract PausableMock is Pausable {
count = 0;
}

function normalProcess() external stopInEmergency {
function normalProcess() external whenNotPaused {
count++;
}

function drasticMeasure() external onlyInEmergency {
function drasticMeasure() external whenPaused {
drasticMeasureTaken = true;
}

Expand Down
12 changes: 12 additions & 0 deletions test/helpers/PausableTokenMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.4.8;

import '../../contracts/token/PausableToken.sol';

// mock class using PausableToken
contract PausableTokenMock is PausableToken {

function PausableTokenMock(address initialAccount, uint initialBalance) {
balances[initialAccount] = initialBalance;
}

}