Compare commits

..

83 Commits

Author SHA1 Message Date
7ac697be2c v1.0.5 2017-05-09 15:13:45 -03:00
f7a7fc3b06 Merge pull request #206 from DavidKnott/create-pausable-token
Create and test PausableToken Contract
2017-05-03 15:32:33 -03:00
b4b6029f66 Create and test PausableToken Contract 2017-04-29 01:08:14 +01:00
071040f3eb Merge pull request #203 from ORBAT/fix/bounty-claim-fail-test
Await event handling result in Bounty test
2017-04-24 11:06:38 -03:00
cd78c20e0e await event handling result in Bounty test. Fixes #202 2017-04-21 20:08:17 +03:00
453a198250 Merge pull request #184 from maraoz/safemath
Make SafeMath a library
2017-04-20 12:40:15 -03:00
7592122e4d fix indentation 2017-04-19 18:34:53 -03:00
609869f087 change safe* to * 2017-04-19 16:19:22 -03:00
4fad1505c7 Make SafeMath a library 2017-04-19 15:36:49 -03:00
5881429952 Merge pull request #195 from OpenZeppelin/maraoz-patch-1
Update README with new slack url
2017-04-19 13:49:29 -03:00
526ed43d61 Update README with new slack url 2017-04-19 13:49:09 -03:00
ab9591ba80 Merge pull request #193 from AugustoL/master
Added totalPayments uint on PullPayments contract
2017-04-18 11:42:48 -03:00
a8bcb0fcfe Added totalPayments uint on PullPayments contract 2017-04-14 14:30:31 -03:00
b420316783 Merge pull request #192 from tatiesmars/patch-1
Add MintFinished event to MintableToken
2017-04-13 16:18:07 -03:00
37c6782f95 Add MintFinished event to MintableToken #191 2017-04-13 14:23:41 +08:00
70a4395a97 Merge pull request #190 from lastperson/patch-1
Remove excessive condition from SafeMath.safeAdd()
2017-04-12 19:11:55 -03:00
31c05c4c7d Remove excessive condition from SafeMath.safeAdd()
There is no situation when `c>=a` will be `true` while `c>=b` will be `false`.
2017-04-12 15:22:52 +08:00
22018fd374 Merge pull request #188 from jdetychey/patch-3
fix for short address attack
2017-04-11 17:02:24 -03:00
d1f63f5c7e Merge pull request #185 from DavidKnott/create-token-with-owner-contract
Create and test MintableToken contract
2017-04-11 13:01:01 -03:00
5d75264f0f Fix for bigger payloads
as suggested by izqui in case a function calls transfer under the hood
2017-04-11 17:25:03 +02:00
a605f66972 Create and test MintableToken contract 2017-04-07 14:51:33 -06:00
0328250554 Merge pull request #187 from DavidKnott/change-killable-contract-to-destructible
Change killable to destructible and kill to destroy
2017-04-07 15:16:06 -03:00
70b5ffd928 Change killable to destructible and kill to destroy 2017-04-07 11:34:35 -06:00
d9b9ed227b fix for short address attack
as suggested by /u/izqui9 here https://www.reddit.com/r/ethereum/comments/63s917/worrysome_bug_exploit_with_erc20_token/dfwmhc3/
Attack description: 
https://blog.golemproject.net/how-to-find-10m-by-just-reading-blockchain-6ae9d39fcd95
2017-04-07 11:30:18 +02:00
14274f8677 Merge pull request #169 from maraoz/audit
Follow security audit recommendations
2017-04-05 19:04:11 -03:00
af604379d3 rebase conflict fixes 2017-04-05 18:30:14 -03:00
7d83a08658 8.2.3 removed ownable 2017-04-05 18:27:34 -03:00
2ccbfea8c5 8.1.2 add comment clarifying Migrations 2017-04-05 18:27:34 -03:00
fad287007b 7.2 add preconditions to Shareable contructor 2017-04-05 18:27:34 -03:00
cd9f820b85 fix syntax error 2017-04-05 18:27:34 -03:00
582166d378 7.1 pull payment safe math 2017-04-05 18:27:34 -03:00
ab9eecb104 6.1 fix stuck Ether in Crowdsale contract 2017-04-05 18:27:34 -03:00
52120a8c42 5.3 always throw on error 2017-04-05 18:27:34 -03:00
a1aa74f96d 5.2 all contracts now solidity ^0.4.8 2017-04-05 18:25:14 -03:00
6ae7ac97f6 Merge pull request #181 from roderik/patch-1
Shareable is not used in this contract
2017-04-05 17:25:28 -03:00
ba6c036628 Merge pull request #183 from AragonOne/fix-vesting
Fix vesting calculation logic
2017-04-05 12:54:23 -03:00
7a2fda9076 Improve VestedToken tests 2017-04-05 13:20:18 +02:00
41d2fde952 Fix vesting calculation logic 2017-04-05 13:13:17 +02:00
f0e7396619 Shareable is not used in this contract 2017-04-05 08:33:30 +02:00
6139500e97 Merge pull request #180 from frangio/fix-179-rename-transferable
Rename `TransferableToken` to `LimitedTransferToken`
2017-04-03 23:28:09 -03:00
0b88944b38 fix a few typos 2017-04-03 22:06:41 -03:00
61e33197b2 rename TransferableToken to LimitedTransferToken
fixes #179
2017-04-03 22:04:07 -03:00
025e7db765 Merge pull request #178 from frangio/fix-solidity-renamed-methods
Change sha3 and suicide methods to keccak256 and selfdestruct respectively
2017-04-03 12:03:16 -03:00
6f311e72b3 change sha3 and suicide methods to keccak256 and selfdestruct
see ethereum/solidity#1476
2017-04-03 11:31:56 -03:00
a344d42a00 Merge pull request #170 from Recmo/feature/no-owner
Prevent contracts from owning Ether, Tokens and other contracts.
2017-03-31 16:51:18 -03:00
f2159be68d Merge pull request #176 from frangio/shareable-owners-type
Change type of `owners` from uint[] to address[]
2017-03-31 13:33:09 -03:00
7f7238378c change type of owners from uint[] to address[]
fixes #175
2017-03-31 13:14:59 -03:00
1d2b989e8e Fix typo in comment 2017-03-31 09:39:29 +01:00
bc0f9b0ea5 Merge pull request #174 from demibrener/readme
Add Wings to collaborating organizations
2017-03-29 18:15:13 -03:00
a885dabc81 added Wings 2017-03-29 18:04:13 -03:00
6673da46b1 Merge pull request #173 from demibrener/readme
Name consistency: OpenZeppelin
2017-03-29 18:01:23 -03:00
2e9587477c Merge pull request #171 from Recmo/feature/reentrancy-guard
Add ReentrancyGuard
2017-03-28 15:10:30 -03:00
967ee13565 OpenZeppelin 2017-03-27 17:21:29 -03:00
a2bd1bb7f6 Add ReentrancyGuard 2017-03-24 11:01:06 +00:00
ac3b3652c3 Add NoOwner 2017-03-23 15:48:22 +00:00
166a1070e5 Add HasNoContracts 2017-03-23 15:43:52 +00:00
94d3c447b7 Add HasNoTokens 2017-03-23 15:22:48 +00:00
9ff82aecef Remove non-compiling payable default 2017-03-23 12:54:42 +00:00
d1af3ef1b3 Add HasNoEther 2017-03-23 12:23:27 +00:00
ffce7e3b08 Merge pull request #166 from AragonOne/transaferable
Add comments for Transferable Token
2017-03-20 15:43:56 -03:00
f896790ca3 Merge pull request #167 from maraoz/fix-travis
Fix travis build
2017-03-20 15:25:59 -03:00
960500a078 try to fix travis 2 2017-03-20 15:19:14 -03:00
ce25e2db98 try to fix travis 1 2017-03-20 11:58:56 -03:00
463ae97117 Merge pull request #160 from Recmo/feature/token-killable
Add TokenKillable
2017-03-20 11:27:19 -03:00
350ab09855 Add comments for TransferableToken 2017-03-20 10:16:48 +01:00
312220fe63 Merge pull request #162 from AragonOne/transaferable
Decouple transferable logic from VestedToken
2017-03-15 16:09:13 -03:00
72029b6847 Decouple transferable logic from VestedToken 2017-03-14 20:22:18 +01:00
525a5522b0 Add TokenKillable 2017-03-14 14:09:59 +00:00
9c5975a706 Merge pull request #158 from maraoz/update-readme
update README on new code distribution strategy (ethpm)
2017-03-09 18:13:27 -03:00
9006f89f9f update README on new code distribution 2017-03-09 18:02:41 -03:00
15169b245b v1.0.4 2017-03-09 15:04:14 -03:00
cc222c02a9 Merge pull request #156 from maraoz/ethpm
Add ethpm support
2017-03-09 15:00:09 -03:00
29ab824e5c revert TokenGrant instantiation to old style 2017-03-09 14:37:02 -03:00
8d828bc789 fix ethpm.json 2017-03-07 16:57:51 -03:00
f038cdc9c9 fix ethpm.json 2017-03-07 16:57:12 -03:00
72d31c2471 attempt to publish to ethpm 2017-03-07 16:56:13 -03:00
b31c92c27c ethpm integration 2017-03-07 16:17:48 -03:00
eb2cb5dd06 v1.0.3 2017-03-06 11:14:18 -03:00
52d0df7961 Merge pull request #155 from AragonOne/vested-transfer-from
[VestedToken] Check if tokens can be transferred on transferFrom
2017-03-06 11:11:29 -03:00
12aadbed51 Move check to transferFrom and add tests 2017-03-06 11:09:01 +01:00
23703280ee Check if tokens are transaferable on approve 2017-03-06 11:02:57 +01:00
5e50090da0 Merge pull request #151 from AragonOne/clarity
Add clarifying comment to standard token transfer from function
2017-03-01 17:16:15 -03:00
b9257a1092 Add clarifying comment to standard token transfer from function 2017-02-27 16:32:42 +01:00
60 changed files with 1076 additions and 252 deletions

View File

@ -2,9 +2,9 @@
[![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)
Zeppelin is a library for writing secure [Smart Contracts](https://en.wikipedia.org/wiki/Smart_contract) on Ethereum.
OpenZeppelin 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 OpenZeppelin, 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](http://solidity.readthedocs.io/en/develop/).
@ -12,64 +12,49 @@ With Zeppelin, you can build distributed applications, protocols and organizatio
## 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`.
OpenZeppelin integrates with [Truffle](https://github.com/ConsenSys/truffle), an Ethereum development environment. Please install Truffle and initialize your project with `truffle init`.
```sh
npm install -g truffle
npm install -g truffle@beta
mkdir myproject && cd myproject
truffle init
```
To install the Zeppelin library, run:
To install the OpenZeppelin library, run:
```sh
npm i zeppelin-solidity
truffle install zeppelin
```
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:
After that, you'll get all the library's contracts in the `installed_contracts` folder. You can use the contracts in the library like so:
```js
import "./zeppelin/Ownable.sol";
import 'zeppelin/ownership/Ownable.sol';
contract MyContract is Ownable {
...
}
```
> 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:
```js
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).
## Security
Zeppelin is meant to provide secure, tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problem you might experience.
OpenZeppelin is meant to provide secure, tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problem you might experience.
If you find a security issue, please email [security@openzeppelin.org](mailto:security@openzeppelin.org).
## Developer Resources
Building a distributed application, protocol or organization with Zeppelin?
Building a distributed application, protocol or organization with OpenZeppelin?
- 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://slack.openzeppelin.org/
Interested in contributing to Zeppelin?
Interested in contributing to OpenZeppelin?
- 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
## Collaborating organizations and audits by Zeppelin
## Collaborating organizations and audits by OpenZeppelin
- [Golem](https://golem.network/)
- [Mediachain](http://www.mediachain.io/)
- [Truffle](http://truffleframework.com/)
@ -82,6 +67,7 @@ Interested in contributing to Zeppelin?
- [Signatura](https://signatura.co/)
- [Ether.camp](http://www.ether.camp/)
- [Aragon](https://aragon.one/)
- [Wings](https://wings.ai/)
among others...

View File

@ -2,7 +2,7 @@ pragma solidity ^0.4.8;
import './payment/PullPayment.sol';
import './lifecycle/Killable.sol';
import './lifecycle/Destructible.sol';
/*
@ -10,7 +10,7 @@ import './lifecycle/Killable.sol';
*
* This bounty will pay out to a researcher if they break invariant logic of the contract.
*/
contract Bounty is PullPayment, Killable {
contract Bounty is PullPayment, Destructible {
bool public claimed;
mapping(address => address) public researchers;

View File

@ -1,9 +1,5 @@
pragma solidity ^0.4.8;
import './ownership/Shareable.sol';
/*
* DayLimit
*

View File

@ -24,9 +24,9 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
Shareable(_owners, _required)
DayLimit(_daylimit) { }
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
// destroys the contract sending everything to `_to`.
function destroy(address _to) onlymanyowners(keccak256(msg.data)) external {
selfdestruct(_to);
}
// gets called when no other function matches
@ -51,7 +51,7 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
return 0;
}
// determine our operation hash.
_r = sha3(msg.data, block.number);
_r = keccak256(msg.data, block.number);
if (!confirm(_r) && txs[_r].to == 0) {
txs[_r].to = _to;
txs[_r].value = _value;
@ -73,11 +73,11 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
}
}
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
function setDailyLimit(uint _newLimit) onlymanyowners(keccak256(msg.data)) external {
_setDailyLimit(_newLimit);
}
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
function resetSpentToday() onlymanyowners(keccak256(msg.data)) external {
_resetSpentToday();
}

View File

@ -0,0 +1,28 @@
pragma solidity ^0.4.8;
/// @title Helps contracts guard agains rentrancy attacks.
/// @author Remco Bloemen <remco@2π.com>
/// @notice If you mark a function `nonReentrant`, you should also
/// mark it `external`.
contract ReentrancyGuard {
/// @dev We use a single lock for the whole contract.
bool private rentrancy_lock = false;
/// Prevent contract from calling itself, directly or indirectly.
/// @notice If you mark a function `nonReentrant`, you should also
/// mark it `external`. Calling one nonReentrant function from
/// another is not supported. Instead, you can implement a
/// `private` function doing the actual work, and a `external`
/// wrapper marked as `nonReentrant`.
modifier nonReentrant() {
if(rentrancy_lock == false) {
rentrancy_lock = true;
_;
rentrancy_lock = false;
} else {
throw;
}
}
}

View File

@ -4,28 +4,28 @@ pragma solidity ^0.4.8;
/**
* Math operations with safety checks
*/
contract SafeMath {
function safeMul(uint a, uint b) internal returns (uint) {
library SafeMath {
function mul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeDiv(uint a, uint b) internal returns (uint) {
function div(uint a, uint b) internal returns (uint) {
assert(b > 0);
uint c = a / b;
assert(a == b * c + a % b);
return c;
}
function safeSub(uint a, uint b) internal returns (uint) {
function sub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal returns (uint) {
function add(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
assert(c >= a);
return c;
}

View File

@ -0,0 +1,15 @@
pragma solidity ^0.4.8;
import "../ownership/Ownable.sol";
/*
* Destructible
* Base contract that can be destroyed by owner. All funds in contract will be sent to the owner.
*/
contract Destructible is Ownable {
function destroy() onlyOwner {
selfdestruct(owner);
}
}

View File

@ -1,15 +0,0 @@
pragma solidity ^0.4.8;
import "../ownership/Ownable.sol";
/*
* Killable
* Base contract that can be killed by owner. All funds in contract will be sent to the owner.
*/
contract Killable is Ownable {
function kill() onlyOwner {
selfdestruct(owner);
}
}

View File

@ -4,6 +4,7 @@ pragma solidity ^0.4.8;
import '../ownership/Ownable.sol';
// This is a truffle contract, needed for truffle integration, not meant for use by Zeppelin users.
contract Migrations is Ownable {
uint public lastCompletedMigration;

View File

@ -6,32 +6,36 @@ import "../ownership/Ownable.sol";
/*
* Pausable
* Abstract contract that allows children to implement an
* emergency stop mechanism.
* Abstract contract that allows children to implement a
* pause mechanism.
*/
contract Pausable is Ownable {
bool public stopped;
event Pause();
event Unpause();
modifier stopInEmergency {
if (!stopped) {
_;
}
}
modifier onlyInEmergency {
if (stopped) {
_;
}
bool public paused = false;
modifier whenNotPaused() {
if (paused) throw;
_;
}
// called by the owner on emergency, triggers stopped state
function emergencyStop() external onlyOwner {
stopped = true;
modifier whenPaused {
if (!paused) throw;
_;
}
// called by the owner on end of emergency, returns to normal state
function release() external onlyOwner onlyInEmergency {
stopped = false;
// called by the owner to pause, triggers stopped state
function pause() onlyOwner whenNotPaused returns (bool) {
paused = true;
Pause();
return true;
}
// called by the owner to unpause, returns to normal state
function unpause() onlyOwner whenPaused returns (bool) {
paused = false;
Unpause();
return true;
}
}

View File

@ -0,0 +1,30 @@
pragma solidity ^0.4.8;
import "../ownership/Ownable.sol";
import "../token/ERC20Basic.sol";
/// @title TokenDestructible:
/// @author Remco Bloemen <remco@2π.com>
///.Base contract that can be destroyed by owner. All funds in contract including
/// listed tokens will be sent to the owner
contract TokenDestructible is Ownable {
/// @notice Terminate contract and refund to owner
/// @param tokens List of addresses of ERC20 or ERC20Basic token contracts to
// refund
/// @notice The called token contracts could try to re-enter this contract.
// Only supply token contracts you
function destroy(address[] tokens) onlyOwner {
// Transfer tokens to owner
for(uint i = 0; i < tokens.length; i++) {
ERC20Basic token = ERC20Basic(tokens[i]);
uint256 balance = token.balanceOf(this);
token.transfer(owner, balance);
}
// Transfer Eth to owner and terminate contract
selfdestruct(owner);
}
}

View File

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

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.0;
pragma solidity ^0.4.8;
import './Ownable.sol';
/*

View File

@ -1,7 +1,6 @@
pragma solidity ^0.4.8;
import './Ownable.sol';
import './Claimable.sol';
@ -9,7 +8,7 @@ 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 {
contract DelayedClaimable is Claimable {
uint public end;
uint public start;

View File

@ -0,0 +1,18 @@
pragma solidity ^0.4.8;
import "./Ownable.sol";
/// @title Contracts that should not own Contracts
/// @author Remco Bloemen <remco@2π.com>
///
/// Should contracts (anything Ownable) end up being owned by
/// this contract, it allows the owner of this contract to
/// reclaim ownership of the contracts.
contract HasNoContracts is Ownable {
/// Reclaim ownership of Ownable contracts
function reclaimContract(address contractAddr) external onlyOwner {
Ownable contractInst = Ownable(contractAddr);
contractInst.transferOwnership(owner);
}
}

View File

@ -0,0 +1,42 @@
pragma solidity ^0.4.8;
import "./Ownable.sol";
/// @title Contracts that should not own Ether
/// @author Remco Bloemen <remco@2π.com>
///
/// This tries to block incoming ether to prevent accidental
/// loss of Ether. Should Ether end up in the contrat, it will
/// allow the owner to reclaim this ether.
///
/// @notice Ether can still be send to this contract by:
/// * calling functions labeled `payable`
/// * `selfdestruct(contract_address)`
/// * mining directly to the contract address
contract HasNoEther is Ownable {
/// Constructor that rejects incoming Ether
/// @dev The flag `payable` is added so we can access `msg.value`
/// without compiler warning. If we leave out payable, then
/// Solidity will allow inheriting contracts to implement a
/// payable constructor. By doing it this way we prevent a
/// payable constructor from working.
/// Alternatively we could use assembly to access msg.value.
function HasNoEther() payable {
if(msg.value > 0) {
throw;
}
}
/// Disallow direct send by settings a default function without `payable`
function() external {
}
/// Transfer all Ether owned by the contract to the owner
/// @dev What if owner is itself a contract marked HasNoEther?
function reclaimEther() external onlyOwner {
if(!owner.send(this.balance)) {
throw;
}
}
}

View File

@ -0,0 +1,26 @@
pragma solidity ^0.4.8;
import "./Ownable.sol";
import "../token/ERC20Basic.sol";
/// @title Contracts that should not own Tokens
/// @author Remco Bloemen <remco@2π.com>
///
/// This blocks incoming ERC23 tokens to prevent accidental
/// loss of tokens. Should tokens (any ERC20Basic compatible)
/// end up in the contract, it allows the owner to reclaim
/// the tokens.
contract HasNoTokens is Ownable {
/// Reject all ERC23 compatible tokens
function tokenFallback(address from_, uint value_, bytes data_) external {
throw;
}
/// Reclaim all ERC20Basic compatible tokens
function reclaimToken(address tokenAddr) external onlyOwner {
ERC20Basic tokenInst = ERC20Basic(tokenAddr);
uint256 balance = tokenInst.balanceOf(this);
tokenInst.transfer(owner, balance);
}
}

View File

@ -0,0 +1,14 @@
pragma solidity ^0.4.8;
import "./HasNoEther.sol";
import "./HasNoTokens.sol";
import "./HasNoContracts.sol";
/// @title Base contract for contracts that should not own things.
/// @author Remco Bloemen <remco@2π.com>
///
/// Solves a class of errors where a contract accidentally
/// becomes owner of Ether, Tokens or Owned contracts. See
/// respective base contracts for details.
contract NoOwner is HasNoEther, HasNoTokens, HasNoContracts {
}

View File

@ -23,9 +23,9 @@ contract Shareable {
uint public required;
// list of owners
uint[256] owners;
address[256] owners;
// index on the list of owners to allow reverse lookup
mapping(uint => uint) ownerIndex;
mapping(address => uint) ownerIndex;
// the ongoing operations.
mapping(bytes32 => PendingState) pendings;
bytes32[] pendingsIndex;
@ -57,18 +57,21 @@ contract Shareable {
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function Shareable(address[] _owners, uint _required) {
owners[1] = uint(msg.sender);
ownerIndex[uint(msg.sender)] = 1;
owners[1] = msg.sender;
ownerIndex[msg.sender] = 1;
for (uint i = 0; i < _owners.length; ++i) {
owners[2 + i] = uint(_owners[i]);
ownerIndex[uint(_owners[i])] = 2 + i;
owners[2 + i] = _owners[i];
ownerIndex[_owners[i]] = 2 + i;
}
required = _required;
if (required > owners.length) {
throw;
}
}
// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint index = ownerIndex[uint(msg.sender)];
uint index = ownerIndex[msg.sender];
// make sure they're an owner
if (index == 0) {
return;
@ -88,12 +91,12 @@ contract Shareable {
}
function isOwner(address _addr) constant returns (bool) {
return ownerIndex[uint(_addr)] > 0;
return ownerIndex[_addr] > 0;
}
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
var pending = pendings[_operation];
uint index = ownerIndex[uint(_owner)];
uint index = ownerIndex[_owner];
// make sure they're an owner
if (index == 0) {
@ -105,12 +108,13 @@ contract Shareable {
return !(pending.ownersDone & ownerIndexBit == 0);
}
// returns true when operation can be executed
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
// determine what index the present sender is:
uint index = ownerIndex[uint(msg.sender)];
uint index = ownerIndex[msg.sender];
// make sure they're an owner
if (index == 0) {
return;
throw;
}
var pending = pendings[_operation];
@ -140,6 +144,7 @@ contract Shareable {
pending.ownersDone |= ownerIndexBit;
}
}
return false;
}
function clearPending() internal {

View File

@ -1,24 +1,31 @@
pragma solidity ^0.4.8;
import '../SafeMath.sol';
/*
* PullPayment
* Base contract supporting async send for pull payments.
* Inherit from this contract and use asyncSend instead of send.
*/
contract PullPayment {
using SafeMath for uint;
mapping(address => uint) public payments;
uint public totalPayments;
// store sent amount as credit to be pulled, called by payer
function asyncSend(address dest, uint amount) internal {
payments[dest] += amount;
payments[dest] = payments[dest].add(amount);
totalPayments = totalPayments.add(amount);
}
// withdraw accumulated balance, called by payee
function withdrawPayments() {
address payee = msg.sender;
uint payment = payments[payee];
if (payment == 0) {
throw;
}
@ -27,6 +34,7 @@ contract PullPayment {
throw;
}
totalPayments = totalPayments.sub(payment);
payments[payee] = 0;
if (!payee.send(payment)) {

View File

@ -9,13 +9,24 @@ import '../SafeMath.sol';
* Basic token
* Basic version of StandardToken, with no allowances
*/
contract BasicToken is ERC20Basic, SafeMath {
contract BasicToken is ERC20Basic {
using SafeMath for uint;
mapping(address => uint) balances;
function transfer(address _to, uint _value) {
balances[msg.sender] = safeSub(balances[msg.sender], _value);
balances[_to] = safeAdd(balances[_to], _value);
/*
* Fix for the ERC20 short address attack
*/
modifier onlyPayloadSize(uint size) {
if(msg.data.length < size + 4) {
throw;
}
_;
}
function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
}

View File

@ -8,15 +8,20 @@ import "./StandardToken.sol";
* CrowdsaleToken
*
* Simple ERC20 Token example, with crowdsale token creation
* IMPORTANT NOTE: do not use or deploy this contract as-is.
* It needs some changes to be production ready.
*/
contract CrowdsaleToken is StandardToken {
string public name = "CrowdsaleToken";
string public symbol = "CRW";
uint public decimals = 18;
string public constant name = "CrowdsaleToken";
string public constant symbol = "CRW";
uint public constant decimals = 18;
// replace with your fund collection multisig address
address public constant multisig = 0x0;
// 1 ether = 500 example tokens
uint PRICE = 500;
uint public constant PRICE = 500;
function () payable {
createTokens(msg.sender);
@ -27,10 +32,14 @@ contract CrowdsaleToken is StandardToken {
throw;
}
uint tokens = safeMul(msg.value, getPrice());
uint tokens = msg.value.mul(getPrice());
totalSupply = totalSupply.add(tokens);
totalSupply = safeAdd(totalSupply, tokens);
balances[recipient] = safeAdd(balances[recipient], tokens);
balances[recipient] = balances[recipient].add(tokens);
if (!multisig.send(msg.value)) {
throw;
}
}
// replace this with any other price function

View File

@ -1,18 +1,16 @@
pragma solidity ^0.4.8;
import './ERC20Basic.sol';
/*
* ERC20 interface
* see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 {
uint public totalSupply;
function balanceOf(address who) constant returns (uint);
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) constant returns (uint);
function transfer(address to, uint value) returns (bool ok);
function transferFrom(address from, address to, uint value) returns (bool ok);
function approve(address spender, uint value) returns (bool ok);
event Transfer(address indexed from, address indexed to, uint value);
function transferFrom(address from, address to, uint value);
function approve(address spender, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}

View File

@ -0,0 +1,49 @@
pragma solidity ^0.4.8;
import "./ERC20.sol";
/*
LimitedTransferToken defines the generic interface and the implementation
to limit token transferability for different events.
It is intended to be used as a base class for other token contracts.
Overwriting transferableTokens(address holder, uint64 time) is the way to provide
the specific logic for limiting token transferability for a holder over time.
LimitedTransferToken has been designed to allow for different limiting factors,
this can be achieved by recursively calling super.transferableTokens() until the
base class is hit. For example:
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
return min256(unlockedTokens, super.transferableTokens(holder, time));
}
A working example is VestedToken.sol:
https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/VestedToken.sol
*/
contract LimitedTransferToken is ERC20 {
// Checks whether it can transfer or otherwise throws.
modifier canTransfer(address _sender, uint _value) {
if (_value > transferableTokens(_sender, uint64(now))) throw;
_;
}
// Checks modifier and allows transfer if tokens are not locked.
function transfer(address _to, uint _value) canTransfer(msg.sender, _value) {
return super.transfer(_to, _value);
}
// Checks modifier and allows transfer if tokens are not locked.
function transferFrom(address _from, address _to, uint _value) canTransfer(_from, _value) {
return super.transferFrom(_from, _to, _value);
}
// Default transferable tokens function returns all tokens for a holder (no limit).
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
return balanceOf(holder);
}
}

View File

@ -0,0 +1,43 @@
pragma solidity ^0.4.8;
import './StandardToken.sol';
import '../ownership/Ownable.sol';
/**
* Mintable token
*
* Simple ERC20 Token example, with mintable token creation
* Issue:
* https://github.com/OpenZeppelin/zeppelin-solidity/issues/120
* Based on code by TokenMarketNet:
* https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
*/
contract MintableToken is StandardToken, Ownable {
event Mint(address indexed to, uint value);
event MintFinished();
bool public mintingFinished = false;
uint public totalSupply = 0;
modifier canMint() {
if(mintingFinished) throw;
_;
}
function mint(address _to, uint _amount) onlyOwner canMint returns (bool) {
totalSupply = totalSupply.add(_amount);
balances[_to] = balances[_to].add(_amount);
Mint(_to, _amount);
return true;
}
function finishMinting() onlyOwner returns (bool) {
mintingFinished = true;
MintFinished();
return true;
}
}

View File

@ -0,0 +1,25 @@
pragma solidity ^0.4.8;
import './StandardToken.sol';
import '../lifecycle/Pausable.sol';
/**
* Pausable token
*
* Simple ERC20 Token example, with pausable token creation
* Issue:
* https://github.com/OpenZeppelin/zeppelin-solidity/issues/194
* Based on code by BCAPtoken:
* https://github.com/BCAPtoken/BCAPToken/blob/5cb5e76338cc47343ba9268663a915337c8b268e/sol/BCAPToken.sol#L27
**/
contract PausableToken is Pausable, StandardToken {
function transfer(address _to, uint _value) whenNotPaused {
return super.transfer(_to, _value);
}
function transferFrom(address _from, address _to, uint _value) whenNotPaused {
return super.transferFrom(_from, _to, _value);
}
}

View File

@ -1,8 +1,8 @@
pragma solidity ^0.4.8;
import './BasicToken.sol';
import './ERC20.sol';
import '../SafeMath.sol';
/**
@ -12,36 +12,25 @@ import '../SafeMath.sol';
* Based on code by FirstBlood:
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, SafeMath {
contract StandardToken is BasicToken, ERC20 {
mapping(address => uint) balances;
mapping (address => mapping (address => uint)) allowed;
function transfer(address _to, uint _value) returns (bool success) {
balances[msg.sender] = safeSub(balances[msg.sender], _value);
balances[_to] = safeAdd(balances[_to], _value);
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint _value) returns (bool success) {
function transferFrom(address _from, address _to, uint _value) {
var _allowance = allowed[_from][msg.sender];
balances[_to] = safeAdd(balances[_to], _value);
balances[_from] = safeSub(balances[_from], _value);
allowed[_from][msg.sender] = safeSub(_allowance, _value);
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
balances[_to] = balances[_to].add(_value);
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = _allowance.sub(_value);
Transfer(_from, _to, _value);
return true;
}
function balanceOf(address _owner) constant returns (uint balance) {
return balances[_owner];
}
function approve(address _spender, uint _value) returns (bool success) {
function approve(address _spender, uint _value) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint remaining) {

View File

@ -2,9 +2,9 @@ pragma solidity ^0.4.8;
import "./StandardToken.sol";
import "./LimitedTransferToken.sol";
contract VestedToken is StandardToken {
contract VestedToken is StandardToken, LimitedTransferToken {
struct TokenGrant {
address granter;
uint256 value;
@ -32,7 +32,8 @@ contract VestedToken is StandardToken {
throw;
}
TokenGrant memory grant = TokenGrant({start: _start, value: _value, cliff: _cliff, vesting: _vesting, granter: msg.sender});
TokenGrant memory grant = TokenGrant(msg.sender, _value, _cliff, _vesting, _start);
grants[_to].push(grant);
transfer(_to, _value);
@ -51,8 +52,8 @@ contract VestedToken is StandardToken {
grants[_holder][_grantId] = grants[_holder][grants[_holder].length - 1];
grants[_holder].length -= 1;
balances[msg.sender] = safeAdd(balances[msg.sender], nonVested);
balances[_holder] = safeSub(balances[_holder], nonVested);
balances[msg.sender] = balances[msg.sender].add(nonVested);
balances[_holder] = balances[_holder].sub(nonVested);
Transfer(_holder, msg.sender, nonVested);
}
@ -93,45 +94,37 @@ contract VestedToken is StandardToken {
if (time < cliff) {
return 0;
}
if (time > vesting) {
if (time >= vesting) {
return tokens;
}
uint256 cliffTokens = safeDiv(safeMul(tokens, safeSub(cliff, start)), safeSub(vesting, start));
uint256 cliffTokens = tokens.mul(cliff.sub(start)).div(vesting.sub(start));
vestedTokens = cliffTokens;
uint256 vestingTokens = safeSub(tokens, cliffTokens);
uint256 vestingTokens = tokens.sub(cliffTokens);
vestedTokens = safeAdd(vestedTokens, safeDiv(safeMul(vestingTokens, safeSub(time, cliff)), safeSub(vesting, start)));
vestedTokens = vestedTokens.add(vestingTokens.mul(time.sub(cliff)).div(vesting.sub(cliff)));
}
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
return safeSub(grant.value, vestedTokens(grant, time));
return grant.value.sub(vestedTokens(grant, time));
}
function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) {
date = uint64(now);
uint256 grantIndex = grants[holder].length;
for (uint256 i = 0; i < grantIndex; i++) {
date = max64(grants[holder][i].vesting, date);
date = SafeMath.max64(grants[holder][i].vesting, date);
}
}
function transferableTokens(address holder, uint64 time) constant public returns (uint256 nonVested) {
uint256 grantIndex = grants[holder].length;
for (uint256 i = 0; i < grantIndex; i++) {
nonVested = safeAdd(nonVested, nonVestedTokens(grants[holder][i], time));
uint256 current = nonVestedTokens(grants[holder][i], time);
nonVested = nonVested.add(current);
}
return safeSub(balances[holder], nonVested);
}
function transfer(address _to, uint _value) returns (bool success) {
if (_value > transferableTokens(msg.sender, uint64(now))) {
throw;
}
return super.transfer(_to, _value);
return SafeMath.min256(balances[holder].sub(nonVested), super.transferableTokens(holder, time));
}
}

View File

@ -55,6 +55,6 @@ 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.::
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, destroy the contract so that all the rewards go back to the owner.::
bounty.kill();
bounty.destroy();

View File

@ -27,7 +27,7 @@ The code is open-source, and `available on github <https://github.com/OpenZeppel
ownable
Pausable
killable
destructible
claimable
migrations
safemath

View File

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

View File

@ -1,26 +1,27 @@
Pausable
=============================================
Base contract that provides an emergency stop mechanism.
Base contract that provides a pause mechanism.
Inherits from contract Ownable.
emergencyStop( ) external onlyOwner
pause() onlyOwner whenNotPaused returns (bool)
"""""""""""""""""""""""""""""""""""""
Triggers the stop mechanism on the contract. After this function is called (by the owner of the contract), any function with modifier stopInEmergency will not run.
Triggers pause mechanism on the contract. After this function is called (by the owner of the contract), any function with modifier whenNotPaused will not run.
modifier stopInEmergency
modifier whenNotPaused()
"""""""""""""""""""""""""""""""""""""
Prevents function from running if stop mechanism is activated.
Prevents function from running if pause mechanism is activated.
modifier onlyInEmergency
modifier whenPaused()
"""""""""""""""""""""""""""""""""""""
Only runs if stop mechanism is activated.
Only runs if pause mechanism is activated.
release( ) external onlyOwner onlyInEmergency
unpause() onlyOwner whenPaused returns (bool)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Deactivates the stop mechanism.
Deactivates the pause mechanism.

17
ethpm.json Normal file
View File

@ -0,0 +1,17 @@
{
"package_name": "zeppelin",
"version": "1.0.5",
"description": "Secure Smart Contract library for Solidity",
"authors": [
"Manuel Araoz <manuelaraoz@gmail.com>"
],
"keywords": [
"solidity",
"ethereum",
"smart",
"contracts",
"security",
"zeppelin"
],
"license": "MIT"
}

View File

@ -1,16 +1,8 @@
{
"name": "zeppelin-solidity",
"version": "1.0.2",
"version": "1.0.5",
"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",
"babel-register": "^6.23.0",
"ethereumjs-testrpc": "^3.0.2",
"truffle": "https://github.com/ConsenSys/truffle.git#3.1.9"
},
"scripts": {
"test": "scripts/test.sh",
"console": "truffle console",
@ -33,5 +25,16 @@
"bugs": {
"url": "https://github.com/OpenZeppelin/zeppelin-solidity/issues"
},
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity"
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity",
"dependencies": {
"truffle-hdwallet-provider": "0.0.3"
},
"devDependencies": {
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0",
"babel-register": "^6.23.0",
"ethereumjs-testrpc": "^3.0.2",
"truffle": "https://github.com/ConsenSys/truffle.git#3.1.9"
}
}

View File

@ -31,7 +31,7 @@ contract('Bounty', function(accounts) {
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
});
it('empties itself when killed', async function(){
it('empties itself when destroyed', async function(){
let owner = accounts[0];
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
@ -39,7 +39,7 @@ contract('Bounty', function(accounts) {
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
await bounty.kill();
await bounty.destroy();
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
});
@ -52,7 +52,7 @@ contract('Bounty', function(accounts) {
let bounty = await SecureTargetBounty.new();
let event = bounty.TargetCreated({});
event.watch(async function(err, result) {
let watcher = async function(err, result) {
event.stopWatching();
if (err) { throw err; }
@ -66,8 +66,8 @@ contract('Bounty', function(accounts) {
await bounty.claim(targetAddress, {from:researcher});
assert.isTrue(false); // should never reach here
} catch(error) {
let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty);
let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty);
}
try {
@ -77,8 +77,9 @@ contract('Bounty', function(accounts) {
assert.equal(reward,
web3.eth.getBalance(bounty.address).toNumber());
}
});
};
bounty.createTarget({from:researcher});
await awaitEvent(event, watcher);
});
});

18
test/Destructible.js Normal file
View File

@ -0,0 +1,18 @@
'use strict';
var Destructible = artifacts.require('../contracts/lifecycle/Destructible.sol');
require('./helpers/transactionMined.js');
contract('Destructible', function(accounts) {
it('should send balance to owner after destruction', async function() {
let destructible = await Destructible.new({from: accounts[0], value: web3.toWei('10','ether')});
let owner = await destructible.owner();
let initBalance = web3.eth.getBalance(owner);
await destructible.destroy({from: owner});
let newBalance = web3.eth.getBalance(owner);
assert.isTrue(newBalance > initBalance);
});
});

35
test/HasNoContracts.js Normal file
View File

@ -0,0 +1,35 @@
'use strict';
import expectThrow from './helpers/expectThrow';
import toPromise from './helpers/toPromise';
const Ownable = artifacts.require('../contracts/ownership/Ownable.sol');
const HasNoContracts = artifacts.require(
'../contracts/ownership/HasNoContracts.sol',
);
contract('HasNoContracts', function(accounts) {
let hasNoContracts = null;
let ownable = null;
beforeEach(async () => {
// Create contract and token
hasNoContracts = await HasNoContracts.new();
ownable = await Ownable.new();
// Force ownership into contract
await ownable.transferOwnership(hasNoContracts.address);
const owner = await ownable.owner();
assert.equal(owner, hasNoContracts.address);
});
it('should allow owner to reclaim contracts', async function() {
await hasNoContracts.reclaimContract(ownable.address);
const owner = await ownable.owner();
assert.equal(owner, accounts[0]);
});
it('should allow only owner to reclaim contracts', async function() {
await expectThrow(
hasNoContracts.reclaimContract(ownable.address, {from: accounts[1]}),
);
});
});

63
test/HasNoEther.js Normal file
View File

@ -0,0 +1,63 @@
'use strict';
import expectThrow from './helpers/expectThrow';
import toPromise from './helpers/toPromise';
const HasNoEther = artifacts.require('../contracts/lifecycle/HasNoEther.sol');
const HasNoEtherTest = artifacts.require('../helpers/HasNoEtherTest.sol');
const ForceEther = artifacts.require('../helpers/ForceEther.sol');
contract('HasNoEther', function(accounts) {
const amount = web3.toWei('1', 'ether');
it('should be constructorable', async function() {
let hasNoEther = await HasNoEtherTest.new();
});
it('should not accept ether in constructor', async function() {
await expectThrow(HasNoEtherTest.new({value: amount}));
});
it('should not accept ether', async function() {
let hasNoEther = await HasNoEtherTest.new();
await expectThrow(
toPromise(web3.eth.sendTransaction)({
from: accounts[1],
to: hasNoEther.address,
value: amount,
}),
);
});
it('should allow owner to reclaim ether', async function() {
// Create contract
let hasNoEther = await HasNoEtherTest.new();
const startBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(startBalance, 0);
// Force ether into it
await ForceEther.new(hasNoEther.address, {value: amount});
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(forcedBalance, amount);
// Reclaim
const ownerStartBalance = await web3.eth.getBalance(accounts[0]);
await hasNoEther.reclaimEther();
const ownerFinalBalance = await web3.eth.getBalance(accounts[0]);
const finalBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(finalBalance, 0);
assert.isAbove(ownerFinalBalance, ownerStartBalance);
});
it('should allow only owner to reclaim ether', async function() {
// Create contract
let hasNoEther = await HasNoEtherTest.new({from: accounts[0]});
// Force ether into it
await ForceEther.new(hasNoEther.address, {value: amount});
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
assert.equal(forcedBalance, amount);
// Reclaim
await expectThrow(hasNoEther.reclaimEther({from: accounts[1]}));
});
});

40
test/HasNoTokens.js Normal file
View File

@ -0,0 +1,40 @@
'use strict';
import expectThrow from './helpers/expectThrow';
import toPromise from './helpers/toPromise';
const HasNoTokens = artifacts.require('../contracts/lifecycle/HasNoTokens.sol');
const ERC23TokenMock = artifacts.require('./helpers/ERC23TokenMock.sol');
contract('HasNoTokens', function(accounts) {
let hasNoTokens = null;
let token = null;
beforeEach(async () => {
// Create contract and token
hasNoTokens = await HasNoTokens.new();
token = await ERC23TokenMock.new(accounts[0], 100);
// Force token into contract
await token.transfer(hasNoTokens.address, 10);
const startBalance = await token.balanceOf(hasNoTokens.address);
assert.equal(startBalance, 10);
});
it('should not accept ERC23 tokens', async function() {
await expectThrow(token.transferERC23(hasNoTokens.address, 10, ''));
});
it('should allow owner to reclaim tokens', async function() {
const ownerStartBalance = await token.balanceOf(accounts[0]);
await hasNoTokens.reclaimToken(token.address);
const ownerFinalBalance = await token.balanceOf(accounts[0]);
const finalBalance = await token.balanceOf(hasNoTokens.address);
assert.equal(finalBalance, 0);
assert.equal(ownerFinalBalance - ownerStartBalance, 10);
});
it('should allow only owner to reclaim tokens', async function() {
await expectThrow(
hasNoTokens.reclaimToken(token.address, {from: accounts[1]}),
);
});
});

View File

@ -1,18 +0,0 @@
'use strict';
var Killable = artifacts.require('../contracts/lifecycle/Killable.sol');
require('./helpers/transactionMined.js');
contract('Killable', function(accounts) {
it('should send balance to owner after death', async function() {
let killable = await Killable.new({from: accounts[0], value: web3.toWei('10','ether')});
let owner = await killable.owner();
let initBalance = web3.eth.getBalance(owner);
await killable.kill({from: owner});
let newBalance = web3.eth.getBalance(owner);
assert.isTrue(newBalance > initBalance);
});
});

35
test/MintableToken.js Normal file
View File

@ -0,0 +1,35 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var MintableToken = artifacts.require('../contracts/Tokens/MintableToken.sol');
contract('Mintable', function(accounts) {
let token;
beforeEach(async function() {
token = await MintableToken.new();
});
it('should start with a totalSupply of 0', async function() {
let totalSupply = await token.totalSupply();
assert.equal(totalSupply, 0);
});
it('should return mintingFinished false after construction', async function() {
let mintingFinished = await token.mintingFinished();
assert.equal(mintingFinished, false);
});
it('should mint a given amount of tokens to a given address', async function() {
await token.mint(accounts[0], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert(balance0, 100);
let totalSupply = await token.totalSupply();
assert(totalSupply, 100);
})
});

View File

@ -22,11 +22,11 @@ contract('MultisigWallet', function(accounts) {
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});
//Call destroy function from two different owner accounts, satisfying owners required
await wallet.destroy(accounts[0], {data: hash});
let txnHash = await wallet.destroy(accounts[0], {from: accounts[1], data: hash});
//Get balances of owner and wallet after kill function is complete, compare with previous values
//Get balances of owner and wallet after destroy function is complete, compare with previous values
let newOwnerBalance = web3.eth.getBalance(accounts[0]);
let newWalletBalance = web3.eth.getBalance(wallet.address);

View File

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

73
test/PausableToken.js Normal file
View File

@ -0,0 +1,73 @@
'user strict';
const assertJump = require('./helpers/assertJump');
var PausableTokenMock = artifacts.require('./helpers/PausableTokenMock.sol');
contract('PausableToken', function(accounts) {
let token;
beforeEach(async function() {
token = await PausableTokenMock.new(accounts[0], 100);
});
it('should return paused false after construction', async function() {
let paused = await token.paused();
assert.equal(paused, false);
});
it('should return paused true after pause', async function() {
await token.pause();
let paused = await token.paused();
assert.equal(paused, true);
});
it('should return paused false after pause and unpause', async function() {
await token.pause();
await token.unpause();
let paused = await token.paused();
assert.equal(paused, false);
});
it('should be able to transfer if transfers are unpaused', async function() {
await token.transfer(accounts[1], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
let balance1 = await token.balanceOf(accounts[1]);
assert.equal(balance1, 100);
});
it('should be able to transfer after transfers are paused and unpaused', async function() {
await token.pause();
await token.unpause();
await token.transfer(accounts[1], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
let balance1 = await token.balanceOf(accounts[1]);
assert.equal(balance1, 100);
});
it('should throw an error trying to transfer while transactions are paused', async function() {
await token.pause();
try {
await token.transfer(accounts[1], 100);
} catch (error) {
return assertJump(error);
}
assert.fail('should have thrown before');
});
it('should throw an error trying to transfer from another account while transactions are paused', async function() {
await token.pause();
try {
await token.transferFrom(accounts[0], accounts[1], 100);
} catch (error) {
return assertJump(error);
}
assert.fail('should have thrown before');
});
})

View File

@ -12,7 +12,9 @@ contract('PullPayment', function(accounts) {
let ppce = await PullPaymentMock.new();
let callSend = await ppce.callSend(accounts[0], AMOUNT);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, AMOUNT);
assert.equal(paymentsToAccount0, AMOUNT);
});
@ -21,7 +23,9 @@ contract('PullPayment', function(accounts) {
let call1 = await ppce.callSend(accounts[0], 200);
let call2 = await ppce.callSend(accounts[0], 300);
let paymentsToAccount0 = await ppce.payments(accounts[0]);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, 500);
assert.equal(paymentsToAccount0, 500);
});
@ -35,6 +39,9 @@ contract('PullPayment', function(accounts) {
let paymentsToAccount1 = await ppce.payments(accounts[1]);
assert.equal(paymentsToAccount1, 300);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, 500);
});
it("can withdraw payment", async function() {
@ -48,10 +55,16 @@ contract('PullPayment', function(accounts) {
let payment1 = await ppce.payments(payee);
assert.equal(payment1, AMOUNT);
let totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, AMOUNT);
let withdraw = await ppce.withdrawPayments({from: payee});
let payment2 = await ppce.payments(payee);
assert.equal(payment2, 0);
totalPayments = await ppce.totalPayments();
assert.equal(totalPayments, 0);
let balance = web3.eth.getBalance(payee);
assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
});

31
test/ReentrancyGuard.js Normal file
View File

@ -0,0 +1,31 @@
'use strict';
import expectThrow from './helpers/expectThrow';
const ReentrancyMock = artifacts.require('./helper/ReentrancyMock.sol');
const ReentrancyAttack = artifacts.require('./helper/ReentrancyAttack.sol');
contract('ReentrancyGuard', function(accounts) {
let reentrancyMock;
beforeEach(async function() {
reentrancyMock = await ReentrancyMock.new();
let initialCounter = await reentrancyMock.counter();
assert.equal(initialCounter, 0);
});
it('should not allow remote callback', async function() {
let attacker = await ReentrancyAttack.new();
await expectThrow(reentrancyMock.countAndCall(attacker.address));
});
// The following are more side-effects that intended behaviour:
// I put them here as documentation, and to monitor any changes
// in the side-effects.
it('should not allow local recursion', async function() {
await expectThrow(reentrancyMock.countLocalRecursive(10));
});
it('should not allow indirect local recursion', async function() {
await expectThrow(reentrancyMock.countThisRecursive(10));
});
});

View File

@ -1,26 +1,28 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var StandardTokenMock = artifacts.require("./helpers/StandardTokenMock.sol");
var StandardTokenMock = artifacts.require('./helpers/StandardTokenMock.sol');
contract('StandardToken', function(accounts) {
it("should return the correct totalSupply after construction", async function() {
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", async function() {
it('should return the correct allowance amount after approval', async function() {
let token = await StandardTokenMock.new();
let approve = await token.approve(accounts[1], 100);
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", async function() {
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);
await token.transfer(accounts[1], 100);
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
@ -28,20 +30,20 @@ contract('StandardToken', function(accounts) {
assert.equal(balance1, 100);
});
it("should throw an error when trying to transfer more than balance", async function() {
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);
await token.transfer(accounts[1], 101);
} catch(error) {
return assertJump(error);
}
assert.fail('should have thrown before');
});
it("should return correct balances after transfering from another account", async function() {
it('should return correct balances after transfering from another account', async function() {
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]});
await token.approve(accounts[1], 100);
await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
let balance0 = await token.balanceOf(accounts[0]);
assert.equal(balance0, 0);
@ -53,11 +55,11 @@ contract('StandardToken', function(accounts) {
assert.equal(balance2, 0);
});
it("should throw an error when trying to transfer more than allowed", async function() {
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);
await token.approve(accounts[1], 99);
try {
let transfer = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]});
} catch (error) {
return assertJump(error);
}

32
test/TokenDestructible.js Normal file
View File

@ -0,0 +1,32 @@
'use strict';
var TokenDestructible = artifacts.require('../contracts/lifecycle/TokenDestructible.sol');
var StandardTokenMock = artifacts.require("./helpers/StandardTokenMock.sol");
require('./helpers/transactionMined.js');
contract('TokenDestructible', function(accounts) {
it('should send balance to owner after destruction', async function() {
let destructible = await TokenDestructible.new({from: accounts[0], value: web3.toWei('10','ether')});
let owner = await destructible.owner();
let initBalance = web3.eth.getBalance(owner);
await destructible.destroy([], {from: owner});
let newBalance = web3.eth.getBalance(owner);
assert.isTrue(newBalance > initBalance);
});
it('should send tokens to owner after destruction', async function() {
let destructible = await TokenDestructible.new({from: accounts[0], value: web3.toWei('10','ether')});
let owner = await destructible.owner();
let token = await StandardTokenMock.new(destructible.address, 100);
let initContractBalance = await token.balanceOf(destructible.address);
let initOwnerBalance = await token.balanceOf(owner);
assert.equal(initContractBalance, 100);
assert.equal(initOwnerBalance, 0);
await destructible.destroy([token.address], {from: owner});
let newContractBalance = await token.balanceOf(destructible.address);
let newOwnerBalance = await token.balanceOf(owner);
assert.equal(newContractBalance, 0);
assert.equal(newOwnerBalance, 100);
});
});

View File

@ -40,7 +40,7 @@ contract('VestedToken', function(accounts) {
})
it('all tokens are transferable after vesting', async () => {
assert.equal(await token.transferableTokens(receiver, now + vesting + 1), tokenAmount);
assert.equal(await token.transferableTokens(receiver, now + vesting), tokenAmount);
})
it('throws when trying to transfer non vested tokens', async () => {
@ -52,6 +52,16 @@ contract('VestedToken', function(accounts) {
assert.fail('should have thrown before');
})
it('throws when trying to transfer from non vested tokens', async () => {
try {
await token.approve(accounts[7], 1, { from: receiver })
await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] })
} catch(error) {
return assertJump(error);
}
assert.fail('should have thrown before');
})
it('can be revoked by granter', async () => {
await token.revokeTokenGrant(receiver, 0, { from: granter });
assert.equal(await token.balanceOf(receiver), 0);
@ -74,9 +84,34 @@ contract('VestedToken', function(accounts) {
})
it('can transfer all tokens after vesting ends', async () => {
await timer(vesting + 1);
await timer(vesting);
await token.transfer(accounts[7], tokenAmount, { from: receiver })
assert.equal(await token.balanceOf(accounts[7]), tokenAmount);
})
it('can approve and transferFrom all tokens after vesting ends', async () => {
await timer(vesting);
await token.approve(accounts[7], tokenAmount, { from: receiver })
await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] })
assert.equal(await token.balanceOf(accounts[7]), tokenAmount);
})
it('can handle composed vesting schedules', async () => {
await timer(cliff);
await token.transfer(accounts[7], 12, { from: receiver })
assert.equal(await token.balanceOf(accounts[7]), 12);
let newNow = web3.eth.getBlock(web3.eth.blockNumber).timestamp
await token.grantVestedTokens(receiver, tokenAmount, newNow, newNow + cliff, newNow + vesting, { from: granter })
await token.transfer(accounts[7], 13, { from: receiver })
assert.equal(await token.balanceOf(accounts[7]), tokenAmount / 2);
assert.equal(await token.balanceOf(receiver), 3 * tokenAmount / 2)
assert.equal(await token.transferableTokens(receiver, newNow), 0)
await timer(vesting);
await token.transfer(accounts[7], 3 * tokenAmount / 2, { from: receiver })
assert.equal(await token.balanceOf(accounts[7]), tokenAmount * 2)
})
})
});

View File

@ -0,0 +1,33 @@
pragma solidity ^0.4.8;
import '../../contracts/token/BasicToken.sol';
contract ERC23ContractInterface {
function tokenFallback(address _from, uint _value, bytes _data) external;
}
contract ERC23TokenMock is BasicToken {
function ERC23TokenMock(address initialAccount, uint initialBalance) {
balances[initialAccount] = initialBalance;
totalSupply = initialBalance;
}
// ERC23 compatible transfer function (except the name)
function transferERC23(address _to, uint _value, bytes _data)
returns (bool success)
{
transfer(_to, _value);
bool is_contract = false;
assembly {
is_contract := not(iszero(extcodesize(_to)))
}
if(is_contract) {
ERC23ContractInterface receiver = ERC23ContractInterface(_to);
receiver.tokenFallback(msg.sender, _value, _data);
}
return true;
}
}

View File

@ -0,0 +1,13 @@
pragma solidity ^0.4.8;
// @title Force Ether into a contract.
// @notice even
// if the contract is not payable.
// @notice To use, construct the contract with the target as argument.
// @author Remco Bloemen <remco@neufund.org>
contract ForceEther {
function ForceEther(address target) payable {
// Selfdestruct transfers all Ether to the arget address
selfdestruct(target);
}
}

View File

@ -0,0 +1,11 @@
pragma solidity ^0.4.8;
import "../../contracts/ownership/HasNoEther.sol";
contract HasNoEtherTest is HasNoEther {
// Constructor with explicit payable — should still fail
function HasNoEtherTest() payable {
}
}

View File

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

View File

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

View File

@ -0,0 +1,11 @@
pragma solidity ^0.4.8;
contract ReentrancyAttack {
function callSender(bytes4 data) {
if(!msg.sender.call(data)) {
throw;
}
}
}

View File

@ -0,0 +1,46 @@
pragma solidity ^0.4.8;
import '../../contracts/ReentrancyGuard.sol';
import './ReentrancyAttack.sol';
contract ReentrancyMock is ReentrancyGuard {
uint256 public counter;
function ReentrancyMock() {
counter = 0;
}
function count() private {
counter += 1;
}
function countLocalRecursive(uint n) public nonReentrant {
if(n > 0) {
count();
countLocalRecursive(n - 1);
}
}
function countThisRecursive(uint256 n) public nonReentrant {
bytes4 func = bytes4(keccak256("countThisRecursive(uint256)"));
if(n > 0) {
count();
bool result = this.call(func, n - 1);
if(result != true) {
throw;
}
}
}
function countAndCall(ReentrancyAttack attacker) public nonReentrant {
count();
bytes4 func = bytes4(keccak256("callback()"));
attacker.callSender(func);
}
function callback() external nonReentrant {
count();
}
}

View File

@ -4,18 +4,18 @@ pragma solidity ^0.4.8;
import '../../contracts/SafeMath.sol';
contract SafeMathMock is SafeMath {
contract SafeMathMock {
uint public result;
function multiply(uint a, uint b) {
result = safeMul(a, b);
result = SafeMath.mul(a, b);
}
function subtract(uint a, uint b) {
result = safeSub(a, b);
result = SafeMath.sub(a, b);
}
function add(uint a, uint b) {
result = safeAdd(a, b);
result = SafeMath.add(a, b);
}
}

View File

@ -0,0 +1,20 @@
export default async promise => {
try {
await promise;
} catch (error) {
// TODO: Check jump destination to destinguish between a throw
// and an actual invalid jump.
const invalidJump = error.message.search('invalid JUMP') >= 0;
// TODO: When we contract A calls contract B, and B throws, instead
// of an 'invalid jump', we get an 'out of gas' error. How do
// we distinguish this from an actual out of gas event? (The
// testrpc log actually show an 'invalid jump' event.)
const outOfGas = error.message.search('out of gas') >= 0;
assert(
invalidJump || outOfGas,
"Expected throw, got '" + error + "' instead",
);
return;
}
assert.fail('Expected throw not received');
};

View File

@ -0,0 +1,4 @@
export default func =>
(...args) =>
new Promise((accept, reject) =>
func(...args, (error, data) => error ? reject(error) : accept(data)));

View File

@ -1,12 +1,22 @@
require('babel-register');
require('babel-polyfill');
var HDWalletProvider = require('truffle-hdwallet-provider');
var mnemonic = '[REDACTED]';
var provider = new HDWalletProvider(mnemonic, 'https://ropsten.infura.io/');
module.exports = {
networks: {
development: {
host: "localhost",
host: 'localhost',
port: 8545,
network_id: "*"
network_id: '*'
},
ropsten: {
provider: provider,
network_id: 3 // official id of the ropsten network
}
}
};