Compare commits

...

58 Commits

Author SHA1 Message Date
ac6f9288a8 v1.0.1 2017-01-05 15:36:20 -03:00
d54e56f970 Merge pull request #78 from AugustoL/master
DelayedClaimable contract extension of Claimable fix #64
2016-12-30 12:04:05 -03:00
d974924045 Added claimAfterBlock variable and tests changes to support changes 2016-12-29 16:12:32 -03:00
e310e6728b Merge remote-tracking branch 'OpenZeppelin/master' 2016-12-29 15:54:19 -03:00
74adaeedf2 Merge pull request #119 from adklempner/refactorDayLimit
Decouple Shareable from DayLimit
2016-12-27 13:12:51 -03:00
a5602e7e7b Make resetSpentToday and setDailyLimit internal functions 2016-12-26 13:59:21 -08:00
2a75a2c9f4 Merge pull request #102 from adklempner/test/MultisigWallet
Create tests for MultisigWallet
2016-12-26 15:25:40 -03:00
6fa9e048da Merge pull request #123 from demibrener/readme
fix link
2016-12-23 14:34:51 -03:00
c21006e6d3 fix link 2016-12-23 14:29:17 -03:00
aa438fa053 Merge pull request #122 from demibrener/readme
add introductory guide to smart contracts
2016-12-23 14:26:04 -03:00
1529e19b09 introductory guide to smart contracts 2016-12-23 14:19:20 -03:00
813c1ff0cd Add payable to MultisigWalletMock() 2016-12-22 18:34:05 -08:00
8aa06bda38 Create tests for MultisigWallet 2016-12-22 18:34:05 -08:00
36fa2a72cf Decouple Shareable from DayLimit 2016-12-21 16:43:39 -08:00
11cf9f8752 Merge pull request #118 from mediachain/master
Add payable modifier to bid functions in examples
2016-12-21 16:51:40 -03:00
9c81dd29bb Fix example contracts 2016-12-21 14:26:32 -05:00
bb22cd62ac Merge pull request #100 from adklempner/shareable-test
Create tests for Shareable
2016-12-20 18:22:23 -03:00
e5eaa919b1 Make isOwner() const. Add necessary toString() calls in tests. 2016-12-20 13:02:24 -08:00
ba37d91878 Merge pull request #101 from adklempner/test/DayLimit
Create tests for DayLimit
2016-12-20 11:26:13 -03:00
e8459148a8 Remove unnecessary toString calls 2016-12-19 21:52:37 -08:00
f292643543 DelayedClaimable tests changed to work with rename of transfer() to transferOwnership() 2016-12-19 19:15:22 -03:00
80cc1808fc Merge remote-tracking branch 'OpenZeppelin/master' 2016-12-19 19:11:31 -03:00
d38941062b Merge pull request #114 from maraoz/2-lines
add 2 lines between top level definitions
2016-12-19 18:10:27 -03:00
f79ad4e2f6 add 2 lines between top level definitions 2016-12-19 17:43:02 -03:00
5567e73730 Merge pull request #113 from demibrener/documentation
update readme
2016-12-19 16:36:02 -03:00
2ddfb80a5e update readme 2016-12-19 16:29:02 -03:00
6a64d98a34 Merge pull request #112 from demibrener/documentation
Documentation
2016-12-19 14:32:58 -03:00
b1b66ce165 remove DS_Store 2016-12-19 14:29:48 -03:00
e6c50b3318 mend 2016-12-19 14:02:39 -03:00
260edd8702 Merge pull request #110 from FundRequest/master
resolve conflict between Ownable and ERC20 token.
2016-12-19 13:56:52 -03:00
920e3ec940 improve docs 2016-12-19 12:59:36 -03:00
2299cd1c39 remove unneeded files 2016-12-19 12:10:00 -03:00
31a01ebde1 developer resources update 2016-12-19 11:56:34 -03:00
ec07dce76f writing docs 2016-12-19 11:56:34 -03:00
8ba40d066f building docs 2016-12-19 11:56:34 -03:00
ed6df57f88 updated documentation for claimable and ownable interfaces 2016-12-16 10:15:08 +01:00
02f7f4c49d Fix invalid character on Claimable 2016-12-15 21:21:54 -03:00
77e732218e Also change claimable to avoid conflicts with ERC20 tokens 2016-12-15 15:59:25 +01:00
41fdd5d4c6 fixes gh-109: resolve conflict between Ownable and ERC20 token. 2016-12-15 15:32:08 +01:00
e859d53b6c Merge pull request #106 from FundRequest/master
fixes gh-99 PullPaymentMock needed a payable constructor
2016-12-13 16:35:49 +02:00
9d56414b75 fixed the code 2016-12-13 12:00:48 +01:00
fe184fdbdf Merge branch 'master' of https://github.com/FundRequest/zeppelin-solidity 2016-12-13 11:59:59 +01:00
c15fa95bea fixes gh-99 PullPaymentMock needed a payable constructor 2016-12-13 11:59:52 +01:00
0e58f7b992 fixes gh-99 PullPaymentMock needed a payable constructor 2016-12-13 11:53:31 +01:00
423cec41e3 claimBefore modifier removed on DelayedClaimable contract 2016-12-07 18:32:42 -03:00
c390e94da6 Create tests for DayLimit 2016-12-06 11:45:02 -08:00
0ec09f4131 Create tests for Shareable 2016-12-06 11:33:39 -08:00
ab93a8e3e8 Merge pull request #97 from sogoiii/async_await_tests
issue #57: Async await tests
2016-12-03 04:27:32 +02:00
c7eb6736ee DelayedClaimable contract with test added
Renamed DelayedClaimable function and modifier
2016-12-02 18:48:53 -03:00
42ccbb2276 Sync with v1.0.0 of zeppelin-solidity 2016-12-02 17:46:42 -03:00
38545eb648 fix: spelling in a test 2016-12-01 20:20:44 -08:00
eb41a81faa fix: made code cleaner. added helper. removed done from most tests. 2016-12-01 08:42:05 -08:00
688106e9c3 fix: made all the tests consistent. now with done. 2016-11-30 13:24:02 -08:00
f2142545c7 modified test to use async await pattern. 2016-11-27 12:09:48 -08:00
c920c40507 fix Build badge 2016-11-24 14:38:39 -08:00
475cb5dc1f Renamed DelayedClaimable function and modifier 2016-11-19 12:13:09 -03:00
85a4013f49 Using modifier to check block on delayedClaimable, tests fixed 2016-11-18 20:32:22 -03:00
0aa4d02044 DelayedClaimable contract with test added 2016-11-16 19:49:57 -03:00
65 changed files with 1542 additions and 905 deletions

3
.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["es2015", "stage-2", "stage-3"]
}

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
*.swo
node_modules/
build/
.DS_Store/

230
README.md
View File

@ -1,12 +1,14 @@
# Zeppelin Solidity
[![NPM Package](https://img.shields.io/npm/v/zeppelin-solidity.svg?style=flat-square)](https://www.npmjs.org/package/zeppelin-solidity)
[![Build Status](https://img.shields.io/travis/OpenZeppelin/zeppelin-solidity.svg?branch=master&style=flat-square)](https://travis-ci.org/bitpay/bitcore-lib)
[![Build Status](https://img.shields.io/travis/OpenZeppelin/zeppelin-solidity.svg?branch=master&style=flat-square)](https://travis-ci.org/OpenZeppelin/zeppelin-solidity)
Zeppelin is a library for writing secure Smart Contracts on Ethereum.
Zeppelin is a library for writing secure [Smart Contracts](https://en.wikipedia.org/wiki/Smart_contract) on Ethereum.
With Zeppelin, you can build distributed applications, protocols and organizations:
- using common contract security patterns (See [Onward with Ethereum Smart Contract Security](https://medium.com/bitcorps-blog/onward-with-ethereum-smart-contract-security-97a827e47702#.y3kvdetbz))
- in the Solidity language.
- in the [Solidity language](http://solidity.readthedocs.io/en/develop/).
> NOTE: New to smart contract development? Check our [introductory guide](https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05#.cox40d2ut).
## Getting Started
@ -53,228 +55,12 @@ Zeppelin is meant to provide secure, tested and community-audited code, but plea
If you find a security issue, please email [security@openzeppelin.org](mailto:security@openzeppelin.org).
## Contracts
### Ownable
Base contract with an owner.
#### Ownable( )
Sets the address of the creator of the contract as the owner.
#### modifier onlyOwner( )
Prevents function from running if it is called by anyone other than the owner.
#### transfer(address newOwner) onlyOwner
Transfers ownership of the contract to the passed address.
---
### Stoppable
Base contract that provides an emergency stop mechanism.
Inherits from contract Ownable.
#### emergencyStop( ) external onlyOwner
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.
#### modifier stopInEmergency
Prevents function from running if stop mechanism is activated.
#### modifier onlyInEmergency
Only runs if stop mechanism is activated.
#### release( ) external onlyOwner onlyInEmergency
Deactivates the stop mechanism.
---
### Killable
Base contract that can be killed by owner.
Inherits from contract Ownable.
#### kill( ) onlyOwner
Destroys the contract and sends funds back to the owner.
___
### Claimable
Extension for the Ownable contract, where the ownership needs to be claimed
#### transfer(address newOwner) onlyOwner
Sets the passed address as the pending owner.
#### modifier onlyPendingOwner
Function only runs if called by pending owner.
#### claimOwnership( ) onlyPendingOwner
Completes transfer of ownership by setting pending owner as the new owner.
___
### Migrations
Base contract that allows for a new instance of itself to be created at a different address.
Inherits from contract Ownable.
#### upgrade(address new_address) onlyOwner
Creates a new instance of the contract at the passed address.
#### setCompleted(uint completed) onlyOwner
Sets the last time that a migration was completed.
___
### SafeMath
Provides functions of mathematical operations with safety checks.
#### assert(bool assertion) internal
Throws an error if the passed result is false. Used in this contract by checking mathematical expressions.
#### 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.
#### safeSub(uint a, unit b) internal returns (uint)
Checks that b is not greater than a before subtracting.
#### safeAdd(unit a, unit b) internal returns (uint)
Checks that the result is greater than both a and b.
___
### LimitBalance
Base contract that provides mechanism for limiting the amount of funds a contract can hold.
#### LimitBalance(unit _limit)
Constructor takes an unisgned integer and sets it as the limit of funds this contract can hold.
#### modifier limitedPayable()
Throws an error if this contract's balance is already above the limit.
___
### PullPayment
Base contract supporting async send for pull payments.
Inherit from this contract and use asyncSend instead of send.
#### asyncSend(address dest, uint amount) internal
Adds sent amount to available balance that payee can pull from this contract, called by payer.
#### withdrawPayments( )
Sends designated balance to payee calling the contract. Throws error if designated balance is 0, if contract does not hold enough funds ot pay the payee, or if the send transaction is not successful.
___
### StandardToken
Based on code by FirstBlood: [FirstBloodToken.sol]
Inherits from contract SafeMath. Implementation of abstract contract ERC20 (see https://github.com/ethereum/EIPs/issues/20)
[FirstBloodToken.sol]: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
#### approve(address _spender, uint _value) returns (bool success)
Sets the amount of the sender's token balance that the passed address is approved to use.
###allowance(address _owner, address _spender) constant returns (uint remaining)
Returns the approved amount of the owner's balance that the spender can use.
###balanceOf(address _owner) constant returns (uint balance)
Returns the token balance of the passed address.
###transferFrom(address _from, address _to, uint _value) returns (bool success)
Transfers tokens from an account that the sender is approved to transfer from. Amount must not be greater than the approved amount or the account's balance.
###function transfer(address _to, uint _value) returns (bool success)
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
___
### BasicToken
Simpler version of StandardToken, with no allowances
#### balanceOf(address _owner) constant returns (uint balance)
Returns the token balance of the passed address.
###function transfer(address _to, uint _value) returns (bool success)
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
___
### CrowdsaleToken
Simple ERC20 Token example, with crowdsale token creation.
Inherits from contract StandardToken.
#### createTokens(address recipient) payable
Creates tokens based on message value and credits to the recipient.
#### getPrice() constant returns (uint result)
Returns the amount of tokens per 1 ether.
___
### Bounty
To create a bounty for your contract, inherit from the base `Bounty` contract and provide an implementation for `deployContract()` returning the new contract address.
```
import {Bounty, Target} from "./zeppelin/Bounty.sol";
import "./YourContract.sol";
contract YourBounty is Bounty {
function deployContract() internal returns(address) {
return new YourContract()
}
}
```
Next, implement invariant logic into your smart contract.
Your main contract should inherit from the Target class and implement the checkInvariant method. This is a function that should check everything your contract assumes to be true all the time. If this function returns false, it means your contract was broken in some way and is in an inconsistent state. This is what security researchers will try to acomplish when trying to get the bounty.
At contracts/YourContract.sol
```
import {Bounty, Target} from "./zeppelin/Bounty.sol";
contract YourContract is Target {
function checkInvariant() returns(bool) {
// Implement your logic to make sure that none of the invariants are broken.
}
}
```
Next, deploy your bounty contract along with your main contract to the network.
At `migrations/2_deploy_contracts.js`
```
module.exports = function(deployer) {
deployer.deploy(YourContract);
deployer.deploy(YourBounty);
};
```
Next, add a reward to the bounty contract
After deploying the contract, send reward funds into the bounty contract.
From `truffle console`
```
bounty = YourBounty.deployed();
address = 0xb9f68f96cde3b895cc9f6b14b856081b41cb96f1; // your account address
reward = 5; // reward to pay to a researcher who breaks your contract
web3.eth.sendTransaction({
from: address,
to: bounty.address,
value: web3.toWei(reward, "ether")
})
```
If researchers break the contract, they can claim their reward.
For each researcher who wants to hack the contract and claims the reward, refer to our [test](./test/Bounty.js) for the detail.
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, kill the contract so that all the rewards go back to the owner.
```
bounty.kill();
```
## More Developer Resources
## Developer Resources
Building a distributed application, protocol or organization with Zeppelin?
- Read documentation: http://zeppelin-solidity.readthedocs.io/en/latest/
- Ask for help and follow progress at: https://zeppelin-slackin.herokuapp.com/
Interested in contributing to Zeppelin?

View File

@ -1,14 +1,13 @@
pragma solidity ^0.4.0;
import './Ownable.sol';
/*
* Claimable
*
* Extension for the Ownable contract, where the ownership needs to be claimed. This allows the new owner to accept the transfer.
*
* Extension for the Ownable contract, where the ownership needs to be claimed. This allows the new owner to accept the transfer.
*/
contract Claimable is Ownable {
address public pendingOwner;
@ -18,7 +17,7 @@ contract Claimable is Ownable {
_;
}
function transfer(address newOwner) onlyOwner {
function transferOwnership(address newOwner) onlyOwner {
pendingOwner = newOwner;
}

View File

@ -11,7 +11,7 @@ import './Shareable.sol';
* on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
* uses is specified in the modifier.
*/
contract DayLimit is Shareable {
contract DayLimit {
// FIELDS
uint public dailyLimit;
@ -38,13 +38,13 @@ contract DayLimit is Shareable {
// METHODS
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
// (re)sets the daily limit. doesn't alter the amount already spent today.
function _setDailyLimit(uint _newLimit) internal {
dailyLimit = _newLimit;
}
// resets the amount already spent today. needs many of the owners to confirm
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
// resets the amount already spent today.
function _resetSpentToday() internal {
spentToday = 0;
}
@ -53,14 +53,14 @@ contract DayLimit is Shareable {
// 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.
function underLimit(uint _value) internal onlyOwner returns (bool) {
function underLimit(uint _value) internal returns (bool) {
// reset the spend limit if we're on a different day to last time.
if (today() > lastDay) {
spentToday = 0;
lastDay = today();
}
// check to see if there's enough left - if so, subtract and return true.
// overflow protection // dailyLimit check
// overflow protection // dailyLimit check
if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) {
spentToday += _value;
return true;

View File

@ -0,0 +1,30 @@
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,13 @@
pragma solidity ^0.4.4;
/**
* LimitBalance
* Simple contract to limit the balance of child contract.
* Note this doesn't prevent other contracts to send funds
* by using selfdestruct(address);
* See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account
*/
contract LimitBalance {
uint public limit;

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import './Ownable.sol';
contract Migrations is Ownable {
uint public lastCompletedMigration;

View File

@ -83,6 +83,14 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
}
}
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
_setDailyLimit(_newLimit);
}
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
_resetSpentToday();
}
// INTERNAL METHODS

View File

@ -14,12 +14,12 @@ contract Ownable {
owner = msg.sender;
}
modifier onlyOwner() {
modifier onlyOwner() {
if (msg.sender == owner)
_;
}
function transfer(address newOwner) onlyOwner {
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) owner = newOwner;
}

View File

@ -4,7 +4,7 @@ pragma solidity ^0.4.4;
/*
* Shareable
*
* Based on https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol
* Based on https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol
*
* inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a single, or, crucially, each of a number of, designated owners.
*
@ -98,7 +98,7 @@ contract Shareable {
return address(owners[ownerIndex + 1]);
}
function isOwner(address _addr) returns (bool) {
function isOwner(address _addr) constant returns (bool) {
return ownerIndex[uint(_addr)] > 0;
}
@ -162,4 +162,3 @@ contract Shareable {
}
}

View File

@ -1,8 +1,10 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
// UNSAFE CODE, DO NOT USE!
// UNSAFE CODE, DO NOT USE!
contract BadArrayUse is PullPayment {
address[] employees;

View File

@ -1,6 +1,7 @@
pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
// UNSAFE CODE, DO NOT USE!
contract BadFailEarly {
uint constant DEFAULT_SALARY = 50000;

View File

@ -1,12 +1,13 @@
pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
// UNSAFE CODE, DO NOT USE!
contract BadPushPayments {
address highestBidder;
uint highestBid;
function bid() {
function bid() payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
contract GoodArrayUse is PullPayment {
address[] employees;
mapping(address => uint) bonuses;

View File

@ -1,5 +1,6 @@
pragma solidity ^0.4.4;
contract GoodFailEarly {
uint constant DEFAULT_SALARY = 50000;

View File

@ -1,10 +1,12 @@
pragma solidity ^0.4.4;
contract GoodPullPayments {
address highestBidder;
uint highestBid;
mapping(address => uint) refunds;
function bid() external {
function bid() external payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {

View File

@ -1,5 +1,6 @@
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

View File

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

View File

@ -1,13 +1,15 @@
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 stopInEmergency {
function bid() external payable stopInEmergency {
if (msg.value <= highestBid) throw;
if (highestBidder != 0) {

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import '../token/BasicToken.sol';
// mock class using BasicToken
contract BasicTokenMock is BasicToken {

View File

@ -0,0 +1,23 @@
pragma solidity ^0.4.4;
import "../DayLimit.sol";
contract DayLimitMock is DayLimit {
uint public totalSpending;
function DayLimitMock(uint _value) DayLimit(_value) {
totalSpending = 0;
}
function attemptSpend(uint _value) external limitedDaily(_value) {
totalSpending += _value;
}
function setDailyLimit(uint _newLimit) external {
_setDailyLimit(_newLimit);
}
function resetSpentToday() external {
_resetSpentToday();
}
}

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import '../LimitBalance.sol';
// mock class using LimitBalance
contract LimitBalanceMock is LimitBalance(1000) {

View File

@ -0,0 +1,12 @@
pragma solidity ^0.4.4;
import "../MultisigWallet.sol";
contract MultisigWalletMock is MultisigWallet {
uint public totalSpending;
function MultisigWalletMock(address[] _owners, uint _required, uint _daylimit)
MultisigWallet(_owners, _required, _daylimit) payable { }
function changeOwner(address _from, address _to) external { }
}

View File

@ -1,8 +1,14 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
// mock class using PullPayment
contract PullPaymentMock is PullPayment {
function PullPaymentMock() payable { }
// test helper function to call asyncSend
function callSend(address dest, uint amount) {
asyncSend(dest, amount);

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import '../SafeMath.sol';
contract SafeMathMock is SafeMath {
uint public result;

View File

@ -0,0 +1,16 @@
pragma solidity ^0.4.4;
import "../Shareable.sol";
contract ShareableMock is Shareable {
uint public count = 0;
function ShareableMock(address[] _owners, uint _required) Shareable(_owners, _required) {
}
function increaseCount(bytes32 action) onlymanyowners(action) {
count = count + 1;
}
}

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import '../token/StandardToken.sol';
// mock class using StandardToken
contract StandardTokenMock is StandardToken {

View File

@ -1,6 +1,9 @@
pragma solidity ^0.4.4;
import '../Stoppable.sol';
// mock class using Stoppable
contract StoppableMock is Stoppable {
bool public drasticMeasureTaken;

View File

@ -1,10 +1,12 @@
pragma solidity ^0.4.4;
import './ERC20.sol';
import '../SafeMath.sol';
/**
* ERC20 token
* Standard ERC20 token
*
* https://github.com/ethereum/EIPs/issues/20
* Based on code by FirstBlood:

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = zeppelin-solidity
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,12 @@
BasicToken
=============================================
Simpler version of StandardToken, with no allowances
balanceOf(address _owner) constant returns (uint balance)
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Returns the token balance of the passed address.
function transfer(address _to, uint _value) returns (bool success)
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Transfers tokens from sender's account. Amount must not be greater than sender's balance.

60
docs/source/bounty.rst Normal file
View File

@ -0,0 +1,60 @@
Bounty
=============================================
To create a bounty for your contract, inherit from the base `Bounty` contract and provide an implementation for ```deployContract()``` returning the new contract address.::
import {Bounty, Target} from "./zeppelin/Bounty.sol";
import "./YourContract.sol";
contract YourBounty is Bounty {
function deployContract() internal returns(address) {
return new YourContract()
}
}
Next, implement invariant logic into your smart contract.
Your main contract should inherit from the Target class and implement the checkInvariant method. This is a function that should check everything your contract assumes to be true all the time. If this function returns false, it means your contract was broken in some way and is in an inconsistent state. This is what security researchers will try to acomplish when trying to get the bounty.
At contracts/YourContract.sol::
import {Bounty, Target} from "./zeppelin/Bounty.sol";
contract YourContract is Target {
function checkInvariant() returns(bool) {
// Implement your logic to make sure that none of the invariants are broken.
}
}
Next, deploy your bounty contract along with your main contract to the network.
At ```migrations/2_deploy_contracts.js```::
module.exports = function(deployer) {
deployer.deploy(YourContract);
deployer.deploy(YourBounty);
};
Next, add a reward to the bounty contract
After deploying the contract, send reward funds into the bounty contract.
From ```truffle console```::
bounty = YourBounty.deployed();
address = 0xb9f68f96cde3b895cc9f6b14b856081b41cb96f1; // your account address
reward = 5; // reward to pay to a researcher who breaks your contract
web3.eth.sendTransaction({
from: address,
to: bounty.address,
value: web3.toWei(reward, "ether")
})
If researchers break the contract, they can claim their reward.
For each researcher who wants to hack the contract and claims the reward, refer to our `Test <https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/Bounty.js/>`_ for the detail.
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, kill the contract so that all the rewards go back to the owner.::
bounty.kill();

20
docs/source/claimable.rst Normal file
View File

@ -0,0 +1,20 @@
Claimable
=============================================
Extension for the Ownable contract, where the ownership needs to be claimed
transfer(address newOwner) onlyOwner
""""""""""""""""""""""""""""""""""""""
Sets the passed address as the pending owner.
modifier onlyPendingOwner
""""""""""""""""""""""""""""""""""""""
Function only runs if called by pending owner.
claimOwnership( ) onlyPendingOwner
""""""""""""""""""""""""""""""""""""""
Completes transfer of ownership by setting pending owner as the new owner.

160
docs/source/conf.py Normal file
View File

@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
#
# zeppelin-solidity documentation build configuration file, created by
# sphinx-quickstart on Tue Dec 13 11:35:05 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'zeppelin-solidity'
copyright = u'2016, Smart Contract Solutions, Inc'
author = u'Smart Contract Solutions, Inc'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'1.0.0'
# The full version, including alpha/beta/rc tags.
release = u'1.0.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'zeppelin-soliditydoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'zeppelin-solidity.tex', u'zeppelin-solidity Documentation',
u'Zeppelin', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'zeppelin-solidity', u'zeppelin-solidity Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'zeppelin-solidity', u'zeppelin-solidity Documentation',
author, 'zeppelin-solidity', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -0,0 +1,4 @@
Common Contract Security Patterns
=============================================
Zeppelin smart contracts are developed using industry standard contract security patterns and best practices. To learn more, please see `Onward with Ethereum Smart Contract Security <https://medium.com/zeppelin-blog/onward-with-ethereum-smart-contract-security-97a827e47702#.ybvzeyz0k/>`_.

View File

@ -0,0 +1,14 @@
CrowdsaleToken
=============================================
Simple ERC20 Token example, with crowdsale token creation.
Inherits from contract StandardToken.
createTokens(address recipient) payable
"""""""""""""""""""""""""""""""""""""""""
Creates tokens based on message value and credits to the recipient.
getPrice() constant returns (uint result)
"""""""""""""""""""""""""""""""""""""""""
Returns the amount of tokens per 1 ether.

View File

@ -0,0 +1,12 @@
Developer Resources
=============================================
Building a distributed application, protocol or organization with Zeppelin?
Ask for help and follow progress at: https://zeppelin-slackin.herokuapp.com/
Interested in contributing to Zeppelin?
* Framework proposal and roadmap: https://medium.com/zeppelin-blog/zeppelin-framework-proposal-and-development-roadmap-fdfa9a3a32ab#.iain47pak
* Issue tracker: https://github.com/OpenZeppelin/zeppelin-solidity/issues
* Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md

View File

@ -0,0 +1,36 @@
Getting Started
=============================================
Zeppelin integrates with `Truffle <https://github.com/ConsenSys/truffle/>`_, an Ethereum development environment. Please install Truffle and initialize your project with ``truffle init``::
npm install -g truffle
mkdir myproject && cd myproject
truffle init
To install the Zeppelin library, run::
npm i zeppelin-solidity
After that, you'll get all the library's contracts in the contracts/zeppelin folder. You can use the contracts in the library like so::
import "./zeppelin/Ownable.sol";
contract MyContract is Ownable {
...
}
.. epigraph::
NOTE: The current distribution channel is npm, which is not ideal. `We're looking into providing a better tool for code distribution <https://github.com/OpenZeppelin/zeppelin-solidity/issues/13/>`_ , and ideas are welcome.
Truffle Beta Support
""""""""""""""""""""""""
We also support Truffle Beta npm integration. If you're using Truffle Beta, the contracts in ``node_modules`` will be enough, so feel free to delete the copies at your ``contracts`` folder. If you're using Truffle Beta, you can use Zeppelin contracts like so::
import "zeppelin-solidity/contracts/Ownable.sol";
contract MyContract is Ownable {
...
}
For more info see the `Truffle Beta package management tutorial <http://truffleframework.com/tutorials/package-management/>`_.

47
docs/source/index.rst Normal file
View File

@ -0,0 +1,47 @@
.. zeppelin-solidity documentation master file, created by
sphinx-quickstart on Tue Dec 13 11:35:05 2016.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Zeppelin-Solidity
=============================================
Zeppelin is a library for writing secure Smart Contracts on Ethereum.
With Zeppelin, you can build distributed applications, protocols and organizations:
* using :doc:`contract-security-patterns`
* in the `Solidity language <https://solidity.readthedocs.io/en/develop/>`_.
The code is open-source, and `available on github <https://github.com/OpenZeppelin/zeppelin-solidity>`_.
.. toctree::
:maxdepth: 2
getting-started
.. toctree::
:maxdepth: 2
:caption: Smart Contracts
ownable
stoppable
killable
claimable
migrations
safemath
limitbalance
pullpayment
standardtoken
basictoken
crowdsaletoken
bounty
.. toctree::
:maxdepth: 2
:caption: Developer Resources
contract-security-patterns
developer-resources
license

11
docs/source/killable.rst Normal file
View File

@ -0,0 +1,11 @@
Killable
=============================================
Base contract that can be killed by owner.
Inherits from contract Ownable.
kill( ) onlyOwner
"""""""""""""""""""
Destroys the contract and sends funds back to the owner.

23
docs/source/license.rst Normal file
View File

@ -0,0 +1,23 @@
The MIT License (MIT)
=============================================
Copyright (c) 2016 Smart Contract Solutions, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,12 @@
LimitBalance
=============================================
Base contract that provides mechanism for limiting the amount of funds a contract can hold.
LimitBalance(unit _limit)
""""""""""""""""""""""""""""
Constructor takes an unisgned integer and sets it as the limit of funds this contract can hold.
modifier limitedPayable()
""""""""""""""""""""""""""""
Throws an error if this contract's balance is already above the limit.

View File

@ -0,0 +1,16 @@
Migrations
=============================================
Base contract that allows for a new instance of itself to be created at a different address.
Inherits from contract Ownable.
upgrade(address new_address) onlyOwner
""""""""""""""""""""""""""""""""""""""""
Creates a new instance of the contract at the passed address.
setCompleted(uint completed) onlyOwner**
""""""""""""""""""""""""""""""""""""""""
Sets the last time that a migration was completed.

16
docs/source/ownable.rst Normal file
View File

@ -0,0 +1,16 @@
Ownable
=============================================
Base contract with an owner.
Ownable( )
""""""""""""""""""""""""""""""""""""""
Sets the address of the creator of the contract as the owner.
modifier onlyOwner( )
""""""""""""""""""""""""""""""""""""""
Prevents function from running if it is called by anyone other than the owner.
transfer(address newOwner) onlyOwner
""""""""""""""""""""""""""""""""""""""
Transfers ownership of the contract to the passed address.

View File

@ -0,0 +1,12 @@
PullPayment
=============================================
Base contract supporting async send for pull payments. Inherit from this contract and use asyncSend instead of send.
asyncSend(address dest, uint amount) internal
"""""""""""""""""""""""""""""""""""""""""""""""
Adds sent amount to available balance that payee can pull from this contract, called by payer.
withdrawPayments( )
"""""""""""""""""""""""""""""""""""""""""""""""
Sends designated balance to payee calling the contract. Throws error if designated balance is 0, if contract does not hold enough funds ot pay the payee, or if the send transaction is not successful.

24
docs/source/safemath.rst Normal file
View File

@ -0,0 +1,24 @@
SafeMath
=============================================
Provides functions of mathematical operations with safety checks.
assert(bool assertion) internal
"""""""""""""""""""""""""""""""""""""""""""""""""
Throws an error if the passed result is false. Used in this contract by checking mathematical expressions.
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.
safeSub(uint a, unit b) internal returns (uint)
"""""""""""""""""""""""""""""""""""""""""""""""""
Checks that b is not greater than a before subtracting.
safeAdd(unit a, unit b) internal returns (uint)
"""""""""""""""""""""""""""""""""""""""""""""""""
Checks that the result is greater than both a and b.

View File

@ -0,0 +1,26 @@
StandardToken
=============================================
Based on code by FirstBlood: `Link FirstBloodToken.sol <https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol/>`_
Inherits from contract SafeMath. Implementation of abstract contract ERC20 (see https://github.com/ethereum/EIPs/issues/20)
approve(address _spender, uint _value) returns (bool success)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Sets the amount of the sender's token balance that the passed address is approved to use.
allowance(address _owner, address _spender) constant returns (uint remaining)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Returns the approved amount of the owner's balance that the spender can use.
balanceOf(address _owner) constant returns (uint balance)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Returns the token balance of the passed address.
transferFrom(address _from, address _to, uint _value) returns (bool success)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Transfers tokens from an account that the sender is approved to transfer from. Amount must not be greater than the approved amount or the account's balance.
function transfer(address _to, uint _value) returns (bool success)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Transfers tokens from sender's account. Amount must not be greater than sender's balance.

26
docs/source/stoppable.rst Normal file
View File

@ -0,0 +1,26 @@
Stoppable
=============================================
Base contract that provides an emergency stop mechanism.
Inherits from contract Ownable.
emergencyStop( ) external onlyOwner
"""""""""""""""""""""""""""""""""""""
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.
modifier stopInEmergency
"""""""""""""""""""""""""""""""""""""
Prevents function from running if stop mechanism is activated.
modifier onlyInEmergency
"""""""""""""""""""""""""""""""""""""
Only runs if stop mechanism is activated.
release( ) external onlyOwner onlyInEmergency
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Deactivates the stop mechanism.

View File

@ -1,9 +1,12 @@
{
"name": "zeppelin-solidity",
"version": "1.0.0",
"version": "1.0.1",
"description": "Secure Smart Contract library for Solidity",
"main": "truffle.js",
"devDependencies": {
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0",
"ethereumjs-testrpc": "^3.0.2",
"truffle": "^2.1.1"
},

View File

@ -1,49 +1,32 @@
const assertJump = require('./helpers/assertJump');
contract('BasicToken', function(accounts) {
it("should return the correct totalSupply after construction", function(done) {
return BasicTokenMock.new(accounts[0], 100)
.then(function(token) {
return token.totalSupply();
})
.then(function(totalSupply) {
assert.equal(totalSupply, 100);
})
.then(done);
it("should return the correct totalSupply after construction", async function() {
let token = await BasicTokenMock.new(accounts[0], 100);
let totalSupply = await token.totalSupply();
assert.equal(totalSupply, 100);
})
it("should return correct balances after transfer", function(done) {
var token;
return BasicTokenMock.new(accounts[0], 100)
.then(function(_token) {
token = _token;
return token.transfer(accounts[1], 100);
})
.then(function() {
return token.balanceOf(accounts[0]);
})
.then(function(balance) {
assert.equal(balance, 0);
})
.then(function() {
return token.balanceOf(accounts[1]);
})
.then(function(balance) {
assert.equal(balance, 100);
})
.then(done);
it("should return correct balances after transfer", async function(){
let token = await BasicTokenMock.new(accounts[0], 100);
let transfer = await token.transfer(accounts[1], 100);
let firstAccountBalance = await token.balanceOf(accounts[0]);
assert.equal(firstAccountBalance, 0);
let secondAccountBalance = await token.balanceOf(accounts[1]);
assert.equal(secondAccountBalance, 100);
});
it("should throw an error when trying to transfer more than balance", function(done) {
var token;
return BasicTokenMock.new(accounts[0], 100)
.then(function(_token) {
token = _token;
return token.transfer(accounts[1], 101);
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
it("should throw an error when trying to transfer more than balance", async function() {
let token = await BasicTokenMock.new(accounts[0], 100);
try {
let transfer = await token.transfer(accounts[1], 101);
} catch(error) {
assertJump(error);
}
});
});

View File

@ -1,4 +1,4 @@
var sendReward = function(sender, receiver, value){
let sendReward = function(sender, receiver, value){
web3.eth.sendTransaction({
from:sender,
to:receiver,
@ -8,134 +8,108 @@ var sendReward = function(sender, receiver, value){
contract('Bounty', function(accounts) {
it("sets reward", function(done){
var owner = accounts[0];
var reward = web3.toWei(1, "ether");
it("sets reward", async function(){
let owner = accounts[0];
let reward = web3.toWei(1, "ether");
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
SecureTargetBounty.new().
then(function(bounty){
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
}).
then(done);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
})
it("empties itself when killed", function(done){
var owner = accounts[0];
var reward = web3.toWei(1, "ether");
var bounty;
it("empties itself when killed", async function(){
let owner = accounts[0];
let reward = web3.toWei(1, "ether");
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
SecureTargetBounty.new().
then(function(_bounty){
bounty = _bounty;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
return bounty.kill()
}).
then(function(){
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber())
}).
then(done);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
await bounty.kill();
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
})
describe("Against secure contract", function(){
it("checkInvariant returns true", function(done){
var bounty;
SecureTargetBounty.new().
then(function(_bounty) {
bounty = _bounty;
return bounty.createTarget();
}).
then(function() {
return bounty.checkInvariant.call()
}).
then(function(result) {
assert.isTrue(result);
}).
then(done);
it("checkInvariant returns true", 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", function(done){
var owner = accounts[0];
var researcher = accounts[1];
var reward = web3.toWei(1, "ether");
it("cannot claim reward", async function(done){
let owner = accounts[0];
let researcher = accounts[1];
let reward = web3.toWei(1, "ether");
let bounty = await SecureTargetBounty.new();
let event = bounty.TargetCreated({});
SecureTargetBounty.new().
then(function(bounty) {
var event = bounty.TargetCreated({});
event.watch(function(err, result) {
event.stopWatching();
if (err) { throw err }
var targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
bounty.claim(targetAddress, {from:researcher}).
then(function(){ throw("should not come here")}).
catch(function() {
return bounty.claimed.call();
}).
then(function(result) {
assert.isFalse(result);
bounty.withdrawPayments({from:researcher}).
then(function(){ throw("should not come here")}).
catch(function() {
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
done();
})
})
})
bounty.createTarget({from:researcher});
})
event.watch(async function(err, result) {
event.stopWatching();
if (err) { throw err }
var targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
try {
let tmpClain = await bounty.claim(targetAddress, {from:researcher});
done("should not come here");
} catch(error) {
let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty);
try {
let withdraw = await bounty.withdrawPayments({from:researcher});
done("should not come here")
} catch (err) {
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
done();
}
}//end of first try catch
});
bounty.createTarget({from:researcher});
})
})
describe("Against broken contract", function(){
it("checkInvariant returns false", function(done){
var bounty;
it("checkInvariant returns false", async function(){
let bounty = await InsecureTargetBounty.new();
let target = await bounty.createTarget();
let invariantCall = await bounty.checkInvariant.call();
InsecureTargetBounty.new().
then(function(_bounty) {
bounty = _bounty;
return bounty.createTarget();
}).
then(function() {
return bounty.checkInvariant.call()
}).
then(function(result) {
assert.isFalse(result);
}).
then(done);
assert.isFalse(invariantCall);
})
it("claims reward", function(done){
var owner = accounts[0];
var researcher = accounts[1];
var reward = web3.toWei(1, "ether");
it("claims reward", async function(done){
let owner = accounts[0];
let researcher = accounts[1];
let reward = web3.toWei(1, "ether");
let bounty = await InsecureTargetBounty.new();
let event = bounty.TargetCreated({});
InsecureTargetBounty.new().
then(function(bounty) {
var event = bounty.TargetCreated({});
event.watch(function(err, result) {
event.stopWatching();
if (err) { throw err }
var targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
bounty.claim(targetAddress, {from:researcher}).
then(function() {
return bounty.claimed.call();
}).
then(function(result) {
assert.isTrue(result);
return bounty.withdrawPayments({from:researcher})
}).
then(function() {
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber())
}).then(done);
})
bounty.createTarget({from:researcher});
})
event.watch(async function(err, result) {
event.stopWatching();
if (err) { throw err }
let targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
let bountyClaim = await bounty.claim(targetAddress, {from:researcher});
let claim = await bounty.claimed.call();
assert.isTrue(claim);
let payment = await bounty.withdrawPayments({from:researcher});
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
done();
})
bounty.createTarget({from:researcher});
})
})
});

View File

@ -1,71 +1,50 @@
contract('Claimable', function(accounts) {
var claimable;
let claimable;
beforeEach(function() {
return Claimable.new().then(function(deployed) {
claimable = deployed;
});
beforeEach(async function() {
claimable = await Claimable.new();
});
it("should have an owner", function(done) {
return claimable.owner()
.then(function(owner) {
assert.isTrue(owner != 0);
})
.then(done)
it("should have an owner", async function() {
let owner = await claimable.owner();
assert.isTrue(owner != 0);
});
it("changes pendingOwner after transfer", function(done) {
var newOwner = accounts[1];
return claimable.transfer(newOwner)
.then(function() {
return claimable.pendingOwner();
})
.then(function(pendingOwner) {
assert.isTrue(pendingOwner === newOwner);
})
.then(done)
it("changes pendingOwner after transfer", async function() {
let newOwner = accounts[1];
let transfer = await claimable.transferOwnership(newOwner);
let pendingOwner = await claimable.pendingOwner();
assert.isTrue(pendingOwner === newOwner);
});
it("should prevent to claimOwnership from no pendingOwner", function(done) {
return claimable.claimOwnership({from: accounts[2]})
.then(function() {
return claimable.owner();
})
.then(function(owner) {
assert.isTrue(owner != accounts[2]);
})
.then(done)
it("should prevent to claimOwnership from no pendingOwner", async function() {
let claimedOwner = await claimable.claimOwnership({from: accounts[2]});
let owner = await claimable.owner();
assert.isTrue(owner != accounts[2]);
});
it("should prevent non-owners from transfering" ,function(done) {
return claimable.transfer(accounts[2], {from: accounts[2]})
.then(function() {
return claimable.pendingOwner();
})
.then(function(pendingOwner) {
assert.isFalse(pendingOwner === accounts[2]);
})
.then(done)
it("should prevent non-owners from transfering", async function() {
let transfer = await claimable.transferOwnership(accounts[2], {from: accounts[2]});
let pendingOwner = await claimable.pendingOwner();
assert.isFalse(pendingOwner === accounts[2]);
});
describe("after initiating a transfer", function () {
var newOwner;
let newOwner;
beforeEach(function () {
beforeEach(async function () {
newOwner = accounts[1];
return claimable.transfer(newOwner);
await claimable.transferOwnership(newOwner);
});
it("changes allow pending owner to claim ownership", function(done) {
return claimable.claimOwnership({from: newOwner})
.then(function() {
return claimable.owner();
})
.then(function(owner) {
assert.isTrue(owner === newOwner);
})
.then(done)
it("changes allow pending owner to claim ownership", async function() {
let claimedOwner = await claimable.claimOwnership({from: newOwner})
let owner = await claimable.owner();
assert.isTrue(owner === newOwner);
});
});
});

72
test/DayLimit.js Normal file
View File

@ -0,0 +1,72 @@
contract('DayLimit', function(accounts) {
it('should construct with the passed daily limit', async function() {
let initLimit = 10;
let dayLimit = await DayLimitMock.new(initLimit);
let dailyLimit = await dayLimit.dailyLimit();
assert.equal(initLimit, dailyLimit);
});
it('should be able to spend if daily limit is not reached', async function() {
let limit = 10;
let dayLimit = await DayLimitMock.new(limit);
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(2);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 10);
});
it('should prevent spending if daily limit is reached', async function() {
let limit = 10;
let dayLimit = await DayLimitMock.new(limit);
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
});
it('should allow spending if daily limit is reached and then set higher', async function() {
let limit = 10;
let dayLimit = await DayLimitMock.new(limit);
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.setDailyLimit(15);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 11);
});
it('should allow spending if daily limit is reached and then amount spent is reset', async function() {
let limit = 10;
let dayLimit = await DayLimitMock.new(limit);
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.resetSpentToday(15);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 3);
});
});

82
test/DelayedClaimble.js Normal file
View File

@ -0,0 +1,82 @@
contract('DelayedClaimable', function(accounts) {
var delayedClaimable;
beforeEach(function() {
return DelayedClaimable.new().then(function(deployed) {
delayedClaimable = deployed;
});
});
it("Changes pendingOwner after transfer succesfull", function(done) {
return delayedClaimable.transferOwnership(accounts[2])
.then(function(){
return delayedClaimable.setClaimBlocks(1000,0);
})
.then(function(){
return delayedClaimable.claimBeforeBlock();
})
.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) {
return delayedClaimable.transferOwnership(accounts[1])
.then(function(){
return delayedClaimable.setClaimBlocks(11000,10000);
})
.then(function(){
return delayedClaimable.claimBeforeBlock();
})
.then(function(claimBeforeBlock) {
assert.isTrue(claimBeforeBlock == 11000);
return delayedClaimable.claimAfterBlock();
})
.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) {
return delayedClaimable.transferOwnership(accounts[1])
.then(function(){
return delayedClaimable.setClaimBlocks(1000,10000);
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error;
})
.then(done);
});
});

View File

@ -32,38 +32,25 @@ contract('Killable', function(accounts) {
}
};
it("should send balance to owner after death", function(done) {
var initBalance, newBalance, owner, address, killable, kBalance;
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);
}
})
return Killable.new({from: accounts[0], value: web3.toWei('10','ether')})
.then(function(_killable) {
killable = _killable;
return killable.owner();
})
.then(function(_owner) {
owner = _owner;
initBalance = web3.eth.getBalance(owner);
kBalance = web3.eth.getBalance(killable.address);
})
.then(function() {
return killable.kill({from: owner});
})
.then(function (txnHash) {
return web3.eth.getTransactionReceiptMined(txnHash);
})
.then(function() {
newBalance = web3.eth.getBalance(owner);
})
.then(function() {
assert.isTrue(newBalance > initBalance);
})
.then(done);
});
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);
});
});

View File

@ -1,64 +1,56 @@
const assertJump = require('./helpers/assertJump');
contract('LimitBalance', function(accounts) {
var lb;
let lb;
beforeEach(function() {
return LimitBalanceMock.new().then(function(deployed) {
lb = deployed;
});
beforeEach(async function() {
lb = await LimitBalanceMock.new();
});
var LIMIT = 1000;
let LIMIT = 1000;
it("should expose limit", function(done) {
return lb.limit()
.then(function(limit) {
assert.equal(limit, LIMIT);
})
.then(done)
it("should expose limit", async function() {
let limit = await lb.limit();
assert.equal(limit, LIMIT);
});
it("should allow sending below limit", function(done) {
var amount = 1;
return lb.limitedDeposit({value: amount})
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount);
})
.then(done)
it("should allow sending below limit", async function() {
let amount = 1;
let limDeposit = await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount);
});
it("shouldnt allow sending above limit", function(done) {
var amount = 1100;
return lb.limitedDeposit({value: amount})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done)
it("shouldnt allow sending above limit", async function() {
let amount = 1110;
try {
let limDeposit = await lb.limitedDeposit({value: amount});
} catch(error) {
assertJump(error);
}
});
it("should allow multiple sends below limit", function(done) {
var amount = 500;
return lb.limitedDeposit({value: amount})
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount);
return lb.limitedDeposit({value: amount})
})
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount*2);
})
.then(done)
it("should allow multiple sends below limit", async function() {
let amount = 500;
let limDeposit = await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount);
let limDeposit2 = await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount*2);
});
it("shouldnt allow multiple sends above limit", function(done) {
var amount = 500;
return lb.limitedDeposit({value: amount})
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount);
return lb.limitedDeposit({value: amount+1})
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error;
})
.then(done)
it("shouldnt allow multiple sends above limit", async function() {
let amount = 500;
let limDeposit = await lb.limitedDeposit({value: amount});
assert.equal(web3.eth.getBalance(lb.address), amount);
try {
await lb.limitedDeposit({value: amount+1})
} catch(error) {
assertJump(error);
}
});
});

163
test/MultisigWallet.js Normal file
View File

@ -0,0 +1,163 @@
contract('MultisigWallet', 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)) {
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() {
//Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10;
let ownersRequired = 2;
//Create MultisigWallet contract with 10 ether
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
//Get balances of owner and wallet after wallet creation.
let ownerBalance = web3.eth.getBalance(accounts[0]);
let walletBalance = web3.eth.getBalance(wallet.address);
let hash = 1234;
//Call kill function from two different owner accounts, satisfying owners required
await wallet.kill(accounts[0], {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
let newOwnerBalance = web3.eth.getBalance(accounts[0]);
let newWalletBalance = web3.eth.getBalance(wallet.address);
assert.isTrue(newOwnerBalance > ownerBalance);
assert.isTrue(newWalletBalance < walletBalance);
});
it('should execute transaction if below daily limit', async function() {
//Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10;
let ownersRequired = 2;
//Create MultisigWallet contract with 10 ether
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
let accountBalance = web3.eth.getBalance(accounts[2]);
let hash = 1234;
//Owner account0 commands wallet to send 9 wei to account2
let txnHash = await wallet.execute(accounts[2], 9, hash);
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should have increased
let newAccountBalance = web3.eth.getBalance(accounts[2]);
assert.isTrue(newAccountBalance > accountBalance);
});
it('should prevent execution of transaction if above daily limit', async function() {
//Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10;
let ownersRequired = 2;
//Create MultisigWallet contract with 10 ether
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
let accountBalance = web3.eth.getBalance(accounts[2]);
let hash = 1234;
//Owner account0 commands wallet to send 9 wei to account2
let txnHash = await wallet.execute(accounts[2], 9, hash);
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should have increased
let newAccountBalance = web3.eth.getBalance(accounts[2]);
assert.isTrue(newAccountBalance > accountBalance);
accountBalance = newAccountBalance;
hash = 4567;
//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);
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should not change
newAccountBalance = web3.eth.getBalance(accounts[2]);
assert.equal(newAccountBalance.toString(), accountBalance.toString());
});
it('should execute transaction if above daily limit and enough owners approve', async function() {
//Give account[0] 20 ether
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
if(err)
console.log("ERROR:" + err);
});
let dailyLimit = 10;
let ownersRequired = 2;
//Create MultisigWallet contract with 10 ether
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
let accountBalance = web3.eth.getBalance(accounts[2]);
let hash = 1234;
//Owner account0 commands wallet to send 11 wei to account2
let txnHash = await wallet.execute(accounts[2], 11, hash);
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should not change
let newAccountBalance = web3.eth.getBalance(accounts[2]);
assert.equal(newAccountBalance.toString(), accountBalance.toString());
accountBalance = newAccountBalance;
//Owner account1 commands wallet to send 11 wei to account2
txnHash = await wallet.execute(accounts[2], 2, hash);
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
//Balance of account2 should change
newAccountBalance = web3.eth.getBalance(accounts[2]);
assert.isTrue(newAccountBalance > accountBalance);
});
});

View File

@ -1,58 +1,38 @@
contract('Ownable', function(accounts) {
var ownable;
let ownable;
beforeEach(function() {
return Ownable.new().then(function(deployed) {
ownable = deployed;
});
beforeEach(async function() {
ownable = await Ownable.new();
});
it("should have an owner", function(done) {
return ownable.owner()
.then(function(owner) {
assert.isTrue(owner != 0);
})
.then(done)
it("should have an owner", async function() {
let owner = await ownable.owner();
assert.isTrue(owner != 0);
});
it("changes owner after transfer", function(done) {
var other = accounts[1];
return ownable.transfer(other)
.then(function() {
return ownable.owner();
})
.then(function(owner) {
assert.isTrue(owner === other);
})
.then(done)
it("changes owner after transfer", async function() {
let other = accounts[1];
let transfer = await ownable.transferOwnership(other);
let owner = await ownable.owner();
assert.isTrue(owner === other);
});
it("should prevent non-owners from transfering" ,function(done) {
var other = accounts[2];
return ownable.transfer(other, {from: accounts[2]})
.then(function() {
return ownable.owner();
})
.then(function(owner) {
assert.isFalse(owner === other);
})
.then(done)
it("should prevent non-owners from transfering", async function() {
let other = accounts[2];
let transfer = await ownable.transferOwnership(other, {from: accounts[2]});
let owner = await ownable.owner();
assert.isFalse(owner === other);
});
it("should guard ownership against stuck state" ,function(done) {
var ownable = Ownable.deployed();
it("should guard ownership against stuck state", async function() {
let ownable = Ownable.deployed();
let originalOwner = await ownable.owner();
let transfer = await ownable.transferOwnership(null, {from: originalOwner});
let newOwner = await ownable.owner();
return ownable.owner()
.then(function (originalOwner) {
return ownable.transfer(null, {from: originalOwner})
.then(function() {
return ownable.owner();
})
.then(function(newOwner) {
assert.equal(originalOwner, newOwner);
})
.then(done);
});
assert.equal(originalOwner, newOwner);
});
});

View File

@ -1,102 +1,57 @@
contract('PullPayment', function(accounts) {
it("can't call asyncSend externally", function(done) {
return PullPaymentMock.new()
.then(function(ppc) {
assert.isUndefined(ppc.asyncSend);
})
.then(done);
it("can't call asyncSend externally", async function() {
let ppc = await PullPaymentMock.new();
assert.isUndefined(ppc.asyncSend);
});
it("can record an async payment correctly", function(done) {
var ppce;
var AMOUNT = 100;
return PullPaymentMock.new()
.then(function(_ppce) {
ppce = _ppce;
ppce.callSend(accounts[0], AMOUNT)
})
.then(function() {
return ppce.payments(accounts[0]);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, AMOUNT);
})
.then(done);
it("can record an async payment correctly", async function() {
let AMOUNT = 100;
let ppce = await PullPaymentMock.new();
let callSend = await ppce.callSend(accounts[0], AMOUNT);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
assert.equal(paymentsToAccount0, AMOUNT);
});
it("can add multiple balances on one account", function(done) {
var ppce;
return PullPaymentMock.new()
.then(function(_ppce) {
ppce = _ppce;
return ppce.callSend(accounts[0], 200)
})
.then(function() {
return ppce.callSend(accounts[0], 300)
})
.then(function() {
return ppce.payments(accounts[0]);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, 500);
})
.then(done);
it("can add multiple balances on one account", async function() {
let ppce = await PullPaymentMock.new();
let call1 = await ppce.callSend(accounts[0], 200);
let call2 = await ppce.callSend(accounts[0], 300);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
assert.equal(paymentsToAccount0, 500);
});
it("can add balances on multiple accounts", function(done) {
var ppce;
return PullPaymentMock.new()
.then(function(_ppce) {
ppce = _ppce;
return ppce.callSend(accounts[0], 200)
})
.then(function() {
return ppce.callSend(accounts[1], 300)
})
.then(function() {
return ppce.payments(accounts[0]);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, 200);
})
.then(function() {
return ppce.payments(accounts[1]);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, 300);
})
.then(done);
it("can add balances on multiple accounts", async function() {
let ppce = await PullPaymentMock.new();
let call1 = await ppce.callSend(accounts[0], 200);
let call2 = await ppce.callSend(accounts[1], 300);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
assert.equal(paymentsToAccount0, 200);
let paymentsToAccount1 = await ppce.payments(accounts[1]);
assert.equal(paymentsToAccount1, 300);
});
it("can withdraw payment", function(done) {
var ppce;
var AMOUNT = 17*1e18;
var payee = accounts[1];
var initialBalance = web3.eth.getBalance(payee);
return PullPaymentMock.new({value: AMOUNT})
.then(function(_ppce) {
ppce = _ppce;
return ppce.callSend(payee, AMOUNT);
})
.then(function() {
return ppce.payments(payee);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, AMOUNT);
})
.then(function() {
return ppce.withdrawPayments({from: payee});
})
.then(function() {
return ppce.payments(payee);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, 0);
var balance = web3.eth.getBalance(payee);
assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
})
.then(done);
it("can withdraw payment", async function() {
let AMOUNT = 17*1e18;
let payee = accounts[1];
let initialBalance = web3.eth.getBalance(payee);
let ppce = await PullPaymentMock.new({value: AMOUNT});
let call1 = await ppce.callSend(payee, AMOUNT);
let payment1 = await ppce.payments(payee);
assert.equal(payment1, AMOUNT);
let withdraw = await ppce.withdrawPayments({from: payee});
let payment2 = await ppce.payments(payee);
assert.equal(payment2, 0);
let balance = web3.eth.getBalance(payee);
assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
});
});

View File

@ -1,82 +1,67 @@
const assertJump = require('./helpers/assertJump');
contract('SafeMath', function(accounts) {
var safeMath;
let safeMath;
before(function() {
return SafeMathMock.new()
.then(function(_safeMath) {
safeMath = _safeMath;
});
before(async function() {
safeMath = await SafeMathMock.new();
});
it("multiplies correctly", function(done) {
var a = 5678;
var b = 1234;
return safeMath.multiply(a, b)
.then(function() {
return safeMath.result();
})
.then(function(result) {
assert.equal(result, a*b);
})
.then(done);
it("multiplies correctly", async function() {
let a = 5678;
let b = 1234;
let mult = await safeMath.multiply(a, b);
let result = await safeMath.result();
assert.equal(result, a*b);
});
it("adds correctly", function(done) {
var a = 5678;
var b = 1234;
return safeMath.add(a, b)
.then(function() {
return safeMath.result();
})
.then(function(result) {
assert.equal(result, a+b);
})
.then(done);
it("adds correctly", async function() {
let a = 5678;
let b = 1234;
let add = await safeMath.add(a, b);
let result = await safeMath.result();
assert.equal(result, a+b);
});
it("subtracts correctly", function(done) {
var a = 5678;
var b = 1234;
return safeMath.subtract(a, b)
.then(function() {
return safeMath.result();
})
.then(function(result) {
assert.equal(result, a-b);
})
.then(done);
it("subtracts correctly", async function() {
let a = 5678;
let b = 1234;
let subtract = await safeMath.subtract(a, b);
let result = await safeMath.result();
assert.equal(result, a-b);
});
it("should throw an error if subtraction result would be negative", function (done) {
var a = 1234;
var b = 5678;
return safeMath.subtract(a, b)
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
it("should throw an error if subtraction result would be negative", async function () {
let a = 1234;
let b = 5678;
try {
let subtract = await safeMath.subtract(a, b);
} catch(error) {
assertJump(error);
}
});
it("should throw an error on addition overflow", function(done) {
var a = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
var b = 1;
return safeMath.add(a, b)
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
it("should throw an error on addition overflow", async function() {
let a = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
let b = 1;
try {
let add = await safeMath.add(a, b);
} catch(error) {
assertJump(error);
}
});
it("should throw an error on multiplication overflow", function(done) {
var a = 115792089237316195423570985008687907853269984665640564039457584007913129639933;
var b = 2;
return safeMath.multiply(a, b)
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
it("should throw an error on multiplication overflow", async function() {
let a = 115792089237316195423570985008687907853269984665640564039457584007913129639933;
let b = 2;
try {
let multiply = await safeMath.multiply(a, b);
} catch(error) {
assertJump(error);
}
});
});

101
test/Shareable.js Normal file
View File

@ -0,0 +1,101 @@
contract('Shareable', function(accounts) {
it('should construct with correct owners and number of sigs required', async function() {
let requiredSigs = 2;
let owners = accounts.slice(1,4);
let shareable = await ShareableMock.new(owners, requiredSigs);
let required = await shareable.required();
assert.equal(required, requiredSigs);
let owner = await shareable.getOwner(0);
assert.equal(owner, accounts[0]);
for(let i = 0; i < accounts.length; i++) {
let owner = await shareable.getOwner(i);
let isowner = await shareable.isOwner(accounts[i]);
if(i <= owners.length) {
assert.equal(accounts[i], owner);
assert.isTrue(isowner);
} else {
assert.notEqual(accounts[i], owner);
assert.isFalse(isowner);
}
}
});
it('should only perform multisig function with enough sigs', async function() {
let requiredSigs = 3;
let owners = accounts.slice(1,4);
let shareable = await ShareableMock.new(owners, requiredSigs);
let hash = 1234;
let initCount = await shareable.count();
initCount = initCount.toString();
for(let i = 0; i < requiredSigs; i++) {
await shareable.increaseCount(hash, {from: accounts[i]});
let count = await shareable.count();
if(i == requiredSigs - 1) {
assert.equal(Number(initCount)+1, count.toString());
} else {
assert.equal(initCount, count.toString());
}
}
});
it('should require approval from different owners', async function() {
let requiredSigs = 2;
let owners = accounts.slice(1,4);
let shareable = await ShareableMock.new(owners, requiredSigs);
let hash = 1234;
let initCount = await shareable.count();
initCount = initCount.toString();
//Count shouldn't increase when the same owner calls repeatedly
for(let i = 0; i < 2; i++) {
await shareable.increaseCount(hash);
let count = await shareable.count();
assert.equal(initCount, count.toString());
}
});
it('should reset sig count after operation is approved', async function() {
let requiredSigs = 3;
let owners = accounts.slice(1,4);
let shareable = await ShareableMock.new(owners, requiredSigs);
let hash = 1234;
let initCount = await shareable.count();
for(let i = 0; i < requiredSigs * 3; i++) {
await shareable.increaseCount(hash, {from: accounts[i % 4]});
let count = await shareable.count();
if((i%(requiredSigs)) == requiredSigs - 1) {
initCount = Number(initCount)+1;
assert.equal(initCount, count);
} else {
assert.equal(initCount.toString(), count);
}
}
});
it('should not perform multisig function after an owner revokes', async function() {
let requiredSigs = 3;
let owners = accounts.slice(1,4);
let shareable = await ShareableMock.new(owners, requiredSigs);
let hash = 1234;
let initCount = await shareable.count();
for(let i = 0; i < requiredSigs; i++) {
if(i == 1) {
await shareable.revoke(hash, {from: accounts[i-1]});
}
await shareable.increaseCount(hash, {from: accounts[i]});
let count = await shareable.count();
assert.equal(initCount.toString(), count);
}
});
});

View File

@ -1,108 +1,64 @@
const assertJump = require('./helpers/assertJump');
contract('StandardToken', function(accounts) {
it("should return the correct totalSupply after construction", function(done) {
return StandardTokenMock.new(accounts[0], 100)
.then(function(token) {
return token.totalSupply();
})
.then(function(totalSupply) {
assert.equal(totalSupply, 100);
})
.then(done);
it("should return the correct totalSupply after construction", async function() {
let token = await StandardTokenMock.new(accounts[0], 100);
let totalSupply = await token.totalSupply();
assert.equal(totalSupply, 100);
})
it("should return the correct allowance amount after approval", function(done) {
var token;
return StandardTokenMock.new()
.then(function(_token) {
token = _token;
return token.approve(accounts[1], 100);
})
.then(function() {
return token.allowance(accounts[0], accounts[1]);
})
.then(function(allowance) {
assert.equal(allowance, 100);
})
.then(done);
it("should return the correct allowance amount after approval", async function() {
let token = await StandardTokenMock.new();
let approve = await token.approve(accounts[1], 100);
let allowance = await token.allowance(accounts[0], accounts[1]);
assert.equal(allowance, 100);
});
it("should return correct balances after transfer", function(done) {
var token;
return StandardTokenMock.new(accounts[0], 100)
.then(function(_token) {
token = _token;
return token.transfer(accounts[1], 100);
})
.then(function() {
return token.balanceOf(accounts[0]);
})
.then(function(balance) {
assert.equal(balance, 0);
})
.then(function() {
return token.balanceOf(accounts[1]);
})
.then(function(balance) {
assert.equal(balance, 100);
})
.then(done);
it("should return correct balances after transfer", async function() {
let token = await StandardTokenMock.new(accounts[0], 100);
let transfer = 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 when trying to transfer more than balance", function(done) {
var token;
return StandardTokenMock.new(accounts[0], 100)
.then(function(_token) {
token = _token;
return token.transfer(accounts[1], 101);
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
it("should throw an error when trying to transfer more than balance", async function() {
let token = await StandardTokenMock.new(accounts[0], 100);
try {
let transfer = await token.transfer(accounts[1], 101);
} catch(error) {
assertJump(error);
}
});
it("should return correct balances after transfering from another account", function(done) {
var token;
return StandardTokenMock.new(accounts[0], 100)
.then(function(_token) {
token = _token;
return token.approve(accounts[1], 100);
})
.then(function() {
return token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
})
.then(function() {
return token.balanceOf(accounts[0]);
})
.then(function(balance) {
assert.equal(balance, 0);
return token.balanceOf(accounts[2]);
})
.then(function(balance) {
assert.equal(balance, 100)
return token.balanceOf(accounts[1]);
})
.then(function(balance) {
assert.equal(balance, 0);
})
.then(done);
it("should return correct balances after transfering from another account", async function() {
let token = await StandardTokenMock.new(accounts[0], 100);
let approve = await token.approve(accounts[1], 100);
let transferFrom = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
let balance1 = await token.balanceOf(accounts[2]);
assert.equal(balance1, 100);
let balance2 = await token.balanceOf(accounts[1]);
assert.equal(balance2, 0);
});
it("should throw an error when trying to transfer more than allowed", function(done) {
var token;
return StandardTokenMock.new(accounts[0], 100)
.then(function(_token) {
token = _token;
return token.approve(accounts[1], 99);
})
.then(function() {
return token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
it("should throw an error when trying to transfer more than allowed", async function() {
let token = await StandardTokenMock.new();
let approve = await token.approve(accounts[1], 99);
try {
let transfer = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
} catch (error) {
assertJump(error);
}
});
});

View File

@ -1,108 +1,52 @@
contract('Stoppable', function(accounts) {
it("can perform normal process in non-emergency", function(done) {
var stoppable;
return StoppableMock.new()
.then(function(_stoppable) {
stoppable = _stoppable;
return stoppable.count();
})
.then(function(count) {
assert.equal(count, 0);
})
.then(function () {
return stoppable.normalProcess();
})
.then(function() {
return stoppable.count();
})
.then(function(count) {
assert.equal(count, 1);
})
.then(done);
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", function(done) {
var stoppable;
return StoppableMock.new()
.then(function(_stoppable) {
stoppable = _stoppable;
return stoppable.emergencyStop();
})
.then(function () {
return stoppable.count();
})
.then(function(count) {
assert.equal(count, 0);
})
.then(function () {
return stoppable.normalProcess();
})
.then(function() {
return stoppable.count();
})
.then(function(count) {
assert.equal(count, 0);
})
.then(done);
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", function(done) {
var stoppable;
return StoppableMock.new()
.then(function(_stoppable) {
stoppable = _stoppable;
return stoppable.drasticMeasure();
})
.then(function() {
return stoppable.drasticMeasureTaken();
})
.then(function(taken) {
assert.isFalse(taken);
})
.then(done);
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", function(done) {
var stoppable;
return StoppableMock.new()
.then(function(_stoppable) {
stoppable = _stoppable;
return stoppable.emergencyStop();
})
.then(function() {
return stoppable.drasticMeasure();
})
.then(function() {
return stoppable.drasticMeasureTaken();
})
.then(function(taken) {
assert.isTrue(taken);
})
.then(done);
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", function(done) {
var stoppable;
return StoppableMock.new()
.then(function(_stoppable) {
stoppable = _stoppable;
return stoppable.emergencyStop();
})
.then(function () {
return stoppable.release();
})
.then(function() {
return stoppable.normalProcess();
})
.then(function() {
return stoppable.count();
})
.then(function(count) {
assert.equal(count, 1);
})
.then(done);
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);
});
});

View File

@ -0,0 +1,3 @@
module.exports = function(error) {
assert.isAbove(error.message.search('invalid JUMP'), -1, 'Invalid JUMP error must be returned');
}