Compare commits
265 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 725ed40a57 | |||
| 1df75a951f | |||
| adfd8fb6a9 | |||
| 98f672b534 | |||
| e9068859b6 | |||
| 1203a2473d | |||
| 4a1ff913ab | |||
| 83918cad4b | |||
| d3c5ce268f | |||
| f60c5ad88a | |||
| bcda5bfe9b | |||
| 33eb8b1da5 | |||
| b069827bad | |||
| 74e416f04f | |||
| b395b06b65 | |||
| bd84db735d | |||
| bd99b207c9 | |||
| 2438557255 | |||
| 9f1826d83e | |||
| 676f5f6931 | |||
| 6d8bb497c8 | |||
| 5fd8684a5a | |||
| 1737555b0d | |||
| eda63c75c4 | |||
| 61f62eb3c7 | |||
| f96ebfca31 | |||
| fac580d1a9 | |||
| 07b688a0e6 | |||
| 20187f2970 | |||
| 84be318ca9 | |||
| 5035718e4b | |||
| fdc8fbaa45 | |||
| 99887da49a | |||
| 7a36a6c92a | |||
| 6beac0f215 | |||
| b59469c151 | |||
| b07466a477 | |||
| 0bd22e78d3 | |||
| f073352632 | |||
| aad25205cd | |||
| dcdc453a55 | |||
| 5636575327 | |||
| 00f80c726a | |||
| 74db6c2b3b | |||
| d095ba84bf | |||
| 209e2de93b | |||
| c37c233d22 | |||
| 7b463769a5 | |||
| c7636bdc4c | |||
| 307d34e05a | |||
| 69e21dacb8 | |||
| f27a1ac040 | |||
| af6fdae3dd | |||
| 6ae22e084a | |||
| d463b26709 | |||
| 271e6192e8 | |||
| 51906bae6c | |||
| 406004a99a | |||
| b50894aabe | |||
| d6f07267ee | |||
| 1827cbfd2a | |||
| a0c0cb66c5 | |||
| 47aa4bbf8f | |||
| 33de70158c | |||
| f45a4e8243 | |||
| ac75f4e2ed | |||
| 631748480e | |||
| 3d5c759d62 | |||
| 2e0440e549 | |||
| 86beb5b730 | |||
| 289fd87ef8 | |||
| 8b11035b39 | |||
| 21f251eafb | |||
| e7b1c33955 | |||
| 1da0467820 | |||
| 05609de044 | |||
| 02fb43ff52 | |||
| 1626c0095f | |||
| 341ce621a9 | |||
| 52efe2412c | |||
| 84e3e76bb8 | |||
| 1d162e95c5 | |||
| 060843d912 | |||
| d3bc0a4ef7 | |||
| d1a82dde63 | |||
| 99efe80c61 | |||
| b12db57f34 | |||
| 9333fec5f1 | |||
| 6f94af9264 | |||
| 0ed98ea9b9 | |||
| 0b66144942 | |||
| b91a3eded3 | |||
| 7c9c0f6538 | |||
| 0d6846a882 | |||
| 84a68e0c25 | |||
| e82fa4eae0 | |||
| c79c6d76d8 | |||
| 36452136f1 | |||
| 17cfb0fc78 | |||
| e86ac90853 | |||
| 99e0f5b5cb | |||
| 4fc6bb7977 | |||
| 6e66ba321e | |||
| 2c2067ecc1 | |||
| e6a7a978d1 | |||
| 3e02f45f67 | |||
| b972f43513 | |||
| 30d02b1c78 | |||
| 479d01101d | |||
| 2b079136fb | |||
| 46c5759b88 | |||
| 7c883b6368 | |||
| 69daed7746 | |||
| 0b9afefa93 | |||
| 52490b92bf | |||
| 2261039d52 | |||
| 24323d3ce3 | |||
| c6e055689b | |||
| 2b5192b9ce | |||
| 77dfcb6e23 | |||
| 4fe837704b | |||
| cf58330936 | |||
| 287b873add | |||
| 115e7afe0b | |||
| a9e1fcd623 | |||
| 04e0b2e5c2 | |||
| 2403508e1b | |||
| b414545b6e | |||
| 3296471bda | |||
| eb5b6252bc | |||
| e5da857d4f | |||
| 09a8da5d6a | |||
| 863ad48a81 | |||
| de0e6ba222 | |||
| 91e5a925d9 | |||
| 5e7c168b25 | |||
| 6fccd202fe | |||
| 18e98fbba8 | |||
| a799d218fd | |||
| 9f0cbd3303 | |||
| 93fb8f35a8 | |||
| 2303fd51a3 | |||
| 910fc4915e | |||
| 002acb984e | |||
| bebe704688 | |||
| 9a006f3a68 | |||
| 58e2e4d742 | |||
| 60bc6a6da5 | |||
| b3f60b9320 | |||
| e9cf6dfb60 | |||
| 5e7847537a | |||
| 64787b1ac5 | |||
| 58fdb956b5 | |||
| 4d91118dd9 | |||
| ed872ca0a1 | |||
| 1bb1d41beb | |||
| 1db46aa5d5 | |||
| c991eaf916 | |||
| 86a69fd6d6 | |||
| 6735a3ccd6 | |||
| 6d565ef841 | |||
| b2e36314cb | |||
| 6904f12032 | |||
| c3a30e9be3 | |||
| 93b953fb48 | |||
| e2fdf09e9b | |||
| 4f44427966 | |||
| f8c0fab5d8 | |||
| 478d1b1a93 | |||
| a68eaa4e2d | |||
| 68c2bcb9dd | |||
| 635c04378d | |||
| 227c7aae0f | |||
| 641b751385 | |||
| 4201d50dcf | |||
| b05fb9c200 | |||
| 4f34d41523 | |||
| 91fc295a42 | |||
| 67d3a759a7 | |||
| 950b6a5e56 | |||
| db40fd314a | |||
| 6035bd522b | |||
| b8884687df | |||
| 18581f138f | |||
| f3867f8477 | |||
| 353285e5d9 | |||
| a56bcbf8fd | |||
| 81297fac88 | |||
| 6331dd125d | |||
| f8790c1ed7 | |||
| 5afbdf00db | |||
| 322a9598ab | |||
| ba86e8e095 | |||
| 5e423bc353 | |||
| 6eac859781 | |||
| f2c2c01f8e | |||
| 7434b3d6d2 | |||
| 63b5fc5971 | |||
| f507a0ea29 | |||
| c254d8703d | |||
| 981ec2dbdc | |||
| d5a75362ec | |||
| e748c3ea36 | |||
| 421ed4f8ab | |||
| 0b1f08043b | |||
| 759f8de81f | |||
| 96b550b722 | |||
| b961eea89d | |||
| d2dd6e40b6 | |||
| 070bcbcdbd | |||
| 46fe7ee76d | |||
| ba383a6d20 | |||
| a66f5f8f03 | |||
| 262b7dd7dd | |||
| 5d847ed4d9 | |||
| b3ca0c73b3 | |||
| d6a45ef14e | |||
| 54d74b1c26 | |||
| 657c56c650 | |||
| 50a903a62d | |||
| 0791e6639a | |||
| 59e9609926 | |||
| fadb2cf47e | |||
| 4d55d8fa8e | |||
| e31abffcb6 | |||
| cd47fbe953 | |||
| 42c6392533 | |||
| 3c9638b62e | |||
| 60ef284a9a | |||
| b40a01e97b | |||
| 34e3ce4ab4 | |||
| 4d6b48f39e | |||
| de99e7bf34 | |||
| 3430c7a289 | |||
| daf1784be4 | |||
| de92a02127 | |||
| 549590d105 | |||
| 82ca385725 | |||
| e33d9bb41b | |||
| 79b3a1b1a2 | |||
| c6a3c29eb4 | |||
| dd34d8df0f | |||
| 80d052d43f | |||
| 710f77dfe1 | |||
| b1e504d6c6 | |||
| 7deaee04c8 | |||
| 2020d56ba4 | |||
| a49fe53fa1 | |||
| bc3db5d4c1 | |||
| 30e202313d | |||
| 2e0bd06da2 | |||
| 3c4d0d0a77 | |||
| 403e7336a9 | |||
| fea2a8383c | |||
| ffe464a43e | |||
| 3fbcb1b282 | |||
| 28b2a0c881 | |||
| 7b9c1429d9 | |||
| d53ddd4a95 | |||
| 6ede3d9001 | |||
| c3cb0dcf80 | |||
| f214dccab9 | |||
| c0cce27703 | |||
| 2eded7a337 | |||
| 41a6f1cd84 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,4 +4,6 @@ node_modules/
|
||||
build/
|
||||
.DS_Store/
|
||||
/coverage
|
||||
coverage.json
|
||||
coverage.json
|
||||
allFiredEvents
|
||||
scTopics
|
||||
|
||||
5
.solcover.js
Normal file
5
.solcover.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
norpc: true,
|
||||
testCommand: 'node --max-old-space-size=4096 ../node_modules/.bin/truffle test --network coverage',
|
||||
skipFiles: ['lifecycle/Migrations.sol']
|
||||
}
|
||||
@ -1 +1 @@
|
||||
node_modules
|
||||
node_modules
|
||||
|
||||
17
.travis.yml
17
.travis.yml
@ -1,12 +1,17 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
sudo: required
|
||||
group: beta
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
before_install:
|
||||
- npm install truffle@3.1.9 -g
|
||||
- npm i -g ethereumjs-testrpc
|
||||
cache:
|
||||
yarn: true
|
||||
env:
|
||||
-
|
||||
- SOLIDITY_COVERAGE=true
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: SOLIDITY_COVERAGE=true
|
||||
script:
|
||||
- testrpc > /dev/null &
|
||||
- truffle test
|
||||
- yarn test
|
||||
|
||||
16
LICENSE
16
LICENSE
@ -5,18 +5,18 @@ Copyright (c) 2016 Smart Contract Solutions, Inc.
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
# Zeppelin Solidity
|
||||
[](https://www.npmjs.org/package/zeppelin-solidity)
|
||||
[](https://travis-ci.org/OpenZeppelin/zeppelin-solidity)
|
||||
[](https://coveralls.io/github/OpenZeppelin/zeppelin-solidity?branch=master)
|
||||
|
||||
OpenZeppelin is a library for writing secure [Smart Contracts](https://en.wikipedia.org/wiki/Smart_contract) on Ethereum.
|
||||
|
||||
@ -15,7 +16,7 @@ With OpenZeppelin, you can build distributed applications, protocols and organiz
|
||||
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@beta
|
||||
npm install -g truffle
|
||||
mkdir myproject && cd myproject
|
||||
truffle init
|
||||
```
|
||||
@ -23,6 +24,9 @@ truffle init
|
||||
To install the OpenZeppelin library, run:
|
||||
```sh
|
||||
npm install zeppelin-solidity
|
||||
|
||||
# If you are using yarn, add dependency like this -
|
||||
yarn add zeppelin-solidity
|
||||
```
|
||||
|
||||
After that, you'll get all the library's contracts in the `node_modules/zeppelin-solidity/contracts` folder. You can use the contracts in the library like so:
|
||||
|
||||
@ -5,7 +5,7 @@ Authored by Dennis Peterson and Peter Vessenes
|
||||
|
||||
# Introduction
|
||||
|
||||
Zeppelin requested that New Alchemy perform an audit of the contracts in their OpenZeppelin library. The OpenZeppelin contracts are a set of contracts intended to be a safe building block for a variety of uses by parties that may not be as sophisticated as the OpenZeppelin team. It is a design goal that the contracts be deployable safely and "as-is".
|
||||
Zeppelin requested that New Alchemy perform an audit of the contracts in their OpenZeppelin library. The OpenZeppelin contracts are a set of contracts intended to be a safe building block for a variety of uses by parties that may not be as sophisticated as the OpenZeppelin team. It is a design goal that the contracts be deployable safely and "as-is".
|
||||
|
||||
The contracts are hosted at:
|
||||
|
||||
@ -22,9 +22,9 @@ The audit makes no statements or warrantees about utility of the code, safety of
|
||||
|
||||
# Executive Summary
|
||||
|
||||
Overall the OpenZeppelin codebase is of reasonably high quality -- it is clean, modular and follows best practices throughout.
|
||||
Overall the OpenZeppelin codebase is of reasonably high quality -- it is clean, modular and follows best practices throughout.
|
||||
|
||||
It is still in flux as a codebase, and needs better documentation per file as to expected behavior and future plans. It probably needs more comprehensive and aggressive tests written by people less nice than the current OpenZeppelin team.
|
||||
It is still in flux as a codebase, and needs better documentation per file as to expected behavior and future plans. It probably needs more comprehensive and aggressive tests written by people less nice than the current OpenZeppelin team.
|
||||
|
||||
We identified two critical errors and one moderate issue, and would not recommend this commit hash for public use until these bugs are remedied.
|
||||
|
||||
@ -34,12 +34,12 @@ The repository includes a set of Truffle unit tests, a requirement and best prac
|
||||
|
||||
## Big Picture: Is This A Worthwhile Project?
|
||||
|
||||
As soon as a developer touches OpenZeppelin contracts, they will modify something, leaving them in an un-audited state. We do not recommend developers deploy any unaudited code to the Blockchain if it will handle money, information or other things of value.
|
||||
As soon as a developer touches OpenZeppelin contracts, they will modify something, leaving them in an un-audited state. We do not recommend developers deploy any unaudited code to the Blockchain if it will handle money, information or other things of value.
|
||||
|
||||
> "In accordance with Unix philosophy, Perl gives you enough rope to hang yourself"
|
||||
> --Larry Wall
|
||||
|
||||
We think this is an incredibly worthwhile project -- aided by the high code quality. Creating a framework that can be easily extended helps increase the average code quality on the Blockchain by charting a course for developers and encouraging containment of modifications to certain sections.
|
||||
We think this is an incredibly worthwhile project -- aided by the high code quality. Creating a framework that can be easily extended helps increase the average code quality on the Blockchain by charting a course for developers and encouraging containment of modifications to certain sections.
|
||||
|
||||
> "Rust: The language that makes you take the safety off before shooting yourself in the foot"
|
||||
> -- (@mbrubeck)
|
||||
@ -48,7 +48,7 @@ We think much more could be done here, and recommend the OpenZeppelin team keep
|
||||
|
||||
## Solidity Version Updates Recommended
|
||||
|
||||
Most of the code uses Solidity 0.4.8, but some files under `Ownership` are marked 0.4.0. These should be updated.
|
||||
Most of the code uses Solidity 0.4.11, but some files under `Ownership` are marked 0.4.0. These should be updated.
|
||||
|
||||
Solidity 0.4.10 will add several features which could be useful in these contracts:
|
||||
|
||||
@ -65,7 +65,7 @@ In general we prefer `throw` in our code audits, because it is simpler -- it's l
|
||||
|
||||
In the OpenZeppelin contracts, both styles are used in different parts of the codebase. `SimpleToken` transfers throw upon failure, while the full ERC20 token returns `false`. Some modifiers `throw`, others just wrap the function body in a conditional, effectively allowing the function to return false if the condition is not met.
|
||||
|
||||
We don't love this, and would usually recommend you stick with one style or the other throughout the codebase.
|
||||
We don't love this, and would usually recommend you stick with one style or the other throughout the codebase.
|
||||
|
||||
In at least one case, these different techniques are combined cleverly (see the Multisig comments, line 65). As a set of contracts intended for general use, we recommend you either strive for more consistency or document explicit design criteria that govern which techniques are used where.
|
||||
|
||||
@ -77,19 +77,19 @@ Note that it may be impossible to use either one in all situations. For example,
|
||||
CrowdsaleToken.sol has no provision for withdrawing the raised ether. We *strongly* recommend a standard `withdraw` function be added. There is no scenario in which someone should deploy this contract as is, whether for testing or live.
|
||||
|
||||
## Recursive Call in MultisigWallet
|
||||
Line 45 of `MultisigWallet.sol` checks if the amount being sent by `execute` is under a daily limit.
|
||||
Line 45 of `MultisigWallet.sol` checks if the amount being sent by `execute` is under a daily limit.
|
||||
|
||||
This function can only be called by the "Owner". As a first angle of attack, it's worth asking what will happen if the multisig wallet owners reset the daily limit by approving a call to `resetSpentToday`.
|
||||
This function can only be called by the "Owner". As a first angle of attack, it's worth asking what will happen if the multisig wallet owners reset the daily limit by approving a call to `resetSpentToday`.
|
||||
|
||||
If a chain of calls can be constructed in which the owner confirms the `resetSpentToday` function and then withdraws through `execute` in a recursive call, the contract can be drained. In fact, this could be done without a recursive call, just through repeated `execute` calls alternating with the `confirm` calls.
|
||||
|
||||
We are still working through the confirmation protocol in `Shareable.sol`, but we are not convinced that this is impossible, in fact it looks possible. The flexibility any shared owner has in being able to revoke confirmation later is another worrisome angle of approach even if some simple patches are included.
|
||||
We are still working through the confirmation protocol in `Shareable.sol`, but we are not convinced that this is impossible, in fact it looks possible. The flexibility any shared owner has in being able to revoke confirmation later is another worrisome angle of approach even if some simple patches are included.
|
||||
|
||||
This bug has a number of causes that need to be addressed:
|
||||
|
||||
1. `resetSpentToday` and `confirm` together do not limit the days on which the function can be called or (it appears) the number of times it can be called.
|
||||
1. Once a call has been confirmed and `execute`d it appears that it can be re-executed. This is not good.
|
||||
3. `confirmandCheck` doesn't seem to have logic about whether or not the function in question has been called.
|
||||
1. `resetSpentToday` and `confirm` together do not limit the days on which the function can be called or (it appears) the number of times it can be called.
|
||||
1. Once a call has been confirmed and `execute`d it appears that it can be re-executed. This is not good.
|
||||
3. `confirmandCheck` doesn't seem to have logic about whether or not the function in question has been called.
|
||||
4. Even if it did, `revoke` would need updates and logic to deal with revocation requests after a function call had been completed.
|
||||
|
||||
We do not recommend using the MultisigWallet until these issues are fixed.
|
||||
@ -97,9 +97,9 @@ We do not recommend using the MultisigWallet until these issues are fixed.
|
||||
# Moderate to Minor Issues
|
||||
|
||||
## PullPayment
|
||||
PullPayment.sol needs some work. It has no explicit provision for cancelling a payment. This would be desirable in a number of scenarios; consider a payee losing their wallet, or giving a griefing address, or just an address that requires more than the default gas offered by `send`.
|
||||
PullPayment.sol needs some work. It has no explicit provision for cancelling a payment. This would be desirable in a number of scenarios; consider a payee losing their wallet, or giving a griefing address, or just an address that requires more than the default gas offered by `send`.
|
||||
|
||||
`asyncSend` has no overflow checking. This is a bad plan. We recommend overflow and underflow checking at the layer closest to the data manipulation.
|
||||
`asyncSend` has no overflow checking. This is a bad plan. We recommend overflow and underflow checking at the layer closest to the data manipulation.
|
||||
|
||||
`asyncSend` allows more balance to be queued up for sending than the contract holds. This is probably a bad idea, or at the very least should be called something different. If the intent is to allow this, it should have provisions for dealing with race conditions between competing `withdrawPayments` calls.
|
||||
|
||||
@ -107,7 +107,7 @@ It would be nice to see how many payments are pending. This would imply a bit of
|
||||
|
||||
## Shareable Contract
|
||||
|
||||
We do not believe the `Shareable.sol` contract is ready for primetime. It is missing functions, and as written may be vulnerable to a reordering attack -- an attack in which a miner or other party "racing" with a smart contract participant inserts their own information into a list or mapping.
|
||||
We do not believe the `Shareable.sol` contract is ready for primetime. It is missing functions, and as written may be vulnerable to a reordering attack -- an attack in which a miner or other party "racing" with a smart contract participant inserts their own information into a list or mapping.
|
||||
|
||||
The confirmation and revocation code needs to be looked over with a very careful eye imagining extraordinarily bad behavior by shared owners before this contract can be called safe.
|
||||
|
||||
@ -129,7 +129,7 @@ I presume that the goal of this contract is to allow and annotate a migration to
|
||||
|
||||
### Pausable
|
||||
|
||||
We like these pauses! Note that these allow significant griefing potential by owners, and that this might not be obvious to participants in smart contracts using the OpenZeppelin framework. We would recommend that additional sample logic be added to for instance the TokenContract showing safer use of the pause and resume functions. In particular, we would recommend a timelock after which anyone could unpause the contract.
|
||||
We like these pauses! Note that these allow significant griefing potential by owners, and that this might not be obvious to participants in smart contracts using the OpenZeppelin framework. We would recommend that additional sample logic be added to for instance the TokenContract showing safer use of the pause and resume functions. In particular, we would recommend a timelock after which anyone could unpause the contract.
|
||||
|
||||
The modifers use the pattern `if(bool){_;}`. This is fine for functions that return false upon failure, but could be problematic for functions expected to throw upon failure. See our comments above on standardizing on `throw` or `return(false)`.
|
||||
|
||||
@ -163,7 +163,7 @@ Line 34: "this contract only has six types of events"...actually only two.
|
||||
|
||||
Line 61: Why is `ownerIndex` keyed by addresses hashed to `uint`s? Why not use the addresses directly, so `ownerIndex` is less obscure, and so there's stronger typing?
|
||||
|
||||
Line 62: Do not love `++i) ... owners[2+ i]`. Makes me do math, which is not what I want to do. I want to not have to do math.
|
||||
Line 62: Do not love `++i) ... owners[2+ i]`. Makes me do math, which is not what I want to do. I want to not have to do math.
|
||||
|
||||
There should probably be a function for adding a new operation, so the developer doesn't have to work directly with the internal data. (This would make the multisig contract even shorter.)
|
||||
|
||||
@ -171,7 +171,7 @@ There's a `revoke` function but not a `propose` function that we can see.
|
||||
|
||||
Beware reordering. If `propose` allows the user to choose a bytes string for their proposal, bad things(TM) will happen as currently written.
|
||||
|
||||
|
||||
|
||||
### Multisig
|
||||
|
||||
Just an interface. Note it allows changing an owner address, but not changing the number of owners. This is somewhat limiting but also simplifies implementation.
|
||||
@ -184,9 +184,9 @@ Safe from reentrance attack since ether send is at the end, plus it uses `.send(
|
||||
|
||||
There's an argument to be made that `.call.value()` is a better option *if* you're sure that it will be done after all state updates, since `.send` will fail if the recipient has an expensive fallback function. However, in the context of a function meant to be embedded in other contracts, it's probably better to use `.send`. One possible compromise is to add a function which allows only the owner to send ether via `.call.value`.
|
||||
|
||||
If you don't use `call.value` you should implement a `cancel` function in case some value is pending here.
|
||||
If you don't use `call.value` you should implement a `cancel` function in case some value is pending here.
|
||||
|
||||
Line 14:
|
||||
Line 14:
|
||||
Doesn't use safeAdd. Although it appears that payout amounts can only be increased, in fact the payer could lower the payout as much as desired via overflow. Also, the payer could add a large non-overflowing amount, causing the payment to exceed the contract balance and therefore fail when withdraw is attempted.
|
||||
|
||||
Recommendation: track the sum of non-withdrawn asyncSends, and don't allow a new one which exceeds the leftover balance. If it's ever desirable to make payments revocable, it should be done explicitly.
|
||||
@ -195,7 +195,7 @@ Recommendation: track the sum of non-withdrawn asyncSends, and don't allow a new
|
||||
|
||||
### ERC20
|
||||
|
||||
Standard ERC20 interface only.
|
||||
Standard ERC20 interface only.
|
||||
|
||||
There's a security hole in the standard, reported at Edcon: `approve` does not protect against race conditions and simply replaces the current value. An approved spender could wait for the owner to call `approve` again, then attempt to spend the old limit before the new limit is applied. If successful, this attacker could successfully spend the sum of both limits.
|
||||
|
||||
@ -208,11 +208,11 @@ https://drive.google.com/file/d/0ByMtMw2hul0EN3NCaVFHSFdxRzA/view
|
||||
|
||||
### ERC20Basic
|
||||
|
||||
Simpler interface skipping the Approve function. Note this departs from ERC20 in another way: transfer throws instead of returning false.
|
||||
Simpler interface skipping the Approve function. Note this departs from ERC20 in another way: transfer throws instead of returning false.
|
||||
|
||||
### BasicToken
|
||||
|
||||
Uses `SafeSub` and `SafeMath`, so transfer `throw`s instead of returning false. This complies with ERC20Basic but not the actual ERC20 standard.
|
||||
Uses `SafeSub` and `SafeMath`, so transfer `throw`s instead of returning false. This complies with ERC20Basic but not the actual ERC20 standard.
|
||||
|
||||
### StandardToken
|
||||
|
||||
@ -234,10 +234,10 @@ Note: an alternative pattern is a mint() function which is only callable from a
|
||||
|
||||
### VestedToken
|
||||
|
||||
Lines 23, 27:
|
||||
Functions `transfer()` and `transferFrom()` have a modifier canTransfer which throws if not enough tokens are available. However, transfer() returns a boolean success. Inconsistent treatment of failure conditions may cause problems for other contracts using the token. (Note that transferableTokens() relies on safeSub(), so will also throw if there's insufficient balance.)
|
||||
Lines 23, 27:
|
||||
Functions `transfer()` and `transferFrom()` have a modifier canTransfer which throws if not enough tokens are available. However, transfer() returns a boolean success. Inconsistent treatment of failure conditions may cause problems for other contracts using the token. (Note that transferableTokens() relies on safeSub(), so will also throw if there's insufficient balance.)
|
||||
|
||||
Line 64:
|
||||
Line 64:
|
||||
Delete not actually necessary since the value is overwritten in the next line anyway.
|
||||
|
||||
## Root level
|
||||
@ -255,7 +255,7 @@ The modifier `limitedDaily` calls `underLimit`, which both checks that the spend
|
||||
Lines 4, 11:
|
||||
Comment claims that `DayLimit` is multiowned, and Shareable is imported, but DayLimit does not actually inherit from Shareable. The intent may be for child contracts to inherit from Shareable (as Multisig does); in this case the import should be removed and the comment altered.
|
||||
|
||||
Line 46:
|
||||
Line 46:
|
||||
Manual overflow check instead of using safeAdd. Since this is called from a function that throws upon failure anyway, there's no real downside to using safeAdd.
|
||||
|
||||
### LimitBalance
|
||||
@ -264,19 +264,19 @@ No issues.
|
||||
|
||||
### MultisigWallet
|
||||
|
||||
Lines 28, 76, 80:
|
||||
Lines 28, 76, 80:
|
||||
`kill`, `setDailyLimit`, and `resetSpentToday` only happen with multisig approval, and hashes for these actions are logged by Shareable. However, they should probably post their own events for easy reading.
|
||||
|
||||
Line 45:
|
||||
Line 45:
|
||||
This call to underLimit will reduce the daily limit, and then either throw or return 0. So in this case there's no danger that the limit will be reduced without the operation going through.
|
||||
|
||||
Line 65:
|
||||
Line 65:
|
||||
Shareable's onlyManyOwners will take the user's confirmation, and execute the function body if and only if enough users have confirmed. Whole thing throws if the send fails, which will roll back the confirmation. Confirm returns false if not enough have confirmed yet, true if the whole thing succeeds, and throws only in the exceptional circumstance that the designated transaction unexpectedly fails. Elegant design.
|
||||
|
||||
Line 68:
|
||||
Line 68:
|
||||
Throw here is good but note this function can fail either by returning false or by throwing.
|
||||
|
||||
Line 92:
|
||||
Line 92:
|
||||
A bit odd to split `clearPending()` between this contract and Shareable. However this does allow contracts inheriting from Shareable to use custom structs for pending transactions.
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './payment/PullPayment.sol';
|
||||
@ -16,20 +16,18 @@ contract Bounty is PullPayment, Destructible {
|
||||
event TargetCreated(address createdAddress);
|
||||
|
||||
/**
|
||||
* @dev Fallback function allowing the contract to recieve funds, if they haven't already been claimed.
|
||||
* @dev Fallback function allowing the contract to receive funds, if they haven't already been claimed.
|
||||
*/
|
||||
function() payable {
|
||||
if (claimed) {
|
||||
throw;
|
||||
}
|
||||
require(!claimed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Create and deploy the target contract (extension of Target contract), and sets the
|
||||
* @dev Create and deploy the target contract (extension of Target contract), and sets the
|
||||
* msg.sender as a researcher
|
||||
* @return A target contract
|
||||
*/
|
||||
function createTarget() returns(Target) {
|
||||
function createTarget() public returns(Target) {
|
||||
Target target = Target(deployContract());
|
||||
researchers[target] = msg.sender;
|
||||
TargetCreated(target);
|
||||
@ -46,15 +44,11 @@ contract Bounty is PullPayment, Destructible {
|
||||
* @dev Sends the contract funds to the researcher that proved the contract is broken.
|
||||
* @param target contract
|
||||
*/
|
||||
function claim(Target target) {
|
||||
function claim(Target target) public {
|
||||
address researcher = researchers[target];
|
||||
if (researcher == 0) {
|
||||
throw;
|
||||
}
|
||||
require(researcher != 0);
|
||||
// Check Target contract invariants
|
||||
if (target.checkInvariant()) {
|
||||
throw;
|
||||
}
|
||||
require(!target.checkInvariant());
|
||||
asyncSend(researcher, this.balance);
|
||||
claimed = true;
|
||||
}
|
||||
@ -69,10 +63,10 @@ contract Bounty is PullPayment, Destructible {
|
||||
contract Target {
|
||||
|
||||
/**
|
||||
* @dev Checks all values a contract assumes to be true all the time. If this function returns
|
||||
* false, the contract is broken in some way and is in an inconsistent state.
|
||||
* In order to win the bounty, security researchers will try to cause this broken state.
|
||||
* @return True if all invariant values are correct, false otherwise.
|
||||
* @dev Checks all values a contract assumes to be true all the time. If this function returns
|
||||
* false, the contract is broken in some way and is in an inconsistent state.
|
||||
* In order to win the bounty, security researchers will try to cause this broken state.
|
||||
* @return True if all invariant values are correct, false otherwise.
|
||||
*/
|
||||
function checkInvariant() returns(bool);
|
||||
function checkInvariant() public returns(bool);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
/**
|
||||
* @title DayLimit
|
||||
@ -7,24 +7,24 @@ pragma solidity ^0.4.8;
|
||||
*/
|
||||
contract DayLimit {
|
||||
|
||||
uint public dailyLimit;
|
||||
uint public spentToday;
|
||||
uint public lastDay;
|
||||
uint256 public dailyLimit;
|
||||
uint256 public spentToday;
|
||||
uint256 public lastDay;
|
||||
|
||||
/**
|
||||
* @dev Constructor that sets the passed value as a dailyLimit.
|
||||
* @param _limit Uint to represent the daily limit.
|
||||
* @param _limit uint256 to represent the daily limit.
|
||||
*/
|
||||
function DayLimit(uint _limit) {
|
||||
function DayLimit(uint256 _limit) {
|
||||
dailyLimit = _limit;
|
||||
lastDay = today();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev sets the daily limit. Does not alter the amount already spent today.
|
||||
* @param _newLimit Uint to represent the new limit.
|
||||
* @param _newLimit uint256 to represent the new limit.
|
||||
*/
|
||||
function _setDailyLimit(uint _newLimit) internal {
|
||||
function _setDailyLimit(uint256 _newLimit) internal {
|
||||
dailyLimit = _newLimit;
|
||||
}
|
||||
|
||||
@ -37,10 +37,10 @@ contract DayLimit {
|
||||
|
||||
/**
|
||||
* @dev Checks to see if there is enough resource to spend today. If true, the resource may be expended.
|
||||
* @param _value Uint representing the amount of resource to spend.
|
||||
* @return A boolean that is True if the resource was spended and false otherwise.
|
||||
* @param _value uint256 representing the amount of resource to spend.
|
||||
* @return A boolean that is True if the resource was spent and false otherwise.
|
||||
*/
|
||||
function underLimit(uint _value) internal returns (bool) {
|
||||
function underLimit(uint256 _value) internal returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > lastDay) {
|
||||
spentToday = 0;
|
||||
@ -57,19 +57,17 @@ contract DayLimit {
|
||||
|
||||
/**
|
||||
* @dev Private function to determine today's index
|
||||
* @return Uint of today's index.
|
||||
* @return uint256 of today's index.
|
||||
*/
|
||||
function today() private constant returns (uint) {
|
||||
function today() private constant returns (uint256) {
|
||||
return now / 1 days;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Simple modifier for daily limit.
|
||||
*/
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (!underLimit(_value)) {
|
||||
throw;
|
||||
}
|
||||
modifier limitedDaily(uint256 _value) {
|
||||
require(underLimit(_value));
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
47
contracts/ECRecovery.sol
Normal file
47
contracts/ECRecovery.sol
Normal file
@ -0,0 +1,47 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
/**
|
||||
* @title Eliptic curve signature operations
|
||||
*
|
||||
* @dev Based on https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
|
||||
*/
|
||||
|
||||
library ECRecovery {
|
||||
|
||||
/**
|
||||
* @dev Recover signer address from a message by using his signature
|
||||
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
|
||||
* @param sig bytes signature, the signature is generated using web3.eth.sign()
|
||||
*/
|
||||
function recover(bytes32 hash, bytes sig) public constant returns (address) {
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
uint8 v;
|
||||
|
||||
//Check the signature length
|
||||
if (sig.length != 65) {
|
||||
return (address(0));
|
||||
}
|
||||
|
||||
// Divide the signature in r, s and v variables
|
||||
assembly {
|
||||
r := mload(add(sig, 32))
|
||||
s := mload(add(sig, 64))
|
||||
v := byte(0, mload(add(sig, 96)))
|
||||
}
|
||||
|
||||
// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
|
||||
if (v < 27) {
|
||||
v += 27;
|
||||
}
|
||||
|
||||
// If the version is correct return the signer address
|
||||
if (v != 27 && v != 28) {
|
||||
return (address(0));
|
||||
} else {
|
||||
return ecrecover(hash, v, r, s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
/**
|
||||
@ -9,13 +9,13 @@ pragma solidity ^0.4.8;
|
||||
*/
|
||||
contract LimitBalance {
|
||||
|
||||
uint public limit;
|
||||
uint256 public limit;
|
||||
|
||||
/**
|
||||
* @dev Constructor that sets the passed value as a limit.
|
||||
* @param _limit Uint to represent the limit.
|
||||
* @dev Constructor that sets the passed value as a limit.
|
||||
* @param _limit uint256 to represent the limit.
|
||||
*/
|
||||
function LimitBalance(uint _limit) {
|
||||
function LimitBalance(uint256 _limit) {
|
||||
limit = _limit;
|
||||
}
|
||||
|
||||
@ -23,9 +23,7 @@ contract LimitBalance {
|
||||
* @dev Checks if limit was reached. Case true, it throws.
|
||||
*/
|
||||
modifier limitedPayable() {
|
||||
if (this.balance > limit) {
|
||||
throw;
|
||||
}
|
||||
require(this.balance <= limit);
|
||||
_;
|
||||
|
||||
}
|
||||
|
||||
41
contracts/MerkleProof.sol
Normal file
41
contracts/MerkleProof.sol
Normal file
@ -0,0 +1,41 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
/*
|
||||
* @title MerkleProof
|
||||
* @dev Merkle proof verification
|
||||
* @note Based on https://github.com/ameensol/merkle-tree-solidity/blob/master/src/MerkleProof.sol
|
||||
*/
|
||||
library MerkleProof {
|
||||
/*
|
||||
* @dev Verifies a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves
|
||||
* and each pair of pre-images is sorted.
|
||||
* @param _proof Merkle proof containing sibling hashes on the branch from the leaf to the root of the Merkle tree
|
||||
* @param _root Merkle root
|
||||
* @param _leaf Leaf of Merkle tree
|
||||
*/
|
||||
function verifyProof(bytes _proof, bytes32 _root, bytes32 _leaf) constant returns (bool) {
|
||||
// Check if proof length is a multiple of 32
|
||||
if (_proof.length % 32 != 0) return false;
|
||||
|
||||
bytes32 proofElement;
|
||||
bytes32 computedHash = _leaf;
|
||||
|
||||
for (uint256 i = 32; i <= _proof.length; i += 32) {
|
||||
assembly {
|
||||
// Load the current element of the proof
|
||||
proofElement := mload(add(_proof, i))
|
||||
}
|
||||
|
||||
if (computedHash < proofElement) {
|
||||
// Hash(current computed hash + current element of the proof)
|
||||
computedHash = keccak256(computedHash, proofElement);
|
||||
} else {
|
||||
// Hash(current element of the proof + current computed hash)
|
||||
computedHash = keccak256(proofElement, computedHash);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the computed hash (root) is equal to the provided root
|
||||
return computedHash == _root;
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./ownership/Multisig.sol";
|
||||
import "./ownership/Shareable.sol";
|
||||
import "./DayLimit.sol";
|
||||
|
||||
|
||||
/**
|
||||
* MultisigWallet
|
||||
* Usage:
|
||||
* bytes32 h = Wallet(w).from(oneOwner).execute(to, value, data);
|
||||
* Wallet(w).from(anotherOwner).confirm(h);
|
||||
*/
|
||||
contract MultisigWallet is Multisig, Shareable, DayLimit {
|
||||
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor, sets the owners addresses, number of approvals required, and daily spending limit
|
||||
* @param _owners A list of owners.
|
||||
* @param _required The amount required for a transaction to be approved.
|
||||
*/
|
||||
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
|
||||
Shareable(_owners, _required)
|
||||
DayLimit(_daylimit) { }
|
||||
|
||||
/**
|
||||
* @dev destroys the contract sending everything to `_to`.
|
||||
*/
|
||||
function destroy(address _to) onlymanyowners(keccak256(msg.data)) external {
|
||||
selfdestruct(_to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Fallback function, receives value and emits a deposit event.
|
||||
*/
|
||||
function() payable {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Outside-visible transaction entry point. Executes transaction immediately if below daily
|
||||
* spending limit. If not, goes into multisig process. We provide a hash on return to allow the
|
||||
* sender to provide shortcuts for the other confirmations (allowing them to avoid replicating
|
||||
* the _to, _value, and _data arguments). They still get the option of using them if they want,
|
||||
* anyways.
|
||||
* @param _to The receiver address
|
||||
* @param _value The value to send
|
||||
* @param _data The data part of the transaction
|
||||
*/
|
||||
function execute(address _to, uint _value, bytes _data) external onlyOwner returns (bytes32 _r) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if (underLimit(_value)) {
|
||||
SingleTransact(msg.sender, _value, _to, _data);
|
||||
// yes - just execute the call.
|
||||
if (!_to.call.value(_value)(_data)) {
|
||||
throw;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// determine our operation hash.
|
||||
_r = keccak256(msg.data, block.number);
|
||||
if (!confirm(_r) && txs[_r].to == 0) {
|
||||
txs[_r].to = _to;
|
||||
txs[_r].value = _value;
|
||||
txs[_r].data = _data;
|
||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Confirm a transaction by providing just the hash. We use the previous transactions map,
|
||||
* txs, in order to determine the body of the transaction from the hash provided.
|
||||
* @param _h The transaction hash to approve.
|
||||
*/
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
||||
if (txs[_h].to != 0) {
|
||||
if (!txs[_h].to.call.value(txs[_h].value)(txs[_h].data)) {
|
||||
throw;
|
||||
}
|
||||
MultiTransact(msg.sender, _h, txs[_h].value, txs[_h].to, txs[_h].data);
|
||||
delete txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Updates the daily limit value.
|
||||
* @param _newLimit
|
||||
*/
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(keccak256(msg.data)) external {
|
||||
_setDailyLimit(_newLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Resets the value spent to enable more spending
|
||||
*/
|
||||
function resetSpentToday() onlymanyowners(keccak256(msg.data)) external {
|
||||
_resetSpentToday();
|
||||
}
|
||||
|
||||
|
||||
// INTERNAL METHODS
|
||||
/**
|
||||
* @dev Clears the list of transactions pending approval.
|
||||
*/
|
||||
function clearPending() internal {
|
||||
uint length = pendingsIndex.length;
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
delete txs[pendingsIndex[i]];
|
||||
}
|
||||
super.clearPending();
|
||||
}
|
||||
|
||||
|
||||
// FIELDS
|
||||
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) txs;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
/**
|
||||
* @title Helps contracts guard agains rentrancy attacks.
|
||||
@ -9,7 +9,7 @@ pragma solidity ^0.4.8;
|
||||
contract ReentrancyGuard {
|
||||
|
||||
/**
|
||||
* @dev We use a single lock for the whole contract.
|
||||
* @dev We use a single lock for the whole contract.
|
||||
*/
|
||||
bool private rentrancy_lock = false;
|
||||
|
||||
@ -22,13 +22,10 @@ contract ReentrancyGuard {
|
||||
* wrapper marked as `nonReentrant`.
|
||||
*/
|
||||
modifier nonReentrant() {
|
||||
if(rentrancy_lock == false) {
|
||||
rentrancy_lock = true;
|
||||
_;
|
||||
rentrancy_lock = false;
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
require(!rentrancy_lock);
|
||||
rentrancy_lock = true;
|
||||
_;
|
||||
rentrancy_lock = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
* Math operations with safety checks
|
||||
*/
|
||||
library SafeMath {
|
||||
function mul(uint a, uint b) internal returns (uint) {
|
||||
uint c = a * b;
|
||||
assert(a == 0 || c / a == b);
|
||||
return c;
|
||||
}
|
||||
|
||||
function div(uint a, uint b) internal returns (uint) {
|
||||
// assert(b > 0); // Solidity automatically throws when dividing by 0
|
||||
uint c = a / b;
|
||||
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
|
||||
return c;
|
||||
}
|
||||
|
||||
function sub(uint a, uint b) internal returns (uint) {
|
||||
assert(b <= a);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function add(uint a, uint b) internal returns (uint) {
|
||||
uint c = a + 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
contracts/crowdsale/CappedCrowdsale.sol
Normal file
34
contracts/crowdsale/CappedCrowdsale.sol
Normal file
@ -0,0 +1,34 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import '../math/SafeMath.sol';
|
||||
import './Crowdsale.sol';
|
||||
|
||||
/**
|
||||
* @title CappedCrowdsale
|
||||
* @dev Extension of Crowdsale with a max amount of funds raised
|
||||
*/
|
||||
contract CappedCrowdsale is Crowdsale {
|
||||
using SafeMath for uint256;
|
||||
|
||||
uint256 public cap;
|
||||
|
||||
function CappedCrowdsale(uint256 _cap) {
|
||||
require(_cap > 0);
|
||||
cap = _cap;
|
||||
}
|
||||
|
||||
// overriding Crowdsale#validPurchase to add extra cap logic
|
||||
// @return true if investors can buy at the moment
|
||||
function validPurchase() internal constant returns (bool) {
|
||||
bool withinCap = weiRaised.add(msg.value) <= cap;
|
||||
return super.validPurchase() && withinCap;
|
||||
}
|
||||
|
||||
// overriding Crowdsale#hasEnded to add cap logic
|
||||
// @return true if crowdsale event has ended
|
||||
function hasEnded() public constant returns (bool) {
|
||||
bool capReached = weiRaised >= cap;
|
||||
return super.hasEnded() || capReached;
|
||||
}
|
||||
|
||||
}
|
||||
106
contracts/crowdsale/Crowdsale.sol
Normal file
106
contracts/crowdsale/Crowdsale.sol
Normal file
@ -0,0 +1,106 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import '../token/MintableToken.sol';
|
||||
import '../math/SafeMath.sol';
|
||||
|
||||
/**
|
||||
* @title Crowdsale
|
||||
* @dev Crowdsale is a base contract for managing a token crowdsale.
|
||||
* Crowdsales have a start and end timestamps, where investors can make
|
||||
* token purchases and the crowdsale will assign them tokens based
|
||||
* on a token per ETH rate. Funds collected are forwarded to a wallet
|
||||
* as they arrive.
|
||||
*/
|
||||
contract Crowdsale {
|
||||
using SafeMath for uint256;
|
||||
|
||||
// The token being sold
|
||||
MintableToken public token;
|
||||
|
||||
// start and end timestamps where investments are allowed (both inclusive)
|
||||
uint256 public startTime;
|
||||
uint256 public endTime;
|
||||
|
||||
// address where funds are collected
|
||||
address public wallet;
|
||||
|
||||
// how many token units a buyer gets per wei
|
||||
uint256 public rate;
|
||||
|
||||
// amount of raised money in wei
|
||||
uint256 public weiRaised;
|
||||
|
||||
/**
|
||||
* event for token purchase logging
|
||||
* @param purchaser who paid for the tokens
|
||||
* @param beneficiary who got the tokens
|
||||
* @param value weis paid for purchase
|
||||
* @param amount amount of tokens purchased
|
||||
*/
|
||||
event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
|
||||
|
||||
|
||||
function Crowdsale(uint256 _startTime, uint256 _endTime, uint256 _rate, address _wallet) {
|
||||
require(_startTime >= now);
|
||||
require(_endTime >= _startTime);
|
||||
require(_rate > 0);
|
||||
require(_wallet != 0x0);
|
||||
|
||||
token = createTokenContract();
|
||||
startTime = _startTime;
|
||||
endTime = _endTime;
|
||||
rate = _rate;
|
||||
wallet = _wallet;
|
||||
}
|
||||
|
||||
// creates the token to be sold.
|
||||
// override this method to have crowdsale of a specific mintable token.
|
||||
function createTokenContract() internal returns (MintableToken) {
|
||||
return new MintableToken();
|
||||
}
|
||||
|
||||
|
||||
// fallback function can be used to buy tokens
|
||||
function () payable {
|
||||
buyTokens(msg.sender);
|
||||
}
|
||||
|
||||
// low level token purchase function
|
||||
function buyTokens(address beneficiary) public payable {
|
||||
require(beneficiary != 0x0);
|
||||
require(validPurchase());
|
||||
|
||||
uint256 weiAmount = msg.value;
|
||||
|
||||
// calculate token amount to be created
|
||||
uint256 tokens = weiAmount.mul(rate);
|
||||
|
||||
// update state
|
||||
weiRaised = weiRaised.add(weiAmount);
|
||||
|
||||
token.mint(beneficiary, tokens);
|
||||
TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
|
||||
|
||||
forwardFunds();
|
||||
}
|
||||
|
||||
// send ether to the fund collection wallet
|
||||
// override to create custom fund forwarding mechanisms
|
||||
function forwardFunds() internal {
|
||||
wallet.transfer(msg.value);
|
||||
}
|
||||
|
||||
// @return true if the transaction can buy tokens
|
||||
function validPurchase() internal constant returns (bool) {
|
||||
bool withinPeriod = now >= startTime && now <= endTime;
|
||||
bool nonZeroPurchase = msg.value != 0;
|
||||
return withinPeriod && nonZeroPurchase;
|
||||
}
|
||||
|
||||
// @return true if crowdsale event has ended
|
||||
function hasEnded() public constant returns (bool) {
|
||||
return now > endTime;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
40
contracts/crowdsale/FinalizableCrowdsale.sol
Normal file
40
contracts/crowdsale/FinalizableCrowdsale.sol
Normal file
@ -0,0 +1,40 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import '../math/SafeMath.sol';
|
||||
import '../ownership/Ownable.sol';
|
||||
import './Crowdsale.sol';
|
||||
|
||||
/**
|
||||
* @title FinalizableCrowdsale
|
||||
* @dev Extension of Crowdsale where an owner can do extra work
|
||||
* after finishing.
|
||||
*/
|
||||
contract FinalizableCrowdsale is Crowdsale, Ownable {
|
||||
using SafeMath for uint256;
|
||||
|
||||
bool public isFinalized = false;
|
||||
|
||||
event Finalized();
|
||||
|
||||
/**
|
||||
* @dev Must be called after crowdsale ends, to do some extra finalization
|
||||
* work. Calls the contract's finalization function.
|
||||
*/
|
||||
function finalize() onlyOwner public {
|
||||
require(!isFinalized);
|
||||
require(hasEnded());
|
||||
|
||||
finalization();
|
||||
Finalized();
|
||||
|
||||
isFinalized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Can be overridden to add finalization logic. The overriding function
|
||||
* should call super.finalization() to ensure the chain of finalization is
|
||||
* executed entirely.
|
||||
*/
|
||||
function finalization() internal {
|
||||
}
|
||||
}
|
||||
56
contracts/crowdsale/RefundVault.sol
Normal file
56
contracts/crowdsale/RefundVault.sol
Normal file
@ -0,0 +1,56 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import '../math/SafeMath.sol';
|
||||
import '../ownership/Ownable.sol';
|
||||
|
||||
/**
|
||||
* @title RefundVault
|
||||
* @dev This contract is used for storing funds while a crowdsale
|
||||
* is in progress. Supports refunding the money if crowdsale fails,
|
||||
* and forwarding it if crowdsale is successful.
|
||||
*/
|
||||
contract RefundVault is Ownable {
|
||||
using SafeMath for uint256;
|
||||
|
||||
enum State { Active, Refunding, Closed }
|
||||
|
||||
mapping (address => uint256) public deposited;
|
||||
address public wallet;
|
||||
State public state;
|
||||
|
||||
event Closed();
|
||||
event RefundsEnabled();
|
||||
event Refunded(address indexed beneficiary, uint256 weiAmount);
|
||||
|
||||
function RefundVault(address _wallet) {
|
||||
require(_wallet != 0x0);
|
||||
wallet = _wallet;
|
||||
state = State.Active;
|
||||
}
|
||||
|
||||
function deposit(address investor) onlyOwner public payable {
|
||||
require(state == State.Active);
|
||||
deposited[investor] = deposited[investor].add(msg.value);
|
||||
}
|
||||
|
||||
function close() onlyOwner public {
|
||||
require(state == State.Active);
|
||||
state = State.Closed;
|
||||
Closed();
|
||||
wallet.transfer(this.balance);
|
||||
}
|
||||
|
||||
function enableRefunds() onlyOwner public {
|
||||
require(state == State.Active);
|
||||
state = State.Refunding;
|
||||
RefundsEnabled();
|
||||
}
|
||||
|
||||
function refund(address investor) public {
|
||||
require(state == State.Refunding);
|
||||
uint256 depositedValue = deposited[investor];
|
||||
deposited[investor] = 0;
|
||||
investor.transfer(depositedValue);
|
||||
Refunded(investor, depositedValue);
|
||||
}
|
||||
}
|
||||
60
contracts/crowdsale/RefundableCrowdsale.sol
Normal file
60
contracts/crowdsale/RefundableCrowdsale.sol
Normal file
@ -0,0 +1,60 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import '../math/SafeMath.sol';
|
||||
import './FinalizableCrowdsale.sol';
|
||||
import './RefundVault.sol';
|
||||
|
||||
|
||||
/**
|
||||
* @title RefundableCrowdsale
|
||||
* @dev Extension of Crowdsale contract that adds a funding goal, and
|
||||
* the possibility of users getting a refund if goal is not met.
|
||||
* Uses a RefundVault as the crowdsale's vault.
|
||||
*/
|
||||
contract RefundableCrowdsale is FinalizableCrowdsale {
|
||||
using SafeMath for uint256;
|
||||
|
||||
// minimum amount of funds to be raised in weis
|
||||
uint256 public goal;
|
||||
|
||||
// refund vault used to hold funds while crowdsale is running
|
||||
RefundVault public vault;
|
||||
|
||||
function RefundableCrowdsale(uint256 _goal) {
|
||||
require(_goal > 0);
|
||||
vault = new RefundVault(wallet);
|
||||
goal = _goal;
|
||||
}
|
||||
|
||||
// We're overriding the fund forwarding from Crowdsale.
|
||||
// In addition to sending the funds, we want to call
|
||||
// the RefundVault deposit function
|
||||
function forwardFunds() internal {
|
||||
vault.deposit.value(msg.value)(msg.sender);
|
||||
}
|
||||
|
||||
// if crowdsale is unsuccessful, investors can claim refunds here
|
||||
function claimRefund() public {
|
||||
require(isFinalized);
|
||||
require(!goalReached());
|
||||
|
||||
vault.refund(msg.sender);
|
||||
}
|
||||
|
||||
// vault finalization task, called when owner calls finalize()
|
||||
function finalization() internal {
|
||||
if (goalReached()) {
|
||||
vault.close();
|
||||
} else {
|
||||
vault.enableRefunds();
|
||||
}
|
||||
|
||||
super.finalization();
|
||||
}
|
||||
|
||||
function goalReached() public constant returns (bool) {
|
||||
return weiRaised >= goal;
|
||||
}
|
||||
|
||||
}
|
||||
48
contracts/examples/SampleCrowdsale.sol
Normal file
48
contracts/examples/SampleCrowdsale.sol
Normal file
@ -0,0 +1,48 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "../crowdsale/CappedCrowdsale.sol";
|
||||
import "../crowdsale/RefundableCrowdsale.sol";
|
||||
import "../token/MintableToken.sol";
|
||||
|
||||
/**
|
||||
* @title SampleCrowdsaleToken
|
||||
* @dev Very simple ERC20 Token that can be minted.
|
||||
* It is meant to be used in a crowdsale contract.
|
||||
*/
|
||||
contract SampleCrowdsaleToken is MintableToken {
|
||||
|
||||
string public constant name = "Sample Crowdsale Token";
|
||||
string public constant symbol = "SCT";
|
||||
uint8 public constant decimals = 18;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @title SampleCrowdsale
|
||||
* @dev This is an example of a fully fledged crowdsale.
|
||||
* The way to add new features to a base crowdsale is by multiple inheritance.
|
||||
* In this example we are providing following extensions:
|
||||
* CappedCrowdsale - sets a max boundary for raised funds
|
||||
* RefundableCrowdsale - set a min goal to be reached and returns funds if it's not met
|
||||
*
|
||||
* After adding multiple features it's good practice to run integration tests
|
||||
* to ensure that subcontracts works together as intended.
|
||||
*/
|
||||
contract SampleCrowdsale is CappedCrowdsale, RefundableCrowdsale {
|
||||
|
||||
function SampleCrowdsale(uint256 _startTime, uint256 _endTime, uint256 _rate, uint256 _goal, uint256 _cap, address _wallet)
|
||||
CappedCrowdsale(_cap)
|
||||
FinalizableCrowdsale()
|
||||
RefundableCrowdsale(_goal)
|
||||
Crowdsale(_startTime, _endTime, _rate, _wallet)
|
||||
{
|
||||
//As goal needs to be met for a successful crowdsale
|
||||
//the value needs to less or equal than a cap which is limit for accepted funds
|
||||
require(_goal <= _cap);
|
||||
}
|
||||
|
||||
function createTokenContract() internal returns (MintableToken) {
|
||||
return new SampleCrowdsaleToken();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,24 +1,25 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import "./StandardToken.sol";
|
||||
import "../token/StandardToken.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title SimpleToken
|
||||
* @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
|
||||
* @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
|
||||
* Note they can later distribute these tokens as they wish using `transfer` and other
|
||||
* `StandardToken` functions.
|
||||
*/
|
||||
contract SimpleToken is StandardToken {
|
||||
|
||||
string public name = "SimpleToken";
|
||||
string public symbol = "SIM";
|
||||
uint public decimals = 18;
|
||||
uint public INITIAL_SUPPLY = 10000;
|
||||
string public constant name = "SimpleToken";
|
||||
string public constant symbol = "SIM";
|
||||
uint8 public constant decimals = 18;
|
||||
|
||||
uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** uint256(decimals));
|
||||
|
||||
/**
|
||||
* @dev Contructor that gives msg.sender all of existing tokens.
|
||||
* @dev Constructor that gives msg.sender all of existing tokens.
|
||||
*/
|
||||
function SimpleToken() {
|
||||
totalSupply = INITIAL_SUPPLY;
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import "../ownership/Ownable.sol";
|
||||
@ -10,14 +10,16 @@ import "../ownership/Ownable.sol";
|
||||
*/
|
||||
contract Destructible is Ownable {
|
||||
|
||||
function Destructible() payable { }
|
||||
|
||||
/**
|
||||
* @dev Transfers the current balance to the owner and terminates the contract.
|
||||
* @dev Transfers the current balance to the owner and terminates the contract.
|
||||
*/
|
||||
function destroy() onlyOwner {
|
||||
function destroy() onlyOwner public {
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
function destroyAndSend(address _recipient) onlyOwner {
|
||||
function destroyAndSend(address _recipient) onlyOwner public {
|
||||
selfdestruct(_recipient);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import '../ownership/Ownable.sol';
|
||||
@ -8,13 +8,13 @@ import '../ownership/Ownable.sol';
|
||||
* @dev This is a truffle contract, needed for truffle integration, not meant for use by Zeppelin users.
|
||||
*/
|
||||
contract Migrations is Ownable {
|
||||
uint public lastCompletedMigration;
|
||||
uint256 public lastCompletedMigration;
|
||||
|
||||
function setCompleted(uint completed) onlyOwner {
|
||||
function setCompleted(uint256 completed) onlyOwner public {
|
||||
lastCompletedMigration = completed;
|
||||
}
|
||||
|
||||
function upgrade(address newAddress) onlyOwner {
|
||||
function upgrade(address newAddress) onlyOwner public {
|
||||
Migrations upgraded = Migrations(newAddress);
|
||||
upgraded.setCompleted(lastCompletedMigration);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import "../ownership/Ownable.sol";
|
||||
@ -16,36 +16,34 @@ contract Pausable is Ownable {
|
||||
|
||||
|
||||
/**
|
||||
* @dev modifier to allow actions only when the contract IS paused
|
||||
* @dev Modifier to make a function callable only when the contract is not paused.
|
||||
*/
|
||||
modifier whenNotPaused() {
|
||||
if (paused) throw;
|
||||
require(!paused);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev modifier to allow actions only when the contract IS NOT paused
|
||||
* @dev Modifier to make a function callable only when the contract is paused.
|
||||
*/
|
||||
modifier whenPaused {
|
||||
if (!paused) throw;
|
||||
modifier whenPaused() {
|
||||
require(paused);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev called by the owner to pause, triggers stopped state
|
||||
*/
|
||||
function pause() onlyOwner whenNotPaused returns (bool) {
|
||||
function pause() onlyOwner whenNotPaused public {
|
||||
paused = true;
|
||||
Pause();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev called by the owner to unpause, returns to normal state
|
||||
*/
|
||||
function unpause() onlyOwner whenPaused returns (bool) {
|
||||
function unpause() onlyOwner whenPaused public {
|
||||
paused = false;
|
||||
Unpause();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import "../ownership/Ownable.sol";
|
||||
import "../token/ERC20Basic.sol";
|
||||
|
||||
/**
|
||||
/**
|
||||
* @title TokenDestructible:
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @dev Base contract that can be destroyed by owner. All funds in contract including
|
||||
@ -12,17 +12,19 @@ import "../token/ERC20Basic.sol";
|
||||
*/
|
||||
contract TokenDestructible is Ownable {
|
||||
|
||||
/**
|
||||
function TokenDestructible() payable { }
|
||||
|
||||
/**
|
||||
* @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 trust.
|
||||
*/
|
||||
function destroy(address[] tokens) onlyOwner {
|
||||
function destroy(address[] tokens) onlyOwner public {
|
||||
|
||||
// Transfer tokens to owner
|
||||
for(uint i = 0; i < tokens.length; i++) {
|
||||
for(uint256 i = 0; i < tokens.length; i++) {
|
||||
ERC20Basic token = ERC20Basic(tokens[i]);
|
||||
uint256 balance = token.balanceOf(this);
|
||||
token.transfer(owner, balance);
|
||||
|
||||
24
contracts/math/Math.sol
Normal file
24
contracts/math/Math.sol
Normal file
@ -0,0 +1,24 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
/**
|
||||
* @title Math
|
||||
* @dev Assorted math operations
|
||||
*/
|
||||
|
||||
library Math {
|
||||
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;
|
||||
}
|
||||
}
|
||||
32
contracts/math/SafeMath.sol
Normal file
32
contracts/math/SafeMath.sol
Normal file
@ -0,0 +1,32 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
/**
|
||||
* @title SafeMath
|
||||
* @dev Math operations with safety checks that throw on error
|
||||
*/
|
||||
library SafeMath {
|
||||
function mul(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
uint256 c = a * b;
|
||||
assert(a == 0 || c / a == b);
|
||||
return c;
|
||||
}
|
||||
|
||||
function div(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
// assert(b > 0); // Solidity automatically throws when dividing by 0
|
||||
uint256 c = a / b;
|
||||
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
|
||||
return c;
|
||||
}
|
||||
|
||||
function sub(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
assert(b <= a);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function add(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
uint256 c = a + b;
|
||||
assert(c >= a);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
25
contracts/ownership/CanReclaimToken.sol
Normal file
25
contracts/ownership/CanReclaimToken.sol
Normal file
@ -0,0 +1,25 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./Ownable.sol";
|
||||
import "../token/ERC20Basic.sol";
|
||||
import "../token/SafeERC20.sol";
|
||||
|
||||
/**
|
||||
* @title Contracts that should be able to recover tokens
|
||||
* @author SylTi
|
||||
* @dev This allow a contract to recover any ERC20 token received in a contract by transferring the balance to the contract owner.
|
||||
* This will prevent any accidental loss of tokens.
|
||||
*/
|
||||
contract CanReclaimToken is Ownable {
|
||||
using SafeERC20 for ERC20Basic;
|
||||
|
||||
/**
|
||||
* @dev Reclaim all ERC20Basic compatible tokens
|
||||
* @param token ERC20Basic The address of the token contract
|
||||
*/
|
||||
function reclaimToken(ERC20Basic token) external onlyOwner {
|
||||
uint256 balance = token.balanceOf(this);
|
||||
token.safeTransfer(owner, balance);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './Ownable.sol';
|
||||
@ -6,34 +6,33 @@ import './Ownable.sol';
|
||||
|
||||
/**
|
||||
* @title Claimable
|
||||
* @dev Extension for the Ownable contract, where the ownership needs to be claimed.
|
||||
* @dev Extension for the Ownable contract, where the ownership needs to be claimed.
|
||||
* This allows the new owner to accept the transfer.
|
||||
*/
|
||||
contract Claimable is Ownable {
|
||||
address public pendingOwner;
|
||||
|
||||
/**
|
||||
* @dev Modifier throws if called by any account other than the pendingOwner.
|
||||
* @dev Modifier throws if called by any account other than the pendingOwner.
|
||||
*/
|
||||
modifier onlyPendingOwner() {
|
||||
if (msg.sender != pendingOwner) {
|
||||
throw;
|
||||
}
|
||||
require(msg.sender == pendingOwner);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the current owner to set the pendingOwner address.
|
||||
* @param newOwner The address to transfer ownership to.
|
||||
* @dev Allows the current owner to set the pendingOwner address.
|
||||
* @param newOwner The address to transfer ownership to.
|
||||
*/
|
||||
function transferOwnership(address newOwner) onlyOwner {
|
||||
function transferOwnership(address newOwner) onlyOwner public {
|
||||
pendingOwner = newOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows the pendingOwner address to finalize the transfer.
|
||||
*/
|
||||
function claimOwnership() onlyPendingOwner {
|
||||
function claimOwnership() onlyPendingOwner public {
|
||||
OwnershipTransferred(owner, pendingOwner);
|
||||
owner = pendingOwner;
|
||||
pendingOwner = 0x0;
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import './Ownable.sol';
|
||||
|
||||
/**
|
||||
* @title Contactable token
|
||||
* @dev Basic version of a contactable contract, allowing the owner to provide a string with their
|
||||
* @dev Basic version of a contactable contract, allowing the owner to provide a string with their
|
||||
* contact information.
|
||||
*/
|
||||
contract Contactable is Ownable{
|
||||
@ -15,7 +15,7 @@ contract Contactable is Ownable{
|
||||
* @dev Allows the owner to set a string with their contact information.
|
||||
* @param info The contact information to attach to the contract.
|
||||
*/
|
||||
function setContactInformation(string info) onlyOwner{
|
||||
function setContactInformation(string info) onlyOwner public {
|
||||
contactInformation = info;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './Claimable.sol';
|
||||
@ -11,30 +11,29 @@ import './Claimable.sol';
|
||||
*/
|
||||
contract DelayedClaimable is Claimable {
|
||||
|
||||
uint public end;
|
||||
uint public start;
|
||||
uint256 public end;
|
||||
uint256 public start;
|
||||
|
||||
/**
|
||||
* @dev Used to specify the time period during which a pending
|
||||
* owner can claim ownership.
|
||||
* @dev Used to specify the time period during which a pending
|
||||
* owner can claim ownership.
|
||||
* @param _start The earliest time ownership can be claimed.
|
||||
* @param _end The latest time ownership can be claimed.
|
||||
* @param _end The latest time ownership can be claimed.
|
||||
*/
|
||||
function setLimits(uint _start, uint _end) onlyOwner {
|
||||
if (_start > _end)
|
||||
throw;
|
||||
function setLimits(uint256 _start, uint256 _end) onlyOwner public {
|
||||
require(_start <= _end);
|
||||
end = _end;
|
||||
start = _start;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Allows the pendingOwner address to finalize the transfer, as long as it is called within
|
||||
* the specified start and end time.
|
||||
* @dev Allows the pendingOwner address to finalize the transfer, as long as it is called within
|
||||
* the specified start and end time.
|
||||
*/
|
||||
function claimOwnership() onlyPendingOwner {
|
||||
if ((block.number > end) || (block.number < start))
|
||||
throw;
|
||||
function claimOwnership() onlyPendingOwner public {
|
||||
require((block.number <= end) && (block.number >= start));
|
||||
OwnershipTransferred(owner, pendingOwner);
|
||||
owner = pendingOwner;
|
||||
pendingOwner = 0x0;
|
||||
end = 0;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./Ownable.sol";
|
||||
|
||||
/**
|
||||
/**
|
||||
* @title Contracts that should not own Contracts
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @dev Should contracts (anything Ownable) end up being owned by this contract, it allows the owner
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./Ownable.sol";
|
||||
|
||||
/**
|
||||
/**
|
||||
* @title Contracts that should not own Ether
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @dev This tries to block incoming ether to prevent accidental loss of Ether. Should Ether end up
|
||||
@ -16,15 +16,13 @@ contract HasNoEther is Ownable {
|
||||
|
||||
/**
|
||||
* @dev Constructor that rejects incoming Ether
|
||||
* @dev The `payable` flag 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
|
||||
* @dev The `payable` flag 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;
|
||||
}
|
||||
require(msg.value == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,8 +35,6 @@ contract HasNoEther is Ownable {
|
||||
* @dev Transfer all Ether held by the contract to the owner.
|
||||
*/
|
||||
function reclaimEther() external onlyOwner {
|
||||
if(!owner.send(this.balance)) {
|
||||
throw;
|
||||
}
|
||||
assert(owner.send(this.balance));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,34 +1,24 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./Ownable.sol";
|
||||
import "../token/ERC20Basic.sol";
|
||||
import "./CanReclaimToken.sol";
|
||||
|
||||
/**
|
||||
/**
|
||||
* @title Contracts that should not own Tokens
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @dev 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 {
|
||||
contract HasNoTokens is CanReclaimToken {
|
||||
|
||||
/**
|
||||
/**
|
||||
* @dev Reject all ERC23 compatible tokens
|
||||
* @param from_ address The address that is transferring the tokens
|
||||
* @param value_ Uint the amount of the specified token
|
||||
* @param value_ uint256 the amount of the specified token
|
||||
* @param data_ Bytes The data passed from the caller.
|
||||
*/
|
||||
function tokenFallback(address from_, uint value_, bytes data_) external {
|
||||
throw;
|
||||
function tokenFallback(address from_, uint256 value_, bytes data_) external {
|
||||
revert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Reclaim all ERC20Basic compatible tokens
|
||||
* @param tokenAddr address The address of the token contract
|
||||
*/
|
||||
function reclaimToken(address tokenAddr) external onlyOwner {
|
||||
ERC20Basic tokenInst = ERC20Basic(tokenAddr);
|
||||
uint256 balance = tokenInst.balanceOf(this);
|
||||
tokenInst.transfer(owner, balance);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
* @title Multisig
|
||||
* @dev Interface contract for multisig proxy contracts; see below for docs.
|
||||
*/
|
||||
contract Multisig {
|
||||
// EVENTS
|
||||
|
||||
// logged events:
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
// TODO: document
|
||||
function changeOwner(address _from, address _to) external;
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
|
||||
function confirm(bytes32 _h) returns (bool);
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
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>
|
||||
* @dev Solves a class of errors where a contract accidentally becomes owner of Ether, Tokens or
|
||||
* @dev 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,16 +1,19 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
/**
|
||||
* @title Ownable
|
||||
* @dev The Ownable contract has an owner address, and provides basic authorization control
|
||||
* functions, this simplifies the implementation of "user permissions".
|
||||
* @dev The Ownable contract has an owner address, and provides basic authorization control
|
||||
* functions, this simplifies the implementation of "user permissions".
|
||||
*/
|
||||
contract Ownable {
|
||||
address public owner;
|
||||
|
||||
|
||||
/**
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
|
||||
/**
|
||||
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
|
||||
* account.
|
||||
*/
|
||||
@ -20,24 +23,22 @@ contract Ownable {
|
||||
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
if (msg.sender != owner) {
|
||||
throw;
|
||||
}
|
||||
require(msg.sender == owner);
|
||||
_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Allows the current owner to transfer control of the contract to a newOwner.
|
||||
* @param newOwner The address to transfer ownership to.
|
||||
* @param newOwner The address to transfer ownership to.
|
||||
*/
|
||||
function transferOwnership(address newOwner) onlyOwner {
|
||||
if (newOwner != address(0)) {
|
||||
owner = newOwner;
|
||||
}
|
||||
function transferOwnership(address newOwner) onlyOwner public {
|
||||
require(newOwner != address(0));
|
||||
OwnershipTransferred(owner, newOwner);
|
||||
owner = newOwner;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,189 +0,0 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
* @title Shareable
|
||||
* @dev inheritable "property" contract that enables methods to be protected by requiring the
|
||||
* acquiescence of either a single, or, crucially, each of a number of, designated owners.
|
||||
* @dev Usage: 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 {
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
}
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public required;
|
||||
|
||||
// list of owners
|
||||
address[256] owners;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(address => uint) ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) pendings;
|
||||
bytes32[] pendingsIndex;
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyOwner {
|
||||
if (!isOwner(msg.sender)) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Modifier for multisig functions.
|
||||
* @param _operation 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)) {
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Constructor is given the number of sigs required to do protected "onlymanyowners"
|
||||
* transactions as well as the selection of addresses capable of confirming them.
|
||||
* @param _owners A list of owners.
|
||||
* @param _required The amount required for a transaction to be approved.
|
||||
*/
|
||||
function Shareable(address[] _owners, uint _required) {
|
||||
owners[1] = msg.sender;
|
||||
ownerIndex[msg.sender] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i) {
|
||||
owners[2 + i] = _owners[i];
|
||||
ownerIndex[_owners[i]] = 2 + i;
|
||||
}
|
||||
required = _required;
|
||||
if (required > owners.length) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revokes a prior confirmation of the given operation.
|
||||
* @param _operation A string identifying the operation.
|
||||
*/
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint index = ownerIndex[msg.sender];
|
||||
// make sure they're an owner
|
||||
if (index == 0) {
|
||||
return;
|
||||
}
|
||||
uint ownerIndexBit = 2**index;
|
||||
var pending = pendings[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
* @param ownerIndex Uint The index of the owner
|
||||
* @return The address of the owner
|
||||
*/
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Checks if given address is an owner.
|
||||
* @param _addr address The address which you want to check.
|
||||
* @return True if the address is an owner and fase otherwise.
|
||||
*/
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return ownerIndex[_addr] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to check is specific owner has already confirme the operation.
|
||||
* @param _operation The operation identifier.
|
||||
* @param _owner The owner address.
|
||||
* @return True if the owner has confirmed and false otherwise.
|
||||
*/
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
var pending = pendings[_operation];
|
||||
uint index = ownerIndex[_owner];
|
||||
|
||||
// make sure they're an owner
|
||||
if (index == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**index;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Confirm and operation and checks if it's already executable.
|
||||
* @param _operation The operation identifier.
|
||||
* @return Returns true when operation can be executed.
|
||||
*/
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint index = ownerIndex[msg.sender];
|
||||
// make sure they're an owner
|
||||
if (index == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
var pending = pendings[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = pendingsIndex.length++;
|
||||
pendingsIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**index;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Clear the pending list.
|
||||
*/
|
||||
function clearPending() internal {
|
||||
uint length = pendingsIndex.length;
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
if (pendingsIndex[i] != 0) {
|
||||
delete pendings[pendingsIndex[i]];
|
||||
}
|
||||
}
|
||||
delete pendingsIndex;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import '../SafeMath.sol';
|
||||
import '../math/SafeMath.sol';
|
||||
|
||||
|
||||
/**
|
||||
@ -10,17 +10,17 @@ import '../SafeMath.sol';
|
||||
* contract and use asyncSend instead of send.
|
||||
*/
|
||||
contract PullPayment {
|
||||
using SafeMath for uint;
|
||||
using SafeMath for uint256;
|
||||
|
||||
mapping(address => uint) public payments;
|
||||
uint public totalPayments;
|
||||
mapping(address => uint256) public payments;
|
||||
uint256 public totalPayments;
|
||||
|
||||
/**
|
||||
* @dev Called by the payer to store the sent amount as credit to be pulled.
|
||||
* @param dest The destination address of the funds.
|
||||
* @param amount The amount to transfer.
|
||||
*/
|
||||
function asyncSend(address dest, uint amount) internal {
|
||||
function asyncSend(address dest, uint256 amount) internal {
|
||||
payments[dest] = payments[dest].add(amount);
|
||||
totalPayments = totalPayments.add(amount);
|
||||
}
|
||||
@ -28,23 +28,16 @@ contract PullPayment {
|
||||
/**
|
||||
* @dev withdraw accumulated balance, called by payee.
|
||||
*/
|
||||
function withdrawPayments() {
|
||||
function withdrawPayments() public {
|
||||
address payee = msg.sender;
|
||||
uint payment = payments[payee];
|
||||
uint256 payment = payments[payee];
|
||||
|
||||
if (payment == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
if (this.balance < payment) {
|
||||
throw;
|
||||
}
|
||||
require(payment != 0);
|
||||
require(this.balance >= payment);
|
||||
|
||||
totalPayments = totalPayments.sub(payment);
|
||||
payments[payee] = 0;
|
||||
|
||||
if (!payee.send(payment)) {
|
||||
throw;
|
||||
}
|
||||
assert(payee.send(payment));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,46 +1,40 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './ERC20Basic.sol';
|
||||
import '../SafeMath.sol';
|
||||
import '../math/SafeMath.sol';
|
||||
|
||||
|
||||
/**
|
||||
* @title Basic token
|
||||
* @dev Basic version of StandardToken, with no allowances.
|
||||
* @dev Basic version of StandardToken, with no allowances.
|
||||
*/
|
||||
contract BasicToken is ERC20Basic {
|
||||
using SafeMath for uint;
|
||||
using SafeMath for uint256;
|
||||
|
||||
mapping(address => uint) balances;
|
||||
|
||||
/**
|
||||
* @dev Fix for the ERC20 short address attack.
|
||||
*/
|
||||
modifier onlyPayloadSize(uint size) {
|
||||
if(msg.data.length < size + 4) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
mapping(address => uint256) balances;
|
||||
|
||||
/**
|
||||
* @dev transfer token for a specified address
|
||||
* @param _to The address to transfer to.
|
||||
* @param _value The amount to be transferred.
|
||||
*/
|
||||
function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) {
|
||||
function transfer(address _to, uint256 _value) public returns (bool) {
|
||||
require(_to != address(0));
|
||||
|
||||
// SafeMath.sub will throw if there is not enough balance.
|
||||
balances[msg.sender] = balances[msg.sender].sub(_value);
|
||||
balances[_to] = balances[_to].add(_value);
|
||||
Transfer(msg.sender, _to, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the balance of the specified address.
|
||||
* @param _owner The address to query the the balance of.
|
||||
* @return An uint representing the amount owned by the passed address.
|
||||
* @param _owner The address to query the the balance of.
|
||||
* @return An uint256 representing the amount owned by the passed address.
|
||||
*/
|
||||
function balanceOf(address _owner) constant returns (uint balance) {
|
||||
function balanceOf(address _owner) public constant returns (uint256 balance) {
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
|
||||
25
contracts/token/BurnableToken.sol
Normal file
25
contracts/token/BurnableToken.sol
Normal file
@ -0,0 +1,25 @@
|
||||
pragma solidity ^0.4.13;
|
||||
|
||||
import './StandardToken.sol';
|
||||
|
||||
/**
|
||||
* @title Burnable Token
|
||||
* @dev Token that can be irreversibly burned (destroyed).
|
||||
*/
|
||||
contract BurnableToken is StandardToken {
|
||||
|
||||
event Burn(address indexed burner, uint256 value);
|
||||
|
||||
/**
|
||||
* @dev Burns a specific amount of tokens.
|
||||
* @param _value The amount of token to be burned.
|
||||
*/
|
||||
function burn(uint256 _value) public {
|
||||
require(_value > 0);
|
||||
|
||||
address burner = msg.sender;
|
||||
balances[burner] = balances[burner].sub(_value);
|
||||
totalSupply = totalSupply.sub(_value);
|
||||
Burn(burner, _value);
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./StandardToken.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title CrowdsaleToken
|
||||
*
|
||||
* @dev Simple ERC20 Token example, with crowdsale token creation
|
||||
* @dev 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 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 public constant PRICE = 500;
|
||||
|
||||
/**
|
||||
* @dev Fallback function which receives ether and sends the appropriate number of tokens to the
|
||||
* msg.sender.
|
||||
*/
|
||||
function () payable {
|
||||
createTokens(msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Creates tokens and send to the specified address.
|
||||
* @param recipient The address which will recieve the new tokens.
|
||||
*/
|
||||
function createTokens(address recipient) payable {
|
||||
if (msg.value == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
uint tokens = msg.value.mul(getPrice());
|
||||
totalSupply = totalSupply.add(tokens);
|
||||
|
||||
balances[recipient] = balances[recipient].add(tokens);
|
||||
|
||||
if (!multisig.send(msg.value)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev replace this with any other price function
|
||||
* @return The price per unit of token.
|
||||
*/
|
||||
function getPrice() constant returns (uint result) {
|
||||
return PRICE;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './ERC20Basic.sol';
|
||||
@ -9,8 +9,8 @@ import './ERC20Basic.sol';
|
||||
* @dev see https://github.com/ethereum/EIPs/issues/20
|
||||
*/
|
||||
contract ERC20 is ERC20Basic {
|
||||
function allowance(address owner, address spender) constant returns (uint);
|
||||
function transferFrom(address from, address to, uint value);
|
||||
function approve(address spender, uint value);
|
||||
event Approval(address indexed owner, address indexed spender, uint value);
|
||||
function allowance(address owner, address spender) public constant returns (uint256);
|
||||
function transferFrom(address from, address to, uint256 value) public returns (bool);
|
||||
function approve(address spender, uint256 value) public returns (bool);
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC20Basic
|
||||
* @dev Simpler version of ERC20 interface
|
||||
* @dev see https://github.com/ethereum/EIPs/issues/20
|
||||
* @dev see https://github.com/ethereum/EIPs/issues/179
|
||||
*/
|
||||
contract ERC20Basic {
|
||||
uint public totalSupply;
|
||||
function balanceOf(address who) constant returns (uint);
|
||||
function transfer(address to, uint value);
|
||||
event Transfer(address indexed from, address indexed to, uint value);
|
||||
uint256 public totalSupply;
|
||||
function balanceOf(address who) public constant returns (uint256);
|
||||
function transfer(address to, uint256 value) public returns (bool);
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./ERC20.sol";
|
||||
|
||||
/**
|
||||
* @title LimitedTransferToken
|
||||
* @dev 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.
|
||||
* @dev 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.
|
||||
* LimitedTransferToken has been designed to allow for different limiting factors,
|
||||
* this can be achieved by recursively calling super.transferableTokens() until the base class is
|
||||
* 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));
|
||||
@ -22,36 +22,36 @@ contract LimitedTransferToken is ERC20 {
|
||||
/**
|
||||
* @dev Checks whether it can transfer or otherwise throws.
|
||||
*/
|
||||
modifier canTransfer(address _sender, uint _value) {
|
||||
if (_value > transferableTokens(_sender, uint64(now))) throw;
|
||||
modifier canTransfer(address _sender, uint256 _value) {
|
||||
require(_value <= transferableTokens(_sender, uint64(now)));
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Checks modifier and allows transfer if tokens are not locked.
|
||||
* @param _to The address that will recieve the tokens.
|
||||
* @param _to The address that will receive the tokens.
|
||||
* @param _value The amount of tokens to be transferred.
|
||||
*/
|
||||
function transfer(address _to, uint _value) canTransfer(msg.sender, _value) {
|
||||
return super.transfer(_to, _value);
|
||||
function transfer(address _to, uint256 _value) canTransfer(msg.sender, _value) public returns (bool) {
|
||||
return super.transfer(_to, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Checks modifier and allows transfer if tokens are not locked.
|
||||
* @param _from The address that will send the tokens.
|
||||
* @param _to The address that will recieve the tokens.
|
||||
* @param _to The address that will receive the tokens.
|
||||
* @param _value The amount of tokens to be transferred.
|
||||
*/
|
||||
function transferFrom(address _from, address _to, uint _value) canTransfer(_from, _value) {
|
||||
return super.transferFrom(_from, _to, _value);
|
||||
function transferFrom(address _from, address _to, uint256 _value) canTransfer(_from, _value) public returns (bool) {
|
||||
return super.transferFrom(_from, _to, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Default transferable tokens function returns all tokens for a holder (no limit).
|
||||
* @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the
|
||||
* @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the
|
||||
* specific logic for limiting token transferability for a holder over time.
|
||||
*/
|
||||
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
|
||||
function transferableTokens(address holder, uint64 time) public constant returns (uint256) {
|
||||
return balanceOf(holder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './StandardToken.sol';
|
||||
@ -14,28 +14,28 @@ import '../ownership/Ownable.sol';
|
||||
*/
|
||||
|
||||
contract MintableToken is StandardToken, Ownable {
|
||||
event Mint(address indexed to, uint value);
|
||||
event Mint(address indexed to, uint256 amount);
|
||||
event MintFinished();
|
||||
|
||||
bool public mintingFinished = false;
|
||||
uint public totalSupply = 0;
|
||||
|
||||
|
||||
modifier canMint() {
|
||||
if(mintingFinished) throw;
|
||||
require(!mintingFinished);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to mint tokens
|
||||
* @param _to The address that will recieve the minted tokens.
|
||||
* @param _to The address that will receive the minted tokens.
|
||||
* @param _amount The amount of tokens to mint.
|
||||
* @return A boolean that indicates if the operation was successful.
|
||||
*/
|
||||
function mint(address _to, uint _amount) onlyOwner canMint returns (bool) {
|
||||
function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
|
||||
totalSupply = totalSupply.add(_amount);
|
||||
balances[_to] = balances[_to].add(_amount);
|
||||
Mint(_to, _amount);
|
||||
Transfer(0x0, _to, _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ contract MintableToken is StandardToken, Ownable {
|
||||
* @dev Function to stop minting new tokens.
|
||||
* @return True if the operation was successful.
|
||||
*/
|
||||
function finishMinting() onlyOwner returns (bool) {
|
||||
function finishMinting() onlyOwner public returns (bool) {
|
||||
mintingFinished = true;
|
||||
MintFinished();
|
||||
return true;
|
||||
|
||||
@ -1,25 +1,33 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import './StandardToken.sol';
|
||||
import '../lifecycle/Pausable.sol';
|
||||
|
||||
/**
|
||||
* Pausable token
|
||||
* @title 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
|
||||
* @dev StandardToken modified with pausable transfers.
|
||||
**/
|
||||
|
||||
contract PausableToken is Pausable, StandardToken {
|
||||
contract PausableToken is StandardToken, Pausable {
|
||||
|
||||
function transfer(address _to, uint _value) whenNotPaused {
|
||||
function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
|
||||
return super.transfer(_to, _value);
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint _value) whenNotPaused {
|
||||
function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) {
|
||||
return super.transferFrom(_from, _to, _value);
|
||||
}
|
||||
}
|
||||
|
||||
function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) {
|
||||
return super.approve(_spender, _value);
|
||||
}
|
||||
|
||||
function increaseApproval(address _spender, uint _addedValue) public whenNotPaused returns (bool success) {
|
||||
return super.increaseApproval(_spender, _addedValue);
|
||||
}
|
||||
|
||||
function decreaseApproval(address _spender, uint _subtractedValue) public whenNotPaused returns (bool success) {
|
||||
return super.decreaseApproval(_spender, _subtractedValue);
|
||||
}
|
||||
}
|
||||
|
||||
24
contracts/token/SafeERC20.sol
Normal file
24
contracts/token/SafeERC20.sol
Normal file
@ -0,0 +1,24 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import './ERC20Basic.sol';
|
||||
import './ERC20.sol';
|
||||
|
||||
/**
|
||||
* @title SafeERC20
|
||||
* @dev Wrappers around ERC20 operations that throw on failure.
|
||||
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
|
||||
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
|
||||
*/
|
||||
library SafeERC20 {
|
||||
function safeTransfer(ERC20Basic token, address to, uint256 value) internal {
|
||||
assert(token.transfer(to, value));
|
||||
}
|
||||
|
||||
function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
|
||||
assert(token.transferFrom(from, to, value));
|
||||
}
|
||||
|
||||
function safeApprove(ERC20 token, address spender, uint256 value) internal {
|
||||
assert(token.approve(spender, value));
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './BasicToken.sol';
|
||||
@ -8,58 +8,85 @@ import './ERC20.sol';
|
||||
/**
|
||||
* @title Standard ERC20 token
|
||||
*
|
||||
* @dev Implemantation of the basic standart token.
|
||||
* @dev Implementation of the basic standard token.
|
||||
* @dev https://github.com/ethereum/EIPs/issues/20
|
||||
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
|
||||
*/
|
||||
contract StandardToken is BasicToken, ERC20 {
|
||||
contract StandardToken is ERC20, BasicToken {
|
||||
|
||||
mapping (address => mapping (address => uint)) allowed;
|
||||
mapping (address => mapping (address => uint256)) allowed;
|
||||
|
||||
|
||||
/**
|
||||
* @dev Transfer tokens from one address to another
|
||||
* @param _from address The address which you want to send tokens from
|
||||
* @param _to address The address which you want to transfer to
|
||||
* @param _value uint the amout of tokens to be transfered
|
||||
* @param _value uint256 the amount of tokens to be transferred
|
||||
*/
|
||||
function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) {
|
||||
var _allowance = allowed[_from][msg.sender];
|
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
|
||||
require(_to != address(0));
|
||||
|
||||
uint256 _allowance = allowed[_from][msg.sender];
|
||||
|
||||
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
|
||||
// if (_value > _allowance) throw;
|
||||
// require (_value <= _allowance);
|
||||
|
||||
balances[_to] = balances[_to].add(_value);
|
||||
balances[_from] = balances[_from].sub(_value);
|
||||
balances[_to] = balances[_to].add(_value);
|
||||
allowed[_from][msg.sender] = _allowance.sub(_value);
|
||||
Transfer(_from, _to, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Aprove the passed address to spend the specified amount of tokens on beahlf of msg.sender.
|
||||
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
||||
*
|
||||
* Beware that changing an allowance with this method brings the risk that someone may use both the old
|
||||
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
|
||||
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
|
||||
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
* @param _spender The address which will spend the funds.
|
||||
* @param _value The amount of tokens to be spent.
|
||||
*/
|
||||
function approve(address _spender, uint _value) {
|
||||
|
||||
// To change the approve amount you first have to reduce the addresses`
|
||||
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
||||
// already 0 to mitigate the race condition described here:
|
||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;
|
||||
|
||||
function approve(address _spender, uint256 _value) public returns (bool) {
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to check the amount of tokens than an owner allowed to a spender.
|
||||
* @dev Function to check the amount of tokens that an owner allowed to a spender.
|
||||
* @param _owner address The address which owns the funds.
|
||||
* @param _spender address The address which will spend the funds.
|
||||
* @return A uint specifing the amount of tokens still avaible for the spender.
|
||||
* @return A uint256 specifying the amount of tokens still available for the spender.
|
||||
*/
|
||||
function allowance(address _owner, address _spender) constant returns (uint remaining) {
|
||||
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
/**
|
||||
* approve should be called when allowed[_spender] == 0. To increment
|
||||
* allowed value is better to use this function to avoid 2 calls (and wait until
|
||||
* the first transaction is mined)
|
||||
* From MonolithDAO Token.sol
|
||||
*/
|
||||
function increaseApproval (address _spender, uint _addedValue)
|
||||
returns (bool success) {
|
||||
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
|
||||
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
|
||||
return true;
|
||||
}
|
||||
|
||||
function decreaseApproval (address _spender, uint _subtractedValue)
|
||||
returns (bool success) {
|
||||
uint oldValue = allowed[msg.sender][_spender];
|
||||
if (_subtractedValue > oldValue) {
|
||||
allowed[msg.sender][_spender] = 0;
|
||||
} else {
|
||||
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
|
||||
}
|
||||
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
51
contracts/token/TokenTimelock.sol
Normal file
51
contracts/token/TokenTimelock.sol
Normal file
@ -0,0 +1,51 @@
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import './ERC20Basic.sol';
|
||||
import "../token/SafeERC20.sol";
|
||||
|
||||
/**
|
||||
* @title TokenTimelock
|
||||
* @dev TokenTimelock is a token holder contract that will allow a
|
||||
* beneficiary to extract the tokens after a given release time
|
||||
*/
|
||||
contract TokenTimelock {
|
||||
using SafeERC20 for ERC20Basic;
|
||||
|
||||
// ERC20 basic token contract being held
|
||||
ERC20Basic public token;
|
||||
|
||||
// beneficiary of tokens after they are released
|
||||
address public beneficiary;
|
||||
|
||||
// timestamp when token release is enabled
|
||||
uint64 public releaseTime;
|
||||
|
||||
function TokenTimelock(ERC20Basic _token, address _beneficiary, uint64 _releaseTime) {
|
||||
require(_releaseTime > now);
|
||||
token = _token;
|
||||
beneficiary = _beneficiary;
|
||||
releaseTime = _releaseTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Transfers tokens held by timelock to beneficiary.
|
||||
* Deprecated: please use TokenTimelock#release instead.
|
||||
*/
|
||||
function claim() public {
|
||||
require(msg.sender == beneficiary);
|
||||
release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Transfers tokens held by timelock to beneficiary.
|
||||
*/
|
||||
function release() public {
|
||||
require(now >= releaseTime);
|
||||
|
||||
uint256 amount = token.balanceOf(this);
|
||||
require(amount > 0);
|
||||
|
||||
token.safeTransfer(beneficiary, amount);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
pragma solidity ^0.4.8;
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
import "../math/Math.sol";
|
||||
import "./StandardToken.sol";
|
||||
import "./LimitedTransferToken.sol";
|
||||
|
||||
@ -44,13 +45,11 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
) public {
|
||||
|
||||
// Check for date inconsistencies that may cause unexpected behavior
|
||||
if (_cliff < _start || _vesting < _cliff) {
|
||||
throw;
|
||||
}
|
||||
require(_cliff >= _start && _vesting >= _cliff);
|
||||
|
||||
if (tokenGrantsCount(_to) > MAX_GRANTS_PER_ADDRESS) throw; // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting).
|
||||
require(tokenGrantsCount(_to) < MAX_GRANTS_PER_ADDRESS); // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting).
|
||||
|
||||
uint count = grants[_to].push(
|
||||
uint256 count = grants[_to].push(
|
||||
TokenGrant(
|
||||
_revokable ? msg.sender : 0, // avoid storing an extra 20 bytes when it is non-revokable
|
||||
_value,
|
||||
@ -72,16 +71,11 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
* @param _holder The address which will have its tokens revoked.
|
||||
* @param _grantId The id of the token grant.
|
||||
*/
|
||||
function revokeTokenGrant(address _holder, uint _grantId) public {
|
||||
TokenGrant grant = grants[_holder][_grantId];
|
||||
function revokeTokenGrant(address _holder, uint256 _grantId) public {
|
||||
TokenGrant storage grant = grants[_holder][_grantId];
|
||||
|
||||
if (!grant.revokable) { // Check if grant was revokable
|
||||
throw;
|
||||
}
|
||||
|
||||
if (grant.granter != msg.sender) { // Only granter can revoke it
|
||||
throw;
|
||||
}
|
||||
require(grant.revokable);
|
||||
require(grant.granter == msg.sender); // Only granter can revoke it
|
||||
|
||||
address receiver = grant.burnsOnRevoke ? 0xdead : msg.sender;
|
||||
|
||||
@ -103,12 +97,12 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
* @dev Calculate the total amount of transferable tokens of a holder at a given time
|
||||
* @param holder address The address of the holder
|
||||
* @param time uint64 The specific time.
|
||||
* @return An uint representing a holder's total amount of transferable tokens.
|
||||
* @return An uint256 representing a holder's total amount of transferable tokens.
|
||||
*/
|
||||
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
|
||||
function transferableTokens(address holder, uint64 time) public constant returns (uint256) {
|
||||
uint256 grantIndex = tokenGrantsCount(holder);
|
||||
|
||||
if (grantIndex == 0) return balanceOf(holder); // shortcut for holder without grants
|
||||
if (grantIndex == 0) return super.transferableTokens(holder, time); // shortcut for holder without grants
|
||||
|
||||
// Iterate through all the grants the holder has, and add all non-vested tokens
|
||||
uint256 nonVested = 0;
|
||||
@ -121,26 +115,26 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
|
||||
// Return the minimum of how many vested can transfer and other value
|
||||
// in case there are other limiting transferability factors (default is balanceOf)
|
||||
return SafeMath.min256(vestedTransferable, super.transferableTokens(holder, time));
|
||||
return Math.min256(vestedTransferable, super.transferableTokens(holder, time));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Check the amount of grants that an address has.
|
||||
* @param _holder The holder of the grants.
|
||||
* @return A uint representing the total amount of grants.
|
||||
* @return A uint256 representing the total amount of grants.
|
||||
*/
|
||||
function tokenGrantsCount(address _holder) constant returns (uint index) {
|
||||
function tokenGrantsCount(address _holder) public constant returns (uint256 index) {
|
||||
return grants[_holder].length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculate amount of vested tokens at a specifc time.
|
||||
* @param tokens uint256 The amount of tokens grantted.
|
||||
* @dev Calculate amount of vested tokens at a specific time
|
||||
* @param tokens uint256 The amount of tokens granted
|
||||
* @param time uint64 The time to be checked
|
||||
* @param start uint64 A time representing the begining of the grant
|
||||
* @param cliff uint64 The cliff period.
|
||||
* @param vesting uint64 The vesting period.
|
||||
* @return An uint representing the amount of vested tokensof a specif grant.
|
||||
* @param start uint64 The time representing the beginning of the grant
|
||||
* @param cliff uint64 The cliff period, the period before nothing can be paid out
|
||||
* @param vesting uint64 The vesting period
|
||||
* @return An uint256 representing the amount of vested tokens of a specific grant
|
||||
* transferableTokens
|
||||
* | _/-------- vestedTokens rect
|
||||
* | _/
|
||||
@ -155,14 +149,14 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
* | . |
|
||||
* | . |
|
||||
* +===+===========+---------+----------> time
|
||||
* Start Clift Vesting
|
||||
* Start Cliff Vesting
|
||||
*/
|
||||
function calculateVestedTokens(
|
||||
uint256 tokens,
|
||||
uint256 time,
|
||||
uint256 start,
|
||||
uint256 cliff,
|
||||
uint256 vesting) constant returns (uint256)
|
||||
uint256 vesting) public constant returns (uint256)
|
||||
{
|
||||
// Shortcuts for before cliff and after vesting cases.
|
||||
if (time < cliff) return 0;
|
||||
@ -172,7 +166,7 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
// As before cliff the shortcut returns 0, we can use just calculate a value
|
||||
// in the vesting rect (as shown in above's figure)
|
||||
|
||||
// vestedTokens = tokens * (time - start) / (vesting - start)
|
||||
// vestedTokens = (tokens * (time - start)) / (vesting - start)
|
||||
uint256 vestedTokens = SafeMath.div(
|
||||
SafeMath.mul(
|
||||
tokens,
|
||||
@ -185,14 +179,14 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get all information about a specifc grant.
|
||||
* @dev Get all information about a specific grant.
|
||||
* @param _holder The address which will have its tokens revoked.
|
||||
* @param _grantId The id of the token grant.
|
||||
* @return Returns all the values that represent a TokenGrant(address, value, start, cliff,
|
||||
* revokability, burnsOnRevoke, and vesting) plus the vested value at the current time.
|
||||
*/
|
||||
function tokenGrant(address _holder, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) {
|
||||
TokenGrant grant = grants[_holder][_grantId];
|
||||
function tokenGrant(address _holder, uint256 _grantId) public constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) {
|
||||
TokenGrant storage grant = grants[_holder][_grantId];
|
||||
|
||||
granter = grant.granter;
|
||||
value = grant.value;
|
||||
@ -209,7 +203,7 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
* @dev Get the amount of vested tokens at a specific time.
|
||||
* @param grant TokenGrant The grant to be checked.
|
||||
* @param time The time to be checked
|
||||
* @return An uint representing the amount of vested tokens of a specific grant at a specific time.
|
||||
* @return An uint256 representing the amount of vested tokens of a specific grant at a specific time.
|
||||
*/
|
||||
function vestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
|
||||
return calculateVestedTokens(
|
||||
@ -225,7 +219,7 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
* @dev Calculate the amount of non vested tokens at a specific time.
|
||||
* @param grant TokenGrant The grant to be checked.
|
||||
* @param time uint64 The time to be checked
|
||||
* @return An uint representing the amount of non vested tokens of a specifc grant on the
|
||||
* @return An uint256 representing the amount of non vested tokens of a specific grant on the
|
||||
* passed time frame.
|
||||
*/
|
||||
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
|
||||
@ -233,15 +227,15 @@ contract VestedToken is StandardToken, LimitedTransferToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculate the date when the holder can trasfer all its tokens
|
||||
* @dev Calculate the date when the holder can transfer all its tokens
|
||||
* @param holder address The address of the holder
|
||||
* @return An uint representing the date of the last transferable tokens.
|
||||
* @return An uint256 representing the date of the last transferable tokens.
|
||||
*/
|
||||
function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) {
|
||||
function lastTokenIsTransferableDate(address holder) public constant 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);
|
||||
date = Math.max64(grants[holder][i].vesting, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,4 +17,4 @@ help:
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
@ -9,4 +9,4 @@ Returns the token balance of the passed address.
|
||||
|
||||
function transfer(address _to, uint _value) returns (bool success)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
CrowdsaleToken
|
||||
=============================================
|
||||
|
||||
Simple ERC20 Token example, with crowdsale token creation.
|
||||
|
||||
Inherits from contract StandardToken.
|
||||
|
||||
createTokens(address recipient) payable
|
||||
"""""""""""""""""""""""""""""""""""""""""
|
||||
Creates tokens based on message value and credits to the recipient.
|
||||
|
||||
getPrice() constant returns (uint result)
|
||||
"""""""""""""""""""""""""""""""""""""""""
|
||||
Returns the amount of tokens per 1 ether.
|
||||
@ -9,4 +9,4 @@ Interested in contributing to Zeppelin?
|
||||
|
||||
* Framework proposal and roadmap: https://medium.com/zeppelin-blog/zeppelin-framework-proposal-and-development-roadmap-fdfa9a3a32ab#.iain47pak
|
||||
* Issue tracker: https://github.com/OpenZeppelin/zeppelin-solidity/issues
|
||||
* Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md
|
||||
* Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md
|
||||
|
||||
9
docs/source/ecrecovery.rst
Normal file
9
docs/source/ecrecovery.rst
Normal file
@ -0,0 +1,9 @@
|
||||
ECReovery
|
||||
=============================================
|
||||
|
||||
Returns the signer of the the hash using the signature divided in v, r, and s values.
|
||||
|
||||
recover(bytes32 hash, bytes sig) internal returns (address)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Returns the signer of the the hash using the signature that provides the web3.eth.sign() method.
|
||||
@ -13,7 +13,7 @@ To install the Zeppelin library, run::
|
||||
|
||||
After that, you'll get all the library's contracts in the contracts/zeppelin folder. You can use the contracts in the library like so::
|
||||
|
||||
import "./zeppelin/Ownable.sol";
|
||||
import "zeppelin-solidity/contracts/Ownable.sol";
|
||||
|
||||
contract MyContract is Ownable {
|
||||
...
|
||||
|
||||
@ -6,14 +6,14 @@
|
||||
Welcome to Zeppelin-Solidity
|
||||
=============================================
|
||||
|
||||
Zeppelin is a library for writing secure Smart Contracts on Ethereum.
|
||||
Zeppelin is a library for writing secure Smart Contracts on Ethereum.
|
||||
|
||||
With Zeppelin, you can build distributed applications, protocols and organizations:
|
||||
|
||||
* using :doc:`contract-security-patterns`
|
||||
* in the `Solidity language <https://solidity.readthedocs.io/en/develop/>`_.
|
||||
|
||||
The code is open-source, and `available on github <https://github.com/OpenZeppelin/zeppelin-solidity>`_.
|
||||
The code is open-source, and `available on github <https://github.com/OpenZeppelin/zeppelin-solidity>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
@ -24,7 +24,7 @@ The code is open-source, and `available on github <https://github.com/OpenZeppel
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Smart Contracts
|
||||
|
||||
|
||||
ownable
|
||||
Pausable
|
||||
destructible
|
||||
@ -41,7 +41,7 @@ The code is open-source, and `available on github <https://github.com/OpenZeppel
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Developer Resources
|
||||
|
||||
|
||||
contract-security-patterns
|
||||
developer-resources
|
||||
license
|
||||
license
|
||||
|
||||
@ -13,4 +13,4 @@ Destroys the contract and sends funds back to the owner.
|
||||
destroyAndSend(address _recipient) onlyOwner
|
||||
"""""""""""""""""""
|
||||
|
||||
Destroys the contract and sends funds back to the _recepient.
|
||||
Destroys the contract and sends funds back to the _recepient.
|
||||
|
||||
@ -6,18 +6,18 @@ Copyright (c) 2016 Smart Contract Solutions, Inc.
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@ -5,8 +5,8 @@ Base contract that provides mechanism for limiting the amount of funds a contrac
|
||||
|
||||
LimitBalance(unit _limit)
|
||||
""""""""""""""""""""""""""""
|
||||
Constructor takes an unisgned integer and sets it as the limit of funds this contract can hold.
|
||||
Constructor takes an unsigned integer and sets it as the limit of funds this contract can hold.
|
||||
|
||||
modifier limitedPayable()
|
||||
""""""""""""""""""""""""""""
|
||||
Throws an error if this contract's balance is already above the limit.
|
||||
Throws an error if this contract's balance is already above the limit.
|
||||
|
||||
24
docs/source/math.rst
Normal file
24
docs/source/math.rst
Normal file
@ -0,0 +1,24 @@
|
||||
Math
|
||||
=============================================
|
||||
|
||||
Provides assorted low-level math operations.
|
||||
|
||||
max64(uint64 a, uint64 b) internal constant returns (uint64)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Returns the largest of two uint64 numbers.
|
||||
|
||||
min64(uint64 a, uint64 b) internal constant returns (uint64)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Returns the smallest of two uint64 numbers.
|
||||
|
||||
max64(uint256 a, uint256 b) internal constant returns (uint256)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Returns the largest of two uint256 numbers.
|
||||
|
||||
min64(uint256 a, uint256 b) internal constant returns (uint256)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Returns the smallest of two uint256 numbers.
|
||||
9
docs/source/merkleproof.rst
Normal file
9
docs/source/merkleproof.rst
Normal file
@ -0,0 +1,9 @@
|
||||
MerkleProof
|
||||
=============================================
|
||||
|
||||
Merkle proof verification for leaves of a Merkle tree.
|
||||
|
||||
verifyProof(bytes _proof, bytes32 _root, bytes32 _leaf) internal constant returns (bool)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Verifies a Merkle proof proving the existence of a leaf in a Merkle tree. Assumes that each pair of leaves and each pair of pre-images is sorted.
|
||||
@ -13,4 +13,4 @@ Creates a new instance of the contract at the passed address.
|
||||
setCompleted(uint completed) onlyOwner**
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Sets the last time that a migration was completed.
|
||||
Sets the last time that a migration was completed.
|
||||
|
||||
@ -11,6 +11,6 @@ modifier onlyOwner( )
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
Prevents function from running if it is called by anyone other than the owner.
|
||||
|
||||
transfer(address newOwner) onlyOwner
|
||||
transferOwnership(address newOwner) onlyOwner
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
Transfers ownership of the contract to the passed address.
|
||||
Transfers ownership of the contract to the passed address.
|
||||
|
||||
@ -24,4 +24,4 @@ Only runs if pause mechanism is activated.
|
||||
unpause() onlyOwner whenPaused returns (bool)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Deactivates the pause mechanism.
|
||||
Deactivates the pause mechanism.
|
||||
|
||||
@ -9,4 +9,4 @@ Adds sent amount to available balance that payee can pull from this contract, ca
|
||||
|
||||
withdrawPayments( )
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Sends designated balance to payee calling the contract. Throws error if designated balance is 0, if contract does not hold enough funds ot pay the payee, or if the send transaction is not successful.
|
||||
Sends designated balance to payee calling the contract. Throws error if designated balance is 0, if contract does not hold enough funds to pay the payee, or if the send transaction is not successful.
|
||||
|
||||
@ -8,17 +8,17 @@ assert(bool assertion) internal
|
||||
|
||||
Throws an error if the passed result is false. Used in this contract by checking mathematical expressions.
|
||||
|
||||
safeMul(uint a, uint b) internal returns (uint)
|
||||
mul(uint256 a, uint256 b) internal returns (uint256)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Multiplies two unisgned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier.
|
||||
Multiplies two unsigned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier.
|
||||
|
||||
safeSub(uint a, uint b) internal returns (uint)
|
||||
sub(uint256 a, uint256 b) internal returns (uint256)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Checks that b is not greater than a before subtracting.
|
||||
|
||||
safeAdd(uint a, uint b) internal returns (uint)
|
||||
add(uint256 a, uint256 b) internal returns (uint256)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Checks that the result is greater than both a and b.
|
||||
|
||||
@ -23,4 +23,4 @@ Transfers tokens from an account that the sender is approved to transfer from. A
|
||||
|
||||
function transfer(address _to, uint _value) returns (bool success)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"package_name": "zeppelin",
|
||||
"version": "1.0.6",
|
||||
"version": "1.3.0",
|
||||
"description": "Secure Smart Contract library for Solidity",
|
||||
"authors": [
|
||||
"Manuel Araoz <manuelaraoz@gmail.com>"
|
||||
|
||||
6033
package-lock.json
generated
Normal file
6033
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@ -1,12 +1,10 @@
|
||||
{
|
||||
"name": "zeppelin-solidity",
|
||||
"version": "1.0.6",
|
||||
"version": "1.3.0",
|
||||
"description": "Secure Smart Contract library for Solidity",
|
||||
"main": "truffle.js",
|
||||
"scripts": {
|
||||
"test": "scripts/test.sh",
|
||||
"console": "truffle console",
|
||||
"install": "scripts/install.sh",
|
||||
"coverage": "scripts/coverage.sh"
|
||||
},
|
||||
"repository": {
|
||||
@ -27,16 +25,21 @@
|
||||
"url": "https://github.com/OpenZeppelin/zeppelin-solidity/issues"
|
||||
},
|
||||
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity",
|
||||
"dependencies": {
|
||||
"truffle-hdwallet-provider": "0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"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",
|
||||
"solidity-coverage": "^0.1.0",
|
||||
"truffle": "https://github.com/ConsenSys/truffle.git#3.1.9"
|
||||
"chai": "^4.0.2",
|
||||
"chai-as-promised": "^7.0.0",
|
||||
"chai-bignumber": "^2.0.0",
|
||||
"coveralls": "^2.13.1",
|
||||
"ethereumjs-util": "^5.1.2",
|
||||
"ethereumjs-testrpc": "^4.1.1",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"solidity-coverage": "^0.2.2",
|
||||
"truffle": "^3.4.6",
|
||||
"truffle-hdwallet-provider": "0.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#! /bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SOLIDITY_COVERAGE=true ./node_modules/.bin/solidity-coverage
|
||||
SOLIDITY_COVERAGE=true scripts/test.sh
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
#! /bin/bash
|
||||
|
||||
mkdir -p ../../contracts/zeppelin && cp -r contracts/* "$_"
|
||||
@ -1,13 +1,65 @@
|
||||
#! /bin/bash
|
||||
#!/usr/bin/env 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=$!
|
||||
# Exit script as soon as a command fails.
|
||||
set -o errexit
|
||||
|
||||
# Executes cleanup function at script exit.
|
||||
trap cleanup EXIT
|
||||
|
||||
cleanup() {
|
||||
# Kill the testrpc instance that we started (if we started one and if it's still running).
|
||||
if [ -n "$testrpc_pid" ] && ps -p $testrpc_pid > /dev/null; then
|
||||
kill -9 $testrpc_pid
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$SOLIDITY_COVERAGE" = true ]; then
|
||||
testrpc_port=8555
|
||||
else
|
||||
testrpc_port=8545
|
||||
fi
|
||||
./node_modules/truffle/cli.js test
|
||||
if [ ! $trpc_running ]; then
|
||||
kill -9 $trpc_pid
|
||||
|
||||
testrpc_running() {
|
||||
nc -z localhost "$testrpc_port"
|
||||
}
|
||||
|
||||
start_testrpc() {
|
||||
# We define 10 accounts with balance 1M ether, needed for high-value tests.
|
||||
local accounts=(
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208,1000000000000000000000000"
|
||||
--account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000"
|
||||
)
|
||||
|
||||
if [ "$SOLIDITY_COVERAGE" = true ]; then
|
||||
node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port "$testrpc_port" "${accounts[@]}" > /dev/null &
|
||||
else
|
||||
node_modules/.bin/testrpc "${accounts[@]}" > /dev/null &
|
||||
fi
|
||||
|
||||
testrpc_pid=$!
|
||||
}
|
||||
|
||||
if testrpc_running; then
|
||||
echo "Using existing testrpc instance"
|
||||
else
|
||||
echo "Starting our own testrpc instance"
|
||||
start_testrpc
|
||||
fi
|
||||
|
||||
if [ "$SOLIDITY_COVERAGE" = true ]; then
|
||||
node_modules/.bin/solidity-coverage
|
||||
|
||||
if [ "$CONTINUOUS_INTEGRATION" = true ]; then
|
||||
cat coverage/lcov.info | node_modules/.bin/coveralls
|
||||
fi
|
||||
else
|
||||
node_modules/.bin/truffle test "$@"
|
||||
fi
|
||||
|
||||
@ -22,14 +22,24 @@ contract('BasicToken', function(accounts) {
|
||||
assert.equal(secondAccountBalance, 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 BasicTokenMock.new(accounts[0], 100);
|
||||
try {
|
||||
let transfer = await token.transfer(accounts[1], 101);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw an error when trying to transfer to 0x0', async function() {
|
||||
let token = await BasicTokenMock.new(accounts[0], 100);
|
||||
try {
|
||||
let transfer = await token.transfer(0x0, 100);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -15,7 +15,7 @@ function awaitEvent(event, handler) {
|
||||
function wrappedHandler(...args) {
|
||||
Promise.resolve(handler(...args)).then(resolve).catch(reject);
|
||||
}
|
||||
|
||||
|
||||
event.watch(wrappedHandler);
|
||||
});
|
||||
}
|
||||
@ -90,7 +90,7 @@ contract('Bounty', function(accounts) {
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
let bounty = await InsecureTargetBounty.new();
|
||||
let event = bounty.TargetCreated({});
|
||||
|
||||
|
||||
let watcher = async function(err, result) {
|
||||
event.stopWatching();
|
||||
if (err) { throw err; }
|
||||
|
||||
39
test/BurnableToken.js
Normal file
39
test/BurnableToken.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
const EVMThrow = require('./helpers/EVMThrow.js')
|
||||
const BurnableTokenMock = artifacts.require("./helpers/BurnableTokenMock.sol")
|
||||
const BigNumber = web3.BigNumber
|
||||
|
||||
require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should()
|
||||
|
||||
const expect = require('chai').expect
|
||||
|
||||
contract('BurnableToken', function (accounts) {
|
||||
let token
|
||||
let expectedTokenSupply = new BigNumber(999)
|
||||
|
||||
beforeEach(async function () {
|
||||
token = await BurnableTokenMock.new(accounts[0], 1000)
|
||||
})
|
||||
|
||||
it('owner should be able to burn tokens', async function () {
|
||||
const { logs } = await token.burn(1, { from: accounts[0] })
|
||||
|
||||
const balance = await token.balanceOf(accounts[0])
|
||||
balance.should.be.bignumber.equal(expectedTokenSupply)
|
||||
|
||||
const totalSupply = await token.totalSupply()
|
||||
totalSupply.should.be.bignumber.equal(expectedTokenSupply)
|
||||
|
||||
const event = logs.find(e => e.event === 'Burn')
|
||||
expect(event).to.exist
|
||||
})
|
||||
|
||||
it('cannot burn more tokens than your balance', async function () {
|
||||
await token.burn(2000, { from: accounts[0] })
|
||||
.should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
})
|
||||
35
test/CanReclaimToken.js
Normal file
35
test/CanReclaimToken.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
import expectThrow from './helpers/expectThrow';
|
||||
import toPromise from './helpers/toPromise';
|
||||
const CanReclaimToken = artifacts.require('../contracts/ownership/CanReclaimToken.sol');
|
||||
const BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");
|
||||
|
||||
contract('CanReclaimToken', function(accounts) {
|
||||
let token = null;
|
||||
let canReclaimToken = null;
|
||||
|
||||
beforeEach(async function() {
|
||||
// Create contract and token
|
||||
token = await BasicTokenMock.new(accounts[0], 100);
|
||||
canReclaimToken = await CanReclaimToken.new();
|
||||
// Force token into contract
|
||||
await token.transfer(canReclaimToken.address, 10);
|
||||
const startBalance = await token.balanceOf(canReclaimToken.address);
|
||||
assert.equal(startBalance, 10);
|
||||
});
|
||||
|
||||
it('should allow owner to reclaim tokens', async function() {
|
||||
const ownerStartBalance = await token.balanceOf(accounts[0]);
|
||||
await canReclaimToken.reclaimToken(token.address);
|
||||
const ownerFinalBalance = await token.balanceOf(accounts[0]);
|
||||
const finalBalance = await token.balanceOf(canReclaimToken.address);
|
||||
assert.equal(finalBalance, 0);
|
||||
assert.equal(ownerFinalBalance - ownerStartBalance, 10);
|
||||
});
|
||||
|
||||
it('should allow only owner to reclaim tokens', async function() {
|
||||
await expectThrow(
|
||||
canReclaimToken.reclaimToken(token.address, {from: accounts[1]}),
|
||||
);
|
||||
});
|
||||
});
|
||||
96
test/CappedCrowdsale.js
Normal file
96
test/CappedCrowdsale.js
Normal file
@ -0,0 +1,96 @@
|
||||
import ether from './helpers/ether'
|
||||
import {advanceBlock} from './helpers/advanceToBlock'
|
||||
import {increaseTimeTo, duration} from './helpers/increaseTime'
|
||||
import latestTime from './helpers/latestTime'
|
||||
import EVMThrow from './helpers/EVMThrow'
|
||||
|
||||
const BigNumber = web3.BigNumber
|
||||
|
||||
require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should()
|
||||
|
||||
const CappedCrowdsale = artifacts.require('./helpers/CappedCrowdsaleImpl.sol')
|
||||
const MintableToken = artifacts.require('MintableToken')
|
||||
|
||||
contract('CappedCrowdsale', function ([_, wallet]) {
|
||||
|
||||
const rate = new BigNumber(1000)
|
||||
|
||||
const cap = ether(300)
|
||||
const lessThanCap = ether(60)
|
||||
|
||||
before(async function() {
|
||||
//Advance to the next block to correctly read time in the solidity "now" function interpreted by testrpc
|
||||
await advanceBlock()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.startTime = latestTime() + duration.weeks(1);
|
||||
this.endTime = this.startTime + duration.weeks(1);
|
||||
|
||||
this.crowdsale = await CappedCrowdsale.new(this.startTime, this.endTime, rate, wallet, cap)
|
||||
|
||||
this.token = MintableToken.at(await this.crowdsale.token())
|
||||
})
|
||||
|
||||
describe('creating a valid crowdsale', function () {
|
||||
|
||||
it('should fail with zero cap', async function () {
|
||||
await CappedCrowdsale.new(this.startTime, this.endTime, rate, wallet, 0).should.be.rejectedWith(EVMThrow);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('accepting payments', function () {
|
||||
|
||||
beforeEach(async function () {
|
||||
await increaseTimeTo(this.startTime)
|
||||
})
|
||||
|
||||
it('should accept payments within cap', async function () {
|
||||
await this.crowdsale.send(cap.minus(lessThanCap)).should.be.fulfilled
|
||||
await this.crowdsale.send(lessThanCap).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should reject payments outside cap', async function () {
|
||||
await this.crowdsale.send(cap)
|
||||
await this.crowdsale.send(1).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('should reject payments that exceed cap', async function () {
|
||||
await this.crowdsale.send(cap.plus(1)).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('ending', function () {
|
||||
|
||||
beforeEach(async function () {
|
||||
await increaseTimeTo(this.startTime)
|
||||
})
|
||||
|
||||
it('should not be ended if under cap', async function () {
|
||||
let hasEnded = await this.crowdsale.hasEnded()
|
||||
hasEnded.should.equal(false)
|
||||
await this.crowdsale.send(lessThanCap)
|
||||
hasEnded = await this.crowdsale.hasEnded()
|
||||
hasEnded.should.equal(false)
|
||||
})
|
||||
|
||||
it('should not be ended if just under cap', async function () {
|
||||
await this.crowdsale.send(cap.minus(1))
|
||||
let hasEnded = await this.crowdsale.hasEnded()
|
||||
hasEnded.should.equal(false)
|
||||
})
|
||||
|
||||
it('should be ended if cap reached', async function () {
|
||||
await this.crowdsale.send(cap)
|
||||
let hasEnded = await this.crowdsale.hasEnded()
|
||||
hasEnded.should.equal(true)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@ -25,9 +25,10 @@ contract('Claimable', function(accounts) {
|
||||
|
||||
it('should prevent to claimOwnership from no pendingOwner', async function() {
|
||||
try {
|
||||
await claimable.claimOwnership({from: accounts[2]});
|
||||
await claimable.claimOwnership({from: accounts[2]});
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
@ -36,9 +37,10 @@ contract('Claimable', function(accounts) {
|
||||
const owner = await claimable.owner.call();
|
||||
assert.isTrue(owner !== other);
|
||||
try {
|
||||
await claimable.transferOwnership(other, {from: other});
|
||||
await claimable.transferOwnership(other, {from: other});
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
152
test/Crowdsale.js
Normal file
152
test/Crowdsale.js
Normal file
@ -0,0 +1,152 @@
|
||||
import ether from './helpers/ether'
|
||||
import {advanceBlock} from './helpers/advanceToBlock'
|
||||
import {increaseTimeTo, duration} from './helpers/increaseTime'
|
||||
import latestTime from './helpers/latestTime'
|
||||
import EVMThrow from './helpers/EVMThrow'
|
||||
|
||||
const BigNumber = web3.BigNumber
|
||||
|
||||
const should = require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should()
|
||||
|
||||
const Crowdsale = artifacts.require('Crowdsale')
|
||||
const MintableToken = artifacts.require('MintableToken')
|
||||
|
||||
contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
|
||||
|
||||
const rate = new BigNumber(1000)
|
||||
const value = ether(42)
|
||||
|
||||
const expectedTokenAmount = rate.mul(value)
|
||||
|
||||
before(async function() {
|
||||
//Advance to the next block to correctly read time in the solidity "now" function interpreted by testrpc
|
||||
await advanceBlock()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.startTime = latestTime() + duration.weeks(1);
|
||||
this.endTime = this.startTime + duration.weeks(1);
|
||||
this.afterEndTime = this.endTime + duration.seconds(1)
|
||||
|
||||
|
||||
this.crowdsale = await Crowdsale.new(this.startTime, this.endTime, rate, wallet)
|
||||
|
||||
this.token = MintableToken.at(await this.crowdsale.token())
|
||||
})
|
||||
|
||||
it('should be token owner', async function () {
|
||||
const owner = await this.token.owner()
|
||||
owner.should.equal(this.crowdsale.address)
|
||||
})
|
||||
|
||||
it('should be ended only after end', async function () {
|
||||
let ended = await this.crowdsale.hasEnded()
|
||||
ended.should.equal(false)
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
ended = await this.crowdsale.hasEnded()
|
||||
ended.should.equal(true)
|
||||
})
|
||||
|
||||
describe('accepting payments', function () {
|
||||
|
||||
it('should reject payments before start', async function () {
|
||||
await this.crowdsale.send(value).should.be.rejectedWith(EVMThrow)
|
||||
await this.crowdsale.buyTokens(investor, {from: purchaser, value: value}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('should accept payments after start', async function () {
|
||||
await increaseTimeTo(this.startTime)
|
||||
await this.crowdsale.send(value).should.be.fulfilled
|
||||
await this.crowdsale.buyTokens(investor, {value: value, from: purchaser}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should reject payments after end', async function () {
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
await this.crowdsale.send(value).should.be.rejectedWith(EVMThrow)
|
||||
await this.crowdsale.buyTokens(investor, {value: value, from: purchaser}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('high-level purchase', function () {
|
||||
|
||||
beforeEach(async function() {
|
||||
await increaseTimeTo(this.startTime)
|
||||
})
|
||||
|
||||
it('should log purchase', async function () {
|
||||
const {logs} = await this.crowdsale.sendTransaction({value: value, from: investor})
|
||||
|
||||
const event = logs.find(e => e.event === 'TokenPurchase')
|
||||
|
||||
should.exist(event)
|
||||
event.args.purchaser.should.equal(investor)
|
||||
event.args.beneficiary.should.equal(investor)
|
||||
event.args.value.should.be.bignumber.equal(value)
|
||||
event.args.amount.should.be.bignumber.equal(expectedTokenAmount)
|
||||
})
|
||||
|
||||
it('should increase totalSupply', async function () {
|
||||
await this.crowdsale.send(value)
|
||||
const totalSupply = await this.token.totalSupply()
|
||||
totalSupply.should.be.bignumber.equal(expectedTokenAmount)
|
||||
})
|
||||
|
||||
it('should assign tokens to sender', async function () {
|
||||
await this.crowdsale.sendTransaction({value: value, from: investor})
|
||||
let balance = await this.token.balanceOf(investor);
|
||||
balance.should.be.bignumber.equal(expectedTokenAmount)
|
||||
})
|
||||
|
||||
it('should forward funds to wallet', async function () {
|
||||
const pre = web3.eth.getBalance(wallet)
|
||||
await this.crowdsale.sendTransaction({value, from: investor})
|
||||
const post = web3.eth.getBalance(wallet)
|
||||
post.minus(pre).should.be.bignumber.equal(value)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('low-level purchase', function () {
|
||||
|
||||
beforeEach(async function() {
|
||||
await increaseTimeTo(this.startTime)
|
||||
})
|
||||
|
||||
it('should log purchase', async function () {
|
||||
const {logs} = await this.crowdsale.buyTokens(investor, {value: value, from: purchaser})
|
||||
|
||||
const event = logs.find(e => e.event === 'TokenPurchase')
|
||||
|
||||
should.exist(event)
|
||||
event.args.purchaser.should.equal(purchaser)
|
||||
event.args.beneficiary.should.equal(investor)
|
||||
event.args.value.should.be.bignumber.equal(value)
|
||||
event.args.amount.should.be.bignumber.equal(expectedTokenAmount)
|
||||
})
|
||||
|
||||
it('should increase totalSupply', async function () {
|
||||
await this.crowdsale.buyTokens(investor, {value, from: purchaser})
|
||||
const totalSupply = await this.token.totalSupply()
|
||||
totalSupply.should.be.bignumber.equal(expectedTokenAmount)
|
||||
})
|
||||
|
||||
it('should assign tokens to beneficiary', async function () {
|
||||
await this.crowdsale.buyTokens(investor, {value, from: purchaser})
|
||||
const balance = await this.token.balanceOf(investor)
|
||||
balance.should.be.bignumber.equal(expectedTokenAmount)
|
||||
})
|
||||
|
||||
it('should forward funds to wallet', async function () {
|
||||
const pre = web3.eth.getBalance(wallet)
|
||||
await this.crowdsale.buyTokens(investor, {value, from: purchaser})
|
||||
const post = web3.eth.getBalance(wallet)
|
||||
post.minus(pre).should.be.bignumber.equal(value)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@ -1,21 +1,25 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
const timer = require('./helpers/timer');
|
||||
|
||||
var DayLimitMock = artifacts.require('helpers/DayLimitMock.sol');
|
||||
var DayLimitMock = artifacts.require('./helpers/DayLimitMock.sol');
|
||||
|
||||
contract('DayLimit', function(accounts) {
|
||||
const day = 60 * 60 * 24;
|
||||
|
||||
let dayLimit;
|
||||
let initLimit = 10;
|
||||
|
||||
beforeEach( async function() {
|
||||
dayLimit = await DayLimitMock.new(initLimit);
|
||||
});
|
||||
|
||||
it('should construct with the passed daily limit', async function() {
|
||||
let initLimit = 10;
|
||||
let dayLimit = await DayLimitMock.new(initLimit);
|
||||
let dailyLimit = await dayLimit.dailyLimit();
|
||||
assert.equal(initLimit, dailyLimit);
|
||||
});
|
||||
|
||||
it('should be able to spend if daily limit is not reached', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
@ -26,32 +30,28 @@ contract('DayLimit', function(accounts) {
|
||||
});
|
||||
|
||||
it('should prevent spending if daily limit is reached', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
await dayLimit.attemptSpend(3);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow spending if daily limit is reached and then set higher', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
await dayLimit.attemptSpend(3);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
@ -63,6 +63,26 @@ contract('DayLimit', function(accounts) {
|
||||
});
|
||||
|
||||
it('should allow spending if daily limit is reached and then amount spent is reset', async function() {
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.resetSpentToday();
|
||||
await dayLimit.attemptSpend(3);
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 3);
|
||||
});
|
||||
|
||||
it('should allow spending if daily limit is reached and then the next has come', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
@ -71,14 +91,16 @@ contract('DayLimit', function(accounts) {
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
await dayLimit.attemptSpend(3);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.resetSpentToday(15);
|
||||
await timer(day);
|
||||
|
||||
await dayLimit.attemptSpend(3);
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 3);
|
||||
|
||||
@ -49,7 +49,7 @@ contract('DelayedClaimable', function(accounts) {
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
assert.isFalse(err.message.search('invalid JUMP') === -1);
|
||||
assert.isFalse(err.message.search('invalid opcode') === -1);
|
||||
let owner = await delayedClaimable.owner();
|
||||
assert.isTrue(owner !== accounts[1]);
|
||||
});
|
||||
@ -62,7 +62,7 @@ contract('DelayedClaimable', function(accounts) {
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
assert.isFalse(err.message.search('invalid JUMP') === -1);
|
||||
assert.isFalse(err.message.search('invalid opcode') === -1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
55
test/ECRecovery.js
Normal file
55
test/ECRecovery.js
Normal file
@ -0,0 +1,55 @@
|
||||
var ECRecovery = artifacts.require("../contracts/ECRecovery.sol");
|
||||
var utils = require('ethereumjs-util');
|
||||
var hashMessage = require('./helpers/hashMessage.js');
|
||||
|
||||
contract('ECRecovery', function(accounts) {
|
||||
|
||||
let ecrecovery;
|
||||
|
||||
before(async function() {
|
||||
ecrecovery = await ECRecovery.new();
|
||||
});
|
||||
|
||||
it("recover v0", async function() {
|
||||
// Signature generated outside testrpc with method web3.eth.sign(signer, message)
|
||||
let signer = '0x2cc1166f6212628a0deef2b33befb2187d35b86c';
|
||||
let message = '0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d'; // web3.sha3('OpenZeppelin')
|
||||
let signature = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200';
|
||||
assert.equal(signer, await ecrecovery.recover(message, signature));
|
||||
});
|
||||
|
||||
it("recover v1", async function() {
|
||||
// Signature generated outside testrpc with method web3.eth.sign(signer, message)
|
||||
let signer = '0x1e318623ab09fe6de3c9b8672098464aeda9100e';
|
||||
let message = '0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d'; // web3.sha3('OpenZeppelin')
|
||||
let signature = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001';
|
||||
assert.equal(signer, await ecrecovery.recover(message, signature));
|
||||
});
|
||||
|
||||
it("recover using web3.eth.sign()", async function() {
|
||||
// Create the signature using account[0]
|
||||
const signature = web3.eth.sign(web3.eth.accounts[0], web3.sha3('OpenZeppelin'));
|
||||
|
||||
// Recover the signer address form the generated message and signature.
|
||||
assert.equal(web3.eth.accounts[0], await ecrecovery.recover(hashMessage('OpenZeppelin'), signature));
|
||||
});
|
||||
|
||||
it("recover using web3.eth.sign() should return wrong signer", async function() {
|
||||
// Create the signature using account[0]
|
||||
const signature = web3.eth.sign(web3.eth.accounts[0], web3.sha3('OpenZeppelin'));
|
||||
|
||||
// Recover the signer address form the generated message and wrong signature.
|
||||
assert.notEqual(web3.eth.accounts[0], await ecrecovery.recover(hashMessage('Test'), signature));
|
||||
});
|
||||
|
||||
it("recover should fail when a wrong hash is sent", async function() {
|
||||
// Create the signature using account[0]
|
||||
let signature = web3.eth.sign(web3.eth.accounts[0], web3.sha3('OpenZeppelin'));
|
||||
|
||||
// Recover the signer address form the generated message and wrong signature.
|
||||
assert.equal('0x0000000000000000000000000000000000000000',
|
||||
await ecrecovery.recover(hashMessage('OpenZeppelin').substring(2), signature)
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
63
test/FinalizableCrowdsale.js
Normal file
63
test/FinalizableCrowdsale.js
Normal file
@ -0,0 +1,63 @@
|
||||
import {advanceBlock} from './helpers/advanceToBlock'
|
||||
import {increaseTimeTo, duration} from './helpers/increaseTime'
|
||||
import latestTime from './helpers/latestTime'
|
||||
import EVMThrow from './helpers/EVMThrow'
|
||||
|
||||
const BigNumber = web3.BigNumber
|
||||
|
||||
const should = require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should()
|
||||
|
||||
const FinalizableCrowdsale = artifacts.require('./helpers/FinalizableCrowdsaleImpl.sol')
|
||||
const MintableToken = artifacts.require('MintableToken')
|
||||
|
||||
contract('FinalizableCrowdsale', function ([_, owner, wallet, thirdparty]) {
|
||||
|
||||
const rate = new BigNumber(1000)
|
||||
|
||||
before(async function() {
|
||||
//Advance to the next block to correctly read time in the solidity "now" function interpreted by testrpc
|
||||
await advanceBlock()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.startTime = latestTime() + duration.weeks(1)
|
||||
this.endTime = this.startTime + duration.weeks(1)
|
||||
this.afterEndTime = this.endTime + duration.seconds(1)
|
||||
|
||||
|
||||
this.crowdsale = await FinalizableCrowdsale.new(this.startTime, this.endTime, rate, wallet, {from: owner})
|
||||
|
||||
this.token = MintableToken.at(await this.crowdsale.token())
|
||||
})
|
||||
|
||||
it('cannot be finalized before ending', async function () {
|
||||
await this.crowdsale.finalize({from: owner}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('cannot be finalized by third party after ending', async function () {
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
await this.crowdsale.finalize({from: thirdparty}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('can be finalized by owner after ending', async function () {
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
await this.crowdsale.finalize({from: owner}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('cannot be finalized twice', async function () {
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
await this.crowdsale.finalize({from: owner})
|
||||
await this.crowdsale.finalize({from: owner}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('logs finalized', async function () {
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
const {logs} = await this.crowdsale.finalize({from: owner})
|
||||
const event = logs.find(e => e.event === 'Finalized')
|
||||
should.exist(event)
|
||||
})
|
||||
|
||||
})
|
||||
@ -35,7 +35,8 @@ contract('HasNoEther', function(accounts) {
|
||||
assert.equal(startBalance, 0);
|
||||
|
||||
// Force ether into it
|
||||
await ForceEther.new(hasNoEther.address, {value: amount});
|
||||
let forceEther = await ForceEther.new({value: amount});
|
||||
await forceEther.destroyAndSend(hasNoEther.address);
|
||||
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
|
||||
assert.equal(forcedBalance, amount);
|
||||
|
||||
@ -53,7 +54,8 @@ contract('HasNoEther', function(accounts) {
|
||||
let hasNoEther = await HasNoEtherTest.new({from: accounts[0]});
|
||||
|
||||
// Force ether into it
|
||||
await ForceEther.new(hasNoEther.address, {value: amount});
|
||||
let forceEther = await ForceEther.new({value: amount});
|
||||
await forceEther.destroyAndSend(hasNoEther.address);
|
||||
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
|
||||
assert.equal(forcedBalance, amount);
|
||||
|
||||
|
||||
@ -27,11 +27,11 @@ contract('LimitBalance', function(accounts) {
|
||||
it('shouldnt allow sending above limit', async function() {
|
||||
let amount = 1110;
|
||||
try {
|
||||
await lb.limitedDeposit({value: amount});
|
||||
await lb.limitedDeposit({value: amount});
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it('should allow multiple sends below limit', async function() {
|
||||
@ -52,10 +52,10 @@ contract('LimitBalance', function(accounts) {
|
||||
|
||||
try {
|
||||
await lb.limitedDeposit({value: amount+1});
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
60
test/MerkleProof.js
Normal file
60
test/MerkleProof.js
Normal file
@ -0,0 +1,60 @@
|
||||
var MerkleProof = artifacts.require("./MerkleProof.sol");
|
||||
|
||||
import MerkleTree from "./helpers/merkleTree.js";
|
||||
import { sha3, bufferToHex } from "ethereumjs-util";
|
||||
|
||||
contract('MerkleProof', function(accounts) {
|
||||
let merkleProof;
|
||||
|
||||
before(async function() {
|
||||
merkleProof = await MerkleProof.new();
|
||||
});
|
||||
|
||||
describe("verifyProof", function() {
|
||||
it("should return true for a valid Merkle proof", async function() {
|
||||
const elements = ["a", "b", "c", "d"];
|
||||
const merkleTree = new MerkleTree(elements);
|
||||
|
||||
const root = merkleTree.getHexRoot();
|
||||
|
||||
const proof = merkleTree.getHexProof(elements[0]);
|
||||
|
||||
const leaf = bufferToHex(sha3(elements[0]));
|
||||
|
||||
const result = await merkleProof.verifyProof(proof, root, leaf);
|
||||
assert.isOk(result, "verifyProof did not return true for a valid proof");
|
||||
});
|
||||
|
||||
it("should return false for an invalid Merkle proof", async function() {
|
||||
const correctElements = ["a", "b", "c"]
|
||||
const correctMerkleTree = new MerkleTree(correctElements);
|
||||
|
||||
const correctRoot = correctMerkleTree.getHexRoot();
|
||||
|
||||
const correctLeaf = bufferToHex(sha3(correctElements[0]));
|
||||
|
||||
const badElements = ["d", "e", "f"]
|
||||
const badMerkleTree = new MerkleTree(badElements)
|
||||
|
||||
const badProof = badMerkleTree.getHexProof(badElements[0])
|
||||
|
||||
const result = await merkleProof.verifyProof(badProof, correctRoot, correctLeaf);
|
||||
assert.isNotOk(result, "verifyProof did not return false for an invalid proof");
|
||||
});
|
||||
|
||||
it("should return false for a Merkle proof of invalid length", async function() {
|
||||
const elements = ["a", "b", "c"]
|
||||
const merkleTree = new MerkleTree(elements);
|
||||
|
||||
const root = merkleTree.getHexRoot();
|
||||
|
||||
const proof = merkleTree.getHexProof(elements[0]);
|
||||
const badProof = proof.slice(0, proof.length - 5);
|
||||
|
||||
const leaf = bufferToHex(sha3(elements[0]));
|
||||
|
||||
const result = await merkleProof.verifyProof(badProof, root, leaf);
|
||||
assert.isNotOk(result, "verifyProof did not return false for proof of invalid length");
|
||||
})
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
import expectThrow from './helpers/expectThrow';
|
||||
var MintableToken = artifacts.require('../contracts/Tokens/MintableToken.sol');
|
||||
|
||||
contract('Mintable', function(accounts) {
|
||||
@ -23,13 +23,24 @@ contract('Mintable', function(accounts) {
|
||||
});
|
||||
|
||||
it('should mint a given amount of tokens to a given address', async function() {
|
||||
await token.mint(accounts[0], 100);
|
||||
|
||||
const result = await token.mint(accounts[0], 100);
|
||||
assert.equal(result.logs[0].event, 'Mint');
|
||||
assert.equal(result.logs[0].args.to.valueOf(), accounts[0]);
|
||||
assert.equal(result.logs[0].args.amount.valueOf(), 100);
|
||||
assert.equal(result.logs[1].event, 'Transfer');
|
||||
assert.equal(result.logs[1].args.from.valueOf(), 0x0);
|
||||
|
||||
let balance0 = await token.balanceOf(accounts[0]);
|
||||
assert(balance0, 100);
|
||||
|
||||
|
||||
let totalSupply = await token.totalSupply();
|
||||
assert(totalSupply, 100);
|
||||
})
|
||||
|
||||
it('should fail to mint after call to finishMinting', async function () {
|
||||
await token.finishMinting();
|
||||
assert.equal(await token.mintingFinished(), true);
|
||||
await expectThrow(token.mint(accounts[0], 100));
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
@ -1,119 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var MultisigWalletMock = artifacts.require('./helpers/MultisigWalletMock.sol');
|
||||
require('./helpers/transactionMined.js');
|
||||
|
||||
contract('MultisigWallet', function(accounts) {
|
||||
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')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
//Get balances of owner and wallet after wallet creation.
|
||||
let ownerBalance = web3.eth.getBalance(accounts[0]);
|
||||
let walletBalance = web3.eth.getBalance(wallet.address);
|
||||
let hash = 1234;
|
||||
|
||||
//Call destroy function from two different owner accounts, satisfying owners required
|
||||
await wallet.destroy(accounts[0], {data: hash});
|
||||
let txnHash = await wallet.destroy(accounts[0], {from: accounts[1], data: hash});
|
||||
|
||||
//Get balances of owner and wallet after destroy function is complete, compare with previous values
|
||||
let newOwnerBalance = web3.eth.getBalance(accounts[0]);
|
||||
let newWalletBalance = web3.eth.getBalance(wallet.address);
|
||||
|
||||
assert.isTrue(newOwnerBalance > ownerBalance);
|
||||
assert.isTrue(newWalletBalance < walletBalance);
|
||||
});
|
||||
|
||||
it('should execute transaction if below daily limit', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
let accountBalance = web3.eth.getBalance(accounts[2]);
|
||||
let hash = 1234;
|
||||
|
||||
//Owner account0 commands wallet to send 9 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 9, hash);
|
||||
|
||||
//Balance of account2 should have increased
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.isTrue(newAccountBalance > accountBalance);
|
||||
});
|
||||
|
||||
it('should prevent execution of transaction if above daily limit', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
let accountBalance = web3.eth.getBalance(accounts[2]);
|
||||
let hash = 1234;
|
||||
|
||||
//Owner account0 commands wallet to send 9 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 9, hash);
|
||||
|
||||
//Balance of account2 should have increased
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.isTrue(newAccountBalance > accountBalance);
|
||||
|
||||
accountBalance = newAccountBalance;
|
||||
hash = 4567;
|
||||
|
||||
//Owner account0 commands wallet to send 2 more wei to account2, going over the daily limit of 10
|
||||
txnHash = await wallet.execute(accounts[2], 2, hash);
|
||||
|
||||
//Balance of account2 should not change
|
||||
newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.equal(newAccountBalance.toString(), accountBalance.toString());
|
||||
});
|
||||
|
||||
it('should execute transaction if above daily limit and enough owners approve', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
let accountBalance = web3.eth.getBalance(accounts[2]);
|
||||
let hash = 1234;
|
||||
|
||||
//Owner account0 commands wallet to send 11 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 11, hash);
|
||||
|
||||
//Balance of account2 should not change
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.equal(newAccountBalance.toString(), accountBalance.toString());
|
||||
|
||||
accountBalance = newAccountBalance;
|
||||
|
||||
//Owner account1 commands wallet to send 11 wei to account2
|
||||
txnHash = await wallet.execute(accounts[2], 2, hash);
|
||||
|
||||
//Balance of account2 should change
|
||||
newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.isTrue(newAccountBalance > accountBalance);
|
||||
});
|
||||
|
||||
});
|
||||
@ -29,6 +29,7 @@ contract('Ownable', function(accounts) {
|
||||
assert.isTrue(owner !== other);
|
||||
try {
|
||||
await ownable.transferOwnership(other, {from: other});
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
@ -36,10 +37,12 @@ contract('Ownable', function(accounts) {
|
||||
|
||||
it('should guard ownership against stuck state', async function() {
|
||||
let originalOwner = await ownable.owner();
|
||||
await ownable.transferOwnership(null, {from: originalOwner});
|
||||
let newOwner = await ownable.owner();
|
||||
|
||||
assert.equal(originalOwner, newOwner);
|
||||
try {
|
||||
await ownable.transferOwnership(null, {from: originalOwner});
|
||||
assert.fail();
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -23,6 +23,7 @@ contract('Pausable', function(accounts) {
|
||||
|
||||
try {
|
||||
await Pausable.normalProcess();
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
@ -35,10 +36,10 @@ contract('Pausable', function(accounts) {
|
||||
let Pausable = await PausableMock.new();
|
||||
try {
|
||||
await Pausable.drasticMeasure();
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
|
||||
const drasticMeasureTaken = await Pausable.drasticMeasureTaken();
|
||||
assert.isFalse(drasticMeasureTaken);
|
||||
});
|
||||
@ -62,4 +63,19 @@ contract('Pausable', function(accounts) {
|
||||
assert.equal(count0, 1);
|
||||
});
|
||||
|
||||
it('should prevent drastic measure after pause is over', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
await Pausable.unpause();
|
||||
try {
|
||||
await Pausable.drasticMeasure();
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
|
||||
const drasticMeasureTaken = await Pausable.drasticMeasureTaken();
|
||||
assert.isFalse(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -53,21 +53,21 @@ contract('PausableToken', function(accounts) {
|
||||
|
||||
it('should throw an error trying to transfer while transactions are paused', async function() {
|
||||
await token.pause();
|
||||
try {
|
||||
try {
|
||||
await token.transfer(accounts[1], 100);
|
||||
assert.fail('should have thrown before');
|
||||
} catch (error) {
|
||||
return assertJump(error);
|
||||
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 {
|
||||
try {
|
||||
await token.transferFrom(accounts[0], accounts[1], 100);
|
||||
assert.fail('should have thrown before');
|
||||
} catch (error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
var PullPaymentMock = artifacts.require("./helpers/PullPaymentMock.sol");
|
||||
|
||||
contract('PullPayment', function(accounts) {
|
||||
let ppce;
|
||||
let amount = 17*1e18;
|
||||
|
||||
beforeEach(async function() {
|
||||
ppce = await PullPaymentMock.new({value: amount});
|
||||
});
|
||||
|
||||
it("can't call asyncSend externally", async function() {
|
||||
let ppc = await PullPaymentMock.new();
|
||||
assert.isUndefined(ppc.asyncSend);
|
||||
assert.isUndefined(ppce.asyncSend);
|
||||
});
|
||||
|
||||
it("can record an async payment correctly", async function() {
|
||||
let AMOUNT = 100;
|
||||
let ppce = await PullPaymentMock.new();
|
||||
let callSend = await ppce.callSend(accounts[0], AMOUNT);
|
||||
let paymentsToAccount0 = await ppce.payments(accounts[0]);
|
||||
let totalPayments = await ppce.totalPayments();
|
||||
@ -19,7 +23,6 @@ contract('PullPayment', function(accounts) {
|
||||
});
|
||||
|
||||
it("can add multiple balances on one account", async function() {
|
||||
let ppce = await PullPaymentMock.new();
|
||||
let call1 = await ppce.callSend(accounts[0], 200);
|
||||
let call2 = await ppce.callSend(accounts[0], 300);
|
||||
let paymentsToAccount0 = await ppce.payments(accounts[0]);
|
||||
@ -30,7 +33,6 @@ contract('PullPayment', function(accounts) {
|
||||
});
|
||||
|
||||
it("can add balances on multiple accounts", async function() {
|
||||
let ppce = await PullPaymentMock.new();
|
||||
let call1 = await ppce.callSend(accounts[0], 200);
|
||||
let call2 = await ppce.callSend(accounts[1], 300);
|
||||
|
||||
@ -45,18 +47,16 @@ contract('PullPayment', function(accounts) {
|
||||
});
|
||||
|
||||
it("can withdraw payment", async function() {
|
||||
let AMOUNT = 17*1e18;
|
||||
let payee = accounts[1];
|
||||
let initialBalance = web3.eth.getBalance(payee);
|
||||
|
||||
let ppce = await PullPaymentMock.new({value: AMOUNT});
|
||||
let call1 = await ppce.callSend(payee, AMOUNT);
|
||||
let call1 = await ppce.callSend(payee, amount);
|
||||
|
||||
let payment1 = await ppce.payments(payee);
|
||||
assert.equal(payment1, AMOUNT);
|
||||
assert.equal(payment1, amount);
|
||||
|
||||
let totalPayments = await ppce.totalPayments();
|
||||
assert.equal(totalPayments, AMOUNT);
|
||||
assert.equal(totalPayments, amount);
|
||||
|
||||
let withdraw = await ppce.withdrawPayments({from: payee});
|
||||
let payment2 = await ppce.payments(payee);
|
||||
@ -66,7 +66,7 @@ contract('PullPayment', function(accounts) {
|
||||
assert.equal(totalPayments, 0);
|
||||
|
||||
let balance = web3.eth.getBalance(payee);
|
||||
assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
|
||||
assert(Math.abs(balance-initialBalance-amount) < 1e16);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
61
test/RefundVault.js
Normal file
61
test/RefundVault.js
Normal file
@ -0,0 +1,61 @@
|
||||
const BigNumber = web3.BigNumber
|
||||
|
||||
require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should()
|
||||
|
||||
import ether from './helpers/ether'
|
||||
import EVMThrow from './helpers/EVMThrow'
|
||||
|
||||
const RefundVault = artifacts.require('RefundVault')
|
||||
|
||||
contract('RefundVault', function ([_, owner, wallet, investor]) {
|
||||
|
||||
const value = ether(42)
|
||||
|
||||
beforeEach(async function () {
|
||||
this.vault = await RefundVault.new(wallet, {from: owner})
|
||||
})
|
||||
|
||||
it('should accept contributions', async function () {
|
||||
await this.vault.deposit(investor, {value, from: owner}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should not refund contribution during active state', async function () {
|
||||
await this.vault.deposit(investor, {value, from: owner})
|
||||
await this.vault.refund(investor).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('only owner can enter refund mode', async function () {
|
||||
await this.vault.enableRefunds({from: _}).should.be.rejectedWith(EVMThrow)
|
||||
await this.vault.enableRefunds({from: owner}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should refund contribution after entering refund mode', async function () {
|
||||
await this.vault.deposit(investor, {value, from: owner})
|
||||
await this.vault.enableRefunds({from: owner})
|
||||
|
||||
const pre = web3.eth.getBalance(investor)
|
||||
await this.vault.refund(investor)
|
||||
const post = web3.eth.getBalance(investor)
|
||||
|
||||
post.minus(pre).should.be.bignumber.equal(value)
|
||||
})
|
||||
|
||||
it('only owner can close', async function () {
|
||||
await this.vault.close({from: _}).should.be.rejectedWith(EVMThrow)
|
||||
await this.vault.close({from: owner}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should forward funds to wallet after closing', async function () {
|
||||
await this.vault.deposit(investor, {value, from: owner})
|
||||
|
||||
const pre = web3.eth.getBalance(wallet)
|
||||
await this.vault.close({from: owner})
|
||||
const post = web3.eth.getBalance(wallet)
|
||||
|
||||
post.minus(pre).should.be.bignumber.equal(value)
|
||||
})
|
||||
|
||||
})
|
||||
83
test/RefundableCrowdsale.js
Normal file
83
test/RefundableCrowdsale.js
Normal file
@ -0,0 +1,83 @@
|
||||
import ether from './helpers/ether'
|
||||
import {advanceBlock} from './helpers/advanceToBlock'
|
||||
import {increaseTimeTo, duration} from './helpers/increaseTime'
|
||||
import latestTime from './helpers/latestTime'
|
||||
import EVMThrow from './helpers/EVMThrow'
|
||||
|
||||
const BigNumber = web3.BigNumber
|
||||
|
||||
require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should()
|
||||
|
||||
const RefundableCrowdsale = artifacts.require('./helpers/RefundableCrowdsaleImpl.sol')
|
||||
|
||||
contract('RefundableCrowdsale', function ([_, owner, wallet, investor]) {
|
||||
|
||||
const rate = new BigNumber(1000)
|
||||
const goal = ether(800)
|
||||
const lessThanGoal = ether(750)
|
||||
|
||||
before(async function() {
|
||||
//Advance to the next block to correctly read time in the solidity "now" function interpreted by testrpc
|
||||
await advanceBlock()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.startTime = latestTime() + duration.weeks(1)
|
||||
this.endTime = this.startTime + duration.weeks(1)
|
||||
this.afterEndTime = this.endTime + duration.seconds(1)
|
||||
|
||||
this.crowdsale = await RefundableCrowdsale.new(this.startTime, this.endTime, rate, wallet, goal, {from: owner})
|
||||
})
|
||||
|
||||
describe('creating a valid crowdsale', function () {
|
||||
|
||||
it('should fail with zero goal', async function () {
|
||||
await RefundableCrowdsale.new(this.startTime, this.endTime, rate, wallet, 0, {from: owner}).should.be.rejectedWith(EVMThrow);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it('should deny refunds before end', async function () {
|
||||
await this.crowdsale.claimRefund({from: investor}).should.be.rejectedWith(EVMThrow)
|
||||
await increaseTimeTo(this.startTime)
|
||||
await this.crowdsale.claimRefund({from: investor}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('should deny refunds after end if goal was reached', async function () {
|
||||
await increaseTimeTo(this.startTime)
|
||||
await this.crowdsale.sendTransaction({value: goal, from: investor})
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
await this.crowdsale.claimRefund({from: investor}).should.be.rejectedWith(EVMThrow)
|
||||
})
|
||||
|
||||
it('should allow refunds after end if goal was not reached', async function () {
|
||||
await increaseTimeTo(this.startTime)
|
||||
await this.crowdsale.sendTransaction({value: lessThanGoal, from: investor})
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
|
||||
await this.crowdsale.finalize({from: owner})
|
||||
|
||||
const pre = web3.eth.getBalance(investor)
|
||||
await this.crowdsale.claimRefund({from: investor, gasPrice: 0})
|
||||
.should.be.fulfilled
|
||||
const post = web3.eth.getBalance(investor)
|
||||
|
||||
post.minus(pre).should.be.bignumber.equal(lessThanGoal)
|
||||
})
|
||||
|
||||
it('should forward funds to wallet after end if goal was reached', async function () {
|
||||
await increaseTimeTo(this.startTime)
|
||||
await this.crowdsale.sendTransaction({value: goal, from: investor})
|
||||
await increaseTimeTo(this.afterEndTime)
|
||||
|
||||
const pre = web3.eth.getBalance(wallet)
|
||||
await this.crowdsale.finalize({from: owner})
|
||||
const post = web3.eth.getBalance(wallet)
|
||||
|
||||
post.minus(pre).should.be.bignumber.equal(goal)
|
||||
})
|
||||
|
||||
})
|
||||
38
test/SafeERC20.js
Normal file
38
test/SafeERC20.js
Normal file
@ -0,0 +1,38 @@
|
||||
import EVMThrow from './helpers/EVMThrow';
|
||||
|
||||
require('chai')
|
||||
.use(require('chai-as-promised'))
|
||||
.should();
|
||||
|
||||
const SafeERC20Helper = artifacts.require('./helpers/SafeERC20Helper.sol');
|
||||
|
||||
contract('SafeERC20', function () {
|
||||
|
||||
beforeEach(async function () {
|
||||
this.helper = await SafeERC20Helper.new();
|
||||
});
|
||||
|
||||
it('should throw on failed transfer', async function () {
|
||||
await this.helper.doFailingTransfer().should.be.rejectedWith(EVMThrow);
|
||||
});
|
||||
|
||||
it('should throw on failed transferFrom', async function () {
|
||||
await this.helper.doFailingTransferFrom().should.be.rejectedWith(EVMThrow);
|
||||
});
|
||||
|
||||
it('should throw on failed approve', async function () {
|
||||
await this.helper.doFailingApprove().should.be.rejectedWith(EVMThrow);
|
||||
});
|
||||
|
||||
it('should not throw on succeeding transfer', async function () {
|
||||
await this.helper.doSucceedingTransfer().should.be.fulfilled;
|
||||
});
|
||||
|
||||
it('should not throw on succeeding transferFrom', async function () {
|
||||
await this.helper.doSucceedingTransferFrom().should.be.fulfilled;
|
||||
});
|
||||
|
||||
it('should not throw on succeeding approve', async function () {
|
||||
await this.helper.doSucceedingApprove().should.be.fulfilled;
|
||||
});
|
||||
});
|
||||
@ -40,10 +40,10 @@ contract('SafeMath', function(accounts) {
|
||||
let b = 5678;
|
||||
try {
|
||||
let subtract = await safeMath.subtract(a, b);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should throw an error on addition overflow", async function() {
|
||||
@ -51,10 +51,10 @@ contract('SafeMath', function(accounts) {
|
||||
let b = 1;
|
||||
try {
|
||||
let add = await safeMath.add(a, b);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should throw an error on multiplication overflow", async function() {
|
||||
@ -62,10 +62,10 @@ contract('SafeMath', function(accounts) {
|
||||
let b = 2;
|
||||
try {
|
||||
let multiply = await safeMath.multiply(a, b);
|
||||
assert.fail('should have thrown before');
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user