Skip to content

Commit

Permalink
Merge pull request OpenZeppelin#206 from DavidKnott/create-pausable-t…
Browse files Browse the repository at this point in the history
…oken

Create and test PausableToken Contract
  • Loading branch information
maraoz authored May 3, 2017
2 parents 5d97277 + 83a3809 commit 58613dc
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 39 deletions.
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;
}

}

0 comments on commit 58613dc

Please sign in to comment.