Compare commits
236 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e1eed257f1 | |||
| 2bcb54b3cb | |||
| 7100e30bed | |||
| 7f6921e787 | |||
| 73ec224c93 | |||
| a62621eb59 | |||
| 84843d42da | |||
| d08185fc3e | |||
| efe09575e3 | |||
| c4796bb918 | |||
| 111f13f0ce | |||
| d357cf3a2e | |||
| 83f2849446 | |||
| d85be4a866 | |||
| f6f91298f0 | |||
| 1547922b61 | |||
| 602c18b394 | |||
| b5d4120adb | |||
| e851938199 | |||
| 48a2e11dfe | |||
| 22f60eace0 | |||
| b788f33c6a | |||
| 592548715f | |||
| 0983f0065d | |||
| c4f78a5dd5 | |||
| e032b86231 | |||
| a6a2ee2bf1 | |||
| 929367f0ab | |||
| 5a6340395f | |||
| f812433706 | |||
| 713b472253 | |||
| a1d2b673c6 | |||
| 5b2f0aa93d | |||
| ecbfcbb302 | |||
| 964185dec3 | |||
| 5c491987f3 | |||
| e1cf602487 | |||
| a3446507ec | |||
| ff61c7d92d | |||
| 726593c0a2 | |||
| 7826fddba7 | |||
| 38a450460d | |||
| de7751aae2 | |||
| cf7bc06856 | |||
| 7ac697be2c | |||
| 4de772f5ed | |||
| f7a7fc3b06 | |||
| b4b6029f66 | |||
| 071040f3eb | |||
| cd78c20e0e | |||
| 453a198250 | |||
| 7592122e4d | |||
| 609869f087 | |||
| 4fad1505c7 | |||
| 5881429952 | |||
| 526ed43d61 | |||
| ab9591ba80 | |||
| a8bcb0fcfe | |||
| b420316783 | |||
| 37c6782f95 | |||
| 70a4395a97 | |||
| 31c05c4c7d | |||
| 22018fd374 | |||
| d1f63f5c7e | |||
| 5d75264f0f | |||
| a605f66972 | |||
| 0328250554 | |||
| 70b5ffd928 | |||
| d9b9ed227b | |||
| 14274f8677 | |||
| af604379d3 | |||
| 7d83a08658 | |||
| 2ccbfea8c5 | |||
| fad287007b | |||
| cd9f820b85 | |||
| 582166d378 | |||
| ab9eecb104 | |||
| 52120a8c42 | |||
| a1aa74f96d | |||
| 6ae7ac97f6 | |||
| ba6c036628 | |||
| 7a2fda9076 | |||
| 41d2fde952 | |||
| f0e7396619 | |||
| 6139500e97 | |||
| 0b88944b38 | |||
| 61e33197b2 | |||
| 025e7db765 | |||
| 6f311e72b3 | |||
| a344d42a00 | |||
| f2159be68d | |||
| 7f7238378c | |||
| 1d2b989e8e | |||
| bc0f9b0ea5 | |||
| a885dabc81 | |||
| 6673da46b1 | |||
| 2e9587477c | |||
| 967ee13565 | |||
| a2bd1bb7f6 | |||
| ac3b3652c3 | |||
| 166a1070e5 | |||
| 94d3c447b7 | |||
| 9ff82aecef | |||
| d1af3ef1b3 | |||
| ffce7e3b08 | |||
| f896790ca3 | |||
| 960500a078 | |||
| ce25e2db98 | |||
| 463ae97117 | |||
| 350ab09855 | |||
| 312220fe63 | |||
| 72029b6847 | |||
| 525a5522b0 | |||
| 9c5975a706 | |||
| 9006f89f9f | |||
| 15169b245b | |||
| cc222c02a9 | |||
| 29ab824e5c | |||
| 8d828bc789 | |||
| f038cdc9c9 | |||
| 72d31c2471 | |||
| b31c92c27c | |||
| eb2cb5dd06 | |||
| 52d0df7961 | |||
| 12aadbed51 | |||
| 23703280ee | |||
| 5e50090da0 | |||
| b9257a1092 | |||
| 8cd58303f9 | |||
| b134645637 | |||
| d41cc437b3 | |||
| 26335e669e | |||
| 52b66c744f | |||
| 65c9711754 | |||
| 3d6988cf90 | |||
| c10a2cf15c | |||
| 44082c029f | |||
| 8ba0f008df | |||
| fcabd9957e | |||
| 9f12e870a9 | |||
| d24a2ed9a8 | |||
| b1cb7b5975 | |||
| 9ab997d788 | |||
| 90654cb2c9 | |||
| 9bd51db2b2 | |||
| 7e7193ae61 | |||
| a69333e2b8 | |||
| 1f35727442 | |||
| cbc18e6dcc | |||
| 60b48b0235 | |||
| f4624837a3 | |||
| 0a5af4b8ac | |||
| 74dfd3511f | |||
| ee56abcc8a | |||
| b67f60929c | |||
| 0b71dcded2 | |||
| f305382ef4 | |||
| 917b129517 | |||
| abc646a95c | |||
| 1697518da8 | |||
| 93e7984c61 | |||
| 8d9e12eda3 | |||
| fb0a96332c | |||
| ba7a0f7649 | |||
| a72de719c9 | |||
| ec6e728c0f | |||
| 70fbc7fe27 | |||
| ced4e2da3f | |||
| ae73f1a564 | |||
| 26127ee36a | |||
| 2f0471fff2 | |||
| ad833fb40d | |||
| a544f1517c | |||
| 67ad6abdaa | |||
| 0dfb27ff5f | |||
| ca8f2f2362 | |||
| 2de7fec6dc | |||
| 342e7bf9ff | |||
| ac6f9288a8 | |||
| d54e56f970 | |||
| d974924045 | |||
| e310e6728b | |||
| 74adaeedf2 | |||
| a5602e7e7b | |||
| 2a75a2c9f4 | |||
| 6fa9e048da | |||
| c21006e6d3 | |||
| aa438fa053 | |||
| 1529e19b09 | |||
| 813c1ff0cd | |||
| 8aa06bda38 | |||
| 36fa2a72cf | |||
| 11cf9f8752 | |||
| 9c81dd29bb | |||
| bb22cd62ac | |||
| e5eaa919b1 | |||
| ba37d91878 | |||
| e8459148a8 | |||
| f292643543 | |||
| 80cc1808fc | |||
| d38941062b | |||
| f79ad4e2f6 | |||
| 5567e73730 | |||
| 2ddfb80a5e | |||
| 6a64d98a34 | |||
| b1b66ce165 | |||
| e6c50b3318 | |||
| 260edd8702 | |||
| 920e3ec940 | |||
| 2299cd1c39 | |||
| 31a01ebde1 | |||
| ec07dce76f | |||
| 8ba40d066f | |||
| ed6df57f88 | |||
| 02f7f4c49d | |||
| 77e732218e | |||
| 41fdd5d4c6 | |||
| e859d53b6c | |||
| 9d56414b75 | |||
| fe184fdbdf | |||
| c15fa95bea | |||
| 0e58f7b992 | |||
| 423cec41e3 | |||
| c390e94da6 | |||
| 0ec09f4131 | |||
| ab93a8e3e8 | |||
| c7eb6736ee | |||
| 42ccbb2276 | |||
| 38545eb648 | |||
| eb41a81faa | |||
| 688106e9c3 | |||
| f2142545c7 | |||
| c920c40507 | |||
| 475cb5dc1f | |||
| 85a4013f49 | |||
| 0aa4d02044 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,3 +2,6 @@
|
||||
*.swo
|
||||
node_modules/
|
||||
build/
|
||||
.DS_Store/
|
||||
/coverage
|
||||
coverage.json
|
||||
48
.jshintrc
Normal file
48
.jshintrc
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
|
||||
"browser": true, // Standard browser globals e.g. `window`, `document`.
|
||||
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
|
||||
"curly": true, // Require {} for every new block or scope.
|
||||
"devel": false, // Allow development statements e.g. `console.log();`.
|
||||
"eqeqeq": true, // Require triple equals i.e. `===`.
|
||||
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
|
||||
"freeze": true, // Forbid overwriting prototypes of native objects such as Array, Date and so on.
|
||||
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
|
||||
"indent": 2, // Specify indentation spacing
|
||||
"latedef": true, // Prohibit variable use before definition.
|
||||
"newcap": false, // Require capitalization of all constructor functions e.g. `new F()`.
|
||||
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
|
||||
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
|
||||
"noempty": true, // Prohibit use of empty blocks.
|
||||
"nonew": true, // Prohibits the use of constructor functions for side-effects
|
||||
"quotmark": "single", // Define quotes to string values.
|
||||
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
|
||||
"smarttabs": false, // Supress warnings about mixed tabs and spaces
|
||||
"strict": true, // Require `use strict` pragma in every file.
|
||||
"trailing": true, // Prohibit trailing whitespaces.
|
||||
"undef": true, // Require all non-global variables be declared before they are used.
|
||||
"unused": true, // Warn unused variables.
|
||||
|
||||
"maxparams": 4, // Maximum number of parameters for a function
|
||||
"maxstatements": 15, // Maximum number of statements in a function
|
||||
"maxcomplexity": 10, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity)
|
||||
"maxdepth": 4, // Maximum depth of nested control structures
|
||||
"maxlen": 120, // Maximum number of cols in a line
|
||||
"multistr": true, // Allow use of multiline EOL escaping
|
||||
"experimental": ["asyncawait", "asyncreqawait"],
|
||||
|
||||
"predef": [ // Extra globals.
|
||||
"after",
|
||||
"afterEach",
|
||||
"before",
|
||||
"beforeEach",
|
||||
"define",
|
||||
"describe",
|
||||
"exports",
|
||||
"it",
|
||||
"web3",
|
||||
"artifacts",
|
||||
"contract",
|
||||
"assert"
|
||||
]
|
||||
}
|
||||
1
.soliumignore
Normal file
1
.soliumignore
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
||||
22
.soliumrc.json
Normal file
22
.soliumrc.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"custom-rules-filename": null,
|
||||
"rules": {
|
||||
"imports-on-top": true,
|
||||
"variable-declarations": true,
|
||||
"array-declarations": true,
|
||||
"operator-whitespace": true,
|
||||
"lbrace": true,
|
||||
"mixedcase": false,
|
||||
"camelcase": true,
|
||||
"uppercase": true,
|
||||
"no-with": true,
|
||||
"no-empty-blocks": true,
|
||||
"no-unused-vars": true,
|
||||
"double-quotes": true,
|
||||
"blank-lines": true,
|
||||
"indentation": true,
|
||||
"whitespace": true,
|
||||
"deprecated-suicide": true,
|
||||
"pragma-on-top": true
|
||||
}
|
||||
}
|
||||
@ -5,8 +5,8 @@ language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
before_install:
|
||||
- npm install truffle@3.1.9 -g
|
||||
- npm i -g ethereumjs-testrpc
|
||||
- npm i -g truffle
|
||||
script:
|
||||
- testrpc&
|
||||
- npm test
|
||||
- testrpc > /dev/null &
|
||||
- truffle test
|
||||
|
||||
@ -102,4 +102,6 @@ Main contributors will review your code and possibly ask for changes before your
|
||||
If you have any questions feel free to post them to
|
||||
[github.com/OpenZeppelin/zeppelin-solidity/issues](https://github.com/OpenZeppelin/zeppelin-solidity/issues).
|
||||
|
||||
Finally, if you're looking to collaborate and want to find easy tasks to start, [look at the issues we marked as easy](https://github.com/OpenZeppelin/zeppelin-solidity/labels/easy).
|
||||
|
||||
Thanks for your time and code!
|
||||
|
||||
272
README.md
272
README.md
@ -1,291 +1,63 @@
|
||||
# Zeppelin Solidity
|
||||
[](https://www.npmjs.org/package/zeppelin-solidity)
|
||||
[](https://travis-ci.org/bitpay/bitcore-lib)
|
||||
[](https://travis-ci.org/OpenZeppelin/zeppelin-solidity)
|
||||
|
||||
Zeppelin is a library for writing secure Smart Contracts 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.
|
||||
- in the [Solidity language](http://solidity.readthedocs.io/en/develop/).
|
||||
|
||||
> NOTE: New to smart contract development? Check our [introductory guide](https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05#.cox40d2ut).
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
## Contracts
|
||||
## Developer Resources
|
||||
|
||||
### Ownable
|
||||
Base contract with an owner.
|
||||
Building a distributed application, protocol or organization with OpenZeppelin?
|
||||
|
||||
#### Ownable( )
|
||||
Sets the address of the creator of the contract as the owner.
|
||||
- Read documentation: http://zeppelin-solidity.readthedocs.io/en/latest/
|
||||
|
||||
#### modifier onlyOwner( )
|
||||
Prevents function from running if it is called by anyone other than the owner.
|
||||
- Ask for help and follow progress at: https://slack.openzeppelin.org/
|
||||
|
||||
#### transfer(address newOwner) onlyOwner
|
||||
Transfers ownership of the contract to the passed address.
|
||||
|
||||
---
|
||||
### Stoppable
|
||||
Base contract that provides an emergency stop mechanism.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
#### emergencyStop( ) external onlyOwner
|
||||
Triggers the stop mechanism on the contract. After this function is called (by the owner of the contract), any function with modifier stopInEmergency will not run.
|
||||
|
||||
#### modifier stopInEmergency
|
||||
Prevents function from running if stop mechanism is activated.
|
||||
|
||||
#### modifier onlyInEmergency
|
||||
Only runs if stop mechanism is activated.
|
||||
|
||||
#### release( ) external onlyOwner onlyInEmergency
|
||||
Deactivates the stop mechanism.
|
||||
|
||||
---
|
||||
### Killable
|
||||
Base contract that can be killed by owner.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
#### kill( ) onlyOwner
|
||||
Destroys the contract and sends funds back to the owner.
|
||||
___
|
||||
### Claimable
|
||||
Extension for the Ownable contract, where the ownership needs to be claimed
|
||||
|
||||
#### transfer(address newOwner) onlyOwner
|
||||
Sets the passed address as the pending owner.
|
||||
|
||||
#### modifier onlyPendingOwner
|
||||
Function only runs if called by pending owner.
|
||||
|
||||
#### claimOwnership( ) onlyPendingOwner
|
||||
Completes transfer of ownership by setting pending owner as the new owner.
|
||||
___
|
||||
### Migrations
|
||||
Base contract that allows for a new instance of itself to be created at a different address.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
#### upgrade(address new_address) onlyOwner
|
||||
Creates a new instance of the contract at the passed address.
|
||||
|
||||
#### setCompleted(uint completed) onlyOwner
|
||||
Sets the last time that a migration was completed.
|
||||
|
||||
___
|
||||
### SafeMath
|
||||
Provides functions of mathematical operations with safety checks.
|
||||
|
||||
#### assert(bool assertion) internal
|
||||
Throws an error if the passed result is false. Used in this contract by checking mathematical expressions.
|
||||
|
||||
#### safeMul(uint a, uint b) internal returns (uint)
|
||||
Multiplies two unisgned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier.
|
||||
|
||||
#### safeSub(uint a, unit b) internal returns (uint)
|
||||
Checks that b is not greater than a before subtracting.
|
||||
|
||||
#### safeAdd(unit a, unit b) internal returns (uint)
|
||||
Checks that the result is greater than both a and b.
|
||||
|
||||
___
|
||||
### LimitBalance
|
||||
|
||||
Base contract that provides mechanism for limiting the amount of funds a contract can hold.
|
||||
|
||||
#### LimitBalance(unit _limit)
|
||||
Constructor takes an unisgned integer and sets it as the limit of funds this contract can hold.
|
||||
|
||||
#### modifier limitedPayable()
|
||||
Throws an error if this contract's balance is already above the limit.
|
||||
|
||||
___
|
||||
### PullPayment
|
||||
Base contract supporting async send for pull payments.
|
||||
Inherit from this contract and use asyncSend instead of send.
|
||||
|
||||
#### asyncSend(address dest, uint amount) internal
|
||||
Adds sent amount to available balance that payee can pull from this contract, called by payer.
|
||||
|
||||
#### withdrawPayments( )
|
||||
Sends designated balance to payee calling the contract. Throws error if designated balance is 0, if contract does not hold enough funds ot pay the payee, or if the send transaction is not successful.
|
||||
|
||||
___
|
||||
### StandardToken
|
||||
Based on code by FirstBlood: [FirstBloodToken.sol]
|
||||
|
||||
Inherits from contract SafeMath. Implementation of abstract contract ERC20 (see https://github.com/ethereum/EIPs/issues/20)
|
||||
|
||||
[FirstBloodToken.sol]: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
|
||||
|
||||
#### approve(address _spender, uint _value) returns (bool success)
|
||||
Sets the amount of the sender's token balance that the passed address is approved to use.
|
||||
|
||||
###allowance(address _owner, address _spender) constant returns (uint remaining)
|
||||
Returns the approved amount of the owner's balance that the spender can use.
|
||||
|
||||
###balanceOf(address _owner) constant returns (uint balance)
|
||||
Returns the token balance of the passed address.
|
||||
|
||||
###transferFrom(address _from, address _to, uint _value) returns (bool success)
|
||||
Transfers tokens from an account that the sender is approved to transfer from. Amount must not be greater than the approved amount or the account's balance.
|
||||
|
||||
###function transfer(address _to, uint _value) returns (bool success)
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
|
||||
___
|
||||
### BasicToken
|
||||
Simpler version of StandardToken, with no allowances
|
||||
|
||||
#### balanceOf(address _owner) constant returns (uint balance)
|
||||
Returns the token balance of the passed address.
|
||||
|
||||
###function transfer(address _to, uint _value) returns (bool success)
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
|
||||
___
|
||||
### CrowdsaleToken
|
||||
Simple ERC20 Token example, with crowdsale token creation.
|
||||
|
||||
Inherits from contract StandardToken.
|
||||
|
||||
#### createTokens(address recipient) payable
|
||||
Creates tokens based on message value and credits to the recipient.
|
||||
|
||||
#### getPrice() constant returns (uint result)
|
||||
Returns the amount of tokens per 1 ether.
|
||||
|
||||
|
||||
___
|
||||
### Bounty
|
||||
To create a bounty for your contract, inherit from the base `Bounty` contract and provide an implementation for `deployContract()` returning the new contract address.
|
||||
|
||||
```
|
||||
import {Bounty, Target} from "./zeppelin/Bounty.sol";
|
||||
import "./YourContract.sol";
|
||||
|
||||
contract YourBounty is Bounty {
|
||||
function deployContract() internal returns(address) {
|
||||
return new YourContract()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, implement invariant logic into your smart contract.
|
||||
Your main contract should inherit from the Target class and implement the checkInvariant method. This is a function that should check everything your contract assumes to be true all the time. If this function returns false, it means your contract was broken in some way and is in an inconsistent state. This is what security researchers will try to acomplish when trying to get the bounty.
|
||||
|
||||
At contracts/YourContract.sol
|
||||
|
||||
```
|
||||
import {Bounty, Target} from "./zeppelin/Bounty.sol";
|
||||
contract YourContract is Target {
|
||||
function checkInvariant() returns(bool) {
|
||||
// Implement your logic to make sure that none of the invariants are broken.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, deploy your bounty contract along with your main contract to the network.
|
||||
|
||||
At `migrations/2_deploy_contracts.js`
|
||||
|
||||
```
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(YourContract);
|
||||
deployer.deploy(YourBounty);
|
||||
};
|
||||
```
|
||||
|
||||
Next, add a reward to the bounty contract
|
||||
|
||||
After deploying the contract, send reward funds into the bounty contract.
|
||||
|
||||
From `truffle console`
|
||||
|
||||
```
|
||||
bounty = YourBounty.deployed();
|
||||
address = 0xb9f68f96cde3b895cc9f6b14b856081b41cb96f1; // your account address
|
||||
reward = 5; // reward to pay to a researcher who breaks your contract
|
||||
|
||||
web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: bounty.address,
|
||||
value: web3.toWei(reward, "ether")
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
If researchers break the contract, they can claim their reward.
|
||||
|
||||
For each researcher who wants to hack the contract and claims the reward, refer to our [test](./test/Bounty.js) for the detail.
|
||||
|
||||
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, kill the contract so that all the rewards go back to the owner.
|
||||
|
||||
```
|
||||
bounty.kill();
|
||||
```
|
||||
|
||||
|
||||
## More Developer Resources
|
||||
|
||||
Building a distributed application, protocol or organization with Zeppelin?
|
||||
|
||||
- Ask for help and follow progress at: https://zeppelin-slackin.herokuapp.com/
|
||||
|
||||
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/)
|
||||
@ -295,6 +67,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
290
audit/ZeppelinAudit.md
Normal 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.8, 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.
|
||||
|
||||
@ -1,42 +1,56 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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;
|
||||
if (claimed) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
if (researcher == 0) {
|
||||
throw;
|
||||
}
|
||||
// Check Target contract invariants
|
||||
if (target.checkInvariant()) {
|
||||
throw;
|
||||
@ -47,12 +61,18 @@ contract Bounty is PullPayment, Killable {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
@ -1,30 +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 transfer(address newOwner) onlyOwner {
|
||||
pendingOwner = newOwner;
|
||||
}
|
||||
|
||||
function claimOwnership() onlyPendingOwner {
|
||||
owner = pendingOwner;
|
||||
pendingOwner = 0x0;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,66 +1,53 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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 is Shareable {
|
||||
// FIELDS
|
||||
contract DayLimit {
|
||||
|
||||
uint public dailyLimit;
|
||||
uint public spentToday;
|
||||
uint public lastDay;
|
||||
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple modifier for daily limit.
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (underLimit(_value))
|
||||
_;
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR
|
||||
// stores initial daily limit and records the present day's index.
|
||||
/**
|
||||
* @dev Constructor that sets the passed value as a dailyLimit.
|
||||
* @param _limit Uint to represent the daily limit.
|
||||
*/
|
||||
function DayLimit(uint _limit) {
|
||||
dailyLimit = _limit;
|
||||
lastDay = today();
|
||||
}
|
||||
|
||||
|
||||
// METHODS
|
||||
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
/**
|
||||
* @dev sets the daily limit. Does not alter the amount already spent today.
|
||||
* @param _newLimit Uint to represent the new limit.
|
||||
*/
|
||||
function _setDailyLimit(uint _newLimit) internal {
|
||||
dailyLimit = _newLimit;
|
||||
}
|
||||
|
||||
// resets the amount already spent today. needs many of the owners to confirm
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
/**
|
||||
* @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 onlyOwner returns (bool) {
|
||||
/**
|
||||
* @dev Checks to see if there is enough resource to spend today. If true, the resource may be expended.
|
||||
* @param _value Uint representing the amount of resource to spend.
|
||||
* @return A boolean that is True if the resource was spended and false otherwise.
|
||||
*/
|
||||
function underLimit(uint _value) internal returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > lastDay) {
|
||||
spentToday = 0;
|
||||
lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
// overflow protection // dailyLimit check
|
||||
if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) {
|
||||
spentToday += _value;
|
||||
return true;
|
||||
@ -68,8 +55,21 @@ contract DayLimit is Shareable {
|
||||
return false;
|
||||
}
|
||||
|
||||
// determines today's index.
|
||||
/**
|
||||
* @dev Private function to determine today's index
|
||||
* @return Uint of today's index.
|
||||
*/
|
||||
function today() private constant returns (uint) {
|
||||
return now / 1 days;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Simple modifier for daily limit.
|
||||
*/
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (!underLimit(_value)) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import "./Ownable.sol";
|
||||
|
||||
|
||||
/*
|
||||
* Killable
|
||||
* Base contract that can be killed by owner. All funds in contract will be sent to the owner.
|
||||
*/
|
||||
contract Killable is Ownable {
|
||||
function kill() onlyOwner {
|
||||
selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,33 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @dev Constructor that sets the passed value as a limit.
|
||||
* @param _limit Uint to represent the limit.
|
||||
*/
|
||||
function LimitBalance(uint _limit) {
|
||||
limit = _limit;
|
||||
}
|
||||
|
||||
modifier limitedPayable() {
|
||||
/**
|
||||
* @dev Checks if limit was reached. Case true, it throws.
|
||||
*/
|
||||
modifier limitedPayable() {
|
||||
if (this.balance > limit) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,55 +1,60 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./Multisig.sol";
|
||||
import "./Shareable.sol";
|
||||
import "./ownership/Multisig.sol";
|
||||
import "./ownership/Shareable.sol";
|
||||
import "./DayLimit.sol";
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* 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;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR
|
||||
|
||||
// just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
|
||||
Shareable(_owners, _required)
|
||||
/**
|
||||
* Constructor, sets the owners addresses, number of approvals required, and daily spending limit
|
||||
* @param _owners A list of owners.
|
||||
* @param _required The amount required for a transaction to be approved.
|
||||
*/
|
||||
function MultisigWallet(address[] _owners, uint _required, uint _daylimit)
|
||||
Shareable(_owners, _required)
|
||||
DayLimit(_daylimit) { }
|
||||
|
||||
|
||||
// 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.
|
||||
/**
|
||||
* @dev Outside-visible transaction entry point. Executes transaction immediately if below daily
|
||||
* spending limit. If not, goes into multisig process. We provide a hash on return to allow the
|
||||
* sender to provide shortcuts for the other confirmations (allowing them to avoid replicating
|
||||
* the _to, _value, and _data arguments). They still get the option of using them if they want,
|
||||
* anyways.
|
||||
* @param _to The receiver address
|
||||
* @param _value The value to send
|
||||
* @param _data The data part of the transaction
|
||||
*/
|
||||
function execute(address _to, uint _value, bytes _data) external onlyOwner returns (bytes32 _r) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if (underLimit(_value)) {
|
||||
@ -61,7 +66,7 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
|
||||
return 0;
|
||||
}
|
||||
// determine our operation hash.
|
||||
_r = sha3(msg.data, block.number);
|
||||
_r = keccak256(msg.data, block.number);
|
||||
if (!confirm(_r) && txs[_r].to == 0) {
|
||||
txs[_r].to = _to;
|
||||
txs[_r].value = _value;
|
||||
@ -70,8 +75,11 @@ 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)) {
|
||||
@ -83,9 +91,26 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Updates the daily limit value.
|
||||
* @param _newLimit
|
||||
*/
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(keccak256(msg.data)) external {
|
||||
_setDailyLimit(_newLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Resets the value spent to enable more spending
|
||||
*/
|
||||
function resetSpentToday() onlymanyowners(keccak256(msg.data)) external {
|
||||
_resetSpentToday();
|
||||
}
|
||||
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
/**
|
||||
* @dev Clears the list of transactions pending approval.
|
||||
*/
|
||||
function clearPending() internal {
|
||||
uint length = pendingsIndex.length;
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
|
||||
@ -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 transfer(address newOwner) onlyOwner {
|
||||
if (newOwner != address(0)) owner = newOwner;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
contracts/ReentrancyGuard.sol
Normal file
34
contracts/ReentrancyGuard.sol
Normal file
@ -0,0 +1,34 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
/**
|
||||
* @title Helps contracts guard agains rentrancy attacks.
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @notice If you mark a function `nonReentrant`, you should also
|
||||
* mark it `external`.
|
||||
*/
|
||||
contract ReentrancyGuard {
|
||||
|
||||
/**
|
||||
* @dev We use a single lock for the whole contract.
|
||||
*/
|
||||
bool private rentrancy_lock = false;
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
if(rentrancy_lock == false) {
|
||||
rentrancy_lock = true;
|
||||
_;
|
||||
rentrancy_lock = false;
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +1,53 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
* Math operations with safety checks
|
||||
*/
|
||||
contract SafeMath {
|
||||
function safeMul(uint a, uint b) internal returns (uint) {
|
||||
library SafeMath {
|
||||
function mul(uint a, uint b) internal returns (uint) {
|
||||
uint c = a * b;
|
||||
assert(a == 0 || c / a == b);
|
||||
return c;
|
||||
}
|
||||
|
||||
function safeSub(uint a, uint b) internal returns (uint) {
|
||||
function div(uint a, uint b) internal returns (uint) {
|
||||
// assert(b > 0); // Solidity automatically throws when dividing by 0
|
||||
uint c = a / b;
|
||||
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
|
||||
return c;
|
||||
}
|
||||
|
||||
function sub(uint a, uint b) internal returns (uint) {
|
||||
assert(b <= a);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function safeAdd(uint a, uint b) internal returns (uint) {
|
||||
function add(uint a, uint b) internal returns (uint) {
|
||||
uint c = a + b;
|
||||
assert(c>=a && c>=b);
|
||||
assert(c >= a);
|
||||
return c;
|
||||
}
|
||||
|
||||
function max64(uint64 a, uint64 b) internal constant returns (uint64) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
function min64(uint64 a, uint64 b) internal constant returns (uint64) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function max256(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
function min256(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function assert(bool assertion) internal {
|
||||
if (!assertion) throw;
|
||||
if (!assertion) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
|
||||
import "./Ownable.sol";
|
||||
|
||||
|
||||
/*
|
||||
* Stoppable
|
||||
* Abstract contract that allows children to implement an
|
||||
* emergency stop mechanism.
|
||||
*/
|
||||
contract Stoppable is Ownable {
|
||||
bool public stopped;
|
||||
|
||||
modifier stopInEmergency { if (!stopped) _; }
|
||||
modifier onlyInEmergency { if (stopped) _; }
|
||||
|
||||
// called by the owner on emergency, triggers stopped state
|
||||
function emergencyStop() external onlyOwner {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
// called by the owner on end of emergency, returns to normal state
|
||||
function release() external onlyOwner onlyInEmergency {
|
||||
stopped = false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import '../PullPayment.sol';
|
||||
|
||||
// UNSAFE CODE, DO NOT USE!
|
||||
|
||||
contract BadArrayUse is PullPayment {
|
||||
address[] employees;
|
||||
|
||||
function payBonus() {
|
||||
for (var i = 0; i < employees.length; i++) {
|
||||
address employee = employees[i];
|
||||
uint bonus = calculateBonus(employee);
|
||||
asyncSend(employee, bonus);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateBonus(address employee) returns (uint) {
|
||||
// some expensive computation...
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
// UNSAFE CODE, DO NOT USE!
|
||||
|
||||
contract BadFailEarly {
|
||||
|
||||
uint constant DEFAULT_SALARY = 50000;
|
||||
mapping(string => uint) nameToSalary;
|
||||
|
||||
function getSalary(string name) constant returns (uint) {
|
||||
if (bytes(name).length != 0 && nameToSalary[name] != 0) {
|
||||
return nameToSalary[name];
|
||||
} else {
|
||||
return DEFAULT_SALARY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
// UNSAFE CODE, DO NOT USE!
|
||||
|
||||
contract BadPushPayments {
|
||||
|
||||
address highestBidder;
|
||||
uint highestBid;
|
||||
|
||||
function bid() {
|
||||
if (msg.value < highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
// return bid to previous winner
|
||||
if (!highestBidder.send(highestBid)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import '../PullPayment.sol';
|
||||
|
||||
contract GoodArrayUse is PullPayment {
|
||||
address[] employees;
|
||||
mapping(address => uint) bonuses;
|
||||
|
||||
function payBonus() {
|
||||
for (uint i = 0; i < employees.length; i++) {
|
||||
address employee = employees[i];
|
||||
uint bonus = bonuses[employee];
|
||||
asyncSend(employee, bonus);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateBonus(address employee) returns (uint) {
|
||||
uint bonus = 0;
|
||||
// some expensive computation...
|
||||
bonuses[employee] = bonus;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
contract GoodFailEarly {
|
||||
|
||||
uint constant DEFAULT_SALARY = 50000;
|
||||
mapping(string => uint) nameToSalary;
|
||||
|
||||
function getSalary(string name) constant returns (uint) {
|
||||
if (bytes(name).length == 0) throw;
|
||||
if (nameToSalary[name] == 0) throw;
|
||||
|
||||
return nameToSalary[name];
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
contract GoodPullPayments {
|
||||
address highestBidder;
|
||||
uint highestBid;
|
||||
mapping(address => uint) refunds;
|
||||
|
||||
function bid() external {
|
||||
if (msg.value < highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
refunds[highestBidder] += highestBid;
|
||||
}
|
||||
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
|
||||
function withdrawBid() external {
|
||||
uint refund = refunds[msg.sender];
|
||||
refunds[msg.sender] = 0;
|
||||
if (!msg.sender.send(refund)) {
|
||||
refunds[msg.sender] = refund;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
/*
|
||||
* Proof of Existence example contract
|
||||
* see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05
|
||||
*/
|
||||
contract ProofOfExistence {
|
||||
|
||||
mapping (bytes32 => bool) public proofs;
|
||||
|
||||
// store a proof of existence in the contract state
|
||||
function storeProof(bytes32 proof) {
|
||||
proofs[proof] = true;
|
||||
}
|
||||
|
||||
// calculate and store the proof for a document
|
||||
function notarize(string document) {
|
||||
var proof = calculateProof(document);
|
||||
storeProof(proof);
|
||||
}
|
||||
|
||||
// helper function to get a document's sha256
|
||||
function calculateProof(string document) constant returns (bytes32) {
|
||||
return sha256(document);
|
||||
}
|
||||
|
||||
// check if a document has been notarized
|
||||
function checkDocument(string document) constant returns (bool) {
|
||||
var proof = calculateProof(document);
|
||||
return hasProof(proof);
|
||||
}
|
||||
|
||||
// returns true if proof is stored
|
||||
function hasProof(bytes32 proof) constant returns (bool) {
|
||||
return proofs[proof];
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
import '../PullPayment.sol';
|
||||
|
||||
contract PullPaymentBid is PullPayment {
|
||||
address public highestBidder;
|
||||
uint public highestBid;
|
||||
|
||||
function bid() external {
|
||||
if (msg.value <= highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
asyncSend(highestBidder, highestBid);
|
||||
}
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
}
|
||||
@ -1,24 +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 stopInEmergency {
|
||||
if (msg.value <= highestBid) throw;
|
||||
|
||||
if (highestBidder != 0) {
|
||||
asyncSend(highestBidder, highestBid);
|
||||
}
|
||||
highestBidder = msg.sender;
|
||||
highestBid = msg.value;
|
||||
}
|
||||
|
||||
function withdraw() onlyInEmergency {
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
}
|
||||
23
contracts/lifecycle/Destructible.sol
Normal file
23
contracts/lifecycle/Destructible.sol
Normal file
@ -0,0 +1,23 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* @dev Transfers the current balance to the owner and terminates the contract.
|
||||
*/
|
||||
function destroy() onlyOwner {
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
function destroyAndSend(address _recipient) onlyOwner {
|
||||
selfdestruct(_recipient);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,12 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import './Ownable.sol';
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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 {
|
||||
uint public lastCompletedMigration;
|
||||
|
||||
51
contracts/lifecycle/Pausable.sol
Normal file
51
contracts/lifecycle/Pausable.sol
Normal file
@ -0,0 +1,51 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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() {
|
||||
if (paused) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev modifier to allow actions only when the contract IS NOT paused
|
||||
*/
|
||||
modifier whenPaused {
|
||||
if (!paused) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
34
contracts/lifecycle/TokenDestructible.sol
Normal file
34
contracts/lifecycle/TokenDestructible.sol
Normal file
@ -0,0 +1,34 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* @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(uint i = 0; i < tokens.length; i++) {
|
||||
ERC20Basic token = ERC20Basic(tokens[i]);
|
||||
uint256 balance = token.balanceOf(this);
|
||||
token.transfer(owner, balance);
|
||||
}
|
||||
|
||||
// Transfer Eth to owner and terminate contract
|
||||
selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
40
contracts/ownership/Claimable.sol
Normal file
40
contracts/ownership/Claimable.sol
Normal file
@ -0,0 +1,40 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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() {
|
||||
if (msg.sender != pendingOwner) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
21
contracts/ownership/Contactable.sol
Normal file
21
contracts/ownership/Contactable.sol
Normal file
@ -0,0 +1,21 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
43
contracts/ownership/DelayedClaimable.sol
Normal file
43
contracts/ownership/DelayedClaimable.sol
Normal file
@ -0,0 +1,43 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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 {
|
||||
|
||||
uint public end;
|
||||
uint 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(uint _start, uint _end) onlyOwner {
|
||||
if (_start > _end)
|
||||
throw;
|
||||
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 {
|
||||
if ((block.number > end) || (block.number < start))
|
||||
throw;
|
||||
owner = pendingOwner;
|
||||
pendingOwner = 0x0;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
}
|
||||
21
contracts/ownership/HasNoContracts.sol
Normal file
21
contracts/ownership/HasNoContracts.sol
Normal file
@ -0,0 +1,21 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
44
contracts/ownership/HasNoEther.sol
Normal file
44
contracts/ownership/HasNoEther.sol
Normal file
@ -0,0 +1,44 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
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 {
|
||||
if(msg.value > 0) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
if(!owner.send(this.balance)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
contracts/ownership/HasNoTokens.sol
Normal file
34
contracts/ownership/HasNoTokens.sol
Normal file
@ -0,0 +1,34 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
import "./Ownable.sol";
|
||||
import "../token/ERC20Basic.sol";
|
||||
|
||||
/**
|
||||
* @title Contracts that should not own Tokens
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @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_ Uint the amount of the specified token
|
||||
* @param data_ Bytes The data passed from the caller.
|
||||
*/
|
||||
function tokenFallback(address from_, uint value_, bytes data_) external {
|
||||
throw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Reclaim all ERC20Basic compatible tokens
|
||||
* @param tokenAddr address The address of the token contract
|
||||
*/
|
||||
function reclaimToken(address tokenAddr) external onlyOwner {
|
||||
ERC20Basic tokenInst = ERC20Basic(tokenAddr);
|
||||
uint256 balance = tokenInst.balanceOf(this);
|
||||
tokenInst.transfer(owner, balance);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -26,4 +26,3 @@ contract Multisig {
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
|
||||
function confirm(bytes32 _h) returns (bool);
|
||||
}
|
||||
|
||||
14
contracts/ownership/NoOwner.sol
Normal file
14
contracts/ownership/NoOwner.sol
Normal file
@ -0,0 +1,14 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
import "./HasNoEther.sol";
|
||||
import "./HasNoTokens.sol";
|
||||
import "./HasNoContracts.sol";
|
||||
|
||||
/**
|
||||
* @title Base contract for contracts that should not own things.
|
||||
* @author Remco Bloemen <remco@2π.com>
|
||||
* @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 {
|
||||
}
|
||||
43
contracts/ownership/Ownable.sol
Normal file
43
contracts/ownership/Ownable.sol
Normal file
@ -0,0 +1,43 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
if (msg.sender != owner) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +1,13 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
/**
|
||||
* @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 {
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
@ -21,69 +16,72 @@ contract Shareable {
|
||||
uint index;
|
||||
}
|
||||
|
||||
|
||||
// FIELDS
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public required;
|
||||
|
||||
// list of owners
|
||||
uint[256] owners;
|
||||
uint constant c_maxOwners = 250;
|
||||
address[256] owners;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) ownerIndex;
|
||||
mapping(address => uint) ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) pendings;
|
||||
bytes32[] pendingsIndex;
|
||||
|
||||
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyOwner {
|
||||
if (isOwner(msg.sender))
|
||||
_;
|
||||
if (!isOwner(msg.sender)) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
|
||||
/**
|
||||
* @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))
|
||||
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.
|
||||
/**
|
||||
* @dev Constructor is given the number of sigs required to do protected "onlymanyowners"
|
||||
* transactions as well as the selection of addresses capable of confirming them.
|
||||
* @param _owners A list of owners.
|
||||
* @param _required The amount required for a transaction to be approved.
|
||||
*/
|
||||
function Shareable(address[] _owners, uint _required) {
|
||||
owners[1] = uint(msg.sender);
|
||||
ownerIndex[uint(msg.sender)] = 1;
|
||||
owners[1] = msg.sender;
|
||||
ownerIndex[msg.sender] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i) {
|
||||
owners[2 + i] = uint(_owners[i]);
|
||||
ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
owners[2 + i] = _owners[i];
|
||||
ownerIndex[_owners[i]] = 2 + i;
|
||||
}
|
||||
required = _required;
|
||||
if (required > owners.length) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// METHODS
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
/**
|
||||
* @dev Revokes a prior confirmation of the given operation.
|
||||
* @param _operation A string identifying the operation.
|
||||
*/
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint index = ownerIndex[uint(msg.sender)];
|
||||
uint index = ownerIndex[msg.sender];
|
||||
// make sure they're an owner
|
||||
if (index == 0) return;
|
||||
if (index == 0) {
|
||||
return;
|
||||
}
|
||||
uint ownerIndexBit = 2**index;
|
||||
var pending = pendings[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
@ -93,34 +91,56 @@ contract Shareable {
|
||||
}
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
/**
|
||||
* @dev Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
* @param ownerIndex Uint The index of the owner
|
||||
* @return The address of the owner
|
||||
*/
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) returns (bool) {
|
||||
return ownerIndex[uint(_addr)] > 0;
|
||||
/**
|
||||
* @dev Checks if given address is an owner.
|
||||
* @param _addr address The address which you want to check.
|
||||
* @return True if the address is an owner and fase otherwise.
|
||||
*/
|
||||
function isOwner(address _addr) constant returns (bool) {
|
||||
return ownerIndex[_addr] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to check is specific owner has already confirme the operation.
|
||||
* @param _operation The operation identifier.
|
||||
* @param _owner The owner address.
|
||||
* @return True if the owner has confirmed and false otherwise.
|
||||
*/
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
var pending = pendings[_operation];
|
||||
uint index = ownerIndex[uint(_owner)];
|
||||
uint index = ownerIndex[_owner];
|
||||
|
||||
// make sure they're an owner
|
||||
if (index == 0) return false;
|
||||
if (index == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**index;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
/**
|
||||
* @dev Confirm and operation and checks if it's already executable.
|
||||
* @param _operation The operation identifier.
|
||||
* @return Returns true when operation can be executed.
|
||||
*/
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint index = ownerIndex[uint(msg.sender)];
|
||||
uint index = ownerIndex[msg.sender];
|
||||
// make sure they're an owner
|
||||
if (index == 0) return;
|
||||
if (index == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
var pending = pendings[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
@ -143,23 +163,27 @@ contract Shareable {
|
||||
delete pendingsIndex[pendings[_operation].index];
|
||||
delete pendings[_operation];
|
||||
return true;
|
||||
} else {
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Clear the pending list.
|
||||
*/
|
||||
function clearPending() internal {
|
||||
uint length = pendingsIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
if (pendingsIndex[i] != 0)
|
||||
delete pendings[pendingsIndex[i]];
|
||||
for (uint i = 0; i < length; ++i) {
|
||||
if (pendingsIndex[i] != 0) {
|
||||
delete pendings[pendingsIndex[i]];
|
||||
}
|
||||
}
|
||||
delete pendingsIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
50
contracts/payment/PullPayment.sol
Normal file
50
contracts/payment/PullPayment.sol
Normal file
@ -0,0 +1,50 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import '../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 uint;
|
||||
|
||||
mapping(address => uint) public payments;
|
||||
uint public totalPayments;
|
||||
|
||||
/**
|
||||
* @dev Called by the payer to store the sent amount as credit to be pulled.
|
||||
* @param dest The destination address of the funds.
|
||||
* @param amount The amount to transfer.
|
||||
*/
|
||||
function asyncSend(address dest, uint amount) internal {
|
||||
payments[dest] = payments[dest].add(amount);
|
||||
totalPayments = totalPayments.add(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 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;
|
||||
}
|
||||
|
||||
totalPayments = totalPayments.sub(payment);
|
||||
payments[payee] = 0;
|
||||
|
||||
if (!payee.send(payment)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import '../SafeMath.sol';
|
||||
|
||||
contract SafeMathMock is SafeMath {
|
||||
uint public result;
|
||||
|
||||
function multiply(uint a, uint b) {
|
||||
result = safeMul(a, b);
|
||||
}
|
||||
|
||||
function subtract(uint a, uint b) {
|
||||
result = safeSub(a, b);
|
||||
}
|
||||
|
||||
function add(uint a, uint b) {
|
||||
result = safeAdd(a, b);
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
pragma solidity ^0.4.4;
|
||||
import '../Stoppable.sol';
|
||||
|
||||
// mock class using Stoppable
|
||||
contract StoppableMock is Stoppable {
|
||||
bool public drasticMeasureTaken;
|
||||
uint public count;
|
||||
|
||||
function StoppableMock() {
|
||||
drasticMeasureTaken = false;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
function normalProcess() external stopInEmergency {
|
||||
count++;
|
||||
}
|
||||
|
||||
function drasticMeasure() external onlyInEmergency {
|
||||
drasticMeasureTaken = true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +1,47 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './ERC20Basic.sol';
|
||||
import '../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 uint;
|
||||
|
||||
mapping(address => uint) balances;
|
||||
|
||||
function transfer(address _to, uint _value) {
|
||||
if (balances[msg.sender] < _value) {
|
||||
throw;
|
||||
}
|
||||
balances[msg.sender] = safeSub(balances[msg.sender], _value);
|
||||
balances[_to] = safeAdd(balances[_to], _value);
|
||||
/**
|
||||
* @dev Fix for the ERC20 short address attack.
|
||||
*/
|
||||
modifier onlyPayloadSize(uint size) {
|
||||
if(msg.data.length < size + 4) {
|
||||
throw;
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev transfer token for a specified address
|
||||
* @param _to The address to transfer to.
|
||||
* @param _value The amount to be transferred.
|
||||
*/
|
||||
function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) {
|
||||
balances[msg.sender] = balances[msg.sender].sub(_value);
|
||||
balances[_to] = balances[_to].add(_value);
|
||||
Transfer(msg.sender, _to, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the balance of the specified address.
|
||||
* @param _owner The address to query the the balance of.
|
||||
* @return An uint representing the amount owned by the passed address.
|
||||
*/
|
||||
function balanceOf(address _owner) constant returns (uint balance) {
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,38 +1,60 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./StandardToken.sol";
|
||||
|
||||
|
||||
/*
|
||||
* CrowdsaleToken
|
||||
/**
|
||||
* @title CrowdsaleToken
|
||||
*
|
||||
* Simple ERC20 Token example, with crowdsale token creation
|
||||
* @dev Simple ERC20 Token example, with crowdsale token creation
|
||||
* @dev IMPORTANT NOTE: do not use or deploy this contract as-is. It needs some changes to be
|
||||
* production ready.
|
||||
*/
|
||||
contract CrowdsaleToken is StandardToken {
|
||||
|
||||
string public name = "CrowdsaleToken";
|
||||
string public symbol = "CRW";
|
||||
uint public decimals = 18;
|
||||
string public constant name = "CrowdsaleToken";
|
||||
string public constant symbol = "CRW";
|
||||
uint public constant decimals = 18;
|
||||
// replace with your fund collection multisig address
|
||||
address public constant multisig = 0x0;
|
||||
|
||||
// 1 ether = 500 example tokens
|
||||
uint PRICE = 500;
|
||||
|
||||
// 1 ether = 500 example tokens
|
||||
uint public constant PRICE = 500;
|
||||
|
||||
/**
|
||||
* @dev Fallback function which receives ether and sends the appropriate number of tokens to the
|
||||
* msg.sender.
|
||||
*/
|
||||
function () payable {
|
||||
createTokens(msg.sender);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dev Creates tokens and send to the specified address.
|
||||
* @param recipient The address which will recieve the new tokens.
|
||||
*/
|
||||
function createTokens(address recipient) payable {
|
||||
if (msg.value == 0) throw;
|
||||
if (msg.value == 0) {
|
||||
throw;
|
||||
}
|
||||
|
||||
uint tokens = safeMul(msg.value, getPrice());
|
||||
uint tokens = msg.value.mul(getPrice());
|
||||
totalSupply = totalSupply.add(tokens);
|
||||
|
||||
totalSupply = safeAdd(totalSupply, tokens);
|
||||
balances[recipient] = safeAdd(balances[recipient], tokens);
|
||||
balances[recipient] = balances[recipient].add(tokens);
|
||||
|
||||
if (!multisig.send(msg.value)) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// replace this with any other price function
|
||||
function getPrice() constant returns (uint result){
|
||||
|
||||
/**
|
||||
* @dev replace this with any other price function
|
||||
* @return The price per unit of token.
|
||||
*/
|
||||
function getPrice() constant returns (uint result) {
|
||||
return PRICE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
contract ERC20 is ERC20Basic {
|
||||
function allowance(address owner, address spender) constant returns (uint);
|
||||
|
||||
function transfer(address to, uint value) returns (bool ok);
|
||||
function transferFrom(address from, address to, uint value) returns (bool ok);
|
||||
function approve(address spender, uint value) returns (bool ok);
|
||||
event Transfer(address indexed from, address indexed to, uint value);
|
||||
function transferFrom(address from, address to, uint value);
|
||||
function approve(address spender, uint value);
|
||||
event Approval(address indexed owner, address indexed spender, uint value);
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
/*
|
||||
* 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/20
|
||||
*/
|
||||
contract ERC20Basic {
|
||||
uint public totalSupply;
|
||||
|
||||
57
contracts/token/LimitedTransferToken.sol
Normal file
57
contracts/token/LimitedTransferToken.sol
Normal file
@ -0,0 +1,57 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
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, uint _value) {
|
||||
if (_value > transferableTokens(_sender, uint64(now))) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, uint _value) canTransfer(msg.sender, _value) {
|
||||
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, uint _value) canTransfer(_from, _value) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
51
contracts/token/MintableToken.sol
Normal file
51
contracts/token/MintableToken.sol
Normal file
@ -0,0 +1,51 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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, uint value);
|
||||
event MintFinished();
|
||||
|
||||
bool public mintingFinished = false;
|
||||
uint public totalSupply = 0;
|
||||
|
||||
|
||||
modifier canMint() {
|
||||
if(mintingFinished) throw;
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, uint _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;
|
||||
}
|
||||
}
|
||||
25
contracts/token/PausableToken.sol
Normal file
25
contracts/token/PausableToken.sol
Normal file
@ -0,0 +1,25 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
import './StandardToken.sol';
|
||||
import '../lifecycle/Pausable.sol';
|
||||
|
||||
/**
|
||||
* Pausable token
|
||||
*
|
||||
* Simple ERC20 Token example, with pausable token creation
|
||||
* Issue:
|
||||
* https://github.com/OpenZeppelin/zeppelin-solidity/issues/194
|
||||
* Based on code by BCAPtoken:
|
||||
* https://github.com/BCAPtoken/BCAPToken/blob/5cb5e76338cc47343ba9268663a915337c8b268e/sol/BCAPToken.sol#L27
|
||||
**/
|
||||
|
||||
contract PausableToken is Pausable, StandardToken {
|
||||
|
||||
function transfer(address _to, uint _value) whenNotPaused {
|
||||
return super.transfer(_to, _value);
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint _value) whenNotPaused {
|
||||
return super.transferFrom(_from, _to, _value);
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,14 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
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 {
|
||||
|
||||
@ -17,7 +16,10 @@ contract SimpleToken is StandardToken {
|
||||
string public symbol = "SIM";
|
||||
uint public decimals = 18;
|
||||
uint public INITIAL_SUPPLY = 10000;
|
||||
|
||||
|
||||
/**
|
||||
* @dev Contructor that gives msg.sender all of existing tokens.
|
||||
*/
|
||||
function SimpleToken() {
|
||||
totalSupply = INITIAL_SUPPLY;
|
||||
balances[msg.sender] = INITIAL_SUPPLY;
|
||||
|
||||
@ -1,47 +1,63 @@
|
||||
pragma solidity ^0.4.4;
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import './BasicToken.sol';
|
||||
import './ERC20.sol';
|
||||
import '../SafeMath.sol';
|
||||
|
||||
|
||||
/**
|
||||
* 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 Implemantation of the basic standart 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 BasicToken, ERC20 {
|
||||
|
||||
mapping(address => uint) balances;
|
||||
mapping (address => mapping (address => uint)) allowed;
|
||||
|
||||
function transfer(address _to, uint _value) returns (bool success) {
|
||||
balances[msg.sender] = safeSub(balances[msg.sender], _value);
|
||||
balances[_to] = safeAdd(balances[_to], _value);
|
||||
Transfer(msg.sender, _to, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint _value) returns (bool success) {
|
||||
/**
|
||||
* @dev Transfer tokens from one address to another
|
||||
* @param _from address The address which you want to send tokens from
|
||||
* @param _to address The address which you want to transfer to
|
||||
* @param _value uint the amout of tokens to be transfered
|
||||
*/
|
||||
function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) {
|
||||
var _allowance = allowed[_from][msg.sender];
|
||||
|
||||
balances[_to] = safeAdd(balances[_to], _value);
|
||||
balances[_from] = safeSub(balances[_from], _value);
|
||||
allowed[_from][msg.sender] = safeSub(_allowance, _value);
|
||||
|
||||
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
|
||||
// if (_value > _allowance) throw;
|
||||
|
||||
balances[_to] = balances[_to].add(_value);
|
||||
balances[_from] = balances[_from].sub(_value);
|
||||
allowed[_from][msg.sender] = _allowance.sub(_value);
|
||||
Transfer(_from, _to, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function balanceOf(address _owner) constant returns (uint balance) {
|
||||
return balances[_owner];
|
||||
}
|
||||
/**
|
||||
* @dev Aprove the passed address to spend the specified amount of tokens on beahlf 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, uint _value) {
|
||||
|
||||
// To change the approve amount you first have to reduce the addresses`
|
||||
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
||||
// already 0 to mitigate the race condition described here:
|
||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;
|
||||
|
||||
function approve(address _spender, uint _value) returns (bool success) {
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to check the amount of tokens than an owner allowed to a spender.
|
||||
* @param _owner address The address which owns the funds.
|
||||
* @param _spender address The address which will spend the funds.
|
||||
* @return A uint specifing the amount of tokens still avaible for the spender.
|
||||
*/
|
||||
function allowance(address _owner, address _spender) constant returns (uint remaining) {
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
247
contracts/token/VestedToken.sol
Normal file
247
contracts/token/VestedToken.sol
Normal file
@ -0,0 +1,247 @@
|
||||
pragma solidity ^0.4.8;
|
||||
|
||||
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
|
||||
if (_cliff < _start || _vesting < _cliff) {
|
||||
throw;
|
||||
}
|
||||
|
||||
if (tokenGrantsCount(_to) > MAX_GRANTS_PER_ADDRESS) throw; // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting).
|
||||
|
||||
uint 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, uint _grantId) public {
|
||||
TokenGrant grant = grants[_holder][_grantId];
|
||||
|
||||
if (!grant.revokable) { // Check if grant was revokable
|
||||
throw;
|
||||
}
|
||||
|
||||
if (grant.granter != msg.sender) { // Only granter can revoke it
|
||||
throw;
|
||||
}
|
||||
|
||||
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 uint 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 balanceOf(holder); // 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 SafeMath.min256(vestedTransferable, super.transferableTokens(holder, time));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Check the amount of grants that an address has.
|
||||
* @param _holder The holder of the grants.
|
||||
* @return A uint representing the total amount of grants.
|
||||
*/
|
||||
function tokenGrantsCount(address _holder) constant returns (uint 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 uint 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, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) {
|
||||
TokenGrant grant = grants[_holder][_grantId];
|
||||
|
||||
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 uint 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 uint 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 uint 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 = SafeMath.max64(grants[holder][i].vesting, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = zeppelin-solidity
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
12
docs/source/basictoken.rst
Normal file
12
docs/source/basictoken.rst
Normal file
@ -0,0 +1,12 @@
|
||||
BasicToken
|
||||
=============================================
|
||||
|
||||
Simpler version of StandardToken, with no allowances
|
||||
|
||||
balanceOf(address _owner) constant returns (uint balance)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Returns the token balance of the passed address.
|
||||
|
||||
function transfer(address _to, uint _value) returns (bool success)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
60
docs/source/bounty.rst
Normal file
60
docs/source/bounty.rst
Normal file
@ -0,0 +1,60 @@
|
||||
Bounty
|
||||
=============================================
|
||||
|
||||
To create a bounty for your contract, inherit from the base `Bounty` contract and provide an implementation for ```deployContract()``` returning the new contract address.::
|
||||
|
||||
import {Bounty, Target} from "./zeppelin/Bounty.sol";
|
||||
import "./YourContract.sol";
|
||||
|
||||
contract YourBounty is Bounty {
|
||||
function deployContract() internal returns(address) {
|
||||
return new YourContract()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Next, implement invariant logic into your smart contract.
|
||||
Your main contract should inherit from the Target class and implement the checkInvariant method. This is a function that should check everything your contract assumes to be true all the time. If this function returns false, it means your contract was broken in some way and is in an inconsistent state. This is what security researchers will try to acomplish when trying to get the bounty.
|
||||
|
||||
At contracts/YourContract.sol::
|
||||
|
||||
|
||||
import {Bounty, Target} from "./zeppelin/Bounty.sol";
|
||||
contract YourContract is Target {
|
||||
function checkInvariant() returns(bool) {
|
||||
// Implement your logic to make sure that none of the invariants are broken.
|
||||
}
|
||||
}
|
||||
|
||||
Next, deploy your bounty contract along with your main contract to the network.
|
||||
|
||||
At ```migrations/2_deploy_contracts.js```::
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(YourContract);
|
||||
deployer.deploy(YourBounty);
|
||||
};
|
||||
|
||||
Next, add a reward to the bounty contract
|
||||
|
||||
After deploying the contract, send reward funds into the bounty contract.
|
||||
|
||||
From ```truffle console```::
|
||||
|
||||
bounty = YourBounty.deployed();
|
||||
address = 0xb9f68f96cde3b895cc9f6b14b856081b41cb96f1; // your account address
|
||||
reward = 5; // reward to pay to a researcher who breaks your contract
|
||||
|
||||
web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: bounty.address,
|
||||
value: web3.toWei(reward, "ether")
|
||||
})
|
||||
|
||||
If researchers break the contract, they can claim their reward.
|
||||
|
||||
For each researcher who wants to hack the contract and claims the reward, refer to our `Test <https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/Bounty.js/>`_ for the detail.
|
||||
|
||||
Finally, if you manage to protect your contract from security researchers, you can reclaim the bounty funds. To end the bounty, destroy the contract so that all the rewards go back to the owner.::
|
||||
|
||||
bounty.destroy();
|
||||
20
docs/source/claimable.rst
Normal file
20
docs/source/claimable.rst
Normal file
@ -0,0 +1,20 @@
|
||||
Claimable
|
||||
=============================================
|
||||
|
||||
Extension for the Ownable contract, where the ownership needs to be claimed
|
||||
|
||||
transfer(address newOwner) onlyOwner
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Sets the passed address as the pending owner.
|
||||
|
||||
modifier onlyPendingOwner
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Function only runs if called by pending owner.
|
||||
|
||||
claimOwnership( ) onlyPendingOwner
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Completes transfer of ownership by setting pending owner as the new owner.
|
||||
|
||||
160
docs/source/conf.py
Normal file
160
docs/source/conf.py
Normal file
@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# zeppelin-solidity documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Dec 13 11:35:05 2016.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'zeppelin-solidity'
|
||||
copyright = u'2016, Smart Contract Solutions, Inc'
|
||||
author = u'Smart Contract Solutions, Inc'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'1.0.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'1.0.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
import sphinx_rtd_theme
|
||||
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'zeppelin-soliditydoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'zeppelin-solidity.tex', u'zeppelin-solidity Documentation',
|
||||
u'Zeppelin', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'zeppelin-solidity', u'zeppelin-solidity Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'zeppelin-solidity', u'zeppelin-solidity Documentation',
|
||||
author, 'zeppelin-solidity', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
4
docs/source/contract-security-patterns.rst
Normal file
4
docs/source/contract-security-patterns.rst
Normal file
@ -0,0 +1,4 @@
|
||||
Common Contract Security Patterns
|
||||
=============================================
|
||||
|
||||
Zeppelin smart contracts are developed using industry standard contract security patterns and best practices. To learn more, please see `Onward with Ethereum Smart Contract Security <https://medium.com/zeppelin-blog/onward-with-ethereum-smart-contract-security-97a827e47702#.ybvzeyz0k/>`_.
|
||||
14
docs/source/crowdsaletoken.rst
Normal file
14
docs/source/crowdsaletoken.rst
Normal file
@ -0,0 +1,14 @@
|
||||
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.
|
||||
12
docs/source/developer-resources.rst
Normal file
12
docs/source/developer-resources.rst
Normal file
@ -0,0 +1,12 @@
|
||||
Developer Resources
|
||||
=============================================
|
||||
|
||||
Building a distributed application, protocol or organization with Zeppelin?
|
||||
|
||||
Ask for help and follow progress at: https://zeppelin-slackin.herokuapp.com/
|
||||
|
||||
Interested in contributing to Zeppelin?
|
||||
|
||||
* Framework proposal and roadmap: https://medium.com/zeppelin-blog/zeppelin-framework-proposal-and-development-roadmap-fdfa9a3a32ab#.iain47pak
|
||||
* Issue tracker: https://github.com/OpenZeppelin/zeppelin-solidity/issues
|
||||
* Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md
|
||||
36
docs/source/getting-started.rst
Normal file
36
docs/source/getting-started.rst
Normal file
@ -0,0 +1,36 @@
|
||||
Getting Started
|
||||
=============================================
|
||||
|
||||
Zeppelin integrates with `Truffle <https://github.com/ConsenSys/truffle/>`_, an Ethereum development environment. Please install Truffle and initialize your project with ``truffle init``::
|
||||
|
||||
npm install -g truffle
|
||||
mkdir myproject && cd myproject
|
||||
truffle init
|
||||
|
||||
To install the Zeppelin library, run::
|
||||
|
||||
npm i zeppelin-solidity
|
||||
|
||||
After that, you'll get all the library's contracts in the contracts/zeppelin folder. You can use the contracts in the library like so::
|
||||
|
||||
import "./zeppelin/Ownable.sol";
|
||||
|
||||
contract MyContract is Ownable {
|
||||
...
|
||||
}
|
||||
|
||||
.. epigraph::
|
||||
|
||||
NOTE: The current distribution channel is npm, which is not ideal. `We're looking into providing a better tool for code distribution <https://github.com/OpenZeppelin/zeppelin-solidity/issues/13/>`_ , and ideas are welcome.
|
||||
|
||||
Truffle Beta Support
|
||||
""""""""""""""""""""""""
|
||||
We also support Truffle Beta npm integration. If you're using Truffle Beta, the contracts in ``node_modules`` will be enough, so feel free to delete the copies at your ``contracts`` folder. If you're using Truffle Beta, you can use Zeppelin contracts like so::
|
||||
|
||||
import "zeppelin-solidity/contracts/Ownable.sol";
|
||||
|
||||
contract MyContract is Ownable {
|
||||
...
|
||||
}
|
||||
|
||||
For more info see the `Truffle Beta package management tutorial <http://truffleframework.com/tutorials/package-management/>`_.
|
||||
47
docs/source/index.rst
Normal file
47
docs/source/index.rst
Normal file
@ -0,0 +1,47 @@
|
||||
.. zeppelin-solidity documentation master file, created by
|
||||
sphinx-quickstart on Tue Dec 13 11:35:05 2016.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Zeppelin-Solidity
|
||||
=============================================
|
||||
|
||||
Zeppelin is a library for writing secure Smart Contracts on Ethereum.
|
||||
|
||||
With Zeppelin, you can build distributed applications, protocols and organizations:
|
||||
|
||||
* using :doc:`contract-security-patterns`
|
||||
* in the `Solidity language <https://solidity.readthedocs.io/en/develop/>`_.
|
||||
|
||||
The code is open-source, and `available on github <https://github.com/OpenZeppelin/zeppelin-solidity>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
getting-started
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Smart Contracts
|
||||
|
||||
ownable
|
||||
Pausable
|
||||
destructible
|
||||
claimable
|
||||
migrations
|
||||
safemath
|
||||
limitbalance
|
||||
pullpayment
|
||||
standardtoken
|
||||
basictoken
|
||||
crowdsaletoken
|
||||
bounty
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Developer Resources
|
||||
|
||||
contract-security-patterns
|
||||
developer-resources
|
||||
license
|
||||
16
docs/source/killable.rst
Normal file
16
docs/source/killable.rst
Normal file
@ -0,0 +1,16 @@
|
||||
Destructible
|
||||
=============================================
|
||||
|
||||
Base contract that can be destroyed by owner.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
destroy( ) onlyOwner
|
||||
"""""""""""""""""""
|
||||
|
||||
Destroys the contract and sends funds back to the owner.
|
||||
|
||||
destroyAndSend(address _recipient) onlyOwner
|
||||
"""""""""""""""""""
|
||||
|
||||
Destroys the contract and sends funds back to the _recepient.
|
||||
23
docs/source/license.rst
Normal file
23
docs/source/license.rst
Normal file
@ -0,0 +1,23 @@
|
||||
The MIT License (MIT)
|
||||
=============================================
|
||||
|
||||
Copyright (c) 2016 Smart Contract Solutions, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
12
docs/source/limitbalance.rst
Normal file
12
docs/source/limitbalance.rst
Normal file
@ -0,0 +1,12 @@
|
||||
LimitBalance
|
||||
=============================================
|
||||
|
||||
Base contract that provides mechanism for limiting the amount of funds a contract can hold.
|
||||
|
||||
LimitBalance(unit _limit)
|
||||
""""""""""""""""""""""""""""
|
||||
Constructor takes an unisgned integer and sets it as the limit of funds this contract can hold.
|
||||
|
||||
modifier limitedPayable()
|
||||
""""""""""""""""""""""""""""
|
||||
Throws an error if this contract's balance is already above the limit.
|
||||
16
docs/source/migrations.rst
Normal file
16
docs/source/migrations.rst
Normal file
@ -0,0 +1,16 @@
|
||||
Migrations
|
||||
=============================================
|
||||
|
||||
Base contract that allows for a new instance of itself to be created at a different address.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
upgrade(address new_address) onlyOwner
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Creates a new instance of the contract at the passed address.
|
||||
|
||||
setCompleted(uint completed) onlyOwner**
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Sets the last time that a migration was completed.
|
||||
16
docs/source/ownable.rst
Normal file
16
docs/source/ownable.rst
Normal file
@ -0,0 +1,16 @@
|
||||
Ownable
|
||||
=============================================
|
||||
|
||||
Base contract with an owner.
|
||||
|
||||
Ownable( )
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
Sets the address of the creator of the contract as the owner.
|
||||
|
||||
modifier onlyOwner( )
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
Prevents function from running if it is called by anyone other than the owner.
|
||||
|
||||
transfer(address newOwner) onlyOwner
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
Transfers ownership of the contract to the passed address.
|
||||
27
docs/source/pausable.rst
Normal file
27
docs/source/pausable.rst
Normal file
@ -0,0 +1,27 @@
|
||||
Pausable
|
||||
=============================================
|
||||
|
||||
Base contract that provides a pause mechanism.
|
||||
|
||||
Inherits from contract Ownable.
|
||||
|
||||
pause() onlyOwner whenNotPaused returns (bool)
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Triggers pause mechanism on the contract. After this function is called (by the owner of the contract), any function with modifier whenNotPaused will not run.
|
||||
|
||||
|
||||
modifier whenNotPaused()
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Prevents function from running if pause mechanism is activated.
|
||||
|
||||
modifier whenPaused()
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Only runs if pause mechanism is activated.
|
||||
|
||||
unpause() onlyOwner whenPaused returns (bool)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Deactivates the pause mechanism.
|
||||
12
docs/source/pullpayment.rst
Normal file
12
docs/source/pullpayment.rst
Normal file
@ -0,0 +1,12 @@
|
||||
PullPayment
|
||||
=============================================
|
||||
|
||||
Base contract supporting async send for pull payments. Inherit from this contract and use asyncSend instead of send.
|
||||
|
||||
asyncSend(address dest, uint amount) internal
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Adds sent amount to available balance that payee can pull from this contract, called by payer.
|
||||
|
||||
withdrawPayments( )
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Sends designated balance to payee calling the contract. Throws error if designated balance is 0, if contract does not hold enough funds ot pay the payee, or if the send transaction is not successful.
|
||||
24
docs/source/safemath.rst
Normal file
24
docs/source/safemath.rst
Normal file
@ -0,0 +1,24 @@
|
||||
SafeMath
|
||||
=============================================
|
||||
|
||||
Provides functions of mathematical operations with safety checks.
|
||||
|
||||
assert(bool assertion) internal
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Throws an error if the passed result is false. Used in this contract by checking mathematical expressions.
|
||||
|
||||
safeMul(uint a, uint b) internal returns (uint)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Multiplies two unisgned integers. Asserts that dividing the product by the non-zero multiplicand results in the multiplier.
|
||||
|
||||
safeSub(uint a, uint b) internal returns (uint)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Checks that b is not greater than a before subtracting.
|
||||
|
||||
safeAdd(uint a, uint b) internal returns (uint)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Checks that the result is greater than both a and b.
|
||||
26
docs/source/standardtoken.rst
Normal file
26
docs/source/standardtoken.rst
Normal file
@ -0,0 +1,26 @@
|
||||
StandardToken
|
||||
=============================================
|
||||
|
||||
Based on code by FirstBlood: `Link FirstBloodToken.sol <https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol/>`_
|
||||
|
||||
Inherits from contract SafeMath. Implementation of abstract contract ERC20 (see https://github.com/ethereum/EIPs/issues/20)
|
||||
|
||||
approve(address _spender, uint _value) returns (bool success)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Sets the amount of the sender's token balance that the passed address is approved to use.
|
||||
|
||||
allowance(address _owner, address _spender) constant returns (uint remaining)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Returns the approved amount of the owner's balance that the spender can use.
|
||||
|
||||
balanceOf(address _owner) constant returns (uint balance)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Returns the token balance of the passed address.
|
||||
|
||||
transferFrom(address _from, address _to, uint _value) returns (bool success)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Transfers tokens from an account that the sender is approved to transfer from. Amount must not be greater than the approved amount or the account's balance.
|
||||
|
||||
function transfer(address _to, uint _value) returns (bool success)
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
Transfers tokens from sender's account. Amount must not be greater than sender's balance.
|
||||
17
ethpm.json
Normal file
17
ethpm.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"package_name": "zeppelin",
|
||||
"version": "1.0.6",
|
||||
"description": "Secure Smart Contract library for Solidity",
|
||||
"authors": [
|
||||
"Manuel Araoz <manuelaraoz@gmail.com>"
|
||||
],
|
||||
"keywords": [
|
||||
"solidity",
|
||||
"ethereum",
|
||||
"smart",
|
||||
"contracts",
|
||||
"security",
|
||||
"zeppelin"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
var Migrations = artifacts.require("./Migrations.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(Migrations);
|
||||
};
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
//var Ownable = artifacts.require("ownership/Ownable.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(PullPaymentBid);
|
||||
deployer.deploy(BadArrayUse);
|
||||
deployer.deploy(ProofOfExistence);
|
||||
deployer.deploy(Ownable);
|
||||
deployer.deploy(Claimable);
|
||||
deployer.deploy(LimitBalance);
|
||||
if(deployer.network == 'test'){
|
||||
deployer.deploy(SecureTargetBounty);
|
||||
deployer.deploy(InsecureTargetBounty);
|
||||
};
|
||||
//deployer.deploy(Ownable);
|
||||
};
|
||||
|
||||
26
package.json
26
package.json
@ -1,15 +1,13 @@
|
||||
{
|
||||
"name": "zeppelin-solidity",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.6",
|
||||
"description": "Secure Smart Contract library for Solidity",
|
||||
"main": "truffle.js",
|
||||
"devDependencies": {
|
||||
"ethereumjs-testrpc": "^3.0.2",
|
||||
"truffle": "^2.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "truffle test",
|
||||
"install": "scripts/install.sh"
|
||||
"test": "scripts/test.sh",
|
||||
"console": "truffle console",
|
||||
"install": "scripts/install.sh",
|
||||
"coverage": "scripts/coverage.sh"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -28,5 +26,17 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/OpenZeppelin/zeppelin-solidity/issues"
|
||||
},
|
||||
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity"
|
||||
"homepage": "https://github.com/OpenZeppelin/zeppelin-solidity",
|
||||
"dependencies": {
|
||||
"truffle-hdwallet-provider": "0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-stage-2": "^6.18.0",
|
||||
"babel-preset-stage-3": "^6.17.0",
|
||||
"babel-register": "^6.23.0",
|
||||
"ethereumjs-testrpc": "^3.0.2",
|
||||
"solidity-coverage": "^0.1.0",
|
||||
"truffle": "https://github.com/ConsenSys/truffle.git#3.1.9"
|
||||
}
|
||||
}
|
||||
|
||||
3
scripts/coverage.sh
Executable file
3
scripts/coverage.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#! /bin/bash
|
||||
|
||||
SOLIDITY_COVERAGE=true ./node_modules/.bin/solidity-coverage
|
||||
13
scripts/test.sh
Executable file
13
scripts/test.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
|
||||
output=$(nc -z localhost 8545; echo $?)
|
||||
[ $output -eq "0" ] && trpc_running=true
|
||||
if [ ! $trpc_running ]; then
|
||||
echo "Starting our own testrpc node instance"
|
||||
testrpc > /dev/null &
|
||||
trpc_pid=$!
|
||||
fi
|
||||
./node_modules/truffle/cli.js test
|
||||
if [ ! $trpc_running ]; then
|
||||
kill -9 $trpc_pid
|
||||
fi
|
||||
@ -1,49 +1,35 @@
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");
|
||||
|
||||
contract('BasicToken', function(accounts) {
|
||||
|
||||
it("should return the correct totalSupply after construction", function(done) {
|
||||
return BasicTokenMock.new(accounts[0], 100)
|
||||
.then(function(token) {
|
||||
return token.totalSupply();
|
||||
})
|
||||
.then(function(totalSupply) {
|
||||
assert.equal(totalSupply, 100);
|
||||
})
|
||||
.then(done);
|
||||
it("should return the correct totalSupply after construction", async function() {
|
||||
let token = await BasicTokenMock.new(accounts[0], 100);
|
||||
let totalSupply = await token.totalSupply();
|
||||
|
||||
assert.equal(totalSupply, 100);
|
||||
})
|
||||
|
||||
it("should return correct balances after transfer", function(done) {
|
||||
var token;
|
||||
return BasicTokenMock.new(accounts[0], 100)
|
||||
.then(function(_token) {
|
||||
token = _token;
|
||||
return token.transfer(accounts[1], 100);
|
||||
})
|
||||
.then(function() {
|
||||
return token.balanceOf(accounts[0]);
|
||||
})
|
||||
.then(function(balance) {
|
||||
assert.equal(balance, 0);
|
||||
})
|
||||
.then(function() {
|
||||
return token.balanceOf(accounts[1]);
|
||||
})
|
||||
.then(function(balance) {
|
||||
assert.equal(balance, 100);
|
||||
})
|
||||
.then(done);
|
||||
it("should return correct balances after transfer", async function(){
|
||||
let token = await BasicTokenMock.new(accounts[0], 100);
|
||||
let transfer = await token.transfer(accounts[1], 100);
|
||||
|
||||
let firstAccountBalance = await token.balanceOf(accounts[0]);
|
||||
assert.equal(firstAccountBalance, 0);
|
||||
|
||||
let secondAccountBalance = await token.balanceOf(accounts[1]);
|
||||
assert.equal(secondAccountBalance, 100);
|
||||
});
|
||||
|
||||
it("should throw an error when trying to transfer more than balance", function(done) {
|
||||
var token;
|
||||
return BasicTokenMock.new(accounts[0], 100)
|
||||
.then(function(_token) {
|
||||
token = _token;
|
||||
return token.transfer(accounts[1], 101);
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error.message.search('invalid JUMP') == -1) throw error
|
||||
})
|
||||
.then(done);
|
||||
it("should throw an error when trying to transfer more than balance", async function() {
|
||||
let token = await BasicTokenMock.new(accounts[0], 100);
|
||||
try {
|
||||
let transfer = await token.transfer(accounts[1], 101);
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
218
test/Bounty.js
218
test/Bounty.js
@ -1,141 +1,115 @@
|
||||
var sendReward = function(sender, receiver, value){
|
||||
'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", function(done){
|
||||
var owner = accounts[0];
|
||||
var reward = web3.toWei(1, "ether");
|
||||
it('sets reward', async function() {
|
||||
let owner = accounts[0];
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
let bounty = await SecureTargetBounty.new();
|
||||
sendReward(owner, bounty.address, reward);
|
||||
|
||||
SecureTargetBounty.new().
|
||||
then(function(bounty){
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
|
||||
});
|
||||
|
||||
it('empties itself when destroyed', async function(){
|
||||
let owner = accounts[0];
|
||||
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.destroy();
|
||||
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
|
||||
});
|
||||
|
||||
describe('Against secure contract', function(){
|
||||
|
||||
it('cannot claim reward', async function(){
|
||||
let owner = accounts[0];
|
||||
let researcher = accounts[1];
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
let bounty = await SecureTargetBounty.new();
|
||||
let event = bounty.TargetCreated({});
|
||||
|
||||
let watcher = async function(err, result) {
|
||||
event.stopWatching();
|
||||
if (err) { throw err; }
|
||||
|
||||
var targetAddress = result.args.createdAddress;
|
||||
sendReward(owner, bounty.address, reward);
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
|
||||
}).
|
||||
then(done);
|
||||
})
|
||||
|
||||
it("empties itself when killed", function(done){
|
||||
var owner = accounts[0];
|
||||
var reward = web3.toWei(1, "ether");
|
||||
var bounty;
|
||||
assert.equal(reward,
|
||||
web3.eth.getBalance(bounty.address).toNumber());
|
||||
|
||||
SecureTargetBounty.new().
|
||||
then(function(_bounty){
|
||||
bounty = _bounty;
|
||||
try {
|
||||
await bounty.claim(targetAddress, {from:researcher});
|
||||
assert.isTrue(false); // should never reach here
|
||||
} catch(error) {
|
||||
let reClaimedBounty = await bounty.claimed.call();
|
||||
assert.isFalse(reClaimedBounty);
|
||||
|
||||
}
|
||||
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('claims reward', async function() {
|
||||
let owner = accounts[0];
|
||||
let researcher = accounts[1];
|
||||
let reward = web3.toWei(1, 'ether');
|
||||
let bounty = await InsecureTargetBounty.new();
|
||||
let event = bounty.TargetCreated({});
|
||||
|
||||
let watcher = async function(err, result) {
|
||||
event.stopWatching();
|
||||
if (err) { throw err; }
|
||||
let targetAddress = result.args.createdAddress;
|
||||
sendReward(owner, bounty.address, reward);
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
|
||||
return bounty.kill()
|
||||
}).
|
||||
then(function(){
|
||||
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber())
|
||||
}).
|
||||
then(done);
|
||||
})
|
||||
|
||||
describe("Against secure contract", function(){
|
||||
it("checkInvariant returns true", function(done){
|
||||
var bounty;
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber());
|
||||
|
||||
SecureTargetBounty.new().
|
||||
then(function(_bounty) {
|
||||
bounty = _bounty;
|
||||
return bounty.createTarget();
|
||||
}).
|
||||
then(function() {
|
||||
return bounty.checkInvariant.call()
|
||||
}).
|
||||
then(function(result) {
|
||||
assert.isTrue(result);
|
||||
}).
|
||||
then(done);
|
||||
})
|
||||
await bounty.claim(targetAddress, {from:researcher});
|
||||
let claim = await bounty.claimed.call();
|
||||
|
||||
it("cannot claim reward", function(done){
|
||||
var owner = accounts[0];
|
||||
var researcher = accounts[1];
|
||||
var reward = web3.toWei(1, "ether");
|
||||
assert.isTrue(claim);
|
||||
|
||||
SecureTargetBounty.new().
|
||||
then(function(bounty) {
|
||||
var event = bounty.TargetCreated({});
|
||||
event.watch(function(err, result) {
|
||||
event.stopWatching();
|
||||
if (err) { throw err }
|
||||
var targetAddress = result.args.createdAddress;
|
||||
sendReward(owner, bounty.address, reward);
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
|
||||
bounty.claim(targetAddress, {from:researcher}).
|
||||
then(function(){ throw("should not come here")}).
|
||||
catch(function() {
|
||||
return bounty.claimed.call();
|
||||
}).
|
||||
then(function(result) {
|
||||
assert.isFalse(result);
|
||||
bounty.withdrawPayments({from:researcher}).
|
||||
then(function(){ throw("should not come here")}).
|
||||
catch(function() {
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
bounty.createTarget({from:researcher});
|
||||
})
|
||||
})
|
||||
})
|
||||
await bounty.withdrawPayments({from:researcher});
|
||||
|
||||
describe("Against broken contract", function(){
|
||||
it("checkInvariant returns false", function(done){
|
||||
var bounty;
|
||||
|
||||
InsecureTargetBounty.new().
|
||||
then(function(_bounty) {
|
||||
bounty = _bounty;
|
||||
return bounty.createTarget();
|
||||
}).
|
||||
then(function() {
|
||||
return bounty.checkInvariant.call()
|
||||
}).
|
||||
then(function(result) {
|
||||
assert.isFalse(result);
|
||||
}).
|
||||
then(done);
|
||||
})
|
||||
|
||||
it("claims reward", function(done){
|
||||
var owner = accounts[0];
|
||||
var researcher = accounts[1];
|
||||
var reward = web3.toWei(1, "ether");
|
||||
|
||||
InsecureTargetBounty.new().
|
||||
then(function(bounty) {
|
||||
var event = bounty.TargetCreated({});
|
||||
event.watch(function(err, result) {
|
||||
event.stopWatching();
|
||||
if (err) { throw err }
|
||||
var targetAddress = result.args.createdAddress;
|
||||
sendReward(owner, bounty.address, reward);
|
||||
assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber())
|
||||
bounty.claim(targetAddress, {from:researcher}).
|
||||
then(function() {
|
||||
return bounty.claimed.call();
|
||||
}).
|
||||
then(function(result) {
|
||||
assert.isTrue(result);
|
||||
return bounty.withdrawPayments({from:researcher})
|
||||
}).
|
||||
then(function() {
|
||||
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber())
|
||||
}).then(done);
|
||||
})
|
||||
bounty.createTarget({from:researcher});
|
||||
})
|
||||
})
|
||||
})
|
||||
assert.equal(0, web3.eth.getBalance(bounty.address).toNumber());
|
||||
};
|
||||
bounty.createTarget({from:researcher});
|
||||
await awaitEvent(event, watcher);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,71 +1,60 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var Claimable = artifacts.require('../contracts/ownership/Claimable.sol');
|
||||
|
||||
contract('Claimable', function(accounts) {
|
||||
var claimable;
|
||||
let claimable;
|
||||
|
||||
beforeEach(function() {
|
||||
return Claimable.new().then(function(deployed) {
|
||||
claimable = deployed;
|
||||
});
|
||||
beforeEach(async function() {
|
||||
claimable = await Claimable.new();
|
||||
});
|
||||
|
||||
it("should have an owner", function(done) {
|
||||
return claimable.owner()
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner != 0);
|
||||
})
|
||||
.then(done)
|
||||
it('should have an owner', async function() {
|
||||
let owner = await claimable.owner();
|
||||
assert.isTrue(owner !== 0);
|
||||
});
|
||||
|
||||
it("changes pendingOwner after transfer", function(done) {
|
||||
var newOwner = accounts[1];
|
||||
return claimable.transfer(newOwner)
|
||||
.then(function() {
|
||||
return claimable.pendingOwner();
|
||||
})
|
||||
.then(function(pendingOwner) {
|
||||
assert.isTrue(pendingOwner === newOwner);
|
||||
})
|
||||
.then(done)
|
||||
it('changes pendingOwner after transfer', async function() {
|
||||
let newOwner = accounts[1];
|
||||
await claimable.transferOwnership(newOwner);
|
||||
let pendingOwner = await claimable.pendingOwner();
|
||||
|
||||
assert.isTrue(pendingOwner === newOwner);
|
||||
});
|
||||
|
||||
it("should prevent to claimOwnership from no pendingOwner", function(done) {
|
||||
return claimable.claimOwnership({from: accounts[2]})
|
||||
.then(function() {
|
||||
return claimable.owner();
|
||||
})
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner != accounts[2]);
|
||||
})
|
||||
.then(done)
|
||||
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" ,function(done) {
|
||||
return claimable.transfer(accounts[2], {from: accounts[2]})
|
||||
.then(function() {
|
||||
return claimable.pendingOwner();
|
||||
})
|
||||
.then(function(pendingOwner) {
|
||||
assert.isFalse(pendingOwner === accounts[2]);
|
||||
})
|
||||
.then(done)
|
||||
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 () {
|
||||
var newOwner;
|
||||
describe('after initiating a transfer', function () {
|
||||
let newOwner;
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(async function () {
|
||||
newOwner = accounts[1];
|
||||
return claimable.transfer(newOwner);
|
||||
await claimable.transferOwnership(newOwner);
|
||||
});
|
||||
|
||||
it("changes allow pending owner to claim ownership", function(done) {
|
||||
return claimable.claimOwnership({from: newOwner})
|
||||
.then(function() {
|
||||
return claimable.owner();
|
||||
})
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner === newOwner);
|
||||
})
|
||||
.then(done)
|
||||
it('changes allow pending owner to claim ownership', async function() {
|
||||
await claimable.claimOwnership({from: newOwner});
|
||||
let owner = await claimable.owner();
|
||||
|
||||
assert.isTrue(owner === newOwner);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
30
test/Contactable.js
Normal file
30
test/Contactable.js
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var Contactable = artifacts.require('../contracts/ownership/Contactable.sol');
|
||||
|
||||
contract('Contactable', function(accounts) {
|
||||
let contactable;
|
||||
|
||||
beforeEach(async function() {
|
||||
contactable = await Contactable.new();
|
||||
});
|
||||
|
||||
it('should have an empty contact info', async function() {
|
||||
let info = await contactable.contactInformation();
|
||||
assert.isTrue(info == "");
|
||||
});
|
||||
|
||||
describe('after setting the contact information', function () {
|
||||
let contactInfo = "contact information"
|
||||
|
||||
beforeEach(async function () {
|
||||
await contactable.setContactInformation(contactInfo);
|
||||
});
|
||||
|
||||
it('should return the setted contact information', async function() {
|
||||
let info = await contactable.contactInformation();
|
||||
assert.isTrue(info === contactInfo);
|
||||
});
|
||||
});
|
||||
});
|
||||
87
test/DayLimit.js
Normal file
87
test/DayLimit.js
Normal file
@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var DayLimitMock = artifacts.require('helpers/DayLimitMock.sol');
|
||||
|
||||
contract('DayLimit', function(accounts) {
|
||||
|
||||
it('should construct with the passed daily limit', async function() {
|
||||
let initLimit = 10;
|
||||
let dayLimit = await DayLimitMock.new(initLimit);
|
||||
let dailyLimit = await dayLimit.dailyLimit();
|
||||
assert.equal(initLimit, dailyLimit);
|
||||
});
|
||||
|
||||
it('should be able to spend if daily limit is not reached', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.attemptSpend(2);
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 10);
|
||||
});
|
||||
|
||||
it('should prevent spending if daily limit is reached', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow spending if daily limit is reached and then set higher', async function() {
|
||||
let limit = 10;
|
||||
let dayLimit = await DayLimitMock.new(limit);
|
||||
|
||||
await dayLimit.attemptSpend(8);
|
||||
let spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
try {
|
||||
await dayLimit.attemptSpend(3);
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 8);
|
||||
|
||||
await dayLimit.setDailyLimit(15);
|
||||
await dayLimit.attemptSpend(3);
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 11);
|
||||
});
|
||||
|
||||
it('should allow spending if daily limit is reached and then amount spent is reset', 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 dayLimit.resetSpentToday(15);
|
||||
await dayLimit.attemptSpend(3);
|
||||
spentToday = await dayLimit.spentToday();
|
||||
assert.equal(spentToday, 3);
|
||||
});
|
||||
|
||||
});
|
||||
68
test/DelayedClaimble.js
Normal file
68
test/DelayedClaimble.js
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
var DelayedClaimable = artifacts.require('../contracts/ownership/DelayedClaimable.sol');
|
||||
|
||||
contract('DelayedClaimable', function(accounts) {
|
||||
var delayedClaimable;
|
||||
|
||||
beforeEach(function() {
|
||||
return DelayedClaimable.new().then(function(deployed) {
|
||||
delayedClaimable = deployed;
|
||||
});
|
||||
});
|
||||
|
||||
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 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('changes pendingOwner after transfer fails', async function() {
|
||||
await delayedClaimable.transferOwnership(accounts[1]);
|
||||
await delayedClaimable.setLimits(100, 110);
|
||||
let end = await delayedClaimable.end();
|
||||
assert.equal(end, 110);
|
||||
let start = await delayedClaimable.start();
|
||||
assert.equal(start, 100);
|
||||
let pendingOwner = await delayedClaimable.pendingOwner();
|
||||
assert.equal(pendingOwner, accounts[1]);
|
||||
var err = null;
|
||||
try {
|
||||
await delayedClaimable.claimOwnership({from: accounts[1]});
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
assert.isFalse(err.message.search('invalid JUMP') === -1);
|
||||
let owner = await delayedClaimable.owner();
|
||||
assert.isTrue(owner !== accounts[1]);
|
||||
});
|
||||
|
||||
it('set end and start invalid values fail', async function() {
|
||||
await delayedClaimable.transferOwnership(accounts[1]);
|
||||
var err = null;
|
||||
try {
|
||||
await delayedClaimable.setLimits(1001, 1000);
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
assert.isFalse(err.message.search('invalid JUMP') === -1);
|
||||
});
|
||||
|
||||
});
|
||||
26
test/Destructible.js
Normal file
26
test/Destructible.js
Normal 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));
|
||||
});
|
||||
|
||||
});
|
||||
35
test/HasNoContracts.js
Normal file
35
test/HasNoContracts.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
import expectThrow from './helpers/expectThrow';
|
||||
import toPromise from './helpers/toPromise';
|
||||
const Ownable = artifacts.require('../contracts/ownership/Ownable.sol');
|
||||
const HasNoContracts = artifacts.require(
|
||||
'../contracts/ownership/HasNoContracts.sol',
|
||||
);
|
||||
|
||||
contract('HasNoContracts', function(accounts) {
|
||||
let hasNoContracts = null;
|
||||
let ownable = null;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create contract and token
|
||||
hasNoContracts = await HasNoContracts.new();
|
||||
ownable = await Ownable.new();
|
||||
|
||||
// Force ownership into contract
|
||||
await ownable.transferOwnership(hasNoContracts.address);
|
||||
const owner = await ownable.owner();
|
||||
assert.equal(owner, hasNoContracts.address);
|
||||
});
|
||||
|
||||
it('should allow owner to reclaim contracts', async function() {
|
||||
await hasNoContracts.reclaimContract(ownable.address);
|
||||
const owner = await ownable.owner();
|
||||
assert.equal(owner, accounts[0]);
|
||||
});
|
||||
|
||||
it('should allow only owner to reclaim contracts', async function() {
|
||||
await expectThrow(
|
||||
hasNoContracts.reclaimContract(ownable.address, {from: accounts[1]}),
|
||||
);
|
||||
});
|
||||
});
|
||||
63
test/HasNoEther.js
Normal file
63
test/HasNoEther.js
Normal file
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
import expectThrow from './helpers/expectThrow';
|
||||
import toPromise from './helpers/toPromise';
|
||||
const HasNoEther = artifacts.require('../contracts/lifecycle/HasNoEther.sol');
|
||||
const HasNoEtherTest = artifacts.require('../helpers/HasNoEtherTest.sol');
|
||||
const ForceEther = artifacts.require('../helpers/ForceEther.sol');
|
||||
|
||||
contract('HasNoEther', function(accounts) {
|
||||
const amount = web3.toWei('1', 'ether');
|
||||
|
||||
it('should be constructorable', async function() {
|
||||
let hasNoEther = await HasNoEtherTest.new();
|
||||
});
|
||||
|
||||
it('should not accept ether in constructor', async function() {
|
||||
await expectThrow(HasNoEtherTest.new({value: amount}));
|
||||
});
|
||||
|
||||
it('should not accept ether', async function() {
|
||||
let hasNoEther = await HasNoEtherTest.new();
|
||||
|
||||
await expectThrow(
|
||||
toPromise(web3.eth.sendTransaction)({
|
||||
from: accounts[1],
|
||||
to: hasNoEther.address,
|
||||
value: amount,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow owner to reclaim ether', async function() {
|
||||
// Create contract
|
||||
let hasNoEther = await HasNoEtherTest.new();
|
||||
const startBalance = await web3.eth.getBalance(hasNoEther.address);
|
||||
assert.equal(startBalance, 0);
|
||||
|
||||
// Force ether into it
|
||||
await ForceEther.new(hasNoEther.address, {value: amount});
|
||||
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
|
||||
assert.equal(forcedBalance, amount);
|
||||
|
||||
// Reclaim
|
||||
const ownerStartBalance = await web3.eth.getBalance(accounts[0]);
|
||||
await hasNoEther.reclaimEther();
|
||||
const ownerFinalBalance = await web3.eth.getBalance(accounts[0]);
|
||||
const finalBalance = await web3.eth.getBalance(hasNoEther.address);
|
||||
assert.equal(finalBalance, 0);
|
||||
assert.isAbove(ownerFinalBalance, ownerStartBalance);
|
||||
});
|
||||
|
||||
it('should allow only owner to reclaim ether', async function() {
|
||||
// Create contract
|
||||
let hasNoEther = await HasNoEtherTest.new({from: accounts[0]});
|
||||
|
||||
// Force ether into it
|
||||
await ForceEther.new(hasNoEther.address, {value: amount});
|
||||
const forcedBalance = await web3.eth.getBalance(hasNoEther.address);
|
||||
assert.equal(forcedBalance, amount);
|
||||
|
||||
// Reclaim
|
||||
await expectThrow(hasNoEther.reclaimEther({from: accounts[1]}));
|
||||
});
|
||||
});
|
||||
40
test/HasNoTokens.js
Normal file
40
test/HasNoTokens.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
import expectThrow from './helpers/expectThrow';
|
||||
import toPromise from './helpers/toPromise';
|
||||
const HasNoTokens = artifacts.require('../contracts/lifecycle/HasNoTokens.sol');
|
||||
const ERC23TokenMock = artifacts.require('./helpers/ERC23TokenMock.sol');
|
||||
|
||||
contract('HasNoTokens', function(accounts) {
|
||||
let hasNoTokens = null;
|
||||
let token = null;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create contract and token
|
||||
hasNoTokens = await HasNoTokens.new();
|
||||
token = await ERC23TokenMock.new(accounts[0], 100);
|
||||
|
||||
// Force token into contract
|
||||
await token.transfer(hasNoTokens.address, 10);
|
||||
const startBalance = await token.balanceOf(hasNoTokens.address);
|
||||
assert.equal(startBalance, 10);
|
||||
});
|
||||
|
||||
it('should not accept ERC23 tokens', async function() {
|
||||
await expectThrow(token.transferERC23(hasNoTokens.address, 10, ''));
|
||||
});
|
||||
|
||||
it('should allow owner to reclaim tokens', async function() {
|
||||
const ownerStartBalance = await token.balanceOf(accounts[0]);
|
||||
await hasNoTokens.reclaimToken(token.address);
|
||||
const ownerFinalBalance = await token.balanceOf(accounts[0]);
|
||||
const finalBalance = await token.balanceOf(hasNoTokens.address);
|
||||
assert.equal(finalBalance, 0);
|
||||
assert.equal(ownerFinalBalance - ownerStartBalance, 10);
|
||||
});
|
||||
|
||||
it('should allow only owner to reclaim tokens', async function() {
|
||||
await expectThrow(
|
||||
hasNoTokens.reclaimToken(token.address, {from: accounts[1]}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,69 +0,0 @@
|
||||
|
||||
contract('Killable', function(accounts) {
|
||||
//from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6
|
||||
web3.eth.getTransactionReceiptMined = function (txnHash, interval) {
|
||||
var transactionReceiptAsync;
|
||||
interval = interval ? interval : 500;
|
||||
transactionReceiptAsync = function(txnHash, resolve, reject) {
|
||||
try {
|
||||
var receipt = web3.eth.getTransactionReceipt(txnHash);
|
||||
if (receipt == null) {
|
||||
setTimeout(function () {
|
||||
transactionReceiptAsync(txnHash, resolve, reject);
|
||||
}, interval);
|
||||
} else {
|
||||
resolve(receipt);
|
||||
}
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
if (Array.isArray(txnHash)) {
|
||||
var promises = [];
|
||||
txnHash.forEach(function (oneTxHash) {
|
||||
promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval));
|
||||
});
|
||||
return Promise.all(promises);
|
||||
} else {
|
||||
return new Promise(function (resolve, reject) {
|
||||
transactionReceiptAsync(txnHash, resolve, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
it("should send balance to owner after death", function(done) {
|
||||
var initBalance, newBalance, owner, address, killable, kBalance;
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('50','ether')}, function(err, result) {
|
||||
if(err)
|
||||
console.log("ERROR:" + err);
|
||||
else {
|
||||
console.log(result);
|
||||
}
|
||||
})
|
||||
return Killable.new({from: accounts[0], value: web3.toWei('10','ether')})
|
||||
.then(function(_killable) {
|
||||
killable = _killable;
|
||||
return killable.owner();
|
||||
})
|
||||
.then(function(_owner) {
|
||||
owner = _owner;
|
||||
initBalance = web3.eth.getBalance(owner);
|
||||
kBalance = web3.eth.getBalance(killable.address);
|
||||
})
|
||||
.then(function() {
|
||||
return killable.kill({from: owner});
|
||||
})
|
||||
.then(function (txnHash) {
|
||||
return web3.eth.getTransactionReceiptMined(txnHash);
|
||||
})
|
||||
.then(function() {
|
||||
newBalance = web3.eth.getBalance(owner);
|
||||
})
|
||||
.then(function() {
|
||||
assert.isTrue(newBalance > initBalance);
|
||||
})
|
||||
.then(done);
|
||||
});
|
||||
|
||||
});
|
||||
@ -1,64 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
var LimitBalanceMock = artifacts.require('helpers/LimitBalanceMock.sol');
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
contract('LimitBalance', function(accounts) {
|
||||
var lb;
|
||||
let lb;
|
||||
|
||||
beforeEach(function() {
|
||||
return LimitBalanceMock.new().then(function(deployed) {
|
||||
lb = deployed;
|
||||
});
|
||||
beforeEach(async function() {
|
||||
lb = await LimitBalanceMock.new();
|
||||
});
|
||||
|
||||
var LIMIT = 1000;
|
||||
let LIMIT = 1000;
|
||||
|
||||
it("should expose limit", function(done) {
|
||||
return lb.limit()
|
||||
.then(function(limit) {
|
||||
assert.equal(limit, LIMIT);
|
||||
})
|
||||
.then(done)
|
||||
it('should expose limit', async function() {
|
||||
let limit = await lb.limit();
|
||||
assert.equal(limit, LIMIT);
|
||||
});
|
||||
|
||||
it("should allow sending below limit", function(done) {
|
||||
var amount = 1;
|
||||
return lb.limitedDeposit({value: amount})
|
||||
.then(function() {
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
})
|
||||
.then(done)
|
||||
it('should allow sending below limit', async function() {
|
||||
let amount = 1;
|
||||
await lb.limitedDeposit({value: amount});
|
||||
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
});
|
||||
|
||||
it("shouldnt allow sending above limit", function(done) {
|
||||
var amount = 1100;
|
||||
return lb.limitedDeposit({value: amount})
|
||||
.catch(function(error) {
|
||||
if (error.message.search('invalid JUMP') == -1) throw error
|
||||
})
|
||||
.then(done)
|
||||
it('shouldnt allow sending above limit', async function() {
|
||||
let amount = 1110;
|
||||
try {
|
||||
await lb.limitedDeposit({value: amount});
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it("should allow multiple sends below limit", function(done) {
|
||||
var amount = 500;
|
||||
return lb.limitedDeposit({value: amount})
|
||||
.then(function() {
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
return lb.limitedDeposit({value: amount})
|
||||
})
|
||||
.then(function() {
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount*2);
|
||||
})
|
||||
.then(done)
|
||||
it('should allow multiple sends below limit', async function() {
|
||||
let amount = 500;
|
||||
await lb.limitedDeposit({value: amount});
|
||||
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
|
||||
await lb.limitedDeposit({value: amount});
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount*2);
|
||||
});
|
||||
|
||||
it("shouldnt allow multiple sends above limit", function(done) {
|
||||
var amount = 500;
|
||||
return lb.limitedDeposit({value: amount})
|
||||
.then(function() {
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
return lb.limitedDeposit({value: amount+1})
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error.message.search('invalid JUMP') == -1) throw error;
|
||||
})
|
||||
.then(done)
|
||||
it('shouldnt allow multiple sends above limit', async function() {
|
||||
let amount = 500;
|
||||
await lb.limitedDeposit({value: amount});
|
||||
|
||||
assert.equal(web3.eth.getBalance(lb.address), amount);
|
||||
|
||||
try {
|
||||
await lb.limitedDeposit({value: amount+1});
|
||||
} catch(error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
35
test/MintableToken.js
Normal file
35
test/MintableToken.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
var MintableToken = artifacts.require('../contracts/Tokens/MintableToken.sol');
|
||||
|
||||
contract('Mintable', function(accounts) {
|
||||
let token;
|
||||
|
||||
beforeEach(async function() {
|
||||
token = await MintableToken.new();
|
||||
});
|
||||
|
||||
it('should start with a totalSupply of 0', async function() {
|
||||
let totalSupply = await token.totalSupply();
|
||||
|
||||
assert.equal(totalSupply, 0);
|
||||
});
|
||||
|
||||
it('should return mintingFinished false after construction', async function() {
|
||||
let mintingFinished = await token.mintingFinished();
|
||||
|
||||
assert.equal(mintingFinished, false);
|
||||
});
|
||||
|
||||
it('should mint a given amount of tokens to a given address', async function() {
|
||||
await token.mint(accounts[0], 100);
|
||||
|
||||
let balance0 = await token.balanceOf(accounts[0]);
|
||||
assert(balance0, 100);
|
||||
|
||||
let totalSupply = await token.totalSupply();
|
||||
assert(totalSupply, 100);
|
||||
})
|
||||
|
||||
});
|
||||
119
test/MultisigWallet.js
Normal file
119
test/MultisigWallet.js
Normal file
@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
var MultisigWalletMock = artifacts.require('./helpers/MultisigWalletMock.sol');
|
||||
require('./helpers/transactionMined.js');
|
||||
|
||||
contract('MultisigWallet', function(accounts) {
|
||||
let shouldntFail = function(err) {
|
||||
assert.isFalse(!!err);
|
||||
};
|
||||
it('should send balance to passed address upon death', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
//Get balances of owner and wallet after wallet creation.
|
||||
let ownerBalance = web3.eth.getBalance(accounts[0]);
|
||||
let walletBalance = web3.eth.getBalance(wallet.address);
|
||||
let hash = 1234;
|
||||
|
||||
//Call destroy function from two different owner accounts, satisfying owners required
|
||||
await wallet.destroy(accounts[0], {data: hash});
|
||||
let txnHash = await wallet.destroy(accounts[0], {from: accounts[1], data: hash});
|
||||
|
||||
//Get balances of owner and wallet after destroy function is complete, compare with previous values
|
||||
let newOwnerBalance = web3.eth.getBalance(accounts[0]);
|
||||
let newWalletBalance = web3.eth.getBalance(wallet.address);
|
||||
|
||||
assert.isTrue(newOwnerBalance > ownerBalance);
|
||||
assert.isTrue(newWalletBalance < walletBalance);
|
||||
});
|
||||
|
||||
it('should execute transaction if below daily limit', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
let accountBalance = web3.eth.getBalance(accounts[2]);
|
||||
let hash = 1234;
|
||||
|
||||
//Owner account0 commands wallet to send 9 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 9, hash);
|
||||
|
||||
//Balance of account2 should have increased
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.isTrue(newAccountBalance > accountBalance);
|
||||
});
|
||||
|
||||
it('should prevent execution of transaction if above daily limit', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
let accountBalance = web3.eth.getBalance(accounts[2]);
|
||||
let hash = 1234;
|
||||
|
||||
//Owner account0 commands wallet to send 9 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 9, hash);
|
||||
|
||||
//Balance of account2 should have increased
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.isTrue(newAccountBalance > accountBalance);
|
||||
|
||||
accountBalance = newAccountBalance;
|
||||
hash = 4567;
|
||||
|
||||
//Owner account0 commands wallet to send 2 more wei to account2, going over the daily limit of 10
|
||||
txnHash = await wallet.execute(accounts[2], 2, hash);
|
||||
|
||||
//Balance of account2 should not change
|
||||
newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.equal(newAccountBalance.toString(), accountBalance.toString());
|
||||
});
|
||||
|
||||
it('should execute transaction if above daily limit and enough owners approve', async function() {
|
||||
//Give account[0] 20 ether
|
||||
web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, shouldntFail);
|
||||
|
||||
let dailyLimit = 10;
|
||||
let ownersRequired = 2;
|
||||
|
||||
//Create MultisigWallet contract with 10 ether
|
||||
let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')});
|
||||
|
||||
let accountBalance = web3.eth.getBalance(accounts[2]);
|
||||
let hash = 1234;
|
||||
|
||||
//Owner account0 commands wallet to send 11 wei to account2
|
||||
let txnHash = await wallet.execute(accounts[2], 11, hash);
|
||||
|
||||
//Balance of account2 should not change
|
||||
let newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.equal(newAccountBalance.toString(), accountBalance.toString());
|
||||
|
||||
accountBalance = newAccountBalance;
|
||||
|
||||
//Owner account1 commands wallet to send 11 wei to account2
|
||||
txnHash = await wallet.execute(accounts[2], 2, hash);
|
||||
|
||||
//Balance of account2 should change
|
||||
newAccountBalance = web3.eth.getBalance(accounts[2]);
|
||||
assert.isTrue(newAccountBalance > accountBalance);
|
||||
});
|
||||
|
||||
});
|
||||
@ -1,58 +1,45 @@
|
||||
'use strict';
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
|
||||
var Ownable = artifacts.require('../contracts/ownership/Ownable.sol');
|
||||
|
||||
contract('Ownable', function(accounts) {
|
||||
var ownable;
|
||||
let ownable;
|
||||
|
||||
beforeEach(function() {
|
||||
return Ownable.new().then(function(deployed) {
|
||||
ownable = deployed;
|
||||
});
|
||||
beforeEach(async function() {
|
||||
ownable = await Ownable.new();
|
||||
});
|
||||
|
||||
it("should have an owner", function(done) {
|
||||
return ownable.owner()
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner != 0);
|
||||
})
|
||||
.then(done)
|
||||
it('should have an owner', async function() {
|
||||
let owner = await ownable.owner();
|
||||
assert.isTrue(owner !== 0);
|
||||
});
|
||||
|
||||
it("changes owner after transfer", function(done) {
|
||||
var other = accounts[1];
|
||||
return ownable.transfer(other)
|
||||
.then(function() {
|
||||
return ownable.owner();
|
||||
})
|
||||
.then(function(owner) {
|
||||
assert.isTrue(owner === other);
|
||||
})
|
||||
.then(done)
|
||||
it('changes owner after transfer', async function() {
|
||||
let other = accounts[1];
|
||||
await ownable.transferOwnership(other);
|
||||
let owner = await ownable.owner();
|
||||
|
||||
assert.isTrue(owner === other);
|
||||
});
|
||||
|
||||
it("should prevent non-owners from transfering" ,function(done) {
|
||||
var other = accounts[2];
|
||||
return ownable.transfer(other, {from: accounts[2]})
|
||||
.then(function() {
|
||||
return ownable.owner();
|
||||
})
|
||||
.then(function(owner) {
|
||||
assert.isFalse(owner === other);
|
||||
})
|
||||
.then(done)
|
||||
it('should prevent non-owners from transfering', async function() {
|
||||
const other = accounts[2];
|
||||
const owner = await ownable.owner.call();
|
||||
assert.isTrue(owner !== other);
|
||||
try {
|
||||
await ownable.transferOwnership(other, {from: other});
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
});
|
||||
|
||||
it("should guard ownership against stuck state" ,function(done) {
|
||||
var ownable = Ownable.deployed();
|
||||
it('should guard ownership against stuck state', async function() {
|
||||
let originalOwner = await ownable.owner();
|
||||
await ownable.transferOwnership(null, {from: originalOwner});
|
||||
let newOwner = await ownable.owner();
|
||||
|
||||
return ownable.owner()
|
||||
.then(function (originalOwner) {
|
||||
return ownable.transfer(null, {from: originalOwner})
|
||||
.then(function() {
|
||||
return ownable.owner();
|
||||
})
|
||||
.then(function(newOwner) {
|
||||
assert.equal(originalOwner, newOwner);
|
||||
})
|
||||
.then(done);
|
||||
});
|
||||
assert.equal(originalOwner, newOwner);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
65
test/Pausable.js
Normal file
65
test/Pausable.js
Normal file
@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
const PausableMock = artifacts.require('helpers/PausableMock.sol');
|
||||
|
||||
contract('Pausable', function(accounts) {
|
||||
|
||||
it('can perform normal process in non-pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
let count0 = await Pausable.count();
|
||||
assert.equal(count0, 0);
|
||||
|
||||
await Pausable.normalProcess();
|
||||
let count1 = await Pausable.count();
|
||||
assert.equal(count1, 1);
|
||||
});
|
||||
|
||||
it('can not perform normal process in pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
let count0 = await Pausable.count();
|
||||
assert.equal(count0, 0);
|
||||
|
||||
try {
|
||||
await Pausable.normalProcess();
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
let count1 = await Pausable.count();
|
||||
assert.equal(count1, 0);
|
||||
});
|
||||
|
||||
|
||||
it('can not take drastic measure in non-pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
try {
|
||||
await Pausable.drasticMeasure();
|
||||
} catch(error) {
|
||||
assertJump(error);
|
||||
}
|
||||
|
||||
const drasticMeasureTaken = await Pausable.drasticMeasureTaken();
|
||||
assert.isFalse(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
it('can take a drastic measure in a pause', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
await Pausable.drasticMeasure();
|
||||
let drasticMeasureTaken = await Pausable.drasticMeasureTaken();
|
||||
|
||||
assert.isTrue(drasticMeasureTaken);
|
||||
});
|
||||
|
||||
it('should resume allowing normal process after pause is over', async function() {
|
||||
let Pausable = await PausableMock.new();
|
||||
await Pausable.pause();
|
||||
await Pausable.unpause();
|
||||
await Pausable.normalProcess();
|
||||
let count0 = await Pausable.count();
|
||||
|
||||
assert.equal(count0, 1);
|
||||
});
|
||||
|
||||
});
|
||||
73
test/PausableToken.js
Normal file
73
test/PausableToken.js
Normal file
@ -0,0 +1,73 @@
|
||||
'user strict';
|
||||
|
||||
const assertJump = require('./helpers/assertJump');
|
||||
var PausableTokenMock = artifacts.require('./helpers/PausableTokenMock.sol');
|
||||
|
||||
contract('PausableToken', function(accounts) {
|
||||
let token;
|
||||
|
||||
beforeEach(async function() {
|
||||
token = await PausableTokenMock.new(accounts[0], 100);
|
||||
});
|
||||
|
||||
it('should return paused false after construction', async function() {
|
||||
let paused = await token.paused();
|
||||
|
||||
assert.equal(paused, false);
|
||||
});
|
||||
|
||||
it('should return paused true after pause', async function() {
|
||||
await token.pause();
|
||||
let paused = await token.paused();
|
||||
|
||||
assert.equal(paused, true);
|
||||
});
|
||||
|
||||
it('should return paused false after pause and unpause', async function() {
|
||||
await token.pause();
|
||||
await token.unpause();
|
||||
let paused = await token.paused();
|
||||
|
||||
assert.equal(paused, false);
|
||||
});
|
||||
|
||||
it('should be able to transfer if transfers are unpaused', async function() {
|
||||
await token.transfer(accounts[1], 100);
|
||||
let balance0 = await token.balanceOf(accounts[0]);
|
||||
assert.equal(balance0, 0);
|
||||
|
||||
let balance1 = await token.balanceOf(accounts[1]);
|
||||
assert.equal(balance1, 100);
|
||||
});
|
||||
|
||||
it('should be able to transfer after transfers are paused and unpaused', async function() {
|
||||
await token.pause();
|
||||
await token.unpause();
|
||||
await token.transfer(accounts[1], 100);
|
||||
let balance0 = await token.balanceOf(accounts[0]);
|
||||
assert.equal(balance0, 0);
|
||||
|
||||
let balance1 = await token.balanceOf(accounts[1]);
|
||||
assert.equal(balance1, 100);
|
||||
});
|
||||
|
||||
it('should throw an error trying to transfer while transactions are paused', async function() {
|
||||
await token.pause();
|
||||
try {
|
||||
await token.transfer(accounts[1], 100);
|
||||
} catch (error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
|
||||
it('should throw an error trying to transfer from another account while transactions are paused', async function() {
|
||||
await token.pause();
|
||||
try {
|
||||
await token.transferFrom(accounts[0], accounts[1], 100);
|
||||
} catch (error) {
|
||||
return assertJump(error);
|
||||
}
|
||||
assert.fail('should have thrown before');
|
||||
});
|
||||
})
|
||||
@ -1,102 +1,72 @@
|
||||
var PullPaymentMock = artifacts.require("./helpers/PullPaymentMock.sol");
|
||||
|
||||
contract('PullPayment', function(accounts) {
|
||||
|
||||
it("can't call asyncSend externally", function(done) {
|
||||
return PullPaymentMock.new()
|
||||
.then(function(ppc) {
|
||||
assert.isUndefined(ppc.asyncSend);
|
||||
})
|
||||
.then(done);
|
||||
it("can't call asyncSend externally", async function() {
|
||||
let ppc = await PullPaymentMock.new();
|
||||
assert.isUndefined(ppc.asyncSend);
|
||||
});
|
||||
|
||||
it("can record an async payment correctly", function(done) {
|
||||
var ppce;
|
||||
var AMOUNT = 100;
|
||||
return PullPaymentMock.new()
|
||||
.then(function(_ppce) {
|
||||
ppce = _ppce;
|
||||
ppce.callSend(accounts[0], AMOUNT)
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.payments(accounts[0]);
|
||||
})
|
||||
.then(function(paymentsToAccount0) {
|
||||
assert.equal(paymentsToAccount0, AMOUNT);
|
||||
})
|
||||
.then(done);
|
||||
it("can record an async payment correctly", async function() {
|
||||
let AMOUNT = 100;
|
||||
let ppce = await PullPaymentMock.new();
|
||||
let callSend = await ppce.callSend(accounts[0], AMOUNT);
|
||||
let paymentsToAccount0 = await ppce.payments(accounts[0]);
|
||||
let totalPayments = await ppce.totalPayments();
|
||||
|
||||
assert.equal(totalPayments, AMOUNT);
|
||||
assert.equal(paymentsToAccount0, AMOUNT);
|
||||
});
|
||||
|
||||
it("can add multiple balances on one account", function(done) {
|
||||
var ppce;
|
||||
return PullPaymentMock.new()
|
||||
.then(function(_ppce) {
|
||||
ppce = _ppce;
|
||||
return ppce.callSend(accounts[0], 200)
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.callSend(accounts[0], 300)
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.payments(accounts[0]);
|
||||
})
|
||||
.then(function(paymentsToAccount0) {
|
||||
assert.equal(paymentsToAccount0, 500);
|
||||
})
|
||||
.then(done);
|
||||
it("can add multiple balances on one account", async function() {
|
||||
let ppce = await PullPaymentMock.new();
|
||||
let call1 = await ppce.callSend(accounts[0], 200);
|
||||
let call2 = await ppce.callSend(accounts[0], 300);
|
||||
let paymentsToAccount0 = await ppce.payments(accounts[0]);
|
||||
let totalPayments = await ppce.totalPayments();
|
||||
|
||||
assert.equal(totalPayments, 500);
|
||||
assert.equal(paymentsToAccount0, 500);
|
||||
});
|
||||
|
||||
it("can add balances on multiple accounts", function(done) {
|
||||
var ppce;
|
||||
return PullPaymentMock.new()
|
||||
.then(function(_ppce) {
|
||||
ppce = _ppce;
|
||||
return ppce.callSend(accounts[0], 200)
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.callSend(accounts[1], 300)
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.payments(accounts[0]);
|
||||
})
|
||||
.then(function(paymentsToAccount0) {
|
||||
assert.equal(paymentsToAccount0, 200);
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.payments(accounts[1]);
|
||||
})
|
||||
.then(function(paymentsToAccount0) {
|
||||
assert.equal(paymentsToAccount0, 300);
|
||||
})
|
||||
.then(done);
|
||||
it("can add balances on multiple accounts", async function() {
|
||||
let ppce = await PullPaymentMock.new();
|
||||
let call1 = await ppce.callSend(accounts[0], 200);
|
||||
let call2 = await ppce.callSend(accounts[1], 300);
|
||||
|
||||
let paymentsToAccount0 = await ppce.payments(accounts[0]);
|
||||
assert.equal(paymentsToAccount0, 200);
|
||||
|
||||
let paymentsToAccount1 = await ppce.payments(accounts[1]);
|
||||
assert.equal(paymentsToAccount1, 300);
|
||||
|
||||
let totalPayments = await ppce.totalPayments();
|
||||
assert.equal(totalPayments, 500);
|
||||
});
|
||||
|
||||
it("can withdraw payment", function(done) {
|
||||
var ppce;
|
||||
var AMOUNT = 17*1e18;
|
||||
var payee = accounts[1];
|
||||
var initialBalance = web3.eth.getBalance(payee);
|
||||
return PullPaymentMock.new({value: AMOUNT})
|
||||
.then(function(_ppce) {
|
||||
ppce = _ppce;
|
||||
return ppce.callSend(payee, AMOUNT);
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.payments(payee);
|
||||
})
|
||||
.then(function(paymentsToAccount0) {
|
||||
assert.equal(paymentsToAccount0, AMOUNT);
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.withdrawPayments({from: payee});
|
||||
})
|
||||
.then(function() {
|
||||
return ppce.payments(payee);
|
||||
})
|
||||
.then(function(paymentsToAccount0) {
|
||||
assert.equal(paymentsToAccount0, 0);
|
||||
var balance = web3.eth.getBalance(payee);
|
||||
assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
|
||||
})
|
||||
.then(done);
|
||||
it("can withdraw payment", async function() {
|
||||
let AMOUNT = 17*1e18;
|
||||
let payee = accounts[1];
|
||||
let initialBalance = web3.eth.getBalance(payee);
|
||||
|
||||
let ppce = await PullPaymentMock.new({value: AMOUNT});
|
||||
let call1 = await ppce.callSend(payee, AMOUNT);
|
||||
|
||||
let payment1 = await ppce.payments(payee);
|
||||
assert.equal(payment1, AMOUNT);
|
||||
|
||||
let totalPayments = await ppce.totalPayments();
|
||||
assert.equal(totalPayments, AMOUNT);
|
||||
|
||||
let withdraw = await ppce.withdrawPayments({from: payee});
|
||||
let payment2 = await ppce.payments(payee);
|
||||
assert.equal(payment2, 0);
|
||||
|
||||
totalPayments = await ppce.totalPayments();
|
||||
assert.equal(totalPayments, 0);
|
||||
|
||||
let balance = web3.eth.getBalance(payee);
|
||||
assert(Math.abs(balance-initialBalance-AMOUNT) < 1e16);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
31
test/ReentrancyGuard.js
Normal file
31
test/ReentrancyGuard.js
Normal file
@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
import expectThrow from './helpers/expectThrow';
|
||||
const ReentrancyMock = artifacts.require('./helper/ReentrancyMock.sol');
|
||||
const ReentrancyAttack = artifacts.require('./helper/ReentrancyAttack.sol');
|
||||
|
||||
contract('ReentrancyGuard', function(accounts) {
|
||||
let reentrancyMock;
|
||||
|
||||
beforeEach(async function() {
|
||||
reentrancyMock = await ReentrancyMock.new();
|
||||
let initialCounter = await reentrancyMock.counter();
|
||||
assert.equal(initialCounter, 0);
|
||||
});
|
||||
|
||||
it('should not allow remote callback', async function() {
|
||||
let attacker = await ReentrancyAttack.new();
|
||||
await expectThrow(reentrancyMock.countAndCall(attacker.address));
|
||||
});
|
||||
|
||||
// The following are more side-effects that intended behaviour:
|
||||
// I put them here as documentation, and to monitor any changes
|
||||
// in the side-effects.
|
||||
|
||||
it('should not allow local recursion', async function() {
|
||||
await expectThrow(reentrancyMock.countLocalRecursive(10));
|
||||
});
|
||||
|
||||
it('should not allow indirect local recursion', async function() {
|
||||
await expectThrow(reentrancyMock.countThisRecursive(10));
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user