Merge pull request #1 from OpenZeppelin/master

Upgrade to latest OpenZeppelin
pull/31/head
Bill Gleim 8 years ago committed by GitHub
commit 4bab15a4e6
  1. 8
      CONTRIBUTING.md
  2. 24
      README.md
  3. 18
      contracts/Bounty.sol
  4. 22
      contracts/ERC20.sol
  5. 1
      contracts/Killable.sol
  6. 1
      contracts/LimitFunds.sol
  7. 3
      contracts/Migrations.sol
  8. 3
      contracts/Ownable.sol
  9. 22
      contracts/PullPayment.sol
  10. 1
      contracts/Rejector.sol
  11. 28
      contracts/SafeMath.sol
  12. 56
      contracts/StandardToken.sol
  13. 5
      contracts/Stoppable.sol
  14. 72
      contracts/Token.sol
  15. 5
      contracts/examples/BadArrayUse.sol
  16. 1
      contracts/examples/BadFailEarly.sol
  17. 1
      contracts/examples/BadPushPayments.sol
  18. 5
      contracts/examples/GoodArrayUse.sol
  19. 2
      contracts/examples/GoodFailEarly.sol
  20. 1
      contracts/examples/GoodPullPayments.sol
  21. 39
      contracts/examples/ProofOfExistence.sol
  22. 6
      contracts/examples/PullPaymentBid.sol
  23. 8
      contracts/examples/StoppableBid.sol
  24. 11
      contracts/test-helpers/PullPaymentMock.sol
  25. 34
      contracts/token/CrowdsaleToken.sol
  26. 22
      contracts/token/SimpleToken.sol
  27. 1
      migrations/2_deploy_contracts.js
  28. 4
      package.json
  29. 36
      test/Ownable.js
  30. 102
      test/PullPayment.js
  31. 25
      test/TestOwnable.sol

@ -63,7 +63,7 @@ Don't write long tests, write helper functions to make them be as short and conc
#### T2 - Tests Must not be Random
Inputs for tests should not be generated randomly. Also, the type and structure of outputs should be checked.
Inputs for tests should not be generated randomly. Accounts used to create test contracts are an exception, those can be random. Also, the type and structure of outputs should be checked.
### Documentation
@ -82,7 +82,7 @@ git checkout -b remove/some-file
We expect pull requests to be rebased to the master branch before merging:
```sh
git remote add zep git@github.com:OpenZeppelin/zep-solidity.git
git remote add zep git@github.com:OpenZeppelin/zeppelin-solidity.git
git pull --rebase zep master
```
@ -95,11 +95,11 @@ git push origin feature/some-new-stuff
git push origin fix/some-bug
```
Finally go to [github.com/OpenZeppelin/zep-solidity](https://github.com/OpenZeppelin/zep-solidity) in your web browser and issue a new pull request.
Finally go to [github.com/OpenZeppelin/zeppelin-solidity](https://github.com/OpenZeppelin/zeppelin-solidity) in your web browser and issue a new pull request.
Main contributors will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of Zeppelin.
If you have any questions feel free to post them to
[github.com/OpenZeppelin/zep-solidity/issues](https://github.com/OpenZeppelin/zep-solidity/issues).
[github.com/OpenZeppelin/zeppelin-solidity/issues](https://github.com/OpenZeppelin/zeppelin-solidity/issues).
Thanks for your time and code!

@ -29,7 +29,20 @@ contract MetaCoin is Rejector {
}
```
> 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/zep-solidity/issues/13), and ideas are welcome.
> 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/Rejector.sol";
contract MetaCoin is Rejector {
...
}
```
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.
@ -45,11 +58,14 @@ Building a distributed application, protocol or organization with Zeppelin?
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/zep-solidity/issues
- Contribution guidelines: https://github.com/OpenZeppelin/zep-solidity/blob/master/CONTRIBUTING.md
- Issue tracker: https://github.com/OpenZeppelin/zeppelin-solidity/issues
- Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md
## Projects using Zeppelin
- [Blockparty](https://github.com/makoto/blockparty)
## Contracts
TODO
## License
Code released under the [MIT License](https://github.com/OpenZeppelin/zep-solidity/blob/master/LICENSE).
Code released under the [MIT License](https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/LICENSE).

@ -1,13 +1,14 @@
import './PullPaymentCapable.sol';
import './Token.sol';
pragma solidity ^0.4.0;
import './PullPayment.sol';
import './token/SimpleToken.sol';
/*
* Bounty
* This bounty will pay out if you can cause a Token's balance
* This bounty will pay out if you can cause a SimpleToken's balance
* to be lower than its totalSupply, which would mean that it doesn't
* have sufficient ether for everyone to withdraw.
*/
contract Bounty is PullPaymentCapable {
contract Bounty is PullPayment {
bool public claimed;
mapping(address => address) public researchers;
@ -16,16 +17,17 @@ contract Bounty is PullPaymentCapable {
if (claimed) throw;
}
function createTarget() returns(Token) {
Token target = new Token(0);
function createTarget() returns(SimpleToken) {
SimpleToken target = new SimpleToken();
researchers[target] = msg.sender;
return target;
}
function claim(Token target) {
function claim(SimpleToken target) {
address researcher = researchers[target];
if (researcher == 0) throw;
// check Token contract invariants
// Check SimpleToken contract invariants
// Customize this to the specifics of your contract
if (target.totalSupply() == target.balance) {
throw;
}

@ -1,12 +1,16 @@
contract ERC20 {
function totalSupply() constant returns (uint);
function balanceOf(address who) constant returns (uint);
function allowance(address owner, address spender) constant returns (uint);
pragma solidity ^0.4.0;
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);
// see https://github.com/ethereum/EIPs/issues/20
contract ERC20 {
uint public totalSupply;
function balanceOf(address who) constant returns (uint);
function allowance(address owner, address spender) constant returns (uint);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function transfer(address to, uint value) returns (bool ok);
function transferFrom(address from, address to, uint value) returns (bool ok);
function approve(address spender, uint value) returns (bool ok);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}

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

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
contract LimitFunds {
uint LIMIT = 5000;

@ -1,9 +1,10 @@
pragma solidity ^0.4.0;
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _
if (msg.sender == owner) _;
}
function Migrations() {

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
/*
* Ownable
* Base contract with an owner
@ -11,7 +12,7 @@ contract Ownable {
modifier onlyOwner() {
if (msg.sender == owner)
_
_;
}
function transfer(address newOwner) onlyOwner {

@ -1,10 +1,11 @@
pragma solidity ^0.4.0;
/*
* PullPaymentCapable
* PullPayment
* Base contract supporting async send for pull payments.
* Inherit from this contract and use asyncSend instead of send.
*/
contract PullPaymentCapable {
mapping(address => uint) payments;
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 {
@ -12,11 +13,16 @@ contract PullPaymentCapable {
}
// withdraw accumulated balance, called by payee
function withdrawPayments() external {
uint payment = payments[msg.sender];
payments[msg.sender] = 0;
if (!msg.sender.send(payment)) {
payments[msg.sender] = payment;
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;
}
}
}

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
/*
* Rejector
* Base contract for rejecting direct deposits.

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

@ -0,0 +1,56 @@
pragma solidity ^0.4.0;
import './ERC20.sol';
import './SafeMath.sol';
/**
* 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
*/
contract StandardToken is ERC20, SafeMath {
mapping(address => uint) balances;
mapping (address => mapping (address => uint)) allowed;
function transfer(address _to, uint _value) returns (bool success) {
if (balances[msg.sender] < _value) {
throw;
}
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) {
var _allowance = allowed[_from][msg.sender];
if (balances[_from] < _value ||
_allowance < _value) {
throw;
}
balances[_to] = safeAdd(balances[_to], _value);
balances[_from] = safeSub(balances[_from], _value);
allowed[_from][msg.sender] = safeSub(_allowance, _value);
Transfer(_from, _to, _value);
return true;
}
function balanceOf(address _owner) constant returns (uint balance) {
return balances[_owner];
}
function approve(address _spender, uint _value) returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint remaining) {
return allowed[_owner][_spender];
}
}

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
/*
* Stoppable
* Abstract contract that allows children to implement an
@ -7,8 +8,8 @@ contract Stoppable {
address public curator;
bool public stopped;
modifier stopInEmergency { if (!stopped) _ }
modifier onlyInEmergency { if (stopped) _ }
modifier stopInEmergency { if (!stopped) _; }
modifier onlyInEmergency { if (stopped) _; }
function Stoppable(address _curator) {
if (_curator == 0) throw;

@ -1,72 +0,0 @@
// Source: https://github.com/nexusdev/erc20
// Flat file implementation of `dappsys/token/base.sol::DSTokenBase`
// Everything throws instead of returning false on failure.
import './ERC20.sol';
contract Token is ERC20 {
mapping( address => uint ) _balances;
mapping( address => mapping( address => uint ) ) _approvals;
uint _supply;
function Token( uint initial_balance ) {
_balances[msg.sender] = initial_balance;
_supply = initial_balance;
}
function totalSupply() constant returns (uint supply) {
return _supply;
}
function balanceOf( address who ) constant returns (uint value) {
return _balances[who];
}
function transfer( address to, uint value) returns (bool ok) {
if( _balances[msg.sender] < value ) {
throw;
}
if( !safeToAdd(_balances[to], value) ) {
throw;
}
_balances[msg.sender] -= value;
_balances[to] += value;
Transfer( msg.sender, to, value );
return true;
}
function transferFrom( address from, address to, uint value) returns (bool ok) {
// if you don't have enough balance, throw
if( _balances[from] < value ) {
throw;
}
// if you don't have approval, throw
if( _approvals[from][msg.sender] < value ) {
throw;
}
if( !safeToAdd(_balances[to], value) ) {
throw;
}
// transfer and return true
_approvals[from][msg.sender] -= value;
_balances[from] -= value;
_balances[to] += value;
Transfer( from, to, value );
return true;
}
function approve(address spender, uint value) returns (bool ok) {
_approvals[msg.sender][spender] = value;
Approval( msg.sender, spender, value );
return true;
}
function allowance(address owner, address spender) constant returns (uint _allowance) {
return _approvals[owner][spender];
}
function safeToAdd(uint a, uint b) internal returns (bool) {
return (a + b >= a);
}
}

@ -1,8 +1,9 @@
import '../PullPaymentCapable.sol';
pragma solidity ^0.4.0;
import '../PullPayment.sol';
// UNSAFE CODE, DO NOT USE!
contract BadArrayUse is PullPaymentCapable {
contract BadArrayUse is PullPayment {
address[] employees;
function payBonus() {

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
// UNSAFE CODE, DO NOT USE!
contract BadFailEarly {

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
// UNSAFE CODE, DO NOT USE!
contract BadPushPayments {

@ -1,6 +1,7 @@
import '../PullPaymentCapable.sol';
pragma solidity ^0.4.0;
import '../PullPayment.sol';
contract GoodArrayUse is PullPaymentCapable {
contract GoodArrayUse is PullPayment {
address[] employees;
mapping(address => uint) bonuses;

@ -1,3 +1,5 @@
pragma solidity ^0.4.0;
contract GoodFailEarly {
uint constant DEFAULT_SALARY = 50000;

@ -1,3 +1,4 @@
pragma solidity ^0.4.0;
contract GoodPullPayments {
address highestBidder;
uint highestBid;

@ -0,0 +1,39 @@
pragma solidity ^0.4.0;
import "../Rejector.sol";
/*
* Proof of Existence example contract
* see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05
*/
contract ProofOfExistence is Rejector {
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,6 +1,8 @@
import '../PullPaymentCapable.sol';
pragma solidity ^0.4.0;
contract PullPaymentBid is PullPaymentCapable {
import '../PullPayment.sol';
contract PullPaymentBid is PullPayment {
address public highestBidder;
uint public highestBid;

@ -1,13 +1,15 @@
import '../PullPaymentCapable.sol';
pragma solidity ^0.4.0;
import '../PullPayment.sol';
import '../Stoppable.sol';
contract StoppableBid is Stoppable, PullPaymentCapable {
contract StoppableBid is Stoppable, PullPayment {
address public highestBidder;
uint public highestBid;
function StoppableBid(address _curator)
Stoppable(_curator)
PullPaymentCapable() {}
PullPayment() {}
function bid() external stopInEmergency {
if (msg.value <= highestBid) throw;

@ -0,0 +1,11 @@
pragma solidity ^0.4.0;
import '../PullPayment.sol';
// mock class using PullPayment
contract PullPaymentMock is PullPayment {
// test helper function to call asyncSend
function callSend(address dest, uint amount) {
asyncSend(dest, amount);
}
}

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

@ -0,0 +1,22 @@
pragma solidity ^0.4.0;
import "../StandardToken.sol";
/*
* Very simple ERC20 Token example, where all tokens are pre-assigned
* to the creator. Note they can later distribute these tokens
* as they wish using `transfer` and other `StandardToken` functions.
*/
contract SimpleToken is StandardToken {
string public name = "SimpleToken";
string public symbol = "SIM";
uint public decimals = 18;
uint public INITIAL_SUPPLY = 10000;
function SimpleToken() {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
}

@ -1,6 +1,7 @@
module.exports = function(deployer) {
deployer.deploy(PullPaymentBid);
deployer.deploy(BadArrayUse);
deployer.deploy(ProofOfExistence);
deployer.deploy(Bounty);
deployer.deploy(Ownable);
deployer.deploy(LimitFunds);

@ -1,11 +1,11 @@
{
"name": "zeppelin-solidity",
"version": "0.0.6",
"version": "0.0.10",
"description": "Secure Smart Contract library for Solidity",
"main": "truffle.js",
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "truffle test",
"install": "scripts/install.sh"
},
"repository": {

@ -2,36 +2,36 @@ contract('Ownable', function(accounts) {
it("should have an owner", function(done) {
var ownable = Ownable.deployed();
return ownable.owner()
.then(function(owner) {
assert.isTrue(owner != 0);
})
.then(done)
.then(function(owner) {
assert.isTrue(owner != 0);
})
.then(done)
});
it("changes owner after transfer", function(done) {
var ownable = Ownable.deployed();
var other = accounts[1];
return ownable.transfer(other)
.then(function() {
return ownable.owner();
})
.then(function(owner) {
assert.isTrue(owner === other);
})
.then(done)
.then(function() {
return ownable.owner();
})
.then(function(owner) {
assert.isTrue(owner === other);
})
.then(done)
});
it("should prevent non-owners from transfering" ,function(done) {
var ownable = Ownable.deployed();
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)
.then(function() {
return ownable.owner();
})
.then(function(owner) {
assert.isFalse(owner === other);
})
.then(done)
});
});

@ -0,0 +1,102 @@
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 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 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 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 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);
});
});

@ -0,0 +1,25 @@
pragma solidity ^0.4.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Ownable.sol";
contract TestOwnable {
Ownable ownable = new Ownable();
function testHasOwner() {
Assert.isNotZero(ownable.owner(), "Ownable should have an owner upon creation.");
}
function testChangesOwner() {
address originalOwner = ownable.owner();
ownable.transfer(0x0);
Assert.notEqual(originalOwner, ownable.owner(), "Ownable should change owners after transfer.");
}
function testOnlyOwnerCanChangeOwner() {
Ownable deployedOwnable = Ownable(DeployedAddresses.Ownable());
address originalOwner = deployedOwnable.owner();
deployedOwnable.transfer(0x0);
Assert.equal(originalOwner, deployedOwnable.owner(), "Ownable should prevent non-owners from transfering");
}
}
Loading…
Cancel
Save