Commit 80cc1808 by AugustoL

Merge remote-tracking branch 'OpenZeppelin/master'

parents e6c50b33 d3894106
{
"presets": ["es2015", "stage-2", "stage-3"]
}
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
*.swo *.swo
node_modules/ node_modules/
build/ build/
.DS_Store/
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
[![NPM Package](https://img.shields.io/npm/v/zeppelin-solidity.svg?style=flat-square)](https://www.npmjs.org/package/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/OpenZeppelin/zeppelin-solidity) [![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: 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)) - 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/).
## Getting Started ## Getting Started
...@@ -53,228 +53,12 @@ Zeppelin is meant to provide secure, tested and community-audited code, but plea ...@@ -53,228 +53,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). If you find a security issue, please email [security@openzeppelin.org](mailto:security@openzeppelin.org).
## Contracts ## Developer Resources
### 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
Building a distributed application, protocol or organization with Zeppelin? 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/ - Ask for help and follow progress at: https://zeppelin-slackin.herokuapp.com/
Interested in contributing to Zeppelin? Interested in contributing to Zeppelin?
......
...@@ -17,7 +17,7 @@ contract Claimable is Ownable { ...@@ -17,7 +17,7 @@ contract Claimable is Ownable {
_; _;
} }
function transfer(address newOwner) onlyOwner { function transferOwnership(address newOwner) onlyOwner {
pendingOwner = newOwner; pendingOwner = newOwner;
} }
......
pragma solidity ^0.4.4; 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 { contract LimitBalance {
uint public limit; uint public limit;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import './Ownable.sol'; import './Ownable.sol';
contract Migrations is Ownable { contract Migrations is Ownable {
uint public lastCompletedMigration; uint public lastCompletedMigration;
......
...@@ -14,12 +14,12 @@ contract Ownable { ...@@ -14,12 +14,12 @@ contract Ownable {
owner = msg.sender; owner = msg.sender;
} }
modifier onlyOwner() { modifier onlyOwner() {
if (msg.sender == owner) if (msg.sender == owner)
_; _;
} }
function transfer(address newOwner) onlyOwner { function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) owner = newOwner; if (newOwner != address(0)) owner = newOwner;
} }
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../PullPayment.sol'; import '../PullPayment.sol';
// UNSAFE CODE, DO NOT USE!
// UNSAFE CODE, DO NOT USE!
contract BadArrayUse is PullPayment { contract BadArrayUse is PullPayment {
address[] employees; address[] employees;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
// UNSAFE CODE, DO NOT USE!
contract BadFailEarly { contract BadFailEarly {
uint constant DEFAULT_SALARY = 50000; uint constant DEFAULT_SALARY = 50000;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
// UNSAFE CODE, DO NOT USE!
contract BadPushPayments { contract BadPushPayments {
address highestBidder; address highestBidder;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../PullPayment.sol'; import '../PullPayment.sol';
contract GoodArrayUse is PullPayment { contract GoodArrayUse is PullPayment {
address[] employees; address[] employees;
mapping(address => uint) bonuses; mapping(address => uint) bonuses;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
contract GoodFailEarly { contract GoodFailEarly {
uint constant DEFAULT_SALARY = 50000; uint constant DEFAULT_SALARY = 50000;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
contract GoodPullPayments { contract GoodPullPayments {
address highestBidder; address highestBidder;
uint highestBid; uint highestBid;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
/* /*
* Proof of Existence example contract * Proof of Existence example contract
* see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05 * see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../PullPayment.sol'; import '../PullPayment.sol';
contract PullPaymentBid is PullPayment { contract PullPaymentBid is PullPayment {
address public highestBidder; address public highestBidder;
uint public highestBid; uint public highestBid;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../PullPayment.sol'; import '../PullPayment.sol';
import '../Stoppable.sol'; import '../Stoppable.sol';
contract StoppableBid is Stoppable, PullPayment { contract StoppableBid is Stoppable, PullPayment {
address public highestBidder; address public highestBidder;
uint public highestBid; uint public highestBid;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../token/BasicToken.sol'; import '../token/BasicToken.sol';
// mock class using BasicToken // mock class using BasicToken
contract BasicTokenMock is BasicToken { contract BasicTokenMock is BasicToken {
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../LimitBalance.sol'; import '../LimitBalance.sol';
// mock class using LimitBalance // mock class using LimitBalance
contract LimitBalanceMock is LimitBalance(1000) { contract LimitBalanceMock is LimitBalance(1000) {
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../PullPayment.sol'; import '../PullPayment.sol';
// mock class using PullPayment // mock class using PullPayment
contract PullPaymentMock is PullPayment { contract PullPaymentMock is PullPayment {
function PullPaymentMock() payable { }
// test helper function to call asyncSend // test helper function to call asyncSend
function callSend(address dest, uint amount) { function callSend(address dest, uint amount) {
asyncSend(dest, amount); asyncSend(dest, amount);
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../SafeMath.sol'; import '../SafeMath.sol';
contract SafeMathMock is SafeMath { contract SafeMathMock is SafeMath {
uint public result; uint public result;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../token/StandardToken.sol'; import '../token/StandardToken.sol';
// mock class using StandardToken // mock class using StandardToken
contract StandardTokenMock is StandardToken { contract StandardTokenMock is StandardToken {
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import '../Stoppable.sol'; import '../Stoppable.sol';
// mock class using Stoppable // mock class using Stoppable
contract StoppableMock is Stoppable { contract StoppableMock is Stoppable {
bool public drasticMeasureTaken; bool public drasticMeasureTaken;
......
pragma solidity ^0.4.4; pragma solidity ^0.4.4;
import './ERC20.sol'; import './ERC20.sol';
import '../SafeMath.sol'; import '../SafeMath.sol';
/** /**
* ERC20 token * Standard ERC20 token
* *
* https://github.com/ethereum/EIPs/issues/20 * https://github.com/ethereum/EIPs/issues/20
* Based on code by FirstBlood: * Based on code by FirstBlood:
......
# 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)
\ No newline at end of file
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.
\ No newline at end of file
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();
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.
# -*- 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'),
]
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/>`_.
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.
\ No newline at end of file
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
\ No newline at end of file
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/>`_.
.. 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
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
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.
\ No newline at end of file
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
"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-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0",
"ethereumjs-testrpc": "^3.0.2", "ethereumjs-testrpc": "^3.0.2",
"truffle": "^2.1.1" "truffle": "^2.1.1"
}, },
......
const assertJump = require('./helpers/assertJump');
contract('BasicToken', function(accounts) { contract('BasicToken', function(accounts) {
it("should return the correct totalSupply after construction", function(done) { it("should return the correct totalSupply after construction", async function() {
return BasicTokenMock.new(accounts[0], 100) let token = await BasicTokenMock.new(accounts[0], 100);
.then(function(token) { let totalSupply = await token.totalSupply();
return token.totalSupply();
}) assert.equal(totalSupply, 100);
.then(function(totalSupply) {
assert.equal(totalSupply, 100);
})
.then(done);
}) })
it("should return correct balances after transfer", function(done) { it("should return correct balances after transfer", async function(){
var token; let token = await BasicTokenMock.new(accounts[0], 100);
return BasicTokenMock.new(accounts[0], 100) let transfer = await token.transfer(accounts[1], 100);
.then(function(_token) {
token = _token; let firstAccountBalance = await token.balanceOf(accounts[0]);
return token.transfer(accounts[1], 100); assert.equal(firstAccountBalance, 0);
})
.then(function() { let secondAccountBalance = await token.balanceOf(accounts[1]);
return token.balanceOf(accounts[0]); assert.equal(secondAccountBalance, 100);
})
.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 throw an error when trying to transfer more than balance", function(done) { it("should throw an error when trying to transfer more than balance", async function() {
var token; let token = await BasicTokenMock.new(accounts[0], 100);
return BasicTokenMock.new(accounts[0], 100) try {
.then(function(_token) { let transfer = await token.transfer(accounts[1], 101);
token = _token; } catch(error) {
return token.transfer(accounts[1], 101); assertJump(error);
}) }
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
}); });
}); });
var 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,
...@@ -8,134 +8,108 @@ var sendReward = function(sender, receiver, value){ ...@@ -8,134 +8,108 @@ var sendReward = function(sender, receiver, value){
contract('Bounty', function(accounts) { contract('Bounty', function(accounts) {
it("sets reward", function(done){ it("sets reward", async function(){
var owner = accounts[0]; let owner = accounts[0];
var reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, "ether");
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
SecureTargetBounty.new(). assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
then(function(bounty){
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
}).
then(done);
}) })
it("empties itself when killed", function(done){ it("empties itself when killed", async function(){
var owner = accounts[0]; let owner = accounts[0];
var reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, "ether");
var bounty; let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
SecureTargetBounty.new(). assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
then(function(_bounty){
bounty = _bounty; await bounty.kill();
sendReward(owner, bounty.address, reward); assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
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);
}) })
describe("Against secure contract", function(){ describe("Against secure contract", function(){
it("checkInvariant returns true", function(done){
var bounty; it("checkInvariant returns true", async function(){
let bounty = await SecureTargetBounty.new();
SecureTargetBounty.new(). let target = await bounty.createTarget();
then(function(_bounty) { let check = await bounty.checkInvariant.call();
bounty = _bounty;
return bounty.createTarget(); assert.isTrue(check);
}).
then(function() {
return bounty.checkInvariant.call()
}).
then(function(result) {
assert.isTrue(result);
}).
then(done);
}) })
it("cannot claim reward", function(done){ it("cannot claim reward", async function(done){
var owner = accounts[0]; let owner = accounts[0];
var researcher = accounts[1]; let researcher = accounts[1];
var reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, "ether");
let bounty = await SecureTargetBounty.new();
SecureTargetBounty.new(). let event = bounty.TargetCreated({});
then(function(bounty) {
var event = bounty.TargetCreated({}); event.watch(async function(err, result) {
event.watch(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())
bounty.claim(targetAddress, {from:researcher}). assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
then(function(){ throw("should not come here")}).
catch(function() { try {
return bounty.claimed.call(); let tmpClain = await bounty.claim(targetAddress, {from:researcher});
}). done("should not come here");
then(function(result) { } catch(error) {
assert.isFalse(result); let reClaimedBounty = await bounty.claimed.call();
bounty.withdrawPayments({from:researcher}). assert.isFalse(reClaimedBounty);
then(function(){ throw("should not come here")}).
catch(function() { try {
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) let withdraw = await bounty.withdrawPayments({from:researcher});
done(); done("should not come here")
}) } catch (err) {
}) assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
}) done();
bounty.createTarget({from:researcher}); }
}) }//end of first try catch
});
bounty.createTarget({from:researcher});
}) })
}) })
describe("Against broken contract", function(){ describe("Against broken contract", function(){
it("checkInvariant returns false", function(done){ it("checkInvariant returns false", async function(){
var bounty; let bounty = await InsecureTargetBounty.new();
let target = await bounty.createTarget();
InsecureTargetBounty.new(). let invariantCall = await bounty.checkInvariant.call();
then(function(_bounty) {
bounty = _bounty; assert.isFalse(invariantCall);
return bounty.createTarget();
}).
then(function() {
return bounty.checkInvariant.call()
}).
then(function(result) {
assert.isFalse(result);
}).
then(done);
}) })
it("claims reward", function(done){ it("claims reward", async function(done){
var owner = accounts[0]; let owner = accounts[0];
var researcher = accounts[1]; let researcher = accounts[1];
var reward = web3.toWei(1, "ether"); let reward = web3.toWei(1, "ether");
let bounty = await InsecureTargetBounty.new();
InsecureTargetBounty.new(). let event = bounty.TargetCreated({});
then(function(bounty) {
var event = bounty.TargetCreated({}); event.watch(async function(err, result) {
event.watch(function(err, result) { event.stopWatching();
event.stopWatching(); if (err) { throw err }
if (err) { throw err } let 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());
bounty.claim(targetAddress, {from:researcher}).
then(function() { let bountyClaim = await bounty.claim(targetAddress, {from:researcher});
return bounty.claimed.call(); let claim = await bounty.claimed.call();
}).
then(function(result) { assert.isTrue(claim);
assert.isTrue(result);
return bounty.withdrawPayments({from:researcher}) let payment = await bounty.withdrawPayments({from:researcher});
}).
then(function() { assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber()) done();
}).then(done); })
}) bounty.createTarget({from:researcher});
bounty.createTarget({from:researcher});
})
}) })
}) })
}); });
contract('Claimable', function(accounts) { contract('Claimable', function(accounts) {
var claimable; let claimable;
beforeEach(function() { beforeEach(async function() {
return Claimable.new().then(function(deployed) { claimable = await Claimable.new();
claimable = deployed;
});
}); });
it("should have an owner", function(done) { it("should have an owner", async function() {
return claimable.owner() let owner = await claimable.owner();
.then(function(owner) { assert.isTrue(owner != 0);
assert.isTrue(owner != 0);
})
.then(done)
}); });
it("changes pendingOwner after transfer", function(done) { it("changes pendingOwner after transfer", async function() {
var newOwner = accounts[1]; let newOwner = accounts[1];
return claimable.transfer(newOwner) let transfer = await claimable.transferOwnership(newOwner);
.then(function() { let pendingOwner = await claimable.pendingOwner();
return claimable.pendingOwner();
}) assert.isTrue(pendingOwner === newOwner);
.then(function(pendingOwner) {
assert.isTrue(pendingOwner === newOwner);
})
.then(done)
}); });
it("should prevent to claimOwnership from no pendingOwner", function(done) { it("should prevent to claimOwnership from no pendingOwner", async function() {
return claimable.claimOwnership({from: accounts[2]}) let claimedOwner = await claimable.claimOwnership({from: accounts[2]});
.then(function() { let owner = await claimable.owner();
return claimable.owner();
}) assert.isTrue(owner != accounts[2]);
.then(function(owner) {
assert.isTrue(owner != accounts[2]);
})
.then(done)
}); });
it("should prevent non-owners from transfering" ,function(done) { it("should prevent non-owners from transfering", async function() {
return claimable.transfer(accounts[2], {from: accounts[2]}) let transfer = await claimable.transferOwnership(accounts[2], {from: accounts[2]});
.then(function() { let pendingOwner = await claimable.pendingOwner();
return claimable.pendingOwner();
}) assert.isFalse(pendingOwner === accounts[2]);
.then(function(pendingOwner) {
assert.isFalse(pendingOwner === accounts[2]);
})
.then(done)
}); });
describe("after initiating a transfer", function () { describe("after initiating a transfer", function () {
var newOwner; let newOwner;
beforeEach(function () { beforeEach(async function () {
newOwner = accounts[1]; newOwner = accounts[1];
return claimable.transfer(newOwner); await claimable.transferOwnership(newOwner);
}); });
it("changes allow pending owner to claim ownership", function(done) { it("changes allow pending owner to claim ownership", async function() {
return claimable.claimOwnership({from: newOwner}) let claimedOwner = await claimable.claimOwnership({from: newOwner})
.then(function() { let owner = await claimable.owner();
return claimable.owner();
}) assert.isTrue(owner === newOwner);
.then(function(owner) {
assert.isTrue(owner === newOwner);
})
.then(done)
}); });
}); });
}); });
...@@ -32,38 +32,25 @@ contract('Killable', function(accounts) { ...@@ -32,38 +32,25 @@ contract('Killable', function(accounts) {
} }
}; };
it("should send balance to owner after death", function(done) { it("should send balance to owner after death", async function() {
var initBalance, newBalance, owner, address, killable, kBalance; 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) { web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('50','ether')}, function(err, result) {
if(err) if(err)
console.log("ERROR:" + err); console.log("ERROR:" + err);
else { else {
console.log(result); console.log(result);
} }
}) });
return Killable.new({from: accounts[0], value: web3.toWei('10','ether')})
.then(function(_killable) { killable = await Killable.new({from: accounts[0], value: web3.toWei('10','ether')});
killable = _killable; owner = await killable.owner();
return killable.owner(); initBalance = web3.eth.getBalance(owner);
}) kBalance = web3.eth.getBalance(killable.address);
.then(function(_owner) { txnHash = await killable.kill({from: owner});
owner = _owner; receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
initBalance = web3.eth.getBalance(owner); newBalance = web3.eth.getBalance(owner);
kBalance = web3.eth.getBalance(killable.address);
}) assert.isTrue(newBalance > initBalance);
.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);
}); });
}); });
const assertJump = require('./helpers/assertJump');
contract('LimitBalance', function(accounts) { contract('LimitBalance', function(accounts) {
var lb; let lb;
beforeEach(function() { beforeEach(async function() {
return LimitBalanceMock.new().then(function(deployed) { lb = await LimitBalanceMock.new();
lb = deployed;
});
}); });
var LIMIT = 1000; let LIMIT = 1000;
it("should expose limit", function(done) { it("should expose limit", async function() {
return lb.limit() let limit = await lb.limit();
.then(function(limit) { assert.equal(limit, LIMIT);
assert.equal(limit, LIMIT);
})
.then(done)
}); });
it("should allow sending below limit", function(done) { it("should allow sending below limit", async function() {
var amount = 1; let amount = 1;
return lb.limitedDeposit({value: amount}) let limDeposit = await lb.limitedDeposit({value: amount});
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount); assert.equal(web3.eth.getBalance(lb.address), amount);
})
.then(done)
}); });
it("shouldnt allow sending above limit", function(done) { it("shouldnt allow sending above limit", async function() {
var amount = 1100; let amount = 1110;
return lb.limitedDeposit({value: amount}) try {
.catch(function(error) { let limDeposit = await lb.limitedDeposit({value: amount});
if (error.message.search('invalid JUMP') == -1) throw error } catch(error) {
}) assertJump(error);
.then(done) }
}); });
it("should allow multiple sends below limit", function(done) { it("should allow multiple sends below limit", async function() {
var amount = 500; let amount = 500;
return lb.limitedDeposit({value: amount}) let limDeposit = await lb.limitedDeposit({value: amount});
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount); assert.equal(web3.eth.getBalance(lb.address), amount);
return lb.limitedDeposit({value: amount})
}) let limDeposit2 = await lb.limitedDeposit({value: amount});
.then(function() { assert.equal(web3.eth.getBalance(lb.address), amount*2);
assert.equal(web3.eth.getBalance(lb.address), amount*2);
})
.then(done)
}); });
it("shouldnt allow multiple sends above limit", function(done) { it("shouldnt allow multiple sends above limit", async function() {
var amount = 500; let amount = 500;
return lb.limitedDeposit({value: amount}) let limDeposit = await lb.limitedDeposit({value: amount});
.then(function() {
assert.equal(web3.eth.getBalance(lb.address), amount); assert.equal(web3.eth.getBalance(lb.address), amount);
return lb.limitedDeposit({value: amount+1})
}) try {
.catch(function(error) { await lb.limitedDeposit({value: amount+1})
if (error.message.search('invalid JUMP') == -1) throw error; } catch(error) {
}) assertJump(error);
.then(done) }
}); });
}); });
contract('Ownable', function(accounts) { contract('Ownable', function(accounts) {
var ownable; let ownable;
beforeEach(function() { beforeEach(async function() {
return Ownable.new().then(function(deployed) { ownable = await Ownable.new();
ownable = deployed;
});
}); });
it("should have an owner", function(done) { it("should have an owner", async function() {
return ownable.owner() let owner = await ownable.owner();
.then(function(owner) { assert.isTrue(owner != 0);
assert.isTrue(owner != 0);
})
.then(done)
}); });
it("changes owner after transfer", function(done) { it("changes owner after transfer", async function() {
var other = accounts[1]; let other = accounts[1];
return ownable.transfer(other) let transfer = await ownable.transferOwnership(other);
.then(function() { let owner = await ownable.owner();
return ownable.owner();
}) assert.isTrue(owner === other);
.then(function(owner) {
assert.isTrue(owner === other);
})
.then(done)
}); });
it("should prevent non-owners from transfering" ,function(done) { it("should prevent non-owners from transfering", async function() {
var other = accounts[2]; let other = accounts[2];
return ownable.transfer(other, {from: accounts[2]}) let transfer = await ownable.transferOwnership(other, {from: accounts[2]});
.then(function() { let owner = await ownable.owner();
return ownable.owner();
}) assert.isFalse(owner === other);
.then(function(owner) {
assert.isFalse(owner === other);
})
.then(done)
}); });
it("should guard ownership against stuck state" ,function(done) { it("should guard ownership against stuck state", async function() {
var ownable = Ownable.deployed(); let ownable = Ownable.deployed();
let originalOwner = await ownable.owner();
return ownable.owner() let transfer = await ownable.transferOwnership(null, {from: originalOwner});
.then(function (originalOwner) { let newOwner = await ownable.owner();
return ownable.transfer(null, {from: originalOwner})
.then(function() { assert.equal(originalOwner, newOwner);
return ownable.owner();
})
.then(function(newOwner) {
assert.equal(originalOwner, newOwner);
})
.then(done);
});
}); });
}); });
contract('PullPayment', function(accounts) { contract('PullPayment', function(accounts) {
it("can't call asyncSend externally", function(done) { it("can't call asyncSend externally", async function() {
return PullPaymentMock.new() let ppc = await PullPaymentMock.new();
.then(function(ppc) { assert.isUndefined(ppc.asyncSend);
assert.isUndefined(ppc.asyncSend);
})
.then(done);
}); });
it("can record an async payment correctly", function(done) { it("can record an async payment correctly", async function() {
var ppce; let AMOUNT = 100;
var AMOUNT = 100; let ppce = await PullPaymentMock.new();
return PullPaymentMock.new() let callSend = await ppce.callSend(accounts[0], AMOUNT);
.then(function(_ppce) { let paymentsToAccount0 = await ppce.payments(accounts[0]);
ppce = _ppce;
ppce.callSend(accounts[0], AMOUNT) assert.equal(paymentsToAccount0, AMOUNT);
})
.then(function() {
return ppce.payments(accounts[0]);
})
.then(function(paymentsToAccount0) {
assert.equal(paymentsToAccount0, AMOUNT);
})
.then(done);
}); });
it("can add multiple balances on one account", function(done) { it("can add multiple balances on one account", async function() {
var ppce; let ppce = await PullPaymentMock.new();
return PullPaymentMock.new() let call1 = await ppce.callSend(accounts[0], 200);
.then(function(_ppce) { let call2 = await ppce.callSend(accounts[0], 300);
ppce = _ppce; let paymentsToAccount0 = await ppce.payments(accounts[0]);
return ppce.callSend(accounts[0], 200)
}) assert.equal(paymentsToAccount0, 500);
.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 balances on multiple accounts", function(done) { it("can add balances on multiple accounts", async function() {
var ppce; let ppce = await PullPaymentMock.new();
return PullPaymentMock.new() let call1 = await ppce.callSend(accounts[0], 200);
.then(function(_ppce) { let call2 = await ppce.callSend(accounts[1], 300);
ppce = _ppce;
return ppce.callSend(accounts[0], 200) let paymentsToAccount0 = await ppce.payments(accounts[0]);
}) assert.equal(paymentsToAccount0, 200);
.then(function() {
return ppce.callSend(accounts[1], 300) let paymentsToAccount1 = await ppce.payments(accounts[1]);
}) assert.equal(paymentsToAccount1, 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 withdraw payment", function(done) { it("can withdraw payment", async function() {
var ppce; let AMOUNT = 17*1e18;
var AMOUNT = 17*1e18; let payee = accounts[1];
var payee = accounts[1]; let initialBalance = web3.eth.getBalance(payee);
var initialBalance = web3.eth.getBalance(payee);
return PullPaymentMock.new({value: AMOUNT}) let ppce = await PullPaymentMock.new({value: AMOUNT});
.then(function(_ppce) { let call1 = await ppce.callSend(payee, AMOUNT);
ppce = _ppce;
return ppce.callSend(payee, AMOUNT); let payment1 = await ppce.payments(payee);
}) assert.equal(payment1, AMOUNT);
.then(function() {
return ppce.payments(payee); let withdraw = await ppce.withdrawPayments({from: payee});
}) let payment2 = await ppce.payments(payee);
.then(function(paymentsToAccount0) { assert.equal(payment2, 0);
assert.equal(paymentsToAccount0, AMOUNT);
}) let balance = web3.eth.getBalance(payee);
.then(function() { assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
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);
}); });
}); });
const assertJump = require('./helpers/assertJump');
contract('SafeMath', function(accounts) { contract('SafeMath', function(accounts) {
var safeMath; let safeMath;
before(function() { before(async function() {
return SafeMathMock.new() safeMath = await SafeMathMock.new();
.then(function(_safeMath) {
safeMath = _safeMath;
});
}); });
it("multiplies correctly", function(done) { it("multiplies correctly", async function() {
var a = 5678; let a = 5678;
var b = 1234; let b = 1234;
return safeMath.multiply(a, b) let mult = await safeMath.multiply(a, b);
.then(function() { let result = await safeMath.result();
return safeMath.result(); assert.equal(result, a*b);
})
.then(function(result) {
assert.equal(result, a*b);
})
.then(done);
}); });
it("adds correctly", function(done) { it("adds correctly", async function() {
var a = 5678; let a = 5678;
var b = 1234; let b = 1234;
return safeMath.add(a, b) let add = await safeMath.add(a, b);
.then(function() { let result = await safeMath.result();
return safeMath.result();
}) assert.equal(result, a+b);
.then(function(result) {
assert.equal(result, a+b);
})
.then(done);
}); });
it("subtracts correctly", function(done) { it("subtracts correctly", async function() {
var a = 5678; let a = 5678;
var b = 1234; let b = 1234;
return safeMath.subtract(a, b) let subtract = await safeMath.subtract(a, b);
.then(function() { let result = await safeMath.result();
return safeMath.result();
}) assert.equal(result, a-b);
.then(function(result) {
assert.equal(result, a-b);
})
.then(done);
}); });
it("should throw an error if subtraction result would be negative", function (done) { it("should throw an error if subtraction result would be negative", async function () {
var a = 1234; let a = 1234;
var b = 5678; let b = 5678;
return safeMath.subtract(a, b) try {
.catch(function(error) { let subtract = await safeMath.subtract(a, b);
if (error.message.search('invalid JUMP') == -1) throw error } catch(error) {
}) assertJump(error);
.then(done); }
}); });
it("should throw an error on addition overflow", function(done) { it("should throw an error on addition overflow", async function() {
var a = 115792089237316195423570985008687907853269984665640564039457584007913129639935; let a = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
var b = 1; let b = 1;
return safeMath.add(a, b) try {
.catch(function(error) { let add = await safeMath.add(a, b);
if (error.message.search('invalid JUMP') == -1) throw error } catch(error) {
}) assertJump(error);
.then(done); }
}); });
it("should throw an error on multiplication overflow", function(done) { it("should throw an error on multiplication overflow", async function() {
var a = 115792089237316195423570985008687907853269984665640564039457584007913129639933; let a = 115792089237316195423570985008687907853269984665640564039457584007913129639933;
var b = 2; let b = 2;
return safeMath.multiply(a, b) try {
.catch(function(error) { let multiply = await safeMath.multiply(a, b);
if (error.message.search('invalid JUMP') == -1) throw error } catch(error) {
}) assertJump(error);
.then(done); }
}); });
}); });
const assertJump = require('./helpers/assertJump');
contract('StandardToken', function(accounts) { contract('StandardToken', function(accounts) {
it("should return the correct totalSupply after construction", function(done) { it("should return the correct totalSupply after construction", async function() {
return StandardTokenMock.new(accounts[0], 100) let token = await StandardTokenMock.new(accounts[0], 100);
.then(function(token) { let totalSupply = await token.totalSupply();
return token.totalSupply();
}) assert.equal(totalSupply, 100);
.then(function(totalSupply) {
assert.equal(totalSupply, 100);
})
.then(done);
}) })
it("should return the correct allowance amount after approval", function(done) { it("should return the correct allowance amount after approval", async function() {
var token; let token = await StandardTokenMock.new();
return StandardTokenMock.new() let approve = await token.approve(accounts[1], 100);
.then(function(_token) { let allowance = await token.allowance(accounts[0], accounts[1]);
token = _token;
return token.approve(accounts[1], 100); assert.equal(allowance, 100);
})
.then(function() {
return token.allowance(accounts[0], accounts[1]);
})
.then(function(allowance) {
assert.equal(allowance, 100);
})
.then(done);
}); });
it("should return correct balances after transfer", function(done) { it("should return correct balances after transfer", async function() {
var token; let token = await StandardTokenMock.new(accounts[0], 100);
return StandardTokenMock.new(accounts[0], 100) let transfer = await token.transfer(accounts[1], 100);
.then(function(_token) { let balance0 = await token.balanceOf(accounts[0]);
token = _token; assert.equal(balance0, 0);
return token.transfer(accounts[1], 100);
}) let balance1 = await token.balanceOf(accounts[1]);
.then(function() { assert.equal(balance1, 100);
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 throw an error when trying to transfer more than balance", function(done) { it("should throw an error when trying to transfer more than balance", async function() {
var token; let token = await StandardTokenMock.new(accounts[0], 100);
return StandardTokenMock.new(accounts[0], 100) try {
.then(function(_token) { let transfer = await token.transfer(accounts[1], 101);
token = _token; } catch(error) {
return token.transfer(accounts[1], 101); assertJump(error);
}) }
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error
})
.then(done);
}); });
it("should return correct balances after transfering from another account", function(done) { it("should return correct balances after transfering from another account", async function() {
var token; let token = await StandardTokenMock.new(accounts[0], 100);
return StandardTokenMock.new(accounts[0], 100) let approve = await token.approve(accounts[1], 100);
.then(function(_token) { let transferFrom = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
token = _token;
return token.approve(accounts[1], 100); let balance0 = await token.balanceOf(accounts[0]);
}) assert.equal(balance0, 0);
.then(function() {
return token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]}); let balance1 = await token.balanceOf(accounts[2]);
}) assert.equal(balance1, 100);
.then(function() {
return token.balanceOf(accounts[0]); let balance2 = await token.balanceOf(accounts[1]);
}) assert.equal(balance2, 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 throw an error when trying to transfer more than allowed", function(done) { it("should throw an error when trying to transfer more than allowed", async function() {
var token; let token = await StandardTokenMock.new();
return StandardTokenMock.new(accounts[0], 100) let approve = await token.approve(accounts[1], 99);
.then(function(_token) { try {
token = _token; let transfer = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
return token.approve(accounts[1], 99); } catch (error) {
}) assertJump(error);
.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);
}); });
}); });
contract('Stoppable', function(accounts) { contract('Stoppable', function(accounts) {
it("can perform normal process in non-emergency", function(done) { it("can perform normal process in non-emergency", async function() {
var stoppable; let stoppable = await StoppableMock.new();
return StoppableMock.new() let count0 = await stoppable.count();
.then(function(_stoppable) { assert.equal(count0, 0);
stoppable = _stoppable;
return stoppable.count(); let normalProcess = await stoppable.normalProcess();
}) let count1 = await stoppable.count();
.then(function(count) { assert.equal(count1, 1);
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 not perform normal process in emergency", function(done) { it("can not perform normal process in emergency", async function() {
var stoppable; let stoppable = await StoppableMock.new();
return StoppableMock.new() let emergencyStop = await stoppable.emergencyStop();
.then(function(_stoppable) { let count0 = await stoppable.count();
stoppable = _stoppable; assert.equal(count0, 0);
return stoppable.emergencyStop();
}) let normalProcess = await stoppable.normalProcess();
.then(function () { let count1 = await stoppable.count();
return stoppable.count(); assert.equal(count1, 0);
})
.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 take drastic measure in non-emergency", function(done) { it("can not take drastic measure in non-emergency", async function() {
var stoppable; let stoppable = await StoppableMock.new();
return StoppableMock.new() let drasticMeasure = await stoppable.drasticMeasure();
.then(function(_stoppable) { let drasticMeasureTaken = await stoppable.drasticMeasureTaken();
stoppable = _stoppable;
return stoppable.drasticMeasure(); assert.isFalse(drasticMeasureTaken);
})
.then(function() {
return stoppable.drasticMeasureTaken();
})
.then(function(taken) {
assert.isFalse(taken);
})
.then(done);
}); });
it("can take a drastic measure in an emergency", function(done) { it("can take a drastic measure in an emergency", async function() {
var stoppable; let stoppable = await StoppableMock.new();
return StoppableMock.new() let emergencyStop = await stoppable.emergencyStop();
.then(function(_stoppable) { let drasticMeasure = await stoppable.drasticMeasure();
stoppable = _stoppable; let drasticMeasureTaken = await stoppable.drasticMeasureTaken();
return stoppable.emergencyStop();
}) assert.isTrue(drasticMeasureTaken);
.then(function() {
return stoppable.drasticMeasure();
})
.then(function() {
return stoppable.drasticMeasureTaken();
})
.then(function(taken) {
assert.isTrue(taken);
})
.then(done);
}); });
it("should resume allowing normal process after emergency is over", function(done) { it("should resume allowing normal process after emergency is over", async function() {
var stoppable; let stoppable = await StoppableMock.new();
return StoppableMock.new() let emergencyStop = await stoppable.emergencyStop();
.then(function(_stoppable) { let release = await stoppable.release();
stoppable = _stoppable; let normalProcess = await stoppable.normalProcess();
return stoppable.emergencyStop(); let count0 = await stoppable.count();
})
.then(function () { assert.equal(count0, 1);
return stoppable.release();
})
.then(function() {
return stoppable.normalProcess();
})
.then(function() {
return stoppable.count();
})
.then(function(count) {
assert.equal(count, 1);
})
.then(done);
}); });
}); });
module.exports = function(error) {
assert.isAbove(error.message.search('invalid JUMP'), -1, 'Invalid JUMP error must be returned');
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment