Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ac697be2c | |||
| f7a7fc3b06 | |||
| b4b6029f66 | |||
| 071040f3eb | |||
| cd78c20e0e | |||
| 453a198250 | |||
| 7592122e4d | |||
| 609869f087 | |||
| 4fad1505c7 | |||
| 5881429952 | |||
| 526ed43d61 | |||
| ab9591ba80 | |||
| a8bcb0fcfe | |||
| b420316783 | |||
| 37c6782f95 | |||
| 70a4395a97 | |||
| 31c05c4c7d | |||
| 22018fd374 | |||
| d1f63f5c7e | |||
| 5d75264f0f | |||
| a605f66972 | |||
| 0328250554 | |||
| 70b5ffd928 | |||
| d9b9ed227b | |||
| 14274f8677 | |||
| af604379d3 | |||
| 7d83a08658 | |||
| 2ccbfea8c5 | |||
| fad287007b | |||
| cd9f820b85 | |||
| 582166d378 | |||
| ab9eecb104 | |||
| 52120a8c42 | |||
| a1aa74f96d | |||
| 6ae7ac97f6 | |||
| ba6c036628 | |||
| 7a2fda9076 | |||
| 41d2fde952 | |||
| f0e7396619 | |||
| 6139500e97 | |||
| 0b88944b38 | |||
| 61e33197b2 | |||
| 025e7db765 | |||
| 6f311e72b3 | |||
| a344d42a00 | |||
| f2159be68d | |||
| 7f7238378c | |||
| 1d2b989e8e | |||
| bc0f9b0ea5 | |||
| a885dabc81 | |||
| 6673da46b1 | |||
| 2e9587477c | |||
| 967ee13565 | |||
| a2bd1bb7f6 | |||
| ac3b3652c3 | |||
| 166a1070e5 | |||
| 94d3c447b7 | |||
| 9ff82aecef | |||
| d1af3ef1b3 | |||
| ffce7e3b08 | |||
| f896790ca3 | |||
| 960500a078 | |||
| ce25e2db98 | |||
| 463ae97117 | |||
| 350ab09855 | |||
| 312220fe63 | |||
| 72029b6847 | |||
| 525a5522b0 | |||
| 9c5975a706 | |||
| 9006f89f9f | |||
| 15169b245b | |||
| cc222c02a9 | |||
| 29ab824e5c | |||
| 8d828bc789 | |||
| f038cdc9c9 | |||
| 72d31c2471 | |||
| b31c92c27c | |||
| eb2cb5dd06 | |||
| 52d0df7961 | |||
| 12aadbed51 | |||
| 23703280ee | |||
| 5e50090da0 | |||
| b9257a1092 | |||
| 8cd58303f9 | |||
| b134645637 | |||
| d41cc437b3 | |||
| 26335e669e | |||
| 52b66c744f | |||
| 65c9711754 | |||
| 3d6988cf90 | |||
| c10a2cf15c | |||
| 44082c029f | |||
| 8ba0f008df | |||
| fcabd9957e | |||
| 9f12e870a9 | |||
| d24a2ed9a8 | |||
| b1cb7b5975 | |||
| 9ab997d788 | |||
| 90654cb2c9 | |||
| 9bd51db2b2 | |||
| 7e7193ae61 | |||
| a69333e2b8 | |||
| 1f35727442 | |||
| cbc18e6dcc | |||
| 60b48b0235 | |||
| f4624837a3 | |||
| 0a5af4b8ac | |||
| 74dfd3511f | |||
| ee56abcc8a | |||
| b67f60929c | |||
| 0b71dcded2 | |||
| f305382ef4 | |||
| 917b129517 | |||
| abc646a95c | |||
| 1697518da8 | |||
| 93e7984c61 | |||
| 8d9e12eda3 | |||
| fb0a96332c | |||
| ba7a0f7649 | |||
| a72de719c9 | |||
| ec6e728c0f | |||
| 70fbc7fe27 | |||
| ced4e2da3f | |||
| ae73f1a564 | |||
| 26127ee36a | |||
| 2f0471fff2 | |||
| ad833fb40d | |||
| a544f1517c | |||
| 67ad6abdaa | |||
| 0dfb27ff5f | |||
| ca8f2f2362 | |||
| 2de7fec6dc | |||
| 342e7bf9ff |
48
.jshintrc
Normal file
48
.jshintrc
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
|
||||
"browser": true, // Standard browser globals e.g. `window`, `document`.
|
||||
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
|
||||
"curly": true, // Require {} for every new block or scope.
|
||||
"devel": false, // Allow development statements e.g. `console.log();`.
|
||||
"eqeqeq": true, // Require triple equals i.e. `===`.
|
||||
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
|
||||
"freeze": true, // Forbid overwriting prototypes of native objects such as Array, Date and so on.
|
||||
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
|
||||
"indent": 2, // Specify indentation spacing
|
||||
"latedef": true, // Prohibit variable use before definition.
|
||||
"newcap": false, // Require capitalization of all constructor functions e.g. `new F()`.
|
||||
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
|
||||
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
|
||||
"noempty": true, // Prohibit use of empty blocks.
|
||||
"nonew": true, // Prohibits the use of constructor functions for side-effects
|
||||
"quotmark": "single", // Define quotes to string values.
|
||||
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
|
||||
"smarttabs": false, // Supress warnings about mixed tabs and spaces
|
||||
"strict": true, // Require `use strict` pragma in every file.
|
||||
"trailing": true, // Prohibit trailing whitespaces.
|
||||
"undef": true, // Require all non-global variables be declared before they are used.
|
||||
"unused": true, // Warn unused variables.
|
||||
|
||||
"maxparams": 4, // Maximum number of parameters for a function
|
||||
"maxstatements": 15, // Maximum number of statements in a function
|
||||
"maxcomplexity": 10, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity)
|
||||
"maxdepth": 4, // Maximum depth of nested control structures
|
||||
"maxlen": 120, // Maximum number of cols in a line
|
||||
"multistr": true, // Allow use of multiline EOL escaping
|
||||
"experimental": ["asyncawait", "asyncreqawait"],
|
||||
|
||||
"predef": [ // Extra globals.
|
||||
"after",
|
||||
"afterEach",
|
||||
"before",
|
||||
"beforeEach",
|
||||
"define",
|
||||
"describe",
|
||||
"exports",
|
||||
"it",
|
||||
"web3",
|
||||
"artifacts",
|
||||
"contract",
|
||||
"assert"
|
||||
]
|
||||
}
|
||||
1
.soliumignore
Normal file
1
.soliumignore
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
||||
22
.soliumrc.json
Normal file
22
.soliumrc.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"custom-rules-filename": null,
|
||||
"rules": {
|
||||
"imports-on-top": true,
|
||||
"variable-declarations": true,
|
||||
"array-declarations": true,
|
||||
"operator-whitespace": true,
|
||||
"lbrace": true,
|
||||
"mixedcase": false,
|
||||
"camelcase": true,
|
||||
"uppercase": true,
|
||||
"no-with": true,
|
||||
"no-empty-blocks": true,
|
||||
"no-unused-vars": true,
|
||||
"double-quotes": true,
|
||||
"blank-lines": true,
|
||||
"indentation": true,
|
||||
"whitespace": true,
|
||||
"deprecated-suicide": true,
|
||||
"pragma-on-top": true
|
||||
}
|
||||
}
|
||||
@ -5,8 +5,8 @@ language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
before_install:
|
||||
- npm install truffle@3.1.9 -g
|
||||
- npm i -g ethereumjs-testrpc
|
||||
- npm i -g truffle
|
||||
script:
|
||||
- testrpc&
|
||||
- npm test
|
||||
- testrpc > /dev/null &
|
||||
- truffle test
|
||||
|
||||
@ -102,4 +102,6 @@ Main contributors will review your code and possibly ask for changes before your
|
||||
If you have any questions feel free to post them to
|
||||
[github.com/OpenZeppelin/zeppelin-solidity/issues](https://github.com/OpenZeppelin/zeppelin-solidity/issues).
|
||||
|
||||
Finally, if you're looking to collaborate and want to find easy tasks to start, [look at the issues we marked as easy](https://github.com/OpenZeppelin/zeppelin-solidity/labels/easy).
|
||||
|
||||
Thanks for your time and code!
|
||||
|
||||
47
README.md
47
README.md
@ -2,9 +2,9 @@
|
||||
[](https://www.npmjs.org/package/zeppelin-solidity)
|
||||
[](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,66 +12,51 @@ 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](https://golem.network/)
|
||||
- [Mediachain](http://www.mediachain.io/)
|
||||
- [Truffle](http://truffleframework.com/)
|
||||
- [Firstblood](http://firstblood.io/)
|
||||
- [Rootstock](http://www.rsk.co/)
|
||||
@ -81,6 +66,8 @@ Interested in contributing to Zeppelin?
|
||||
- [DemocracyEarth](http://democracy.earth/)
|
||||
- [Signatura](https://signatura.co/)
|
||||
- [Ether.camp](http://www.ether.camp/)
|
||||
- [Aragon](https://aragon.one/)
|
||||
- [Wings](https://wings.ai/)
|
||||
|
||||
among others...
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './PullPayment.sol';
|
||||
import './Killable.sol';
|
||||
import './payment/PullPayment.sol';
|
||||
import './lifecycle/Destructible.sol';
|
||||
|
||||
|
||||
/*
|
||||
@ -10,19 +10,20 @@ import './Killable.sol';
|
||||
*
|
||||
* This bounty will pay out to a researcher if they break invariant logic of the contract.
|
||||
*/
|
||||
contract Bounty is PullPayment, Killable {
|
||||
Target target;
|
||||
contract Bounty is PullPayment, Destructible {
|
||||
bool public claimed;
|
||||
mapping(address => address) public researchers;
|
||||
|
||||
event TargetCreated(address createdAddress);
|
||||
|
||||
function() payable {
|
||||
if (claimed) throw;
|
||||
if (claimed) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
function createTarget() returns(Target) {
|
||||
target = Target(deployContract());
|
||||
Target target = Target(deployContract());
|
||||
researchers[target] = msg.sender;
|
||||
TargetCreated(target);
|
||||
return target;
|
||||
@ -30,13 +31,11 @@ contract Bounty is PullPayment, Killable {
|
||||
|
||||
function deployContract() internal returns(address);
|
||||
|
||||
function checkInvariant() returns(bool){
|
||||
return target.checkInvariant();
|
||||
}
|
||||
|
||||
function claim(Target target) {
|
||||
address researcher = researchers[target];
|
||||
if (researcher == 0) throw;
|
||||
if (researcher == 0) {
|
||||
throw;
|
||||
}
|
||||
// Check Target contract invariants
|
||||
if (target.checkInvariant()) {
|
||||
throw;
|
||||
@ -47,6 +46,7 @@ contract Bounty is PullPayment, Killable {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Target
|
||||
*
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import './Shareable.sol';
|
||||
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
/*
|
||||
* DayLimit
|
||||
@ -12,33 +8,18 @@ import './Shareable.sol';
|
||||
* uses is specified in the modifier.
|
||||
*/
|
||||
contract DayLimit {
|
||||
// FIELDS
|
||||
|
||||
uint public dailyLimit;
|
||||
uint public spentToday;
|
||||
uint public lastDay;
|
||||
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple modifier for daily limit.
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (underLimit(_value))
|
||||
_;
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR
|
||||
// stores initial daily limit and records the present day's index.
|
||||
function DayLimit(uint _limit) {
|
||||
dailyLimit = _limit;
|
||||
lastDay = today();
|
||||
}
|
||||
|
||||
|
||||
// METHODS
|
||||
|
||||
// (re)sets the daily limit. doesn't alter the amount already spent today.
|
||||
// sets the daily limit. doesn't alter the amount already spent today
|
||||
function _setDailyLimit(uint _newLimit) internal {
|
||||
dailyLimit = _newLimit;
|
||||
}
|
||||
@ -48,9 +29,6 @@ contract DayLimit {
|
||||
spentToday = 0;
|
||||
}
|
||||
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal returns (bool) {
|
||||
@ -72,4 +50,13 @@ contract DayLimit {
|
||||
function today() private constant returns (uint) {
|
||||
return now / 1 days;
|
||||
}
|
||||
|
||||
|
||||
// simple modifier for daily limit.
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (!underLimit(_value)) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import './Ownable.sol';
|
||||
import './Claimable.sol';
|
||||
|
||||
/*
|
||||
* DelayedClaimable
|
||||
* Extension for the Claimable contract, where the ownership needs to be claimed before/after certain block number
|
||||
*/
|
||||
|
||||
contract DelayedClaimable is Ownable, Claimable {
|
||||
|
||||
uint public claimBeforeBlock;
|
||||
uint public claimAfterBlock;
|
||||
|
||||
function setClaimBlocks(uint _claimBeforeBlock, uint _claimAfterBlock) onlyOwner {
|
||||
if (_claimAfterBlock > claimBeforeBlock)
|
||||
throw;
|
||||
claimBeforeBlock = _claimBeforeBlock;
|
||||
claimAfterBlock = _claimAfterBlock;
|
||||
}
|
||||
|
||||
function claimOwnership() onlyPendingOwner {
|
||||
if ((block.number > claimBeforeBlock) || (block.number < claimAfterBlock))
|
||||
throw;
|
||||
owner = pendingOwner;
|
||||
pendingOwner = 0x0;
|
||||
claimBeforeBlock = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import "./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);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./Multisig.sol";
|
||||
import "./Shareable.sol";
|
||||
import "./ownership/Multisig.sol";
|
||||
import "./ownership/Shareable.sol";
|
||||
import "./DayLimit.sol";
|
||||
|
||||
|
||||
@ -13,30 +13,20 @@ import "./DayLimit.sol";
|
||||
* Wallet(w).from(anotherOwner).confirm(h);
|
||||
*/
|
||||
contract MultisigWallet is Multisig, Shareable, DayLimit {
|
||||
// TYPES
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR
|
||||
|
||||
// just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
|
||||
Shareable(_owners, _required)
|
||||
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
|
||||
Shareable(_owners, _required)
|
||||
DayLimit(_daylimit) { }
|
||||
|
||||
|
||||
// METHODS
|
||||
|
||||
// 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
|
||||
@ -61,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;
|
||||
@ -83,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();
|
||||
}
|
||||
|
||||
|
||||
28
contracts/ReentrancyGuard.sol
Normal file
28
contracts/ReentrancyGuard.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +1,53 @@
|
||||
pragma solidity ^0.4.4;
|
||||
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 safeSub(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 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;
|
||||
}
|
||||
|
||||
function max64(uint64 a, uint64 b) internal constant returns (uint64) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
function min64(uint64 a, uint64 b) internal constant returns (uint64) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function max256(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
function min256(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function assert(bool assertion) internal {
|
||||
if (!assertion) throw;
|
||||
if (!assertion) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import "./Ownable.sol";
|
||||
|
||||
|
||||
/*
|
||||
* Stoppable
|
||||
* Abstract contract that allows children to implement an
|
||||
* emergency stop mechanism.
|
||||
*/
|
||||
contract Stoppable is Ownable {
|
||||
bool public stopped;
|
||||
|
||||
modifier stopInEmergency { if (!stopped) _; }
|
||||
modifier onlyInEmergency { if (stopped) _; }
|
||||
|
||||
// called by the owner on emergency, triggers stopped state
|
||||
function emergencyStop() external onlyOwner {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
// called by the owner on end of emergency, returns to normal state
|
||||
function release() external onlyOwner onlyInEmergency {
|
||||
stopped = false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import '../PullPayment.sol';
|
||||
|
||||
|
||||
// UNSAFE CODE, DO NOT USE!
|
||||
contract BadArrayUse is PullPayment {
|
||||
address[] employees;
|
||||
|
||||
function payBonus() {
|
||||
for (var i = 0; i < employees.length; i++) {
|
||||
address employee = employees[i];
|
||||
uint bonus = calculateBonus(employee);
|
||||
asyncSend(employee, bonus);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateBonus(address employee) returns (uint) {
|
||||
// some expensive computation...
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
// UNSAFE CODE, DO NOT USE!
|
||||
contract BadFailEarly {
|
||||
|
||||
uint constant DEFAULT_SALARY = 50000;
|
||||
mapping(string => uint) nameToSalary;
|
||||
|
||||
function getSalary(string name) constant returns (uint) {
|
||||
if (bytes(name).length != 0 && nameToSalary[name] != 0) {
|
||||
return nameToSalary[name];
|
||||
} else {
|
||||
return DEFAULT_SALARY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
// UNSAFE CODE, DO NOT USE!
|
||||
contract BadPushPayments {
|
||||
|
||||
address highestBidder;
|
||||
uint highestBid;
|
||||
|
||||
function bid() payable {
|
||||
if (msg.value < highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
// return bid to previous winner
|
||||
if (!highestBidder.send(highestBid)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import '../PullPayment.sol';
|
||||
|
||||
|
||||
contract GoodArrayUse is PullPayment {
|
||||
address[] employees;
|
||||
mapping(address => uint) bonuses;
|
||||
|
||||
function payBonus() {
|
||||
for (uint i = 0; i < employees.length; i++) {
|
||||
address employee = employees[i];
|
||||
uint bonus = bonuses[employee];
|
||||
asyncSend(employee, bonus);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateBonus(address employee) returns (uint) {
|
||||
uint bonus = 0;
|
||||
// some expensive computation...
|
||||
bonuses[employee] = bonus;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
contract GoodFailEarly {
|
||||
|
||||
uint constant DEFAULT_SALARY = 50000;
|
||||
mapping(string => uint) nameToSalary;
|
||||
|
||||
function getSalary(string name) constant returns (uint) {
|
||||
if (bytes(name).length == 0) throw;
|
||||
if (nameToSalary[name] == 0) throw;
|
||||
|
||||
return nameToSalary[name];
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
contract GoodPullPayments {
|
||||
address highestBidder;
|
||||
uint highestBid;
|
||||
mapping(address => uint) refunds;
|
||||
|
||||
function bid() external payable {
|
||||
if (msg.value < highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
refunds[highestBidder] += highestBid;
|
||||
}
|
||||
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
|
||||
function withdrawBid() external {
|
||||
uint refund = refunds[msg.sender];
|
||||
refunds[msg.sender] = 0;
|
||||
if (!msg.sender.send(refund)) {
|
||||
refunds[msg.sender] = refund;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
/*
|
||||
* Proof of Existence example contract
|
||||
* see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05
|
||||
*/
|
||||
contract ProofOfExistence {
|
||||
|
||||
mapping (bytes32 => bool) public proofs;
|
||||
|
||||
// store a proof of existence in the contract state
|
||||
function storeProof(bytes32 proof) {
|
||||
proofs[proof] = true;
|
||||
}
|
||||
|
||||
// calculate and store the proof for a document
|
||||
function notarize(string document) {
|
||||
var proof = calculateProof(document);
|
||||
storeProof(proof);
|
||||
}
|
||||
|
||||
// helper function to get a document's sha256
|
||||
function calculateProof(string document) constant returns (bytes32) {
|
||||
return sha256(document);
|
||||
}
|
||||
|
||||
// check if a document has been notarized
|
||||
function checkDocument(string document) constant returns (bool) {
|
||||
var proof = calculateProof(document);
|
||||
return hasProof(proof);
|
||||
}
|
||||
|
||||
// returns true if proof is stored
|
||||
function hasProof(bytes32 proof) constant returns (bool) {
|
||||
return proofs[proof];
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import '../PullPayment.sol';
|
||||
|
||||
|
||||
contract PullPaymentBid is PullPayment {
|
||||
address public highestBidder;
|
||||
uint public highestBid;
|
||||
|
||||
function bid() external payable {
|
||||
if (msg.value <= highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
asyncSend(highestBidder, highestBid);
|
||||
}
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import '../PullPayment.sol';
|
||||
import '../Stoppable.sol';
|
||||
|
||||
|
||||
contract StoppableBid is Stoppable, PullPayment {
|
||||
address public highestBidder;
|
||||
uint public highestBid;
|
||||
|
||||
function bid() external payable stopInEmergency {
|
||||
if (msg.value <= highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
asyncSend(highestBidder, highestBid);
|
||||
}
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
|
||||
function withdraw() onlyInEmergency {
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
}
|
||||
15
contracts/lifecycle/Destructible.sol
Normal file
15
contracts/lifecycle/Destructible.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './Ownable.sol';
|
||||
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;
|
||||
|
||||
41
contracts/lifecycle/Pausable.sol
Normal file
41
contracts/lifecycle/Pausable.sol
Normal file
@ -0,0 +1,41 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "../ownership/Ownable.sol";
|
||||
|
||||
|
||||
/*
|
||||
* Pausable
|
||||
* Abstract contract that allows children to implement a
|
||||
* pause mechanism.
|
||||
*/
|
||||
contract Pausable is Ownable {
|
||||
event Pause();
|
||||
event Unpause();
|
||||
|
||||
bool public paused = false;
|
||||
|
||||
modifier whenNotPaused() {
|
||||
if (paused) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
modifier whenPaused {
|
||||
if (!paused) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
30
contracts/lifecycle/TokenDestructible.sol
Normal file
30
contracts/lifecycle/TokenDestructible.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './Ownable.sol';
|
||||
@ -13,8 +13,10 @@ contract Claimable is Ownable {
|
||||
address public pendingOwner;
|
||||
|
||||
modifier onlyPendingOwner() {
|
||||
if (msg.sender == pendingOwner)
|
||||
_;
|
||||
if (msg.sender != pendingOwner) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
function transferOwnership(address newOwner) onlyOwner {
|
||||
16
contracts/ownership/Contactable.sol
Normal file
16
contracts/ownership/Contactable.sol
Normal file
@ -0,0 +1,16 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
import './Ownable.sol';
|
||||
/*
|
||||
* Contactable token
|
||||
* Basic version of a contactable contract
|
||||
*/
|
||||
contract Contactable is Ownable{
|
||||
|
||||
string public contactInformation;
|
||||
|
||||
function setContactInformation(string info) onlyOwner{
|
||||
contactInformation = info;
|
||||
}
|
||||
|
||||
}
|
||||
31
contracts/ownership/DelayedClaimable.sol
Normal file
31
contracts/ownership/DelayedClaimable.sol
Normal file
@ -0,0 +1,31 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './Claimable.sol';
|
||||
|
||||
|
||||
/*
|
||||
* DelayedClaimable
|
||||
* Extension for the Claimable contract, where the ownership needs to be claimed before/after certain block number
|
||||
*/
|
||||
contract DelayedClaimable is Claimable {
|
||||
|
||||
uint public end;
|
||||
uint public start;
|
||||
|
||||
function setLimits(uint _start, uint _end) onlyOwner {
|
||||
if (_start > _end)
|
||||
throw;
|
||||
end = _end;
|
||||
start = _start;
|
||||
}
|
||||
|
||||
function claimOwnership() onlyPendingOwner {
|
||||
if ((block.number > end) || (block.number < start))
|
||||
throw;
|
||||
owner = pendingOwner;
|
||||
pendingOwner = 0x0;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
}
|
||||
18
contracts/ownership/HasNoContracts.sol
Normal file
18
contracts/ownership/HasNoContracts.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
42
contracts/ownership/HasNoEther.sol
Normal file
42
contracts/ownership/HasNoEther.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
contracts/ownership/HasNoTokens.sol
Normal file
26
contracts/ownership/HasNoTokens.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
14
contracts/ownership/NoOwner.sol
Normal file
14
contracts/ownership/NoOwner.sol
Normal 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 {
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
@ -15,12 +15,16 @@ contract Ownable {
|
||||
}
|
||||
|
||||
modifier onlyOwner() {
|
||||
if (msg.sender == owner)
|
||||
_;
|
||||
if (msg.sender != owner) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
function transferOwnership(address newOwner) onlyOwner {
|
||||
if (newOwner != address(0)) owner = newOwner;
|
||||
if (newOwner != address(0)) {
|
||||
owner = newOwner;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
@ -12,8 +12,6 @@ pragma solidity ^0.4.4;
|
||||
* use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by some number (specified in constructor) of the set of owners (specified in the constructor) before the interior is executed.
|
||||
*/
|
||||
contract Shareable {
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
@ -21,69 +19,63 @@ contract Shareable {
|
||||
uint index;
|
||||
}
|
||||
|
||||
|
||||
// FIELDS
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public required;
|
||||
|
||||
// list of owners
|
||||
uint[256] owners;
|
||||
uint constant c_maxOwners = 250;
|
||||
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;
|
||||
|
||||
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyOwner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
if (!isOwner(msg.sender)) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
if (confirmAndCheck(_operation)) {
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// METHODS
|
||||
|
||||
// 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;
|
||||
if (index == 0) {
|
||||
return;
|
||||
}
|
||||
uint ownerIndexBit = 2**index;
|
||||
var pending = pendings[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
@ -99,28 +91,31 @@ 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) return false;
|
||||
if (index == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**index;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
// 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;
|
||||
if (index == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
var pending = pendings[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
@ -143,21 +138,22 @@ contract Shareable {
|
||||
delete pendingsIndex[pendings[_operation].index];
|
||||
delete pendings[_operation];
|
||||
return true;
|
||||
} else {
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = pendingsIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
if (pendingsIndex[i] != 0)
|
||||
delete pendings[pendingsIndex[i]];
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
if (pendingsIndex[i] != 0) {
|
||||
delete pendings[pendingsIndex[i]];
|
||||
}
|
||||
}
|
||||
delete pendingsIndex;
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import '../SafeMath.sol';
|
||||
|
||||
|
||||
/*
|
||||
@ -7,22 +10,33 @@ pragma solidity ^0.4.4;
|
||||
* 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;
|
||||
if (this.balance < payment) throw;
|
||||
|
||||
if (payment == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
if (this.balance < payment) {
|
||||
throw;
|
||||
}
|
||||
|
||||
totalPayments = totalPayments.sub(payment);
|
||||
payments[payee] = 0;
|
||||
|
||||
if (!payee.send(payment)) {
|
||||
throw;
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import '../SafeMath.sol';
|
||||
|
||||
|
||||
contract SafeMathMock is SafeMath {
|
||||
uint public result;
|
||||
|
||||
function multiply(uint a, uint b) {
|
||||
result = safeMul(a, b);
|
||||
}
|
||||
|
||||
function subtract(uint a, uint b) {
|
||||
result = safeSub(a, b);
|
||||
}
|
||||
|
||||
function add(uint a, uint b) {
|
||||
result = safeAdd(a, b);
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import '../Stoppable.sol';
|
||||
|
||||
|
||||
// mock class using Stoppable
|
||||
contract StoppableMock is Stoppable {
|
||||
bool public drasticMeasureTaken;
|
||||
uint public count;
|
||||
|
||||
function StoppableMock() {
|
||||
drasticMeasureTaken = false;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
function normalProcess() external stopInEmergency {
|
||||
count++;
|
||||
}
|
||||
|
||||
function drasticMeasure() external onlyInEmergency {
|
||||
drasticMeasureTaken = true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './ERC20Basic.sol';
|
||||
@ -9,16 +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) {
|
||||
if (balances[msg.sender] < _value) {
|
||||
throw;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./StandardToken.sol";
|
||||
@ -8,31 +8,42 @@ 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);
|
||||
}
|
||||
|
||||
function createTokens(address recipient) payable {
|
||||
if (msg.value == 0) throw;
|
||||
if (msg.value == 0) {
|
||||
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
|
||||
function getPrice() constant returns (uint result){
|
||||
function getPrice() constant returns (uint result) {
|
||||
return PRICE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
pragma solidity ^0.4.4;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
|
||||
49
contracts/token/LimitedTransferToken.sol
Normal file
49
contracts/token/LimitedTransferToken.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
43
contracts/token/MintableToken.sol
Normal file
43
contracts/token/MintableToken.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
25
contracts/token/PausableToken.sol
Normal file
25
contracts/token/PausableToken.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./StandardToken.sol";
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
pragma solidity ^0.4.4;
|
||||
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) {
|
||||
|
||||
130
contracts/token/VestedToken.sol
Normal file
130
contracts/token/VestedToken.sol
Normal file
@ -0,0 +1,130 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./StandardToken.sol";
|
||||
import "./LimitedTransferToken.sol";
|
||||
|
||||
contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
struct TokenGrant {
|
||||
address granter;
|
||||
uint256 value;
|
||||
uint64 cliff;
|
||||
uint64 vesting;
|
||||
uint64 start;
|
||||
}
|
||||
|
||||
mapping (address => TokenGrant[]) public grants;
|
||||
|
||||
function grantVestedTokens(
|
||||
address _to,
|
||||
uint256 _value,
|
||||
uint64 _start,
|
||||
uint64 _cliff,
|
||||
uint64 _vesting) {
|
||||
|
||||
if (_cliff < _start) {
|
||||
throw;
|
||||
}
|
||||
if (_vesting < _start) {
|
||||
throw;
|
||||
}
|
||||
if (_vesting < _cliff) {
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
TokenGrant memory grant = TokenGrant(msg.sender, _value, _cliff, _vesting, _start);
|
||||
grants[_to].push(grant);
|
||||
|
||||
transfer(_to, _value);
|
||||
}
|
||||
|
||||
function revokeTokenGrant(address _holder, uint _grantId) {
|
||||
TokenGrant grant = grants[_holder][_grantId];
|
||||
|
||||
if (grant.granter != msg.sender) {
|
||||
throw;
|
||||
}
|
||||
uint256 nonVested = nonVestedTokens(grant, uint64(now));
|
||||
|
||||
// remove grant from array
|
||||
delete grants[_holder][_grantId];
|
||||
grants[_holder][_grantId] = grants[_holder][grants[_holder].length - 1];
|
||||
grants[_holder].length -= 1;
|
||||
|
||||
balances[msg.sender] = balances[msg.sender].add(nonVested);
|
||||
balances[_holder] = balances[_holder].sub(nonVested);
|
||||
Transfer(_holder, msg.sender, nonVested);
|
||||
}
|
||||
|
||||
function tokenGrantsCount(address _holder) constant returns (uint index) {
|
||||
return grants[_holder].length;
|
||||
}
|
||||
|
||||
function tokenGrant(address _holder, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting) {
|
||||
TokenGrant grant = grants[_holder][_grantId];
|
||||
|
||||
granter = grant.granter;
|
||||
value = grant.value;
|
||||
start = grant.start;
|
||||
cliff = grant.cliff;
|
||||
vesting = grant.vesting;
|
||||
|
||||
vested = vestedTokens(grant, uint64(now));
|
||||
}
|
||||
|
||||
function vestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
|
||||
return calculateVestedTokens(
|
||||
grant.value,
|
||||
uint256(time),
|
||||
uint256(grant.start),
|
||||
uint256(grant.cliff),
|
||||
uint256(grant.vesting)
|
||||
);
|
||||
}
|
||||
|
||||
function calculateVestedTokens(
|
||||
uint256 tokens,
|
||||
uint256 time,
|
||||
uint256 start,
|
||||
uint256 cliff,
|
||||
uint256 vesting) constant returns (uint256 vestedTokens)
|
||||
{
|
||||
|
||||
if (time < cliff) {
|
||||
return 0;
|
||||
}
|
||||
if (time >= vesting) {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
uint256 cliffTokens = tokens.mul(cliff.sub(start)).div(vesting.sub(start));
|
||||
vestedTokens = cliffTokens;
|
||||
|
||||
uint256 vestingTokens = tokens.sub(cliffTokens);
|
||||
|
||||
vestedTokens = vestedTokens.add(vestingTokens.mul(time.sub(cliff)).div(vesting.sub(cliff)));
|
||||
}
|
||||
|
||||
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
|
||||
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 = 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++) {
|
||||
uint256 current = nonVestedTokens(grants[holder][i], time);
|
||||
nonVested = nonVested.add(current);
|
||||
}
|
||||
|
||||
return SafeMath.min256(balances[holder].sub(nonVested), super.transferableTokens(holder, time));
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -26,8 +26,8 @@ The code is open-source, and `available on github <https://github.com/OpenZeppel
|
||||
:caption: Smart Contracts
|
||||
|
||||
ownable
|
||||
stoppable
|
||||
killable
|
||||
Pausable
|
||||
destructible
|
||||
claimable
|
||||
migrations
|
||||
safemath
|
||||
|
||||
@ -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.
|
||||
27
docs/source/pausable.rst
Normal file
27
docs/source/pausable.rst
Normal file
@ -0,0 +1,27 @@
|
||||
Pausable
|
||||
=============================================
|
||||
|
||||
Base contract that provides a pause mechanism.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
pause() onlyOwner whenNotPaused returns (bool)
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
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 whenNotPaused()
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Prevents function from running if pause mechanism is activated.
|
||||
|
||||
modifier whenPaused()
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Only runs if pause mechanism is activated.
|
||||
|
||||
unpause() onlyOwner whenPaused returns (bool)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Deactivates the pause mechanism.
|
||||
@ -13,12 +13,12 @@ safeMul(uint a, uint b) internal returns (uint)
|
||||
|
||||
Multiplies two unisgned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier.
|
||||
|
||||
safeSub(uint a, unit b) internal returns (uint)
|
||||
safeSub(uint a, uint b) internal returns (uint)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Checks that b is not greater than a before subtracting.
|
||||
|
||||
safeAdd(unit a, unit b) internal returns (uint)
|
||||
safeAdd(uint a, uint b) internal returns (uint)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Checks that the result is greater than both a and b.
|
||||
Checks that the result is greater than both a and b.
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
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.
|
||||
17
ethpm.json
Normal file
17
ethpm.json
Normal 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"
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
var Migrations = artifacts.require("./Migrations.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(Migrations);
|
||||
};
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
//var Ownable = artifacts.require("ownership/Ownable.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(PullPaymentBid);
|
||||
deployer.deploy(BadArrayUse);
|
||||
deployer.deploy(ProofOfExistence);
|
||||
deployer.deploy(Ownable);
|
||||
deployer.deploy(Claimable);
|
||||
deployer.deploy(LimitBalance);
|
||||
if(deployer.network == 'test'){
|
||||
deployer.deploy(SecureTargetBounty);
|
||||
deployer.deploy(InsecureTargetBounty);
|
||||
};
|
||||
//deployer.deploy(Ownable);
|
||||
};
|
||||
|
||||
25
package.json
25
package.json
@ -1,17 +1,11 @@
|
||||
{
|
||||
"name": "zeppelin-solidity",
|
||||
"version": "1.0.1",
|
||||
"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",
|
||||
"ethereumjs-testrpc": "^3.0.2",
|
||||
"truffle": "^2.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "truffle test",
|
||||
"test": "scripts/test.sh",
|
||||
"console": "truffle console",
|
||||
"install": "scripts/install.sh"
|
||||
},
|
||||
"repository": {
|
||||
@ -31,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"
|
||||
}
|
||||
}
|
||||
|
||||
13
scripts/test.sh
Executable file
13
scripts/test.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
|
||||
output=$(nc -z localhost 8545; echo $?)
|
||||
[ $output -eq "0" ] && trpc_running=true
|
||||
if [ ! $trpc_running ]; then
|
||||
echo "Starting our own testrpc node instance"
|
||||
testrpc > /dev/null &
|
||||
trpc_pid=$!
|
||||
fi
|
||||
./node_modules/truffle/cli.js test
|
||||
if [ ! $trpc_running ]; then
|
||||
kill -9 $trpc_pid
|
||||
fi
|
||||
@ -1,5 +1,7 @@
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");
|
||||
|
||||
contract('BasicToken', function(accounts) {
|
||||
|
||||
it("should return the correct totalSupply after construction", async function() {
|
||||
@ -25,8 +27,9 @@ contract('BasicToken', function(accounts) {
|
||||
try {
|
||||
let transfer = await token.transfer(accounts[1], 101);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
114
test/Bounty.js
114
test/Bounty.js
@ -1,115 +1,115 @@
|
||||
'use strict';
|
||||
|
||||
let sendReward = function(sender, receiver, value){
|
||||
web3.eth.sendTransaction({
|
||||
from:sender,
|
||||
to:receiver,
|
||||
value: value
|
||||
})
|
||||
});
|
||||
};
|
||||
var SecureTargetBounty = artifacts.require('helpers/SecureTargetBounty.sol');
|
||||
var InsecureTargetBounty = artifacts.require('helpers/InsecureTargetBounty.sol');
|
||||
|
||||
function awaitEvent(event, handler) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function wrappedHandler(...args) {
|
||||
Promise.resolve(handler(...args)).then(resolve).catch(reject);
|
||||
}
|
||||
|
||||
event.watch(wrappedHandler);
|
||||
});
|
||||
}
|
||||
|
||||
contract('Bounty', function(accounts) {
|
||||
|
||||
it("sets reward", async function(){
|
||||
it('sets reward', async function() {
|
||||
let owner = accounts[0];
|
||||
let reward = web3.toWei(1, "ether");
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
let bounty = await SecureTargetBounty.new();
|
||||
sendReward(owner, bounty.address, reward);
|
||||
|
||||
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 reward = web3.toWei(1, 'ether');
|
||||
let bounty = await SecureTargetBounty.new();
|
||||
sendReward(owner, bounty.address, reward);
|
||||
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
|
||||
|
||||
await bounty.kill();
|
||||
await bounty.destroy();
|
||||
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
|
||||
})
|
||||
});
|
||||
|
||||
describe("Against secure contract", function(){
|
||||
describe('Against secure contract', function(){
|
||||
|
||||
it("checkInvariant returns true", async function(){
|
||||
let bounty = await SecureTargetBounty.new();
|
||||
let target = await bounty.createTarget();
|
||||
let check = await bounty.checkInvariant.call();
|
||||
|
||||
assert.isTrue(check);
|
||||
})
|
||||
|
||||
it("cannot claim reward", async function(done){
|
||||
it('cannot claim reward', async function(){
|
||||
let owner = accounts[0];
|
||||
let researcher = accounts[1];
|
||||
let reward = web3.toWei(1, "ether");
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
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 }
|
||||
if (err) { throw err; }
|
||||
|
||||
var targetAddress = result.args.createdAddress;
|
||||
sendReward(owner, bounty.address, reward);
|
||||
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
|
||||
assert.equal(reward,
|
||||
web3.eth.getBalance(bounty.address).toNumber());
|
||||
|
||||
try {
|
||||
let tmpClain = await bounty.claim(targetAddress, {from:researcher});
|
||||
done("should not come here");
|
||||
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 {
|
||||
let withdraw = await bounty.withdrawPayments({from:researcher});
|
||||
done("should not come here")
|
||||
} catch (err) {
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
|
||||
done();
|
||||
}
|
||||
}//end of first try catch
|
||||
});
|
||||
}
|
||||
try {
|
||||
await bounty.withdrawPayments({from:researcher});
|
||||
assert.isTrue(false); // should never reach here
|
||||
} catch (err) {
|
||||
assert.equal(reward,
|
||||
web3.eth.getBalance(bounty.address).toNumber());
|
||||
}
|
||||
};
|
||||
bounty.createTarget({from:researcher});
|
||||
})
|
||||
})
|
||||
await awaitEvent(event, watcher);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Against broken contract", function(){
|
||||
it("checkInvariant returns false", async function(){
|
||||
let bounty = await InsecureTargetBounty.new();
|
||||
let target = await bounty.createTarget();
|
||||
let invariantCall = await bounty.checkInvariant.call();
|
||||
|
||||
assert.isFalse(invariantCall);
|
||||
})
|
||||
|
||||
it("claims reward", async function(done){
|
||||
describe('Against broken contract', function(){
|
||||
it('claims reward', async function() {
|
||||
let owner = accounts[0];
|
||||
let researcher = accounts[1];
|
||||
let reward = web3.toWei(1, "ether");
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
let bounty = await InsecureTargetBounty.new();
|
||||
let event = bounty.TargetCreated({});
|
||||
|
||||
event.watch(async function(err, result) {
|
||||
|
||||
let watcher = async function(err, result) {
|
||||
event.stopWatching();
|
||||
if (err) { throw err }
|
||||
if (err) { throw err; }
|
||||
let targetAddress = result.args.createdAddress;
|
||||
sendReward(owner, bounty.address, reward);
|
||||
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
|
||||
|
||||
let bountyClaim = await bounty.claim(targetAddress, {from:researcher});
|
||||
await bounty.claim(targetAddress, {from:researcher});
|
||||
let claim = await bounty.claimed.call();
|
||||
|
||||
assert.isTrue(claim);
|
||||
|
||||
let payment = await bounty.withdrawPayments({from:researcher});
|
||||
await bounty.withdrawPayments({from:researcher});
|
||||
|
||||
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
|
||||
done();
|
||||
})
|
||||
};
|
||||
bounty.createTarget({from:researcher});
|
||||
})
|
||||
})
|
||||
await awaitEvent(event, watcher);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var Claimable = artifacts.require('../contracts/ownership/Claimable.sol');
|
||||
|
||||
contract('Claimable', function(accounts) {
|
||||
let claimable;
|
||||
|
||||
@ -5,34 +10,39 @@ contract('Claimable', function(accounts) {
|
||||
claimable = await Claimable.new();
|
||||
});
|
||||
|
||||
it("should have an owner", async function() {
|
||||
it('should have an owner', async function() {
|
||||
let owner = await claimable.owner();
|
||||
assert.isTrue(owner != 0);
|
||||
assert.isTrue(owner !== 0);
|
||||
});
|
||||
|
||||
it("changes pendingOwner after transfer", async function() {
|
||||
it('changes pendingOwner after transfer', async function() {
|
||||
let newOwner = accounts[1];
|
||||
let transfer = await claimable.transferOwnership(newOwner);
|
||||
await claimable.transferOwnership(newOwner);
|
||||
let pendingOwner = await claimable.pendingOwner();
|
||||
|
||||
assert.isTrue(pendingOwner === newOwner);
|
||||
});
|
||||
|
||||
it("should prevent to claimOwnership from no pendingOwner", async function() {
|
||||
let claimedOwner = await claimable.claimOwnership({from: accounts[2]});
|
||||
let owner = await claimable.owner();
|
||||
|
||||
assert.isTrue(owner != accounts[2]);
|
||||
it('should prevent to claimOwnership from no pendingOwner', async function() {
|
||||
try {
|
||||
await claimable.claimOwnership({from: accounts[2]});
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it("should prevent non-owners from transfering", async function() {
|
||||
let transfer = await claimable.transferOwnership(accounts[2], {from: accounts[2]});
|
||||
let pendingOwner = await claimable.pendingOwner();
|
||||
|
||||
assert.isFalse(pendingOwner === accounts[2]);
|
||||
it('should prevent non-owners from transfering', async function() {
|
||||
const other = accounts[2];
|
||||
const owner = await claimable.owner.call();
|
||||
assert.isTrue(owner !== other);
|
||||
try {
|
||||
await claimable.transferOwnership(other, {from: other});
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
describe("after initiating a transfer", function () {
|
||||
describe('after initiating a transfer', function () {
|
||||
let newOwner;
|
||||
|
||||
beforeEach(async function () {
|
||||
@ -40,8 +50,8 @@ contract('Claimable', function(accounts) {
|
||||
await claimable.transferOwnership(newOwner);
|
||||
});
|
||||
|
||||
it("changes allow pending owner to claim ownership", async function() {
|
||||
let claimedOwner = await claimable.claimOwnership({from: newOwner})
|
||||
it('changes allow pending owner to claim ownership', async function() {
|
||||
await claimable.claimOwnership({from: newOwner});
|
||||
let owner = await claimable.owner();
|
||||
|
||||
assert.isTrue(owner === newOwner);
|
||||
|
||||
30
test/Contactable.js
Normal file
30
test/Contactable.js
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var Contactable = artifacts.require('../contracts/ownership/Contactable.sol');
|
||||
|
||||
contract('Contactable', function(accounts) {
|
||||
let contactable;
|
||||
|
||||
beforeEach(async function() {
|
||||
contactable = await Contactable.new();
|
||||
});
|
||||
|
||||
it('should have an empty contact info', async function() {
|
||||
let info = await contactable.contactInformation();
|
||||
assert.isTrue(info == "");
|
||||
});
|
||||
|
||||
describe('after setting the contact information', function () {
|
||||
let contactInfo = "contact information"
|
||||
|
||||
beforeEach(async function () {
|
||||
await contactable.setContactInformation(contactInfo);
|
||||
});
|
||||
|
||||
it('should return the setted contact information', async function() {
|
||||
let info = await contactable.contactInformation();
|
||||
assert.isTrue(info === contactInfo);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,3 +1,8 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var DayLimitMock = artifacts.require('helpers/DayLimitMock.sol');
|
||||
|
||||
contract('DayLimit', function(accounts) {
|
||||
|
||||
it('should construct with the passed daily limit', async function() {
|
||||
@ -28,9 +33,11 @@ contract('DayLimit', function(accounts) {
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.attemptSpend(3);
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow spending if daily limit is reached and then set higher', async function() {
|
||||
@ -41,7 +48,11 @@ contract('DayLimit', function(accounts) {
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.attemptSpend(3);
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
@ -59,7 +70,11 @@ contract('DayLimit', function(accounts) {
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.attemptSpend(3);
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var DelayedClaimable = artifacts.require('../contracts/ownership/DelayedClaimable.sol');
|
||||
|
||||
contract('DelayedClaimable', function(accounts) {
|
||||
var delayedClaimable;
|
||||
|
||||
@ -7,76 +11,58 @@ contract('DelayedClaimable', function(accounts) {
|
||||
});
|
||||
});
|
||||
|
||||
it("Changes pendingOwner after transfer succesfull", function(done) {
|
||||
return delayedClaimable.transferOwnership(accounts[2])
|
||||
.then(function(){
|
||||
return delayedClaimable.setClaimBlocks(1000,0);
|
||||
})
|
||||
.then(function(){
|
||||
return delayedClaimable.claimBeforeBlock();
|
||||
})
|
||||
.then(function(claimBeforeBlock) {
|
||||
assert.isTrue(claimBeforeBlock == 1000);
|
||||
return delayedClaimable.claimAfterBlock();
|
||||
})
|
||||
.then(function(claimAfterBlock) {
|
||||
assert.isTrue(claimAfterBlock == 0);
|
||||
return delayedClaimable.pendingOwner();
|
||||
})
|
||||
.then(function(pendingOwner) {
|
||||
assert.isTrue(pendingOwner === accounts[2]);
|
||||
return delayedClaimable.claimOwnership({from: accounts[2]});
|
||||
})
|
||||
.then(function() {
|
||||
return delayedClaimable.owner();
|
||||
})
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner === accounts[2]);
|
||||
})
|
||||
.then(done);
|
||||
it('can set claim blocks', async function() {
|
||||
await delayedClaimable.transferOwnership(accounts[2]);
|
||||
await delayedClaimable.setLimits(0, 1000);
|
||||
let end = await delayedClaimable.end();
|
||||
assert.equal(end, 1000);
|
||||
let start = await delayedClaimable.start();
|
||||
assert.equal(start, 0);
|
||||
});
|
||||
|
||||
it("Changes pendingOwner after transfer fails", function(done) {
|
||||
return delayedClaimable.transferOwnership(accounts[1])
|
||||
.then(function(){
|
||||
return delayedClaimable.setClaimBlocks(11000,10000);
|
||||
})
|
||||
.then(function(){
|
||||
return delayedClaimable.claimBeforeBlock();
|
||||
})
|
||||
.then(function(claimBeforeBlock) {
|
||||
assert.isTrue(claimBeforeBlock == 11000);
|
||||
return delayedClaimable.claimAfterBlock();
|
||||
})
|
||||
.then(function(claimAfterBlock) {
|
||||
assert.isTrue(claimAfterBlock == 10000);
|
||||
return delayedClaimable.pendingOwner();
|
||||
})
|
||||
.then(function(pendingOwner) {
|
||||
assert.isTrue(pendingOwner === accounts[1]);
|
||||
return delayedClaimable.claimOwnership({from: accounts[1]});
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error.message.search('invalid JUMP') == -1) throw error;
|
||||
})
|
||||
.then(function() {
|
||||
return delayedClaimable.owner();
|
||||
})
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner != accounts[1]);
|
||||
})
|
||||
.then(done);
|
||||
it('changes pendingOwner after transfer successful', async function() {
|
||||
await delayedClaimable.transferOwnership(accounts[2]);
|
||||
await delayedClaimable.setLimits(0, 1000);
|
||||
let end = await delayedClaimable.end();
|
||||
assert.equal(end, 1000);
|
||||
let start = await delayedClaimable.start();
|
||||
assert.equal(start, 0);
|
||||
let pendingOwner = await delayedClaimable.pendingOwner();
|
||||
assert.equal(pendingOwner, accounts[2]);
|
||||
await delayedClaimable.claimOwnership({from: accounts[2]});
|
||||
let owner = await delayedClaimable.owner();
|
||||
assert.equal(owner, accounts[2]);
|
||||
});
|
||||
|
||||
it("Set claimBeforeBlock and claimAfterBlock invalid values fail", function(done) {
|
||||
return delayedClaimable.transferOwnership(accounts[1])
|
||||
.then(function(){
|
||||
return delayedClaimable.setClaimBlocks(1000,10000);
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error.message.search('invalid JUMP') == -1) throw error;
|
||||
})
|
||||
.then(done);
|
||||
it('changes pendingOwner after transfer fails', async function() {
|
||||
await delayedClaimable.transferOwnership(accounts[1]);
|
||||
await delayedClaimable.setLimits(100, 110);
|
||||
let end = await delayedClaimable.end();
|
||||
assert.equal(end, 110);
|
||||
let start = await delayedClaimable.start();
|
||||
assert.equal(start, 100);
|
||||
let pendingOwner = await delayedClaimable.pendingOwner();
|
||||
assert.equal(pendingOwner, accounts[1]);
|
||||
var err = null;
|
||||
try {
|
||||
await delayedClaimable.claimOwnership({from: accounts[1]});
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
assert.isFalse(err.message.search('invalid JUMP') === -1);
|
||||
let owner = await delayedClaimable.owner();
|
||||
assert.isTrue(owner !== accounts[1]);
|
||||
});
|
||||
|
||||
it('set end and start invalid values fail', async function() {
|
||||
await delayedClaimable.transferOwnership(accounts[1]);
|
||||
var err = null;
|
||||
try {
|
||||
await delayedClaimable.setLimits(1001, 1000);
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
assert.isFalse(err.message.search('invalid JUMP') === -1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
18
test/Destructible.js
Normal file
18
test/Destructible.js
Normal 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
35
test/HasNoContracts.js
Normal 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
63
test/HasNoEther.js
Normal 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
40
test/HasNoTokens.js
Normal 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]}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,56 +0,0 @@
|
||||
|
||||
contract('Killable', function(accounts) {
|
||||
//from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6
|
||||
web3.eth.getTransactionReceiptMined = function (txnHash, interval) {
|
||||
var transactionReceiptAsync;
|
||||
interval = interval ? interval : 500;
|
||||
transactionReceiptAsync = function(txnHash, resolve, reject) {
|
||||
try {
|
||||
var receipt = web3.eth.getTransactionReceipt(txnHash);
|
||||
if (receipt == null) {
|
||||
setTimeout(function () {
|
||||
transactionReceiptAsync(txnHash, resolve, reject);
|
||||
}, interval);
|
||||
} else {
|
||||
resolve(receipt);
|
||||
}
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
if (Array.isArray(txnHash)) {
|
||||
var promises = [];
|
||||
txnHash.forEach(function (oneTxHash) {
|
||||
promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval));
|
||||
});
|
||||
return Promise.all(promises);
|
||||
} else {
|
||||
return new Promise(function (resolve, reject) {
|
||||
transactionReceiptAsync(txnHash, resolve, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
it("should send balance to owner after death", async function() {
|
||||
let initBalance, newBalance, owner, address, killable, kBalance, txnHash, receiptMined;
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('50','ether')}, function(err, result) {
|
||||
if(err)
|
||||
console.log("ERROR:" + err);
|
||||
else {
|
||||
console.log(result);
|
||||
}
|
||||
});
|
||||
|
||||
killable = await Killable.new({from: accounts[0], value: web3.toWei('10','ether')});
|
||||
owner = await killable.owner();
|
||||
initBalance = web3.eth.getBalance(owner);
|
||||
kBalance = web3.eth.getBalance(killable.address);
|
||||
txnHash = await killable.kill({from: owner});
|
||||
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
newBalance = web3.eth.getBalance(owner);
|
||||
|
||||
assert.isTrue(newBalance > initBalance);
|
||||
});
|
||||
|
||||
});
|
||||
@ -1,3 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var LimitBalanceMock = artifacts.require('helpers/LimitBalanceMock.sol');
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
contract('LimitBalance', function(accounts) {
|
||||
@ -9,48 +12,50 @@ contract('LimitBalance', function(accounts) {
|
||||
|
||||
let LIMIT = 1000;
|
||||
|
||||
it("should expose limit", async function() {
|
||||
it('should expose limit', async function() {
|
||||
let limit = await lb.limit();
|
||||
assert.equal(limit, LIMIT);
|
||||
});
|
||||
|
||||
it("should allow sending below limit", async function() {
|
||||
it('should allow sending below limit', async function() {
|
||||
let amount = 1;
|
||||
let limDeposit = await lb.limitedDeposit({value: amount});
|
||||
await lb.limitedDeposit({value: amount});
|
||||
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
});
|
||||
|
||||
it("shouldnt allow sending above limit", async function() {
|
||||
it('shouldnt allow sending above limit', async function() {
|
||||
let amount = 1110;
|
||||
try {
|
||||
let limDeposit = await lb.limitedDeposit({value: amount});
|
||||
await lb.limitedDeposit({value: amount});
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should allow multiple sends below limit", async function() {
|
||||
it('should allow multiple sends below limit', async function() {
|
||||
let amount = 500;
|
||||
let limDeposit = await lb.limitedDeposit({value: amount});
|
||||
await lb.limitedDeposit({value: amount});
|
||||
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
|
||||
let limDeposit2 = await lb.limitedDeposit({value: amount});
|
||||
await lb.limitedDeposit({value: amount});
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount*2);
|
||||
});
|
||||
|
||||
it("shouldnt allow multiple sends above limit", async function() {
|
||||
it('shouldnt allow multiple sends above limit', async function() {
|
||||
let amount = 500;
|
||||
let limDeposit = await lb.limitedDeposit({value: amount});
|
||||
await lb.limitedDeposit({value: amount});
|
||||
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
|
||||
try {
|
||||
await lb.limitedDeposit({value: amount+1})
|
||||
await lb.limitedDeposit({value: amount+1});
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
35
test/MintableToken.js
Normal file
35
test/MintableToken.js
Normal 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);
|
||||
})
|
||||
|
||||
});
|
||||
@ -1,43 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var MultisigWalletMock = artifacts.require('./helpers/MultisigWalletMock.sol');
|
||||
require('./helpers/transactionMined.js');
|
||||
|
||||
contract('MultisigWallet', function(accounts) {
|
||||
//from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6
|
||||
web3.eth.getTransactionReceiptMined = function (txnHash, interval) {
|
||||
var transactionReceiptAsync;
|
||||
interval = interval ? interval : 500;
|
||||
transactionReceiptAsync = function(txnHash, resolve, reject) {
|
||||
try {
|
||||
var receipt = web3.eth.getTransactionReceipt(txnHash);
|
||||
if (receipt == null) {
|
||||
setTimeout(function () {
|
||||
transactionReceiptAsync(txnHash, resolve, reject);
|
||||
}, interval);
|
||||
} else {
|
||||
resolve(receipt);
|
||||
}
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
if (Array.isArray(txnHash)) {
|
||||
var promises = [];
|
||||
txnHash.forEach(function (oneTxHash) {
|
||||
promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval));
|
||||
});
|
||||
return Promise.all(promises);
|
||||
} else {
|
||||
return new Promise(function (resolve, reject) {
|
||||
transactionReceiptAsync(txnHash, resolve, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let shouldntFail = function(err) {
|
||||
assert.isFalse(!!err);
|
||||
};
|
||||
it('should send balance to passed address upon death', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
|
||||
if(err)
|
||||
console.log("ERROR:" + err);
|
||||
});
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
@ -50,13 +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});
|
||||
|
||||
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
|
||||
//Get balances of owner and wallet after kill function is complete, compare with previous values
|
||||
//Get balances of owner and wallet after destroy function is complete, compare with previous values
|
||||
let newOwnerBalance = web3.eth.getBalance(accounts[0]);
|
||||
let newWalletBalance = web3.eth.getBalance(wallet.address);
|
||||
|
||||
@ -66,10 +36,7 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
it('should execute transaction if below daily limit', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
|
||||
if(err)
|
||||
console.log("ERROR:" + err);
|
||||
});
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
@ -82,7 +49,6 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
//Owner account0 commands wallet to send 9 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 9, hash);
|
||||
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
|
||||
//Balance of account2 should have increased
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
@ -91,10 +57,7 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
it('should prevent execution of transaction if above daily limit', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
|
||||
if(err)
|
||||
console.log("ERROR:" + err);
|
||||
});
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
@ -107,7 +70,6 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
//Owner account0 commands wallet to send 9 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 9, hash);
|
||||
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
|
||||
//Balance of account2 should have increased
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
@ -118,7 +80,6 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
//Owner account0 commands wallet to send 2 more wei to account2, going over the daily limit of 10
|
||||
txnHash = await wallet.execute(accounts[2], 2, hash);
|
||||
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
|
||||
//Balance of account2 should not change
|
||||
newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
@ -127,10 +88,7 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
it('should execute transaction if above daily limit and enough owners approve', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) {
|
||||
if(err)
|
||||
console.log("ERROR:" + err);
|
||||
});
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
@ -143,7 +101,6 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
//Owner account0 commands wallet to send 11 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 11, hash);
|
||||
let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
|
||||
//Balance of account2 should not change
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
@ -153,7 +110,6 @@ contract('MultisigWallet', function(accounts) {
|
||||
|
||||
//Owner account1 commands wallet to send 11 wei to account2
|
||||
txnHash = await wallet.execute(accounts[2], 2, hash);
|
||||
receiptMined = await web3.eth.getTransactionReceiptMined(txnHash);
|
||||
|
||||
//Balance of account2 should change
|
||||
newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var Ownable = artifacts.require('../contracts/ownership/Ownable.sol');
|
||||
|
||||
contract('Ownable', function(accounts) {
|
||||
let ownable;
|
||||
|
||||
@ -5,31 +10,33 @@ contract('Ownable', function(accounts) {
|
||||
ownable = await Ownable.new();
|
||||
});
|
||||
|
||||
it("should have an owner", async function() {
|
||||
it('should have an owner', async function() {
|
||||
let owner = await ownable.owner();
|
||||
assert.isTrue(owner != 0);
|
||||
assert.isTrue(owner !== 0);
|
||||
});
|
||||
|
||||
it("changes owner after transfer", async function() {
|
||||
it('changes owner after transfer', async function() {
|
||||
let other = accounts[1];
|
||||
let transfer = await ownable.transferOwnership(other);
|
||||
await ownable.transferOwnership(other);
|
||||
let owner = await ownable.owner();
|
||||
|
||||
assert.isTrue(owner === other);
|
||||
});
|
||||
|
||||
it("should prevent non-owners from transfering", async function() {
|
||||
let other = accounts[2];
|
||||
let transfer = await ownable.transferOwnership(other, {from: accounts[2]});
|
||||
let owner = await ownable.owner();
|
||||
|
||||
assert.isFalse(owner === other);
|
||||
it('should prevent non-owners from transfering', async function() {
|
||||
const other = accounts[2];
|
||||
const owner = await ownable.owner.call();
|
||||
assert.isTrue(owner !== other);
|
||||
try {
|
||||
await ownable.transferOwnership(other, {from: other});
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it("should guard ownership against stuck state", async function() {
|
||||
let ownable = Ownable.deployed();
|
||||
it('should guard ownership against stuck state', async function() {
|
||||
let originalOwner = await ownable.owner();
|
||||
let transfer = await ownable.transferOwnership(null, {from: originalOwner});
|
||||
await ownable.transferOwnership(null, {from: originalOwner});
|
||||
let newOwner = await ownable.owner();
|
||||
|
||||
assert.equal(originalOwner, newOwner);
|
||||
|
||||
65
test/Pausable.js
Normal file
65
test/Pausable.js
Normal file
@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
const PausableMock = artifacts.require('helpers/PausableMock.sol');
|
||||
|
||||
contract('Pausable', function(accounts) {
|
||||
|
||||
it('can perform normal process in non-pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
let count0 = await Pausable.count();
|
||||
assert.equal(count0, 0);
|
||||
|
||||
await Pausable.normalProcess();
|
||||
let count1 = await Pausable.count();
|
||||
assert.equal(count1, 1);
|
||||
});
|
||||
|
||||
it('can not perform normal process in pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
let count0 = await Pausable.count();
|
||||
assert.equal(count0, 0);
|
||||
|
||||
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-pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
try {
|
||||
await Pausable.drasticMeasure();
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
|
||||
const drasticMeasureTaken = await Pausable.drasticMeasureTaken();
|
||||
assert.isFalse(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
it('can take a drastic measure in a pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
await Pausable.drasticMeasure();
|
||||
let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
|
||||
|
||||
assert.isTrue(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
it('should resume allowing normal process after pause is over', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
await Pausable.unpause();
|
||||
await Pausable.normalProcess();
|
||||
let count0 = await Pausable.count();
|
||||
|
||||
assert.equal(count0, 1);
|
||||
});
|
||||
|
||||
});
|
||||
73
test/PausableToken.js
Normal file
73
test/PausableToken.js
Normal 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');
|
||||
});
|
||||
})
|
||||
@ -1,3 +1,5 @@
|
||||
var PullPaymentMock = artifacts.require("./helpers/PullPaymentMock.sol");
|
||||
|
||||
contract('PullPayment', function(accounts) {
|
||||
|
||||
it("can't call asyncSend externally", async function() {
|
||||
@ -10,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);
|
||||
});
|
||||
|
||||
@ -19,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);
|
||||
});
|
||||
|
||||
@ -33,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() {
|
||||
@ -46,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
31
test/ReentrancyGuard.js
Normal 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));
|
||||
});
|
||||
});
|
||||
@ -1,4 +1,5 @@
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
var SafeMathMock = artifacts.require("./helpers/SafeMathMock.sol");
|
||||
|
||||
contract('SafeMath', function(accounts) {
|
||||
|
||||
@ -40,8 +41,9 @@ contract('SafeMath', function(accounts) {
|
||||
try {
|
||||
let subtract = await safeMath.subtract(a, b);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should throw an error on addition overflow", async function() {
|
||||
@ -50,8 +52,9 @@ contract('SafeMath', function(accounts) {
|
||||
try {
|
||||
let add = await safeMath.add(a, b);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should throw an error on multiplication overflow", async function() {
|
||||
@ -60,8 +63,9 @@ contract('SafeMath', function(accounts) {
|
||||
try {
|
||||
let multiply = await safeMath.multiply(a, b);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
var ShareableMock = artifacts.require("./helpers/ShareableMock.sol");
|
||||
|
||||
contract('Shareable', function(accounts) {
|
||||
|
||||
it('should construct with correct owners and number of sigs required', async function() {
|
||||
|
||||
@ -1,25 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
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);
|
||||
|
||||
@ -27,19 +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) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should return correct balances after transfering from another account", async function() {
|
||||
it('should return correct balances after transfering from another account', async function() {
|
||||
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);
|
||||
@ -51,14 +55,15 @@ 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) {
|
||||
assertJump(error);
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
contract('Stoppable', function(accounts) {
|
||||
|
||||
it("can perform normal process in non-emergency", async function() {
|
||||
let stoppable = await StoppableMock.new();
|
||||
let count0 = await stoppable.count();
|
||||
assert.equal(count0, 0);
|
||||
|
||||
let normalProcess = await stoppable.normalProcess();
|
||||
let count1 = await stoppable.count();
|
||||
assert.equal(count1, 1);
|
||||
});
|
||||
|
||||
it("can not perform normal process in emergency", async function() {
|
||||
let stoppable = await StoppableMock.new();
|
||||
let emergencyStop = await stoppable.emergencyStop();
|
||||
let count0 = await stoppable.count();
|
||||
assert.equal(count0, 0);
|
||||
|
||||
let normalProcess = await stoppable.normalProcess();
|
||||
let count1 = await stoppable.count();
|
||||
assert.equal(count1, 0);
|
||||
});
|
||||
|
||||
|
||||
it("can not take drastic measure in non-emergency", async function() {
|
||||
let stoppable = await StoppableMock.new();
|
||||
let drasticMeasure = await stoppable.drasticMeasure();
|
||||
let drasticMeasureTaken = await stoppable.drasticMeasureTaken();
|
||||
|
||||
assert.isFalse(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
it("can take a drastic measure in an emergency", async function() {
|
||||
let stoppable = await StoppableMock.new();
|
||||
let emergencyStop = await stoppable.emergencyStop();
|
||||
let drasticMeasure = await stoppable.drasticMeasure();
|
||||
let drasticMeasureTaken = await stoppable.drasticMeasureTaken();
|
||||
|
||||
assert.isTrue(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
it("should resume allowing normal process after emergency is over", async function() {
|
||||
let stoppable = await StoppableMock.new();
|
||||
let emergencyStop = await stoppable.emergencyStop();
|
||||
let release = await stoppable.release();
|
||||
let normalProcess = await stoppable.normalProcess();
|
||||
let count0 = await stoppable.count();
|
||||
|
||||
assert.equal(count0, 1);
|
||||
});
|
||||
|
||||
});
|
||||
32
test/TokenDestructible.js
Normal file
32
test/TokenDestructible.js
Normal 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);
|
||||
});
|
||||
});
|
||||
117
test/VestedToken.js
Normal file
117
test/VestedToken.js
Normal file
@ -0,0 +1,117 @@
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
const timer = require('./helpers/timer');
|
||||
var VestedTokenMock = artifacts.require("./helpers/VestedTokenMock.sol");
|
||||
|
||||
contract('VestedToken', function(accounts) {
|
||||
let token = null
|
||||
let now = 0
|
||||
|
||||
const tokenAmount = 50
|
||||
|
||||
const granter = accounts[0]
|
||||
const receiver = accounts[1]
|
||||
|
||||
beforeEach(async () => {
|
||||
token = await VestedTokenMock.new(granter, 100);
|
||||
now = web3.eth.getBlock(web3.eth.blockNumber).timestamp;
|
||||
})
|
||||
|
||||
it('granter can grant tokens without vesting', async () => {
|
||||
await token.transfer(receiver, tokenAmount, { from: granter })
|
||||
|
||||
assert.equal(await token.balanceOf(receiver), tokenAmount);
|
||||
assert.equal(await token.transferableTokens(receiver, now), tokenAmount);
|
||||
})
|
||||
|
||||
describe('getting a token grant', async () => {
|
||||
const cliff = 10000
|
||||
const vesting = 20000 // seconds
|
||||
|
||||
beforeEach(async () => {
|
||||
await token.grantVestedTokens(receiver, tokenAmount, now, now + cliff, now + vesting, { from: granter })
|
||||
})
|
||||
|
||||
it('tokens are received', async () => {
|
||||
assert.equal(await token.balanceOf(receiver), tokenAmount);
|
||||
})
|
||||
|
||||
it('has 0 transferable tokens before cliff', async () => {
|
||||
assert.equal(await token.transferableTokens(receiver, now), 0);
|
||||
})
|
||||
|
||||
it('all tokens are transferable after vesting', async () => {
|
||||
assert.equal(await token.transferableTokens(receiver, now + vesting), tokenAmount);
|
||||
})
|
||||
|
||||
it('throws when trying to transfer non vested tokens', async () => {
|
||||
try {
|
||||
await token.transfer(accounts[7], 1, { from: receiver })
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
})
|
||||
|
||||
it('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);
|
||||
assert.equal(await token.balanceOf(granter), 100);
|
||||
})
|
||||
|
||||
it('cannot be revoked by non granter', async () => {
|
||||
try {
|
||||
await token.revokeTokenGrant(receiver, 0, { from: accounts[3] });
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
})
|
||||
|
||||
it('can be revoked by granter and non vested tokens are returned', async () => {
|
||||
await timer(cliff);
|
||||
await token.revokeTokenGrant(receiver, 0, { from: granter });
|
||||
assert.equal(await token.balanceOf(receiver), tokenAmount * cliff / vesting);
|
||||
})
|
||||
|
||||
it('can transfer all tokens after vesting ends', async () => {
|
||||
await timer(vesting);
|
||||
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)
|
||||
})
|
||||
})
|
||||
});
|
||||
@ -1,7 +1,7 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import '../token/BasicToken.sol';
|
||||
import '../../contracts/token/BasicToken.sol';
|
||||
|
||||
|
||||
// mock class using BasicToken
|
||||
@ -1,5 +1,5 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import "../DayLimit.sol";
|
||||
pragma solidity ^0.4.8;
|
||||
import "../../contracts/DayLimit.sol";
|
||||
|
||||
contract DayLimitMock is DayLimit {
|
||||
uint public totalSpending;
|
||||
33
test/helpers/ERC23TokenMock.sol
Normal file
33
test/helpers/ERC23TokenMock.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
13
test/helpers/ForceEther.sol
Normal file
13
test/helpers/ForceEther.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
11
test/helpers/HasNoEtherTest.sol
Normal file
11
test/helpers/HasNoEtherTest.sol
Normal 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 {
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import {Bounty, Target} from "../Bounty.sol";
|
||||
import {Bounty, Target} from "../../contracts/Bounty.sol";
|
||||
|
||||
|
||||
contract InsecureTargetMock is Target {
|
||||
@ -1,7 +1,7 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import '../LimitBalance.sol';
|
||||
import '../../contracts/LimitBalance.sol';
|
||||
|
||||
|
||||
// mock class using LimitBalance
|
||||
@ -1,5 +1,5 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import "../MultisigWallet.sol";
|
||||
pragma solidity ^0.4.8;
|
||||
import "../../contracts/MultisigWallet.sol";
|
||||
|
||||
contract MultisigWalletMock is MultisigWallet {
|
||||
uint public totalSpending;
|
||||
25
test/helpers/PausableMock.sol
Normal file
25
test/helpers/PausableMock.sol
Normal file
@ -0,0 +1,25 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import '../../contracts/lifecycle/Pausable.sol';
|
||||
|
||||
|
||||
// mock class using Pausable
|
||||
contract PausableMock is Pausable {
|
||||
bool public drasticMeasureTaken;
|
||||
uint public count;
|
||||
|
||||
function PausableMock() {
|
||||
drasticMeasureTaken = false;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
function normalProcess() external whenNotPaused {
|
||||
count++;
|
||||
}
|
||||
|
||||
function drasticMeasure() external whenPaused {
|
||||
drasticMeasureTaken = true;
|
||||
}
|
||||
|
||||
}
|
||||
12
test/helpers/PausableTokenMock.sol
Normal file
12
test/helpers/PausableTokenMock.sol
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import '../PullPayment.sol';
|
||||
import '../../contracts/payment/PullPayment.sol';
|
||||
|
||||
|
||||
// mock class using PullPayment
|
||||
11
test/helpers/ReentrancyAttack.sol
Normal file
11
test/helpers/ReentrancyAttack.sol
Normal file
@ -0,0 +1,11 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
contract ReentrancyAttack {
|
||||
|
||||
function callSender(bytes4 data) {
|
||||
if(!msg.sender.call(data)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
46
test/helpers/ReentrancyMock.sol
Normal file
46
test/helpers/ReentrancyMock.sol
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user