Compare commits

..

266 Commits

Author SHA1 Message Date
e2fdf09e9b v1.2.0 2017-07-18 15:07:10 -03:00
4f44427966 Added function helper, few changes on ECRecover lib 2017-07-18 11:31:30 -03:00
f8c0fab5d8 Fixed tests and few changes on contract comments 2017-07-18 11:31:30 -03:00
478d1b1a93 Removed safeRecover, using ecrecover method instead, recovering signature from entire hash 2017-07-18 11:31:30 -03:00
a68eaa4e2d Added NatSpec documentation on ECRecovery contract 2017-07-18 11:31:30 -03:00
68c2bcb9dd Removed .only on ECRecovery tests 2017-07-18 11:31:30 -03:00
635c04378d Removed ECRecovery mock contract, ECRecovery funcions are public constants 2017-07-18 11:31:30 -03:00
227c7aae0f added check to ret boolean on ECRecovery 2017-07-18 11:31:30 -03:00
641b751385 Fix ECRecovery contract indentation
Fix ident on github

Fix ident github

Fix ident on github
2017-07-18 11:31:30 -03:00
4201d50dcf ECRecovery library added with test and docs 2017-07-18 11:31:30 -03:00
b05fb9c200 Update ownable.rst
Change the name of the function in the docs to the actual name in the contract source To prevent misunderstandings.
2017-07-17 21:37:59 -03:00
4f34d41523 Fixing typo. 2017-07-17 21:37:32 -03:00
91fc295a42 Updated reference to Ownable contract. 2017-07-17 21:37:32 -03:00
67d3a759a7 leave weiAmount after PR comments 2017-07-17 21:36:09 -03:00
950b6a5e56 sanity check for RefundableCrowdsale: goal > 0 2017-07-17 21:36:09 -03:00
db40fd314a sanity check for CappedCrowdsale: cap > 0 2017-07-17 21:36:09 -03:00
6035bd522b simplify crowdsale weiRaised update 2017-07-17 21:36:09 -03:00
b8884687df fix off by one error in VestedToken 2017-07-17 21:34:38 -03:00
18581f138f converted if() throw convention to require()/assert()/revert() 2017-07-17 15:35:09 -03:00
f3867f8477 Merge pull request #315 from joeykrug/patch-1
Added Constant to SafeMath functions as they don't modify storage
2017-07-15 15:52:36 -03:00
353285e5d9 Added Constant to SafeMath functions as they don't modify storage 2017-07-15 11:37:35 -07:00
a56bcbf8fd Correctly call super.transferableTokens
Fix #307
2017-07-14 18:17:09 -03:00
81297fac88 Merge pull request #308 from frangio/fix/erc20
Make ERC20 and token contracts conform to standard
2017-07-13 17:28:50 -03:00
6331dd125d fix ERC20 to conform to standard 2017-07-13 12:05:45 -03:00
f8790c1ed7 Merge pull request #304 from cgewecke/fix-coveralls
Fix coverage
2017-07-10 23:24:45 -03:00
5afbdf00db Fix coverage 2017-07-10 15:45:12 -07:00
322a9598ab Merge pull request #289 from ezynda3/master
update according to changes in #288
2017-07-06 11:07:13 -03:00
ba86e8e095 Merge pull request #296 from frangio/fix/293-tokentimelock-claim
Add TokenTimelock#release function that anyone can call
2017-07-05 11:47:15 -03:00
5e423bc353 add release function, deprecate claim 2017-07-05 11:24:59 -03:00
f2c2c01f8e Merge branch 'master' of github.com:OpenZeppelin/zeppelin-solidity 2017-07-02 15:50:40 -07:00
7434b3d6d2 v1.1.0 2017-07-02 18:53:32 -03:00
63b5fc5971 Merge pull request #290 from frangio/fix/safemath-references
Fix references to SafeMath in crowdsale contracts
2017-07-02 18:50:29 -03:00
f507a0ea29 fix references to SafeMath in crowdsale contracts 2017-07-02 18:39:51 -03:00
c254d8703d update according to changes in #288 2017-07-02 14:33:15 -07:00
981ec2dbdc v1.1.0 2017-07-02 18:27:18 -03:00
d5a75362ec Merge pull request #288 from frangio/refactor/math
Move SafeMath and create Math library for assorted operations
2017-07-02 18:13:47 -03:00
e748c3ea36 Merge pull request #283 from OpenZeppelin/crowdsale
Add Crowdsale contracts
2017-07-02 17:59:19 -03:00
421ed4f8ab fix references to max and min functions 2017-07-02 17:56:34 -03:00
0b1f08043b change Math to a library instead of a contract 2017-07-02 17:56:10 -03:00
759f8de81f fix import directories 2017-07-02 17:52:44 -03:00
96b550b722 Merge pull request #287 from jakub-wojciechowski/test/day-limit
Added new test for a day passed scenario to the DayLimit contract
2017-07-02 17:37:28 -03:00
b961eea89d fix claimRefund precondition 2017-07-02 17:10:11 -03:00
d2dd6e40b6 added new test for a day passed scenario to the DayLimit contract 2017-07-02 21:06:58 +01:00
070bcbcdbd rename purchaseValid to validPurchase 2017-07-02 17:05:06 -03:00
46fe7ee76d guard against beneficiary == 0x0 2017-07-02 16:51:26 -03:00
ba383a6d20 refactor max and min out of SafeMath 2017-07-02 13:01:55 -03:00
a66f5f8f03 format SafeMath documentation as NatSpec 2017-07-02 13:00:23 -03:00
262b7dd7dd Merge pull request #285 from frangio/feature/token-timelock
Add a TokenTimelock contract
2017-06-30 23:14:24 -03:00
5d847ed4d9 tweak TokenTimelock documentation 2017-06-30 21:28:45 -03:00
b3ca0c73b3 add a TokenTimelock contract 2017-06-30 21:25:59 -03:00
d6a45ef14e Merge pull request #282 from jasonbellamy/fix/standard-contract-docs-typo
fix(token): typo in documentation
2017-06-30 18:31:53 -03:00
54d74b1c26 rename canBuy to purchaseValid 2017-06-30 17:54:26 -03:00
657c56c650 remove unecessary whitespace 2017-06-30 17:54:07 -03:00
50a903a62d use npm test script for travis 2017-06-30 17:40:34 -03:00
0791e6639a add extra arguments to test command for development testing 2017-06-30 15:52:11 -03:00
59e9609926 remove CrowdsaleToken
it is superseded by the new crowdsale contracts
2017-06-30 15:50:47 -03:00
fadb2cf47e add tests for crowdsale contracts 2017-06-30 15:50:47 -03:00
4d55d8fa8e give more balance to testrpc accounts
includes fix to MultisigWallet test because bigger balances result in
floating point errors (see #204)
2017-06-30 15:50:47 -03:00
e31abffcb6 fix(token): typo in documentation
Implemantation -> Implementation
2017-06-30 14:40:15 -04:00
cd47fbe953 Improve Crowdsale#rate documentation 2017-06-30 11:23:28 -03:00
42c6392533 fix(token): typo in documentation 2017-06-30 00:41:12 -04:00
3430c7a289 Merge pull request #280 from rstormsf/fix/revert_inheritance_order
change inheritance order for StandardToken and PausableToken
2017-06-29 10:40:43 -03:00
daf1784be4 change inheritance order for StandardToken and PausableToken 2017-06-28 22:15:51 -07:00
de92a02127 Add Crowdsale contracts 2017-06-27 18:34:04 -03:00
82ca385725 Merge pull request #277 from frangio/fix-261
Remove all checks for short address attack
2017-06-26 15:32:56 -03:00
e33d9bb41b remove all checks for short address attack (fixes #261) 2017-06-26 15:11:54 -03:00
79b3a1b1a2 Merge pull request #273 from jooray/patch-1
Fix typo in StandardToken allowance documentation
2017-06-22 13:15:28 -03:00
c6a3c29eb4 Merge pull request #272 from rstormsf/Removal-of-install-script
Removes install script
2017-06-22 10:34:07 -03:00
dd34d8df0f Fix typo in StandardToken allowance documentation 2017-06-22 15:33:34 +02:00
80d052d43f Fixes #253 2017-06-21 17:34:43 -07:00
710f77dfe1 Merge pull request #269 from rstormsf/uint256
change uint to uint256
2017-06-20 12:23:52 -03:00
b1e504d6c6 change uint to uint256 2017-06-19 18:55:09 -07:00
7deaee04c8 Merge pull request #263 from DaveAppleton/master
assert is now built in
2017-06-17 16:36:56 -03:00
2020d56ba4 assert is now built in 2017-06-17 11:10:30 +08:00
a49fe53fa1 Merge pull request #258 from mbrix/master
fixed typo
2017-06-15 13:52:34 -03:00
403e7336a9 fixed typo 2017-06-14 16:31:12 -04:00
fea2a8383c Merge pull request #256 from DavidKnott/refactor-mintable-token
Refactor MintableToken Contract
2017-06-14 16:12:23 -03:00
ffe464a43e Refactors the Mintable Token contract 2017-06-14 10:38:32 -06:00
3fbcb1b282 Merge pull request #250 from jdkanani/yarn-setup
Add yarn support and use it for Travis CI
2017-06-12 12:11:14 -03:00
28b2a0c881 Move npm to yarn 2017-06-11 12:50:26 +05:30
7b9c1429d9 v1.0.7 2017-06-09 19:31:09 -03:00
d53ddd4a95 Merge pull request #242 from DavidKnott/upgrade-truffle-and-solidity
Upgrade to Truffle version 3.2.2 and Solidity version 0.4.11
2017-06-07 15:04:57 -03:00
6ede3d9001 Upgrade to Truffle version 3.2.2 and Solidity version 0.4.11 2017-06-06 21:41:54 -06:00
c3cb0dcf80 Merge pull request #238 from DaveAppleton/master
remove redundant return
2017-06-06 11:02:46 -03:00
f214dccab9 removed redundant returns 2017-06-06 17:10:55 +08:00
c0cce27703 remove redundant return 2017-06-02 00:27:16 +08:00
2eded7a337 Merge pull request #237 from maraoz/coveralls
Add coveralls
2017-05-31 20:30:50 -03:00
41a6f1cd84 add coveralls 2017-05-31 17:15:50 -03:00
e1eed257f1 v1.0.6 2017-05-29 18:29:48 -03:00
2bcb54b3cb Merge pull request #231 from maraoz/fix-vested-token
Implement VestedToken bug mitigation #3
2017-05-29 18:27:44 -03:00
7100e30bed Implement VestedToken bug mitigation #3 2017-05-29 17:52:19 -03:00
7f6921e787 Merge pull request #222 from aragon/refactor
[VestedToken] Add revokability and burnablity options and general refactor and optimizations
2017-05-29 17:17:54 -03:00
73ec224c93 Merge pull request #2 from maraoz/aragon-refactor
rebase to master
2017-05-29 22:05:05 +02:00
a62621eb59 merge @izqui PR with new docs changes 2017-05-29 16:19:03 -03:00
84843d42da Merge pull request #225 from DavidKnott/fix-natspec-typos
Edit natspec documentation typos
2017-05-25 18:44:01 -03:00
d08185fc3e Edit natspec documentation typos 2017-05-26 00:29:15 +03:00
efe09575e3 Merge pull request #210 from maurelian/master
Add natspec comments to contracts
2017-05-24 18:33:28 -03:00
c4796bb918 Fix comments again 2017-05-24 17:27:05 -04:00
111f13f0ce Add doc comments to multisig wallet 2017-05-24 16:51:05 -04:00
d357cf3a2e Minor changes to doc comments for token and other contracts 2017-05-24 16:51:01 -04:00
83f2849446 Minor edits to doc commenst in lifecycle, ownership, payment contracts. 2017-05-24 16:49:43 -04:00
d85be4a866 Add natspec to vestedToken.sol 2017-05-24 16:47:25 -04:00
f6f91298f0 Add natspec to all remaing contracts, except for vestedToken.sol 2017-05-24 16:47:21 -04:00
1547922b61 Added natspec to the remaining ownership contracts and payment contracts 2017-05-24 16:45:45 -04:00
602c18b394 Applied change regarding the styling 2017-05-24 16:45:41 -04:00
b5d4120adb Added natsec for all lifecycle contracts + bounty.sol, DayLimit.sol and LimitBalance.sol 2017-05-24 16:43:14 -04:00
e851938199 Add natspec comments to four ownership contracts 2017-05-24 16:35:09 -04:00
48a2e11dfe merge 2017-05-22 18:08:03 -04:00
22f60eace0 Use libraries syntax sugar for revoking grants logic 2017-05-22 18:06:57 -04:00
b788f33c6a Merge pull request #218 from cgewecke/coverage
Add coverage
2017-05-21 23:40:18 -03:00
592548715f Add solidity-coverage 2017-05-21 19:19:50 -07:00
0983f0065d Merge pull request #223 from aragon/safe-math-div
[SafeMath] Remove unnecessary checks from safeDiv
2017-05-21 23:06:42 -03:00
c4f78a5dd5 Remove unnecessary checks from safeDiv 2017-05-21 16:41:18 -04:00
e032b86231 Merge with Zeppelin master 2017-05-21 16:14:06 -04:00
a6a2ee2bf1 Merge with master 2017-05-21 16:08:47 -04:00
929367f0ab Reorg code 2017-05-21 12:10:04 +02:00
5a6340395f Fix grant id parameter in NewTokenGrant event 2017-05-21 11:22:30 +02:00
f812433706 Small refactor and documentation 2017-05-21 11:18:24 +02:00
713b472253 Add tests for non-revokable and revokable/burnable cases 2017-05-21 11:17:24 +02:00
a1d2b673c6 Fix tests for revokable/non burn vesting calendar 2017-05-21 10:44:03 +02:00
5b2f0aa93d Implement revokable and burnsOnRevoke options 2017-05-21 10:38:56 +02:00
ecbfcbb302 Merge pull request #221 from TokenMarketNet/feat/transfer-from-mitigation
Protect transferFrom() against short hand attack.
2017-05-18 12:43:51 -03:00
964185dec3 Protect transferFrom against short hand attack. 2017-05-18 00:38:41 +03:00
5c491987f3 Merge pull request #219 from TokenMarketNet/feat/approve-mitigation
approve() race condition mitigation
2017-05-17 13:26:19 -06:00
e1cf602487 Fix variable naming. 2017-05-17 01:41:43 +03:00
a3446507ec Add fix for the approve() mitigation. 2017-05-17 01:33:13 +03:00
ff61c7d92d Merge pull request #212 from misteraverin/feature/destructible
Add destroy function, allowing send funds to recepient.
2017-05-15 07:19:45 -06:00
726593c0a2 Merge pull request #216 from maraoz/ethpm-npm
Revert to usig npm as preferred installation method
2017-05-12 09:12:24 -06:00
7826fddba7 revert to usig npm as preferred installation method 2017-05-12 12:10:28 -03:00
38a450460d Merge pull request #214 from maraoz/audit-report
Add external audit report
2017-05-11 13:17:53 -03:00
de7751aae2 Add external audit 2017-05-11 13:08:23 -03:00
cf7bc06856 Switch function name to destroyAndSend 2017-05-09 23:20:44 +03:00
7ac697be2c v1.0.5 2017-05-09 15:13:45 -03:00
4de772f5ed add destroy function, allowing send funds to recepient 2017-05-08 22:50:43 +03:00
f7a7fc3b06 Merge pull request #206 from DavidKnott/create-pausable-token
Create and test PausableToken Contract
2017-05-03 15:32:33 -03:00
b4b6029f66 Create and test PausableToken Contract 2017-04-29 01:08:14 +01:00
071040f3eb Merge pull request #203 from ORBAT/fix/bounty-claim-fail-test
Await event handling result in Bounty test
2017-04-24 11:06:38 -03:00
cd78c20e0e await event handling result in Bounty test. Fixes #202 2017-04-21 20:08:17 +03:00
453a198250 Merge pull request #184 from maraoz/safemath
Make SafeMath a library
2017-04-20 12:40:15 -03:00
7592122e4d fix indentation 2017-04-19 18:34:53 -03:00
609869f087 change safe* to * 2017-04-19 16:19:22 -03:00
4fad1505c7 Make SafeMath a library 2017-04-19 15:36:49 -03:00
5881429952 Merge pull request #195 from OpenZeppelin/maraoz-patch-1
Update README with new slack url
2017-04-19 13:49:29 -03:00
526ed43d61 Update README with new slack url 2017-04-19 13:49:09 -03:00
ab9591ba80 Merge pull request #193 from AugustoL/master
Added totalPayments uint on PullPayments contract
2017-04-18 11:42:48 -03:00
a8bcb0fcfe Added totalPayments uint on PullPayments contract 2017-04-14 14:30:31 -03:00
b420316783 Merge pull request #192 from tatiesmars/patch-1
Add MintFinished event to MintableToken
2017-04-13 16:18:07 -03:00
37c6782f95 Add MintFinished event to MintableToken #191 2017-04-13 14:23:41 +08:00
70a4395a97 Merge pull request #190 from lastperson/patch-1
Remove excessive condition from SafeMath.safeAdd()
2017-04-12 19:11:55 -03:00
31c05c4c7d Remove excessive condition from SafeMath.safeAdd()
There is no situation when `c>=a` will be `true` while `c>=b` will be `false`.
2017-04-12 15:22:52 +08:00
22018fd374 Merge pull request #188 from jdetychey/patch-3
fix for short address attack
2017-04-11 17:02:24 -03:00
d1f63f5c7e Merge pull request #185 from DavidKnott/create-token-with-owner-contract
Create and test MintableToken contract
2017-04-11 13:01:01 -03:00
5d75264f0f Fix for bigger payloads
as suggested by izqui in case a function calls transfer under the hood
2017-04-11 17:25:03 +02:00
a605f66972 Create and test MintableToken contract 2017-04-07 14:51:33 -06:00
0328250554 Merge pull request #187 from DavidKnott/change-killable-contract-to-destructible
Change killable to destructible and kill to destroy
2017-04-07 15:16:06 -03:00
70b5ffd928 Change killable to destructible and kill to destroy 2017-04-07 11:34:35 -06:00
d9b9ed227b fix for short address attack
as suggested by /u/izqui9 here https://www.reddit.com/r/ethereum/comments/63s917/worrysome_bug_exploit_with_erc20_token/dfwmhc3/
Attack description: 
https://blog.golemproject.net/how-to-find-10m-by-just-reading-blockchain-6ae9d39fcd95
2017-04-07 11:30:18 +02:00
14274f8677 Merge pull request #169 from maraoz/audit
Follow security audit recommendations
2017-04-05 19:04:11 -03:00
af604379d3 rebase conflict fixes 2017-04-05 18:30:14 -03:00
7d83a08658 8.2.3 removed ownable 2017-04-05 18:27:34 -03:00
2ccbfea8c5 8.1.2 add comment clarifying Migrations 2017-04-05 18:27:34 -03:00
fad287007b 7.2 add preconditions to Shareable contructor 2017-04-05 18:27:34 -03:00
cd9f820b85 fix syntax error 2017-04-05 18:27:34 -03:00
582166d378 7.1 pull payment safe math 2017-04-05 18:27:34 -03:00
ab9eecb104 6.1 fix stuck Ether in Crowdsale contract 2017-04-05 18:27:34 -03:00
52120a8c42 5.3 always throw on error 2017-04-05 18:27:34 -03:00
a1aa74f96d 5.2 all contracts now solidity ^0.4.8 2017-04-05 18:25:14 -03:00
6ae7ac97f6 Merge pull request #181 from roderik/patch-1
Shareable is not used in this contract
2017-04-05 17:25:28 -03:00
ba6c036628 Merge pull request #183 from AragonOne/fix-vesting
Fix vesting calculation logic
2017-04-05 12:54:23 -03:00
7a2fda9076 Improve VestedToken tests 2017-04-05 13:20:18 +02:00
41d2fde952 Fix vesting calculation logic 2017-04-05 13:13:17 +02:00
f0e7396619 Shareable is not used in this contract 2017-04-05 08:33:30 +02:00
6139500e97 Merge pull request #180 from frangio/fix-179-rename-transferable
Rename `TransferableToken` to `LimitedTransferToken`
2017-04-03 23:28:09 -03:00
0b88944b38 fix a few typos 2017-04-03 22:06:41 -03:00
61e33197b2 rename TransferableToken to LimitedTransferToken
fixes #179
2017-04-03 22:04:07 -03:00
025e7db765 Merge pull request #178 from frangio/fix-solidity-renamed-methods
Change sha3 and suicide methods to keccak256 and selfdestruct respectively
2017-04-03 12:03:16 -03:00
6f311e72b3 change sha3 and suicide methods to keccak256 and selfdestruct
see ethereum/solidity#1476
2017-04-03 11:31:56 -03:00
a344d42a00 Merge pull request #170 from Recmo/feature/no-owner
Prevent contracts from owning Ether, Tokens and other contracts.
2017-03-31 16:51:18 -03:00
f2159be68d Merge pull request #176 from frangio/shareable-owners-type
Change type of `owners` from uint[] to address[]
2017-03-31 13:33:09 -03:00
7f7238378c change type of owners from uint[] to address[]
fixes #175
2017-03-31 13:14:59 -03:00
1d2b989e8e Fix typo in comment 2017-03-31 09:39:29 +01:00
bc0f9b0ea5 Merge pull request #174 from demibrener/readme
Add Wings to collaborating organizations
2017-03-29 18:15:13 -03:00
a885dabc81 added Wings 2017-03-29 18:04:13 -03:00
6673da46b1 Merge pull request #173 from demibrener/readme
Name consistency: OpenZeppelin
2017-03-29 18:01:23 -03:00
2e9587477c Merge pull request #171 from Recmo/feature/reentrancy-guard
Add ReentrancyGuard
2017-03-28 15:10:30 -03:00
967ee13565 OpenZeppelin 2017-03-27 17:21:29 -03:00
a2bd1bb7f6 Add ReentrancyGuard 2017-03-24 11:01:06 +00:00
ac3b3652c3 Add NoOwner 2017-03-23 15:48:22 +00:00
166a1070e5 Add HasNoContracts 2017-03-23 15:43:52 +00:00
94d3c447b7 Add HasNoTokens 2017-03-23 15:22:48 +00:00
9ff82aecef Remove non-compiling payable default 2017-03-23 12:54:42 +00:00
d1af3ef1b3 Add HasNoEther 2017-03-23 12:23:27 +00:00
ffce7e3b08 Merge pull request #166 from AragonOne/transaferable
Add comments for Transferable Token
2017-03-20 15:43:56 -03:00
f896790ca3 Merge pull request #167 from maraoz/fix-travis
Fix travis build
2017-03-20 15:25:59 -03:00
960500a078 try to fix travis 2 2017-03-20 15:19:14 -03:00
ce25e2db98 try to fix travis 1 2017-03-20 11:58:56 -03:00
463ae97117 Merge pull request #160 from Recmo/feature/token-killable
Add TokenKillable
2017-03-20 11:27:19 -03:00
350ab09855 Add comments for TransferableToken 2017-03-20 10:16:48 +01:00
312220fe63 Merge pull request #162 from AragonOne/transaferable
Decouple transferable logic from VestedToken
2017-03-15 16:09:13 -03:00
72029b6847 Decouple transferable logic from VestedToken 2017-03-14 20:22:18 +01:00
525a5522b0 Add TokenKillable 2017-03-14 14:09:59 +00:00
9c5975a706 Merge pull request #158 from maraoz/update-readme
update README on new code distribution strategy (ethpm)
2017-03-09 18:13:27 -03:00
9006f89f9f update README on new code distribution 2017-03-09 18:02:41 -03:00
15169b245b v1.0.4 2017-03-09 15:04:14 -03:00
cc222c02a9 Merge pull request #156 from maraoz/ethpm
Add ethpm support
2017-03-09 15:00:09 -03:00
29ab824e5c revert TokenGrant instantiation to old style 2017-03-09 14:37:02 -03:00
8d828bc789 fix ethpm.json 2017-03-07 16:57:51 -03:00
f038cdc9c9 fix ethpm.json 2017-03-07 16:57:12 -03:00
72d31c2471 attempt to publish to ethpm 2017-03-07 16:56:13 -03:00
b31c92c27c ethpm integration 2017-03-07 16:17:48 -03:00
eb2cb5dd06 v1.0.3 2017-03-06 11:14:18 -03:00
52d0df7961 Merge pull request #155 from AragonOne/vested-transfer-from
[VestedToken] Check if tokens can be transferred on transferFrom
2017-03-06 11:11:29 -03:00
12aadbed51 Move check to transferFrom and add tests 2017-03-06 11:09:01 +01:00
23703280ee Check if tokens are transaferable on approve 2017-03-06 11:02:57 +01:00
5e50090da0 Merge pull request #151 from AragonOne/clarity
Add clarifying comment to standard token transfer from function
2017-03-01 17:16:15 -03:00
b9257a1092 Add clarifying comment to standard token transfer from function 2017-02-27 16:32:42 +01:00
8cd58303f9 v1.0.2 2017-02-23 12:56:07 -03:00
b134645637 Merge pull request #150 from maraoz/solidty-version
solidity version 0.4.8
2017-02-23 09:12:54 -03:00
d41cc437b3 Merge pull request #149 from JGcarv/feature/contactable
Added a contactable contract
2017-02-22 18:21:39 -03:00
26335e669e Added a contactable contract and some tests for it 2017-02-22 16:28:01 -03:00
52b66c744f solidity version 0.4.8 2017-02-21 19:29:03 -03:00
65c9711754 Merge pull request #147 from fabioberger/refactor/modifiers
Update modifiers to throw and update corresponding tests
2017-02-21 16:58:06 -03:00
3d6988cf90 Update modifiers so that they fail "loudly" by throwing errors rather then silently no-oping. Updated tests to remain compatible with these changes 2017-02-21 00:05:59 +01:00
c10a2cf15c Merge pull request #146 from maraoz/refactor
Refactor in preparation for audit
2017-02-17 19:30:07 -03:00
44082c029f fix travis tests 4 2017-02-17 19:23:56 -03:00
8ba0f008df fix travis tests 3 2017-02-17 19:17:42 -03:00
fcabd9957e fix travis tests 2 2017-02-17 19:02:44 -03:00
9f12e870a9 fix travis tests 2017-02-17 18:47:56 -03:00
d24a2ed9a8 fix compiler error 2017-02-17 18:32:24 -03:00
b1cb7b5975 implement solium recommendations 2017-02-17 18:31:16 -03:00
9ab997d788 fix travis test runner 2017-02-17 18:04:56 -03:00
90654cb2c9 add .jshintrc 2017-02-17 18:04:02 -03:00
9bd51db2b2 refactor tests for truffle 3 2017-02-17 18:03:52 -03:00
7e7193ae61 truffle 2=>3 2017-02-15 12:14:50 -03:00
a69333e2b8 migrate to truffle 3 2017-02-14 17:57:37 -03:00
1f35727442 Merge pull request #144 from demibrener/readme
Aragon new collaborating organization
2017-02-14 12:37:45 -03:00
cbc18e6dcc Aragon new collaborating organization 2017-02-14 12:07:11 -03:00
60b48b0235 Solium 2017-02-13 13:26:56 -03:00
f4624837a3 Merge pull request #140 from AragonOne/vesting
Vested Token implementation (Standard Token with vesting calendar)
2017-02-13 12:43:54 -03:00
0a5af4b8ac Change operations order for rounding. Make tests use blocktime as reference time rather than date. 2017-02-13 11:35:32 +01:00
74dfd3511f Merge pull request #1 from maraoz/vesting
Vesting test fixing help
2017-02-13 10:08:43 +01:00
ee56abcc8a remove console.logs from timer 2017-02-10 18:35:05 -03:00
b67f60929c make VestedToken test deterministic 2017-02-10 18:32:58 -03:00
0b71dcded2 Rename GrantableToken to VestedToken 2017-02-10 16:20:23 +01:00
f305382ef4 Remove grantTokens without vesting function 2017-02-10 16:18:36 +01:00
917b129517 Add safeDiv and min/max functions to SafeMath 2017-02-10 16:17:02 +01:00
abc646a95c lastTokenIsTransferableDate rename 2017-02-10 16:04:58 +01:00
1697518da8 Make vested stock calculation more testable 2017-02-10 15:28:20 +01:00
93e7984c61 Transferrable is now transferrable 2017-02-10 14:59:19 +01:00
8d9e12eda3 Add GrantableToken tests 2017-02-09 13:23:12 +01:00
fb0a96332c GrantableTokenMock 2017-02-08 15:27:39 +01:00
ba7a0f7649 Merge branch 'master' of https://github.com/OpenZeppelin/zeppelin-solidity into vesting 2017-02-08 15:26:15 +01:00
a72de719c9 Merge pull request #139 from eordano/patch-1
BasicToken.sol: Remove useless check
2017-02-08 11:13:38 -03:00
ec6e728c0f Grantable Token first version 2017-02-08 12:31:20 +01:00
70fbc7fe27 BasicToken.sol: Remove useless check
The condition this commit removes is already implemented by the call to `safeSub`
2017-02-07 19:45:32 -05:00
ced4e2da3f Merge pull request #137 from JGcarv/chore/rename-to-pausable
Renamed files and replaced occurencies of 'Stoppable' to 'Pausable'
2017-02-06 16:34:54 -03:00
ae73f1a564 Renamed files and replaced occurencies of 'Stoppable' to 'Pausable' 2017-01-30 23:30:43 -02:00
26127ee36a Merge pull request #136 from AragonOne/master
Make test cases that use assertJump fail if they dont fail
2017-01-23 15:48:38 -03:00
2f0471fff2 Make test cases that use assertJump fail if they dont fail 2017-01-23 19:18:41 +01:00
ad833fb40d Merge pull request #132 from OpenZeppelin/refactor
Refactor folders in preparation for external audit
2017-01-17 13:02:16 -03:00
a544f1517c Merge pull request #133 from jtakalai/patch-1
Fix typos
2017-01-17 11:24:17 -03:00
67ad6abdaa Fix typos
On unrelated note, why on earth check like in safeAdd? Is it actually an overflow check?
2017-01-17 12:29:54 +02:00
0dfb27ff5f fix tests 2017-01-16 17:42:26 -03:00
ca8f2f2362 rearrange folders 2017-01-16 17:23:28 -03:00
2de7fec6dc Merge pull request #127 from OpenZeppelin/contributing
Add tag comment on CONTRIBUTING.md
2017-01-09 16:27:56 -03:00
342e7bf9ff Add tag comment on CONTRIBUTING.md
Fixes #116
2017-01-09 16:27:32 -03:00
154 changed files with 11001 additions and 1364 deletions

1
.coveralls.yml Normal file
View File

@ -0,0 +1 @@
repo_token: AelGMv47LJ85e3KF1PhYBsjyduSjDmP0h

2
.gitignore vendored
View File

@ -3,3 +3,5 @@
node_modules/
build/
.DS_Store/
/coverage
coverage.json

48
.jshintrc Normal file
View File

@ -0,0 +1,48 @@
{
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
"browser": true, // Standard browser globals e.g. `window`, `document`.
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
"curly": true, // Require {} for every new block or scope.
"devel": false, // Allow development statements e.g. `console.log();`.
"eqeqeq": true, // Require triple equals i.e. `===`.
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
"freeze": true, // Forbid overwriting prototypes of native objects such as Array, Date and so on.
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"indent": 2, // Specify indentation spacing
"latedef": true, // Prohibit variable use before definition.
"newcap": false, // Require capitalization of all constructor functions e.g. `new F()`.
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
"noempty": true, // Prohibit use of empty blocks.
"nonew": true, // Prohibits the use of constructor functions for side-effects
"quotmark": "single", // Define quotes to string values.
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
"smarttabs": false, // Supress warnings about mixed tabs and spaces
"strict": true, // Require `use strict` pragma in every file.
"trailing": true, // Prohibit trailing whitespaces.
"undef": true, // Require all non-global variables be declared before they are used.
"unused": true, // Warn unused variables.
"maxparams": 4, // Maximum number of parameters for a function
"maxstatements": 15, // Maximum number of statements in a function
"maxcomplexity": 10, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity)
"maxdepth": 4, // Maximum depth of nested control structures
"maxlen": 120, // Maximum number of cols in a line
"multistr": true, // Allow use of multiline EOL escaping
"experimental": ["asyncawait", "asyncreqawait"],
"predef": [ // Extra globals.
"after",
"afterEach",
"before",
"beforeEach",
"define",
"describe",
"exports",
"it",
"web3",
"artifacts",
"contract",
"assert"
]
}

5
.solcover.js Normal file
View 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
.soliumignore Normal file
View File

@ -0,0 +1 @@
node_modules

22
.soliumrc.json Normal file
View File

@ -0,0 +1,22 @@
{
"custom-rules-filename": null,
"rules": {
"imports-on-top": true,
"variable-declarations": true,
"array-declarations": true,
"operator-whitespace": true,
"lbrace": true,
"mixedcase": false,
"camelcase": true,
"uppercase": true,
"no-with": true,
"no-empty-blocks": true,
"no-unused-vars": true,
"double-quotes": true,
"blank-lines": true,
"indentation": true,
"whitespace": true,
"deprecated-suicide": true,
"pragma-on-top": true
}
}

View File

@ -1,12 +1,12 @@
dist: trusty
sudo: false
sudo: required
group: beta
language: node_js
node_js:
- "6"
before_install:
- npm i -g ethereumjs-testrpc
- npm i -g truffle
cache:
yarn: true
script:
- testrpc&
- npm test
- yarn test
after_script:
- yarn run coveralls

View File

@ -102,4 +102,6 @@ Main contributors will review your code and possibly ask for changes before your
If you have any questions feel free to post them to
[github.com/OpenZeppelin/zeppelin-solidity/issues](https://github.com/OpenZeppelin/zeppelin-solidity/issues).
Finally, if you're looking to collaborate and want to find easy tasks to start, [look at the issues we marked as easy](https://github.com/OpenZeppelin/zeppelin-solidity/labels/easy).
Thanks for your time and code!

View File

@ -1,10 +1,11 @@
# Zeppelin Solidity
[![NPM Package](https://img.shields.io/npm/v/zeppelin-solidity.svg?style=flat-square)](https://www.npmjs.org/package/zeppelin-solidity)
[![Build Status](https://img.shields.io/travis/OpenZeppelin/zeppelin-solidity.svg?branch=master&style=flat-square)](https://travis-ci.org/OpenZeppelin/zeppelin-solidity)
[![Coverage Status](https://coveralls.io/repos/github/OpenZeppelin/zeppelin-solidity/badge.svg?branch=coveralls)](https://coveralls.io/github/OpenZeppelin/zeppelin-solidity?branch=coveralls)
Zeppelin is a library for writing secure [Smart Contracts](https://en.wikipedia.org/wiki/Smart_contract) on Ethereum.
OpenZeppelin is a library for writing secure [Smart Contracts](https://en.wikipedia.org/wiki/Smart_contract) on Ethereum.
With Zeppelin, you can build distributed applications, protocols and organizations:
With OpenZeppelin, you can build distributed applications, protocols and organizations:
- using common contract security patterns (See [Onward with Ethereum Smart Contract Security](https://medium.com/bitcorps-blog/onward-with-ethereum-smart-contract-security-97a827e47702#.y3kvdetbz))
- in the [Solidity language](http://solidity.readthedocs.io/en/develop/).
@ -12,66 +13,55 @@ With Zeppelin, you can build distributed applications, protocols and organizatio
## Getting Started
Zeppelin integrates with [Truffle](https://github.com/ConsenSys/truffle), an Ethereum development environment. Please install Truffle and initialize your project with `truffle init`.
OpenZeppelin integrates with [Truffle](https://github.com/ConsenSys/truffle), an Ethereum development environment. Please install Truffle and initialize your project with `truffle init`.
```sh
npm install -g truffle
npm install -g truffle@beta
mkdir myproject && cd myproject
truffle init
```
To install the Zeppelin library, run:
To install the OpenZeppelin library, run:
```sh
npm i zeppelin-solidity
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 `contracts/zeppelin` folder. You can use the contracts in the library like so:
After that, you'll get all the library's contracts in the `node_modules/zeppelin-solidity/contracts` folder. You can use the contracts in the library like so:
```js
import "./zeppelin/Ownable.sol";
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
contract MyContract is Ownable {
...
}
```
> NOTE: The current distribution channel is npm, which is not ideal. [We're looking into providing a better tool for code distribution](https://github.com/OpenZeppelin/zeppelin-solidity/issues/13), and ideas are welcome.
#### Truffle Beta Support
We also support Truffle Beta npm integration. If you're using Truffle Beta, the contracts in `node_modules` will be enough, so feel free to delete the copies at your `contracts` folder. If you're using Truffle Beta, you can use Zeppelin contracts like so:
```js
import "zeppelin-solidity/contracts/Ownable.sol";
contract MyContract is Ownable {
...
}
```
For more info see [the Truffle Beta package management tutorial](http://truffleframework.com/tutorials/package-management).
## Security
Zeppelin is meant to provide secure, tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problem you might experience.
OpenZeppelin is meant to provide secure, tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problem you might experience.
If you find a security issue, please email [security@openzeppelin.org](mailto:security@openzeppelin.org).
## Developer Resources
Building a distributed application, protocol or organization with Zeppelin?
Building a distributed application, protocol or organization with OpenZeppelin?
- Read documentation: http://zeppelin-solidity.readthedocs.io/en/latest/
- Ask for help and follow progress at: https://zeppelin-slackin.herokuapp.com/
- Ask for help and follow progress at: https://slack.openzeppelin.org/
Interested in contributing to Zeppelin?
Interested in contributing to OpenZeppelin?
- Framework proposal and roadmap: https://medium.com/zeppelin-blog/zeppelin-framework-proposal-and-development-roadmap-fdfa9a3a32ab#.iain47pak
- Issue tracker: https://github.com/OpenZeppelin/zeppelin-solidity/issues
- Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md
## Collaborating organizations and audits by Zeppelin
## Collaborating organizations and audits by OpenZeppelin
- [Golem](https://golem.network/)
- [Mediachain](https://golem.network/)
- [Mediachain](http://www.mediachain.io/)
- [Truffle](http://truffleframework.com/)
- [Firstblood](http://firstblood.io/)
- [Rootstock](http://www.rsk.co/)
@ -81,6 +71,8 @@ Interested in contributing to Zeppelin?
- [DemocracyEarth](http://democracy.earth/)
- [Signatura](https://signatura.co/)
- [Ether.camp](http://www.ether.camp/)
- [Aragon](https://aragon.one/)
- [Wings](https://wings.ai/)
among others...

290
audit/ZeppelinAudit.md Normal file
View File

@ -0,0 +1,290 @@
# OpenZeppelin Audit
March, 2017
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".
The contracts are hosted at:
https://github.com/OpenZeppelin/zeppelin-solidity
All the contracts in the "contracts" folder are in scope.
The git commit hash we evaluated is:
9c5975a706b076b7000e8179f8101e0c61024c87
# Disclaimer
The audit makes no statements or warrantees about utility of the code, safety of the code, suitability of the business model, regulatory regime for the business model, or any other statements about fitness of the contracts to purpose, or their bugfree status. The audit documentation is for discussion purposes only.
# Executive Summary
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.
We identified two critical errors and one moderate issue, and would not recommend this commit hash for public use until these bugs are remedied.
The repository includes a set of Truffle unit tests, a requirement and best practice for smart contracts like these; we recommend these be bulked up.
# Discussion
## 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.
> "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.
> "Rust: The language that makes you take the safety off before shooting yourself in the foot"
> -- (@mbrubeck)
We think much more could be done here, and recommend the OpenZeppelin team keep at this and keep focusing on the design goal of removing rope and adding safety.
## Solidity Version Updates Recommended
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:
- `assert(condition)`, which throws if the condition is false
- `revert()`, which rolls back without consuming all remaining gas.
- `address.transfer(value)`, which is like `send` but automatically propagates exceptions, and supports `.gas()`. See https://github.com/ethereum/solidity/issues/610 for more on this.
## Error Handling: Throw vs Return False
Solidity standards allow two ways to handle an error -- either calling `throw` or returning `false`. Both have benefits. In particular, a `throw` guarantees a complete wipe of the call stack (up to the preceding external call), whereas `false` allows a function to continue.
In general we prefer `throw` in our code audits, because it is simpler -- it's less for an engineer to keep track of. Returning `false` and using logic to check results can quickly become a poorly-tracked state machine, and this sort of complexity can cause errors.
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.
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.
Note that it may be impossible to use either one in all situations. For example, SafeMath functions pretty much have to throw upon failure, but ERC20 specifies returning booleans. Therefore we make no particular recommendations, but simply point out inconsistencies to consider.
# Critical Issues
## Stuck Ether in Crowdsale contract
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.
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.
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.
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.
# 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`.
`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.
It would be nice to see how many payments are pending. This would imply a bit of a rewrite; we recommend this contract get some design time, and that developers don't rely on it in its current state.
## 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.
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.
No sanity checks on the initial constructor's `required` argument are worrisome as well.
# Line by Line Comments
## Lifecycle
### Killable
Very simple, allows owner to call selfdestruct, sending funds to owner. No issues. However, note that `selfdestruct` should typically not be used; it is common that a developer may want to access data in a former contract, and they may not understand that `selfdestruct` limits access to the contract. We recommend better documentation about this dynamic, and an alternate function name for `kill` like `completelyDestroy` while `kill` would perhaps merely send funds to the owner.
Also note that a killable function allows the owner to take funds regardless of other logic. This may be desirable or undesirable depending on the circumstances. Perhaps `Killable` should have a different name as well.
### Migrations
I presume that the goal of this contract is to allow and annotate a migration to a new smart contract address. We are not clear here how this would be accomplished by the code; we'd like to review with the OpenZeppelin team.
### 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.
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)`.
## Ownership
### Ownable
Line 19: Modifier throws if doesn't meet condition, in contrast to some other inheritable modifiers (e.g. in Pausable) that use `if(bool){_;}`.
### Claimable
Inherits from Ownable but the existing owner sets a pendingOwner who has to claim ownership.
Line 17: Another modifier that throws.
### DelayedClaimable
Is there any reason to descend from Ownable directly, instead of just Claimable, which descends from Ownable? If not, descending from both just adds confusion.
### Contactable
Allows owner to set a public string of contract information. No issues.
### Shareable
This needs some work. Doesn't check if `_required <= len(_owners)` for instance, that would be a bummer. What if _required were like `MAX - 1`?
I have a general concern about the difference between `owners`, `_owners`, and `owner` in `Ownable.sol`. I recommend "Owners" be renamed. In general we do not recomment single character differences in variable names, although a preceding underscore is not uncommon in Solidity code.
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.
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.)
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.
## Payment
### PullPayment
Safe from reentrance attack since ether send is at the end, plus it uses `.send()` rather than `.call.value()`.
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.
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.
## Tokens
### ERC20
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.
This could be fixed by either (1) including the old limit as a parameter, so the update will fail if some gets spent, or (2) using the value parameter as a delta instead of replacement value.
This is not fixable while adhering to the current full ERC20 standard, though it would be possible to add a "secureApprove" function. The impact isn't extreme since at least you can only be attacked by addresses you approved. Also, users could mitigate this by always setting spending limits to zero and checking for spends, before setting the new limit.
Edcon slides:
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.
### BasicToken
Uses `SafeSub` and `SafeMath`, so transfer `throw`s instead of returning false. This complies with ERC20Basic but not the actual ERC20 standard.
### StandardToken
Implementation of full ERC20 token.
Transfer() and transferFrom() use SafeMath functions, which will cause them to throw instead of returning false. Not a security issue but departs from standard.
### SimpleToken
Sample instantiation of StandardToken. Note that in this sample, decimals is 18 and supply only 10,000, so the supply is a small fraction of a single nominal token.
### CrowdsaleToken
StandardToken which mints tokens at a fixed price when sent ether.
There's no provision for owner withdrawing the ether. As a sample for crowdsales it should be Ownable and allow the owner to withdraw ether, rather than stranding the ether in the contract.
Note: an alternative pattern is a mint() function which is only callable from a separate crowdsale contract, so any sort of rules can be added without modifying the token itself.
### 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.)
Line 64:
Delete not actually necessary since the value is overwritten in the next line anyway.
## Root level
### Bounty
Avoids potential race condition by having each researcher deploy a separate contract for attack; if a research manages to break his associated contract, other researchers can't immediately claim the reward, they have to reproduce the attack in their own contracts.
A developer could subvert this intent by implementing `deployContract()` to always return the same address. However, this would break the `researchers` mapping, updating the researcher address associated with the contract. This could be prevented by blocking rewrites in `researchers`.
### DayLimit
The modifier `limitedDaily` calls `underLimit`, which both checks that the spend is below the daily limit, and adds the input value to the daily spend. This is fine if all functions throw upon failure. However, not all OpenZeppelin functions do this; there are functions that returns false, and modifiers that wrap the function body in `if (bool) {_;}`. In these cases, `_value` will be added to `spentToday`, but ether may not actually be sent because other preconditions were not met. (However in the OpenZeppelin multisig this is not a problem.)
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:
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
No issues.
### MultisigWallet
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:
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:
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:
Throw here is good but note this function can fail either by returning false or by throwing.
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.
### SafeMath
Another interesting comment from the same Edcon presentation was that the overflow behavior of Solidity is undocumented, so in theory, source code that relies on it could break with a future revision.
However, compiled code should be fine, and in the unlikely event that the compiler is revised in this way, there should be plenty of warning. (But this is an argument for keeping overflow checks isolated in SafeMath.)
Aside from that small caveat, these are fine.

View File

@ -1,58 +1,72 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
import './PullPayment.sol';
import './Killable.sol';
import './payment/PullPayment.sol';
import './lifecycle/Destructible.sol';
/*
* Bounty
*
* This bounty will pay out to a researcher if they break invariant logic of the contract.
/**
* @title Bounty
* @dev This bounty will pay out to a researcher if they break invariant logic of the contract.
*/
contract Bounty is PullPayment, Killable {
Target target;
contract Bounty is PullPayment, Destructible {
bool public claimed;
mapping(address => address) public researchers;
event TargetCreated(address createdAddress);
/**
* @dev Fallback function allowing the contract to recieve 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
* msg.sender as a researcher
* @return A target contract
*/
function createTarget() returns(Target) {
target = Target(deployContract());
Target target = Target(deployContract());
researchers[target] = msg.sender;
TargetCreated(target);
return target;
}
/**
* @dev Internal function to deploy the target contract.
* @return A target contract address
*/
function deployContract() internal returns(address);
function checkInvariant() returns(bool){
return target.checkInvariant();
}
/**
* @dev Sends the contract funds to the researcher that proved the contract is broken.
* @param target contract
*/
function claim(Target target) {
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;
}
}
/*
* Target
*
* Your main contract should inherit from this class and implement the checkInvariant method. This is a function that should check everything your contract assumes to be true all the time. If this function returns false, it means your contract was broken in some way and is in an inconsistent state. This is what security researchers will try to acomplish when trying to get the bounty.
/**
* @title Target
* @dev Your main contract should inherit from this class and implement the checkInvariant method.
*/
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.
*/
function checkInvariant() returns(bool);
}

View File

@ -1,29 +0,0 @@
pragma solidity ^0.4.0;
import './Ownable.sol';
/*
* Claimable
*
* Extension for the Ownable contract, where the ownership needs to be claimed. This allows the new owner to accept the transfer.
*/
contract Claimable is Ownable {
address public pendingOwner;
modifier onlyPendingOwner() {
if (msg.sender == pendingOwner)
_;
}
function transferOwnership(address newOwner) onlyOwner {
pendingOwner = newOwner;
}
function claimOwnership() onlyPendingOwner {
owner = pendingOwner;
pendingOwner = 0x0;
}
}

View File

@ -1,59 +1,46 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
import './Shareable.sol';
/*
* DayLimit
*
* inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
* on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
* uses is specified in the modifier.
/**
* @title DayLimit
* @dev Base contract that enables methods to be protected by placing a linear limit (specifiable)
* on a particular resource per calendar day. Is multiowned to allow the limit to be altered.
*/
contract DayLimit {
// FIELDS
uint public dailyLimit;
uint public spentToday;
uint public lastDay;
uint256 public dailyLimit;
uint256 public spentToday;
uint256 public lastDay;
// MODIFIERS
// simple modifier for daily limit.
modifier limitedDaily(uint _value) {
if (underLimit(_value))
_;
}
// CONSTRUCTOR
// stores initial daily limit and records the present day's index.
function DayLimit(uint _limit) {
/**
* @dev Constructor that sets the passed value as a dailyLimit.
* @param _limit uint256 to represent the daily limit.
*/
function DayLimit(uint256 _limit) {
dailyLimit = _limit;
lastDay = today();
}
// METHODS
// (re)sets the daily limit. doesn't alter the amount already spent today.
function _setDailyLimit(uint _newLimit) internal {
/**
* @dev sets the daily limit. Does not alter the amount already spent today.
* @param _newLimit uint256 to represent the new limit.
*/
function _setDailyLimit(uint256 _newLimit) internal {
dailyLimit = _newLimit;
}
// resets the amount already spent today.
/**
* @dev Resets the amount already spent today.
*/
function _resetSpentToday() internal {
spentToday = 0;
}
// INTERNAL METHODS
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
// returns true. otherwise just returns false.
function underLimit(uint _value) internal returns (bool) {
/**
* @dev Checks to see if there is enough resource to spend today. If true, the resource may be expended.
* @param _value uint256 representing the amount of resource to spend.
* @return A boolean that is True if the resource was spended and false otherwise.
*/
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;
@ -68,8 +55,19 @@ contract DayLimit {
return false;
}
// determines today's index.
function today() private constant returns (uint) {
/**
* @dev Private function to determine today's index
* @return uint256 of today's index.
*/
function today() private constant returns (uint256) {
return now / 1 days;
}
/**
* @dev Simple modifier for daily limit.
*/
modifier limitedDaily(uint256 _value) {
require(underLimit(_value));
_;
}
}

View File

@ -1,30 +0,0 @@
pragma solidity ^0.4.4;
import './Ownable.sol';
import './Claimable.sol';
/*
* DelayedClaimable
* Extension for the Claimable contract, where the ownership needs to be claimed before/after certain block number
*/
contract DelayedClaimable is Ownable, Claimable {
uint public claimBeforeBlock;
uint public claimAfterBlock;
function setClaimBlocks(uint _claimBeforeBlock, uint _claimAfterBlock) onlyOwner {
if (_claimAfterBlock > claimBeforeBlock)
throw;
claimBeforeBlock = _claimBeforeBlock;
claimAfterBlock = _claimAfterBlock;
}
function claimOwnership() onlyPendingOwner {
if ((block.number > claimBeforeBlock) || (block.number < claimAfterBlock))
throw;
owner = pendingOwner;
pendingOwner = 0x0;
claimBeforeBlock = 0;
}
}

47
contracts/ECRecovery.sol Normal file
View 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) 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);
}
}
}

View File

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

View File

@ -1,27 +1,31 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
/**
* LimitBalance
* Simple contract to limit the balance of child contract.
* Note this doesn't prevent other contracts to send funds
* by using selfdestruct(address);
* See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account
* @title LimitBalance
* @dev Simple contract to limit the balance of child contract.
* @dev Note this doesn't prevent other contracts to send funds by using selfdestruct(address);
* @dev See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account
*/
contract LimitBalance {
uint public limit;
uint256 public limit;
function LimitBalance(uint _limit) {
/**
* @dev Constructor that sets the passed value as a limit.
* @param _limit uint256 to represent the limit.
*/
function LimitBalance(uint256 _limit) {
limit = _limit;
}
modifier limitedPayable() {
if (this.balance > limit) {
throw;
}
/**
* @dev Checks if limit was reached. Case true, it throws.
*/
modifier limitedPayable() {
require(this.balance <= limit);
_;
}
}

View File

@ -1,18 +0,0 @@
pragma solidity ^0.4.4;
import './Ownable.sol';
contract Migrations is Ownable {
uint public lastCompletedMigration;
function setCompleted(uint completed) onlyOwner {
lastCompletedMigration = completed;
}
function upgrade(address newAddress) onlyOwner {
Migrations upgraded = Migrations(newAddress);
upgraded.setCompleted(lastCompletedMigration);
}
}

View File

@ -1,67 +1,72 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
import "./Multisig.sol";
import "./Shareable.sol";
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);
* Usage:
* bytes32 h = Wallet(w).from(oneOwner).execute(to, value, data);
* Wallet(w).from(anotherOwner).confirm(h);
*/
contract MultisigWallet is Multisig, Shareable, DayLimit {
// TYPES
// Transaction structure to remember details of transaction lest it need be saved for a later call.
struct Transaction {
address to;
uint value;
uint256 value;
bytes data;
}
// CONSTRUCTOR
// just pass on the owner array to the multiowned and
// the limit to daylimit
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
/**
* 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, uint256 _required, uint256 _daylimit)
Shareable(_owners, _required)
DayLimit(_daylimit) { }
// METHODS
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
/**
* @dev destroys the contract sending everything to `_to`.
*/
function destroy(address _to) onlymanyowners(keccak256(msg.data)) external {
selfdestruct(_to);
}
// gets called when no other function matches
/**
* @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);
}
// Outside-visible transact entry point. Executes transaction immediately if below daily spend 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.
function execute(address _to, uint _value, bytes _data) external onlyOwner returns (bytes32 _r) {
/**
* @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, uint256 _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;
revert();
}
return 0;
}
// determine our operation hash.
_r = sha3(msg.data, block.number);
_r = keccak256(msg.data, block.number);
if (!confirm(_r) && txs[_r].to == 0) {
txs[_r].to = _to;
txs[_r].value = _value;
@ -70,33 +75,43 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
}
}
// confirm a transaction through just the hash. we use the previous transactions map, txs, in order
// to determine the body of the transaction from the hash provided.
/**
* @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;
}
assert(txs[_h].to.call.value(txs[_h].value)(txs[_h].data));
MultiTransact(msg.sender, _h, txs[_h].value, txs[_h].to, txs[_h].data);
delete txs[_h];
return true;
}
}
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
/**
* @dev Updates the daily limit value.
* @param _newLimit uint256 to represent the new limit.
*/
function setDailyLimit(uint256 _newLimit) onlymanyowners(keccak256(msg.data)) external {
_setDailyLimit(_newLimit);
}
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
/**
* @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) {
uint256 length = pendingsIndex.length;
for (uint256 i = 0; i < length; ++i) {
delete txs[pendingsIndex[i]];
}
super.clearPending();

View File

@ -1,26 +0,0 @@
pragma solidity ^0.4.4;
/*
* Ownable
*
* Base contract with an owner.
* Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
*/
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner() {
if (msg.sender == owner)
_;
}
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) owner = newOwner;
}
}

View File

@ -1,30 +0,0 @@
pragma solidity ^0.4.4;
/*
* PullPayment
* Base contract supporting async send for pull payments.
* Inherit from this contract and use asyncSend instead of send.
*/
contract PullPayment {
mapping(address => uint) public payments;
// store sent amount as credit to be pulled, called by payer
function asyncSend(address dest, uint amount) internal {
payments[dest] += amount;
}
// withdraw accumulated balance, called by payee
function withdrawPayments() {
address payee = msg.sender;
uint payment = payments[payee];
if (payment == 0) throw;
if (this.balance < payment) throw;
payments[payee] = 0;
if (!payee.send(payment)) {
throw;
}
}
}

View File

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

View File

@ -1,29 +0,0 @@
pragma solidity ^0.4.4;
/**
* Math operations with safety checks
*/
contract SafeMath {
function safeMul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
function assert(bool assertion) internal {
if (!assertion) throw;
}
}

View File

@ -1,164 +0,0 @@
pragma solidity ^0.4.4;
/*
* Shareable
*
* Based on https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol
*
* inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a single, or, crucially, each of a number of, designated owners.
*
* 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 {
// TYPES
// struct for the status of a pending operation.
struct PendingState {
uint yetNeeded;
uint ownersDone;
uint index;
}
// FIELDS
// the number of owners that must confirm the same operation before it is run.
uint public required;
// list of owners
uint[256] owners;
uint constant c_maxOwners = 250;
// index on the list of owners to allow reverse lookup
mapping(uint => uint) ownerIndex;
// the ongoing operations.
mapping(bytes32 => PendingState) pendings;
bytes32[] pendingsIndex;
// EVENTS
// this contract only has six types of events: it can accept a confirmation, in which case
// we record owner and operation (hash) alongside it.
event Confirmation(address owner, bytes32 operation);
event Revoke(address owner, bytes32 operation);
// MODIFIERS
// simple single-sig function modifier.
modifier onlyOwner {
if (isOwner(msg.sender))
_;
}
// multi-sig function modifier: the operation must have an intrinsic hash in order
// that later attempts can be realised as the same underlying operation and
// thus count as confirmations.
modifier onlymanyowners(bytes32 _operation) {
if (confirmAndCheck(_operation))
_;
}
// CONSTRUCTOR
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function Shareable(address[] _owners, uint _required) {
owners[1] = uint(msg.sender);
ownerIndex[uint(msg.sender)] = 1;
for (uint i = 0; i < _owners.length; ++i) {
owners[2 + i] = uint(_owners[i]);
ownerIndex[uint(_owners[i])] = 2 + i;
}
required = _required;
}
// METHODS
// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint index = ownerIndex[uint(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);
}
}
// Gets an owner by 0-indexed position (using numOwners as the count)
function getOwner(uint ownerIndex) external constant returns (address) {
return address(owners[ownerIndex + 1]);
}
function isOwner(address _addr) constant returns (bool) {
return ownerIndex[uint(_addr)] > 0;
}
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
var pending = pendings[_operation];
uint index = ownerIndex[uint(_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);
}
// INTERNAL METHODS
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
// determine what index the present sender is:
uint index = ownerIndex[uint(msg.sender)];
// make sure they're an owner
if (index == 0) return;
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;
}
}
}
function clearPending() internal {
uint length = pendingsIndex.length;
for (uint i = 0; i < length; ++i)
if (pendingsIndex[i] != 0)
delete pendings[pendingsIndex[i]];
delete pendingsIndex;
}
}

View File

@ -1,28 +0,0 @@
pragma solidity ^0.4.4;
import "./Ownable.sol";
/*
* Stoppable
* Abstract contract that allows children to implement an
* emergency stop mechanism.
*/
contract Stoppable is Ownable {
bool public stopped;
modifier stopInEmergency { if (!stopped) _; }
modifier onlyInEmergency { if (stopped) _; }
// called by the owner on emergency, triggers stopped state
function emergencyStop() external onlyOwner {
stopped = true;
}
// called by the owner on end of emergency, returns to normal state
function release() external onlyOwner onlyInEmergency {
stopped = false;
}
}

View File

@ -0,0 +1,34 @@
pragma solidity ^0.4.11;
import '../math/SafeMath.sol';
import './Crowdsale.sol';
/**
* @title CappedCrowdsale
* @dev Extension of Crowsdale 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;
}
}

View File

@ -0,0 +1,107 @@
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 block, 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 block where investments are allowed (both inclusive)
uint256 public startBlock;
uint256 public endBlock;
// 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 _startBlock, uint256 _endBlock, uint256 _rate, address _wallet) {
require(_startBlock >= block.number);
require(_endBlock >= _startBlock);
require(_rate > 0);
require(_wallet != 0x0);
token = createTokenContract();
startBlock = _startBlock;
endBlock = _endBlock;
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) 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) {
uint256 current = block.number;
bool withinPeriod = current >= startBlock && current <= endBlock;
bool nonZeroPurchase = msg.value != 0;
return withinPeriod && nonZeroPurchase;
}
// @return true if crowdsale event has ended
function hasEnded() public constant returns (bool) {
return block.number > endBlock;
}
}

View File

@ -0,0 +1,39 @@
pragma solidity ^0.4.11;
import '../math/SafeMath.sol';
import '../ownership/Ownable.sol';
import './Crowdsale.sol';
/**
* @title FinalizableCrowdsale
* @dev Extension of Crowsdale where an owner can do extra work
* after finishing. By default, it will end token minting.
*/
contract FinalizableCrowdsale is Crowdsale, Ownable {
using SafeMath for uint256;
bool public isFinalized = false;
event Finalized();
// should be called after crowdsale ends, to do
// some extra finalization work
function finalize() onlyOwner {
require(!isFinalized);
require(hasEnded());
finalization();
Finalized();
isFinalized = true;
}
// end token minting on finalization
// override this with custom logic if needed
function finalization() internal {
token.finishMinting();
}
}

View 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 payable {
require(state == State.Active);
deposited[investor] = deposited[investor].add(msg.value);
}
function close() onlyOwner {
require(state == State.Active);
state = State.Closed;
Closed();
wallet.transfer(this.balance);
}
function enableRefunds() onlyOwner {
require(state == State.Active);
state = State.Refunding;
RefundsEnabled();
}
function refund(address investor) {
require(state == State.Refunding);
uint256 depositedValue = deposited[investor];
deposited[investor] = 0;
investor.transfer(depositedValue);
Refunded(investor, depositedValue);
}
}

View 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() {
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;
}
}

View File

@ -1,23 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
// UNSAFE CODE, DO NOT USE!
contract BadArrayUse is PullPayment {
address[] employees;
function payBonus() {
for (var i = 0; i < employees.length; i++) {
address employee = employees[i];
uint bonus = calculateBonus(employee);
asyncSend(employee, bonus);
}
}
function calculateBonus(address employee) returns (uint) {
// some expensive computation...
}
}

View File

@ -1,17 +0,0 @@
pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
contract BadFailEarly {
uint constant DEFAULT_SALARY = 50000;
mapping(string => uint) nameToSalary;
function getSalary(string name) constant returns (uint) {
if (bytes(name).length != 0 && nameToSalary[name] != 0) {
return nameToSalary[name];
} else {
return DEFAULT_SALARY;
}
}
}

View File

@ -1,23 +0,0 @@
pragma solidity ^0.4.4;
// UNSAFE CODE, DO NOT USE!
contract BadPushPayments {
address highestBidder;
uint highestBid;
function bid() payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {
// return bid to previous winner
if (!highestBidder.send(highestBid)) {
throw;
}
}
highestBidder = msg.sender;
highestBid = msg.value;
}
}

View File

@ -1,25 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
contract GoodArrayUse is PullPayment {
address[] employees;
mapping(address => uint) bonuses;
function payBonus() {
for (uint i = 0; i < employees.length; i++) {
address employee = employees[i];
uint bonus = bonuses[employee];
asyncSend(employee, bonus);
}
}
function calculateBonus(address employee) returns (uint) {
uint bonus = 0;
// some expensive computation...
bonuses[employee] = bonus;
}
}

View File

@ -1,15 +0,0 @@
pragma solidity ^0.4.4;
contract GoodFailEarly {
uint constant DEFAULT_SALARY = 50000;
mapping(string => uint) nameToSalary;
function getSalary(string name) constant returns (uint) {
if (bytes(name).length == 0) throw;
if (nameToSalary[name] == 0) throw;
return nameToSalary[name];
}
}

View File

@ -1,27 +0,0 @@
pragma solidity ^0.4.4;
contract GoodPullPayments {
address highestBidder;
uint highestBid;
mapping(address => uint) refunds;
function bid() external payable {
if (msg.value < highestBid) throw;
if (highestBidder != 0) {
refunds[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
}
function withdrawBid() external {
uint refund = refunds[msg.sender];
refunds[msg.sender] = 0;
if (!msg.sender.send(refund)) {
refunds[msg.sender] = refund;
}
}
}

View File

@ -1,38 +0,0 @@
pragma solidity ^0.4.4;
/*
* Proof of Existence example contract
* see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05
*/
contract ProofOfExistence {
mapping (bytes32 => bool) public proofs;
// store a proof of existence in the contract state
function storeProof(bytes32 proof) {
proofs[proof] = true;
}
// calculate and store the proof for a document
function notarize(string document) {
var proof = calculateProof(document);
storeProof(proof);
}
// helper function to get a document's sha256
function calculateProof(string document) constant returns (bytes32) {
return sha256(document);
}
// check if a document has been notarized
function checkDocument(string document) constant returns (bool) {
var proof = calculateProof(document);
return hasProof(proof);
}
// returns true if proof is stored
function hasProof(bytes32 proof) constant returns (bool) {
return proofs[proof];
}
}

View File

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

View File

@ -1,26 +0,0 @@
pragma solidity ^0.4.4;
import '../PullPayment.sol';
import '../Stoppable.sol';
contract StoppableBid is Stoppable, PullPayment {
address public highestBidder;
uint public highestBid;
function bid() external payable stopInEmergency {
if (msg.value <= highestBid) throw;
if (highestBidder != 0) {
asyncSend(highestBidder, highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
function withdraw() onlyInEmergency {
selfdestruct(owner);
}
}

View File

@ -0,0 +1,25 @@
pragma solidity ^0.4.11;
import "../ownership/Ownable.sol";
/**
* @title Destructible
* @dev Base contract that can be destroyed by owner. All funds in contract will be sent to the owner.
*/
contract Destructible is Ownable {
function Destructible() payable { }
/**
* @dev Transfers the current balance to the owner and terminates the contract.
*/
function destroy() onlyOwner {
selfdestruct(owner);
}
function destroyAndSend(address _recipient) onlyOwner {
selfdestruct(_recipient);
}
}

View File

@ -0,0 +1,21 @@
pragma solidity ^0.4.11;
import '../ownership/Ownable.sol';
/**
* @title Migrations
* @dev This is a truffle contract, needed for truffle integration, not meant for use by Zeppelin users.
*/
contract Migrations is Ownable {
uint256 public lastCompletedMigration;
function setCompleted(uint256 completed) onlyOwner {
lastCompletedMigration = completed;
}
function upgrade(address newAddress) onlyOwner {
Migrations upgraded = Migrations(newAddress);
upgraded.setCompleted(lastCompletedMigration);
}
}

View File

@ -0,0 +1,51 @@
pragma solidity ^0.4.11;
import "../ownership/Ownable.sol";
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev modifier to allow actions only when the contract IS paused
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev modifier to allow actions only when the contract IS NOT paused
*/
modifier whenPaused {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused returns (bool) {
paused = true;
Pause();
return true;
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused returns (bool) {
paused = false;
Unpause();
return true;
}
}

View File

@ -0,0 +1,36 @@
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
* listed tokens will be sent to the owner.
*/
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 {
// Transfer tokens to owner
for(uint256 i = 0; i < tokens.length; i++) {
ERC20Basic token = ERC20Basic(tokens[i]);
uint256 balance = token.balanceOf(this);
token.transfer(owner, balance);
}
// Transfer Eth to owner and terminate contract
selfdestruct(owner);
}
}

24
contracts/math/Math.sol Normal file
View 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;
}
}

View 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;
}
}

View File

@ -0,0 +1,38 @@
pragma solidity ^0.4.11;
import './Ownable.sol';
/**
* @title Claimable
* @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.
*/
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner);
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) onlyOwner {
pendingOwner = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() onlyPendingOwner {
owner = pendingOwner;
pendingOwner = 0x0;
}
}

View File

@ -0,0 +1,21 @@
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
* contact information.
*/
contract Contactable is Ownable{
string public contactInformation;
/**
* @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{
contactInformation = info;
}
}

View File

@ -0,0 +1,41 @@
pragma solidity ^0.4.11;
import './Claimable.sol';
/**
* @title DelayedClaimable
* @dev Extension for the Claimable contract, where the ownership needs to be claimed before/after
* a certain block number.
*/
contract DelayedClaimable is Claimable {
uint256 public end;
uint256 public start;
/**
* @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.
*/
function setLimits(uint256 _start, uint256 _end) onlyOwner {
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.
*/
function claimOwnership() onlyPendingOwner {
require((block.number <= end) && (block.number >= start));
owner = pendingOwner;
pendingOwner = 0x0;
end = 0;
}
}

View File

@ -0,0 +1,21 @@
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
* of this contract to reclaim ownership of the contracts.
*/
contract HasNoContracts is Ownable {
/**
* @dev Reclaim ownership of Ownable contracts
* @param contractAddr The address of the Ownable to be reclaimed.
*/
function reclaimContract(address contractAddr) external onlyOwner {
Ownable contractInst = Ownable(contractAddr);
contractInst.transferOwnership(owner);
}
}

View File

@ -0,0 +1,40 @@
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
* in the contract, it will allow the owner to reclaim this ether.
* @notice Ether can still be send to this contract by:
* calling functions labeled `payable`
* `selfdestruct(contract_address)`
* mining directly to the contract address
*/
contract HasNoEther is Ownable {
/**
* @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
* we could use assembly to access msg.value.
*/
function HasNoEther() payable {
require(msg.value == 0);
}
/**
* @dev Disallows direct send by settings a default function without the `payable` flag.
*/
function() external {
}
/**
* @dev Transfer all Ether held by the contract to the owner.
*/
function reclaimEther() external onlyOwner {
assert(owner.send(this.balance));
}
}

View File

@ -0,0 +1,34 @@
pragma solidity ^0.4.11;
import "./Ownable.sol";
import "../token/ERC20Basic.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 {
/**
* @dev Reject all ERC23 compatible tokens
* @param from_ address The address that is transferring the tokens
* @param value_ uint256 the amount of the specified token
* @param data_ Bytes The data passed from the caller.
*/
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);
}
}

View File

@ -1,29 +1,28 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
/*
* Multisig
* Interface contract for multisig proxy contracts; see below for docs.
/**
* @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);
event Deposit(address _from, uint256 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);
event SingleTransact(address owner, uint256 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);
event MultiTransact(address owner, bytes32 operation, uint256 value, address to, bytes data);
// Confirmation still needed for a transaction.
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
event ConfirmationNeeded(bytes32 operation, address initiator, uint256 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 execute(address _to, uint256 _value, bytes _data) external returns (bytes32);
function confirm(bytes32 _h) returns (bool);
}

View File

@ -0,0 +1,14 @@
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
* Owned contracts. See respective base contracts for details.
*/
contract NoOwner is HasNoEther, HasNoTokens, HasNoContracts {
}

View File

@ -0,0 +1,41 @@
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".
*/
contract Ownable {
address public owner;
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
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.
*/
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}

View File

@ -0,0 +1,183 @@
pragma solidity ^0.4.11;
/**
* @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 {
uint256 yetNeeded;
uint256 ownersDone;
uint256 index;
}
// the number of owners that must confirm the same operation before it is run.
uint256 public required;
// list of owners
address[256] owners;
// index on the list of owners to allow reverse lookup
mapping(address => uint256) 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 {
require(isOwner(msg.sender));
_;
}
/**
* @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, uint256 _required) {
owners[1] = msg.sender;
ownerIndex[msg.sender] = 1;
for (uint256 i = 0; i < _owners.length; ++i) {
owners[2 + i] = _owners[i];
ownerIndex[_owners[i]] = 2 + i;
}
required = _required;
require(required <= owners.length);
}
/**
* @dev Revokes a prior confirmation of the given operation.
* @param _operation A string identifying the operation.
*/
function revoke(bytes32 _operation) external {
uint256 index = ownerIndex[msg.sender];
// make sure they're an owner
if (index == 0) {
return;
}
uint256 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 uint256 The index of the owner
* @return The address of the owner
*/
function getOwner(uint256 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];
uint256 index = ownerIndex[_owner];
// make sure they're an owner
if (index == 0) {
return false;
}
// determine the bit to set for this owner.
uint256 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:
uint256 index = ownerIndex[msg.sender];
// make sure they're an owner
require(index != 0);
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.
uint256 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 {
uint256 length = pendingsIndex.length;
for (uint256 i = 0; i < length; ++i) {
if (pendingsIndex[i] != 0) {
delete pendings[pendingsIndex[i]];
}
}
delete pendingsIndex;
}
}

View File

@ -0,0 +1,43 @@
pragma solidity ^0.4.11;
import '../math/SafeMath.sol';
/**
* @title PullPayment
* @dev Base contract supporting async send for pull payments. Inherit from this
* contract and use asyncSend instead of send.
*/
contract PullPayment {
using SafeMath for uint256;
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, uint256 amount) internal {
payments[dest] = payments[dest].add(amount);
totalPayments = totalPayments.add(amount);
}
/**
* @dev withdraw accumulated balance, called by payee.
*/
function withdrawPayments() {
address payee = msg.sender;
uint256 payment = payments[payee];
require(payment != 0);
require(this.balance >= payment);
totalPayments = totalPayments.sub(payment);
payments[payee] = 0;
assert(payee.send(payment));
}
}

View File

@ -1,21 +0,0 @@
pragma solidity ^0.4.4;
import '../SafeMath.sol';
contract SafeMathMock is SafeMath {
uint public result;
function multiply(uint a, uint b) {
result = safeMul(a, b);
}
function subtract(uint a, uint b) {
result = safeSub(a, b);
}
function add(uint a, uint b) {
result = safeAdd(a, b);
}
}

View File

@ -1,25 +0,0 @@
pragma solidity ^0.4.4;
import '../Stoppable.sol';
// mock class using Stoppable
contract StoppableMock is Stoppable {
bool public drasticMeasureTaken;
uint public count;
function StoppableMock() {
drasticMeasureTaken = false;
count = 0;
}
function normalProcess() external stopInEmergency {
count++;
}
function drasticMeasure() external onlyInEmergency {
drasticMeasureTaken = true;
}
}

View File

@ -1,29 +1,38 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
import './ERC20Basic.sol';
import '../SafeMath.sol';
import '../math/SafeMath.sol';
/*
* Basic token
* Basic version of StandardToken, with no allowances
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic, SafeMath {
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint) balances;
mapping(address => uint256) balances;
function transfer(address _to, uint _value) {
if (balances[msg.sender] < _value) {
throw;
}
balances[msg.sender] = safeSub(balances[msg.sender], _value);
balances[_to] = safeAdd(balances[_to], _value);
/**
* @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, uint256 _value) returns (bool) {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
function balanceOf(address _owner) constant returns (uint balance) {
/**
* @dev Gets the balance of the specified 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 (uint256 balance) {
return balances[_owner];
}
}

View File

@ -1,38 +0,0 @@
pragma solidity ^0.4.4;
import "./StandardToken.sol";
/*
* CrowdsaleToken
*
* Simple ERC20 Token example, with crowdsale token creation
*/
contract CrowdsaleToken is StandardToken {
string public name = "CrowdsaleToken";
string public symbol = "CRW";
uint public decimals = 18;
// 1 ether = 500 example tokens
uint PRICE = 500;
function () payable {
createTokens(msg.sender);
}
function createTokens(address recipient) payable {
if (msg.value == 0) throw;
uint tokens = safeMul(msg.value, getPrice());
totalSupply = safeAdd(totalSupply, tokens);
balances[recipient] = safeAdd(balances[recipient], tokens);
}
// replace this with any other price function
function getPrice() constant returns (uint result){
return PRICE;
}
}

View File

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

View File

@ -1,14 +1,14 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
/*
* ERC20Basic
* Simpler version of ERC20 interface
* see https://github.com/ethereum/EIPs/issues/20
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @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) constant returns (uint256);
function transfer(address to, uint256 value) returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}

View File

@ -0,0 +1,57 @@
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.
* LimitedTransferToken has been designed to allow for different limiting factors,
* this can be achieved by recursively calling super.transferableTokens() until the base class is
* hit. For example:
* function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
* return min256(unlockedTokens, super.transferableTokens(holder, time));
* }
* A working example is VestedToken.sol:
* https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/VestedToken.sol
*/
contract LimitedTransferToken is ERC20 {
/**
* @dev Checks whether it can transfer or otherwise throws.
*/
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 _value The amount of tokens to be transferred.
*/
function transfer(address _to, uint256 _value) canTransfer(msg.sender, _value) 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 _value The amount of tokens to be transferred.
*/
function transferFrom(address _from, address _to, uint256 _value) canTransfer(_from, _value) 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
* specific logic for limiting token transferability for a holder over time.
*/
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
return balanceOf(holder);
}
}

View File

@ -0,0 +1,50 @@
pragma solidity ^0.4.11;
import './StandardToken.sol';
import '../ownership/Ownable.sol';
/**
* @title Mintable token
* @dev Simple ERC20 Token example, with mintable token creation
* @dev Issue: * https://github.com/OpenZeppelin/zeppelin-solidity/issues/120
* Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
*/
contract MintableToken is StandardToken, Ownable {
event Mint(address indexed to, uint256 amount);
event MintFinished();
bool public mintingFinished = false;
modifier canMint() {
require(!mintingFinished);
_;
}
/**
* @dev Function to mint tokens
* @param _to The address that will recieve 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, uint256 _amount) onlyOwner canMint returns (bool) {
totalSupply = totalSupply.add(_amount);
balances[_to] = balances[_to].add(_amount);
Mint(_to, _amount);
return true;
}
/**
* @dev Function to stop minting new tokens.
* @return True if the operation was successful.
*/
function finishMinting() onlyOwner returns (bool) {
mintingFinished = true;
MintFinished();
return true;
}
}

View File

@ -0,0 +1,21 @@
pragma solidity ^0.4.11;
import './StandardToken.sol';
import '../lifecycle/Pausable.sol';
/**
* Pausable token
*
* Simple ERC20 Token example, with pausable token creation
**/
contract PausableToken is StandardToken, Pausable {
function transfer(address _to, uint _value) whenNotPaused returns (bool) {
return super.transfer(_to, _value);
}
function transferFrom(address _from, address _to, uint _value) whenNotPaused returns (bool) {
return super.transferFrom(_from, _to, _value);
}
}

View File

@ -1,23 +1,25 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
import "./StandardToken.sol";
/*
* SimpleToken
*
* 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.
/**
* @title SimpleToken
* @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;
uint256 public decimals = 18;
uint256 public INITIAL_SUPPLY = 10000;
/**
* @dev Contructor that gives msg.sender all of existing tokens.
*/
function SimpleToken() {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;

View File

@ -1,50 +1,66 @@
pragma solidity ^0.4.4;
pragma solidity ^0.4.11;
import './BasicToken.sol';
import './ERC20.sol';
import '../SafeMath.sol';
/**
* Standard ERC20 token
* @title Standard ERC20 token
*
* https://github.com/ethereum/EIPs/issues/20
* Based on code by FirstBlood:
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
* @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 ERC20, SafeMath {
contract StandardToken is ERC20, BasicToken {
mapping(address => uint) balances;
mapping (address => mapping (address => uint)) allowed;
mapping (address => mapping (address => uint256)) allowed;
function transfer(address _to, uint _value) returns (bool success) {
balances[msg.sender] = safeSub(balances[msg.sender], _value);
balances[_to] = safeAdd(balances[_to], _value);
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint _value) returns (bool success) {
/**
* @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 uint256 the amout of tokens to be transfered
*/
function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
var _allowance = allowed[_from][msg.sender];
balances[_to] = safeAdd(balances[_to], _value);
balances[_from] = safeSub(balances[_from], _value);
allowed[_from][msg.sender] = safeSub(_allowance, _value);
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// require (_value <= _allowance);
balances[_to] = balances[_to].add(_value);
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = _allowance.sub(_value);
Transfer(_from, _to, _value);
return true;
}
function balanceOf(address _owner) constant returns (uint balance) {
return balances[_owner];
}
/**
* @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) returns (bool) {
// 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
require((_value == 0) || (allowed[msg.sender][_spender] == 0));
function approve(address _spender, uint _value) returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint remaining) {
/**
* @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 uint256 specifing the amount of tokens still avaible for the spender.
*/
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}

View File

@ -0,0 +1,49 @@
pragma solidity ^0.4.11;
import './ERC20Basic.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 {
// ERC20 basic token contract being held
ERC20Basic token;
// beneficiary of tokens after they are released
address beneficiary;
// timestamp when token release is enabled
uint releaseTime;
function TokenTimelock(ERC20Basic _token, address _beneficiary, uint _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() {
require(msg.sender == beneficiary);
release();
}
/**
* @notice Transfers tokens held by timelock to beneficiary.
*/
function release() {
require(now >= releaseTime);
uint amount = token.balanceOf(this);
require(amount > 0);
token.transfer(beneficiary, amount);
}
}

View File

@ -0,0 +1,241 @@
pragma solidity ^0.4.11;
import "../math/Math.sol";
import "./StandardToken.sol";
import "./LimitedTransferToken.sol";
/**
* @title Vested token
* @dev Tokens that can be vested for a group of addresses.
*/
contract VestedToken is StandardToken, LimitedTransferToken {
uint256 MAX_GRANTS_PER_ADDRESS = 20;
struct TokenGrant {
address granter; // 20 bytes
uint256 value; // 32 bytes
uint64 cliff;
uint64 vesting;
uint64 start; // 3 * 8 = 24 bytes
bool revokable;
bool burnsOnRevoke; // 2 * 1 = 2 bits? or 2 bytes?
} // total 78 bytes = 3 sstore per operation (32 per sstore)
mapping (address => TokenGrant[]) public grants;
event NewTokenGrant(address indexed from, address indexed to, uint256 value, uint256 grantId);
/**
* @dev Grant tokens to a specified address
* @param _to address The address which the tokens will be granted to.
* @param _value uint256 The amount of tokens to be granted.
* @param _start uint64 Time of the beginning of the grant.
* @param _cliff uint64 Time of the cliff period.
* @param _vesting uint64 The vesting period.
*/
function grantVestedTokens(
address _to,
uint256 _value,
uint64 _start,
uint64 _cliff,
uint64 _vesting,
bool _revokable,
bool _burnsOnRevoke
) public {
// Check for date inconsistencies that may cause unexpected behavior
require(_cliff >= _start && _vesting >= _cliff);
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).
uint256 count = grants[_to].push(
TokenGrant(
_revokable ? msg.sender : 0, // avoid storing an extra 20 bytes when it is non-revokable
_value,
_cliff,
_vesting,
_start,
_revokable,
_burnsOnRevoke
)
);
transfer(_to, _value);
NewTokenGrant(msg.sender, _to, _value, count - 1);
}
/**
* @dev Revoke the grant of tokens of a specifed address.
* @param _holder The address which will have its tokens revoked.
* @param _grantId The id of the token grant.
*/
function revokeTokenGrant(address _holder, uint256 _grantId) public {
TokenGrant grant = grants[_holder][_grantId];
require(grant.revokable);
require(grant.granter == msg.sender); // Only granter can revoke it
address receiver = grant.burnsOnRevoke ? 0xdead : msg.sender;
uint256 nonVested = nonVestedTokens(grant, uint64(now));
// remove grant from array
delete grants[_holder][_grantId];
grants[_holder][_grantId] = grants[_holder][grants[_holder].length.sub(1)];
grants[_holder].length -= 1;
balances[receiver] = balances[receiver].add(nonVested);
balances[_holder] = balances[_holder].sub(nonVested);
Transfer(_holder, receiver, nonVested);
}
/**
* @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 uint256 representing a holder's total amount of transferable tokens.
*/
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
uint256 grantIndex = tokenGrantsCount(holder);
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;
for (uint256 i = 0; i < grantIndex; i++) {
nonVested = SafeMath.add(nonVested, nonVestedTokens(grants[holder][i], time));
}
// Balance - totalNonVested is the amount of tokens a holder can transfer at any given time
uint256 vestedTransferable = SafeMath.sub(balanceOf(holder), nonVested);
// Return the minimum of how many vested can transfer and other value
// in case there are other limiting transferability factors (default is balanceOf)
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 uint256 representing the total amount of grants.
*/
function tokenGrantsCount(address _holder) 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.
* @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 uint256 representing the amount of vested tokensof a specif grant.
* transferableTokens
* | _/-------- vestedTokens rect
* | _/
* | _/
* | _/
* | _/
* | /
* | .|
* | . |
* | . |
* | . |
* | . |
* | . |
* +===+===========+---------+----------> time
* Start Clift Vesting
*/
function calculateVestedTokens(
uint256 tokens,
uint256 time,
uint256 start,
uint256 cliff,
uint256 vesting) constant returns (uint256)
{
// Shortcuts for before cliff and after vesting cases.
if (time < cliff) return 0;
if (time >= vesting) return tokens;
// Interpolate all vested tokens.
// 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)
uint256 vestedTokens = SafeMath.div(
SafeMath.mul(
tokens,
SafeMath.sub(time, start)
),
SafeMath.sub(vesting, start)
);
return vestedTokens;
}
/**
* @dev Get all information about a specifc 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, uint256 _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) {
TokenGrant grant = grants[_holder][_grantId];
granter = grant.granter;
value = grant.value;
start = grant.start;
cliff = grant.cliff;
vesting = grant.vesting;
revokable = grant.revokable;
burnsOnRevoke = grant.burnsOnRevoke;
vested = vestedTokens(grant, uint64(now));
}
/**
* @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 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(
grant.value,
uint256(time),
uint256(grant.start),
uint256(grant.cliff),
uint256(grant.vesting)
);
}
/**
* @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 uint256 representing the amount of non vested tokens of a specifc grant on the
* passed time frame.
*/
function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) {
return grant.value.sub(vestedTokens(grant, time));
}
/**
* @dev Calculate the date when the holder can trasfer all its tokens
* @param holder address The address of the holder
* @return An uint256 representing the date of the last transferable tokens.
*/
function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) {
date = uint64(now);
uint256 grantIndex = grants[holder].length;
for (uint256 i = 0; i < grantIndex; i++) {
date = Math.max64(grants[holder][i].vesting, date);
}
}
}

View File

@ -55,6 +55,6 @@ If researchers break the contract, they can claim their reward.
For each researcher who wants to hack the contract and claims the reward, refer to our `Test <https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/Bounty.js/>`_ for the detail.
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, kill the contract so that all the rewards go back to the owner.::
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, destroy the contract so that all the rewards go back to the owner.::
bounty.kill();
bounty.destroy();

View File

@ -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.

View 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.

View File

@ -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 {
...

View File

@ -26,8 +26,8 @@ The code is open-source, and `available on github <https://github.com/OpenZeppel
:caption: Smart Contracts
ownable
stoppable
killable
Pausable
destructible
claimable
migrations
safemath

View File

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

24
docs/source/math.rst Normal file
View 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.

View File

@ -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.

27
docs/source/pausable.rst Normal file
View File

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

View File

@ -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.
safeSub(uint a, unit b) internal returns (uint)
sub(uint256 a, uint256 b) internal returns (uint256)
"""""""""""""""""""""""""""""""""""""""""""""""""
Checks that b is not greater than a before subtracting.
safeAdd(unit a, unit b) internal returns (uint)
add(uint256 a, uint256 b) internal returns (uint256)
"""""""""""""""""""""""""""""""""""""""""""""""""
Checks that the result is greater than both a and b.

View File

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

17
ethpm.json Normal file
View File

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

View File

@ -1,3 +1,5 @@
var Migrations = artifacts.require("./Migrations.sol");
module.exports = function(deployer) {
deployer.deploy(Migrations);
};

View File

@ -1,12 +1,5 @@
//var Ownable = artifacts.require("ownership/Ownable.sol");
module.exports = function(deployer) {
deployer.deploy(PullPaymentBid);
deployer.deploy(BadArrayUse);
deployer.deploy(ProofOfExistence);
deployer.deploy(Ownable);
deployer.deploy(Claimable);
deployer.deploy(LimitBalance);
if(deployer.network == 'test'){
deployer.deploy(SecureTargetBounty);
deployer.deploy(InsecureTargetBounty);
};
//deployer.deploy(Ownable);
};

View File

@ -1,18 +1,13 @@
{
"name": "zeppelin-solidity",
"version": "1.0.1",
"version": "1.2.0",
"description": "Secure Smart Contract library for Solidity",
"main": "truffle.js",
"devDependencies": {
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0",
"ethereumjs-testrpc": "^3.0.2",
"truffle": "^2.1.1"
},
"scripts": {
"test": "truffle test",
"install": "scripts/install.sh"
"test": "scripts/test.sh",
"console": "truffle console",
"coverage": "scripts/coverage.sh",
"coveralls": "scripts/coveralls.sh"
},
"repository": {
"type": "git",
@ -31,5 +26,23 @@
"bugs": {
"url": "https://github.com/OpenZeppelin/zeppelin-solidity/issues"
},
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity"
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity",
"dependencies": {
"truffle-hdwallet-provider": "0.0.3"
},
"devDependencies": {
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0",
"babel-register": "^6.23.0",
"chai": "^4.0.2",
"chai-as-promised": "^7.0.0",
"chai-bignumber": "^2.0.0",
"coveralls": "^2.13.1",
"ethereumjs-testrpc": "^3.0.2",
"mocha-lcov-reporter": "^1.3.0",
"moment": "^2.18.1",
"solidity-coverage": "^0.1.7",
"truffle": "3.2.2"
}
}

25
scripts/coverage.sh Executable file
View File

@ -0,0 +1,25 @@
#! /bin/bash
output=$(nc -z localhost 8555; echo $?)
[ $output -eq "0" ] && trpc_running=true
if [ ! $trpc_running ]; then
echo "Starting testrpc-sc to generate coverage"
# we give each account 1M ether, needed for high-value tests
./node_modules/ethereumjs-testrpc-sc/bin/testrpc --gasLimit 0xfffffffffff --port 8555 \
--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" \
> /dev/null &
trpc_pid=$!
fi
SOLIDITY_COVERAGE=true && ./node_modules/.bin/solidity-coverage

4
scripts/coveralls.sh Executable file
View File

@ -0,0 +1,4 @@
#! /bin/bash
yarn run coverage && cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

View File

@ -1,3 +0,0 @@
#! /bin/bash
mkdir -p ../../contracts/zeppelin && cp -r contracts/* "$_"

25
scripts/test.sh Executable file
View File

@ -0,0 +1,25 @@
#! /bin/bash
output=$(nc -z localhost 8545; echo $?)
[ $output -eq "0" ] && trpc_running=true
if [ ! $trpc_running ]; then
echo "Starting our own testrpc node instance"
# we give each account 1M ether, needed for high-value tests
testrpc \
--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" \
> /dev/null &
trpc_pid=$!
fi
./node_modules/truffle/cli.js test "$@"
if [ ! $trpc_running ]; then
kill -9 $trpc_pid
fi

View File

@ -1,5 +1,7 @@
const assertJump = require('./helpers/assertJump');
var BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");
contract('BasicToken', function(accounts) {
it("should return the correct totalSupply after construction", async function() {
@ -25,8 +27,9 @@ contract('BasicToken', function(accounts) {
try {
let transfer = await token.transfer(accounts[1], 101);
} catch(error) {
assertJump(error);
return assertJump(error);
}
assert.fail('should have thrown before');
});
});

View File

@ -1,115 +1,115 @@
'use strict';
let sendReward = function(sender, receiver, value){
web3.eth.sendTransaction({
from:sender,
to:receiver,
value: value
})
});
};
var SecureTargetBounty = artifacts.require('helpers/SecureTargetBounty.sol');
var InsecureTargetBounty = artifacts.require('helpers/InsecureTargetBounty.sol');
function awaitEvent(event, handler) {
return new Promise((resolve, reject) => {
function wrappedHandler(...args) {
Promise.resolve(handler(...args)).then(resolve).catch(reject);
}
event.watch(wrappedHandler);
});
}
contract('Bounty', function(accounts) {
it("sets reward", async function(){
it('sets reward', async function() {
let owner = accounts[0];
let reward = web3.toWei(1, "ether");
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
})
});
it("empties itself when killed", async function(){
it('empties itself when destroyed', async function(){
let owner = accounts[0];
let reward = web3.toWei(1, "ether");
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
await bounty.kill();
await bounty.destroy();
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
})
});
describe("Against secure contract", function(){
describe('Against secure contract', function(){
it("checkInvariant returns true", async function(){
let bounty = await SecureTargetBounty.new();
let target = await bounty.createTarget();
let check = await bounty.checkInvariant.call();
assert.isTrue(check);
})
it("cannot claim reward", async function(done){
it('cannot claim reward', async function(){
let owner = accounts[0];
let researcher = accounts[1];
let reward = web3.toWei(1, "ether");
let reward = web3.toWei(1, 'ether');
let bounty = await SecureTargetBounty.new();
let event = bounty.TargetCreated({});
event.watch(async function(err, result) {
let watcher = async function(err, result) {
event.stopWatching();
if (err) { throw err }
if (err) { throw err; }
var targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
assert.equal(reward,
web3.eth.getBalance(bounty.address).toNumber());
try {
let tmpClain = await bounty.claim(targetAddress, {from:researcher});
done("should not come here");
await bounty.claim(targetAddress, {from:researcher});
assert.isTrue(false); // should never reach here
} catch(error) {
let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty);
let reClaimedBounty = await bounty.claimed.call();
assert.isFalse(reClaimedBounty);
try {
let withdraw = await bounty.withdrawPayments({from:researcher});
done("should not come here")
} catch (err) {
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
done();
}
}//end of first try catch
});
}
try {
await bounty.withdrawPayments({from:researcher});
assert.isTrue(false); // should never reach here
} catch (err) {
assert.equal(reward,
web3.eth.getBalance(bounty.address).toNumber());
}
};
bounty.createTarget({from:researcher});
})
})
await awaitEvent(event, watcher);
});
});
describe("Against broken contract", function(){
it("checkInvariant returns false", async function(){
let bounty = await InsecureTargetBounty.new();
let target = await bounty.createTarget();
let invariantCall = await bounty.checkInvariant.call();
assert.isFalse(invariantCall);
})
it("claims reward", async function(done){
describe('Against broken contract', function(){
it('claims reward', async function() {
let owner = accounts[0];
let researcher = accounts[1];
let reward = web3.toWei(1, "ether");
let reward = web3.toWei(1, 'ether');
let bounty = await InsecureTargetBounty.new();
let event = bounty.TargetCreated({});
event.watch(async function(err, result) {
let watcher = async function(err, result) {
event.stopWatching();
if (err) { throw err }
if (err) { throw err; }
let targetAddress = result.args.createdAddress;
sendReward(owner, bounty.address, reward);
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
let bountyClaim = await bounty.claim(targetAddress, {from:researcher});
await bounty.claim(targetAddress, {from:researcher});
let claim = await bounty.claimed.call();
assert.isTrue(claim);
let payment = await bounty.withdrawPayments({from:researcher});
await bounty.withdrawPayments({from:researcher});
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
done();
})
};
bounty.createTarget({from:researcher});
})
})
await awaitEvent(event, watcher);
});
});
});

90
test/CappedCrowdsale.js Normal file
View File

@ -0,0 +1,90 @@
import ether from './helpers/ether'
import advanceToBlock from './helpers/advanceToBlock'
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)
describe('creating a valid crowdsale', function () {
it('should fail with zero cap', async function () {
await CappedCrowdsale.new(this.startBlock, this.endBlock, rate, wallet, 0).should.be.rejectedWith(EVMThrow);
})
});
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await CappedCrowdsale.new(this.startBlock, this.endBlock, rate, wallet, cap)
this.token = MintableToken.at(await this.crowdsale.token())
})
describe('accepting payments', function () {
beforeEach(async function () {
await advanceToBlock(this.startBlock - 1)
})
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 advanceToBlock(this.startBlock - 1)
})
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)
})
})
})

View File

@ -1,3 +1,8 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var Claimable = artifacts.require('../contracts/ownership/Claimable.sol');
contract('Claimable', function(accounts) {
let claimable;
@ -5,34 +10,39 @@ contract('Claimable', function(accounts) {
claimable = await Claimable.new();
});
it("should have an owner", async function() {
it('should have an owner', async function() {
let owner = await claimable.owner();
assert.isTrue(owner != 0);
assert.isTrue(owner !== 0);
});
it("changes pendingOwner after transfer", async function() {
it('changes pendingOwner after transfer', async function() {
let newOwner = accounts[1];
let transfer = await claimable.transferOwnership(newOwner);
await claimable.transferOwnership(newOwner);
let pendingOwner = await claimable.pendingOwner();
assert.isTrue(pendingOwner === newOwner);
});
it("should prevent to claimOwnership from no pendingOwner", async function() {
let claimedOwner = await claimable.claimOwnership({from: accounts[2]});
let owner = await claimable.owner();
assert.isTrue(owner != accounts[2]);
it('should prevent to claimOwnership from no pendingOwner', async function() {
try {
await claimable.claimOwnership({from: accounts[2]});
} catch(error) {
assertJump(error);
}
});
it("should prevent non-owners from transfering", async function() {
let transfer = await claimable.transferOwnership(accounts[2], {from: accounts[2]});
let pendingOwner = await claimable.pendingOwner();
assert.isFalse(pendingOwner === accounts[2]);
it('should prevent non-owners from transfering', async function() {
const other = accounts[2];
const owner = await claimable.owner.call();
assert.isTrue(owner !== other);
try {
await claimable.transferOwnership(other, {from: other});
} catch(error) {
assertJump(error);
}
});
describe("after initiating a transfer", function () {
describe('after initiating a transfer', function () {
let newOwner;
beforeEach(async function () {
@ -40,8 +50,8 @@ contract('Claimable', function(accounts) {
await claimable.transferOwnership(newOwner);
});
it("changes allow pending owner to claim ownership", async function() {
let claimedOwner = await claimable.claimOwnership({from: newOwner})
it('changes allow pending owner to claim ownership', async function() {
await claimable.claimOwnership({from: newOwner});
let owner = await claimable.owner();
assert.isTrue(owner === newOwner);

30
test/Contactable.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
const assertJump = require('./helpers/assertJump');
var Contactable = artifacts.require('../contracts/ownership/Contactable.sol');
contract('Contactable', function(accounts) {
let contactable;
beforeEach(async function() {
contactable = await Contactable.new();
});
it('should have an empty contact info', async function() {
let info = await contactable.contactInformation();
assert.isTrue(info == "");
});
describe('after setting the contact information', function () {
let contactInfo = "contact information"
beforeEach(async function () {
await contactable.setContactInformation(contactInfo);
});
it('should return the setted contact information', async function() {
let info = await contactable.contactInformation();
assert.isTrue(info === contactInfo);
});
});
});

143
test/Crowdsale.js Normal file
View File

@ -0,0 +1,143 @@
import ether from './helpers/ether'
import advanceToBlock from './helpers/advanceToBlock'
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)
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await Crowdsale.new(this.startBlock, this.endBlock, 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 advanceToBlock(this.endBlock + 1)
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, value, {from: purchaser}).should.be.rejectedWith(EVMThrow)
})
it('should accept payments after start', async function () {
await advanceToBlock(this.startBlock - 1)
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 advanceToBlock(this.endBlock)
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 advanceToBlock(this.startBlock)
})
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 advanceToBlock(this.startBlock)
})
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)
})
})
})

View File

@ -1,4 +1,11 @@
'use strict';
const assertJump = require('./helpers/assertJump');
const timer = require('./helpers/timer');
var DayLimitMock = artifacts.require('./helpers/DayLimitMock.sol');
contract('DayLimit', function(accounts) {
const day = 60 * 60 * 24;
it('should construct with the passed daily limit', async function() {
let initLimit = 10;
@ -28,9 +35,11 @@ contract('DayLimit', function(accounts) {
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
try {
await dayLimit.attemptSpend(3);
} catch(error) {
assertJump(error);
}
});
it('should allow spending if daily limit is reached and then set higher', async function() {
@ -41,7 +50,11 @@ contract('DayLimit', function(accounts) {
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3);
try {
await dayLimit.attemptSpend(3);
} catch(error) {
assertJump(error);
}
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
@ -59,7 +72,11 @@ contract('DayLimit', function(accounts) {
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await dayLimit.attemptSpend(3);
try {
await dayLimit.attemptSpend(3);
} catch(error) {
assertJump(error);
}
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
@ -69,4 +86,27 @@ contract('DayLimit', function(accounts) {
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);
await dayLimit.attemptSpend(8);
let spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
try {
await dayLimit.attemptSpend(3);
} catch(error) {
assertJump(error);
}
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 8);
await timer(day);
await dayLimit.attemptSpend(3);
spentToday = await dayLimit.spentToday();
assert.equal(spentToday, 3);
});
});

View File

@ -1,3 +1,7 @@
'use strict';
var DelayedClaimable = artifacts.require('../contracts/ownership/DelayedClaimable.sol');
contract('DelayedClaimable', function(accounts) {
var delayedClaimable;
@ -7,76 +11,58 @@ contract('DelayedClaimable', function(accounts) {
});
});
it("Changes pendingOwner after transfer succesfull", function(done) {
return delayedClaimable.transferOwnership(accounts[2])
.then(function(){
return delayedClaimable.setClaimBlocks(1000,0);
})
.then(function(){
return delayedClaimable.claimBeforeBlock();
})
.then(function(claimBeforeBlock) {
assert.isTrue(claimBeforeBlock == 1000);
return delayedClaimable.claimAfterBlock();
})
.then(function(claimAfterBlock) {
assert.isTrue(claimAfterBlock == 0);
return delayedClaimable.pendingOwner();
})
.then(function(pendingOwner) {
assert.isTrue(pendingOwner === accounts[2]);
return delayedClaimable.claimOwnership({from: accounts[2]});
})
.then(function() {
return delayedClaimable.owner();
})
.then(function(owner) {
assert.isTrue(owner === accounts[2]);
})
.then(done);
it('can set claim blocks', async function() {
await delayedClaimable.transferOwnership(accounts[2]);
await delayedClaimable.setLimits(0, 1000);
let end = await delayedClaimable.end();
assert.equal(end, 1000);
let start = await delayedClaimable.start();
assert.equal(start, 0);
});
it("Changes pendingOwner after transfer fails", function(done) {
return delayedClaimable.transferOwnership(accounts[1])
.then(function(){
return delayedClaimable.setClaimBlocks(11000,10000);
})
.then(function(){
return delayedClaimable.claimBeforeBlock();
})
.then(function(claimBeforeBlock) {
assert.isTrue(claimBeforeBlock == 11000);
return delayedClaimable.claimAfterBlock();
})
.then(function(claimAfterBlock) {
assert.isTrue(claimAfterBlock == 10000);
return delayedClaimable.pendingOwner();
})
.then(function(pendingOwner) {
assert.isTrue(pendingOwner === accounts[1]);
return delayedClaimable.claimOwnership({from: accounts[1]});
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error;
})
.then(function() {
return delayedClaimable.owner();
})
.then(function(owner) {
assert.isTrue(owner != accounts[1]);
})
.then(done);
it('changes pendingOwner after transfer successful', async function() {
await delayedClaimable.transferOwnership(accounts[2]);
await delayedClaimable.setLimits(0, 1000);
let end = await delayedClaimable.end();
assert.equal(end, 1000);
let start = await delayedClaimable.start();
assert.equal(start, 0);
let pendingOwner = await delayedClaimable.pendingOwner();
assert.equal(pendingOwner, accounts[2]);
await delayedClaimable.claimOwnership({from: accounts[2]});
let owner = await delayedClaimable.owner();
assert.equal(owner, accounts[2]);
});
it("Set claimBeforeBlock and claimAfterBlock invalid values fail", function(done) {
return delayedClaimable.transferOwnership(accounts[1])
.then(function(){
return delayedClaimable.setClaimBlocks(1000,10000);
})
.catch(function(error) {
if (error.message.search('invalid JUMP') == -1) throw error;
})
.then(done);
it('changes pendingOwner after transfer fails', async function() {
await delayedClaimable.transferOwnership(accounts[1]);
await delayedClaimable.setLimits(100, 110);
let end = await delayedClaimable.end();
assert.equal(end, 110);
let start = await delayedClaimable.start();
assert.equal(start, 100);
let pendingOwner = await delayedClaimable.pendingOwner();
assert.equal(pendingOwner, accounts[1]);
var err = null;
try {
await delayedClaimable.claimOwnership({from: accounts[1]});
} catch (error) {
err = error;
}
assert.isFalse(err.message.search('invalid opcode') === -1);
let owner = await delayedClaimable.owner();
assert.isTrue(owner !== accounts[1]);
});
it('set end and start invalid values fail', async function() {
await delayedClaimable.transferOwnership(accounts[1]);
var err = null;
try {
await delayedClaimable.setLimits(1001, 1000);
} catch (error) {
err = error;
}
assert.isFalse(err.message.search('invalid opcode') === -1);
});
});

26
test/Destructible.js Normal file
View File

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

55
test/ECRecovery.js Normal file
View 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)
);
});
});

View File

@ -0,0 +1,61 @@
import advanceToBlock from './helpers/advanceToBlock'
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)
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await FinalizableCrowdsale.new(this.startBlock, this.endBlock, 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 advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: thirdparty}).should.be.rejectedWith(EVMThrow)
})
it('can be finalized by owner after ending', async function () {
await advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: owner}).should.be.fulfilled
})
it('cannot be finalized twice', async function () {
await advanceToBlock(this.endBlock + 1)
await this.crowdsale.finalize({from: owner})
await this.crowdsale.finalize({from: owner}).should.be.rejectedWith(EVMThrow)
})
it('logs finalized', async function () {
await advanceToBlock(this.endBlock)
const {logs} = await this.crowdsale.finalize({from: owner})
const event = logs.find(e => e.event === 'Finalized')
should.exist(event)
})
it('finishes minting of token', async function () {
await advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: owner})
const finished = await this.token.mintingFinished()
finished.should.equal(true)
})
})

35
test/HasNoContracts.js Normal file
View File

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

Some files were not shown because too many files have changed in this diff Show More