Create and test PausableToken Contract
This commit is contained in:
@ -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;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyInEmergency {
|
||||
if (!stopped) {
|
||||
throw;
|
||||
}
|
||||
bool public paused = false;
|
||||
|
||||
modifier whenNotPaused() {
|
||||
if (paused) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
// called by the owner on emergency, triggers stopped state
|
||||
function emergencyStop() external onlyOwner {
|
||||
stopped = true;
|
||||
modifier whenPaused {
|
||||
if (!paused) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
// called by the owner on end of emergency, returns to normal state
|
||||
function release() external onlyOwner onlyInEmergency {
|
||||
stopped = false;
|
||||
// called by the owner to pause, triggers stopped state
|
||||
function pause() onlyOwner whenNotPaused returns (bool) {
|
||||
paused = true;
|
||||
Pause();
|
||||
return true;
|
||||
}
|
||||
|
||||
// called by the owner to unpause, returns to normal state
|
||||
function unpause() onlyOwner whenPaused returns (bool) {
|
||||
paused = false;
|
||||
Unpause();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
25
contracts/token/PausableToken.sol
Normal file
25
contracts/token/PausableToken.sol
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
@ -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);
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
@ -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();
|
||||
|
||||
|
||||
73
test/PausableToken.js
Normal file
73
test/PausableToken.js
Normal file
@ -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');
|
||||
});
|
||||
})
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
12
test/helpers/PausableTokenMock.sol
Normal file
12
test/helpers/PausableTokenMock.sol
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user