From 6750c665e830508cd35280c7348b2a549a69f4b6 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Mon, 23 Apr 2018 01:58:58 -0300 Subject: [PATCH] cleanup bootstrap --- .gitattributes | 1 + .gitignore | 40 +++++++++++++ config/blockchain.json | 56 ++++++++++++++++++ config/communication.json | 12 ++++ config/contracts.json | 69 ++++++++++++++++++++++ config/development/genesis.json | 16 +++++ config/development/password | 1 + config/storage.json | 20 +++++++ config/testnet/password | 1 + config/webserver.json | 5 ++ contracts/common/Controlled.sol | 22 +++++++ contracts/common/Owned.sol | 36 ++++++++++++ contracts/common/SafeMath.sol | 46 +++++++++++++++ contracts/token/ERC20Token.sol | 49 ++++++++++++++++ contracts/token/StandardToken.sol | 56 ++++++++++++++++++ embark.json | 10 ++++ package.json | 28 +++++++++ utils/testUtils.js | 97 +++++++++++++++++++++++++++++++ 18 files changed, 565 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 config/blockchain.json create mode 100644 config/communication.json create mode 100644 config/contracts.json create mode 100644 config/development/genesis.json create mode 100644 config/development/password create mode 100644 config/storage.json create mode 100644 config/testnet/password create mode 100644 config/webserver.json create mode 100644 contracts/common/Controlled.sol create mode 100644 contracts/common/Owned.sol create mode 100644 contracts/common/SafeMath.sol create mode 100644 contracts/token/ERC20Token.sol create mode 100644 contracts/token/StandardToken.sol create mode 100644 embark.json create mode 100644 package.json create mode 100644 utils/testUtils.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7cc88f0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d4826d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# embark +.embark/ +chains.json + +# egg-related +viper.egg-info/ +build/ +dist/ +.eggs/ + +# pyenv +.python-version + +# dotenv +.env + +# virtualenv +.venv/ +venv/ +ENV/ + +# Coverage tests +.coverage +.cache/ +coverage/ +coverageEnv/ +coverage.json + +# node +node_modules/ +npm-debug.log + +# other +.vs/ +bin/ diff --git a/config/blockchain.json b/config/blockchain.json new file mode 100644 index 0000000..9dccb92 --- /dev/null +++ b/config/blockchain.json @@ -0,0 +1,56 @@ +{ + "development": { + "enabled": true, + "networkType": "custom", + "genesisBlock": "config/development/genesis.json", + "datadir": ".embark/development/datadir", + "mineWhenNeeded": true, + "nodiscover": true, + "maxpeers": 0, + "rpcHost": "localhost", + "rpcPort": 8545, + "rpcCorsDomain": "http://localhost:8000", + "account": { + "password": "config/development/password" + }, + "targetGasLimit": 8000000, + "wsOrigins": "http://localhost:8000", + "wsRPC": true, + "wsHost": "localhost", + "wsPort": 8546, + "simulatorMnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm", + "simulatorBlocktime": 0 + }, + "testnet": { + "enabled": true, + "networkType": "testnet", + "light": true, + "rpcHost": "localhost", + "rpcPort": 8545, + "rpcCorsDomain": "http://localhost:8000", + "account": { + "password": "config/testnet/password" + } + }, + "livenet": { + "enabled": true, + "networkType": "livenet", + "light": true, + "rpcHost": "localhost", + "rpcPort": 8545, + "rpcCorsDomain": "http://localhost:8000", + "account": { + "password": "config/livenet/password" + } + }, + "privatenet": { + "enabled": true, + "networkType": "custom", + "rpcHost": "localhost", + "rpcPort": 8545, + "rpcCorsDomain": "http://localhost:8000", + "datadir": "yourdatadir", + "networkId": "123", + "bootnodes": "" + } +} diff --git a/config/communication.json b/config/communication.json new file mode 100644 index 0000000..80ec2f8 --- /dev/null +++ b/config/communication.json @@ -0,0 +1,12 @@ +{ + "default": { + "enabled": true, + "provider": "whisper", + "available_providers": ["whisper", "orbit"], + "connection": { + "host": "localhost", + "port": 8546, + "type": "ws" + } + } +} diff --git a/config/contracts.json b/config/contracts.json new file mode 100644 index 0000000..1bf2556 --- /dev/null +++ b/config/contracts.json @@ -0,0 +1,69 @@ +{ + "default": { + "versions": { + "web3.js": "1.0.0-beta", + "solc": "0.4.21" + }, + "deployment": { + "host": "localhost", + "port": 8545, + "type": "rpc" + }, + "dappConnection": [ + "$WEB3", + "http://localhost:8545" + ], + "gas": "auto", + "contracts": { + "Bounty": {"deploy": false}, + "BountyFactory": {"deploy": false}, + "BountyKernel": {"deploy": false}, + "BountyManager": {"deploy": false}, + "BountyManagerFactory": {"deploy": false}, + "BountyManagerKernel": {"deploy": false}, + "BountyManagerPreSigned": {"deploy": false}, + "Controlled": { "deploy": false }, + "ContributionWallet": {"deploy": false}, + "DelayedUpdatableInstance": { "deploy": false }, + "DelayedUpdatableInstanceStorage": { "deploy": false }, + "DelegatedCall": { "deploy": false }, + "DelegationProxy": {"deploy": false}, + "DelegationProxyInterface": {"deploy": false}, + "DelegationProxyKernel": {"deploy": false}, + "DelegationProxyFactory": {"deploy": false}, + "Democracy": {"deploy": false}, + "DemocracyStorage": {"deploy": false}, + "DevTokensHolder": {"deploy": false}, + "DynamicCeiling": {"deploy": false}, + "EmojiBoard": {"deploy": false}, + "EmojiTable": {"deploy": false}, + "ERC725": { "deploy": false }, + "ERC735": { "deploy": false }, + "Factory": { "deploy": false }, + "FeeRecycler": {"deploy": false}, + "FriendsRecovery": { "deploy": false }, + "Identity": { "gas": 5000000 }, + "IdentityFactory": { "deploy": false }, + "IdentityKernel": { "gas": 5000000 }, + "Instance": { "deploy": false }, + "InstanceStorage": { "deploy": false }, + "MessageDeliveryPayout": {"deploy": false}, + "MiniMeToken": {"deploy": false}, + "MiniMeTokenFactory": {"deploy": false}, + "MultiSig": {"deploy": false}, + "Ownable": {"deploy": false}, + "Owned": {"deploy": false}, + "ProposalManager": {"deploy": false}, + "SGTExchanger": {"deploy": false}, + "SNT": {"deploy": false}, + "SNTPlaceHolder": {"deploy": false}, + "StandardToken": {"deploy": false}, + "StatusContribution": {"deploy": false}, + "TestContract": { "deploy": false }, + "TokenRegistry": {"deploy": false}, + "TrustNetwork": {"deploy": false}, + "UpdatableInstance": { "deploy": false }, + "UpdatedIdentityKernel": { "deploy": false } + } + } +} diff --git a/config/development/genesis.json b/config/development/genesis.json new file mode 100644 index 0000000..4b6ce0d --- /dev/null +++ b/config/development/genesis.json @@ -0,0 +1,16 @@ +{ + "config": { + "homesteadBlock": 1 + }, + "nonce": "0x0000000000000042", + "difficulty": "0x0", + "alloc": { + "0x3333333333333333333333333333333333333333": {"balance": "15000000000000000000"} + }, + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x3333333333333333333333333333333333333333", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x7a1200" +} diff --git a/config/development/password b/config/development/password new file mode 100644 index 0000000..c747d67 --- /dev/null +++ b/config/development/password @@ -0,0 +1 @@ +dev_password diff --git a/config/storage.json b/config/storage.json new file mode 100644 index 0000000..b286558 --- /dev/null +++ b/config/storage.json @@ -0,0 +1,20 @@ +{ + "default": { + "versions": { + "ipfs-api": "17.2.4" + }, + "enabled": true, + "ipfs_bin": "ipfs", + "provider": "ipfs", + "available_providers": ["ipfs"], + "host": "localhost", + "port": 5001 + }, + "development": { + "enabled": true, + "provider": "ipfs", + "host": "localhost", + "port": 5001, + "getUrl": "http://localhost:8080/ipfs/" + } +} diff --git a/config/testnet/password b/config/testnet/password new file mode 100644 index 0000000..414f849 --- /dev/null +++ b/config/testnet/password @@ -0,0 +1 @@ +test_password diff --git a/config/webserver.json b/config/webserver.json new file mode 100644 index 0000000..c28a311 --- /dev/null +++ b/config/webserver.json @@ -0,0 +1,5 @@ +{ + "enabled": true, + "host": "localhost", + "port": 8000 +} diff --git a/contracts/common/Controlled.sol b/contracts/common/Controlled.sol new file mode 100644 index 0000000..334b6c5 --- /dev/null +++ b/contracts/common/Controlled.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.4.8; + +contract Controlled { + /// @notice The address of the controller is the only address that can call + /// a function with this modifier + modifier onlyController { + require(msg.sender == controller); + _; + } + + address public controller; + + function Controlled() public { + controller = msg.sender; + } + + /// @notice Changes the controller of the contract + /// @param _newController The new controller of the contract + function changeController(address _newController) public onlyController { + controller = _newController; + } +} diff --git a/contracts/common/Owned.sol b/contracts/common/Owned.sol new file mode 100644 index 0000000..193a160 --- /dev/null +++ b/contracts/common/Owned.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.4.21; + +/// @dev `Owned` is a base level contract that assigns an `owner` that can be +/// later changed +contract Owned { + + /// @dev `owner` is the only address that can call a function with this + /// modifier + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + address public owner; + + /// @notice The Constructor assigns the message sender to be `owner` + function Owned() public { + owner = msg.sender; + } + + address public newOwner; + + /// @notice `owner` can step down and assign some other address to this role + /// @param _newOwner The address of the new owner. 0x0 can be used to create + /// an unowned neutral vault, however that cannot be undone + function changeOwner(address _newOwner) public onlyOwner { + newOwner = _newOwner; + } + + + function acceptOwnership() public { + if (msg.sender == newOwner) { + owner = newOwner; + } + } +} \ No newline at end of file diff --git a/contracts/common/SafeMath.sol b/contracts/common/SafeMath.sol new file mode 100644 index 0000000..ac387c9 --- /dev/null +++ b/contracts/common/SafeMath.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.4.21; + +/** + * Math operations with safety checks + */ +library SafeMath { + function mul(uint a, uint b) internal pure returns (uint) { + uint c = a * b; + assert(a == 0 || c / a == b); + return c; + } + + function div(uint a, uint b) internal pure 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 pure returns (uint) { + assert(b <= a); + return a - b; + } + + function add(uint a, uint b) internal pure returns (uint) { + uint c = a + b; + assert(c >= a); + return c; + } + + function max64(uint64 a, uint64 b) internal pure returns (uint64) { + return a >= b ? a : b; + } + + function min64(uint64 a, uint64 b) internal pure returns (uint64) { + return a < b ? a : b; + } + + function max256(uint256 a, uint256 b) internal pure returns (uint256) { + return a >= b ? a : b; + } + + function min256(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } +} diff --git a/contracts/token/ERC20Token.sol b/contracts/token/ERC20Token.sol new file mode 100644 index 0000000..67cd1f1 --- /dev/null +++ b/contracts/token/ERC20Token.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.4.11; + +// Abstract contract for the full ERC 20 Token standard +// https://github.com/ethereum/EIPs/issues/20 + +contract ERC20Token { + /* This is a slight change to the ERC20 base standard. + function totalSupply() constant returns (uint256 supply); + is replaced with: + uint256 public totalSupply; + This automatically creates a getter function for the totalSupply. + This is moved to the base contract since public getter functions are not + currently recognised as an implementation of the matching abstract + function by the compiler. + */ + /// total amount of tokens + uint256 public totalSupply; + + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) public constant returns (uint256 balance); + + /// @notice send `_value` token to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _value) public returns (bool success); + + /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + + /// @notice `msg.sender` approves `_spender` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of tokens to be approved for transfer + /// @return Whether the approval was successful or not + function approve(address _spender, uint256 _value) public returns (bool success); + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) public constant returns (uint256 remaining); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +} \ No newline at end of file diff --git a/contracts/token/StandardToken.sol b/contracts/token/StandardToken.sol new file mode 100644 index 0000000..d54944c --- /dev/null +++ b/contracts/token/StandardToken.sol @@ -0,0 +1,56 @@ +/* +You should inherit from StandardToken or, for a token like you would want to +deploy in something like Mist, see HumanStandardToken.sol. +(This implements ONLY the standard functions and NOTHING else. +If you deploy this, you won't have anything useful.) + +Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20 +.*/ +pragma solidity ^0.4.8; + +import "./ERC20Token.sol"; + +contract StandardToken is ERC20Token { + + function transfer(address _to, uint256 _value) returns (bool success) { + //Default assumes totalSupply can't be over max (2^256 - 1). + //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. + //Replace the if with this one instead. + //if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { + if (balances[msg.sender] >= _value && _value > 0) { + balances[msg.sender] -= _value; + balances[_to] += _value; + Transfer(msg.sender, _to, _value); + return true; + } else { return false; } + } + + function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { + //same as above. Replace this line with the following if you want to protect against wrapping uints. + //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { + if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { + balances[_to] += _value; + balances[_from] -= _value; + allowed[_from][msg.sender] -= _value; + Transfer(_from, _to, _value); + return true; + } else { return false; } + } + + function balanceOf(address _owner) constant returns (uint256 balance) { + return balances[_owner]; + } + + function approve(address _spender, uint256 _value) returns (bool success) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + return true; + } + + function allowance(address _owner, address _spender) constant returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + mapping (address => uint256) balances; + mapping (address => mapping (address => uint256)) allowed; +} diff --git a/embark.json b/embark.json new file mode 100644 index 0000000..fb7de2c --- /dev/null +++ b/embark.json @@ -0,0 +1,10 @@ +{ + "contracts": ["contracts/**"], + "buildDir": "dist/", + "config": "config/", + "plugins": { + }, + "versions": { + "solc": "0.4.21" + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8317ed6 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "status-contracts", + "version": "1.0.0", + "scripts": { + "solidity-coverage": "./node_modules/.bin/solidity-coverage", + "test": "embark test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/status-im/contracts.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/status-im/contracts/issues" + }, + "homepage": "https://github.com/status-im/contracts#readme", + "devDependencies": { + "solidity-coverage": "^0.4.4", + "elliptic": "^6.4.0" + }, + "dependencies": { + "elliptic-curve": "^0.1.0", + "embark": "^2.7.0", + "ethereumjs-util": "^5.1.5", + "solidity-coverage": "^0.4.4" + } +} diff --git a/utils/testUtils.js b/utils/testUtils.js new file mode 100644 index 0000000..13f7312 --- /dev/null +++ b/utils/testUtils.js @@ -0,0 +1,97 @@ + +// This has been tested with the real Ethereum network and Testrpc. +// Copied and edited from: https://gist.github.com/xavierlepretre/d5583222fde52ddfbc58b7cfa0d2d0a9 +exports.assertReverts = (contractMethodCall, maxGasAvailable) => { + return new Promise((resolve, reject) => { + try { + resolve(contractMethodCall()) + } catch (error) { + reject(error) + } + }) + .then(tx => { + assert.equal(tx.receipt.gasUsed, maxGasAvailable, "tx successful, the max gas available was not consumed") + }) + .catch(error => { + if ((error + "").indexOf("invalid opcode") < 0 && (error + "").indexOf("out of gas") < 0) { + // Checks if the error is from TestRpc. If it is then ignore it. + // Otherwise relay/throw the error produced by the above assertion. + // Note that no error is thrown when using a real Ethereum network AND the assertion above is true. + throw error + } + }) +} + +exports.listenForEvent = event => new Promise((resolve, reject) => { + event({}, (error, response) => { + if (!error) { + resolve(response.args) + } else { + reject(error) + } + event.stopWatching() + }) +}); + +exports.eventValues = (receipt, eventName) => { + if(receipt.events[eventName]) + return receipt.events[eventName].returnValues; +} + +exports.addressToBytes32 = (address) => { + const stringed = "0000000000000000000000000000000000000000000000000000000000000000" + address.slice(2); + return "0x" + stringed.substring(stringed.length - 64, stringed.length); +} + + +// OpenZeppelin's expectThrow helper - +// Source: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers/expectThrow.js +exports.expectThrow = async promise => { + try { + await promise; + } catch (error) { + // TODO: Check jump destination to destinguish between a throw + // and an actual invalid jump. + const invalidOpcode = error.message.search('invalid opcode') >= 0; + // TODO: When we contract A calls contract B, and B throws, instead + // of an 'invalid jump', we get an 'out of gas' error. How do + // we distinguish this from an actual out of gas event? (The + // testrpc log actually show an 'invalid jump' event.) + const outOfGas = error.message.search('out of gas') >= 0; + const revert = error.message.search('revert') >= 0; + assert( + invalidOpcode || outOfGas || revert, + 'Expected throw, got \'' + error + '\' instead', + ); + return; + } + assert.fail('Expected throw not received'); + }; + + + +exports.assertJump = (error) => { + assert(error.message.search('revert') > -1, 'Revert should happen'); +} + + +var callbackToResolve = function (resolve, reject) { + return function (error, value) { + if (error) { + reject(error); + } else { + resolve(value); + } + }; +}; + + + +exports.promisify = (func) => + (...args) => { + return new Promise((resolve, reject) => { + const callback = (err, data) => err ? reject(err) : resolve(data); + func.apply(this, [...args, callback]); + }); + } +