Skip to content

Commit f7a7fc3

Browse files
authored
Merge pull request #206 from DavidKnott/create-pausable-token
Create and test PausableToken Contract
2 parents 071040f + b4b6029 commit f7a7fc3

File tree

7 files changed

+152
-39
lines changed

7 files changed

+152
-39
lines changed

contracts/lifecycle/Pausable.sol

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,36 @@ import "../ownership/Ownable.sol";
66

77
/*
88
* Pausable
9-
* Abstract contract that allows children to implement an
10-
* emergency stop mechanism.
9+
* Abstract contract that allows children to implement a
10+
* pause mechanism.
1111
*/
1212
contract Pausable is Ownable {
13-
bool public stopped;
13+
event Pause();
14+
event Unpause();
1415

15-
modifier stopInEmergency {
16-
if (stopped) {
17-
throw;
18-
}
16+
bool public paused = false;
17+
18+
modifier whenNotPaused() {
19+
if (paused) throw;
1920
_;
2021
}
21-
22-
modifier onlyInEmergency {
23-
if (!stopped) {
24-
throw;
25-
}
22+
23+
modifier whenPaused {
24+
if (!paused) throw;
2625
_;
2726
}
2827

29-
// called by the owner on emergency, triggers stopped state
30-
function emergencyStop() external onlyOwner {
31-
stopped = true;
28+
// called by the owner to pause, triggers stopped state
29+
function pause() onlyOwner whenNotPaused returns (bool) {
30+
paused = true;
31+
Pause();
32+
return true;
3233
}
3334

34-
// called by the owner on end of emergency, returns to normal state
35-
function release() external onlyOwner onlyInEmergency {
36-
stopped = false;
35+
// called by the owner to unpause, returns to normal state
36+
function unpause() onlyOwner whenPaused returns (bool) {
37+
paused = false;
38+
Unpause();
39+
return true;
3740
}
38-
3941
}

contracts/token/PausableToken.sol

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pragma solidity ^0.4.8;
2+
3+
import './StandardToken.sol';
4+
import '../lifecycle/Pausable.sol';
5+
6+
/**
7+
* Pausable token
8+
*
9+
* Simple ERC20 Token example, with pausable token creation
10+
* Issue:
11+
* https://github.com/OpenZeppelin/zeppelin-solidity/issues/194
12+
* Based on code by BCAPtoken:
13+
* https://github.com/BCAPtoken/BCAPToken/blob/5cb5e76338cc47343ba9268663a915337c8b268e/sol/BCAPToken.sol#L27
14+
**/
15+
16+
contract PausableToken is Pausable, StandardToken {
17+
18+
function transfer(address _to, uint _value) whenNotPaused {
19+
return super.transfer(_to, _value);
20+
}
21+
22+
function transferFrom(address _from, address _to, uint _value) whenNotPaused {
23+
return super.transferFrom(_from, _to, _value);
24+
}
25+
}

docs/source/pausable.rst

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
Pausable
22
=============================================
33

4-
Base contract that provides an emergency stop mechanism.
4+
Base contract that provides a pause mechanism.
55

66
Inherits from contract Ownable.
77

8-
emergencyStop( ) external onlyOwner
8+
pause() onlyOwner whenNotPaused returns (bool)
99
"""""""""""""""""""""""""""""""""""""
1010

11-
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.
11+
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.
1212

13-
modifier stopInEmergency
13+
14+
modifier whenNotPaused()
1415
"""""""""""""""""""""""""""""""""""""
1516

16-
Prevents function from running if stop mechanism is activated.
17+
Prevents function from running if pause mechanism is activated.
1718

18-
modifier onlyInEmergency
19+
modifier whenPaused()
1920
"""""""""""""""""""""""""""""""""""""
2021

21-
Only runs if stop mechanism is activated.
22+
Only runs if pause mechanism is activated.
2223

23-
release( ) external onlyOwner onlyInEmergency
24+
unpause() onlyOwner whenPaused returns (bool)
2425
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2526

26-
Deactivates the stop mechanism.
27+
Deactivates the pause mechanism.

test/Pausable.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const PausableMock = artifacts.require('helpers/PausableMock.sol');
55

66
contract('Pausable', function(accounts) {
77

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

18-
it('can not perform normal process in emergency', async function() {
18+
it('can not perform normal process in pause', async function() {
1919
let Pausable = await PausableMock.new();
20-
await Pausable.emergencyStop();
20+
await Pausable.pause();
2121
let count0 = await Pausable.count();
2222
assert.equal(count0, 0);
2323

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

3333

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

46-
it('can take a drastic measure in an emergency', async function() {
46+
it('can take a drastic measure in a pause', async function() {
4747
let Pausable = await PausableMock.new();
48-
await Pausable.emergencyStop();
48+
await Pausable.pause();
4949
await Pausable.drasticMeasure();
5050
let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
5151

5252
assert.isTrue(drasticMeasureTaken);
5353
});
5454

55-
it('should resume allowing normal process after emergency is over', async function() {
55+
it('should resume allowing normal process after pause is over', async function() {
5656
let Pausable = await PausableMock.new();
57-
await Pausable.emergencyStop();
58-
await Pausable.release();
57+
await Pausable.pause();
58+
await Pausable.unpause();
5959
await Pausable.normalProcess();
6060
let count0 = await Pausable.count();
6161

test/PausableToken.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'user strict';
2+
3+
const assertJump = require('./helpers/assertJump');
4+
var PausableTokenMock = artifacts.require('./helpers/PausableTokenMock.sol');
5+
6+
contract('PausableToken', function(accounts) {
7+
let token;
8+
9+
beforeEach(async function() {
10+
token = await PausableTokenMock.new(accounts[0], 100);
11+
});
12+
13+
it('should return paused false after construction', async function() {
14+
let paused = await token.paused();
15+
16+
assert.equal(paused, false);
17+
});
18+
19+
it('should return paused true after pause', async function() {
20+
await token.pause();
21+
let paused = await token.paused();
22+
23+
assert.equal(paused, true);
24+
});
25+
26+
it('should return paused false after pause and unpause', async function() {
27+
await token.pause();
28+
await token.unpause();
29+
let paused = await token.paused();
30+
31+
assert.equal(paused, false);
32+
});
33+
34+
it('should be able to transfer if transfers are unpaused', async function() {
35+
await token.transfer(accounts[1], 100);
36+
let balance0 = await token.balanceOf(accounts[0]);
37+
assert.equal(balance0, 0);
38+
39+
let balance1 = await token.balanceOf(accounts[1]);
40+
assert.equal(balance1, 100);
41+
});
42+
43+
it('should be able to transfer after transfers are paused and unpaused', async function() {
44+
await token.pause();
45+
await token.unpause();
46+
await token.transfer(accounts[1], 100);
47+
let balance0 = await token.balanceOf(accounts[0]);
48+
assert.equal(balance0, 0);
49+
50+
let balance1 = await token.balanceOf(accounts[1]);
51+
assert.equal(balance1, 100);
52+
});
53+
54+
it('should throw an error trying to transfer while transactions are paused', async function() {
55+
await token.pause();
56+
try {
57+
await token.transfer(accounts[1], 100);
58+
} catch (error) {
59+
return assertJump(error);
60+
}
61+
assert.fail('should have thrown before');
62+
});
63+
64+
it('should throw an error trying to transfer from another account while transactions are paused', async function() {
65+
await token.pause();
66+
try {
67+
await token.transferFrom(accounts[0], accounts[1], 100);
68+
} catch (error) {
69+
return assertJump(error);
70+
}
71+
assert.fail('should have thrown before');
72+
});
73+
})

test/helpers/PausableMock.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ contract PausableMock is Pausable {
1414
count = 0;
1515
}
1616

17-
function normalProcess() external stopInEmergency {
17+
function normalProcess() external whenNotPaused {
1818
count++;
1919
}
2020

21-
function drasticMeasure() external onlyInEmergency {
21+
function drasticMeasure() external whenPaused {
2222
drasticMeasureTaken = true;
2323
}
2424

test/helpers/PausableTokenMock.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
pragma solidity ^0.4.8;
2+
3+
import '../../contracts/token/PausableToken.sol';
4+
5+
// mock class using PausableToken
6+
contract PausableTokenMock is PausableToken {
7+
8+
function PausableTokenMock(address initialAccount, uint initialBalance) {
9+
balances[initialAccount] = initialBalance;
10+
}
11+
12+
}

0 commit comments

Comments
 (0)