Compare commits

...

50 Commits

Author SHA1 Message Date
8cd58303f9 v1.0.2 2017-02-23 12:56:07 -03:00
b134645637 Merge pull request #150 from maraoz/solidty-version
solidity version 0.4.8
2017-02-23 09:12:54 -03:00
d41cc437b3 Merge pull request #149 from JGcarv/feature/contactable
Added a contactable contract
2017-02-22 18:21:39 -03:00
26335e669e Added a contactable contract and some tests for it 2017-02-22 16:28:01 -03:00
52b66c744f solidity version 0.4.8 2017-02-21 19:29:03 -03:00
65c9711754 Merge pull request #147 from fabioberger/refactor/modifiers
Update modifiers to throw and update corresponding tests
2017-02-21 16:58:06 -03:00
3d6988cf90 Update modifiers so that they fail "loudly" by throwing errors rather then silently no-oping. Updated tests to remain compatible with these changes 2017-02-21 00:05:59 +01:00
c10a2cf15c Merge pull request #146 from maraoz/refactor
Refactor in preparation for audit
2017-02-17 19:30:07 -03:00
44082c029f fix travis tests 4 2017-02-17 19:23:56 -03:00
8ba0f008df fix travis tests 3 2017-02-17 19:17:42 -03:00
fcabd9957e fix travis tests 2 2017-02-17 19:02:44 -03:00
9f12e870a9 fix travis tests 2017-02-17 18:47:56 -03:00
d24a2ed9a8 fix compiler error 2017-02-17 18:32:24 -03:00
b1cb7b5975 implement solium recommendations 2017-02-17 18:31:16 -03:00
9ab997d788 fix travis test runner 2017-02-17 18:04:56 -03:00
90654cb2c9 add .jshintrc 2017-02-17 18:04:02 -03:00
9bd51db2b2 refactor tests for truffle 3 2017-02-17 18:03:52 -03:00
7e7193ae61 truffle 2=>3 2017-02-15 12:14:50 -03:00
a69333e2b8 migrate to truffle 3 2017-02-14 17:57:37 -03:00
1f35727442 Merge pull request #144 from demibrener/readme
Aragon new collaborating organization
2017-02-14 12:37:45 -03:00
cbc18e6dcc Aragon new collaborating organization 2017-02-14 12:07:11 -03:00
60b48b0235 Solium 2017-02-13 13:26:56 -03:00
f4624837a3 Merge pull request #140 from AragonOne/vesting
Vested Token implementation (Standard Token with vesting calendar)
2017-02-13 12:43:54 -03:00
0a5af4b8ac Change operations order for rounding. Make tests use blocktime as reference time rather than date. 2017-02-13 11:35:32 +01:00
74dfd3511f Merge pull request #1 from maraoz/vesting
Vesting test fixing help
2017-02-13 10:08:43 +01:00
ee56abcc8a remove console.logs from timer 2017-02-10 18:35:05 -03:00
b67f60929c make VestedToken test deterministic 2017-02-10 18:32:58 -03:00
0b71dcded2 Rename GrantableToken to VestedToken 2017-02-10 16:20:23 +01:00
f305382ef4 Remove grantTokens without vesting function 2017-02-10 16:18:36 +01:00
917b129517 Add safeDiv and min/max functions to SafeMath 2017-02-10 16:17:02 +01:00
abc646a95c lastTokenIsTransferableDate rename 2017-02-10 16:04:58 +01:00
1697518da8 Make vested stock calculation more testable 2017-02-10 15:28:20 +01:00
93e7984c61 Transferrable is now transferrable 2017-02-10 14:59:19 +01:00
8d9e12eda3 Add GrantableToken tests 2017-02-09 13:23:12 +01:00
fb0a96332c GrantableTokenMock 2017-02-08 15:27:39 +01:00
ba7a0f7649 Merge branch 'master' of https://github.com/OpenZeppelin/zeppelin-solidity into vesting 2017-02-08 15:26:15 +01:00
a72de719c9 Merge pull request #139 from eordano/patch-1
BasicToken.sol: Remove useless check
2017-02-08 11:13:38 -03:00
ec6e728c0f Grantable Token first version 2017-02-08 12:31:20 +01:00
70fbc7fe27 BasicToken.sol: Remove useless check
The condition this commit removes is already implemented by the call to `safeSub`
2017-02-07 19:45:32 -05:00
ced4e2da3f Merge pull request #137 from JGcarv/chore/rename-to-pausable
Renamed files and replaced occurencies of 'Stoppable' to 'Pausable'
2017-02-06 16:34:54 -03:00
ae73f1a564 Renamed files and replaced occurencies of 'Stoppable' to 'Pausable' 2017-01-30 23:30:43 -02:00
26127ee36a Merge pull request #136 from AragonOne/master
Make test cases that use assertJump fail if they dont fail
2017-01-23 15:48:38 -03:00
2f0471fff2 Make test cases that use assertJump fail if they dont fail 2017-01-23 19:18:41 +01:00
ad833fb40d Merge pull request #132 from OpenZeppelin/refactor
Refactor folders in preparation for external audit
2017-01-17 13:02:16 -03:00
a544f1517c Merge pull request #133 from jtakalai/patch-1
Fix typos
2017-01-17 11:24:17 -03:00
67ad6abdaa Fix typos
On unrelated note, why on earth check like in safeAdd? Is it actually an overflow check?
2017-01-17 12:29:54 +02:00
0dfb27ff5f fix tests 2017-01-16 17:42:26 -03:00
ca8f2f2362 rearrange folders 2017-01-16 17:23:28 -03:00
2de7fec6dc Merge pull request #127 from OpenZeppelin/contributing
Add tag comment on CONTRIBUTING.md
2017-01-09 16:27:56 -03:00
342e7bf9ff Add tag comment on CONTRIBUTING.md
Fixes #116
2017-01-09 16:27:32 -03:00
77 changed files with 915 additions and 738 deletions

48
.jshintrc Normal file
View File

@ -0,0 +1,48 @@
{
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
"browser": true, // Standard browser globals e.g. `window`, `document`.
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
"curly": true, // Require {} for every new block or scope.
"devel": false, // Allow development statements e.g. `console.log();`.
"eqeqeq": true, // Require triple equals i.e. `===`.
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
"freeze": true, // Forbid overwriting prototypes of native objects such as Array, Date and so on.
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"indent": 2, // Specify indentation spacing
"latedef": true, // Prohibit variable use before definition.
"newcap": false, // Require capitalization of all constructor functions e.g. `new F()`.
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
"noempty": true, // Prohibit use of empty blocks.
"nonew": true, // Prohibits the use of constructor functions for side-effects
"quotmark": "single", // Define quotes to string values.
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
"smarttabs": false, // Supress warnings about mixed tabs and spaces
"strict": true, // Require `use strict` pragma in every file.
"trailing": true, // Prohibit trailing whitespaces.
"undef": true, // Require all non-global variables be declared before they are used.
"unused": true, // Warn unused variables.
"maxparams": 4, // Maximum number of parameters for a function
"maxstatements": 15, // Maximum number of statements in a function
"maxcomplexity": 10, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity)
"maxdepth": 4, // Maximum depth of nested control structures
"maxlen": 120, // Maximum number of cols in a line
"multistr": true, // Allow use of multiline EOL escaping
"experimental": ["asyncawait", "asyncreqawait"],
"predef": [ // Extra globals.
"after",
"afterEach",
"before",
"beforeEach",
"define",
"describe",
"exports",
"it",
"web3",
"artifacts",
"contract",
"assert"
]
}

1
.soliumignore Normal file
View File

@ -0,0 +1 @@
node_modules

22
.soliumrc.json Normal file
View File

@ -0,0 +1,22 @@
{
"custom-rules-filename": null,
"rules": {
"imports-on-top": true,
"variable-declarations": true,
"array-declarations": true,
"operator-whitespace": true,
"lbrace": true,
"mixedcase": false,
"camelcase": true,
"uppercase": true,
"no-with": true,
"no-empty-blocks": true,
"no-unused-vars": true,
"double-quotes": true,
"blank-lines": true,
"indentation": true,
"whitespace": true,
"deprecated-suicide": true,
"pragma-on-top": true
}
}

View File

@ -5,8 +5,8 @@ language: node_js
node_js: node_js:
- "6" - "6"
before_install: before_install:
- npm install truffle@3.1.9 -g
- npm i -g ethereumjs-testrpc - npm i -g ethereumjs-testrpc
- npm i -g truffle
script: script:
- testrpc& - testrpc > /dev/null &
- npm test - truffle test

View File

@ -102,4 +102,6 @@ Main contributors will review your code and possibly ask for changes before your
If you have any questions feel free to post them to If you have any questions feel free to post them to
[github.com/OpenZeppelin/zeppelin-solidity/issues](https://github.com/OpenZeppelin/zeppelin-solidity/issues). [github.com/OpenZeppelin/zeppelin-solidity/issues](https://github.com/OpenZeppelin/zeppelin-solidity/issues).
Finally, if you're looking to collaborate and want to find easy tasks to start, [look at the issues we marked as easy](https://github.com/OpenZeppelin/zeppelin-solidity/labels/easy).
Thanks for your time and code! Thanks for your time and code!

View File

@ -71,7 +71,7 @@ Interested in contributing to Zeppelin?
## Collaborating organizations and audits by Zeppelin ## Collaborating organizations and audits by Zeppelin
- [Golem](https://golem.network/) - [Golem](https://golem.network/)
- [Mediachain](https://golem.network/) - [Mediachain](http://www.mediachain.io/)
- [Truffle](http://truffleframework.com/) - [Truffle](http://truffleframework.com/)
- [Firstblood](http://firstblood.io/) - [Firstblood](http://firstblood.io/)
- [Rootstock](http://www.rsk.co/) - [Rootstock](http://www.rsk.co/)
@ -81,6 +81,7 @@ Interested in contributing to Zeppelin?
- [DemocracyEarth](http://democracy.earth/) - [DemocracyEarth](http://democracy.earth/)
- [Signatura](https://signatura.co/) - [Signatura](https://signatura.co/)
- [Ether.camp](http://www.ether.camp/) - [Ether.camp](http://www.ether.camp/)
- [Aragon](https://aragon.one/)
among others... among others...

View File

@ -1,8 +1,8 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import './PullPayment.sol'; import './payment/PullPayment.sol';
import './Killable.sol'; import './lifecycle/Killable.sol';
/* /*
@ -11,18 +11,19 @@ import './Killable.sol';
* This bounty will pay out to a researcher if they break invariant logic of the contract. * This bounty will pay out to a researcher if they break invariant logic of the contract.
*/ */
contract Bounty is PullPayment, Killable { contract Bounty is PullPayment, Killable {
Target target;
bool public claimed; bool public claimed;
mapping(address => address) public researchers; mapping(address => address) public researchers;
event TargetCreated(address createdAddress); event TargetCreated(address createdAddress);
function() payable { function() payable {
if (claimed) throw; if (claimed) {
throw;
}
} }
function createTarget() returns(Target) { function createTarget() returns(Target) {
target = Target(deployContract()); Target target = Target(deployContract());
researchers[target] = msg.sender; researchers[target] = msg.sender;
TargetCreated(target); TargetCreated(target);
return target; return target;
@ -30,13 +31,11 @@ contract Bounty is PullPayment, Killable {
function deployContract() internal returns(address); function deployContract() internal returns(address);
function checkInvariant() returns(bool){
return target.checkInvariant();
}
function claim(Target target) { function claim(Target target) {
address researcher = researchers[target]; address researcher = researchers[target];
if (researcher == 0) throw; if (researcher == 0) {
throw;
}
// Check Target contract invariants // Check Target contract invariants
if (target.checkInvariant()) { if (target.checkInvariant()) {
throw; throw;
@ -47,6 +46,7 @@ contract Bounty is PullPayment, Killable {
} }
/* /*
* Target * Target
* *

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import './Shareable.sol'; import './ownership/Shareable.sol';
/* /*
@ -12,33 +12,18 @@ import './Shareable.sol';
* uses is specified in the modifier. * uses is specified in the modifier.
*/ */
contract DayLimit { contract DayLimit {
// FIELDS
uint public dailyLimit; uint public dailyLimit;
uint public spentToday; uint public spentToday;
uint public lastDay; uint public lastDay;
// MODIFIERS
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
_;
}
// CONSTRUCTOR
// stores initial daily limit and records the present day's index.
function DayLimit(uint _limit) { function DayLimit(uint _limit) {
dailyLimit = _limit; dailyLimit = _limit;
lastDay = today(); lastDay = today();
} }
// sets the daily limit. doesn't alter the amount already spent today
// METHODS
// (re)sets the daily limit. doesn't alter the amount already spent today.
function _setDailyLimit(uint _newLimit) internal { function _setDailyLimit(uint _newLimit) internal {
dailyLimit = _newLimit; dailyLimit = _newLimit;
} }
@ -48,9 +33,6 @@ contract DayLimit {
spentToday = 0; spentToday = 0;
} }
// INTERNAL METHODS
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
// returns true. otherwise just returns false. // returns true. otherwise just returns false.
function underLimit(uint _value) internal returns (bool) { function underLimit(uint _value) internal returns (bool) {
@ -72,4 +54,13 @@ contract DayLimit {
function today() private constant returns (uint) { function today() private constant returns (uint) {
return now / 1 days; return now / 1 days;
} }
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (!underLimit(_value)) {
throw;
}
_;
}
} }

View File

@ -1,30 +0,0 @@
pragma solidity ^0.4.4;
import './Ownable.sol';
import './Claimable.sol';
/*
* DelayedClaimable
* Extension for the Claimable contract, where the ownership needs to be claimed before/after certain block number
*/
contract DelayedClaimable is Ownable, Claimable {
uint public claimBeforeBlock;
uint public claimAfterBlock;
function setClaimBlocks(uint _claimBeforeBlock, uint _claimAfterBlock) onlyOwner {
if (_claimAfterBlock > claimBeforeBlock)
throw;
claimBeforeBlock = _claimBeforeBlock;
claimAfterBlock = _claimAfterBlock;
}
function claimOwnership() onlyPendingOwner {
if ((block.number > claimBeforeBlock) || (block.number < claimAfterBlock))
throw;
owner = pendingOwner;
pendingOwner = 0x0;
claimBeforeBlock = 0;
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/** /**

View File

@ -1,8 +1,8 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "./Multisig.sol"; import "./ownership/Multisig.sol";
import "./Shareable.sol"; import "./ownership/Shareable.sol";
import "./DayLimit.sol"; import "./DayLimit.sol";
@ -13,27 +13,17 @@ import "./DayLimit.sol";
* Wallet(w).from(anotherOwner).confirm(h); * Wallet(w).from(anotherOwner).confirm(h);
*/ */
contract MultisigWallet is Multisig, Shareable, DayLimit { contract MultisigWallet is Multisig, Shareable, DayLimit {
// TYPES
// Transaction structure to remember details of transaction lest it need be saved for a later call.
struct Transaction { struct Transaction {
address to; address to;
uint value; uint value;
bytes data; bytes data;
} }
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
// CONSTRUCTOR Shareable(_owners, _required)
// just pass on the owner array to the multiowned and
// the limit to daylimit
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
Shareable(_owners, _required)
DayLimit(_daylimit) { } DayLimit(_daylimit) { }
// METHODS
// kills the contract sending everything to `_to`. // kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external { function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to); suicide(_to);

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/** /**
@ -11,6 +11,13 @@ contract SafeMath {
return c; return c;
} }
function safeDiv(uint a, uint b) internal returns (uint) {
assert(b > 0);
uint c = a / b;
assert(a == b * c + a % b);
return c;
}
function safeSub(uint a, uint b) internal returns (uint) { function safeSub(uint a, uint b) internal returns (uint) {
assert(b <= a); assert(b <= a);
return a - b; return a - b;
@ -22,8 +29,25 @@ contract SafeMath {
return c; return c;
} }
function max64(uint64 a, uint64 b) internal constant returns (uint64) {
return a >= b ? a : b;
}
function min64(uint64 a, uint64 b) internal constant returns (uint64) {
return a < b ? a : b;
}
function max256(uint256 a, uint256 b) internal constant returns (uint256) {
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b) internal constant returns (uint256) {
return a < b ? a : b;
}
function assert(bool assertion) internal { function assert(bool assertion) internal {
if (!assertion) throw; if (!assertion) {
throw;
}
} }
} }

View File

@ -1,23 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
// UNSAFE CODE, DO NOT USE!
contract BadArrayUse is PullPayment {
address[] employees;
function payBonus() {
for (var i = 0; i < employees.length; i++) {
address employee = employees[i];
uint bonus = calculateBonus(employee);
asyncSend(employee, bonus);
}
}
function calculateBonus(address employee) returns (uint) {
// some expensive computation...
}
}

View File

@ -1,17 +0,0 @@
pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
contract BadFailEarly {
uint constant DEFAULT_SALARY = 50000;
mapping(string => uint) nameToSalary;
function getSalary(string name) constant returns (uint) {
if (bytes(name).length != 0 && nameToSalary[name] != 0) {
return nameToSalary[name];
} else {
return DEFAULT_SALARY;
}
}
}

View File

@ -1,23 +0,0 @@
pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
contract BadPushPayments {
address highestBidder;
uint highestBid;
function bid() payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {
// return bid to previous winner
if (!highestBidder.send(highestBid)) {
throw;
}
}
highestBidder = msg.sender;
highestBid = msg.value;
}
}

View File

@ -1,25 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
contract GoodArrayUse is PullPayment {
address[] employees;
mapping(address => uint) bonuses;
function payBonus() {
for (uint i = 0; i < employees.length; i++) {
address employee = employees[i];
uint bonus = bonuses[employee];
asyncSend(employee, bonus);
}
}
function calculateBonus(address employee) returns (uint) {
uint bonus = 0;
// some expensive computation...
bonuses[employee] = bonus;
}
}

View File

@ -1,15 +0,0 @@
pragma solidity ^0.4.4;
contract GoodFailEarly {
uint constant DEFAULT_SALARY = 50000;
mapping(string => uint) nameToSalary;
function getSalary(string name) constant returns (uint) {
if (bytes(name).length == 0) throw;
if (nameToSalary[name] == 0) throw;
return nameToSalary[name];
}
}

View File

@ -1,27 +0,0 @@
pragma solidity ^0.4.4;
contract GoodPullPayments {
address highestBidder;
uint highestBid;
mapping(address => uint) refunds;
function bid() external payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {
refunds[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
}
function withdrawBid() external {
uint refund = refunds[msg.sender];
refunds[msg.sender] = 0;
if (!msg.sender.send(refund)) {
refunds[msg.sender] = refund;
}
}
}

View File

@ -1,38 +0,0 @@
pragma solidity ^0.4.4;
/*
* Proof of Existence example contract
* see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05
*/
contract ProofOfExistence {
mapping (bytes32 => bool) public proofs;
// store a proof of existence in the contract state
function storeProof(bytes32 proof) {
proofs[proof] = true;
}
// calculate and store the proof for a document
function notarize(string document) {
var proof = calculateProof(document);
storeProof(proof);
}
// helper function to get a document's sha256
function calculateProof(string document) constant returns (bytes32) {
return sha256(document);
}
// check if a document has been notarized
function checkDocument(string document) constant returns (bool) {
var proof = calculateProof(document);
return hasProof(proof);
}
// returns true if proof is stored
function hasProof(bytes32 proof) constant returns (bool) {
return proofs[proof];
}
}

View File

@ -1,20 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
contract PullPaymentBid is PullPayment {
address public highestBidder;
uint public highestBid;
function bid() external payable {
if (msg.value <= highestBid) throw;
if (highestBidder != 0) {
asyncSend(highestBidder, highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
}

View File

@ -1,26 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
import '../Stoppable.sol';
contract StoppableBid is Stoppable, PullPayment {
address public highestBidder;
uint public highestBid;
function bid() external payable stopInEmergency {
if (msg.value <= highestBid) throw;
if (highestBidder != 0) {
asyncSend(highestBidder, highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
function withdraw() onlyInEmergency {
selfdestruct(owner);
}
}

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "./Ownable.sol"; import "../ownership/Ownable.sol";
/* /*

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import './Ownable.sol'; import '../ownership/Ownable.sol';
contract Migrations is Ownable { contract Migrations is Ownable {

View File

@ -1,19 +1,28 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "./Ownable.sol"; import "../ownership/Ownable.sol";
/* /*
* Stoppable * Pausable
* Abstract contract that allows children to implement an * Abstract contract that allows children to implement an
* emergency stop mechanism. * emergency stop mechanism.
*/ */
contract Stoppable is Ownable { contract Pausable is Ownable {
bool public stopped; bool public stopped;
modifier stopInEmergency { if (!stopped) _; } modifier stopInEmergency {
modifier onlyInEmergency { if (stopped) _; } if (!stopped) {
_;
}
}
modifier onlyInEmergency {
if (stopped) {
_;
}
}
// called by the owner on emergency, triggers stopped state // called by the owner on emergency, triggers stopped state
function emergencyStop() external onlyOwner { function emergencyStop() external onlyOwner {

View File

@ -13,8 +13,10 @@ contract Claimable is Ownable {
address public pendingOwner; address public pendingOwner;
modifier onlyPendingOwner() { modifier onlyPendingOwner() {
if (msg.sender == pendingOwner) if (msg.sender != pendingOwner) {
_; throw;
}
_;
} }
function transferOwnership(address newOwner) onlyOwner { function transferOwnership(address newOwner) onlyOwner {

View File

@ -0,0 +1,16 @@
pragma solidity ^0.4.0;
import './Ownable.sol';
/*
* Contactable token
* Basic version of a contactable contract
*/
contract Contactable is Ownable{
string public contactInformation;
function setContactInformation(string info) onlyOwner{
contactInformation = info;
}
}

View File

@ -0,0 +1,32 @@
pragma solidity ^0.4.8;
import './Ownable.sol';
import './Claimable.sol';
/*
* DelayedClaimable
* Extension for the Claimable contract, where the ownership needs to be claimed before/after certain block number
*/
contract DelayedClaimable is Ownable, Claimable {
uint public end;
uint public start;
function setLimits(uint _start, uint _end) onlyOwner {
if (_start > _end)
throw;
end = _end;
start = _start;
}
function claimOwnership() onlyPendingOwner {
if ((block.number > end) || (block.number < start))
throw;
owner = pendingOwner;
pendingOwner = 0x0;
end = 0;
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/* /*

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/* /*
@ -15,12 +15,16 @@ contract Ownable {
} }
modifier onlyOwner() { modifier onlyOwner() {
if (msg.sender == owner) if (msg.sender != owner) {
_; throw;
}
_;
} }
function transferOwnership(address newOwner) onlyOwner { function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) owner = newOwner; if (newOwner != address(0)) {
owner = newOwner;
}
} }
} }

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/* /*
@ -12,8 +12,6 @@ pragma solidity ^0.4.4;
* use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by some number (specified in constructor) of the set of owners (specified in the constructor) before the interior is executed. * use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by some number (specified in constructor) of the set of owners (specified in the constructor) before the interior is executed.
*/ */
contract Shareable { contract Shareable {
// TYPES
// struct for the status of a pending operation. // struct for the status of a pending operation.
struct PendingState { struct PendingState {
uint yetNeeded; uint yetNeeded;
@ -21,15 +19,11 @@ contract Shareable {
uint index; uint index;
} }
// FIELDS
// the number of owners that must confirm the same operation before it is run. // the number of owners that must confirm the same operation before it is run.
uint public required; uint public required;
// list of owners // list of owners
uint[256] owners; uint[256] owners;
uint constant c_maxOwners = 250;
// index on the list of owners to allow reverse lookup // index on the list of owners to allow reverse lookup
mapping(uint => uint) ownerIndex; mapping(uint => uint) ownerIndex;
// the ongoing operations. // the ongoing operations.
@ -37,33 +31,29 @@ contract Shareable {
bytes32[] pendingsIndex; bytes32[] pendingsIndex;
// EVENTS
// this contract only has six types of events: it can accept a confirmation, in which case // this contract only has six types of events: it can accept a confirmation, in which case
// we record owner and operation (hash) alongside it. // we record owner and operation (hash) alongside it.
event Confirmation(address owner, bytes32 operation); event Confirmation(address owner, bytes32 operation);
event Revoke(address owner, bytes32 operation); event Revoke(address owner, bytes32 operation);
// MODIFIERS
// simple single-sig function modifier. // simple single-sig function modifier.
modifier onlyOwner { modifier onlyOwner {
if (isOwner(msg.sender)) if (!isOwner(msg.sender)) {
_; throw;
}
_;
} }
// multi-sig function modifier: the operation must have an intrinsic hash in order // multi-sig function modifier: the operation must have an intrinsic hash in order
// that later attempts can be realised as the same underlying operation and // that later attempts can be realised as the same underlying operation and
// thus count as confirmations. // thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) { modifier onlymanyowners(bytes32 _operation) {
if (confirmAndCheck(_operation)) if (confirmAndCheck(_operation)) {
_; _;
}
} }
// CONSTRUCTOR
// constructor is given number of sigs required to do protected "onlymanyowners" transactions // constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them. // as well as the selection of addresses capable of confirming them.
function Shareable(address[] _owners, uint _required) { function Shareable(address[] _owners, uint _required) {
@ -76,14 +66,13 @@ contract Shareable {
required = _required; required = _required;
} }
// METHODS
// Revokes a prior confirmation of the given operation // Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external { function revoke(bytes32 _operation) external {
uint index = ownerIndex[uint(msg.sender)]; uint index = ownerIndex[uint(msg.sender)];
// make sure they're an owner // make sure they're an owner
if (index == 0) return; if (index == 0) {
return;
}
uint ownerIndexBit = 2**index; uint ownerIndexBit = 2**index;
var pending = pendings[_operation]; var pending = pendings[_operation];
if (pending.ownersDone & ownerIndexBit > 0) { if (pending.ownersDone & ownerIndexBit > 0) {
@ -107,20 +96,22 @@ contract Shareable {
uint index = ownerIndex[uint(_owner)]; uint index = ownerIndex[uint(_owner)];
// make sure they're an owner // make sure they're an owner
if (index == 0) return false; if (index == 0) {
return false;
}
// determine the bit to set for this owner. // determine the bit to set for this owner.
uint ownerIndexBit = 2**index; uint ownerIndexBit = 2**index;
return !(pending.ownersDone & ownerIndexBit == 0); return !(pending.ownersDone & ownerIndexBit == 0);
} }
// INTERNAL METHODS
function confirmAndCheck(bytes32 _operation) internal returns (bool) { function confirmAndCheck(bytes32 _operation) internal returns (bool) {
// determine what index the present sender is: // determine what index the present sender is:
uint index = ownerIndex[uint(msg.sender)]; uint index = ownerIndex[uint(msg.sender)];
// make sure they're an owner // make sure they're an owner
if (index == 0) return; if (index == 0) {
return;
}
var pending = pendings[_operation]; var pending = pendings[_operation];
// if we're not yet working on this operation, switch over and reset the confirmation status. // if we're not yet working on this operation, switch over and reset the confirmation status.
@ -143,21 +134,21 @@ contract Shareable {
delete pendingsIndex[pendings[_operation].index]; delete pendingsIndex[pendings[_operation].index];
delete pendings[_operation]; delete pendings[_operation];
return true; return true;
} else {
// not enough: record that this owner in particular confirmed.
pending.yetNeeded--;
pending.ownersDone |= ownerIndexBit;
} }
else
{
// not enough: record that this owner in particular confirmed.
pending.yetNeeded--;
pending.ownersDone |= ownerIndexBit;
}
} }
} }
function clearPending() internal { function clearPending() internal {
uint length = pendingsIndex.length; uint length = pendingsIndex.length;
for (uint i = 0; i < length; ++i) for (uint i = 0; i < length; ++i) {
if (pendingsIndex[i] != 0) if (pendingsIndex[i] != 0) {
delete pendings[pendingsIndex[i]]; delete pendings[pendingsIndex[i]];
}
}
delete pendingsIndex; delete pendingsIndex;
} }

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/* /*
@ -19,10 +19,16 @@ contract PullPayment {
address payee = msg.sender; address payee = msg.sender;
uint payment = payments[payee]; uint payment = payments[payee];
if (payment == 0) throw; if (payment == 0) {
if (this.balance < payment) throw; throw;
}
if (this.balance < payment) {
throw;
}
payments[payee] = 0; payments[payee] = 0;
if (!payee.send(payment)) { if (!payee.send(payment)) {
throw; throw;
} }

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import './ERC20Basic.sol'; import './ERC20Basic.sol';
@ -14,9 +14,6 @@ contract BasicToken is ERC20Basic, SafeMath {
mapping(address => uint) balances; mapping(address => uint) balances;
function transfer(address _to, uint _value) { function transfer(address _to, uint _value) {
if (balances[msg.sender] < _value) {
throw;
}
balances[msg.sender] = safeSub(balances[msg.sender], _value); balances[msg.sender] = safeSub(balances[msg.sender], _value);
balances[_to] = safeAdd(balances[_to], _value); balances[_to] = safeAdd(balances[_to], _value);
Transfer(msg.sender, _to, _value); Transfer(msg.sender, _to, _value);

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "./StandardToken.sol"; import "./StandardToken.sol";
@ -23,7 +23,9 @@ contract CrowdsaleToken is StandardToken {
} }
function createTokens(address recipient) payable { function createTokens(address recipient) payable {
if (msg.value == 0) throw; if (msg.value == 0) {
throw;
}
uint tokens = safeMul(msg.value, getPrice()); uint tokens = safeMul(msg.value, getPrice());
@ -32,7 +34,7 @@ contract CrowdsaleToken is StandardToken {
} }
// replace this with any other price function // replace this with any other price function
function getPrice() constant returns (uint result){ function getPrice() constant returns (uint result) {
return PRICE; return PRICE;
} }
} }

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/* /*

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
/* /*

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "./StandardToken.sol"; import "./StandardToken.sol";

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import './ERC20.sol'; import './ERC20.sol';

View File

@ -0,0 +1,137 @@
pragma solidity ^0.4.8;
import "./StandardToken.sol";
contract VestedToken is StandardToken {
struct TokenGrant {
address granter;
uint256 value;
uint64 cliff;
uint64 vesting;
uint64 start;
}
mapping (address => TokenGrant[]) public grants;
function grantVestedTokens(
address _to,
uint256 _value,
uint64 _start,
uint64 _cliff,
uint64 _vesting) {
if (_cliff < _start) {
throw;
}
if (_vesting < _start) {
throw;
}
if (_vesting < _cliff) {
throw;
}
TokenGrant memory grant = TokenGrant({start: _start, value: _value, cliff: _cliff, vesting: _vesting, granter: msg.sender});
grants[_to].push(grant);
transfer(_to, _value);
}
function revokeTokenGrant(address _holder, uint _grantId) {
TokenGrant grant = grants[_holder][_grantId];
if (grant.granter != msg.sender) {
throw;
}
uint256 nonVested = nonVestedTokens(grant, uint64(now));
// remove grant from array
delete grants[_holder][_grantId];
grants[_holder][_grantId] = grants[_holder][grants[_holder].length - 1];
grants[_holder].length -= 1;
balances[msg.sender] = safeAdd(balances[msg.sender], nonVested);
balances[_holder] = safeSub(balances[_holder], nonVested);
Transfer(_holder, msg.sender, nonVested);
}
function tokenGrantsCount(address _holder) constant returns (uint index) {
return grants[_holder].length;
}
function tokenGrant(address _holder, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting) {
TokenGrant grant = grants[_holder][_grantId];
granter = grant.granter;
value = grant.value;
start = grant.start;
cliff = grant.cliff;
vesting = grant.vesting;
vested = vestedTokens(grant, uint64(now));
}
function vestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
return calculateVestedTokens(
grant.value,
uint256(time),
uint256(grant.start),
uint256(grant.cliff),
uint256(grant.vesting)
);
}
function calculateVestedTokens(
uint256 tokens,
uint256 time,
uint256 start,
uint256 cliff,
uint256 vesting) constant returns (uint256 vestedTokens)
{
if (time < cliff) {
return 0;
}
if (time > vesting) {
return tokens;
}
uint256 cliffTokens = safeDiv(safeMul(tokens, safeSub(cliff, start)), safeSub(vesting, start));
vestedTokens = cliffTokens;
uint256 vestingTokens = safeSub(tokens, cliffTokens);
vestedTokens = safeAdd(vestedTokens, safeDiv(safeMul(vestingTokens, safeSub(time, cliff)), safeSub(vesting, start)));
}
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
return safeSub(grant.value, vestedTokens(grant, time));
}
function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) {
date = uint64(now);
uint256 grantIndex = grants[holder].length;
for (uint256 i = 0; i < grantIndex; i++) {
date = max64(grants[holder][i].vesting, date);
}
}
function transferableTokens(address holder, uint64 time) constant public returns (uint256 nonVested) {
uint256 grantIndex = grants[holder].length;
for (uint256 i = 0; i < grantIndex; i++) {
nonVested = safeAdd(nonVested, nonVestedTokens(grants[holder][i], time));
}
return safeSub(balances[holder], nonVested);
}
function transfer(address _to, uint _value) returns (bool success) {
if (_value > transferableTokens(msg.sender, uint64(now))) {
throw;
}
return super.transfer(_to, _value);
}
}

View File

@ -26,7 +26,7 @@ The code is open-source, and `available on github <https://github.com/OpenZeppel
:caption: Smart Contracts :caption: Smart Contracts
ownable ownable
stoppable Pausable
killable killable
claimable claimable
migrations migrations

View File

@ -1,4 +1,4 @@
Stoppable Pausable
============================================= =============================================
Base contract that provides an emergency stop mechanism. Base contract that provides an emergency stop mechanism.

View File

@ -13,12 +13,12 @@ safeMul(uint a, uint b) internal returns (uint)
Multiplies two unisgned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier. Multiplies two unisgned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier.
safeSub(uint a, unit b) internal returns (uint) safeSub(uint a, uint b) internal returns (uint)
""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""
Checks that b is not greater than a before subtracting. Checks that b is not greater than a before subtracting.
safeAdd(unit a, unit b) internal returns (uint) safeAdd(uint a, uint b) internal returns (uint)
""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""
Checks that the result is greater than both a and b. Checks that the result is greater than both a and b.

View File

@ -1,3 +1,5 @@
var Migrations = artifacts.require("./Migrations.sol");
module.exports = function(deployer) { module.exports = function(deployer) {
deployer.deploy(Migrations); deployer.deploy(Migrations);
}; };

View File

@ -1,12 +1,5 @@
//var Ownable = artifacts.require("ownership/Ownable.sol");
module.exports = function(deployer) { module.exports = function(deployer) {
deployer.deploy(PullPaymentBid); //deployer.deploy(Ownable);
deployer.deploy(BadArrayUse);
deployer.deploy(ProofOfExistence);
deployer.deploy(Ownable);
deployer.deploy(Claimable);
deployer.deploy(LimitBalance);
if(deployer.network == 'test'){
deployer.deploy(SecureTargetBounty);
deployer.deploy(InsecureTargetBounty);
};
}; };

View File

@ -1,17 +1,19 @@
{ {
"name": "zeppelin-solidity", "name": "zeppelin-solidity",
"version": "1.0.1", "version": "1.0.2",
"description": "Secure Smart Contract library for Solidity", "description": "Secure Smart Contract library for Solidity",
"main": "truffle.js", "main": "truffle.js",
"devDependencies": { "devDependencies": {
"babel-preset-es2015": "^6.18.0", "babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.18.0", "babel-preset-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0", "babel-preset-stage-3": "^6.17.0",
"babel-register": "^6.23.0",
"ethereumjs-testrpc": "^3.0.2", "ethereumjs-testrpc": "^3.0.2",
"truffle": "^2.1.1" "truffle": "https://github.com/ConsenSys/truffle.git#3.1.9"
}, },
"scripts": { "scripts": {
"test": "truffle test", "test": "scripts/test.sh",
"console": "truffle console",
"install": "scripts/install.sh" "install": "scripts/install.sh"
}, },
"repository": { "repository": {

13
scripts/test.sh Executable file
View File

@ -0,0 +1,13 @@
#! /bin/bash
output=$(nc -z localhost 8545; echo $?)
[ $output -eq "0" ] && trpc_running=true
if [ ! $trpc_running ]; then
echo "Starting our own testrpc node instance"
testrpc > /dev/null &
trpc_pid=$!
fi
./node_modules/truffle/cli.js test
if [ ! $trpc_running ]; then
kill -9 $trpc_pid
fi

View File

@ -1,5 +1,7 @@
const assertJump = require('./helpers/assertJump'); const assertJump = require('./helpers/assertJump');
var BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");
contract('BasicToken', function(accounts) { contract('BasicToken', function(accounts) {
it("should return the correct totalSupply after construction", async function() { it("should return the correct totalSupply after construction", async function() {
@ -25,8 +27,9 @@ contract('BasicToken', function(accounts) {
try { try {
let transfer = await token.transfer(accounts[1], 101); let transfer = await token.transfer(accounts[1], 101);
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
}); });

View File

@ -1,25 +1,39 @@
'use strict';
let sendReward = function(sender, receiver, value){ let sendReward = function(sender, receiver, value){
web3.eth.sendTransaction({ web3.eth.sendTransaction({
from:sender, from:sender,
to:receiver, to:receiver,
value: value value: value
}) });
};
var SecureTargetBounty = artifacts.require('helpers/SecureTargetBounty.sol');
var InsecureTargetBounty = artifacts.require('helpers/InsecureTargetBounty.sol');
function awaitEvent(event, handler) {
return new Promise((resolve, reject) => {
function wrappedHandler(...args) {
Promise.resolve(handler(...args)).then(resolve).catch(reject);
}
event.watch(wrappedHandler);
});
} }
contract('Bounty', function(accounts) { contract('Bounty', function(accounts) {
it("sets reward", async function(){ it('sets reward', async function() {
let owner = accounts[0]; let owner = accounts[0];
let reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new(); let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward); sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()); assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
}) });
it("empties itself when killed", async function(){ it('empties itself when killed', async function(){
let owner = accounts[0]; let owner = accounts[0];
let reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new(); let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward); sendReward(owner, bounty.address, reward);
@ -27,89 +41,74 @@ contract('Bounty', function(accounts) {
await bounty.kill(); await bounty.kill();
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber()); assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
}) });
describe("Against secure contract", function(){ describe('Against secure contract', function(){
it("checkInvariant returns true", async function(){ it('cannot claim reward', async function(){
let bounty = await SecureTargetBounty.new();
let target = await bounty.createTarget();
let check = await bounty.checkInvariant.call();
assert.isTrue(check);
})
it("cannot claim reward", async function(done){
let owner = accounts[0]; let owner = accounts[0];
let researcher = accounts[1]; let researcher = accounts[1];
let reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new(); let bounty = await SecureTargetBounty.new();
let event = bounty.TargetCreated({}); let event = bounty.TargetCreated({});
event.watch(async function(err, result) { event.watch(async function(err, result) {
event.stopWatching(); event.stopWatching();
if (err) { throw err } if (err) { throw err; }
var targetAddress = result.args.createdAddress; var targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward); sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) assert.equal(reward,
web3.eth.getBalance(bounty.address).toNumber());
try { try {
let tmpClain = await bounty.claim(targetAddress, {from:researcher}); await bounty.claim(targetAddress, {from:researcher});
done("should not come here"); assert.isTrue(false); // should never reach here
} catch(error) { } catch(error) {
let reClaimedBounty = await bounty.claimed.call(); let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty); assert.isFalse(reClaimedBounty);
try { }
let withdraw = await bounty.withdrawPayments({from:researcher}); try {
done("should not come here") await bounty.withdrawPayments({from:researcher});
} catch (err) { assert.isTrue(false); // should never reach here
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()); } catch (err) {
done(); assert.equal(reward,
} web3.eth.getBalance(bounty.address).toNumber());
}//end of first try catch }
}); });
bounty.createTarget({from:researcher}); bounty.createTarget({from:researcher});
}) });
}) });
describe("Against broken contract", function(){ describe('Against broken contract', function(){
it("checkInvariant returns false", async function(){ it('claims reward', async function() {
let bounty = await InsecureTargetBounty.new();
let target = await bounty.createTarget();
let invariantCall = await bounty.checkInvariant.call();
assert.isFalse(invariantCall);
})
it("claims reward", async function(done){
let owner = accounts[0]; let owner = accounts[0];
let researcher = accounts[1]; let researcher = accounts[1];
let reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, 'ether');
let bounty = await InsecureTargetBounty.new(); let bounty = await InsecureTargetBounty.new();
let event = bounty.TargetCreated({}); let event = bounty.TargetCreated({});
event.watch(async function(err, result) { let watcher = async function(err, result) {
event.stopWatching(); event.stopWatching();
if (err) { throw err } if (err) { throw err; }
let targetAddress = result.args.createdAddress; let targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward); sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()); assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
let bountyClaim = await bounty.claim(targetAddress, {from:researcher}); await bounty.claim(targetAddress, {from:researcher});
let claim = await bounty.claimed.call(); let claim = await bounty.claimed.call();
assert.isTrue(claim); assert.isTrue(claim);
let payment = await bounty.withdrawPayments({from:researcher}); await bounty.withdrawPayments({from:researcher});
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber()); assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
done(); };
})
bounty.createTarget({from:researcher}); bounty.createTarget({from:researcher});
}) await awaitEvent(event, watcher);
}) });
});
}); });

View File

@ -1,3 +1,8 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var Claimable = artifacts.require('../contracts/ownership/Claimable.sol');
contract('Claimable', function(accounts) { contract('Claimable', function(accounts) {
let claimable; let claimable;
@ -5,34 +10,39 @@ contract('Claimable', function(accounts) {
claimable = await Claimable.new(); claimable = await Claimable.new();
}); });
it("should have an owner", async function() { it('should have an owner', async function() {
let owner = await claimable.owner(); let owner = await claimable.owner();
assert.isTrue(owner != 0); assert.isTrue(owner !== 0);
}); });
it("changes pendingOwner after transfer", async function() { it('changes pendingOwner after transfer', async function() {
let newOwner = accounts[1]; let newOwner = accounts[1];
let transfer = await claimable.transferOwnership(newOwner); await claimable.transferOwnership(newOwner);
let pendingOwner = await claimable.pendingOwner(); let pendingOwner = await claimable.pendingOwner();
assert.isTrue(pendingOwner === newOwner); assert.isTrue(pendingOwner === newOwner);
}); });
it("should prevent to claimOwnership from no pendingOwner", async function() { it('should prevent to claimOwnership from no pendingOwner', async function() {
let claimedOwner = await claimable.claimOwnership({from: accounts[2]}); try {
let owner = await claimable.owner(); await claimable.claimOwnership({from: accounts[2]});
} catch(error) {
assert.isTrue(owner != accounts[2]); assertJump(error);
}
}); });
it("should prevent non-owners from transfering", async function() { it('should prevent non-owners from transfering', async function() {
let transfer = await claimable.transferOwnership(accounts[2], {from: accounts[2]}); const other = accounts[2];
let pendingOwner = await claimable.pendingOwner(); const owner = await claimable.owner.call();
assert.isTrue(owner !== other);
assert.isFalse(pendingOwner === accounts[2]); try {
await claimable.transferOwnership(other, {from: other});
} catch(error) {
assertJump(error);
}
}); });
describe("after initiating a transfer", function () { describe('after initiating a transfer', function () {
let newOwner; let newOwner;
beforeEach(async function () { beforeEach(async function () {
@ -40,8 +50,8 @@ contract('Claimable', function(accounts) {
await claimable.transferOwnership(newOwner); await claimable.transferOwnership(newOwner);
}); });
it("changes allow pending owner to claim ownership", async function() { it('changes allow pending owner to claim ownership', async function() {
let claimedOwner = await claimable.claimOwnership({from: newOwner}) await claimable.claimOwnership({from: newOwner});
let owner = await claimable.owner(); let owner = await claimable.owner();
assert.isTrue(owner === newOwner); assert.isTrue(owner === newOwner);

30
test/Contactable.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var Contactable = artifacts.require('../contracts/ownership/Contactable.sol');
contract('Contactable', function(accounts) {
let contactable;
beforeEach(async function() {
contactable = await Contactable.new();
});
it('should have an empty contact info', async function() {
let info = await contactable.contactInformation();
assert.isTrue(info == "");
});
describe('after setting the contact information', function () {
let contactInfo = "contact information"
beforeEach(async function () {
await contactable.setContactInformation(contactInfo);
});
it('should return the setted contact information', async function() {
let info = await contactable.contactInformation();
assert.isTrue(info === contactInfo);
});
});
});

View File

@ -1,3 +1,8 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var DayLimitMock = artifacts.require('helpers/DayLimitMock.sol');
contract('DayLimit', function(accounts) { contract('DayLimit', function(accounts) {
it('should construct with the passed daily limit', async function() { it('should construct with the passed daily limit', async function() {
@ -28,9 +33,11 @@ contract('DayLimit', function(accounts) {
let spentToday = await dayLimit.spentToday(); let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8); assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3); try {
spentToday = await dayLimit.spentToday(); await dayLimit.attemptSpend(3);
assert.equal(spentToday, 8); } catch(error) {
assertJump(error);
}
}); });
it('should allow spending if daily limit is reached and then set higher', async function() { it('should allow spending if daily limit is reached and then set higher', async function() {
@ -41,7 +48,11 @@ contract('DayLimit', function(accounts) {
let spentToday = await dayLimit.spentToday(); let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8); assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3); try {
await dayLimit.attemptSpend(3);
} catch(error) {
assertJump(error);
}
spentToday = await dayLimit.spentToday(); spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8); assert.equal(spentToday, 8);
@ -59,7 +70,11 @@ contract('DayLimit', function(accounts) {
let spentToday = await dayLimit.spentToday(); let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8); assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3); try {
await dayLimit.attemptSpend(3);
} catch(error) {
assertJump(error);
}
spentToday = await dayLimit.spentToday(); spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8); assert.equal(spentToday, 8);

View File

@ -1,3 +1,7 @@
'use strict';
var DelayedClaimable = artifacts.require('../contracts/ownership/DelayedClaimable.sol');
contract('DelayedClaimable', function(accounts) { contract('DelayedClaimable', function(accounts) {
var delayedClaimable; var delayedClaimable;
@ -7,76 +11,58 @@ contract('DelayedClaimable', function(accounts) {
}); });
}); });
it("Changes pendingOwner after transfer succesfull", function(done) { it('can set claim blocks', async function() {
return delayedClaimable.transferOwnership(accounts[2]) await delayedClaimable.transferOwnership(accounts[2]);
.then(function(){ await delayedClaimable.setLimits(0, 1000);
return delayedClaimable.setClaimBlocks(1000,0); let end = await delayedClaimable.end();
}) assert.equal(end, 1000);
.then(function(){ let start = await delayedClaimable.start();
return delayedClaimable.claimBeforeBlock(); assert.equal(start, 0);
})
.then(function(claimBeforeBlock) {
assert.isTrue(claimBeforeBlock == 1000);
return delayedClaimable.claimAfterBlock();
})
.then(function(claimAfterBlock) {
assert.isTrue(claimAfterBlock == 0);
return delayedClaimable.pendingOwner();
})
.then(function(pendingOwner) {
assert.isTrue(pendingOwner === accounts[2]);
return delayedClaimable.claimOwnership({from: accounts[2]});
})
.then(function() {
return delayedClaimable.owner();
})
.then(function(owner) {
assert.isTrue(owner === accounts[2]);
})
.then(done);
}); });
it("Changes pendingOwner after transfer fails", function(done) { it('changes pendingOwner after transfer successful', async function() {
return delayedClaimable.transferOwnership(accounts[1]) await delayedClaimable.transferOwnership(accounts[2]);
.then(function(){ await delayedClaimable.setLimits(0, 1000);
return delayedClaimable.setClaimBlocks(11000,10000); let end = await delayedClaimable.end();
}) assert.equal(end, 1000);
.then(function(){ let start = await delayedClaimable.start();
return delayedClaimable.claimBeforeBlock(); assert.equal(start, 0);
}) let pendingOwner = await delayedClaimable.pendingOwner();
.then(function(claimBeforeBlock) { assert.equal(pendingOwner, accounts[2]);
assert.isTrue(claimBeforeBlock == 11000); await delayedClaimable.claimOwnership({from: accounts[2]});
return delayedClaimable.claimAfterBlock(); let owner = await delayedClaimable.owner();
}) assert.equal(owner, accounts[2]);
.then(function(claimAfterBlock) {
assert.isTrue(claimAfterBlock == 10000);
return delayedClaimable.pendingOwner();
})
.then(function(pendingOwner) {
assert.isTrue(pendingOwner === accounts[1]);
return delayedClaimable.claimOwnership({from: accounts[1]});
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error;
})
.then(function() {
return delayedClaimable.owner();
})
.then(function(owner) {
assert.isTrue(owner != accounts[1]);
})
.then(done);
}); });
it("Set claimBeforeBlock and claimAfterBlock invalid values fail", function(done) { it('changes pendingOwner after transfer fails', async function() {
return delayedClaimable.transferOwnership(accounts[1]) await delayedClaimable.transferOwnership(accounts[1]);
.then(function(){ await delayedClaimable.setLimits(100, 110);
return delayedClaimable.setClaimBlocks(1000,10000); let end = await delayedClaimable.end();
}) assert.equal(end, 110);
.catch(function(error) { let start = await delayedClaimable.start();
if (error.message.search('invalid JUMP') == -1) throw error; assert.equal(start, 100);
}) let pendingOwner = await delayedClaimable.pendingOwner();
.then(done); assert.equal(pendingOwner, accounts[1]);
var err = null;
try {
await delayedClaimable.claimOwnership({from: accounts[1]});
} catch (error) {
err = error;
}
assert.isFalse(err.message.search('invalid JUMP') === -1);
let owner = await delayedClaimable.owner();
assert.isTrue(owner !== accounts[1]);
});
it('set end and start invalid values fail', async function() {
await delayedClaimable.transferOwnership(accounts[1]);
var err = null;
try {
await delayedClaimable.setLimits(1001, 1000);
} catch (error) {
err = error;
}
assert.isFalse(err.message.search('invalid JUMP') === -1);
}); });
}); });

View File

@ -1,54 +1,16 @@
'use strict';
var Killable = artifacts.require('../contracts/lifecycle/Killable.sol');
require('./helpers/transactionMined.js');
contract('Killable', function(accounts) { contract('Killable', function(accounts) {
//from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6
web3.eth.getTransactionReceiptMined = function (txnHash, interval) {
var transactionReceiptAsync;
interval = interval ? interval : 500;
transactionReceiptAsync = function(txnHash, resolve, reject) {
try {
var receipt = web3.eth.getTransactionReceipt(txnHash);
if (receipt == null) {
setTimeout(function () {
transactionReceiptAsync(txnHash, resolve, reject);
}, interval);
} else {
resolve(receipt);
}
} catch(e) {
reject(e);
}
};
if (Array.isArray(txnHash)) { it('should send balance to owner after death', async function() {
var promises = []; let killable = await Killable.new({from: accounts[0], value: web3.toWei('10','ether')});
txnHash.forEach(function (oneTxHash) { let owner = await killable.owner();
promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval)); let initBalance = web3.eth.getBalance(owner);
}); await killable.kill({from: owner});
return Promise.all(promises); let newBalance = web3.eth.getBalance(owner);
} else {
return new Promise(function (resolve, reject) {
transactionReceiptAsync(txnHash, resolve, reject);
});
}
};
it("should send balance to owner after death", async function() {
let initBalance, newBalance, owner, address, killable, kBalance, txnHash, receiptMined;
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('50','ether')}, function(err, result) {
if(err)
console.log("ERROR:" + err);
else {
console.log(result);
}
});
killable = await Killable.new({from: accounts[0], value: web3.toWei('10','ether')});
owner = await killable.owner();
initBalance = web3.eth.getBalance(owner);
kBalance = web3.eth.getBalance(killable.address);
txnHash = await killable.kill({from: owner});
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
newBalance = web3.eth.getBalance(owner);
assert.isTrue(newBalance > initBalance); assert.isTrue(newBalance > initBalance);
}); });

View File

@ -1,3 +1,6 @@
'use strict';
var LimitBalanceMock = artifacts.require('helpers/LimitBalanceMock.sol');
const assertJump = require('./helpers/assertJump'); const assertJump = require('./helpers/assertJump');
contract('LimitBalance', function(accounts) { contract('LimitBalance', function(accounts) {
@ -9,48 +12,50 @@ contract('LimitBalance', function(accounts) {
let LIMIT = 1000; let LIMIT = 1000;
it("should expose limit", async function() { it('should expose limit', async function() {
let limit = await lb.limit(); let limit = await lb.limit();
assert.equal(limit, LIMIT); assert.equal(limit, LIMIT);
}); });
it("should allow sending below limit", async function() { it('should allow sending below limit', async function() {
let amount = 1; let amount = 1;
let limDeposit = await lb.limitedDeposit({value: amount}); await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount); assert.equal(web3.eth.getBalance(lb.address), amount);
}); });
it("shouldnt allow sending above limit", async function() { it('shouldnt allow sending above limit', async function() {
let amount = 1110; let amount = 1110;
try { try {
let limDeposit = await lb.limitedDeposit({value: amount}); await lb.limitedDeposit({value: amount});
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
it("should allow multiple sends below limit", async function() { it('should allow multiple sends below limit', async function() {
let amount = 500; let amount = 500;
let limDeposit = await lb.limitedDeposit({value: amount}); await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount); assert.equal(web3.eth.getBalance(lb.address), amount);
let limDeposit2 = await lb.limitedDeposit({value: amount}); await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount*2); assert.equal(web3.eth.getBalance(lb.address), amount*2);
}); });
it("shouldnt allow multiple sends above limit", async function() { it('shouldnt allow multiple sends above limit', async function() {
let amount = 500; let amount = 500;
let limDeposit = await lb.limitedDeposit({value: amount}); await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount); assert.equal(web3.eth.getBalance(lb.address), amount);
try { try {
await lb.limitedDeposit({value: amount+1}) await lb.limitedDeposit({value: amount+1});
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
}); });

View File

@ -1,43 +1,15 @@
'use strict';
var MultisigWalletMock = artifacts.require('./helpers/MultisigWalletMock.sol');
require('./helpers/transactionMined.js');
contract('MultisigWallet', function(accounts) { contract('MultisigWallet', function(accounts) {
//from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6 let shouldntFail = function(err) {
web3.eth.getTransactionReceiptMined = function (txnHash, interval) { assert.isFalse(!!err);
var transactionReceiptAsync; };
interval = interval ? interval : 500;
transactionReceiptAsync = function(txnHash, resolve, reject) {
try {
var receipt = web3.eth.getTransactionReceipt(txnHash);
if (receipt == null) {
setTimeout(function () {
transactionReceiptAsync(txnHash, resolve, reject);
}, interval);
} else {
resolve(receipt);
}
} catch(e) {
reject(e);
}
};
if (Array.isArray(txnHash)) {
var promises = [];
txnHash.forEach(function (oneTxHash) {
promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval));
});
return Promise.all(promises);
} else {
return new Promise(function (resolve, reject) {
transactionReceiptAsync(txnHash, resolve, reject);
});
}
};
it('should send balance to passed address upon death', async function() { it('should send balance to passed address upon death', async function() {
//Give account[0] 20 ether //Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10; let dailyLimit = 10;
let ownersRequired = 2; let ownersRequired = 2;
@ -54,8 +26,6 @@ contract('MultisigWallet', function(accounts) {
await wallet.kill(accounts[0], {data: hash}); await wallet.kill(accounts[0], {data: hash});
let txnHash = await wallet.kill(accounts[0], {from: accounts[1], data: hash}); let txnHash = await wallet.kill(accounts[0], {from: accounts[1], data: hash});
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Get balances of owner and wallet after kill function is complete, compare with previous values //Get balances of owner and wallet after kill function is complete, compare with previous values
let newOwnerBalance = web3.eth.getBalance(accounts[0]); let newOwnerBalance = web3.eth.getBalance(accounts[0]);
let newWalletBalance = web3.eth.getBalance(wallet.address); let newWalletBalance = web3.eth.getBalance(wallet.address);
@ -66,10 +36,7 @@ contract('MultisigWallet', function(accounts) {
it('should execute transaction if below daily limit', async function() { it('should execute transaction if below daily limit', async function() {
//Give account[0] 20 ether //Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10; let dailyLimit = 10;
let ownersRequired = 2; let ownersRequired = 2;
@ -82,7 +49,6 @@ contract('MultisigWallet', function(accounts) {
//Owner account0 commands wallet to send 9 wei to account2 //Owner account0 commands wallet to send 9 wei to account2
let txnHash = await wallet.execute(accounts[2], 9, hash); let txnHash = await wallet.execute(accounts[2], 9, hash);
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should have increased //Balance of account2 should have increased
let newAccountBalance = web3.eth.getBalance(accounts[2]); let newAccountBalance = web3.eth.getBalance(accounts[2]);
@ -91,10 +57,7 @@ contract('MultisigWallet', function(accounts) {
it('should prevent execution of transaction if above daily limit', async function() { it('should prevent execution of transaction if above daily limit', async function() {
//Give account[0] 20 ether //Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10; let dailyLimit = 10;
let ownersRequired = 2; let ownersRequired = 2;
@ -107,7 +70,6 @@ contract('MultisigWallet', function(accounts) {
//Owner account0 commands wallet to send 9 wei to account2 //Owner account0 commands wallet to send 9 wei to account2
let txnHash = await wallet.execute(accounts[2], 9, hash); let txnHash = await wallet.execute(accounts[2], 9, hash);
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should have increased //Balance of account2 should have increased
let newAccountBalance = web3.eth.getBalance(accounts[2]); let newAccountBalance = web3.eth.getBalance(accounts[2]);
@ -118,7 +80,6 @@ contract('MultisigWallet', function(accounts) {
//Owner account0 commands wallet to send 2 more wei to account2, going over the daily limit of 10 //Owner account0 commands wallet to send 2 more wei to account2, going over the daily limit of 10
txnHash = await wallet.execute(accounts[2], 2, hash); txnHash = await wallet.execute(accounts[2], 2, hash);
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should not change //Balance of account2 should not change
newAccountBalance = web3.eth.getBalance(accounts[2]); newAccountBalance = web3.eth.getBalance(accounts[2]);
@ -127,10 +88,7 @@ contract('MultisigWallet', function(accounts) {
it('should execute transaction if above daily limit and enough owners approve', async function() { it('should execute transaction if above daily limit and enough owners approve', async function() {
//Give account[0] 20 ether //Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10; let dailyLimit = 10;
let ownersRequired = 2; let ownersRequired = 2;
@ -143,7 +101,6 @@ contract('MultisigWallet', function(accounts) {
//Owner account0 commands wallet to send 11 wei to account2 //Owner account0 commands wallet to send 11 wei to account2
let txnHash = await wallet.execute(accounts[2], 11, hash); let txnHash = await wallet.execute(accounts[2], 11, hash);
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should not change //Balance of account2 should not change
let newAccountBalance = web3.eth.getBalance(accounts[2]); let newAccountBalance = web3.eth.getBalance(accounts[2]);
@ -153,7 +110,6 @@ contract('MultisigWallet', function(accounts) {
//Owner account1 commands wallet to send 11 wei to account2 //Owner account1 commands wallet to send 11 wei to account2
txnHash = await wallet.execute(accounts[2], 2, hash); txnHash = await wallet.execute(accounts[2], 2, hash);
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should change //Balance of account2 should change
newAccountBalance = web3.eth.getBalance(accounts[2]); newAccountBalance = web3.eth.getBalance(accounts[2]);

View File

@ -1,3 +1,8 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var Ownable = artifacts.require('../contracts/ownership/Ownable.sol');
contract('Ownable', function(accounts) { contract('Ownable', function(accounts) {
let ownable; let ownable;
@ -5,31 +10,33 @@ contract('Ownable', function(accounts) {
ownable = await Ownable.new(); ownable = await Ownable.new();
}); });
it("should have an owner", async function() { it('should have an owner', async function() {
let owner = await ownable.owner(); let owner = await ownable.owner();
assert.isTrue(owner != 0); assert.isTrue(owner !== 0);
}); });
it("changes owner after transfer", async function() { it('changes owner after transfer', async function() {
let other = accounts[1]; let other = accounts[1];
let transfer = await ownable.transferOwnership(other); await ownable.transferOwnership(other);
let owner = await ownable.owner(); let owner = await ownable.owner();
assert.isTrue(owner === other); assert.isTrue(owner === other);
}); });
it("should prevent non-owners from transfering", async function() { it('should prevent non-owners from transfering', async function() {
let other = accounts[2]; const other = accounts[2];
let transfer = await ownable.transferOwnership(other, {from: accounts[2]}); const owner = await ownable.owner.call();
let owner = await ownable.owner(); assert.isTrue(owner !== other);
try {
assert.isFalse(owner === other); await ownable.transferOwnership(other, {from: other});
} catch(error) {
assertJump(error);
}
}); });
it("should guard ownership against stuck state", async function() { it('should guard ownership against stuck state', async function() {
let ownable = Ownable.deployed();
let originalOwner = await ownable.owner(); let originalOwner = await ownable.owner();
let transfer = await ownable.transferOwnership(null, {from: originalOwner}); await ownable.transferOwnership(null, {from: originalOwner});
let newOwner = await ownable.owner(); let newOwner = await ownable.owner();
assert.equal(originalOwner, newOwner); assert.equal(originalOwner, newOwner);

56
test/Pausable.js Normal file
View File

@ -0,0 +1,56 @@
'use strict';
var PausableMock = artifacts.require('helpers/PausableMock.sol');
contract('Pausable', function(accounts) {
it('can perform normal process in non-emergency', async function() {
let Pausable = await PausableMock.new();
let count0 = await Pausable.count();
assert.equal(count0, 0);
await Pausable.normalProcess();
let count1 = await Pausable.count();
assert.equal(count1, 1);
});
it('can not perform normal process in emergency', async function() {
let Pausable = await PausableMock.new();
await Pausable.emergencyStop();
let count0 = await Pausable.count();
assert.equal(count0, 0);
await Pausable.normalProcess();
let count1 = await Pausable.count();
assert.equal(count1, 0);
});
it('can not take drastic measure in non-emergency', async function() {
let Pausable = await PausableMock.new();
await Pausable.drasticMeasure();
let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
assert.isFalse(drasticMeasureTaken);
});
it('can take a drastic measure in an emergency', async function() {
let Pausable = await PausableMock.new();
await Pausable.emergencyStop();
await Pausable.drasticMeasure();
let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
assert.isTrue(drasticMeasureTaken);
});
it('should resume allowing normal process after emergency is over', async function() {
let Pausable = await PausableMock.new();
await Pausable.emergencyStop();
await Pausable.release();
await Pausable.normalProcess();
let count0 = await Pausable.count();
assert.equal(count0, 1);
});
});

View File

@ -1,3 +1,5 @@
var PullPaymentMock = artifacts.require("./helpers/PullPaymentMock.sol");
contract('PullPayment', function(accounts) { contract('PullPayment', function(accounts) {
it("can't call asyncSend externally", async function() { it("can't call asyncSend externally", async function() {

View File

@ -1,4 +1,5 @@
const assertJump = require('./helpers/assertJump'); const assertJump = require('./helpers/assertJump');
var SafeMathMock = artifacts.require("./helpers/SafeMathMock.sol");
contract('SafeMath', function(accounts) { contract('SafeMath', function(accounts) {
@ -40,8 +41,9 @@ contract('SafeMath', function(accounts) {
try { try {
let subtract = await safeMath.subtract(a, b); let subtract = await safeMath.subtract(a, b);
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
it("should throw an error on addition overflow", async function() { it("should throw an error on addition overflow", async function() {
@ -50,8 +52,9 @@ contract('SafeMath', function(accounts) {
try { try {
let add = await safeMath.add(a, b); let add = await safeMath.add(a, b);
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
it("should throw an error on multiplication overflow", async function() { it("should throw an error on multiplication overflow", async function() {
@ -60,8 +63,9 @@ contract('SafeMath', function(accounts) {
try { try {
let multiply = await safeMath.multiply(a, b); let multiply = await safeMath.multiply(a, b);
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
}); });

View File

@ -1,3 +1,5 @@
var ShareableMock = artifacts.require("./helpers/ShareableMock.sol");
contract('Shareable', function(accounts) { contract('Shareable', function(accounts) {
it('should construct with correct owners and number of sigs required', async function() { it('should construct with correct owners and number of sigs required', async function() {

View File

@ -1,4 +1,5 @@
const assertJump = require('./helpers/assertJump'); const assertJump = require('./helpers/assertJump');
var StandardTokenMock = artifacts.require("./helpers/StandardTokenMock.sol");
contract('StandardToken', function(accounts) { contract('StandardToken', function(accounts) {
@ -32,8 +33,9 @@ contract('StandardToken', function(accounts) {
try { try {
let transfer = await token.transfer(accounts[1], 101); let transfer = await token.transfer(accounts[1], 101);
} catch(error) { } catch(error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
it("should return correct balances after transfering from another account", async function() { it("should return correct balances after transfering from another account", async function() {
@ -57,8 +59,9 @@ contract('StandardToken', function(accounts) {
try { try {
let transfer = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]}); let transfer = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
} catch (error) { } catch (error) {
assertJump(error); return assertJump(error);
} }
assert.fail('should have thrown before');
}); });
}); });

View File

@ -1,52 +0,0 @@
contract('Stoppable', function(accounts) {
it("can perform normal process in non-emergency", async function() {
let stoppable = await StoppableMock.new();
let count0 = await stoppable.count();
assert.equal(count0, 0);
let normalProcess = await stoppable.normalProcess();
let count1 = await stoppable.count();
assert.equal(count1, 1);
});
it("can not perform normal process in emergency", async function() {
let stoppable = await StoppableMock.new();
let emergencyStop = await stoppable.emergencyStop();
let count0 = await stoppable.count();
assert.equal(count0, 0);
let normalProcess = await stoppable.normalProcess();
let count1 = await stoppable.count();
assert.equal(count1, 0);
});
it("can not take drastic measure in non-emergency", async function() {
let stoppable = await StoppableMock.new();
let drasticMeasure = await stoppable.drasticMeasure();
let drasticMeasureTaken = await stoppable.drasticMeasureTaken();
assert.isFalse(drasticMeasureTaken);
});
it("can take a drastic measure in an emergency", async function() {
let stoppable = await StoppableMock.new();
let emergencyStop = await stoppable.emergencyStop();
let drasticMeasure = await stoppable.drasticMeasure();
let drasticMeasureTaken = await stoppable.drasticMeasureTaken();
assert.isTrue(drasticMeasureTaken);
});
it("should resume allowing normal process after emergency is over", async function() {
let stoppable = await StoppableMock.new();
let emergencyStop = await stoppable.emergencyStop();
let release = await stoppable.release();
let normalProcess = await stoppable.normalProcess();
let count0 = await stoppable.count();
assert.equal(count0, 1);
});
});

82
test/VestedToken.js Normal file
View File

@ -0,0 +1,82 @@
const assertJump = require('./helpers/assertJump');
const timer = require('./helpers/timer');
var VestedTokenMock = artifacts.require("./helpers/VestedTokenMock.sol");
contract('VestedToken', function(accounts) {
let token = null
let now = 0
const tokenAmount = 50
const granter = accounts[0]
const receiver = accounts[1]
beforeEach(async () => {
token = await VestedTokenMock.new(granter, 100);
now = web3.eth.getBlock(web3.eth.blockNumber).timestamp;
})
it('granter can grant tokens without vesting', async () => {
await token.transfer(receiver, tokenAmount, { from: granter })
assert.equal(await token.balanceOf(receiver), tokenAmount);
assert.equal(await token.transferableTokens(receiver, now), tokenAmount);
})
describe('getting a token grant', async () => {
const cliff = 10000
const vesting = 20000 // seconds
beforeEach(async () => {
await token.grantVestedTokens(receiver, tokenAmount, now, now + cliff, now + vesting, { from: granter })
})
it('tokens are received', async () => {
assert.equal(await token.balanceOf(receiver), tokenAmount);
})
it('has 0 transferable tokens before cliff', async () => {
assert.equal(await token.transferableTokens(receiver, now), 0);
})
it('all tokens are transferable after vesting', async () => {
assert.equal(await token.transferableTokens(receiver, now + vesting + 1), tokenAmount);
})
it('throws when trying to transfer non vested tokens', async () => {
try {
await token.transfer(accounts[7], 1, { from: receiver })
} catch(error) {
return assertJump(error);
}
assert.fail('should have thrown before');
})
it('can be revoked by granter', async () => {
await token.revokeTokenGrant(receiver, 0, { from: granter });
assert.equal(await token.balanceOf(receiver), 0);
assert.equal(await token.balanceOf(granter), 100);
})
it('cannot be revoked by non granter', async () => {
try {
await token.revokeTokenGrant(receiver, 0, { from: accounts[3] });
} catch(error) {
return assertJump(error);
}
assert.fail('should have thrown before');
})
it('can be revoked by granter and non vested tokens are returned', async () => {
await timer(cliff);
await token.revokeTokenGrant(receiver, 0, { from: granter });
assert.equal(await token.balanceOf(receiver), tokenAmount * cliff / vesting);
})
it('can transfer all tokens after vesting ends', async () => {
await timer(vesting + 1);
await token.transfer(accounts[7], tokenAmount, { from: receiver })
assert.equal(await token.balanceOf(accounts[7]), tokenAmount);
})
})
});

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import '../token/BasicToken.sol'; import '../../contracts/token/BasicToken.sol';
// mock class using BasicToken // mock class using BasicToken

View File

@ -1,5 +1,5 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "../DayLimit.sol"; import "../../contracts/DayLimit.sol";
contract DayLimitMock is DayLimit { contract DayLimitMock is DayLimit {
uint public totalSpending; uint public totalSpending;

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import {Bounty, Target} from "../Bounty.sol"; import {Bounty, Target} from "../../contracts/Bounty.sol";
contract InsecureTargetMock is Target { contract InsecureTargetMock is Target {

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import '../LimitBalance.sol'; import '../../contracts/LimitBalance.sol';
// mock class using LimitBalance // mock class using LimitBalance

View File

@ -1,5 +1,5 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "../MultisigWallet.sol"; import "../../contracts/MultisigWallet.sol";
contract MultisigWalletMock is MultisigWallet { contract MultisigWalletMock is MultisigWallet {
uint public totalSpending; uint public totalSpending;

View File

@ -1,15 +1,15 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import '../Stoppable.sol'; import '../../contracts/lifecycle/Pausable.sol';
// mock class using Stoppable // mock class using Pausable
contract StoppableMock is Stoppable { contract PausableMock is Pausable {
bool public drasticMeasureTaken; bool public drasticMeasureTaken;
uint public count; uint public count;
function StoppableMock() { function PausableMock() {
drasticMeasureTaken = false; drasticMeasureTaken = false;
count = 0; count = 0;
} }

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import '../PullPayment.sol'; import '../../contracts/payment/PullPayment.sol';
// mock class using PullPayment // mock class using PullPayment

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import '../SafeMath.sol'; import '../../contracts/SafeMath.sol';
contract SafeMathMock is SafeMath { contract SafeMathMock is SafeMath {

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import {Bounty, Target} from "../Bounty.sol"; import {Bounty, Target} from "../../contracts/Bounty.sol";
contract SecureTargetMock is Target { contract SecureTargetMock is Target {

View File

@ -1,5 +1,5 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import "../Shareable.sol"; import "../../contracts/ownership/Shareable.sol";
contract ShareableMock is Shareable { contract ShareableMock is Shareable {

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.4; pragma solidity ^0.4.8;
import '../token/StandardToken.sol'; import '../../contracts/token/StandardToken.sol';
// mock class using StandardToken // mock class using StandardToken

View File

@ -0,0 +1,11 @@
pragma solidity ^0.4.8;
import '../../contracts/token/VestedToken.sol';
// mock class using StandardToken
contract VestedTokenMock is VestedToken {
function VestedTokenMock(address initialAccount, uint initialBalance) {
balances[initialAccount] = initialBalance;
totalSupply = initialBalance;
}
}

15
test/helpers/timer.js Normal file
View File

@ -0,0 +1,15 @@
// timer for tests specific to testrpc
module.exports = s => {
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: '2.0',
method: 'evm_increaseTime',
params: [s], // 60 seaconds, may need to be hex, I forget
id: new Date().getTime() // Id of the request; anything works, really
}, function(err) {
if (err) return reject(err);
resolve();
});
//setTimeout(() => resolve(), s * 1000 + 600) // 600ms breathing room for testrpc to sync
});
};

View File

@ -0,0 +1,34 @@
'use strict';
//from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6
module.export = web3.eth.transactionMined = function (txnHash, interval) {
var transactionReceiptAsync;
interval = interval ? interval : 500;
transactionReceiptAsync = function(txnHash, resolve, reject) {
try {
var receipt = web3.eth.getTransactionReceipt(txnHash);
if (receipt === null) {
setTimeout(function () {
transactionReceiptAsync(txnHash, resolve, reject);
}, interval);
} else {
resolve(receipt);
}
} catch(e) {
reject(e);
}
};
if (Array.isArray(txnHash)) {
var promises = [];
txnHash.forEach(function (oneTxHash) {
promises.push(
web3.eth.getTransactionReceiptMined(oneTxHash, interval));
});
return Promise.all(promises);
} else {
return new Promise(function (resolve, reject) {
transactionReceiptAsync(txnHash, resolve, reject);
});
}
};

View File

@ -1,6 +1,12 @@
require('babel-register');
require('babel-polyfill');
module.exports = { module.exports = {
rpc: { networks: {
host: "localhost", development: {
port: 8545 host: "localhost",
port: 8545,
network_id: "*"
}
} }
}; };