Browse Source

Create and test PausableToken Contract

David Knott 8 years ago
parent
commit
b4b6029f66

+ 21 - 19
contracts/lifecycle/Pausable.sol

@@ -6,34 +6,36 @@ import "../ownership/Ownable.sol";
 
 
 /*
 /*
  * Pausable
  * 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 {
 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 - 0
contracts/token/PausableToken.sol

@@ -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);
+  }
+}

+ 10 - 9
docs/source/pausable.rst

@@ -1,26 +1,27 @@
 Pausable
 Pausable
 =============================================
 =============================================
 
 
-Base contract that provides an emergency stop mechanism.
+Base contract that provides a pause mechanism.
 
 
 Inherits from contract Ownable.
 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.

+ 9 - 9
test/Pausable.js

@@ -5,7 +5,7 @@ const PausableMock = artifacts.require('helpers/PausableMock.sol');
 
 
 contract('Pausable', function(accounts) {
 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 Pausable = await PausableMock.new();
     let count0 = await Pausable.count();
     let count0 = await Pausable.count();
     assert.equal(count0, 0);
     assert.equal(count0, 0);
@@ -15,9 +15,9 @@ contract('Pausable', function(accounts) {
     assert.equal(count1, 1);
     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();
     let Pausable = await PausableMock.new();
-    await Pausable.emergencyStop();
+    await Pausable.pause();
     let count0 = await Pausable.count();
     let count0 = await Pausable.count();
     assert.equal(count0, 0);
     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();
     let Pausable = await PausableMock.new();
     try {
     try {
       await Pausable.drasticMeasure();
       await Pausable.drasticMeasure();
@@ -43,19 +43,19 @@ contract('Pausable', function(accounts) {
     assert.isFalse(drasticMeasureTaken);
     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();
     let Pausable = await PausableMock.new();
-    await Pausable.emergencyStop();
+    await Pausable.pause();
     await Pausable.drasticMeasure();
     await Pausable.drasticMeasure();
     let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
     let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
 
 
     assert.isTrue(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();
     let Pausable = await PausableMock.new();
-    await Pausable.emergencyStop();
-    await Pausable.release();
+    await Pausable.pause();
+    await Pausable.unpause();
     await Pausable.normalProcess();
     await Pausable.normalProcess();
     let count0 = await Pausable.count();
     let count0 = await Pausable.count();
 
 

+ 73 - 0
test/PausableToken.js

@@ -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');
+  });
+})

+ 2 - 2
test/helpers/PausableMock.sol

@@ -14,11 +14,11 @@ contract PausableMock is Pausable {
     count = 0;
     count = 0;
   }
   }
 
 
-  function normalProcess() external stopInEmergency {
+  function normalProcess() external whenNotPaused {
     count++;
     count++;
   }
   }
 
 
-  function drasticMeasure() external onlyInEmergency {
+  function drasticMeasure() external whenPaused {
     drasticMeasureTaken = true;
     drasticMeasureTaken = true;
   }
   }
 
 

+ 12 - 0
test/helpers/PausableTokenMock.sol

@@ -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;
+  }
+
+}