├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .node-xmlhttprequest-content-32793 ├── .solcover.js ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── abis └── tokenSale.txt ├── app ├── index.html ├── javascripts │ └── app.js └── stylesheets │ └── app.css ├── array.json ├── array.txt ├── balances.csv ├── build └── contracts │ ├── ApproveAndCallReceiver.json │ ├── CallFallback.json │ ├── Controllable.json │ ├── Controller.json │ ├── ControllerInterface.json │ ├── ERC20.json │ ├── Migrations.json │ ├── Ownable.json │ ├── Pausable.json │ ├── ProofPresaleToken.json │ ├── ProofPresaleTokenInterface.json │ ├── ProofToken.json │ ├── ProofTokenInterface.json │ ├── SafeMath.json │ ├── TokenFactory.json │ ├── TokenFactoryInterface.json │ └── Tokensale.json ├── config.js ├── contracts ├── ApproveAndCallReceiver.sol ├── Controllable.sol ├── ControllerInterface.sol ├── ERC20.sol ├── Migrations.sol ├── Ownable.sol ├── Pausable.sol ├── ProofPresaleToken.sol ├── ProofPresaleTokenInterface.sol ├── ProofToken.sol ├── ProofTokenInterface.sol ├── SafeMath.sol ├── TokenFactory.sol ├── TokenFactoryInterface.sol └── TokenSale.sol ├── coverage.json ├── jsdoc.json ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── other_test └── crowdsale.js ├── package-lock.json ├── package.json ├── scripts ├── .vscode │ └── tasks.json ├── addresses.csv ├── balances.csv ├── controlHelpers.js ├── disableTransfers.js ├── enableTransfers.js ├── getPresaleBalances.js ├── helpers.js ├── importBalances.js ├── jsHelpers.js ├── lockBalances.js ├── logEvents.js ├── ownershipHelpers.js ├── pausableHelpers.js ├── testConfig.js ├── tokenHelpers.js ├── tokenSaleHelpers.js ├── transferTokenControl.js ├── transferTokenSaleOwnership.js ├── utils.js └── watchEvents.js ├── test ├── addresses.csv ├── balances.csv ├── cloneProofToken.js ├── initialState.js ├── ownable.js ├── pausable.js ├── payments.js ├── proofToken.js └── tokenSale.js ├── truffle.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | tmp/** 2 | build/** 3 | node_modules/** 4 | contracts/** 5 | migrations/1_initial_migration.js 6 | migrations/2_deploy_contracts.js 7 | test/metacoin.js 8 | test/TestMetacoin.sol 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard" 5 | ], 6 | "plugins": [ 7 | "babel" 8 | ], 9 | "rules": { 10 | "key-spacing" : 0, 11 | "jsx-quotes" : [2, "prefer-single"], 12 | "max-len" : [2, 120, 2], 13 | "object-curly-spacing" : [2, "always"], 14 | "no-extend-native": "off" 15 | }, 16 | "env": { 17 | "mocha": true, 18 | "node": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # node xml requests 61 | .node-xmlhttprequest-sync* 62 | 63 | -------------------------------------------------------------------------------- /.node-xmlhttprequest-content-32793: -------------------------------------------------------------------------------- 1 | {"err":null,"data":{"statusCode":200,"headers":{"access-control-allow-headers":"Origin, X-Requested-With, Content-Type, Accept","access-control-allow-origin":"*","access-control-allow-methods":"*","content-type":"application/json","date":"Sun, 22 Oct 2017 10:10:31 GMT","connection":"close","transfer-encoding":"chunked"},"text":"{\"id\":231,\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x2491df7ecd7c6c79c15119d4bd98b39721be1732dfd198bc9dc729216a46f23a\",\"transactionIndex\":\"0x00\",\"blockHash\":\"0xf50bf77470ea4a15f6d6f8836633d8eaebee618aaf5b6add9e500f6f2c5de9bc\",\"blockNumber\":\"0x0373\",\"gasUsed\":\"0x02bdd5\",\"cumulativeGasUsed\":\"0x02bdd5\",\"contractAddress\":null,\"logs\":[{\"logIndex\":\"0x00\",\"transactionIndex\":\"0x00\",\"transactionHash\":\"0x2491df7ecd7c6c79c15119d4bd98b39721be1732dfd198bc9dc729216a46f23a\",\"blockHash\":\"0xf50bf77470ea4a15f6d6f8836633d8eaebee618aaf5b6add9e500f6f2c5de9bc\",\"blockNumber\":\"0x0373\",\"address\":\"0x718a6b69565c30b3e337790ee685a30fdd1206dd\",\"data\":\"0x000000000000000000000000000000000000000000000000b9882c13c1f31488\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000006704fbfcd5ef766b287262fa2281c105d57246a6\"],\"type\":\"mined\"},{\"logIndex\":\"0x01\",\"transactionIndex\":\"0x00\",\"transactionHash\":\"0x2491df7ecd7c6c79c15119d4bd98b39721be1732dfd198bc9dc729216a46f23a\",\"blockHash\":\"0xf50bf77470ea4a15f6d6f8836633d8eaebee618aaf5b6add9e500f6f2c5de9bc\",\"blockNumber\":\"0x0373\",\"address\":\"0x555303e243f14c174b687d90e01f5314300c17a7\",\"data\":\"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000b9882c13c1f31488\",\"topics\":[\"0x623b3804fa71d67900d064613da8f94b9617215ee90799290593e1745087ad18\",\"0x0000000000000000000000006704fbfcd5ef766b287262fa2281c105d57246a6\",\"0x0000000000000000000000006704fbfcd5ef766b287262fa2281c105d57246a6\"],\"type\":\"mined\"}]}}"}} -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ['contracts/TokenSale.sol'] 3 | }; -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "attach", 7 | "name": "Attach", 8 | "port": 9229 9 | }, 10 | { 11 | "type": "node", 12 | "request": "launch", 13 | "name": "Mocha Tests", 14 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", 15 | "args": [ 16 | "-u", 17 | "tdd", 18 | "--timeout", 19 | "999999", 20 | "--colors", 21 | "${workspaceRoot}/test" 22 | ], 23 | "internalConsoleOptions": "openOnSessionStart", 24 | "runtimeExecutable": "/Users/davidvanisacker/.nvm/versions/node/v8.5.0/bin/node" 25 | }, 26 | { 27 | "type": "chrome", 28 | "request": "attach", 29 | "name": "Attach to Chrome", 30 | "port": 9222, 31 | "webRoot": "${workspaceRoot}" 32 | }, 33 | { 34 | "type": "node", 35 | "request": "launch", 36 | "name": "Launch Program", 37 | "program": "${file}", 38 | "runtimeExecutable": "/Users/davidvanisacker/.nvm/versions/node/v8.5.0/bin/node" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Proof 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proof Token Sale Smart Contracts 2 | Public Repository for the proof token sale smart contracts 3 | 4 | 5 | 6 | ## Contracts 7 | The ProofToken and TokenSale contracts are inspired by open-zeppelin standard and audited contracts. 8 | 9 | The Proof Tokens are based on the `StandardToken` and `MintableToken` ERC20 contracts. Additional functionality is heavily inspired by the `MinimeToken` (https://github.com/Giveth/minime/blob/master/contracts/MiniMeToken.sol) 10 | 11 | The TokenSale contract is inspired by the open-zeppelin `Crowdsale` contract with additional functionality mixed in. 12 | 13 | 14 | ## Presale and Initial Token Allocations 15 | 295,297 tokens have already been issued during the presale. These tokens will be imported before the first presale 16 | 17 | ## How to use the tokensale contract 18 | The following contracts are hosted on this repository. 19 | - The tokensale contract 20 | - The Proof token contract 21 | - The token factory contract 22 | - Several other interface contracts 23 | 24 | 25 | ## For the ICO team 26 | There are several prerequisites for using the smart contract. 27 | 28 | ### Contract setup: 29 | 30 | #### 1. Compile and migrate the contracts to the network. 31 | This can be done with the `truffle migrate --network ethereum` command. The contract addresses will successively appear 32 | on the console. 33 | #### 2. Import the presale balances 34 | You can use the 'scripts/importBalances.js' along with the provided csv file describing the presale balances. 35 | This import mechanism differs from the traditional way of importing presale balances via a claim function and makes the process 36 | automatic for presale buyers. The counterpart is that this requires more trust in the ICO team. 37 | Only the controller of the Proof token contract can call this function 38 | #### 3. Lock the presale balances 39 | Calling the `lockPresaleBalances` function ensures that no other presale balances can be imported. 40 | Only the controller of the Proof token contract can call this function 41 | #### 4. Transfer the Proof Token control to the Tokensale contract 42 | Proof tokens can only be created by the controller contract. The control of the proof Token can be given 43 | from the contract creator to the tokensale by calling the `changeControl` function. 44 | #### 5. Transfer the ownership of the Tokensale contract to a custom wallet 45 | It is better not to use the wallet that created the contract and transfer ownership to a wallet shared by several members of the team 46 | 47 | 48 | ### Critical Verifications: 49 | 50 | #### 1. Verify the multisig address 51 | The multisig refers to the wallet to which funds from the tokensale are transferred when a investor sends ether 52 | #### 2. Verify the proof token wallet 53 | The proof wallet receives the proof tokens when upon completion of the tokensale 54 | #### 3. Verify the start date and end date 55 | Token purchase can be made only between the start block and the end block 56 | #### 4. Verify the token the token cap, the cap, and the wei cap 57 | The token cap is the maximum number of tokens that can be issued in ERC20 units (= number of tokens times 10^18) 58 | The cap is the maximum number of tokens that can be issued 59 | #### 5. Verify all the rest 60 | 61 | 62 | - The token sale will start on the date indicated by the `startTime` variable. Alternatively, it is possible to force the starting of the token sale by calling the `forceStart` method. Only the tokensale controller can call this method. 63 | 64 | - By default the Proof tokens are not transferable at the start of the tokensale. The controller of the tokensale can call the `enableTransfers` and `lockTransfers` methods to make token transferables. After the endTime, anybody can make the tokens definitely transferables by calling the `enableTransfers` method. The `lockTransfers` method will fail after the end of the token sale. 65 | 66 | - The token master wallet can be used to send tokens to certain addresses (ex: exchanges) without making transfers available to anyone before the end of the presale. The token sale controller can call the `enableMasterTransfers` and `lockMasterTransfers` to activate/deactivate this option. 67 | 68 | 69 | ### Token Sale Pause 70 | 71 | 72 | - The tokensale can be paused with the `pause` function. In this case, token minting is paused and tokens can not be bought anymore. This does not affect whether tokens are transferable or not. 73 | 74 | 75 | ### Token Sale Finalization: 76 | 77 | The token sale can be finalized in different ways. 78 | 79 | - First if the end block is reached, no more tokens can be issued by the token sale. The rest of the functionality of the tokensale contract however remains available. It is necessary to manually enable transfers through the `enableTransfers` function (can only be called by the owner of the tokensale). 80 | 81 | 82 | - The last and standard way to finalize the token sale is to call the `finalize` function. This function can only be called by the owner of the token sale contract and can only be called when the token sale is paused. This automatically finishes the minting of the proof tokens and enables token transfers 83 | 84 | Step-by-step instructions to finalize the token sale: 85 | 86 | 1. Pause the token sale with the `pause` function 87 | 2. Allocate Proof Tokens with the `allocateProofTokens` function - step 1 needs to be completed 88 | 2. Finalize the token sale with the `finalize` function - step 1 and 2 need to be completed 89 | 3. Optionally, transfer the control of the Proof token to another smart-contract 90 | 91 | 92 | ### Development and Testing Environment Setup 93 | 94 | #### Requirements : 95 | - OSX or Linux (Windows setup is likely possible but not covered in this guide) 96 | - Node (version 8.7.0 required for the testing environment) 97 | - testrpc / geth 98 | 99 | #### Testing Environment Setup : 100 | 101 | - Clone the repository and install dependencies 102 | 103 | ``` 104 | git clone https://github.com/ProofSuite/ProofPresaleContract.git 105 | cd ProofPresaleContract 106 | npm install 107 | ``` 108 | 109 | - Install the latest version of truffle 110 | ``` 111 | npm install -g truffle 112 | ``` 113 | 114 | - Compile contracts 115 | ``` 116 | truffle compile 117 | ``` 118 | 119 | - Initialize testrpc (or geth) 120 | 121 | ``` 122 | testrpc 123 | ``` 124 | 125 | - Migrate contracts to chosen network 126 | 127 | ``` 128 | truffle migrate --network development 129 | ``` 130 | 131 | - Make sure you are using the latest version of node 132 | 133 | ``` 134 | nvm install 8.7.0 135 | nvm use 8.7.0 136 | ``` 137 | 138 | 139 | - Fill in `truffle.js` and `deploy_contracts.js` with appropriate wallet addresses. Unlock the corresponding accounts. 140 | 141 | - Verify all tests are passing 142 | 143 | ``` 144 | truffle test 145 | ``` 146 | 147 | - You can interact with the contracts via the console 148 | 149 | ``` 150 | truffle console 151 | ``` 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /abis/tokenSale.txt: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"creationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"presaleBalancesLocked","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_blockNumber","type":"uint256"}],"name":"balanceOfAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_ALLOCATED_TO_PROOF","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"lockPresaleBalances","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newController","type":"address"}],"name":"transferControl","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"parentToken","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_blockNumber","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_amount","type":"uint256"}],"name":"burn","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_snapshotBlock","type":"uint256"},{"name":"_cloneTokenName","type":"string"},{"name":"_cloneTokenSymbol","type":"string"}],"name":"createCloneToken","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"transfersEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"parentSnapShotBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"proofTokenWallet","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenFactory","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"},{"name":"_balances","type":"uint256[]"},{"name":"_presaleAddress","type":"address"}],"name":"importPresaleBalances","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_transfersEnabled","type":"bool"}],"name":"enableTransfers","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"controller","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"presale","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"_tokenFactory","type":"address"},{"name":"_parentToken","type":"address"},{"name":"_parentSnapShotBlock","type":"uint256"},{"name":"_tokenName","type":"string"},{"name":"_tokenSymbol","type":"string"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_token","type":"address"},{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"ClaimedTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_cloneToken","type":"address"},{"indexed":false,"name":"_snapshotBlock","type":"uint256"}],"name":"NewCloneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}] -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MetaCoin - Truffle Webpack Demo w/ Frontend 5 | 6 | 7 | 8 | 9 |

MetaCoin

10 |

Example Truffle Dapp

11 |

You have META

12 | 13 |
14 |

Send MetaCoin

15 |
16 |
17 |

18 |

19 | 20 |
21 | Hint: open the browser developer console to view any errors and warnings. 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/javascripts/app.js: -------------------------------------------------------------------------------- 1 | // Import the page's CSS. Webpack will know what to do with it. 2 | import "../stylesheets/app.css"; 3 | 4 | // Import libraries we need. 5 | import { default as Web3} from 'web3'; 6 | import { default as contract } from 'truffle-contract' 7 | 8 | // Import our contract artifacts and turn them into usable abstractions. 9 | import metacoin_artifacts from '../../build/contracts/MetaCoin.json' 10 | 11 | // MetaCoin is our usable abstraction, which we'll use through the code below. 12 | var MetaCoin = contract(metacoin_artifacts); 13 | 14 | // The following code is simple to show off interacting with your contracts. 15 | // As your needs grow you will likely need to change its form and structure. 16 | // For application bootstrapping, check out window.addEventListener below. 17 | var accounts; 18 | var account; 19 | 20 | window.App = { 21 | start: function() { 22 | var self = this; 23 | 24 | // Bootstrap the MetaCoin abstraction for Use. 25 | MetaCoin.setProvider(web3.currentProvider); 26 | 27 | // Get the initial account balance so it can be displayed. 28 | web3.eth.getAccounts(function(err, accs) { 29 | if (err != null) { 30 | alert("There was an error fetching your accounts."); 31 | return; 32 | } 33 | 34 | if (accs.length == 0) { 35 | alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly."); 36 | return; 37 | } 38 | 39 | accounts = accs; 40 | account = accounts[0]; 41 | 42 | self.refreshBalance(); 43 | }); 44 | }, 45 | 46 | setStatus: function(message) { 47 | var status = document.getElementById("status"); 48 | status.innerHTML = message; 49 | }, 50 | 51 | refreshBalance: function() { 52 | var self = this; 53 | 54 | var meta; 55 | MetaCoin.deployed().then(function(instance) { 56 | meta = instance; 57 | return meta.getBalance.call(account, {from: account}); 58 | }).then(function(value) { 59 | var balance_element = document.getElementById("balance"); 60 | balance_element.innerHTML = value.valueOf(); 61 | }).catch(function(e) { 62 | console.log(e); 63 | self.setStatus("Error getting balance; see log."); 64 | }); 65 | }, 66 | 67 | sendCoin: function() { 68 | var self = this; 69 | 70 | var amount = parseInt(document.getElementById("amount").value); 71 | var receiver = document.getElementById("receiver").value; 72 | 73 | this.setStatus("Initiating transaction... (please wait)"); 74 | 75 | var meta; 76 | MetaCoin.deployed().then(function(instance) { 77 | meta = instance; 78 | return meta.sendCoin(receiver, amount, {from: account}); 79 | }).then(function() { 80 | self.setStatus("Transaction complete!"); 81 | self.refreshBalance(); 82 | }).catch(function(e) { 83 | console.log(e); 84 | self.setStatus("Error sending coin; see log."); 85 | }); 86 | } 87 | }; 88 | 89 | window.addEventListener('load', function() { 90 | // Checking if Web3 has been injected by the browser (Mist/MetaMask) 91 | if (typeof web3 !== 'undefined') { 92 | console.warn("Using web3 detected from external source. If you find that your accounts don't appear or you have 0 MetaCoin, ensure you've configured that source properly. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask") 93 | // Use Mist/MetaMask's provider 94 | window.web3 = new Web3(web3.currentProvider); 95 | } else { 96 | console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask"); 97 | // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail) 98 | window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); 99 | } 100 | 101 | App.start(); 102 | }); 103 | -------------------------------------------------------------------------------- /app/stylesheets/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-left: 25%; 3 | margin-right: 25%; 4 | margin-top: 10%; 5 | font-family: "Open Sans", sans-serif; 6 | } 7 | 8 | label { 9 | display: inline-block; 10 | width: 100px; 11 | } 12 | 13 | input { 14 | width: 500px; 15 | padding: 5px; 16 | font-size: 16px; 17 | } 18 | 19 | button { 20 | font-size: 16px; 21 | padding: 5px; 22 | } 23 | 24 | h1, h2 { 25 | display: inline-block; 26 | vertical-align: middle; 27 | margin-top: 0px; 28 | margin-bottom: 10px; 29 | } 30 | 31 | h2 { 32 | color: #AAA; 33 | font-size: 32px; 34 | } 35 | 36 | h3 { 37 | font-weight: normal; 38 | color: #AAA; 39 | font-size: 24px; 40 | } 41 | 42 | .black { 43 | color: black; 44 | } 45 | 46 | #balance { 47 | color: black; 48 | } 49 | 50 | .hint { 51 | color: #666; 52 | } 53 | -------------------------------------------------------------------------------- /array.json: -------------------------------------------------------------------------------- 1 | [202,500,17680,300,294,200,202,438,202,820,454,220,1500,240,400,202,200,202,399,200,210,400,0,800,400,1360,320,815,300,400,202,548,200,800,202,202,200,200,740,230,1000,1460,201,1329,280,202,200,200,600,400,202,200,60,100,140,40,100,203,202,202,200,200,400,200,200,660,202,200,202,200,200,202,200,976,643,200,220,481,212,200,613,105,270,202,200,200,320,200,202,500,900,900,300,220,400,200,65,200,400,200,200,200,200,200,238,200,200,400,209,202,400,1000,0,200,100,100,400,65,75,400,25,40,100,100,800,200,800,202,200,276,280,22,58,300,100,40,1,9,200,200,202,200,500,200,400,202,204,200,200,200,300,200,200,0,40,50,40,200,200,null,40,230,200,200,280,1166,4000,202,200,1740,200,558,200,652,200,240,210,210,210,660,201,200,202,200,798,350,280,200,1000,50,260,202,200,790,220,240,200,212,200,400,580,200,100,100,500,299,490,204] -------------------------------------------------------------------------------- /array.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProofSuite/contracts/fda152a7859abe2ab0300ca04bec18f2c9bc0300/array.txt -------------------------------------------------------------------------------- /build/contracts/ApproveAndCallReceiver.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "ApproveAndCallReceiver", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "from", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_amount", 13 | "type": "uint256" 14 | }, 15 | { 16 | "name": "_token", 17 | "type": "address" 18 | }, 19 | { 20 | "name": "_data", 21 | "type": "bytes" 22 | } 23 | ], 24 | "name": "receiveApproval", 25 | "outputs": [], 26 | "payable": false, 27 | "type": "function" 28 | } 29 | ], 30 | "unlinked_binary": "0x", 31 | "networks": {}, 32 | "schema_version": "0.0.5", 33 | "updated_at": 1509388887463 34 | } -------------------------------------------------------------------------------- /build/contracts/CallFallback.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "CallFallback", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "from", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_amount", 13 | "type": "uint256" 14 | }, 15 | { 16 | "name": "_token", 17 | "type": "address" 18 | }, 19 | { 20 | "name": "_data", 21 | "type": "bytes" 22 | } 23 | ], 24 | "name": "receiveApproval", 25 | "outputs": [], 26 | "payable": false, 27 | "type": "function" 28 | } 29 | ], 30 | "unlinked_binary": "0x", 31 | "networks": {}, 32 | "schema_version": "0.0.5", 33 | "updated_at": 1508135998543 34 | } -------------------------------------------------------------------------------- /build/contracts/Controllable.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "Controllable", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "newController", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "transferControl", 13 | "outputs": [], 14 | "payable": false, 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "controller", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "address" 25 | } 26 | ], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "inputs": [], 32 | "payable": false, 33 | "type": "constructor" 34 | } 35 | ], 36 | "unlinked_binary": "0x6060604052341561000f57600080fd5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101218061003c6000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416636d16fa4181146046578063f77c4791146064575b600080fd5b3415605057600080fd5b6062600160a060020a03600435166090565b005b3415606e57600080fd5b607460e6565b604051600160a060020a03909116815260200160405180910390f35b60005433600160a060020a0390811691161460aa57600080fd5b600160a060020a0381161560e1576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b5b50565b600054600160a060020a0316815600a165627a7a72305820794b7da8dcff39ac0f1fcb0c785fdb02fe29eca2968d825d80b753f9b2b7bd9c0029", 37 | "networks": {}, 38 | "schema_version": "0.0.5", 39 | "updated_at": 1509388887462 40 | } -------------------------------------------------------------------------------- /build/contracts/Controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "Controller", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "_from", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_to", 13 | "type": "address" 14 | }, 15 | { 16 | "name": "_amount", 17 | "type": "uint256" 18 | } 19 | ], 20 | "name": "onTransfer", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "bool" 25 | } 26 | ], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "constant": false, 32 | "inputs": [ 33 | { 34 | "name": "_owner", 35 | "type": "address" 36 | }, 37 | { 38 | "name": "_spender", 39 | "type": "address" 40 | }, 41 | { 42 | "name": "_amount", 43 | "type": "uint256" 44 | } 45 | ], 46 | "name": "onApprove", 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "bool" 51 | } 52 | ], 53 | "payable": false, 54 | "type": "function" 55 | }, 56 | { 57 | "constant": false, 58 | "inputs": [ 59 | { 60 | "name": "_owner", 61 | "type": "address" 62 | } 63 | ], 64 | "name": "proxyPayment", 65 | "outputs": [ 66 | { 67 | "name": "", 68 | "type": "bool" 69 | } 70 | ], 71 | "payable": true, 72 | "type": "function" 73 | } 74 | ], 75 | "unlinked_binary": "0x", 76 | "networks": {}, 77 | "schema_version": "0.0.5", 78 | "updated_at": 1506973304207 79 | } -------------------------------------------------------------------------------- /build/contracts/ControllerInterface.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "ControllerInterface", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "_from", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_to", 13 | "type": "address" 14 | }, 15 | { 16 | "name": "_amount", 17 | "type": "uint256" 18 | } 19 | ], 20 | "name": "onTransfer", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "bool" 25 | } 26 | ], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "constant": false, 32 | "inputs": [ 33 | { 34 | "name": "_owner", 35 | "type": "address" 36 | }, 37 | { 38 | "name": "_spender", 39 | "type": "address" 40 | }, 41 | { 42 | "name": "_amount", 43 | "type": "uint256" 44 | } 45 | ], 46 | "name": "onApprove", 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "bool" 51 | } 52 | ], 53 | "payable": false, 54 | "type": "function" 55 | }, 56 | { 57 | "constant": false, 58 | "inputs": [ 59 | { 60 | "name": "_owner", 61 | "type": "address" 62 | } 63 | ], 64 | "name": "proxyPayment", 65 | "outputs": [ 66 | { 67 | "name": "", 68 | "type": "bool" 69 | } 70 | ], 71 | "payable": true, 72 | "type": "function" 73 | } 74 | ], 75 | "unlinked_binary": "0x", 76 | "networks": {}, 77 | "schema_version": "0.0.5", 78 | "updated_at": 1509388887463 79 | } -------------------------------------------------------------------------------- /build/contracts/ERC20.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "ERC20", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "_spender", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_amount", 13 | "type": "uint256" 14 | } 15 | ], 16 | "name": "approve", 17 | "outputs": [ 18 | { 19 | "name": "", 20 | "type": "bool" 21 | } 22 | ], 23 | "payable": false, 24 | "type": "function" 25 | }, 26 | { 27 | "constant": true, 28 | "inputs": [], 29 | "name": "totalSupply", 30 | "outputs": [ 31 | { 32 | "name": "", 33 | "type": "uint256" 34 | } 35 | ], 36 | "payable": false, 37 | "type": "function" 38 | }, 39 | { 40 | "constant": false, 41 | "inputs": [ 42 | { 43 | "name": "_from", 44 | "type": "address" 45 | }, 46 | { 47 | "name": "_to", 48 | "type": "address" 49 | }, 50 | { 51 | "name": "_amount", 52 | "type": "uint256" 53 | } 54 | ], 55 | "name": "transferFrom", 56 | "outputs": [ 57 | { 58 | "name": "", 59 | "type": "bool" 60 | } 61 | ], 62 | "payable": false, 63 | "type": "function" 64 | }, 65 | { 66 | "constant": true, 67 | "inputs": [ 68 | { 69 | "name": "_owner", 70 | "type": "address" 71 | } 72 | ], 73 | "name": "balanceOf", 74 | "outputs": [ 75 | { 76 | "name": "", 77 | "type": "uint256" 78 | } 79 | ], 80 | "payable": false, 81 | "type": "function" 82 | }, 83 | { 84 | "constant": false, 85 | "inputs": [ 86 | { 87 | "name": "_to", 88 | "type": "address" 89 | }, 90 | { 91 | "name": "_value", 92 | "type": "uint256" 93 | } 94 | ], 95 | "name": "transfer", 96 | "outputs": [ 97 | { 98 | "name": "", 99 | "type": "bool" 100 | } 101 | ], 102 | "payable": false, 103 | "type": "function" 104 | }, 105 | { 106 | "constant": true, 107 | "inputs": [ 108 | { 109 | "name": "_owner", 110 | "type": "address" 111 | }, 112 | { 113 | "name": "_spender", 114 | "type": "address" 115 | } 116 | ], 117 | "name": "allowance", 118 | "outputs": [ 119 | { 120 | "name": "", 121 | "type": "uint256" 122 | } 123 | ], 124 | "payable": false, 125 | "type": "function" 126 | }, 127 | { 128 | "anonymous": false, 129 | "inputs": [ 130 | { 131 | "indexed": true, 132 | "name": "from", 133 | "type": "address" 134 | }, 135 | { 136 | "indexed": true, 137 | "name": "to", 138 | "type": "address" 139 | }, 140 | { 141 | "indexed": false, 142 | "name": "value", 143 | "type": "uint256" 144 | } 145 | ], 146 | "name": "Transfer", 147 | "type": "event" 148 | }, 149 | { 150 | "anonymous": false, 151 | "inputs": [ 152 | { 153 | "indexed": true, 154 | "name": "owner", 155 | "type": "address" 156 | }, 157 | { 158 | "indexed": true, 159 | "name": "spender", 160 | "type": "address" 161 | }, 162 | { 163 | "indexed": false, 164 | "name": "value", 165 | "type": "uint256" 166 | } 167 | ], 168 | "name": "Approval", 169 | "type": "event" 170 | } 171 | ], 172 | "unlinked_binary": "0x", 173 | "networks": {}, 174 | "schema_version": "0.0.5", 175 | "updated_at": 1509273292392 176 | } -------------------------------------------------------------------------------- /build/contracts/Migrations.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "Migrations", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "new_address", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "upgrade", 13 | "outputs": [], 14 | "payable": false, 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "last_completed_migration", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "uint256" 25 | } 26 | ], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "constant": true, 32 | "inputs": [], 33 | "name": "owner", 34 | "outputs": [ 35 | { 36 | "name": "", 37 | "type": "address" 38 | } 39 | ], 40 | "payable": false, 41 | "type": "function" 42 | }, 43 | { 44 | "constant": false, 45 | "inputs": [ 46 | { 47 | "name": "completed", 48 | "type": "uint256" 49 | } 50 | ], 51 | "name": "setCompleted", 52 | "outputs": [], 53 | "payable": false, 54 | "type": "function" 55 | }, 56 | { 57 | "inputs": [], 58 | "payable": false, 59 | "type": "constructor" 60 | } 61 | ], 62 | "unlinked_binary": "0x6060604052341561000f57600080fd5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101e58061003c6000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630900f010811461005e578063445df0ac1461007f5780638da5cb5b146100a4578063fdacd576146100d3575b600080fd5b341561006957600080fd5b61007d600160a060020a03600435166100eb565b005b341561008a57600080fd5b610092610182565b60405190815260200160405180910390f35b34156100af57600080fd5b6100b7610188565b604051600160a060020a03909116815260200160405180910390f35b34156100de57600080fd5b61007d600435610197565b005b6000805433600160a060020a039081169116141561017c5781905080600160a060020a031663fdacd5766001546040517c010000000000000000000000000000000000000000000000000000000063ffffffff84160281526004810191909152602401600060405180830381600087803b151561016757600080fd5b6102c65a03f1151561017857600080fd5b5050505b5b5b5050565b60015481565b600054600160a060020a031681565b60005433600160a060020a03908116911614156101b45760018190555b5b5b505600a165627a7a72305820294fd71b3d7846240f6e3c14dcc95a10d2a5e70da6f5614c9a19acf3fadf8dae0029", 63 | "networks": { 64 | "1": { 65 | "events": {}, 66 | "links": {}, 67 | "address": "0x7ce0b8bbdddcc4e65aa13393e06a6454782cd8fc", 68 | "updated_at": 1509389180349 69 | }, 70 | "3": { 71 | "events": {}, 72 | "links": {}, 73 | "address": "0x647393fcb69eb31b70d59807dd916f9514a639a1", 74 | "updated_at": 1507535880957 75 | }, 76 | "4": { 77 | "events": {}, 78 | "links": {}, 79 | "address": "0x4a8c3f0ccffc9606f6962d6c7e8a1a4459d1f848", 80 | "updated_at": 1508682577039 81 | }, 82 | "1504670699258": { 83 | "events": {}, 84 | "links": {}, 85 | "address": "0xa6421a7f48498cee3aeb6428a8a2dd5faa3ace2f", 86 | "updated_at": 1504672000058 87 | }, 88 | "1504691386611": { 89 | "events": {}, 90 | "links": {}, 91 | "address": "0x5ed8b031ffdc1a1e73adb20edcfcb1fb8c8ed579", 92 | "updated_at": 1504773328278 93 | }, 94 | "1504870996916": { 95 | "events": {}, 96 | "links": {}, 97 | "address": "0x970ccf2182de4797614906db543521e3e8e99bba", 98 | "updated_at": 1504877175473 99 | }, 100 | "1505119872715": { 101 | "events": {}, 102 | "links": {}, 103 | "address": "0x320051bbd4eee344bb86f0a858d03595837463ef", 104 | "updated_at": 1505120395142 105 | }, 106 | "1506317398791": { 107 | "events": {}, 108 | "links": {}, 109 | "address": "0x5bab00b1582b170dbae7557586a29ba9eea6f55b", 110 | "updated_at": 1506327378631 111 | }, 112 | "1506498226769": { 113 | "events": {}, 114 | "links": {}, 115 | "address": "0x0f5ea0a652e851678ebf77b69484bfcd31f9459b", 116 | "updated_at": 1506498253854 117 | }, 118 | "1506524752656": { 119 | "events": {}, 120 | "links": {}, 121 | "address": "0x0f5ea0a652e851678ebf77b69484bfcd31f9459b", 122 | "updated_at": 1506524829933 123 | }, 124 | "1506969503782": { 125 | "events": {}, 126 | "links": {}, 127 | "address": "0x28b291e74bce603004b52921ec9ad3ddb6f85e44", 128 | "updated_at": 1506974436162 129 | }, 130 | "1507200004236": { 131 | "events": {}, 132 | "links": {}, 133 | "address": "0x46e082904569c60d736017c64ff703ae68da11de", 134 | "updated_at": 1507518432707 135 | }, 136 | "1508136021086": { 137 | "events": {}, 138 | "links": {}, 139 | "address": "0x162a36c9821eadecff9669a3940b7f72d055cd1c", 140 | "updated_at": 1508137979261 141 | }, 142 | "1508171544663": { 143 | "events": {}, 144 | "links": {}, 145 | "address": "0x0f5ea0a652e851678ebf77b69484bfcd31f9459b", 146 | "updated_at": 1508171555409 147 | }, 148 | "1508214325595": { 149 | "events": {}, 150 | "links": {}, 151 | "address": "0x7ef88a3901494435700ccc4c7615903b43ec2dbc", 152 | "updated_at": 1508216404737 153 | }, 154 | "1508669583748": { 155 | "events": {}, 156 | "links": {}, 157 | "address": "0xe02779c7bc8bef5fe10da107150167b505eb045e", 158 | "updated_at": 1508691293374 159 | } 160 | }, 161 | "schema_version": "0.0.5", 162 | "updated_at": 1509389180349 163 | } -------------------------------------------------------------------------------- /build/contracts/Ownable.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "Ownable", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [], 7 | "name": "owner", 8 | "outputs": [ 9 | { 10 | "name": "", 11 | "type": "address" 12 | } 13 | ], 14 | "payable": false, 15 | "type": "function" 16 | }, 17 | { 18 | "constant": false, 19 | "inputs": [ 20 | { 21 | "name": "newOwner", 22 | "type": "address" 23 | } 24 | ], 25 | "name": "transferOwnership", 26 | "outputs": [], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "inputs": [], 32 | "payable": false, 33 | "type": "constructor" 34 | } 35 | ], 36 | "unlinked_binary": "0x6060604052341561000f57600080fd5b5b60008054600160a060020a03191633600160a060020a03161790555b5b6101218061003c6000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638da5cb5b81146046578063f2fde38b146072575b600080fd5b3415605057600080fd5b60566090565b604051600160a060020a03909116815260200160405180910390f35b3415607c57600080fd5b608e600160a060020a0360043516609f565b005b600054600160a060020a031681565b60005433600160a060020a0390811691161460b957600080fd5b600160a060020a0381161560f0576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b5b505600a165627a7a7230582081bc94352517967f296b92fd8dc37701cd7d5e3a103643caea0f7a25a8c581e90029", 37 | "networks": {}, 38 | "schema_version": "0.0.5", 39 | "updated_at": 1509388887463 40 | } -------------------------------------------------------------------------------- /build/contracts/Pausable.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "Pausable", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [], 7 | "name": "unpause", 8 | "outputs": [ 9 | { 10 | "name": "", 11 | "type": "bool" 12 | } 13 | ], 14 | "payable": false, 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "paused", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "bool" 25 | } 26 | ], 27 | "payable": false, 28 | "type": "function" 29 | }, 30 | { 31 | "constant": false, 32 | "inputs": [], 33 | "name": "pause", 34 | "outputs": [ 35 | { 36 | "name": "", 37 | "type": "bool" 38 | } 39 | ], 40 | "payable": false, 41 | "type": "function" 42 | }, 43 | { 44 | "constant": true, 45 | "inputs": [], 46 | "name": "owner", 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "address" 51 | } 52 | ], 53 | "payable": false, 54 | "type": "function" 55 | }, 56 | { 57 | "constant": false, 58 | "inputs": [ 59 | { 60 | "name": "newOwner", 61 | "type": "address" 62 | } 63 | ], 64 | "name": "transferOwnership", 65 | "outputs": [], 66 | "payable": false, 67 | "type": "function" 68 | }, 69 | { 70 | "inputs": [], 71 | "payable": false, 72 | "type": "constructor" 73 | }, 74 | { 75 | "anonymous": false, 76 | "inputs": [], 77 | "name": "Pause", 78 | "type": "event" 79 | }, 80 | { 81 | "anonymous": false, 82 | "inputs": [], 83 | "name": "Unpause", 84 | "type": "event" 85 | } 86 | ], 87 | "unlinked_binary": "0x60606040526000805460a060020a60ff0219169055341561001f57600080fd5b5b5b60008054600160a060020a03191633600160a060020a03161790555b5b5b6102e48061004e6000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633f4ba83a81146100695780635c975abb146100905780638456cb59146100b75780638da5cb5b146100de578063f2fde38b1461010d575b600080fd5b341561007457600080fd5b61007c61012e565b604051901515815260200160405180910390f35b341561009b57600080fd5b61007c6101b5565b604051901515815260200160405180910390f35b34156100c257600080fd5b61007c6101c5565b604051901515815260200160405180910390f35b34156100e957600080fd5b6100f1610251565b604051600160a060020a03909116815260200160405180910390f35b341561011857600080fd5b61012c600160a060020a0360043516610260565b005b6000805433600160a060020a0390811691161461014a57600080fd5b60005460a060020a900460ff16151561016257600080fd5b6000805474ff0000000000000000000000000000000000000000191690557f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b3360405160405180910390a15060015b5b5b90565b60005460a060020a900460ff1681565b6000805433600160a060020a039081169116146101e157600080fd5b60005460a060020a900460ff16156101f857600080fd5b6000805474ff0000000000000000000000000000000000000000191660a060020a1790557f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff62560405160405180910390a15060015b5b5b90565b600054600160a060020a031681565b60005433600160a060020a0390811691161461027b57600080fd5b600160a060020a038116156102b3576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b5b505600a165627a7a723058209c60b6313881749c62989ee94a7cdb4cecbadca33f61d295fb363e29bd01d6c00029", 88 | "networks": {}, 89 | "schema_version": "0.0.5", 90 | "updated_at": 1509388887465 91 | } -------------------------------------------------------------------------------- /build/contracts/ProofPresaleTokenInterface.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "ProofPresaleTokenInterface", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "_spender", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_value", 13 | "type": "uint256" 14 | } 15 | ], 16 | "name": "approve", 17 | "outputs": [ 18 | { 19 | "name": "", 20 | "type": "bool" 21 | } 22 | ], 23 | "payable": false, 24 | "type": "function" 25 | }, 26 | { 27 | "constant": true, 28 | "inputs": [], 29 | "name": "totalSupply", 30 | "outputs": [ 31 | { 32 | "name": "", 33 | "type": "uint256" 34 | } 35 | ], 36 | "payable": false, 37 | "type": "function" 38 | }, 39 | { 40 | "constant": false, 41 | "inputs": [ 42 | { 43 | "name": "_from", 44 | "type": "address" 45 | }, 46 | { 47 | "name": "_to", 48 | "type": "address" 49 | }, 50 | { 51 | "name": "_value", 52 | "type": "uint256" 53 | } 54 | ], 55 | "name": "transferFrom", 56 | "outputs": [ 57 | { 58 | "name": "", 59 | "type": "bool" 60 | } 61 | ], 62 | "payable": false, 63 | "type": "function" 64 | }, 65 | { 66 | "constant": false, 67 | "inputs": [ 68 | { 69 | "name": "_to", 70 | "type": "address" 71 | }, 72 | { 73 | "name": "_amount", 74 | "type": "uint256" 75 | } 76 | ], 77 | "name": "mint", 78 | "outputs": [ 79 | { 80 | "name": "", 81 | "type": "bool" 82 | } 83 | ], 84 | "payable": false, 85 | "type": "function" 86 | }, 87 | { 88 | "constant": true, 89 | "inputs": [ 90 | { 91 | "name": "_owner", 92 | "type": "address" 93 | } 94 | ], 95 | "name": "balanceOf", 96 | "outputs": [ 97 | { 98 | "name": "", 99 | "type": "uint256" 100 | } 101 | ], 102 | "payable": false, 103 | "type": "function" 104 | }, 105 | { 106 | "constant": false, 107 | "inputs": [], 108 | "name": "finishMinting", 109 | "outputs": [ 110 | { 111 | "name": "", 112 | "type": "bool" 113 | } 114 | ], 115 | "payable": false, 116 | "type": "function" 117 | }, 118 | { 119 | "constant": false, 120 | "inputs": [ 121 | { 122 | "name": "_to", 123 | "type": "address" 124 | }, 125 | { 126 | "name": "_value", 127 | "type": "uint256" 128 | } 129 | ], 130 | "name": "transfer", 131 | "outputs": [ 132 | { 133 | "name": "", 134 | "type": "bool" 135 | } 136 | ], 137 | "payable": false, 138 | "type": "function" 139 | }, 140 | { 141 | "constant": true, 142 | "inputs": [ 143 | { 144 | "name": "_owner", 145 | "type": "address" 146 | }, 147 | { 148 | "name": "_spender", 149 | "type": "address" 150 | } 151 | ], 152 | "name": "allowance", 153 | "outputs": [ 154 | { 155 | "name": "", 156 | "type": "uint256" 157 | } 158 | ], 159 | "payable": false, 160 | "type": "function" 161 | } 162 | ], 163 | "unlinked_binary": "0x", 164 | "networks": {}, 165 | "schema_version": "0.0.5", 166 | "updated_at": 1509273292393 167 | } -------------------------------------------------------------------------------- /build/contracts/ProofTokenInterface.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "ProofTokenInterface", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "_spender", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_amount", 13 | "type": "uint256" 14 | } 15 | ], 16 | "name": "approve", 17 | "outputs": [ 18 | { 19 | "name": "success", 20 | "type": "bool" 21 | } 22 | ], 23 | "payable": false, 24 | "type": "function" 25 | }, 26 | { 27 | "constant": true, 28 | "inputs": [], 29 | "name": "totalSupply", 30 | "outputs": [ 31 | { 32 | "name": "", 33 | "type": "uint256" 34 | } 35 | ], 36 | "payable": false, 37 | "type": "function" 38 | }, 39 | { 40 | "constant": false, 41 | "inputs": [ 42 | { 43 | "name": "_from", 44 | "type": "address" 45 | }, 46 | { 47 | "name": "_to", 48 | "type": "address" 49 | }, 50 | { 51 | "name": "_amount", 52 | "type": "uint256" 53 | } 54 | ], 55 | "name": "transferFrom", 56 | "outputs": [ 57 | { 58 | "name": "success", 59 | "type": "bool" 60 | } 61 | ], 62 | "payable": false, 63 | "type": "function" 64 | }, 65 | { 66 | "constant": false, 67 | "inputs": [ 68 | { 69 | "name": "_owner", 70 | "type": "address" 71 | }, 72 | { 73 | "name": "_amount", 74 | "type": "uint256" 75 | } 76 | ], 77 | "name": "mint", 78 | "outputs": [ 79 | { 80 | "name": "", 81 | "type": "bool" 82 | } 83 | ], 84 | "payable": false, 85 | "type": "function" 86 | }, 87 | { 88 | "constant": true, 89 | "inputs": [ 90 | { 91 | "name": "_owner", 92 | "type": "address" 93 | }, 94 | { 95 | "name": "_blockNumber", 96 | "type": "uint256" 97 | } 98 | ], 99 | "name": "balanceOfAt", 100 | "outputs": [ 101 | { 102 | "name": "", 103 | "type": "uint256" 104 | } 105 | ], 106 | "payable": false, 107 | "type": "function" 108 | }, 109 | { 110 | "constant": false, 111 | "inputs": [], 112 | "name": "lockPresaleBalances", 113 | "outputs": [ 114 | { 115 | "name": "", 116 | "type": "bool" 117 | } 118 | ], 119 | "payable": false, 120 | "type": "function" 121 | }, 122 | { 123 | "constant": false, 124 | "inputs": [ 125 | { 126 | "name": "newController", 127 | "type": "address" 128 | } 129 | ], 130 | "name": "transferControl", 131 | "outputs": [], 132 | "payable": false, 133 | "type": "function" 134 | }, 135 | { 136 | "constant": true, 137 | "inputs": [ 138 | { 139 | "name": "_owner", 140 | "type": "address" 141 | } 142 | ], 143 | "name": "balanceOf", 144 | "outputs": [ 145 | { 146 | "name": "balance", 147 | "type": "uint256" 148 | } 149 | ], 150 | "payable": false, 151 | "type": "function" 152 | }, 153 | { 154 | "constant": false, 155 | "inputs": [], 156 | "name": "finishMinting", 157 | "outputs": [ 158 | { 159 | "name": "", 160 | "type": "bool" 161 | } 162 | ], 163 | "payable": false, 164 | "type": "function" 165 | }, 166 | { 167 | "constant": true, 168 | "inputs": [ 169 | { 170 | "name": "_blockNumber", 171 | "type": "uint256" 172 | } 173 | ], 174 | "name": "totalSupplyAt", 175 | "outputs": [ 176 | { 177 | "name": "", 178 | "type": "uint256" 179 | } 180 | ], 181 | "payable": false, 182 | "type": "function" 183 | }, 184 | { 185 | "constant": false, 186 | "inputs": [ 187 | { 188 | "name": "_snapshotBlock", 189 | "type": "uint256" 190 | }, 191 | { 192 | "name": "_cloneTokenName", 193 | "type": "string" 194 | }, 195 | { 196 | "name": "_cloneTokenSymbol", 197 | "type": "string" 198 | } 199 | ], 200 | "name": "createCloneToken", 201 | "outputs": [ 202 | { 203 | "name": "", 204 | "type": "address" 205 | } 206 | ], 207 | "payable": false, 208 | "type": "function" 209 | }, 210 | { 211 | "constant": false, 212 | "inputs": [ 213 | { 214 | "name": "_to", 215 | "type": "address" 216 | }, 217 | { 218 | "name": "_amount", 219 | "type": "uint256" 220 | } 221 | ], 222 | "name": "transfer", 223 | "outputs": [ 224 | { 225 | "name": "success", 226 | "type": "bool" 227 | } 228 | ], 229 | "payable": false, 230 | "type": "function" 231 | }, 232 | { 233 | "constant": false, 234 | "inputs": [ 235 | { 236 | "name": "_value", 237 | "type": "bool" 238 | } 239 | ], 240 | "name": "enableMasterTransfers", 241 | "outputs": [], 242 | "payable": false, 243 | "type": "function" 244 | }, 245 | { 246 | "constant": false, 247 | "inputs": [ 248 | { 249 | "name": "_spender", 250 | "type": "address" 251 | }, 252 | { 253 | "name": "_amount", 254 | "type": "uint256" 255 | }, 256 | { 257 | "name": "_extraData", 258 | "type": "bytes" 259 | } 260 | ], 261 | "name": "approveAndCall", 262 | "outputs": [ 263 | { 264 | "name": "success", 265 | "type": "bool" 266 | } 267 | ], 268 | "payable": false, 269 | "type": "function" 270 | }, 271 | { 272 | "constant": true, 273 | "inputs": [ 274 | { 275 | "name": "_owner", 276 | "type": "address" 277 | }, 278 | { 279 | "name": "_spender", 280 | "type": "address" 281 | } 282 | ], 283 | "name": "allowance", 284 | "outputs": [ 285 | { 286 | "name": "remaining", 287 | "type": "uint256" 288 | } 289 | ], 290 | "payable": false, 291 | "type": "function" 292 | }, 293 | { 294 | "constant": false, 295 | "inputs": [ 296 | { 297 | "name": "_addresses", 298 | "type": "address[]" 299 | }, 300 | { 301 | "name": "_balances", 302 | "type": "uint256[]" 303 | }, 304 | { 305 | "name": "_presaleAddress", 306 | "type": "address" 307 | } 308 | ], 309 | "name": "importPresaleBalances", 310 | "outputs": [ 311 | { 312 | "name": "", 313 | "type": "bool" 314 | } 315 | ], 316 | "payable": false, 317 | "type": "function" 318 | }, 319 | { 320 | "constant": false, 321 | "inputs": [ 322 | { 323 | "name": "_value", 324 | "type": "bool" 325 | } 326 | ], 327 | "name": "enableTransfers", 328 | "outputs": [], 329 | "payable": false, 330 | "type": "function" 331 | }, 332 | { 333 | "constant": true, 334 | "inputs": [], 335 | "name": "controller", 336 | "outputs": [ 337 | { 338 | "name": "", 339 | "type": "address" 340 | } 341 | ], 342 | "payable": false, 343 | "type": "function" 344 | }, 345 | { 346 | "anonymous": false, 347 | "inputs": [ 348 | { 349 | "indexed": true, 350 | "name": "to", 351 | "type": "address" 352 | }, 353 | { 354 | "indexed": false, 355 | "name": "amount", 356 | "type": "uint256" 357 | } 358 | ], 359 | "name": "Mint", 360 | "type": "event" 361 | }, 362 | { 363 | "anonymous": false, 364 | "inputs": [], 365 | "name": "MintFinished", 366 | "type": "event" 367 | }, 368 | { 369 | "anonymous": false, 370 | "inputs": [ 371 | { 372 | "indexed": true, 373 | "name": "_token", 374 | "type": "address" 375 | }, 376 | { 377 | "indexed": true, 378 | "name": "_owner", 379 | "type": "address" 380 | }, 381 | { 382 | "indexed": false, 383 | "name": "_amount", 384 | "type": "uint256" 385 | } 386 | ], 387 | "name": "ClaimedTokens", 388 | "type": "event" 389 | }, 390 | { 391 | "anonymous": false, 392 | "inputs": [ 393 | { 394 | "indexed": true, 395 | "name": "_cloneToken", 396 | "type": "address" 397 | }, 398 | { 399 | "indexed": false, 400 | "name": "_snapshotBlock", 401 | "type": "uint256" 402 | } 403 | ], 404 | "name": "NewCloneToken", 405 | "type": "event" 406 | }, 407 | { 408 | "anonymous": false, 409 | "inputs": [ 410 | { 411 | "indexed": true, 412 | "name": "_owner", 413 | "type": "address" 414 | }, 415 | { 416 | "indexed": true, 417 | "name": "_spender", 418 | "type": "address" 419 | }, 420 | { 421 | "indexed": false, 422 | "name": "_amount", 423 | "type": "uint256" 424 | } 425 | ], 426 | "name": "Approval", 427 | "type": "event" 428 | }, 429 | { 430 | "anonymous": false, 431 | "inputs": [ 432 | { 433 | "indexed": true, 434 | "name": "from", 435 | "type": "address" 436 | }, 437 | { 438 | "indexed": true, 439 | "name": "to", 440 | "type": "address" 441 | }, 442 | { 443 | "indexed": false, 444 | "name": "value", 445 | "type": "uint256" 446 | } 447 | ], 448 | "name": "Transfer", 449 | "type": "event" 450 | } 451 | ], 452 | "unlinked_binary": "0x", 453 | "networks": {}, 454 | "schema_version": "0.0.5", 455 | "updated_at": 1509388887465 456 | } -------------------------------------------------------------------------------- /build/contracts/SafeMath.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "SafeMath", 3 | "abi": [], 4 | "unlinked_binary": "0x60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00a165627a7a7230582061508761e8d7a0b0542f19eb06cf37abb78bd35c2fdbb50ad3b3fa9bf427c0e80029", 5 | "networks": {}, 6 | "schema_version": "0.0.5", 7 | "updated_at": 1509388887465 8 | } -------------------------------------------------------------------------------- /build/contracts/TokenFactoryInterface.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "TokenFactoryInterface", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "_parentToken", 9 | "type": "address" 10 | }, 11 | { 12 | "name": "_snapshotBlock", 13 | "type": "uint256" 14 | }, 15 | { 16 | "name": "_tokenName", 17 | "type": "string" 18 | }, 19 | { 20 | "name": "_tokenSymbol", 21 | "type": "string" 22 | } 23 | ], 24 | "name": "createCloneToken", 25 | "outputs": [ 26 | { 27 | "name": "newToken", 28 | "type": "address" 29 | } 30 | ], 31 | "payable": false, 32 | "type": "function" 33 | } 34 | ], 35 | "unlinked_binary": "0x", 36 | "networks": {}, 37 | "schema_version": "0.0.5", 38 | "updated_at": 1509388887466 39 | } -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | let config = { 2 | infura: { 3 | ethereum: 'https://mainnet.infura.io/Oi27hEUIuGqMsrYGpI7e', 4 | ropsten: 'https://ropsten.infura.io/Oi27hEUIuGqMsrYGpI7e', 5 | rinkeby: 'https://rinkeby.infura.io/Oi27hEUIuGqMsrYGpI7e', 6 | kovan: 'https://kovan.infura.io/Oi27hEUIuGqMsrYGpI7e' 7 | }, 8 | constants: { 9 | DEFAULT_GAS: 2 * 10 ** 6, 10 | MAX_GAS: 4.7 * 10 ** 6, 11 | DEFAULT_LOW_GAS_PRICE: 0.1 * 10 ** 9, 12 | DEFAULT_GAS_PRICE: 1 * 10 ** 9, 13 | DEFAULT_HIGH_GAS_PRICE: 5 * 10 ** 9, 14 | TOKENS_ALLOCATED_TO_PROOF: 1181031 * (10 ** 18), 15 | DECIMALS_POINTS: 10 ** 18, 16 | TOKEN_UNITS: 10 ** 18, 17 | START_TIMESTAMP: 1509541200, 18 | END_TIMESTAMP: 1512133200, 19 | }, 20 | addresses: { 21 | development: { 22 | WALLET_ADDRESS: '0x6704fbfcd5ef766b287262fa2281c105d57246a6', 23 | TOKEN_WALLET_ADDRESS: '0x6704fbfcd5ef766b287262fa2281c105d57246a6' 24 | }, 25 | rinkeby: { 26 | WALLET_ADDRESS: '0x9fbdaac5faf6711f38ab26541b7c0d72cb2c0e11', 27 | TOKEN_WALLET_ADDRESS: '0x9fbdaac5faf6711f38ab26541b7c0d72cb2c0e11' 28 | }, 29 | ropsten: { 30 | WALLET_ADDRESS: '', 31 | TOKEN_WALLET_ADDRESS: '' 32 | }, 33 | ethereum: { 34 | WALLET_ADDRESS: '0x11e3de1bda2650fa6bc74e7cea6a39559e59b103', 35 | TOKEN_WALLET_ADDRESS: '0x11e3de1bda2650fa6bc74e7cea6a39559e59b103', 36 | PRESALE_TOKEN: '0x2469f31A34FCaAc0debf73806cE39B2388874B13' 37 | } 38 | } 39 | } 40 | 41 | module.exports = config -------------------------------------------------------------------------------- /contracts/ApproveAndCallReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract ApproveAndCallReceiver { 4 | function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public; 5 | } -------------------------------------------------------------------------------- /contracts/Controllable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | 4 | /** 5 | * @title Controllable 6 | * @dev The Controllable contract has an controller address, and provides basic authorization control 7 | * functions, this simplifies the implementation of "user permissions". 8 | */ 9 | contract Controllable { 10 | address public controller; 11 | 12 | 13 | /** 14 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender account. 15 | */ 16 | function Controllable() public { 17 | controller = msg.sender; 18 | } 19 | 20 | /** 21 | * @dev Throws if called by any account other than the owner. 22 | */ 23 | modifier onlyController() { 24 | require(msg.sender == controller); 25 | _; 26 | } 27 | 28 | /** 29 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 30 | * @param newController The address to transfer ownership to. 31 | */ 32 | function transferControl(address newController) public onlyController { 33 | if (newController != address(0)) { 34 | controller = newController; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /contracts/ControllerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | /// @dev The token controller contract must implement these functions 4 | contract ControllerInterface { 5 | 6 | function proxyPayment(address _owner) public payable returns(bool); 7 | function onTransfer(address _from, address _to, uint _amount) public returns(bool); 8 | function onApprove(address _owner, address _spender, uint _amount) public returns(bool); 9 | } -------------------------------------------------------------------------------- /contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | /** 4 | * @title ERC20 interface 5 | * @dev see https://github.com/ethereum/EIPs/issues/20 6 | */ 7 | contract ERC20 { 8 | 9 | uint256 public totalSupply; 10 | 11 | function balanceOf(address _owner) constant returns (uint256); 12 | function transfer(address _to, uint256 _value) returns (bool); 13 | function transferFrom(address _from, address _to, uint256 _amount) returns (bool); 14 | function approve(address _spender, uint256 _amount) returns (bool); 15 | function allowance(address _owner, address _spender) constant returns (uint256); 16 | 17 | event Transfer(address indexed from, address indexed to, uint256 value); 18 | event Approval(address indexed owner, address indexed spender, uint256 value); 19 | 20 | } -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | 4 | /** 5 | * @title Ownable 6 | * @dev The Ownable contract has an owner address, and provides basic authorization control 7 | * functions, this simplifies the implementation of "user permissions". 8 | */ 9 | contract Ownable { 10 | address public owner; 11 | 12 | 13 | /** 14 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender account. 15 | */ 16 | function Ownable() public { 17 | owner = msg.sender; 18 | } 19 | 20 | /** 21 | * @dev Throws if called by any account other than the owner. 22 | */ 23 | modifier onlyOwner() { 24 | require(msg.sender == owner); 25 | _; 26 | } 27 | 28 | /** 29 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 30 | * @param newOwner The address to transfer ownership to. 31 | */ 32 | function transferOwnership(address newOwner) public onlyOwner { 33 | if (newOwner != address(0)) { 34 | owner = newOwner; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /contracts/Pausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | 4 | import "./Ownable.sol"; 5 | 6 | 7 | /** 8 | * @title Pausable 9 | * @dev Base contract which allows children to implement an emergency stop mechanism. 10 | */ 11 | contract Pausable is Ownable { 12 | event Pause(); 13 | event Unpause(); 14 | 15 | bool public paused = false; 16 | 17 | function Pausable() public {} 18 | 19 | /** 20 | * @dev modifier to allow actions only when the contract IS paused 21 | */ 22 | modifier whenNotPaused() { 23 | require(!paused); 24 | _; 25 | } 26 | 27 | /** 28 | * @dev modifier to allow actions only when the contract IS NOT paused 29 | */ 30 | modifier whenPaused { 31 | require(paused); 32 | _; 33 | } 34 | 35 | /** 36 | * @dev called by the owner to pause, triggers stopped state 37 | */ 38 | function pause() public onlyOwner whenNotPaused returns (bool) { 39 | paused = true; 40 | Pause(); 41 | return true; 42 | } 43 | 44 | /** 45 | * @dev called by the owner to unpause, returns to normal state 46 | */ 47 | function unpause() public onlyOwner whenPaused returns (bool) { 48 | paused = false; 49 | Unpause(); 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/ProofPresaleToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import './SafeMath.sol'; 4 | import './ERC20.sol'; 5 | import './Controllable.sol'; 6 | 7 | /** 8 | * @title ProofPresaleToken (PROOFP) 9 | * Standard Mintable ERC20 Token 10 | * https://github.com/ethereum/EIPs/issues/20 11 | * Based on code by FirstBlood: 12 | * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 13 | */ 14 | 15 | contract ProofPresaleToken is ERC20, Controllable { 16 | 17 | using SafeMath for uint256; 18 | 19 | uint256 public totalSupply; 20 | 21 | mapping(address => uint) balances; 22 | mapping (address => mapping (address => uint)) allowed; 23 | 24 | string public constant name = "Proof Presale Token"; 25 | string public constant symbol = "PPT"; 26 | uint8 public constant decimals = 18; 27 | bool public mintingFinished = false; 28 | 29 | event Mint(address indexed to, uint256 amount); 30 | event MintFinished(); 31 | 32 | function ProofPresaleToken() {} 33 | 34 | function() payable { 35 | revert(); 36 | } 37 | 38 | function balanceOf(address _owner) constant returns (uint256) { 39 | return balances[_owner]; 40 | } 41 | 42 | function transfer(address _to, uint _value) returns (bool) { 43 | 44 | balances[msg.sender] = balances[msg.sender].sub(_value); 45 | balances[_to] = balances[_to].add(_value); 46 | 47 | Transfer(msg.sender, _to, _value); 48 | return true; 49 | } 50 | 51 | function transferFrom(address _from, address _to, uint _value) returns (bool) { 52 | var _allowance = allowed[_from][msg.sender]; 53 | 54 | balances[_to] = balances[_to].add(_value); 55 | balances[_from] = balances[_from].sub(_value); 56 | allowed[_from][msg.sender] = _allowance.sub(_value); 57 | 58 | Transfer(_from, _to, _value); 59 | return true; 60 | } 61 | 62 | function approve(address _spender, uint _value) returns (bool) { 63 | allowed[msg.sender][_spender] = _value; 64 | Approval(msg.sender, _spender, _value); 65 | return true; 66 | } 67 | 68 | function allowance(address _owner, address _spender) constant returns (uint256) { 69 | return allowed[_owner][_spender]; 70 | } 71 | 72 | 73 | modifier canMint() { 74 | require(!mintingFinished); 75 | _; 76 | } 77 | 78 | function mint(address _to, uint256 _amount) onlyController canMint returns (bool) { 79 | totalSupply = totalSupply.add(_amount); 80 | balances[_to] = balances[_to].add(_amount); 81 | Mint(_to, _amount); 82 | return true; 83 | } 84 | 85 | function finishMinting() onlyController returns (bool) { 86 | mintingFinished = true; 87 | MintFinished(); 88 | return true; 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /contracts/ProofPresaleTokenInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | /** 4 | * @title ProofPresaleToken (PROOFP) 5 | * Standard Mintable ERC20 Token 6 | * https://github.com/ethereum/EIPs/issues/20 7 | * Based on code by FirstBlood: 8 | * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 9 | */ 10 | 11 | contract ProofPresaleTokenInterface { 12 | 13 | uint256 public totalSupply; 14 | 15 | function balanceOf(address _owner) constant returns (uint256); 16 | function transfer(address _to, uint _value) returns (bool); 17 | function transferFrom(address _from, address _to, uint _value) returns (bool); 18 | function approve(address _spender, uint _value) returns (bool); 19 | function allowance(address _owner, address _spender) constant returns (uint256); 20 | function mint(address _to, uint256 _amount) returns (bool); 21 | function finishMinting() returns (bool); 22 | 23 | } -------------------------------------------------------------------------------- /contracts/ProofTokenInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import './Controllable.sol'; 4 | 5 | /** 6 | * @title ProofToken (PRFT) 7 | * Standard Mintable ERC20 Token 8 | * https://github.com/ethereum/EIPs/issues/20 9 | * Based on code by FirstBlood: 10 | * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 11 | */ 12 | contract ProofTokenInterface is Controllable { 13 | 14 | event Mint(address indexed to, uint256 amount); 15 | event MintFinished(); 16 | event ClaimedTokens(address indexed _token, address indexed _owner, uint _amount); 17 | event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock); 18 | event Approval(address indexed _owner, address indexed _spender, uint256 _amount); 19 | event Transfer(address indexed from, address indexed to, uint256 value); 20 | 21 | function totalSupply() public constant returns (uint); 22 | function totalSupplyAt(uint _blockNumber) public constant returns(uint); 23 | function balanceOf(address _owner) public constant returns (uint256 balance); 24 | function balanceOfAt(address _owner, uint _blockNumber) public constant returns (uint); 25 | function transfer(address _to, uint256 _amount) public returns (bool success); 26 | function transferFrom(address _from, address _to, uint256 _amount) public returns (bool success); 27 | function approve(address _spender, uint256 _amount) public returns (bool success); 28 | function approveAndCall(address _spender, uint256 _amount, bytes _extraData) public returns (bool success); 29 | function allowance(address _owner, address _spender) public constant returns (uint256 remaining); 30 | function mint(address _owner, uint _amount) public returns (bool); 31 | function importPresaleBalances(address[] _addresses, uint256[] _balances, address _presaleAddress) public returns (bool); 32 | function lockPresaleBalances() public returns (bool); 33 | function finishMinting() public returns (bool); 34 | function enableTransfers(bool _value) public; 35 | function enableMasterTransfers(bool _value) public; 36 | function createCloneToken(uint _snapshotBlock, string _cloneTokenName, string _cloneTokenSymbol) public returns (address); 37 | 38 | } -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | 4 | /** 5 | * @title SafeMath 6 | * @dev Math operations with safety checks that throw on error 7 | */ 8 | library SafeMath { 9 | function mul(uint256 a, uint256 b) internal constant returns (uint256) { 10 | uint256 c = a * b; 11 | assert(a == 0 || c / a == b); 12 | return c; 13 | } 14 | 15 | function div(uint256 a, uint256 b) internal constant returns (uint256) { 16 | // assert(b > 0); // Solidity automatically throws when dividing by 0 17 | uint256 c = a / b; 18 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 19 | return c; 20 | } 21 | 22 | function sub(uint256 a, uint256 b) internal constant returns (uint256) { 23 | assert(b <= a); 24 | return a - b; 25 | } 26 | 27 | function add(uint256 a, uint256 b) internal constant returns (uint256) { 28 | uint256 c = a + b; 29 | assert(c >= a); 30 | return c; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/TokenFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import './ProofToken.sol'; 4 | import './Ownable.sol'; 5 | 6 | contract TokenFactory { 7 | 8 | function createCloneToken( 9 | address _parentToken, 10 | uint _snapshotBlock, 11 | string _tokenName, 12 | string _tokenSymbol 13 | ) public returns (ProofToken) { 14 | 15 | ProofToken newToken = new ProofToken( 16 | this, 17 | _parentToken, 18 | _snapshotBlock, 19 | _tokenName, 20 | _tokenSymbol 21 | ); 22 | 23 | newToken.transferControl(msg.sender); 24 | return newToken; 25 | } 26 | } -------------------------------------------------------------------------------- /contracts/TokenFactoryInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import './ProofToken.sol'; 4 | 5 | contract TokenFactoryInterface { 6 | 7 | function createCloneToken( 8 | address _parentToken, 9 | uint _snapshotBlock, 10 | string _tokenName, 11 | string _tokenSymbol 12 | ) public returns (ProofToken newToken); 13 | } -------------------------------------------------------------------------------- /contracts/TokenSale.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import './SafeMath.sol'; 4 | import './Pausable.sol'; 5 | import './ProofTokenInterface.sol'; 6 | /** 7 | * @title Tokensale 8 | * Tokensale allows investors to make token purchases and assigns them tokens based 9 | 10 | * on a token per ETH rate. Funds collected are forwarded to a wallet as they arrive. 11 | */ 12 | contract TokenSale is Pausable { 13 | 14 | using SafeMath for uint256; 15 | 16 | ProofTokenInterface public proofToken; 17 | uint256 public totalWeiRaised; 18 | uint256 public tokensMinted; 19 | uint256 public totalSupply; 20 | uint256 public contributors; 21 | uint256 public decimalsMultiplier; 22 | uint256 public startTime; 23 | uint256 public endTime; 24 | uint256 public remainingTokens; 25 | uint256 public allocatedTokens; 26 | 27 | bool public finalized; 28 | 29 | bool public proofTokensAllocated; 30 | address public proofMultiSig = 0x99892Ac6DA1b3851167Cb959fE945926bca89f09; 31 | 32 | uint256 public constant BASE_PRICE_IN_WEI = 88000000000000000; 33 | uint256 public constant PUBLIC_TOKENS = 1181031 * (10 ** 18); 34 | uint256 public constant TOTAL_PRESALE_TOKENS = 112386712924725508802400; 35 | uint256 public constant TOKENS_ALLOCATED_TO_PROOF = 1181031 * (10 ** 18); 36 | 37 | 38 | 39 | uint256 public tokenCap = PUBLIC_TOKENS - TOTAL_PRESALE_TOKENS; 40 | uint256 public cap = tokenCap / (10 ** 18); 41 | uint256 public weiCap = cap * BASE_PRICE_IN_WEI; 42 | 43 | uint256 public firstDiscountPrice = (BASE_PRICE_IN_WEI * 85) / 100; 44 | uint256 public secondDiscountPrice = (BASE_PRICE_IN_WEI * 90) / 100; 45 | uint256 public thirdDiscountPrice = (BASE_PRICE_IN_WEI * 95) / 100; 46 | 47 | uint256 public firstDiscountCap = (weiCap * 5) / 100; 48 | uint256 public secondDiscountCap = (weiCap * 10) / 100; 49 | uint256 public thirdDiscountCap = (weiCap * 20) / 100; 50 | 51 | bool public started = false; 52 | 53 | event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); 54 | event NewClonedToken(address indexed _cloneToken); 55 | event OnTransfer(address _from, address _to, uint _amount); 56 | event OnApprove(address _owner, address _spender, uint _amount); 57 | event LogInt(string _name, uint256 _value); 58 | event Finalized(); 59 | 60 | function TokenSale(address _tokenAddress, uint256 _startTime, uint256 _endTime) public { 61 | require(_tokenAddress != 0x0); 62 | require(_startTime > 0); 63 | require(_endTime > _startTime); 64 | 65 | startTime = _startTime; 66 | endTime = _endTime; 67 | proofToken = ProofTokenInterface(_tokenAddress); 68 | 69 | decimalsMultiplier = (10 ** 18); 70 | } 71 | 72 | 73 | /** 74 | * High level token purchase function 75 | */ 76 | function() public payable { 77 | buyTokens(msg.sender); 78 | } 79 | 80 | /** 81 | * Low level token purchase function 82 | * @param _beneficiary will receive the tokens. 83 | */ 84 | function buyTokens(address _beneficiary) public payable whenNotPaused whenNotFinalized { 85 | require(_beneficiary != 0x0); 86 | require(validPurchase()); 87 | 88 | uint256 weiAmount = msg.value; 89 | uint256 priceInWei = getPriceInWei(); 90 | totalWeiRaised = totalWeiRaised.add(weiAmount); 91 | 92 | uint256 tokens = weiAmount.mul(decimalsMultiplier).div(priceInWei); 93 | tokensMinted = tokensMinted.add(tokens); 94 | require(tokensMinted < tokenCap); 95 | 96 | contributors = contributors.add(1); 97 | 98 | proofToken.mint(_beneficiary, tokens); 99 | TokenPurchase(msg.sender, _beneficiary, weiAmount, tokens); 100 | forwardFunds(); 101 | } 102 | 103 | 104 | /** 105 | * Get the price in wei for current premium 106 | * @return price {uint256} 107 | */ 108 | function getPriceInWei() constant public returns (uint256) { 109 | 110 | uint256 price; 111 | 112 | if (totalWeiRaised < firstDiscountCap) { 113 | price = firstDiscountPrice; 114 | } else if (totalWeiRaised < secondDiscountCap) { 115 | price = secondDiscountPrice; 116 | } else if (totalWeiRaised < thirdDiscountCap) { 117 | price = thirdDiscountPrice; 118 | } else { 119 | price = BASE_PRICE_IN_WEI; 120 | } 121 | 122 | return price; 123 | } 124 | 125 | /** 126 | * Forwards funds to the tokensale wallet 127 | */ 128 | function forwardFunds() internal { 129 | proofMultiSig.transfer(msg.value); 130 | } 131 | 132 | 133 | /** 134 | * Validates the purchase (period, minimum amount, within cap) 135 | * @return {bool} valid 136 | */ 137 | function validPurchase() internal constant returns (bool) { 138 | uint256 current = now; 139 | bool presaleStarted = (current >= startTime || started); 140 | bool presaleNotEnded = current <= endTime; 141 | bool nonZeroPurchase = msg.value != 0; 142 | return nonZeroPurchase && presaleStarted && presaleNotEnded; 143 | } 144 | 145 | /** 146 | * Returns the total Proof token supply 147 | * @return totalSupply {uint256} Proof Token Total Supply 148 | */ 149 | function totalSupply() public constant returns (uint256) { 150 | return proofToken.totalSupply(); 151 | } 152 | 153 | /** 154 | * Returns token holder Proof Token balance 155 | * @param _owner {address} Token holder address 156 | * @return balance {uint256} Corresponding token holder balance 157 | */ 158 | function balanceOf(address _owner) public constant returns (uint256) { 159 | return proofToken.balanceOf(_owner); 160 | } 161 | 162 | /** 163 | * Change the Proof Token controller 164 | * @param _newController {address} New Proof Token controller 165 | */ 166 | function changeController(address _newController) public { 167 | require(isContract(_newController)); 168 | proofToken.transferControl(_newController); 169 | } 170 | 171 | 172 | function enableTransfers() public { 173 | if (now < endTime) { 174 | require(msg.sender == owner); 175 | } 176 | proofToken.enableTransfers(true); 177 | } 178 | 179 | function lockTransfers() public onlyOwner { 180 | require(now < endTime); 181 | proofToken.enableTransfers(false); 182 | } 183 | 184 | function enableMasterTransfers() public onlyOwner { 185 | proofToken.enableMasterTransfers(true); 186 | } 187 | 188 | function lockMasterTransfers() public onlyOwner { 189 | proofToken.enableMasterTransfers(false); 190 | } 191 | 192 | function forceStart() public onlyOwner { 193 | started = true; 194 | } 195 | 196 | function allocateProofTokens() public onlyOwner whenNotFinalized { 197 | require(!proofTokensAllocated); 198 | proofToken.mint(proofMultiSig, TOKENS_ALLOCATED_TO_PROOF); 199 | proofTokensAllocated = true; 200 | } 201 | 202 | function finalize() public onlyOwner { 203 | require(paused); 204 | require(proofTokensAllocated); 205 | 206 | proofToken.finishMinting(); 207 | proofToken.enableTransfers(true); 208 | Finalized(); 209 | 210 | finalized = true; 211 | } 212 | 213 | 214 | function isContract(address _addr) constant internal returns(bool) { 215 | uint size; 216 | if (_addr == 0) 217 | return false; 218 | assembly { 219 | size := extcodesize(_addr) 220 | } 221 | return size>0; 222 | } 223 | 224 | modifier whenNotFinalized() { 225 | require(!finalized); 226 | _; 227 | } 228 | 229 | } -------------------------------------------------------------------------------- /coverage.json: -------------------------------------------------------------------------------- 1 | {"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/ERC20.sol":{"l":{},"path":"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/ERC20.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/Ownable.sol":{"l":{"18":2,"26":0,"27":0,"36":0,"37":0},"path":"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/Ownable.sol","s":{"1":2,"2":0,"3":0,"4":0},"b":{"1":[0,0],"2":[0,0]},"f":{"1":2,"2":0,"3":0},"fnMap":{"1":{"name":"Ownable","line":17,"loc":{"start":{"line":17,"column":2},"end":{"line":17,"column":20}}},"2":{"name":"onlyOwner","line":25,"loc":{"start":{"line":25,"column":2},"end":{"line":25,"column":22}}},"3":{"name":"transferOwnership","line":35,"loc":{"start":{"line":35,"column":2},"end":{"line":35,"column":56}}}},"statementMap":{"1":{"start":{"line":18,"column":4},"end":{"line":18,"column":21}},"2":{"start":{"line":26,"column":4},"end":{"line":26,"column":31}},"3":{"start":{"line":36,"column":4},"end":{"line":36,"column":804}},"4":{"start":{"line":37,"column":6},"end":{"line":37,"column":21}}},"branchMap":{"1":{"line":26,"type":"if","locations":[{"start":{"line":26,"column":4},"end":{"line":26,"column":4}},{"start":{"line":26,"column":4},"end":{"line":26,"column":4}}]},"2":{"line":36,"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":4}},{"start":{"line":36,"column":4},"end":{"line":36,"column":4}}]}}},"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/Pausable.sol":{"l":{"23":0,"24":0,"31":0,"32":0,"39":0,"40":0,"41":0,"48":0,"49":0,"50":0},"path":"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/Pausable.sol","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"1":[0,0],"2":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0,"5":0},"fnMap":{"1":{"name":"Pausable","line":17,"loc":{"start":{"line":17,"column":2},"end":{"line":17,"column":21}}},"2":{"name":"whenNotPaused","line":22,"loc":{"start":{"line":22,"column":2},"end":{"line":22,"column":26}}},"3":{"name":"whenPaused","line":30,"loc":{"start":{"line":30,"column":2},"end":{"line":30,"column":21}}},"4":{"name":"pause","line":38,"loc":{"start":{"line":38,"column":2},"end":{"line":38,"column":57}}},"5":{"name":"unpause","line":47,"loc":{"start":{"line":47,"column":2},"end":{"line":47,"column":56}}}},"statementMap":{"1":{"start":{"line":23,"column":4},"end":{"line":23,"column":19}},"2":{"start":{"line":31,"column":4},"end":{"line":31,"column":18}},"3":{"start":{"line":39,"column":4},"end":{"line":39,"column":16}},"4":{"start":{"line":40,"column":4},"end":{"line":40,"column":10}},"5":{"start":{"line":41,"column":4},"end":{"line":41,"column":15}},"6":{"start":{"line":48,"column":4},"end":{"line":48,"column":17}},"7":{"start":{"line":49,"column":4},"end":{"line":49,"column":12}},"8":{"start":{"line":50,"column":4},"end":{"line":50,"column":15}}},"branchMap":{"1":{"line":23,"type":"if","locations":[{"start":{"line":23,"column":4},"end":{"line":23,"column":4}},{"start":{"line":23,"column":4},"end":{"line":23,"column":4}}]},"2":{"line":31,"type":"if","locations":[{"start":{"line":31,"column":4},"end":{"line":31,"column":4}},{"start":{"line":31,"column":4},"end":{"line":31,"column":4}}]}}},"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/ProofPresaleToken.sol":{"l":{"33":0,"37":0,"42":0,"43":0,"45":0,"46":0,"50":0,"52":0,"53":0,"54":0,"56":0,"57":0,"61":0,"62":0,"63":0,"67":0,"72":0,"73":0,"83":0,"84":0,"85":0,"86":0,"94":0,"95":0,"96":0},"path":"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/ProofPresaleToken.sol","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0},"b":{"1":[0,0]},"f":{"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"fnMap":{"1":{"name":"ProofPresaleToken","line":30,"loc":{"start":{"line":30,"column":2},"end":{"line":30,"column":30}}},"2":{"name":null,"line":32,"loc":{"start":{"line":32,"column":2},"end":{"line":32,"column":20}}},"3":{"name":"balanceOf","line":36,"loc":{"start":{"line":36,"column":2},"end":{"line":36,"column":63}}},"4":{"name":"transfer","line":40,"loc":{"start":{"line":40,"column":2},"end":{"line":40,"column":60}}},"5":{"name":"transferFrom","line":49,"loc":{"start":{"line":49,"column":2},"end":{"line":49,"column":79}}},"6":{"name":"approve","line":60,"loc":{"start":{"line":60,"column":2},"end":{"line":60,"column":64}}},"7":{"name":"allowance","line":66,"loc":{"start":{"line":66,"column":2},"end":{"line":66,"column":81}}},"8":{"name":"canMint","line":71,"loc":{"start":{"line":71,"column":2},"end":{"line":71,"column":20}}},"9":{"name":"mint","line":82,"loc":{"start":{"line":82,"column":2},"end":{"line":82,"column":78}}},"10":{"name":"finishMinting","line":93,"loc":{"start":{"line":93,"column":2},"end":{"line":93,"column":51}}},"11":{"name":"calculateShare","line":100,"loc":{"start":{"line":100,"column":2},"end":{"line":100,"column":45}}}},"statementMap":{"1":{"start":{"line":33,"column":4},"end":{"line":33,"column":11}},"2":{"start":{"line":37,"column":4},"end":{"line":37,"column":27}},"3":{"start":{"line":42,"column":4},"end":{"line":42,"column":58}},"4":{"start":{"line":43,"column":4},"end":{"line":43,"column":44}},"5":{"start":{"line":45,"column":4},"end":{"line":45,"column":36}},"6":{"start":{"line":46,"column":4},"end":{"line":46,"column":15}},"7":{"start":{"line":50,"column":4},"end":{"line":50,"column":47}},"8":{"start":{"line":52,"column":4},"end":{"line":52,"column":44}},"9":{"start":{"line":53,"column":4},"end":{"line":53,"column":48}},"10":{"start":{"line":54,"column":4},"end":{"line":54,"column":54}},"11":{"start":{"line":56,"column":4},"end":{"line":56,"column":31}},"12":{"start":{"line":57,"column":4},"end":{"line":57,"column":15}},"13":{"start":{"line":61,"column":4},"end":{"line":61,"column":41}},"14":{"start":{"line":62,"column":4},"end":{"line":62,"column":41}},"15":{"start":{"line":63,"column":4},"end":{"line":63,"column":15}},"16":{"start":{"line":67,"column":4},"end":{"line":67,"column":36}},"17":{"start":{"line":72,"column":4},"end":{"line":72,"column":28}},"18":{"start":{"line":83,"column":4},"end":{"line":83,"column":41}},"19":{"start":{"line":84,"column":4},"end":{"line":84,"column":45}},"20":{"start":{"line":85,"column":4},"end":{"line":85,"column":21}},"21":{"start":{"line":86,"column":4},"end":{"line":86,"column":15}},"22":{"start":{"line":94,"column":4},"end":{"line":94,"column":25}},"23":{"start":{"line":95,"column":4},"end":{"line":95,"column":17}},"24":{"start":{"line":96,"column":4},"end":{"line":96,"column":15}}},"branchMap":{"1":{"line":72,"type":"if","locations":[{"start":{"line":72,"column":4},"end":{"line":72,"column":4}},{"start":{"line":72,"column":4},"end":{"line":72,"column":4}}]}}},"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/ProofToken.sol":{"l":{"35":1,"39":0,"43":0,"48":0,"49":0,"51":0,"52":0,"56":0,"58":0,"59":0,"60":0,"62":0,"63":0,"67":0,"69":0,"70":0,"74":0,"78":0,"79":0,"81":0,"82":0,"84":0,"89":0,"90":0,"100":0,"101":0,"103":0,"104":0,"112":0,"114":0,"115":0},"path":"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/ProofToken.sol","s":{"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0]},"f":{"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"fnMap":{"1":{"name":"ProofToken","line":34,"loc":{"start":{"line":34,"column":2},"end":{"line":34,"column":51}}},"2":{"name":null,"line":38,"loc":{"start":{"line":38,"column":2},"end":{"line":38,"column":20}}},"3":{"name":"balanceOf","line":42,"loc":{"start":{"line":42,"column":2},"end":{"line":42,"column":63}}},"4":{"name":"transfer","line":46,"loc":{"start":{"line":46,"column":2},"end":{"line":46,"column":60}}},"5":{"name":"transferFrom","line":55,"loc":{"start":{"line":55,"column":2},"end":{"line":55,"column":79}}},"6":{"name":"approve","line":66,"loc":{"start":{"line":66,"column":2},"end":{"line":66,"column":64}}},"7":{"name":"allowance","line":73,"loc":{"start":{"line":73,"column":2},"end":{"line":73,"column":81}}},"8":{"name":"claim","line":77,"loc":{"start":{"line":77,"column":2},"end":{"line":77,"column":33}}},"9":{"name":"canMint","line":88,"loc":{"start":{"line":88,"column":2},"end":{"line":88,"column":20}}},"10":{"name":"mint","line":99,"loc":{"start":{"line":99,"column":2},"end":{"line":99,"column":78}}},"11":{"name":"finishMinting","line":111,"loc":{"start":{"line":111,"column":2},"end":{"line":111,"column":51}}}},"statementMap":{"1":{"start":{"line":35,"column":4},"end":{"line":35,"column":57}},"2":{"start":{"line":39,"column":4},"end":{"line":39,"column":11}},"3":{"start":{"line":43,"column":4},"end":{"line":43,"column":27}},"4":{"start":{"line":48,"column":4},"end":{"line":48,"column":58}},"5":{"start":{"line":49,"column":4},"end":{"line":49,"column":44}},"6":{"start":{"line":51,"column":4},"end":{"line":51,"column":36}},"7":{"start":{"line":52,"column":4},"end":{"line":52,"column":15}},"8":{"start":{"line":56,"column":4},"end":{"line":56,"column":47}},"9":{"start":{"line":58,"column":4},"end":{"line":58,"column":44}},"10":{"start":{"line":59,"column":4},"end":{"line":59,"column":48}},"11":{"start":{"line":60,"column":4},"end":{"line":60,"column":54}},"12":{"start":{"line":62,"column":4},"end":{"line":62,"column":31}},"13":{"start":{"line":63,"column":4},"end":{"line":63,"column":15}},"14":{"start":{"line":67,"column":4},"end":{"line":67,"column":41}},"15":{"start":{"line":69,"column":4},"end":{"line":69,"column":41}},"16":{"start":{"line":70,"column":4},"end":{"line":70,"column":15}},"17":{"start":{"line":74,"column":4},"end":{"line":74,"column":36}},"18":{"start":{"line":78,"column":4},"end":{"line":78,"column":50}},"19":{"start":{"line":79,"column":4},"end":{"line":79,"column":40}},"20":{"start":{"line":81,"column":4},"end":{"line":81,"column":29}},"21":{"start":{"line":82,"column":4},"end":{"line":82,"column":60}},"22":{"start":{"line":84,"column":4},"end":{"line":84,"column":15}},"23":{"start":{"line":89,"column":4},"end":{"line":89,"column":28}},"24":{"start":{"line":100,"column":4},"end":{"line":100,"column":41}},"25":{"start":{"line":101,"column":4},"end":{"line":101,"column":45}},"26":{"start":{"line":103,"column":4},"end":{"line":103,"column":21}},"27":{"start":{"line":104,"column":4},"end":{"line":104,"column":15}},"28":{"start":{"line":112,"column":4},"end":{"line":112,"column":25}},"29":{"start":{"line":114,"column":4},"end":{"line":114,"column":17}},"30":{"start":{"line":115,"column":4},"end":{"line":115,"column":15}}},"branchMap":{"1":{"line":78,"type":"if","locations":[{"start":{"line":78,"column":4},"end":{"line":78,"column":4}},{"start":{"line":78,"column":4},"end":{"line":78,"column":4}}]},"2":{"line":79,"type":"if","locations":[{"start":{"line":79,"column":4},"end":{"line":79,"column":4}},{"start":{"line":79,"column":4},"end":{"line":79,"column":4}}]},"3":{"line":89,"type":"if","locations":[{"start":{"line":89,"column":4},"end":{"line":89,"column":4}},{"start":{"line":89,"column":4},"end":{"line":89,"column":4}}]}}},"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/SafeMath.sol":{"l":{"10":0,"11":0,"12":0,"17":0,"19":0,"23":0,"24":0,"28":0,"29":0,"30":0},"path":"/Users/davidvanisacker/Programming/Ethereum/Proofsuite/ProofTokenSale/contracts/SafeMath.sol","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0},"fnMap":{"1":{"name":"mul","line":9,"loc":{"start":{"line":9,"column":2},"end":{"line":9,"column":72}}},"2":{"name":"div","line":15,"loc":{"start":{"line":15,"column":2},"end":{"line":15,"column":72}}},"3":{"name":"sub","line":22,"loc":{"start":{"line":22,"column":2},"end":{"line":22,"column":72}}},"4":{"name":"add","line":27,"loc":{"start":{"line":27,"column":2},"end":{"line":27,"column":72}}}},"statementMap":{"1":{"start":{"line":10,"column":4},"end":{"line":10,"column":20}},"2":{"start":{"line":11,"column":4},"end":{"line":11,"column":31}},"3":{"start":{"line":12,"column":4},"end":{"line":12,"column":12}},"4":{"start":{"line":17,"column":4},"end":{"line":17,"column":20}},"5":{"start":{"line":19,"column":4},"end":{"line":19,"column":12}},"6":{"start":{"line":23,"column":4},"end":{"line":23,"column":17}},"7":{"start":{"line":24,"column":4},"end":{"line":24,"column":16}},"8":{"start":{"line":28,"column":4},"end":{"line":28,"column":20}},"9":{"start":{"line":29,"column":4},"end":{"line":29,"column":17}},"10":{"start":{"line":30,"column":4},"end":{"line":30,"column":12}}},"branchMap":{"1":{"line":11,"type":"if","locations":[{"start":{"line":11,"column":4},"end":{"line":11,"column":4}},{"start":{"line":11,"column":4},"end":{"line":11,"column":4}}]},"2":{"line":23,"type":"if","locations":[{"start":{"line":23,"column":4},"end":{"line":23,"column":4}},{"start":{"line":23,"column":4},"end":{"line":23,"column":4}}]},"3":{"line":29,"type":"if","locations":[{"start":{"line":29,"column":4},"end":{"line":29,"column":4}},{"start":{"line":29,"column":4},"end":{"line":29,"column":4}}]}}}} -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["node_modules/jsdoc-babel"], 3 | "babel": { 4 | "presets": [ "es2015" ], 5 | "plugins": [ "transform-async-to-generator" ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const config = require('../config.js') 2 | 3 | var Migrations = artifacts.require("./Migrations.sol"); 4 | 5 | let wallet, gas, gasPrice; 6 | module.exports = function(deployer) { 7 | 8 | if (deployer.network == "ethereum") { 9 | wallet = config.addresses.ethereum.WALLET_ADDRESS 10 | gas = config.constants.MAX_GAS 11 | gasPrice = config.constants.DEFAULT_HIGH_GAS_PRICE 12 | } else if (deployer.network == "ropsten") { 13 | wallet = config.addresses.ropsten.WALLET_ADDRESS 14 | gas = config.constants.DEFAULT_GAS 15 | gasPrice = config.constants.DEFAULT_HIGH_GAS_PRICE 16 | } else if (deployer.network == "rinkeby") { 17 | wallet = config.addresses.rinkeby.WALLET_ADDRESS 18 | gas = config.constants.MAX_GAS 19 | gasPrice = config.constants.DEFAULT_GAS_PRICE 20 | } else if (deployer.network == "development") { 21 | wallet = config.addresses.development.WALLET_ADDRESS 22 | gas = config.constants.DEFAULT_GAS 23 | gasPrice = config.constants.DEFAULT_GAS_PRICE 24 | } else { 25 | throw new Error("Wallet not set") 26 | } 27 | 28 | 29 | deployer.deploy(Migrations, {gas: gas, gasPrice: gasPrice }); 30 | }; 31 | -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const config = require('../config.js') 2 | 3 | const ProofToken = artifacts.require('./ProofToken.sol'); 4 | const TokenSale = artifacts.require('./TokenSale.sol'); 5 | const TokenFactory = artifacts.require('./TokenFactory.sol') 6 | 7 | let wallet, gas, gasPrice; 8 | 9 | 10 | module.exports = function(deployer) { 11 | 12 | if (deployer.network == "ethereum") { 13 | wallet = config.addresses.ethereum.WALLET_ADDRESS 14 | gas = config.constants.MAX_GAS 15 | gasPrice = config.constants.DEFAULT_HIGH_GAS_PRICE 16 | } else if (deployer.network == "ropsten") { 17 | wallet = config.addresses.ropsten.WALLET_ADDRESS 18 | gas = config.constants.DEFAULT_GAS 19 | gasPrice = config.constants.DEFAULT_HIGH_GAS_PRICE 20 | } else if (deployer.network == "rinkeby") { 21 | wallet = config.addresses.rinkeby.WALLET_ADDRESS 22 | gas = config.constants.MAX_GAS 23 | gasPrice = config.constants.DEFAULT_GAS_PRICE 24 | } else if (deployer.network == "development") { 25 | wallet = config.addresses.development.WALLET_ADDRESS 26 | gas = config.constants.MAX_GAS 27 | gasPrice = config.constants.DEFAULT_GAS_PRICE 28 | } else { 29 | throw new Error("Wallet not set") 30 | } 31 | 32 | deployer.deploy( 33 | TokenFactory, 34 | {gas: gas, gasPrice: gasPrice } 35 | ) 36 | .then(function() { 37 | return deployer.deploy( 38 | ProofToken, 39 | TokenFactory.address, 40 | "0x0000000000000000000000000000000000000000", 41 | 0, 42 | "Proof", 43 | "PRFT", 44 | {gas: gas, gasPrice: gasPrice } 45 | ) 46 | }) 47 | .then(function() { 48 | return deployer.deploy( 49 | TokenSale, 50 | ProofToken.address, 51 | 1509541200, // November 1st 1PM GMT: 1509541200 52 | 1512133200, // December 1st 1PM GMT: 1512133200 53 | {gas: gas, gasPrice: gasPrice } 54 | ) 55 | }) 56 | } 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /other_test/crowdsale.js: -------------------------------------------------------------------------------- 1 | const BigNumber = web3.BigNumber 2 | let chai = require('chai') 3 | var chaiAsPromised = require('chai-as-promised') 4 | var chaiStats = require('chai-stats') 5 | var chaiBigNumber = require('chai-bignumber')(BigNumber) 6 | chai.use(chaiAsPromised).use(chaiBigNumber).use(chaiStats).should() 7 | 8 | import { DEFAULT_GAS, 9 | DEFAULT_GAS_PRICE, 10 | ether } from '../scripts/testConfig.js' 11 | 12 | import { getAddress, 13 | sendTransaction, 14 | expectInvalidOpcode, 15 | getBalance, 16 | advanceToBlock } from '../scripts/helpers.js' 17 | 18 | import { getTotalSupply, 19 | getTokenBalance, 20 | baseUnits, 21 | mintToken } from '../scripts/tokenHelpers.js' 22 | 23 | import { buyTokens, 24 | numberOfTokensFor, 25 | getWallet, 26 | getPriceInWei, 27 | getCap } from '../scripts/tokenSaleHelpers.js' 28 | 29 | import { 30 | pause, 31 | unpause } from '../scripts/pausableHelpers.js' 32 | 33 | import { transferOwnership } from '../scripts/ownershipHelpers.js' 34 | 35 | const assert = chai.assert 36 | const should = chai.should() 37 | const expect = chai.expect 38 | 39 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 40 | const ProofToken = artifacts.require('./ProofToken.sol') 41 | const TokenSale = artifacts.require('./TokenSale.sol') 42 | 43 | contract('Crowdsale', (accounts) => { 44 | console.log(web3.eth.getBalance(accounts[0]) / 10 ** 18) 45 | let fund = accounts[0] 46 | let tokenSale 47 | let tokenSaleAddress 48 | let proofToken 49 | let proofPresaleToken 50 | let proofPresaleTokenAddress 51 | let proofTokenAddress 52 | let sender = accounts[1] 53 | let receiver = accounts[2] 54 | let hacker1 = accounts[3] 55 | let hacker2 = accounts[4] 56 | let wallet = accounts[5] 57 | 58 | let startBlock 59 | let endBlock 60 | 61 | beforeEach(async function() { 62 | startBlock = web3.eth.blockNumber + 10 63 | endBlock = web3.eth.blockNumber + 20 64 | 65 | proofPresaleToken = await ProofPresaleToken.new() 66 | proofPresaleTokenAddress = await getAddress(proofPresaleToken) 67 | 68 | proofToken = await ProofToken.new(proofPresaleTokenAddress) 69 | proofTokenAddress = await getAddress(proofToken) 70 | 71 | tokenSale = await TokenSale.new( 72 | wallet, 73 | proofPresaleTokenAddress, 74 | proofTokenAddress, 75 | startBlock, 76 | endBlock, 77 | proofWalletAddress) 78 | 79 | tokenSaleAddress = await getAddress(tokenSale) 80 | }) 81 | 82 | it('should be ended only after end', async function() { 83 | let ended = await tokenSale.hasEnded() 84 | ended.should.equal(false) 85 | await advanceToBlock() 86 | }) 87 | 88 | describe('Initial State', function () { 89 | beforeEach(async function() { 90 | transferOwnership(proofToken, fund, tokenSaleAddress) 91 | }) 92 | 93 | it('should initially set the wallet', async function() { 94 | let tokenSaleWallet = await tokenSale.wallet.call() 95 | tokenSaleWallet.should.be.equal(wallet) 96 | }) 97 | 98 | it('should initially be linked to the Proof token', async function() { 99 | let tokenSaleToken = await tokenSale.proofToken.call() 100 | tokenSaleToken.should.be.equal(proofTokenAddress) 101 | }) 102 | 103 | it('should initially be linked to the Proof Presale token', async function() { 104 | let tokenSalePresaleToken = await tokenSale.proofPresaleToken.call() 105 | tokenSalePresaleToken.should.be.equal(proofPresaleTokenAddress) 106 | }) 107 | 108 | it('Price should be equal to 0.088 ether', async function() { 109 | let priceInWei = await tokenSale.priceInWei.call() 110 | priceInWei.should.be.bignumber.equal(0.088 * 10 ** 18) 111 | }) 112 | }) 113 | 114 | describe('Ownership', function () { 115 | it('should initially belong to contract caller', async function() { 116 | let owner = await tokenSale.owner.call() 117 | assert.equal(owner, fund) 118 | }) 119 | 120 | it('should be transferable to another account', async function() { 121 | let owner = await tokenSale.owner.call() 122 | await transferOwnership(tokenSale, owner, receiver) 123 | let newOwner = await tokenSale.owner.call() 124 | assert.equal(newOwner, receiver) 125 | }) 126 | 127 | it('should not be transferable by non-owner', async function() { 128 | let owner = await tokenSale.owner.call() 129 | await expectInvalidOpcode(transferOwnership(tokenSale, hacker1, hacker2)) 130 | const newOwner = await tokenSale.owner.call() 131 | assert.equal(owner, newOwner) 132 | }) 133 | }) 134 | 135 | describe('End and Beginning of Presale', async function() { 136 | beforeEach(async function() { 137 | await transferOwnership(proofToken, fund, tokenSaleAddress) 138 | }) 139 | 140 | it('should reject payments before start', async function() { 141 | await expectInvalidOpcode(tokenSale.buyTokens(sender, { value: 1 * ether, from: sender })) 142 | await expectInvalidOpcode(tokenSale.send(1 * ether, { from: sender })) 143 | }) 144 | 145 | it('should accepts payments after start', async function() { 146 | await advanceToBlock(startBlock) 147 | await tokenSale.send(1 * ether, { from: sender }).should.be.fulfilled 148 | await tokenSale.buyTokens(sender, { value: 1 * ether, from: sender }).should.be.fulfilled 149 | }) 150 | 151 | it('should reject payments after end', async function() { 152 | await advanceToBlock(endBlock) 153 | await expectInvalidOpcode(tokenSale.send(1 * ether, { from: sender })) 154 | await expectInvalidOpcode(tokenSale.buyTokens(sender, { value: 1 * ether, from: sender })) 155 | }) 156 | }) 157 | 158 | describe('Crowdsale', async function() { 159 | beforeEach(async function() { 160 | await transferOwnership(proofToken, fund, tokenSaleAddress) 161 | await advanceToBlock(startBlock) 162 | }) 163 | 164 | it('should accepts ether transactions sent to contract', async function() { 165 | let order = { from: sender, 166 | to: tokenSaleAddress, 167 | value: 1 * ether, 168 | gas: DEFAULT_GAS, 169 | gasPrice: DEFAULT_GAS_PRICE 170 | } 171 | 172 | await sendTransaction(order).should.be.fulfilled 173 | }) 174 | 175 | it('should accept ether through buyTokens function', async function() { 176 | let order = { 177 | from: sender, 178 | value: 1 * ether, 179 | gas: DEFAULT_GAS, 180 | gasPrice: DEFAULT_GAS_PRICE 181 | } 182 | 183 | await tokenSale.buyTokens(sender, order).should.be.fulfilled 184 | }) 185 | 186 | it('should increase total token supply', async function() { 187 | let initialTotalSupply = await getTotalSupply(proofToken) 188 | 189 | await buyTokens(tokenSale, sender, 1 * ether) 190 | 191 | let expectedSupplyIncrease = await numberOfTokensFor(tokenSale, 1 * ether) 192 | expectedSupplyIncrease = await baseUnits(proofToken, expectedSupplyIncrease) 193 | 194 | let totalSupply = await getTotalSupply(proofToken) 195 | 196 | let supplyIncrease = (totalSupply - initialTotalSupply) 197 | supplyIncrease = await baseUnits(proofToken, supplyIncrease) 198 | 199 | expect(supplyIncrease).to.almost.equal(expectedSupplyIncrease) 200 | }) 201 | 202 | it('should increase total supply by 11.36 for 10 ether raised', async function() { 203 | let initialTotalSupply = await getTotalSupply(proofToken) 204 | 205 | await buyTokens(tokenSale, sender, 1 * ether) 206 | 207 | let totalSupply = await getTotalSupply(proofToken) 208 | let supplyIncrease = (totalSupply - initialTotalSupply) 209 | supplyIncrease = await baseUnits(proofToken, supplyIncrease) 210 | 211 | expect(supplyIncrease).almost.equal(11.363636363636) 212 | }) 213 | 214 | it('should transfer money to the wallet after receiving investment', async function() { 215 | let wallet = await getWallet(tokenSale) 216 | let initialWalletBalance = getBalance(wallet) 217 | await buyTokens(tokenSale, sender, 1 * ether) 218 | 219 | let walletBalance = getBalance(wallet) 220 | let balanceIncrease = (walletBalance - initialWalletBalance) / (1 * ether) 221 | expect(balanceIncrease).almost.equal(1, 3) 222 | }) 223 | 224 | it('should create tokens for the sender', async function() { 225 | let initialTokenBalance = await getTokenBalance(proofToken, sender) 226 | 227 | await buyTokens(tokenSale, sender, 1 * ether) 228 | 229 | let tokenBalance = await getTokenBalance(proofToken, sender) 230 | let balanceIncrease = await baseUnits(proofToken, tokenBalance - initialTokenBalance) 231 | 232 | let expectedBalanceIncrease = await numberOfTokensFor(tokenSale, 1 * ether) 233 | expectedBalanceIncrease = await baseUnits(proofToken, expectedBalanceIncrease) 234 | 235 | expect(balanceIncrease).to.almost.equal(expectedBalanceIncrease) 236 | }) 237 | 238 | it('should increase buyer balance by X for 10 ether invested', async function() { 239 | let initialTokenBalance = await getTokenBalance(proofToken, sender) 240 | 241 | await buyTokens(tokenSale, sender, 1 * ether) 242 | 243 | let tokenBalance = await getTokenBalance(proofToken, sender) 244 | let balanceIncrease = (tokenBalance - initialTokenBalance) 245 | balanceIncrease = await baseUnits(proofToken, balanceIncrease) 246 | expect(balanceIncrease).almost.equal(11.363636363636) 247 | }) 248 | }) 249 | 250 | describe('Buying Tokens', async function() { 251 | beforeEach(async function() { 252 | await transferOwnership(proofToken, fund, tokenSaleAddress) 253 | await advanceToBlock(startBlock) 254 | }) 255 | 256 | it('should not be allowed if the contract is paused', async function() { 257 | await pause(tokenSale, fund) 258 | let initialBalance = await getTokenBalance(proofToken, sender) 259 | 260 | let params = { from: sender, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 261 | await expectInvalidOpcode(tokenSale.buyTokens(1, params)) 262 | 263 | let balance = await getTokenBalance(proofToken, sender) 264 | balance.should.be.equal(initialBalance) 265 | }) 266 | 267 | it('should be allowed if the contract is paused and unpaused', async function() { 268 | let initialBalance = await getTokenBalance(proofToken, sender) 269 | 270 | await buyTokens(tokenSale, sender, 1 * ether) 271 | await pause(tokenSale, fund) 272 | await unpause(tokenSale, fund) 273 | await buyTokens(tokenSale, sender, 1 * ether) 274 | 275 | let balance = await getTokenBalance(proofToken, sender) 276 | let balanceIncrease = await baseUnits(proofToken, balance - initialBalance) 277 | expect(balanceIncrease).to.almost.equal(22.7272727272) 278 | }) 279 | 280 | it('should not throw if purchase hits just below the cap', async function() { 281 | let cap = await getCap(tokenSale) 282 | let priceInWei = await getPriceInWei(tokenSale) 283 | let initialBalance = await getTokenBalance(proofToken, sender) 284 | 285 | let amount = cap * priceInWei - 1 286 | let expectedTokens = await numberOfTokensFor(tokenSale, amount) 287 | 288 | await buyTokens(tokenSale, sender, amount) 289 | 290 | let balance = await getTokenBalance(proofToken, sender) 291 | let balanceIncrease = balance - initialBalance 292 | 293 | expect(balanceIncrease).almost.equal(expectedTokens, 3) 294 | }) 295 | 296 | it('should throw if the number of tokens exceeds the cap', async function() { 297 | let cap = await getCap(tokenSale) 298 | let priceInWei = await getPriceInWei(tokenSale) 299 | let initialBalance = await getTokenBalance(proofToken, sender) 300 | 301 | let amount = cap * priceInWei * (1.001) 302 | let params = { value: amount, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 303 | await expectInvalidOpcode(tokenSale.buyTokens(sender, params)) 304 | 305 | let balance = await getTokenBalance(proofToken, sender) 306 | balance.should.be.equal(initialBalance) 307 | }) 308 | }) 309 | }) 310 | 311 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "truffle-init-webpack", 3 | "version": "0.0.2", 4 | "description": "Frontend example using truffle v3", 5 | "scripts": { 6 | "lint": "eslint ./", 7 | "build": "webpack", 8 | "dev": "webpack-dev-server", 9 | "coverage": "./node_modules/.bin/solidity-coverage", 10 | "test": "truffle test", 11 | "watch-test": "watch-run -p 'contracts/*.sol, test/*.js, scripts/*.js' truffle test" 12 | }, 13 | "author": "Douglas von Kohorn", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "babel-cli": "^6.22.2", 17 | "babel-core": "^6.22.1", 18 | "babel-eslint": "^6.1.2", 19 | "babel-loader": "^6.2.10", 20 | "babel-plugin-transform-async-to-generator": "~6.24.1", 21 | "babel-plugin-transform-runtime": "^6.22.0", 22 | "babel-preset-env": "^1.1.8", 23 | "babel-preset-es2015": "~6.24.1", 24 | "babel-register": "^6.22.0", 25 | "copy-webpack-plugin": "^4.0.1", 26 | "coveralls": "~2.13.1", 27 | "css-loader": "^0.26.1", 28 | "eslint": "^3.14.0", 29 | "eslint-config-standard": "^6.0.0", 30 | "eslint-plugin-babel": "^4.0.0", 31 | "eslint-plugin-mocha": "^4.8.0", 32 | "eslint-plugin-promise": "^3.0.0", 33 | "eslint-plugin-standard": "^2.0.0", 34 | "html-webpack-plugin": "^2.28.0", 35 | "istanbul": "~0.4.5", 36 | "jsdoc-babel": "~0.3.0", 37 | "json-loader": "^0.5.4", 38 | "solidity-coverage": "~0.2.2", 39 | "style-loader": "^0.13.1", 40 | "truffle-contract": "^1.1.11", 41 | "web3": "^0.18.2", 42 | "webpack": "^2.2.1", 43 | "webpack-dev-server": "^2.3.0" 44 | }, 45 | "dependencies": { 46 | "@digix/tempo": "~0.2.0", 47 | "@digix/truffle-lightwallet-provider": "~0.1.2", 48 | "async-request": "~1.2.0", 49 | "await-each": "~1.1.0", 50 | "bluebird": "~3.5.0", 51 | "chai": "~4.1.2", 52 | "chai-as-promised": "~7.1.1", 53 | "chai-bignumber": "~2.0.1", 54 | "chai-stats": "~0.3.0", 55 | "csv-parser": "~1.11.0", 56 | "csvdata": "~1.4.0", 57 | "ethereum-address": "0.0.4", 58 | "ethereumjs-testrpc": "~4.1.3", 59 | "json2csv": "~3.11.1", 60 | "moment": "~2.19.1", 61 | "solc": "~0.4.17" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /scripts/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "Chrome", 4 | "osx": { 5 | "command": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" 6 | }, 7 | "args": [ 8 | "${file}" 9 | ] 10 | } -------------------------------------------------------------------------------- /scripts/addresses.csv: -------------------------------------------------------------------------------- 1 | Addresses, 2 | 0xcaf15d9cd7cfd573964e3dc88fc6ffea25f89d8f, 3 | 0xf190f0193b694d9d2bb865a66f0c17cbd8280c71, 4 | 0xecd7da67e6563bbddfc2ddff9ba2632c721af181, 5 | 0x31d04a32f22022ec66afe6c2351db768ed32b873, 6 | 0xc7c78f87e2e8d1d55104c8f6fae07966cfc577fd, 7 | 0x72aed3d59cc889b8ab5d943c703f45c4aeaebf86, 8 | 0x6847dedbb505b0b075b98db8f34f1d62f85e0d9d, 9 | 0xac1d10865db58adba0826d94734129ec8a5741d7, 10 | 0xfe8ef260e63a3388bb3f3174cf080f4423414100, 11 | 0x622b2c840d856181661482a2f7d9a5a41e0f992a, 12 | 0xdfa352daf510611ecc1a4ea0d02d6cae8cc3e2e0, 13 | 0x2746dc307f68a8346e9ab34aec63ab358e6dab82, 14 | 0x9c6d12aaa2fb615467c36b4db6b7e3402fb4f45f, 15 | 0x904367561bae5b96b4332c4c5ec338be097b3240, 16 | 0x16ae23df74c294c4fa4c81b278e7c94c25df8c75, 17 | 0x39fc4cdaa0e79efe7687822f1c0bacc87e1deede, 18 | 0x65c6f8dce120363cad39c85e4e5d643cf8126a83, 19 | 0x019dd0bbd3693f838a544df08ed4e2348d82d7a7, 20 | 0x20621e35f40cb15fe63d1af047413a6c5dc304b8, 21 | 0x80f564f022d22730ef980753f97be31b9fe28aae, 22 | 0xc22d2aeecc87e3907c277457e0152deb01c4a55a, 23 | 0x8761e0dd63d14cf566acf4b730f3540f164b6b56, 24 | 0xf7bad6c588967b9a512a423b21a3856744f8d6d8, 25 | 0xe5d93dd474988b60b354339da3a845d9cd35ec5f, 26 | 0x907f20132c1b706026987d592aa6f5bfe83ff314, 27 | 0xb0febed47971f087695d66ea548599f722cd67a5, 28 | 0x763d5862600d0326e14541c0687e79888999b9ab, 29 | 0x5721e592da25b96edd4d44c632cb841ac7ac47bb, 30 | 0x8cbc229cad155b95b4174647ba705d6a414b8cca, 31 | 0x77f6d9261dd8e45c0e8555aa86ab1648da9b95c3, 32 | 0x8c13278a1d8310cd4c6943eb17fad81765992431, 33 | 0xf062ceb02b73515c603707458cb3ffdf484eeb5a, 34 | 0xe0dd6cd8f2053e34ee34bb4c96c16d7e487896db, 35 | 0x62356e055f2165d0c5e812a7445700727a7934da, 36 | 0x9926df844af52a567929c2b32133ccf1c66dd4fd, 37 | 0x0e3374a49caa66c34eda91517dd5de77a9a4bbee, 38 | 0x49fc5195aad930b4df3ec1e50bb14af3586f0793, 39 | 0x553315181e9d643831936b90d63221a335c793ef, 40 | 0xa5959231dda9b429668bf198aa3580d05eb12f2e, 41 | 0xcd5164c8c8a6c4d0c49e6fa5872435c33ab42af5, 42 | 0x37aa7b1343faa68806f453f45b09dd24a1c8f3d4, 43 | 0x3b412c92e78fe2a323a5fc7cd9d31fa0226ffe14, 44 | 0xe92356c05d03fce81063caaafca1493ef6619445, 45 | 0xdd41883adc2e8db3d3e52ae06ce754ab7563a53b, 46 | 0x349b49ac381d097b12f38747447776a0b4d91bfc, 47 | 0x2198eefc694f5d50634a950e10074a11442f41a8, 48 | 0x42d2184dac8580e6224b361a36ca3a917f7ea67b, 49 | 0x01405ab95de872f1745d6b88a155f00f91a7e93e, 50 | 0x7a03a44fe2c6f98debc35800a6899ccf0869be71, 51 | 0x38ea9aeb4a849f9accde0c17ccfaeeb53ba6a393, 52 | 0x009b740f1670af3a6b62d661f182f15a24a010ae, 53 | 0xd6dda35c4216e343b325ccffe0615accae1f82b0, 54 | 0xea9900f8b55efc75c9bca99377e0f026e537544b, 55 | 0x38148ecc2078da7f65e6233dda28efaf4c51e96f, 56 | 0x100dbb75eab5d98ad65ef16483aaa68e68aafbc5, 57 | 0x9e108a4aaa86d2edc1fe4e393c8dfb0f37433559, 58 | 0x67d33cf1c7c699078f86d517a5a1cd1444a1e85c, 59 | 0x712033cf8adc1e60beadb8b52c8a02f02bffe336, 60 | 0xee9baf220c6afe2e846d1788e53ca5a0d3dcb339, 61 | 0xd082c621b2a166b90ab607b11decc4e038045177, 62 | 0x0a8966d44be0272295814e876791431791440256, 63 | 0x9f9f6768fcd665b0ae17f02601a24a60d1eb5dab, 64 | 0x5fe218e03a4a0ccd7f3eeac9a51fd23c0fc369a1, 65 | 0x4bd120e887cc82285aff8408dc208ed32b132bb3, 66 | 0x8d21e3c8993633546e89cb4a24e5c0a5bc4d97e5, 67 | 0x54ce1d1fc211817f280c5e123eb131d7a0b0beb5, 68 | 0xda4cc246162e13f26864cacf19152695d252e671, 69 | 0xac463695dcdf0aa5c16fd74d6be2b831b0512971, 70 | 0x874f59a177348ae3d540d58ac8c5d23a4e77b1e3, 71 | 0x9c79aef1d586452220ff23fa6b7791fcae1e7edb, 72 | 0x6229cf71deccb5d87767e747f5a5c7738db7e40f, 73 | 0x1bf32181e831dbc781eeb850d58f801495c4c93d, 74 | 0xf085548d7bbf66b8731e1a16bd891cad98cb102a, 75 | 0x8c4531b3f4cdfee616a44eaef29b5b5ee60cdf6d, 76 | 0x486291fea9e334ea87efbac640d7d50ffc7bf8c3, 77 | 0xb9c5b4bf86119fb67cc6f3df9632de2b46648143, 78 | 0x901b68e2099f83dcd6e443c86146969ef837ed37, 79 | 0x4866e8a3683d5b7fca9dabfb9d1188a4a152e976, 80 | 0x66ff831f74b212bc5b787c4401a5bc63812aa592, 81 | 0x4089bc81b3e2e986fbd429e03094faf6685200db, 82 | 0x351faf0b10e7d80fbaa8726b41825a71b9922c95, 83 | 0x23cbe4413e92c10c2edf9e7fab336d35be555d86, 84 | 0x0a70de1a644ced8488ec91b4b463826cbf4b5000, 85 | 0xb18b8edb5d4d1fdff2488714747b609941433ea0, 86 | 0xd8c184f93f11c591da6e321c8288c6dba85da99e, 87 | 0x6ba366de900785e24c605e4a8b5261bb23a97fe0, 88 | 0x97dfdcd746dd32b87aaa2d1a0b292f1a38c77aab, 89 | 0x7089a6315f2642ea1f21f81d5fde64f20f80f75e, 90 | 0x8a11d3d03629fbd5cae5f8ef0743bae86868d040, 91 | 0x240d90a0e69ad8f5de91ddcf5fc83cdc30631f04, 92 | 0x6653acb6d74ac95f750a687177c5c3d6a5f14c4d, 93 | 0x081af29ee22c5b902bc6e9c3ce7fa9de77ce8266, 94 | 0xf81753501afc55776320d567c3af6fcdad1204cf, 95 | 0x945ef6fb870fce7614626070af43a95d482fd99a, 96 | 0x7862e990963405616afd1e08cd369433a87adb3a, 97 | 0x8c245d87069a4e463b954778dad9df0039876a9e, 98 | 0x2b8f14e64b3be49c75c816a6e7d9596b259656ad, 99 | 0x0870f0b06a5009d9f6bdad4d3961b4639bb96750, 100 | 0x123e084fc361a878aef966df784f6b3774084028, 101 | 0x971d97f261abb1ecb9967878f45d7be1b374c81d, 102 | 0xedbaaf0005e47f8cd1e70472e93d33543ce1d38e, 103 | 0x3032967bb88c1289c49d9c8a696e8303ee9b7d88, 104 | 0x3ab79bb7d1e17730ab32a28019d890c02c6197f4, 105 | 0x95a4ac9070051437263a019028e0fb7f0c9d12a0, 106 | 0x8fbaf4d323ba4031f8c1c8614317aefb65c626dc, 107 | 0xc60debaa978d0c215c37859f8b59b07910afe807, 108 | 0x76927e2ccab0084bd19cee74f78b63134b9d181e, 109 | 0x927c78cb90407aa4b4e8e12497022e689cc5cb11, 110 | 0xd80b5f92b713a3ee048a752faf889734a4a1db93, 111 | 0xefca48c0672c6ddd1aedc84d7f54ec1b130cf854, 112 | 0xb23b9c52fc16beb6d44e243ba73936a65fc9b709, 113 | 0x7202cba14b504ac90f49aea2b698530fde9481dd, 114 | 0x782b899b1468051b01581548011660b9b1974cab, 115 | 0x8324de24e3c064af46e0dfaf146d392084e38f0c, 116 | 0x4cd24ceb901f903958f2d205d2fc218efa9f3d72, 117 | 0x79785c9729dc6135ff3060d1d8b08780ceb51668, 118 | 0x17bb4076ab59e0ef20ad5a873ab4b5341bf01b78, 119 | 0xb1b33acc4e491ad70f5837250defa92c3816f539, 120 | 0x6dd3a7060d58c8400e13dee3a63fafc8b1f7a7d5, 121 | 0x4e061bae4a29035c55f62a93b339bc57a9073aaf, 122 | 0x10af69bd4435e2c09eaa447eb89a5d3d407f5f47, 123 | 0x6a49dd97e18106c163a9fc6bca3afeb200656f49, 124 | 0xb5d365aca3e16068ab0eb3b39acf6db62c789267, 125 | 0x38a92bb6290284033b5871477fcdc2dc9a744baa, 126 | 0xf6d61560d1b6e70ab61cc58b0daa5b1dff742351, 127 | 0x18addb4b0ea01ad018417d7de4e37b9b6d35c734, 128 | 0xa3b1af9c33c6fdf7852db56263a44b93fb6f687b, 129 | 0xc2116c5a7533d8619c574ce70312f7c2306c00fd, 130 | 0x8f4e9d43583b7d4b8cff5a027220e36401c12b8e, 131 | 0x29809ede7cbe49c6fe1c1dd40513a7bda438473a, 132 | 0x4931b973792aa349108884a26ad0502690f10cdc, 133 | 0x620703fcd6a3ef6773511ef0bc45a558e5647a8c, 134 | 0x2db04d075c2a96e65d8beef44879ed8d07167384, 135 | 0xcaee01dd455e90ac98d84ae847d22261315db192, 136 | 0xdcbbbd8e22d9945f062909923d4524b9630a32ca, 137 | 0xdde972259b65433c769774e504b312de359c031a, 138 | 0x8d3748096d58695246d8735bba837098b8d3fdd7, 139 | 0x932c23938b9bbfaad91ddaf938c622d315eeef34, 140 | 0x739066ed2d1718fd100cec4d9f347382ec6440dc, 141 | 0xb3fa0fa3682bbafaeb95ef288e841609baa5c68b, 142 | 0x59e5fe8a9637702c6d597c5f1c4ebe3fba747371, 143 | 0x66eb0de38cdbbd157d6940b9542a6ca3c98e9080, 144 | 0xc9d4c2fc8c1401eeaa18c048704934f616ed8002, 145 | 0xca14e2076784f7486cf9c1cb4afe194af96bf3fe, 146 | 0x86834a65e12fc6fa4b564b8d0f6b98a4caf88f50, 147 | 0xe2190203932edc3cf358cb497d165a57a0711f77, 148 | 0xf428237037e5c0acf0751f9b42b83d7b4903d305, 149 | 0x89a94b48b21d3b5f959305643d3c71dc88cc7fbc, 150 | 0xe207214e3bc14154f22a59144ccdff6099ca6567, 151 | 0x1eccd61c9fa53a8d2e823a26cd72a7efd7d0e92e, 152 | 0x3833f8dbdbd6bdcb6a883ff209b869148965b364, 153 | 0x9b422e571eb2cb9837efdc4f9087194d65fb070a, 154 | 0x120c322c0b1477a5c2eeb6f9f06d2c98797f485f, 155 | 0xbe6b82da1815de15c1798f2ea58031d793275ff1, 156 | 0x0c38aa3d82aa58ddd9ff8a0ced05f8995950ae4d, 157 | 0x00e96c34d0d80fc23d730ac0702c86ac746f5534, 158 | 0x34b2cf5894ee3e0537e8f45cae265e68d4df4d8c, 159 | 0x69e753860e3a6bb1fead78ab5b5e573e909108c8, 160 | 0xaad68b51814179146d56e824e9bba8f2b5be4f1e, 161 | 0x22c6a17c9d68d0d8cb057435ab087943a62acbbd, 162 | 0x8c3fdeed9f2f79d294300b4b4d182a814107cf6d, 163 | 0x196d791102f53abad22b8d26f5896c4a48146432, 164 | 0x670be3b80f91b0749146a527e7ffbe1397405e9b, 165 | 0x33d87b186a47beb965fdfa207da1ae00d2d7b812, 166 | 0xa106379acb22df279f1ac3c0299742531b3ba6ee, 167 | 0xb2dd65f58594ce19fcfa7daac204587a3287899b, 168 | 0x525a7eb5dbd784feee9a4667f560406f1ea5367a, 169 | 0x26eeb2711ac35bf1c88b6c0bff75a4dd9e02d636, 170 | 0x6ed8cd17ee5453e17eb731a6b2004af53ea1f26a, 171 | 0xf8bad169cb40a0471d2f30b5772bf2ab7b7c0ee8, 172 | 0xbd131d488746eb914fbb4174b783a1b3d77f25e2, 173 | 0x2764281f229fd932b93c5bc9bf3ed2f7ebdd8d9c, 174 | 0x2707bcc067112cd420159b667c47f1dbfc858df2, 175 | 0xb93147f598f8fc20f9cd6276e56b49dfb608eb16, 176 | 0x22b0ac0be4633f57fa4457268ad365c11227e172, 177 | 0xa87d8906eabb8dfb38cc2370a36afaf013d51857, 178 | 0xb904c6901359606d7954bc0dcaaabad3ac33d6c4, 179 | 0x96921dd0a92ed047954d977e4363ec15a0b807d8, 180 | 0x1f4b0a926e0a1b492bb17bfdb8647d658701b08c, 181 | 0x597aa8e340582363f74acc894b51447a2b7d040c, 182 | 0x2f16cc0f50b1e7088177f71ab02985d4daaf7a03, 183 | 0xd4f756dadd2eb06bd14066a882f611636bf16129, 184 | 0xf59fd77afe562aa4e5b66da6e7a67b4651fac538, 185 | 0x1f81da5cc554766d3626dae05bdc6515522bb00f, 186 | 0x8f53518d306cd730652b166005aaaa1f1dd0c137, 187 | 0x18cb0c10e271fa2daca76fb03afe0fc02e83ab74, 188 | 0x9ea60ba8a75f47757f516fb1b8d48d848504a26c, 189 | 0x5ad003a9a02037b196362784f4a53e72198a0a47, 190 | 0xec9e1351c0dcc363b4d027f93ef196b023f0794f, 191 | 0x95dc704ddaa7708f7c8ec220c3f19fdfd81ae708, 192 | 0xf60bddbf39c3e60a50b328230b851a211db134c7, 193 | 0xa55638362ceda5245fa87471ac80189e8d7ad5c4, 194 | 0x82d99d98115565d2a58e99fc53704cee56e3c0de, 195 | 0x71a93ff96b59b96d2caf474b9a79a51f5b043dc3, 196 | 0x43ffdc79fbcedf718a19ad7d7eb2a705696546e4, 197 | 0xfd5a044f5211b1054d6e18b9cb6ece538ec3057f, 198 | 0x8a9354835138d2367923622f27d6c377546175e0, 199 | 0x609723885766759aaf726c241afaa3fb82a3580a, 200 | 0xcc979952371ba7873983b3fadd38649496d206af, 201 | 0xd4bccbde6dc6c922edd96fa1bfa82f03454fbfd8, 202 | 0x7fd44298916ebcb7ffd80bc2b44c918e5333ec3d, 203 | 0xf2787ca2de0b2d20c6cc9bc1ef3519d7eeba47ae, 204 | 0xc285d754df88d0283adbee7027bdb38f3a38adb6, 205 | 0x81c831e3ed24d4b97585c1a371c7aabeba839bb4, 206 | 0xdba4fd71bcd12a9188a365fccc796c65381b49f1, 207 | 0x071b572c7d6b423973078ae08981d944961775f4, 208 | 0x056d01e62c5b2568517d3af63447076e1456998c, 209 | 0x1b3f224bf8fdb4c23fbd6056fe062ffd456b09d6, 210 | 0x91d7833bf076eb16d51f5bff598ad42f4e79bb51, 211 | 0x92a898d46f19719c38126a8a3c27867ae2cee596, 212 | 0xc24b1a1a4cde1c628202a345708653334d30f05f, 213 | 0x31e156678b8f96005f14f91676bef642c28ad06c, 214 | 0x8d12a197cb00d4747a1fe03395095ce2a5cc6819, 215 | 0x74f9d4f1f58686fb8725c29f9ab2cdd2913dd1f0, 216 | 0x2c21e83a76b2dea9fb85885257eb1ac3f82da450, 217 | 0x34bd23da4689788dfa8e6521f979f5eaa3831b3e, 218 | 0xef3c3dfb36ae7c32f509ec1676659ff86ab3f394, 219 | 0x666d77e5b67ddb1f861b3485b70c60b5256a3aa8, 220 | 0xe9ebee76a902b6536c0bddf79ac4c4343e5bdbfb, 221 | 0x8038339ca5c71a11eb321986bf7acd1282da84d5, 222 | 0xad032694466f2b56b11e8258747af91bf72ed791, 223 | 0xf697eeca8130513cc59b1a3ebcc098ba5c10d80c, 224 | 0x4ea5fb82a9ad7cae9bc4357ee0368eb73f9de17d, 225 | 0x00c5a971dafa5e8e2dcff0af9571e2dbe4f4a0bf, 226 | 0x2f2862e9818ca4ada4ae69e700e3a806236d997e, 227 | 0xc7075ed6bf05549ed8e0d6b11f58721040ab4d05, 228 | 0xc9dd260b16b9402e6244f34a8c52dfdcd6eb9113, 229 | 0xce6d2f7b55eb3066c14b4047b2017d7000697e14, 230 | 0x73aa55afb6f0258b2e8b723b7598eb3b3a8312ed, 231 | 0x76bf705e423ccc18f699f275c7bc2d506090acda, 232 | 0x68f521a871ddd406a57121bf7246c7574bc3cb62, 233 | 0x41bacae05437a3fe126933e57002ae3f129aa079, 234 | 0xcc8f79950bfe3d292608321351c4d45d497a7c6d, 235 | 0x63eed78361018f43cb46fa48a6122e004d963a2d, 236 | 0x3a02b24bf0bfc7b9c485eba503fd73e751ca0b00, 237 | 0x4062b2b12b354bf8f9398685a29e1f5e534f95a8, 238 | 0x286f0171bb5c940314c83c9cd9e355106a7de7e7, 239 | 0x33a96b5993f65e26ad4f51b3d3ccea513271b923, 240 | 0xce838139e92080e8e1266092bafb64d1e43dd139, 241 | 0x133f9d96901e3727596660d32dbdcbcf57c03dbb, 242 | 0xfa60838ac163b8b44108b92c8b64c7c15fd5d1bd, 243 | 0x4fc15720ac8dfbdd20aa47c518cf26ee2fc39ef7, 244 | 0x4e89c8b0cefe3b3239a168325baacec8c1cee218, 245 | 0x450f5f260559e4861cd703b61bb9e5e0c622ef29, 246 | 0x4ecec940deabb584b43f21c91b1238bd43b5e7eb, 247 | 0x9e3e93fc983d84e6397e35bc5cd3c33783041f35, 248 | 0xb8c5bb7d01294f5148488786ac9578e97ffb95fd, 249 | 0xcab61b858a344ad4c878c60de62b2019d3e00aed, 250 | 0x7a82fb208303aab2369105ba03c9c0146ac8fd86, 251 | 0x607e4d73800332bf29ff7729a8b519cce7d54202, 252 | 0xcb37a8f731b4db7238d4f4683824ca54decdbce2, 253 | 0x4a789c070535b51ef86628dc995813c6c2eddb32, 254 | 0x70540666f778b7111d62cd6a9ea8a35f44d4076a, 255 | 0xdee028822bc40ac1d6bb042c2378796efdce8bd9, 256 | 0xab697f58c8e9525df5337052cd71d1ba6fff7306, 257 | 0x56532086984e9f6e806cd20c0d241f97b8fe18d9, 258 | 0x4c5aa30f63955ed90e2c47b401dd67e7a409735b, 259 | 0x2beea5ee986b5fd028f4fa64f832020f3ca35aad, 260 | 0x58562bcb3ccaa3b773053eb61d700c7a20884b50, 261 | 0x801f28a3c0813395ed6f2a7216fa16c88917a542, 262 | 0x3e2021221b0bb5e2d1ceda9f024ed9804b055708, 263 | 0x4212fb436bb377cc3c10f5e2f16894daefa68f2f, 264 | 0xa72c38bd35ef32267b6a9315ad3cf36e2624767e, 265 | 0x53dd1454af6da0c6c80230c6f4ba06352b46fd7c, 266 | 0x6aaf4ca8f73f28acf8ae1d8d90465cc9026fd415, 267 | 0xebb62cf8e22c884b1b28c6fa88fbbc17938aa787, 268 | 0x7bacdb8b1aa9390a1ceee85c1e26a9f03671fd43, 269 | 0x0644c1a6d61e0994c73c86febdf64ec8432b1013, 270 | 0x0bbde14072a835903f2f687dd64dd990680e967f, 271 | 0xd07d158d62b1f2c657ceb7331b75de868b83dccf, 272 | 0x60fee5affc433a53e8751fe55fc4bd1794f5ff51, 273 | 0x13148f7a44f78415ba50e8d6a9d7f97ad5a5aff6, 274 | 0x2b5dbb6907816e4ab6182bf58dae543b48ff319a, 275 | 0x7d2330b2906bfdf1c192fc8b750081a2ee6f8328, 276 | 0x57d0c97313bef304f99f338285de6fdce26d0486, 277 | 0xd82ab13c28b70d2a904e03faf15c48df1c970926, 278 | 0x26444302825fbe13d5a5d1c7e7c5759ca67f490d, 279 | 0x32035a7f88db2ae8b66e84fc9333084fa27d88fb, 280 | 0x388e431d70958a5f338a94081d244ec82165ba9b, 281 | 0x7401d01a9bcf8ee5d44b57f255d8a7c14a12d80c, 282 | 0x0fac3479b2eacb02445932e5b65122d6ffab3db8, 283 | -------------------------------------------------------------------------------- /scripts/controlHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ownership helpers module 3 | * @module ownership-helpers 4 | */ 5 | 6 | var Promise = require('bluebird') 7 | import { gas } from './testConfig.js' 8 | import { waitUntilTransactionsMined } from './helpers.js' 9 | 10 | /** 11 | * @description Transfer ownership of input contract and wait until corresponding transaction is mined 12 | * @param contract {Object} - Truffle Contract Object 13 | * @param controller {String} 14 | * @param newController {String} 15 | * @returns transaction receipt {Object} 16 | */ 17 | const transferControl = async (contract, controller, newController) => { 18 | let params = { from: controller, gas: gas } 19 | let txn = await contract.transferControl(newController, params) 20 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 21 | return txnReceipt 22 | } 23 | 24 | /** 25 | * @description Transfer the ownerships of input contracts and wait until corresponding transactions are mined 26 | * @param contracts {Object} - Truffle Object Contract Array 27 | * @param controller {String} 28 | * @param newController {String} 29 | */ 30 | const transferControls = async (contracts, controller, newController) => { 31 | let promises = contracts.map(function (contract) { transferControl(contract, controller, newController) }) 32 | await Promise.all(promises) 33 | } 34 | 35 | /** 36 | * @description Lock ownership of input contract and wait until corresponding transaction is mined 37 | * @param contract {Object} - Truffle Object Contract Array 38 | * @param controller - {String} 39 | * @returns Transaction Receipt {Object} 40 | */ 41 | const lockControl = async (contract, controller) => { 42 | let params = { from: controller, gas: gas } 43 | let txn = await contract.lockControl(controller, params) 44 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 45 | return txnReceipt 46 | } 47 | 48 | module.exports = { 49 | transferControl, 50 | transferControls, 51 | lockControl 52 | } 53 | -------------------------------------------------------------------------------- /scripts/disableTransfers.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | require('babel-register') 3 | require('babel-polyfill') 4 | require('./jsHelpers.js') 5 | 6 | const Web3 = require('web3') 7 | const ProofToken = artifacts.require('./ProofToken.sol') 8 | const TokenSale = artifacts.require('./TokenSale.sol') 9 | const provider = artifacts.options.provider 10 | const web3 = new Web3(provider) 11 | const config = require('../config') 12 | 13 | let tokenSale 14 | let sender 15 | 16 | web3.eth.getAccounts(function(error,result) { 17 | let sender = result[0] 18 | run(sender) 19 | callback() 20 | }) 21 | 22 | const run = async function(sender) { 23 | tokenSale = await TokenSale.deployed() 24 | let txn = await tokenSale.enableTransfers(false, { from: sender, gas: config.constants.DEFAULT_GAS, gasPrice: config.constants.DEFAULT_GAS_PRICE }) 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /scripts/enableTransfers.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | require('./jsHelpers.js') 6 | 7 | const Web3 = require('web3') 8 | const ProofToken = artifacts.require('./ProofToken.sol') 9 | const TokenSale = artifacts.require('./TokenSale.sol') 10 | const provider = artifacts.options.provider 11 | const web3 = new Web3(provider) 12 | const config = require('../config') 13 | 14 | let tokenSale 15 | let sender 16 | 17 | web3.eth.getAccounts(function(error,result) { 18 | let sender = result[0] 19 | run(sender) 20 | callback("Success") 21 | }) 22 | 23 | const run = async function(sender) { 24 | tokenSale = await TokenSale.deployed() 25 | let txn = await tokenSale.enableTransfers(true, { from: sender, gas: config.constants.DEFAULT_GAS, gasPrice: config.constants.DEFAULT_GAS_PRICE }) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /scripts/getPresaleBalances.js: -------------------------------------------------------------------------------- 1 | module.exports = async function(callback) { 2 | 3 | const csv = require('csv-parser') 4 | const json2csv = require('json2csv') 5 | const Web3 = require('web3') 6 | 7 | const fs = require('fs') 8 | const request = require('async-request') 9 | const csvdata = require('csvdata') 10 | const awaitEach = require('await-each') 11 | 12 | let config = require('../config') 13 | let getTokenBalance = require('./tokenHelpers').getTokenBalance 14 | 15 | let presaleToken = config.addresses.ethereum.PRESALE_TOKEN 16 | 17 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 18 | const proofPresaleToken = await ProofPresaleToken.at(presaleToken) 19 | const provider = artifacts.options.provider 20 | const web3 = new Web3(provider) 21 | 22 | let addresses = []; 23 | var balances = []; 24 | 25 | const writeData = new Promise((resolve, reject) => { 26 | fs.createReadStream('./scripts/addresses.csv') 27 | .pipe(csv()) 28 | .on('data', function(data) { 29 | addresses.push(data['Addresses']) 30 | }) 31 | .on('end', resolve); 32 | }) 33 | 34 | const getBalance = async(address) => { 35 | let balance = await getTokenBalance(proofPresaleToken, address) 36 | return { "address": address, "balance": balance } 37 | } 38 | 39 | async function run() { 40 | await writeData; 41 | 42 | let tokenBalances = await awaitEach(addresses, async function(address) { 43 | return await getBalance(address); 44 | }) 45 | 46 | let fields = ['address', 'balance']; 47 | let csv = json2csv({ data: tokenBalances, fields: fields }) 48 | 49 | fs.writeFile('./balances.csv', csv, function(err) { 50 | if (err) throw err; 51 | console.log('file saved'); 52 | }); 53 | 54 | }; 55 | 56 | run(); 57 | } 58 | -------------------------------------------------------------------------------- /scripts/helpers.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | 3 | /** 4 | * @module Helpers 5 | */ 6 | 7 | var Promise = require('bluebird') 8 | let chai = require('chai') 9 | var chaiAsPromised = require('chai-as-promised') 10 | const expect = chai.expect 11 | chai.should() 12 | chai.use(chaiAsPromised) 13 | 14 | /** 15 | * @description Returns a promise that is resolve when transactions corresponding to input hashes are resolved 16 | * @param txnHashes {String[]} 17 | * @returns Promise resolved upon mining of all input transaction {Promise} 18 | */ 19 | const waitUntilTransactionsMined = (txnHashes) => { 20 | var transactionReceiptAsync 21 | const interval = 500 22 | transactionReceiptAsync = function (txnHashes, resolve, reject) { 23 | try { 24 | var receipt = web3.eth.getTransactionReceipt(txnHashes) 25 | if (receipt == null) { 26 | setTimeout(function () { 27 | transactionReceiptAsync(txnHashes, resolve, reject) 28 | }, interval) 29 | } else { 30 | resolve(receipt) 31 | } 32 | } catch (e) { 33 | reject(e) 34 | } 35 | } 36 | 37 | if (Array.isArray(txnHashes)) { 38 | var promises = [] 39 | txnHashes.forEach(function (txnHash) { 40 | promises.push(waitUntilTransactionsMined(txnHash)) 41 | }) 42 | return Promise.all(promises) 43 | } else { 44 | return new Promise(function (resolve, reject) { transactionReceiptAsync(txnHashes, resolve, reject) }) 45 | } 46 | } 47 | 48 | /** 49 | * @description Returns the balance of an ethereum wallet 50 | * @param address {String} - Ethereum address 51 | * @returns wallet balance {Number} 52 | */ 53 | const getBalance = (address) => { 54 | let balance = web3.eth.getBalance(address) 55 | return balance.toNumber() 56 | } 57 | 58 | /** 59 | * @description Returns the balance of several ethereum wallets (in wei) 60 | * @param addresses {String[]} - Array of ethereum addresses 61 | * @returns wallet balances (in wei) {Number[]} 62 | */ 63 | const getBalances = (addresses) => { 64 | let balances = [] 65 | addresses.map(function (address) { balances.push(getBalance(address)) }) 66 | return balances 67 | } 68 | 69 | /** 70 | * @description Returns the balance of an ethereum wallet (in ether) 71 | * @param address {String} - Ethereum address 72 | * @returns wallet balance (in ether) {Number} 73 | */ 74 | const getEtherBalance = (address) => { 75 | let balance = web3.fromWei(web3.eth.getBalance(address), 'ether') 76 | return balance.toNumber() 77 | } 78 | 79 | /** 80 | * @description Returns the balance of several ethereum wallets (in ether) 81 | * @param addresses {String[]} 82 | * @returns wallet balances (in ether) {Number[]} 83 | */ 84 | const getEtherBalances = (addresses) => { 85 | let balances = [] 86 | addresses.forEach(function (address) { balances.push(getEtherBalance(address)) }) 87 | return balances 88 | } 89 | 90 | /** 91 | * @description Converts wei to ether 92 | * @param valueInWei {Number} 93 | * @returns valueInEther {Number} 94 | */ 95 | const inEther = (valueInWei) => { 96 | let amount = web3.fromWei(valueInWei, 'ether') 97 | return Number(amount) 98 | } 99 | 100 | /** 101 | * @description Converts ether to wei 102 | * @param valueInEther {Number} 103 | * @returns valueInWei {Number} 104 | */ 105 | const inWei = (valueInEther) => { 106 | let amount = web3.toWei(valueInEther, 'ether') 107 | return amount.toNumber() 108 | } 109 | 110 | // in our case the base units are cents 111 | /** 112 | * @description Convert tokens cents to token units 113 | * @param tokenCents {Number} 114 | * @returns token base units {Number} 115 | */ 116 | const inBaseUnits = (tokenCents) => { 117 | return tokenCents / (10 ** 2) 118 | } 119 | 120 | /** 121 | * @description Converts token units to token cents 122 | * @param tokenBaseUnits {Number} 123 | * @returns token cents {Number} 124 | */ 125 | const inCents = (tokenBaseUnits) => { 126 | return tokenBaseUnits * (10 ** 2) 127 | } 128 | 129 | /** 130 | * @description Converts token base units (ERC20 units) to token units 131 | * @param tokenBaseUnits {Number} 132 | * @returns token units {Number} 133 | */ 134 | const inTokenUnits = (tokenBaseUnits) => { 135 | return tokenBaseUnits / (10 ** 18) 136 | } 137 | 138 | /** 139 | * @description Returns address corresponding to a contract 140 | * @param contract {Object} - Truffle Contract Object 141 | * @returns address {String} 142 | */ 143 | const getAddress = async (contract) => { 144 | let address = contract.address 145 | return address 146 | } 147 | 148 | /** 149 | * @description Returns the addresses corresponding to an array of contracts 150 | * @param contracts {Object} Array of truffle contract objects 151 | * @returns address[] {String[]} 152 | */ 153 | const getAddresses = async (contracts) => { 154 | let addresses = contracts.map(function (contract) { 155 | return contract.address 156 | }) 157 | return addresses 158 | } 159 | 160 | /** 161 | * @description Send an ethereum transaction and wait until the transaction is mined 162 | * @param txn {Object} Web3 transaction object 163 | * @returns txnReceipt {Object} transaction receipt 164 | */ 165 | const sendTransaction = async (txn) => { 166 | let txnHash = await web3.eth.sendTransaction(txn) 167 | let txnReceipt = await waitUntilTransactionsMined(txnHash) 168 | return txnReceipt 169 | } 170 | 171 | /** 172 | * @description Send ethereum transactions and wait until all transactions are mined 173 | * @param txns {Object} Web3 transactions object 174 | * @returns txnReceipts {Object} transaction receipts 175 | */ 176 | const sendTransactions = async (txns) => { 177 | let txnHashes = [] 178 | let txnHash 179 | 180 | for (let txn of txns) { 181 | txnHash = await sendTransaction(txn) 182 | txnHashes.push(txnHash) 183 | } 184 | 185 | return txnHashes 186 | } 187 | 188 | /** 189 | * @description Fails if the input promise is not rejected with an Invalid opcode message 190 | * @param promise 191 | */ 192 | const expectInvalidOpcode = async (promise) => { 193 | try { 194 | await promise 195 | } catch (error) { 196 | expect(error.message).to.include('invalid opcode') 197 | return 198 | } 199 | expect.fail('Expected throw not received') 200 | } 201 | 202 | /** 203 | * @description Fails if the input promise is not reject with an Invalid jump message 204 | * @param promise 205 | */ 206 | const expectInvalidJump = async (promise) => { 207 | try { 208 | await promise 209 | } catch (error) { 210 | expect(error.message).to.include('invalid JUMP') 211 | return 212 | } 213 | expect.fail('Expected throw not received') 214 | } 215 | 216 | /** 217 | * @description Fails if the input promise is not rejected with an Out of Gas message 218 | * @param promise 219 | */ 220 | const expectOutOfGas = async (promise) => { 221 | try { 222 | await promise 223 | } catch (error) { 224 | expect(error.message).to.include('out of gas') 225 | return 226 | } 227 | expect.fail('Expected throw not received') 228 | } 229 | 230 | /** 231 | * @description Mine the local evm 232 | * @returns promise 233 | */ 234 | const advanceBlock = () => { 235 | return new Promise((resolve, reject) => { 236 | web3.currentProvider.sendAsync({ 237 | jsonrpc: '2.0', 238 | method: 'evm_mine', 239 | id: Date.now(), 240 | }, (err, res) => { 241 | return err ? reject(err) : resolve(err) 242 | }) 243 | }) 244 | } 245 | 246 | /** 247 | * @description Advance to input block 248 | * @param number 249 | */ 250 | const advanceToBlock = async(number) => { 251 | if (web3.eth.blockNumber > number) { 252 | throw Error(`block number ${number} is in the past (current is ${web3.eth.blockNumber})`) 253 | } 254 | while (web3.eth.blockNumber < number) { 255 | await advanceBlock() 256 | } 257 | } 258 | 259 | const latestTime = function() { 260 | return moment.unix(web3.eth.getBlock('latest').timestamp) 261 | } 262 | 263 | const increaseTime = function(duration) { 264 | const id = Date.now() 265 | 266 | return new Promise((resolve, reject) => { 267 | web3.currentProvider.sendAsync({ 268 | jsonrpc: '2.0', 269 | method: 'evm_increaseTime', 270 | params: [duration.asSeconds()], 271 | id: id, 272 | }, err1 => { 273 | if (err1) return reject(err1) 274 | 275 | web3.currentProvider.sendAsync({ 276 | jsonrpc: '2.0', 277 | method: 'evm_mine', 278 | id: id+1, 279 | }, (err2, res) => { 280 | return err2 ? reject(err2) : resolve(res) 281 | }) 282 | }) 283 | }) 284 | } 285 | 286 | const getTxnReceiptData = (txnReceipt) => { 287 | let logs = txnReceipt.logs 288 | let dataArray = [] 289 | logs.forEach(log => { 290 | let data = log.data 291 | if (data) { 292 | dataArray.push(data) 293 | } else { 294 | dataArray.push('no data') 295 | } 296 | }) 297 | 298 | return dataArray 299 | } 300 | 301 | const getTxnReceiptTopics = (txnReceipt) => { 302 | let logs = txnReceipt.logs 303 | 304 | let topics = logs.map(log => { 305 | let topics = log.topics 306 | let result = { 307 | 'functionID': topics[0], 308 | 'parameters': topics.slice(1) 309 | } 310 | return result 311 | }) 312 | return topics 313 | } 314 | 315 | module.exports = { 316 | waitUntilTransactionsMined, 317 | getBalance, 318 | getBalances, 319 | getEtherBalance, 320 | getEtherBalances, 321 | inEther, 322 | inWei, 323 | inBaseUnits, 324 | inTokenUnits, 325 | inCents, 326 | getAddress, 327 | getAddresses, 328 | sendTransaction, 329 | sendTransactions, 330 | expectInvalidOpcode, 331 | expectInvalidJump, 332 | expectOutOfGas, 333 | advanceToBlock, 334 | getTxnReceiptData, 335 | getTxnReceiptTopics, 336 | latestTime, 337 | increaseTime 338 | } 339 | 340 | -------------------------------------------------------------------------------- /scripts/importBalances.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | Array.prototype.toNumber = function () { 4 | return this.map((elem) => { return parseFloat(elem) }) 5 | } 6 | 7 | require('babel-register') 8 | require('babel-polyfill') 9 | require('./jsHelpers.js') 10 | 11 | const fs = require('fs') 12 | const csv = require('csv-parser') 13 | const Web3 = require('web3') 14 | const config = require('../config.js') 15 | 16 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 17 | const ProofToken = artifacts.require('./ProofToken.sol') 18 | const provider = artifacts.options.provider 19 | const web3 = new Web3(provider) 20 | 21 | let proofToken 22 | let proofPresaleToken 23 | let fund 24 | 25 | web3.eth.getAccounts(function(error,result) { 26 | fund = result[0] 27 | }) 28 | 29 | 30 | const getAddress = async (contract) => { 31 | let address = contract.address 32 | return address 33 | } 34 | 35 | const importBalances = async(token, caller, addresses, balances) => { 36 | let txn = await token.importPresaleBalances(addresses, balances, { from: caller, gas: config.constants.MAX_GAS, gasPrice: config.constants.DEFAULT_HIGH_GAS_PRICE }) 37 | } 38 | 39 | const run = async function(){ 40 | proofToken = await ProofToken.deployed() 41 | await launchImport() 42 | } 43 | 44 | const launchImport = async function() { 45 | let addresses = [] 46 | let balances = [] 47 | 48 | const writeData = new Promise((resolve, reject) => { 49 | fs.createReadStream('./scripts/balances.csv') 50 | .pipe(csv()) 51 | .on('data', function (data) { 52 | addresses.push(data['address']) 53 | balances.push(data['balance']) 54 | }) 55 | .on('end', resolve) 56 | }) 57 | 58 | await writeData 59 | balances = balances.toNumber() 60 | 61 | let addressListNumber = addresses.length 62 | 63 | for (let i = 0; i < addressListNumber; i = i + 50) { 64 | let addressesBatch = addresses.slice(i, i + 50) 65 | let balancesBatch = balances.slice(i, i + 50) 66 | let receipt = await importBalances(proofToken, fund, addressesBatch, balancesBatch) 67 | } 68 | } 69 | 70 | await run() 71 | callback() 72 | } 73 | 74 | -------------------------------------------------------------------------------- /scripts/jsHelpers.js: -------------------------------------------------------------------------------- 1 | Array.prototype.toNumber = function () { 2 | return this.map((elem) => { return parseInt(elem) }) 3 | } 4 | Array.prototype.sum = function () { 5 | return this.reduce((pv, cv) => pv + cv, 0) 6 | } 7 | -------------------------------------------------------------------------------- /scripts/lockBalances.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | require('./jsHelpers.js') 6 | 7 | const Web3 = require('web3') 8 | const ProofToken = artifacts.require('./ProofToken.sol') 9 | const provider = artifacts.options.provider 10 | const web3 = new Web3(provider) 11 | const config = require('../config') 12 | 13 | let proofToken 14 | let sender 15 | 16 | web3.eth.getAccounts(function(error,result) { 17 | let sender = result[0] 18 | run(sender) 19 | callback() 20 | }) 21 | 22 | const run = async function(sender) { 23 | proofToken = await ProofToken.deployed() 24 | let txn = await proofToken.lockPresaleBalances({ from: sender, gas: config.constants.DEFAULT_GAS, gasPrice: config.constants.DEFAULT_HIGH_GAS_PRICE }) 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /scripts/logEvents.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | require('./jsHelpers.js') 6 | 7 | const Web3 = require('web3') 8 | const provider = artifacts.options.provider 9 | const web3 = new Web3(provider) 10 | 11 | const ProofToken = artifacts.require('./ProofToken.sol') 12 | const TokenSale = artifacts.require('./TokenSale.sol') 13 | 14 | const proofToken = await ProofToken.deployed() 15 | const proofTokenSale = await TokenSale.deployed() 16 | 17 | let tokenEvents = proofToken.allEvents({fromBlock: 1047310, toBlock: 'latest'}) 18 | let tokenSaleEvents = proofTokenSale.allEvents({fromBlock: 0, toBlock: 'latest'}) 19 | 20 | tokenEvents.watch((err,res) => { 21 | console.log("\n****************\n") 22 | console.log(res) 23 | console.log("\n****************\n") 24 | }) 25 | 26 | tokenSaleEvents.watch((err,res) => { 27 | console.log("\n****************\n") 28 | console.log(res) 29 | console.log("\n****************\n") 30 | }) 31 | 32 | callback() 33 | } -------------------------------------------------------------------------------- /scripts/ownershipHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ownership helpers module 3 | * @module ownership-helpers 4 | */ 5 | 6 | var Promise = require('bluebird') 7 | import { gas } from './testConfig.js' 8 | import { waitUntilTransactionsMined } from './helpers.js' 9 | 10 | /** 11 | * @description Transfer ownership of input contract and wait until corresponding transaction is mined 12 | * @param contract {Object} - Truffle Contract Object 13 | * @param sender {String} 14 | * @param receiver {String} 15 | * @returns transaction receipt {Object} 16 | */ 17 | const transferOwnership = async (contract, sender, receiver) => { 18 | let params = { from: sender, gas: gas } 19 | let txn = await contract.transferOwnership(receiver, params) 20 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 21 | return txnReceipt 22 | } 23 | 24 | /** 25 | * @description Transfer the ownerships of input contracts and wait until corresponding transactions are mined 26 | * @param contracts {Object} - Truffle Object Contract Array 27 | * @param sender {String} 28 | * @param receiver {String} 29 | */ 30 | const transferOwnerships = async (contracts, sender, receiver) => { 31 | let promises = contracts.map(function (contract) { transferOwnership(contract, sender, receiver) }) 32 | await Promise.all(promises) 33 | } 34 | 35 | /** 36 | * @description Lock ownership of input contract and wait until corresponding transaction is mined 37 | * @param contract {Object} - Truffle Object Contract Array 38 | * @param owner - {String} 39 | * @returns Transaction Receipt {Object} 40 | */ 41 | const lockOwnership = async (contract, owner) => { 42 | let params = { from: owner, gas: gas } 43 | let txn = await contract.lockOwnership(owner, params) 44 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 45 | return txnReceipt 46 | } 47 | 48 | module.exports = { 49 | transferOwnership, 50 | transferOwnerships, 51 | lockOwnership 52 | } 53 | -------------------------------------------------------------------------------- /scripts/pausableHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Pausable helpers module 3 | * @module pausable-helpers 4 | */ 5 | 6 | import { waitUntilTransactionsMined } from './helpers.js' 7 | 8 | /** 9 | * @description Pause the contract and wait until the transaction is mined 10 | * @param contract Token truffle contract 11 | * @param owner Ethereum Address 12 | */ 13 | const pause = async(contract, owner) => { 14 | let tx = await contract.pause({ from: owner, gas: 1000000 }) 15 | await waitUntilTransactionsMined(tx.tx) 16 | } 17 | 18 | /** 19 | * @description Unpause contract and wait until the transaction is mined 20 | * @param contract Token Truffle Contract 21 | * @param owner Ethereum Address 22 | */ 23 | const unpause = async(contract, owner) => { 24 | let tx = await contract.unpause({ from :owner, gas: 1000000 }) 25 | await waitUntilTransactionsMined(tx.tx) 26 | } 27 | 28 | module.exports = { 29 | pause, 30 | unpause 31 | } 32 | -------------------------------------------------------------------------------- /scripts/testConfig.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_GAS = 2 * 10 ** 6 2 | const MAX_GAS = 4.7 * 10 ** 6 3 | const DEFAULT_LOW_GAS_PRICE = 2 * 10 ** 9 4 | const DEFAULT_GAS_PRICE = 6 * 10 ** 9 5 | const DEFAULT_HIGH_GAS_PRICE = 40 * 10 ** 9 6 | const TOKENS_ALLOCATED_TO_PROOF = 1181031 * (10 ** 18) 7 | const DECIMALS_POINTS = 10 ** 18 8 | const TOKEN_UNITS = 10 ** 18 9 | 10 | const WALLET_ADDRESS = '0xe2b3204f29ab45d5fd074ff02ade098fbc381d42' 11 | const PROOF_WALLET_ADDRESS = '0xe2b3204f29ab45d5fd074ff02ade098fbc381d42' 12 | 13 | const pointsMultiplier = 10 ** 18 14 | const ether = 10 ** 18 15 | 16 | module.exports = { 17 | DEFAULT_GAS, 18 | MAX_GAS, 19 | DEFAULT_LOW_GAS_PRICE, 20 | DEFAULT_GAS_PRICE, 21 | DEFAULT_HIGH_GAS_PRICE, 22 | TOKEN_UNITS, 23 | DECIMALS_POINTS, 24 | TOKENS_ALLOCATED_TO_PROOF, 25 | PROOF_WALLET_ADDRESS, 26 | WALLET_ADDRESS, 27 | ether, 28 | pointsMultiplier 29 | } 30 | -------------------------------------------------------------------------------- /scripts/tokenHelpers.js: -------------------------------------------------------------------------------- 1 | const h = require('../scripts/helpers.js') 2 | const config = require('../config.js') 3 | import { getAddress } from './helpers.js' 4 | 5 | let { DEFAULT_GAS, DEFAULT_GAS_PRICE, MAX_GAS } = config.constants; 6 | 7 | /** 8 | * @description Get owner of token contract 9 | * @param token 10 | * @returns owner 11 | */ 12 | const getOwner = async (token) => { 13 | let owner = await token.owner.call() 14 | return owner 15 | } 16 | 17 | /** 18 | * @description Get owner of token contract 19 | * @param token 20 | * @returns owner 21 | */ 22 | const getController = async (token) => { 23 | let controller = await token.controller.call() 24 | return controller 25 | } 26 | 27 | /** 28 | * @description Get total supply of token contract 29 | * @param token 30 | * @returns total supply 31 | */ 32 | const getTotalSupply = async (token) => { 33 | let tokenSupply = await token.totalSupply.call() 34 | return tokenSupply.toNumber() 35 | } 36 | 37 | /** 38 | * @description Get total supply of token contract 39 | * @param token 40 | * @returns total supply 41 | */ 42 | const getTotalSupplyAt = async (token, blockNumber) => { 43 | let tokenSupply = await token.totalSupplyAt.call(blockNumber) 44 | return tokenSupply.toNumber() 45 | } 46 | 47 | /** 48 | * @description Get token balance of owner 49 | * @param token 50 | * @param owner 51 | * @returns token balance 52 | */ 53 | const getTokenBalance = async (token, owner) => { 54 | let balance = await token.balanceOf.call(owner) 55 | return balance.toNumber() 56 | } 57 | 58 | /** 59 | * @description Get token balance of owner 60 | * @param token 61 | * @param owner 62 | * @returns token balance 63 | */ 64 | const getTokenBalanceAt = async (token, owner, blockNumber) => { 65 | let balance = await token.balanceOfAt.call(owner, blockNumber) 66 | return balance.toNumber() 67 | } 68 | 69 | 70 | /** 71 | * @description Transfer amount of token from sender to receiver 72 | * @param token 73 | * @param sender 74 | * @param receiver 75 | * @param amount 76 | * @returns transaction receipt 77 | */ 78 | const transferToken = async(token, sender, receiver, amount) => { 79 | let params = { from: sender, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 80 | let txn = await token.transfer(receiver, amount, params) 81 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 82 | return txnReceipt 83 | } 84 | 85 | /** 86 | * @description Transfer amount of token from sender to receiver 87 | * @param token 88 | * @param caller 89 | * @param receiver 90 | * @param amount 91 | * @returns transaction receipt 92 | */ 93 | const transferTokenFrom = async(token, caller, sender, receiver, amount) => { 94 | let params = { from: caller, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 95 | let txn = await token.transferFrom(sender, receiver, amount, params) 96 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 97 | return txnReceipt 98 | } 99 | 100 | /** 101 | * @description Transfer amount of each token in the tokens array from sender to receiver 102 | * @param token 103 | * @param sender 104 | * @param receiver 105 | * @param amount 106 | * @returns transaction receipts 107 | */ 108 | const transferTokens = async(tokens, sender, receiver, amount) => { 109 | let promises = tokens.map(function (oneToken) { transferToken(oneToken, sender, receiver, amount) }) 110 | let txnReceipts = await Promise.all(promises) 111 | return txnReceipts 112 | } 113 | 114 | /** 115 | * @description Mint amount of token for the receiver. Only works if minter is the owner of the token 116 | * @param contract 117 | * @param minter 118 | * @param receiver 119 | * @param amount 120 | * @returns transaction receipt 121 | */ 122 | const mintToken = async(contract, minter, receiver, amount) => { 123 | let params = { from: minter, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 124 | let txn = await contract.mint(receiver, amount, params) 125 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 126 | return txnReceipt 127 | } 128 | 129 | /** 130 | * @description Finish minting period for the token contract. Only works if sender is the owner of the token 131 | * @param token 132 | * @param sender 133 | * @returns transaction receipt 134 | */ 135 | const finishMinting = async(token, sender) => { 136 | let params = { from: sender, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 137 | let txn = await token.finishMinting(params) 138 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 139 | return txnReceipt 140 | } 141 | 142 | /** 143 | * @description Burn amount of tokens from the owner 144 | * @param contract 145 | * @param minter 146 | * @param receiver 147 | * @param amount 148 | * @returns transaction receipt 149 | */ 150 | const burnTokens = async(contract, caller, owner, amount) => { 151 | let params = { from: caller, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 152 | let txn = await contract.burn(owner, amount, params) 153 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 154 | return txnReceipt 155 | } 156 | 157 | /** 158 | * @description Convert ERC20 units (=number of base units times 10^decimals) to base units 159 | * @param token 160 | * @param amount 161 | * @returns base units 162 | */ 163 | const baseUnits = async(token, amount) => { 164 | let decimals = await token.decimals.call() 165 | return (amount / (10 ** decimals.toNumber())) 166 | } 167 | 168 | /** 169 | * @description Convert base units (=number of ERC20 units divided by 10^decimals) to ERC20 units 170 | * @param token 171 | * @param amount 172 | * @returns ERC20 units 173 | */ 174 | const ERC20Units = async(token, amount) => { 175 | let decimals = await token.decimals.call() 176 | return (amount * (10 ** decimals.toNumber())) 177 | } 178 | 179 | /** 180 | * @description Allocates (presale) tokens to the sender 181 | * @param token 182 | * @param sender 183 | * @returns transaction receipt 184 | */ 185 | const claimTokens = async(token, sender) => { 186 | let params = { from: sender, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 187 | let txn = await token.claimTokens(params) 188 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 189 | return txnReceipt 190 | } 191 | 192 | /** 193 | * @description Import presale tokens to the token sale contract 194 | * @param token 195 | * @param addresses 196 | * @param balances 197 | * @returns transaction receipt 198 | */ 199 | const importBalances = async(token, caller, addresses, balances) => { 200 | let txn = await token.importPresaleBalances(addresses, balances, { from: caller, gas: MAX_GAS, gasPrice: DEFAULT_GAS_PRICE }) 201 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 202 | return txnReceipt 203 | } 204 | 205 | const lockBalances = async(token, caller) => { 206 | let txn = await token.lockPresaleBalances({ from: caller }) 207 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 208 | return txnReceipt 209 | } 210 | 211 | const cloneToken = async(contract, caller, config) => { 212 | let txn = await contract.createCloneToken( 213 | config.block, 214 | config.name, 215 | config.symbol, 216 | { from: caller }) 217 | 218 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 219 | return txnReceipt 220 | } 221 | 222 | const approve = async(token, caller, spender, amount) => { 223 | let txn = await token.approve(spender, amount, {from: caller }) 224 | let txnReceipt = await h.waitUntilTransactionsMined(txn.tx) 225 | return txnReceipt 226 | } 227 | 228 | const getAllowance = async(token, owner, spender) => { 229 | let allowance = await token.allowance.call(owner, spender) 230 | return allowance.toNumber() 231 | } 232 | 233 | const enableTransfers = async(token, caller) => { 234 | let txn = await token.enableTransfers(true, { from: caller }) 235 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 236 | } 237 | 238 | const lockTransfers = async(token, caller) => { 239 | let txn = await token.enableTransfers(false, { from: caller }) 240 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 241 | } 242 | 243 | module.exports = { 244 | getOwner, 245 | getController, 246 | getTotalSupply, 247 | getTotalSupplyAt, 248 | getTokenBalance, 249 | getTokenBalanceAt, 250 | transferToken, 251 | transferTokenFrom, 252 | transferTokens, 253 | mintToken, 254 | burnTokens, 255 | finishMinting, 256 | baseUnits, 257 | ERC20Units, 258 | claimTokens, 259 | importBalances, 260 | lockBalances, 261 | cloneToken, 262 | approve, 263 | getAllowance 264 | } 265 | -------------------------------------------------------------------------------- /scripts/tokenSaleHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Token Sale Helpers Module 3 | * @module token-sale-helpers 4 | */ 5 | 6 | import { DEFAULT_GAS, DEFAULT_GAS_PRICE, ether, pointsMultiplier } from './testConfig.js' 7 | import { waitUntilTransactionsMined } from './helpers.js' 8 | 9 | /** 10 | * @description Returns wallet address for input token sale contract 11 | * @alias module:token-sale-helpers 12 | * @param tokenSale {Object} - Truffle Contract Object 13 | * @returns wallet {String} 14 | */ 15 | const getWallet = async (tokenSale) => { 16 | let wallet = await tokenSale.wallet.call() 17 | return wallet 18 | } 19 | 20 | const getMultisig = async (tokenSale) => { 21 | let multisig = await tokenSale.proofMultiSig.call() 22 | return multisig 23 | } 24 | 25 | const getContributors = async (tokenSale) => { 26 | let contributors = await tokenSale.contributors.call() 27 | return contributors.toNumber() 28 | } 29 | 30 | /** 31 | * @description Returns token address for input token sale contract 32 | * @alias module:token-sale-helpers 33 | * @param tokenSale {Object} - Truffle Contract Object 34 | * @returns token {String} 35 | */ 36 | const getToken = async (tokenSale) => { 37 | let token = await tokenSale.proofToken.call() 38 | return token 39 | } 40 | 41 | /** 42 | * @description Returns presale token for input token sale contract 43 | * @alias module:token-sale-helpers 44 | * @param tokenSale {Object} - Truffle Contract Object 45 | * @returns token {String} 46 | */ 47 | const getPresaleToken = async (tokenSale) => { 48 | let presaleToken = await tokenSale.proofPresaleToken.call() 49 | return presaleToken 50 | } 51 | 52 | /** 53 | * @description Returns cap for input token sale contract 54 | * @alias module:token-sale-helpers 55 | * @param tokenSale {Object} - Truffle Contract Object 56 | * @returns token sale cap {String} 57 | */ 58 | const getCap = async (tokenSale) => { 59 | let cap = await tokenSale.cap.call() 60 | return cap.toNumber() 61 | } 62 | 63 | /** 64 | * @description Returns the price in wei of one token sold by the input token sale contract 65 | * @alias module:token-sale-helpers 66 | * @param tokenSale {Object} - Truffle Contract Object 67 | * @returns token price in wei {String} 68 | */ 69 | const getBasePriceInWei = async (tokenSale) => { 70 | let priceInWei = await tokenSale.BASE_PRICE_IN_WEI.call() 71 | return priceInWei.toNumber() 72 | } 73 | 74 | /** 75 | * @description Returns the base token price (price of the token without current premium) 76 | * @alias module:token-sale-helpers 77 | * @param tokenSale {Object} - Truffle Contract Object 78 | * @returns base token price in wei {Number} 79 | */ 80 | const getBasePrice = async (tokenSale) => { 81 | let price = await getBasePriceInWei(tokenSale) 82 | return (price / 10 ** 18) 83 | } 84 | 85 | /** 86 | * @description Returns token price in wei 87 | * @alias module:token-sale-helpers 88 | * @param tokenSale {Object} - Truffle Contract Object 89 | * @returns token price in wei {Number} 90 | */ 91 | const getPriceInWei = async (tokenSale) => { 92 | let priceInWei = await tokenSale.getPriceInWei.call() 93 | return priceInWei.toNumber() 94 | } 95 | 96 | /** 97 | * @description Returns token price in ether 98 | * @alias module:token-sale-helpers 99 | * @param tokenSale {Object} - Truffle Contract Object 100 | * @returns token price in ether {Number} 101 | */ 102 | const getPrice = async (tokenSale) => { 103 | let price = await getPriceInWei(tokenSale) 104 | return (price / 10 ** 18) 105 | } 106 | 107 | /** 108 | * @description Returns the number of remaining tokens 109 | * @alias module:token-sale-helpers 110 | * @param tokenSale {Object} - Truffle Contract Object 111 | * @returns remaining tokens 112 | */ 113 | const getRemainingTokens = async (tokenSale) => { 114 | let remainingTokens = await tokenSale.remainingTokens.call() 115 | return remainingTokens.toNumber() 116 | } 117 | 118 | /** 119 | * @description Performs an token order from sender to the input token sale contract 120 | * @alias module:token-sale-helpers 121 | * @param tokenSale {Object} - Truffle Contract Object corresponding to token sale contract 122 | * @param sender {String} - Ethereum address from which ether is being sent 123 | * @param value {Number} - Value to be send to the contract 124 | * @returns txnReceipt {Object} - Transaction receipt 125 | */ 126 | const buyTokens = async (tokenSale, sender, value) => { 127 | let order = { 128 | from: sender, 129 | value: value, 130 | gas: DEFAULT_GAS, 131 | gasPrice: DEFAULT_GAS_PRICE 132 | } 133 | 134 | let txn = await tokenSale.buyTokens(sender, order) 135 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 136 | return txnReceipt 137 | } 138 | 139 | /** 140 | * @description Returns the token price in wei 141 | * @alias module:token-sale-helpers 142 | * @param tokenSale {Object} - Truffle Contract Object 143 | * @returns token price in wei {Number} 144 | */ 145 | const tokenPriceInWei = async (tokenSale) => { 146 | let price = await tokenSale.getPriceInWei.call() 147 | return price.toNumber() 148 | } 149 | 150 | /** 151 | * @description Returns the token price 152 | * @alias module:token-sale-helpers 153 | * @param tokenSale {Object} - Truffle Contract Object 154 | * @returns token price in ether {Number} 155 | */ 156 | const tokenPrice = async (tokenSale) => { 157 | let price = await getPriceInWei(tokenSale) 158 | return (price.toNumber() / ether) 159 | } 160 | 161 | /** 162 | * @description Returns number of tokens for a certain amount of ether sent to a token sale contract 163 | * @alias module:token-sale-helpers 164 | * @param tokenSale {Object} - Truffle Contract Object 165 | * @param weiAmount {Number} 166 | * @returns number of tokens {Number} 167 | */ 168 | const numberOfTokensFor = async (tokenSale, weiAmount) => { 169 | let priceInWei = await tokenPriceInWei(tokenSale) 170 | let numberOfTokens = Math.floor(weiAmount * pointsMultiplier / priceInWei) 171 | return numberOfTokens 172 | } 173 | 174 | const finalize = async (tokenSale, caller) => { 175 | let txn = await tokenSale.finalize({ from: caller }) 176 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 177 | return txnReceipt 178 | } 179 | 180 | const enableTransfers = async (tokenSale, caller) => { 181 | let txn = await tokenSale.enableTransfers({ from: caller }) 182 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 183 | return txnReceipt 184 | } 185 | 186 | const lockTransfers = async (tokenSale, caller) => { 187 | let txn = await tokenSale.lockTransfers({ from: caller }) 188 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 189 | return txnReceipt 190 | } 191 | 192 | const enableMasterTransfers = async (tokenSale, caller) => { 193 | let txn = await tokenSale.enableMasterTransfers({ from: caller }) 194 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 195 | return txnReceipt 196 | } 197 | 198 | const lockMasterTransfers = async (tokenSale, caller) => { 199 | let txn = await tokenSale.lockMasterTransfers({from: caller }) 200 | let txnReceipt = await waitUntilTransactionsMined(txn.tx) 201 | return txnReceipt 202 | } 203 | 204 | module.exports = { 205 | getWallet, 206 | getToken, 207 | getPresaleToken, 208 | buyTokens, 209 | tokenPrice, 210 | tokenPriceInWei, 211 | pointsMultiplier, 212 | numberOfTokensFor, 213 | getBasePrice, 214 | getBasePriceInWei, 215 | getPrice, 216 | getPriceInWei, 217 | getCap, 218 | getMultisig, 219 | getContributors, 220 | finalize, 221 | enableTransfers, 222 | lockTransfers, 223 | enableMasterTransfers, 224 | lockMasterTransfers 225 | } 226 | -------------------------------------------------------------------------------- /scripts/transferTokenControl.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | require('./jsHelpers.js') 6 | 7 | const Web3 = require('web3') 8 | const ProofToken = artifacts.require('./ProofToken.sol') 9 | const TokenSale = artifacts.require('./TokenSale.sol') 10 | const provider = artifacts.options.provider 11 | const web3 = new Web3(provider) 12 | const config = require('../config') 13 | 14 | let proofToken 15 | let tokenSale 16 | let sender 17 | 18 | web3.eth.getAccounts(function(error,result) { 19 | let sender = result[0] 20 | run(sender) 21 | callback() 22 | }) 23 | 24 | const run = async function(sender) { 25 | proofToken = await ProofToken.deployed() 26 | tokenSale = await TokenSale.deployed() 27 | let txn = await proofToken.transferControl(tokenSale.address, { from: sender, gas: config.constants.DEFAULT_GAS, gasPrice: config.constants.DEFAULT_HIGH_GAS_PRICE }) 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /scripts/transferTokenSaleOwnership.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | require('./jsHelpers.js') 6 | 7 | const Web3 = require('web3') 8 | const TokenSale = artifacts.require('./TokenSale.sol') 9 | const provider = artifacts.options.provider 10 | const web3 = new Web3(provider) 11 | const config = require('../config') 12 | 13 | let tokenSale 14 | let sender 15 | 16 | let newOwner = '0x2C798A55024Dd46657aD3233e271505D198b36e9' 17 | 18 | web3.eth.getAccounts(function(error,result) { 19 | let sender = result[0] 20 | run(sender) 21 | callback() 22 | }) 23 | 24 | const run = async function(sender) { 25 | tokenSale = await TokenSale.deployed() 26 | let txn = await tokenSale.transferOwnership(newOwner, { from: sender, gas: config.constants.DEFAULT_GAS, gasPrice: config.constants.DEFAULT_GAS_PRICE }) 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | const decodeEthereumAddress = (encoded) => { 2 | return '0x' + encoded.slice(26) 3 | } 4 | 5 | module.exports = { 6 | decodeEthereumAddress 7 | } -------------------------------------------------------------------------------- /scripts/watchEvents.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (callback) { 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | require('./jsHelpers.js') 6 | 7 | const Web3 = require('web3') 8 | const provider = artifacts.options.provider 9 | const web3 = new Web3(provider) 10 | 11 | const ProofToken = artifacts.require('./ProofToken.sol') 12 | const TokenSale = artifacts.require('./TokenSale.sol') 13 | 14 | const proofToken = await ProofToken.deployed() 15 | const proofTokenSale = await TokenSale.deployed() 16 | 17 | console.log(proofToken.address) 18 | console.log(proofTokenSale.address) 19 | 20 | let tokenEvents = proofToken.allEvents() 21 | let tokenSaleEvents = proofTokenSale.allEvents() 22 | 23 | tokenEvents.watch((err,res) => { 24 | console.log("\n****************\n") 25 | console.log(res) 26 | console.log("\n****************\n") 27 | }) 28 | 29 | tokenSaleEvents.watch((err,res) => { 30 | console.log("\n****************\n") 31 | console.log(res) 32 | console.log("\n****************\n") 33 | }) 34 | 35 | callback() 36 | } -------------------------------------------------------------------------------- /test/addresses.csv: -------------------------------------------------------------------------------- 1 | Addresses, 2 | 0xcaf15d9cd7cfd573964e3dc88fc6ffea25f89d8f, 3 | 0xf190f0193b694d9d2bb865a66f0c17cbd8280c71, 4 | 0xecd7da67e6563bbddfc2ddff9ba2632c721af181, 5 | 0x31d04a32f22022ec66afe6c2351db768ed32b873, 6 | 0xc7c78f87e2e8d1d55104c8f6fae07966cfc577fd, 7 | 0x72aed3d59cc889b8ab5d943c703f45c4aeaebf86, 8 | 0x6847dedbb505b0b075b98db8f34f1d62f85e0d9d, 9 | 0xac1d10865db58adba0826d94734129ec8a5741d7, 10 | 0xfe8ef260e63a3388bb3f3174cf080f4423414100, 11 | 0x622b2c840d856181661482a2f7d9a5a41e0f992a, 12 | 0xdfa352daf510611ecc1a4ea0d02d6cae8cc3e2e0, 13 | 0x2746dc307f68a8346e9ab34aec63ab358e6dab82, 14 | 0x9c6d12aaa2fb615467c36b4db6b7e3402fb4f45f, 15 | 0x904367561bae5b96b4332c4c5ec338be097b3240, 16 | 0x16ae23df74c294c4fa4c81b278e7c94c25df8c75, 17 | 0x39fc4cdaa0e79efe7687822f1c0bacc87e1deede, 18 | 0x65c6f8dce120363cad39c85e4e5d643cf8126a83, 19 | 0x019dd0bbd3693f838a544df08ed4e2348d82d7a7, 20 | 0x20621e35f40cb15fe63d1af047413a6c5dc304b8, 21 | 0x80f564f022d22730ef980753f97be31b9fe28aae, 22 | 0xc22d2aeecc87e3907c277457e0152deb01c4a55a, 23 | 0x8761e0dd63d14cf566acf4b730f3540f164b6b56, 24 | 0xf7bad6c588967b9a512a423b21a3856744f8d6d8, 25 | 0xe5d93dd474988b60b354339da3a845d9cd35ec5f, 26 | 0x907f20132c1b706026987d592aa6f5bfe83ff314, 27 | 0xb0febed47971f087695d66ea548599f722cd67a5, 28 | 0x763d5862600d0326e14541c0687e79888999b9ab, 29 | 0x5721e592da25b96edd4d44c632cb841ac7ac47bb, 30 | 0x8cbc229cad155b95b4174647ba705d6a414b8cca, 31 | 0x77f6d9261dd8e45c0e8555aa86ab1648da9b95c3, 32 | 0x8c13278a1d8310cd4c6943eb17fad81765992431, 33 | 0xf062ceb02b73515c603707458cb3ffdf484eeb5a, 34 | 0xe0dd6cd8f2053e34ee34bb4c96c16d7e487896db, 35 | 0x62356e055f2165d0c5e812a7445700727a7934da, 36 | 0x9926df844af52a567929c2b32133ccf1c66dd4fd, 37 | 0x0e3374a49caa66c34eda91517dd5de77a9a4bbee, 38 | 0x49fc5195aad930b4df3ec1e50bb14af3586f0793, 39 | 0x553315181e9d643831936b90d63221a335c793ef, 40 | 0xa5959231dda9b429668bf198aa3580d05eb12f2e, 41 | 0xcd5164c8c8a6c4d0c49e6fa5872435c33ab42af5, 42 | 0x37aa7b1343faa68806f453f45b09dd24a1c8f3d4, 43 | 0x3b412c92e78fe2a323a5fc7cd9d31fa0226ffe14, 44 | 0xe92356c05d03fce81063caaafca1493ef6619445, 45 | 0xdd41883adc2e8db3d3e52ae06ce754ab7563a53b, 46 | 0x349b49ac381d097b12f38747447776a0b4d91bfc, 47 | 0x2198eefc694f5d50634a950e10074a11442f41a8, 48 | 0x42d2184dac8580e6224b361a36ca3a917f7ea67b, 49 | 0x01405ab95de872f1745d6b88a155f00f91a7e93e, 50 | 0x7a03a44fe2c6f98debc35800a6899ccf0869be71, 51 | 0x38ea9aeb4a849f9accde0c17ccfaeeb53ba6a393, 52 | 0x009b740f1670af3a6b62d661f182f15a24a010ae, 53 | 0xd6dda35c4216e343b325ccffe0615accae1f82b0, 54 | 0xea9900f8b55efc75c9bca99377e0f026e537544b, 55 | 0x38148ecc2078da7f65e6233dda28efaf4c51e96f, 56 | 0x100dbb75eab5d98ad65ef16483aaa68e68aafbc5, 57 | 0x9e108a4aaa86d2edc1fe4e393c8dfb0f37433559, 58 | 0x67d33cf1c7c699078f86d517a5a1cd1444a1e85c, 59 | 0x712033cf8adc1e60beadb8b52c8a02f02bffe336, 60 | 0xee9baf220c6afe2e846d1788e53ca5a0d3dcb339, 61 | 0xd082c621b2a166b90ab607b11decc4e038045177, 62 | 0x0a8966d44be0272295814e876791431791440256, 63 | 0x9f9f6768fcd665b0ae17f02601a24a60d1eb5dab, 64 | 0x5fe218e03a4a0ccd7f3eeac9a51fd23c0fc369a1, 65 | 0x4bd120e887cc82285aff8408dc208ed32b132bb3, 66 | 0x8d21e3c8993633546e89cb4a24e5c0a5bc4d97e5, 67 | 0x54ce1d1fc211817f280c5e123eb131d7a0b0beb5, 68 | 0xda4cc246162e13f26864cacf19152695d252e671, 69 | 0xac463695dcdf0aa5c16fd74d6be2b831b0512971, 70 | 0x874f59a177348ae3d540d58ac8c5d23a4e77b1e3, 71 | 0x9c79aef1d586452220ff23fa6b7791fcae1e7edb, 72 | 0x6229cf71deccb5d87767e747f5a5c7738db7e40f, 73 | 0x1bf32181e831dbc781eeb850d58f801495c4c93d, 74 | 0xf085548d7bbf66b8731e1a16bd891cad98cb102a, 75 | 0x8c4531b3f4cdfee616a44eaef29b5b5ee60cdf6d, 76 | 0x486291fea9e334ea87efbac640d7d50ffc7bf8c3, 77 | 0xb9c5b4bf86119fb67cc6f3df9632de2b46648143, 78 | 0x901b68e2099f83dcd6e443c86146969ef837ed37, 79 | 0x4866e8a3683d5b7fca9dabfb9d1188a4a152e976, 80 | 0x66ff831f74b212bc5b787c4401a5bc63812aa592, 81 | 0x4089bc81b3e2e986fbd429e03094faf6685200db, 82 | 0x351faf0b10e7d80fbaa8726b41825a71b9922c95, 83 | 0x23cbe4413e92c10c2edf9e7fab336d35be555d86, 84 | 0x0a70de1a644ced8488ec91b4b463826cbf4b5000, 85 | 0xb18b8edb5d4d1fdff2488714747b609941433ea0, 86 | 0xd8c184f93f11c591da6e321c8288c6dba85da99e, 87 | 0x6ba366de900785e24c605e4a8b5261bb23a97fe0, 88 | 0x97dfdcd746dd32b87aaa2d1a0b292f1a38c77aab, 89 | 0x7089a6315f2642ea1f21f81d5fde64f20f80f75e, 90 | 0x8a11d3d03629fbd5cae5f8ef0743bae86868d040, 91 | 0x240d90a0e69ad8f5de91ddcf5fc83cdc30631f04, 92 | 0x6653acb6d74ac95f750a687177c5c3d6a5f14c4d, 93 | 0x081af29ee22c5b902bc6e9c3ce7fa9de77ce8266, 94 | 0xf81753501afc55776320d567c3af6fcdad1204cf, 95 | 0x945ef6fb870fce7614626070af43a95d482fd99a, 96 | 0x7862e990963405616afd1e08cd369433a87adb3a, 97 | 0x8c245d87069a4e463b954778dad9df0039876a9e, 98 | 0x2b8f14e64b3be49c75c816a6e7d9596b259656ad, 99 | 0x0870f0b06a5009d9f6bdad4d3961b4639bb96750, 100 | 0x123e084fc361a878aef966df784f6b3774084028, 101 | 0x971d97f261abb1ecb9967878f45d7be1b374c81d, 102 | 0xedbaaf0005e47f8cd1e70472e93d33543ce1d38e, 103 | 0x3032967bb88c1289c49d9c8a696e8303ee9b7d88, 104 | 0x3ab79bb7d1e17730ab32a28019d890c02c6197f4, 105 | 0x95a4ac9070051437263a019028e0fb7f0c9d12a0, 106 | 0x8fbaf4d323ba4031f8c1c8614317aefb65c626dc, 107 | 0xc60debaa978d0c215c37859f8b59b07910afe807, 108 | 0x76927e2ccab0084bd19cee74f78b63134b9d181e, 109 | 0x927c78cb90407aa4b4e8e12497022e689cc5cb11, 110 | 0xd80b5f92b713a3ee048a752faf889734a4a1db93, 111 | 0xefca48c0672c6ddd1aedc84d7f54ec1b130cf854, 112 | 0xb23b9c52fc16beb6d44e243ba73936a65fc9b709, 113 | 0x7202cba14b504ac90f49aea2b698530fde9481dd, 114 | 0x782b899b1468051b01581548011660b9b1974cab, 115 | 0x8324de24e3c064af46e0dfaf146d392084e38f0c, 116 | 0x4cd24ceb901f903958f2d205d2fc218efa9f3d72, 117 | 0x79785c9729dc6135ff3060d1d8b08780ceb51668, 118 | 0x17bb4076ab59e0ef20ad5a873ab4b5341bf01b78, 119 | 0xb1b33acc4e491ad70f5837250defa92c3816f539, 120 | 0x6dd3a7060d58c8400e13dee3a63fafc8b1f7a7d5, 121 | 0x4e061bae4a29035c55f62a93b339bc57a9073aaf, 122 | 0x10af69bd4435e2c09eaa447eb89a5d3d407f5f47, 123 | 0x6a49dd97e18106c163a9fc6bca3afeb200656f49, 124 | 0xb5d365aca3e16068ab0eb3b39acf6db62c789267, 125 | 0x38a92bb6290284033b5871477fcdc2dc9a744baa, 126 | 0xf6d61560d1b6e70ab61cc58b0daa5b1dff742351, 127 | 0x18addb4b0ea01ad018417d7de4e37b9b6d35c734, 128 | 0xa3b1af9c33c6fdf7852db56263a44b93fb6f687b, 129 | 0xc2116c5a7533d8619c574ce70312f7c2306c00fd, 130 | 0x8f4e9d43583b7d4b8cff5a027220e36401c12b8e, 131 | 0x29809ede7cbe49c6fe1c1dd40513a7bda438473a, 132 | 0x4931b973792aa349108884a26ad0502690f10cdc, 133 | 0x620703fcd6a3ef6773511ef0bc45a558e5647a8c, 134 | 0x2db04d075c2a96e65d8beef44879ed8d07167384, 135 | 0xcaee01dd455e90ac98d84ae847d22261315db192, 136 | 0xdcbbbd8e22d9945f062909923d4524b9630a32ca, 137 | 0xdde972259b65433c769774e504b312de359c031a, 138 | 0x8d3748096d58695246d8735bba837098b8d3fdd7, 139 | 0x932c23938b9bbfaad91ddaf938c622d315eeef34, 140 | 0x739066ed2d1718fd100cec4d9f347382ec6440dc, 141 | 0xb3fa0fa3682bbafaeb95ef288e841609baa5c68b, 142 | 0x59e5fe8a9637702c6d597c5f1c4ebe3fba747371, 143 | 0x66eb0de38cdbbd157d6940b9542a6ca3c98e9080, 144 | 0xc9d4c2fc8c1401eeaa18c048704934f616ed8002, 145 | 0xca14e2076784f7486cf9c1cb4afe194af96bf3fe, 146 | 0x86834a65e12fc6fa4b564b8d0f6b98a4caf88f50, 147 | 0xe2190203932edc3cf358cb497d165a57a0711f77, 148 | 0xf428237037e5c0acf0751f9b42b83d7b4903d305, 149 | 0x89a94b48b21d3b5f959305643d3c71dc88cc7fbc, 150 | 0xe207214e3bc14154f22a59144ccdff6099ca6567, 151 | 0x1eccd61c9fa53a8d2e823a26cd72a7efd7d0e92e, 152 | 0x3833f8dbdbd6bdcb6a883ff209b869148965b364, 153 | 0x9b422e571eb2cb9837efdc4f9087194d65fb070a, 154 | 0x120c322c0b1477a5c2eeb6f9f06d2c98797f485f, 155 | 0xbe6b82da1815de15c1798f2ea58031d793275ff1, 156 | 0x0c38aa3d82aa58ddd9ff8a0ced05f8995950ae4d, 157 | 0x00e96c34d0d80fc23d730ac0702c86ac746f5534, 158 | 0x34b2cf5894ee3e0537e8f45cae265e68d4df4d8c, 159 | 0x69e753860e3a6bb1fead78ab5b5e573e909108c8, 160 | 0xaad68b51814179146d56e824e9bba8f2b5be4f1e, 161 | 0x22c6a17c9d68d0d8cb057435ab087943a62acbbd, 162 | 0x8c3fdeed9f2f79d294300b4b4d182a814107cf6d, 163 | 0x196d791102f53abad22b8d26f5896c4a48146432, 164 | 0x670be3b80f91b0749146a527e7ffbe1397405e9b, 165 | 0x33d87b186a47beb965fdfa207da1ae00d2d7b812, 166 | 0xa106379acb22df279f1ac3c0299742531b3ba6ee, 167 | 0xb2dd65f58594ce19fcfa7daac204587a3287899b, 168 | 0x525a7eb5dbd784feee9a4667f560406f1ea5367a, 169 | 0x26eeb2711ac35bf1c88b6c0bff75a4dd9e02d636, 170 | 0x6ed8cd17ee5453e17eb731a6b2004af53ea1f26a, 171 | 0xf8bad169cb40a0471d2f30b5772bf2ab7b7c0ee8, 172 | 0xbd131d488746eb914fbb4174b783a1b3d77f25e2, 173 | 0x2764281f229fd932b93c5bc9bf3ed2f7ebdd8d9c, 174 | 0x2707bcc067112cd420159b667c47f1dbfc858df2, 175 | 0xb93147f598f8fc20f9cd6276e56b49dfb608eb16, 176 | 0x22b0ac0be4633f57fa4457268ad365c11227e172, 177 | 0xa87d8906eabb8dfb38cc2370a36afaf013d51857, 178 | 0xb904c6901359606d7954bc0dcaaabad3ac33d6c4, 179 | 0x96921dd0a92ed047954d977e4363ec15a0b807d8, 180 | 0x1f4b0a926e0a1b492bb17bfdb8647d658701b08c, 181 | 0x597aa8e340582363f74acc894b51447a2b7d040c, 182 | 0x2f16cc0f50b1e7088177f71ab02985d4daaf7a03, 183 | 0xd4f756dadd2eb06bd14066a882f611636bf16129, 184 | 0xf59fd77afe562aa4e5b66da6e7a67b4651fac538, 185 | 0x1f81da5cc554766d3626dae05bdc6515522bb00f, 186 | 0x8f53518d306cd730652b166005aaaa1f1dd0c137, 187 | 0x18cb0c10e271fa2daca76fb03afe0fc02e83ab74, 188 | 0x9ea60ba8a75f47757f516fb1b8d48d848504a26c, 189 | 0x5ad003a9a02037b196362784f4a53e72198a0a47, 190 | 0xec9e1351c0dcc363b4d027f93ef196b023f0794f, 191 | 0x95dc704ddaa7708f7c8ec220c3f19fdfd81ae708, 192 | 0xf60bddbf39c3e60a50b328230b851a211db134c7, 193 | 0xa55638362ceda5245fa87471ac80189e8d7ad5c4, 194 | 0x82d99d98115565d2a58e99fc53704cee56e3c0de, 195 | 0x71a93ff96b59b96d2caf474b9a79a51f5b043dc3, 196 | 0x43ffdc79fbcedf718a19ad7d7eb2a705696546e4, 197 | 0xfd5a044f5211b1054d6e18b9cb6ece538ec3057f, 198 | 0x8a9354835138d2367923622f27d6c377546175e0, 199 | 0x609723885766759aaf726c241afaa3fb82a3580a, 200 | 0xcc979952371ba7873983b3fadd38649496d206af, 201 | 0xd4bccbde6dc6c922edd96fa1bfa82f03454fbfd8, 202 | 0x7fd44298916ebcb7ffd80bc2b44c918e5333ec3d, 203 | 0xf2787ca2de0b2d20c6cc9bc1ef3519d7eeba47ae, 204 | 0xc285d754df88d0283adbee7027bdb38f3a38adb6, 205 | 0x81c831e3ed24d4b97585c1a371c7aabeba839bb4, 206 | 0xdba4fd71bcd12a9188a365fccc796c65381b49f1, 207 | 0x071b572c7d6b423973078ae08981d944961775f4, 208 | 0x056d01e62c5b2568517d3af63447076e1456998c, 209 | 0x1b3f224bf8fdb4c23fbd6056fe062ffd456b09d6, 210 | 0x91d7833bf076eb16d51f5bff598ad42f4e79bb51, 211 | 0x92a898d46f19719c38126a8a3c27867ae2cee596, 212 | 0xc24b1a1a4cde1c628202a345708653334d30f05f, 213 | 0x31e156678b8f96005f14f91676bef642c28ad06c, 214 | 0x8d12a197cb00d4747a1fe03395095ce2a5cc6819, 215 | 0x74f9d4f1f58686fb8725c29f9ab2cdd2913dd1f0, 216 | 0x2c21e83a76b2dea9fb85885257eb1ac3f82da450, 217 | 0x34bd23da4689788dfa8e6521f979f5eaa3831b3e, 218 | 0xef3c3dfb36ae7c32f509ec1676659ff86ab3f394, 219 | 0x666d77e5b67ddb1f861b3485b70c60b5256a3aa8, 220 | 0xe9ebee76a902b6536c0bddf79ac4c4343e5bdbfb, 221 | 0x8038339ca5c71a11eb321986bf7acd1282da84d5, 222 | 0xad032694466f2b56b11e8258747af91bf72ed791, 223 | 0xf697eeca8130513cc59b1a3ebcc098ba5c10d80c, 224 | 0x4ea5fb82a9ad7cae9bc4357ee0368eb73f9de17d, 225 | 0x00c5a971dafa5e8e2dcff0af9571e2dbe4f4a0bf, 226 | 0x2f2862e9818ca4ada4ae69e700e3a806236d997e, 227 | 0xc7075ed6bf05549ed8e0d6b11f58721040ab4d05, 228 | 0xc9dd260b16b9402e6244f34a8c52dfdcd6eb9113, 229 | 0xce6d2f7b55eb3066c14b4047b2017d7000697e14, 230 | 0x73aa55afb6f0258b2e8b723b7598eb3b3a8312ed, 231 | 0x76bf705e423ccc18f699f275c7bc2d506090acda, 232 | 0x68f521a871ddd406a57121bf7246c7574bc3cb62, 233 | 0x41bacae05437a3fe126933e57002ae3f129aa079, 234 | 0xcc8f79950bfe3d292608321351c4d45d497a7c6d, 235 | 0x63eed78361018f43cb46fa48a6122e004d963a2d, 236 | 0x3a02b24bf0bfc7b9c485eba503fd73e751ca0b00, 237 | 0x4062b2b12b354bf8f9398685a29e1f5e534f95a8, 238 | 0x286f0171bb5c940314c83c9cd9e355106a7de7e7, 239 | 0x33a96b5993f65e26ad4f51b3d3ccea513271b923, 240 | 0xce838139e92080e8e1266092bafb64d1e43dd139, 241 | 0x133f9d96901e3727596660d32dbdcbcf57c03dbb, 242 | 0xfa60838ac163b8b44108b92c8b64c7c15fd5d1bd, 243 | 0x4fc15720ac8dfbdd20aa47c518cf26ee2fc39ef7, 244 | 0x4e89c8b0cefe3b3239a168325baacec8c1cee218, 245 | 0x450f5f260559e4861cd703b61bb9e5e0c622ef29, 246 | 0x4ecec940deabb584b43f21c91b1238bd43b5e7eb, 247 | 0x9e3e93fc983d84e6397e35bc5cd3c33783041f35, 248 | 0xb8c5bb7d01294f5148488786ac9578e97ffb95fd, 249 | 0xcab61b858a344ad4c878c60de62b2019d3e00aed, 250 | 0x7a82fb208303aab2369105ba03c9c0146ac8fd86, 251 | 0x607e4d73800332bf29ff7729a8b519cce7d54202, 252 | 0xcb37a8f731b4db7238d4f4683824ca54decdbce2, 253 | 0x4a789c070535b51ef86628dc995813c6c2eddb32, 254 | 0x70540666f778b7111d62cd6a9ea8a35f44d4076a, 255 | 0xdee028822bc40ac1d6bb042c2378796efdce8bd9, 256 | 0xab697f58c8e9525df5337052cd71d1ba6fff7306, 257 | 0x56532086984e9f6e806cd20c0d241f97b8fe18d9, 258 | 0x4c5aa30f63955ed90e2c47b401dd67e7a409735b, 259 | 0x2beea5ee986b5fd028f4fa64f832020f3ca35aad, 260 | 0x58562bcb3ccaa3b773053eb61d700c7a20884b50, 261 | 0x801f28a3c0813395ed6f2a7216fa16c88917a542, 262 | 0x3e2021221b0bb5e2d1ceda9f024ed9804b055708, 263 | 0x4212fb436bb377cc3c10f5e2f16894daefa68f2f, 264 | 0xa72c38bd35ef32267b6a9315ad3cf36e2624767e, 265 | 0x53dd1454af6da0c6c80230c6f4ba06352b46fd7c, 266 | 0x6aaf4ca8f73f28acf8ae1d8d90465cc9026fd415, 267 | 0xebb62cf8e22c884b1b28c6fa88fbbc17938aa787, 268 | 0x7bacdb8b1aa9390a1ceee85c1e26a9f03671fd43, 269 | 0x0644c1a6d61e0994c73c86febdf64ec8432b1013, 270 | 0x0bbde14072a835903f2f687dd64dd990680e967f, 271 | 0xd07d158d62b1f2c657ceb7331b75de868b83dccf, 272 | 0x60fee5affc433a53e8751fe55fc4bd1794f5ff51, 273 | 0x13148f7a44f78415ba50e8d6a9d7f97ad5a5aff6, 274 | 0x2b5dbb6907816e4ab6182bf58dae543b48ff319a, 275 | 0x7d2330b2906bfdf1c192fc8b750081a2ee6f8328, 276 | 0x57d0c97313bef304f99f338285de6fdce26d0486, 277 | 0xd82ab13c28b70d2a904e03faf15c48df1c970926, 278 | 0x26444302825fbe13d5a5d1c7e7c5759ca67f490d, 279 | 0x32035a7f88db2ae8b66e84fc9333084fa27d88fb, 280 | 0x388e431d70958a5f338a94081d244ec82165ba9b, 281 | 0x7401d01a9bcf8ee5d44b57f255d8a7c14a12d80c, 282 | 0x0fac3479b2eacb02445932e5b65122d6ffab3db8, 283 | -------------------------------------------------------------------------------- /test/cloneProofToken.js: -------------------------------------------------------------------------------- 1 | require('../scripts/jsHelpers.js') 2 | 3 | const fs = require('fs') 4 | const csv = require('csv-parser') 5 | const json2csv = require('json2csv') 6 | const ethereumAddress = require('ethereum-address') 7 | 8 | const BigNumber = web3.BigNumber 9 | let chai = require('chai') 10 | var chaiAsPromised = require('chai-as-promised') 11 | var chaiStats = require('chai-stats') 12 | var chaiBigNumber = require('chai-bignumber')(BigNumber) 13 | chai.use(chaiAsPromised).use(chaiBigNumber).use(chaiStats).should() 14 | 15 | import moment from 'moment' 16 | 17 | import { 18 | ether 19 | } from '../scripts/testConfig.js' 20 | 21 | import { 22 | getAddress, 23 | getTxnReceiptTopics, 24 | latestTime, 25 | increaseTime 26 | } from '../scripts/helpers.js' 27 | 28 | import { 29 | cloneToken, 30 | getTokenBalance, 31 | getTotalSupply, 32 | transferToken, 33 | baseUnits 34 | } from '../scripts/tokenHelpers.js' 35 | 36 | import { 37 | buyTokens, 38 | enableTransfers 39 | } from '../scripts/tokenSaleHelpers.js' 40 | 41 | import { 42 | transferControl 43 | } from '../scripts/controlHelpers.js' 44 | 45 | import { 46 | decodeEthereumAddress 47 | } from '../scripts/utils.js' 48 | 49 | const assert = chai.assert 50 | const should = chai.should() 51 | const expect = chai.expect 52 | 53 | const ProofToken = artifacts.require('./ProofToken.sol') 54 | const TokenSale = artifacts.require('./TokenSale.sol') 55 | const TokenFactory = artifacts.require('./TokenFactory.sol') 56 | 57 | contract('cloneProofToken', ([fund, buyer, buyer2, wallet]) => { 58 | let tokenSale 59 | let proofToken 60 | let proofTokenFactory 61 | let proofTokenFactoryAddress 62 | 63 | let proofTokenAddress 64 | let tokenSaleAddress 65 | let startTime 66 | let endTime 67 | let contractUploadTime 68 | 69 | let txnReceipt 70 | let topics 71 | let clonedTokenAddress 72 | let clonedToken 73 | 74 | beforeEach(async function() { 75 | 76 | proofTokenFactory = await TokenFactory.new() 77 | proofTokenFactoryAddress = await getAddress(proofTokenFactory) 78 | 79 | proofToken = await ProofToken.new( 80 | proofTokenFactoryAddress, 81 | '0x0', 82 | 0, 83 | 'Proof Token', 84 | 'PRFT' 85 | ) 86 | 87 | proofTokenAddress = await getAddress(proofToken) 88 | 89 | contractUploadTime = latestTime() 90 | startTime = contractUploadTime.add(1, 'day').unix() 91 | endTime = contractUploadTime.add(1, 'day').unix() 92 | 93 | tokenSale = await TokenSale.new( 94 | proofTokenAddress, 95 | startTime, 96 | endTime 97 | ) 98 | 99 | tokenSaleAddress = await getAddress(tokenSale) 100 | 101 | await transferControl(proofToken, fund, tokenSaleAddress) 102 | await increaseTime(moment.duration(1.01, 'day')) 103 | }) 104 | 105 | describe('Cloning: ', function () { 106 | beforeEach(async function() { 107 | 108 | let config = { 109 | name: 'Proof Token', 110 | symbol: 'PRFT2', 111 | block: 0 112 | } 113 | 114 | await buyTokens(tokenSale, buyer, 1 * ether) 115 | 116 | txnReceipt = await cloneToken(proofToken, fund, config) 117 | topics = getTxnReceiptTopics(txnReceipt) 118 | clonedTokenAddress = decodeEthereumAddress(topics[0].parameters[0]) 119 | clonedToken = await ProofToken.at(clonedTokenAddress) 120 | }) 121 | 122 | it('token should be cloneable', async function () { 123 | let validAddress = ethereumAddress.isAddress(clonedTokenAddress) 124 | validAddress.should.be.true 125 | }) 126 | 127 | it('cloned token should return identical balances', async function() { 128 | let balance = await getTokenBalance(proofToken, buyer) 129 | let clonedBalance = await getTokenBalance(clonedToken, buyer) 130 | clonedBalance.should.be.equal(balance) 131 | }) 132 | 133 | it('should return identical total supply', async function() { 134 | let totalSupply = await getTotalSupply(proofToken) 135 | let clonedTotalSupply = await getTotalSupply(clonedToken) 136 | clonedTotalSupply.should.be.equal(totalSupply) 137 | }) 138 | 139 | it('should be pluggable and buyable via a new tokensale instance', async function() { 140 | 141 | let clonedTokenSale = await TokenSale.new( 142 | clonedTokenAddress, 143 | startTime, 144 | endTime 145 | ) 146 | 147 | let clonedTokenSaleAddress = await getAddress(clonedTokenSale) 148 | await transferControl(clonedToken, fund, clonedTokenSaleAddress) 149 | 150 | let initialTokenBalance = await getTokenBalance(clonedToken, buyer2) 151 | await buyTokens(clonedTokenSale, buyer2, 1 * ether) 152 | 153 | let tokenBalance = await getTokenBalance(clonedToken, buyer2) 154 | let balanceIncrease = (tokenBalance - initialTokenBalance) 155 | balanceIncrease = await baseUnits(clonedToken, balanceIncrease) 156 | expect(balanceIncrease).almost.equal(13.3689839572) 157 | }) 158 | 159 | it('cloned tokens should be transferable', async function() { 160 | 161 | let clonedTokenSale = await TokenSale.new( 162 | clonedTokenAddress, 163 | startTime, 164 | endTime 165 | ) 166 | 167 | let clonedTokenSaleAddress = await getAddress(clonedTokenSale) 168 | await transferControl(clonedToken, fund, clonedTokenSaleAddress) 169 | await enableTransfers(clonedTokenSale, fund) 170 | let buyer1InitialBalance = await getTokenBalance(clonedToken, buyer) 171 | let buyer2InitialBalance = await getTokenBalance(clonedToken, buyer2) 172 | 173 | await transferToken(clonedToken, buyer, buyer2, 100) 174 | 175 | let buyer1Balance = await getTokenBalance(clonedToken, buyer) 176 | let buyer2Balance = await getTokenBalance(clonedToken, buyer2) 177 | 178 | buyer1Balance.should.be.equal(buyer1InitialBalance - 100) 179 | buyer2Balance.should.be.equal(buyer2InitialBalance + 100) 180 | }) 181 | }) 182 | }) 183 | -------------------------------------------------------------------------------- /test/initialState.js: -------------------------------------------------------------------------------- 1 | const BigNumber = web3.BigNumber 2 | let chai = require('chai') 3 | var chaiAsPromised = require('chai-as-promised') 4 | var chaiStats = require('chai-stats') 5 | var chaiBigNumber = require('chai-bignumber')(BigNumber) 6 | chai.use(chaiAsPromised).use(chaiBigNumber).use(chaiStats).should() 7 | 8 | import { TOKENS_ALLOCATED_TO_PROOF } from '../scripts/testConfig.js' 9 | import { getAddress, latestTime } from '../scripts/helpers.js' 10 | import { baseUnits, mintToken } from '../scripts/tokenHelpers.js' 11 | import { transferOwnership } from '../scripts/ownershipHelpers.js' 12 | import { transferControl } from '../scripts/controlHelpers.js' 13 | import { getPrice } from '../scripts/tokenSaleHelpers.js' 14 | 15 | const assert = chai.assert 16 | const should = chai.should() 17 | const expect = chai.expect 18 | 19 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 20 | const ProofToken = artifacts.require('./ProofToken.sol') 21 | const TokenSale = artifacts.require('./TokenSale.sol') 22 | 23 | contract('Crowdsale', (accounts) => { 24 | let fund = accounts[0] 25 | let tokenSale 26 | let tokenSaleAddress 27 | let proofToken 28 | let proofPresaleToken 29 | let proofPresaleTokenAddress 30 | let proofTokenAddress 31 | let sender = accounts[1] 32 | let proofWalletAddress = accounts[9] 33 | 34 | let startTime 35 | let endTime 36 | let contractUploadTime 37 | 38 | let proofMultiSig = '0x99892ac6da1b3851167cb959fe945926bca89f09' 39 | 40 | beforeEach(async function() { 41 | 42 | proofPresaleToken = await ProofPresaleToken.new() 43 | proofPresaleTokenAddress = await getAddress(proofPresaleToken) 44 | 45 | proofToken = await ProofToken.new( 46 | '0x0', 47 | '0x0', 48 | 0, 49 | 'Proof Token Test', 50 | 'PRFT Test' 51 | ) 52 | 53 | proofTokenAddress = await getAddress(proofToken) 54 | 55 | contractUploadTime = latestTime() 56 | startTime = contractUploadTime.add(1, 'day').unix() 57 | endTime = contractUploadTime.add(31, 'day').unix() 58 | 59 | 60 | tokenSale = await TokenSale.new( 61 | proofTokenAddress, 62 | startTime, 63 | endTime) 64 | 65 | tokenSaleAddress = await getAddress(tokenSale) 66 | }) 67 | 68 | // it('should be ended only after end', async function() { 69 | // let ended = await tokenSale.hasEnded() 70 | // ended.should.equal(false) 71 | // }) 72 | 73 | describe('Initial State', function () { 74 | 75 | beforeEach(async function() { 76 | transferControl(proofToken, fund, tokenSaleAddress) 77 | }) 78 | 79 | it('should initially set the multisig', async function() { 80 | let multisig = await tokenSale.proofMultiSig.call() 81 | multisig.should.be.equal(proofMultiSig) 82 | }) 83 | 84 | it('should initially be linked to the Proof token', async function() { 85 | let token = await tokenSale.proofToken.call() 86 | token.should.be.equal(proofTokenAddress) 87 | }) 88 | 89 | it('Token base price should be equal to 0.0748 ether', async function() { 90 | let price = await getPrice(tokenSale) 91 | expect(price).almost.equal(0.85 * 0.088) 92 | }) 93 | }) 94 | }) 95 | -------------------------------------------------------------------------------- /test/ownable.js: -------------------------------------------------------------------------------- 1 | const BigNumber = web3.BigNumber 2 | let chai = require('chai') 3 | var chaiAsPromised = require('chai-as-promised') 4 | var chaiStats = require('chai-stats') 5 | var chaiBigNumber = require('chai-bignumber')(BigNumber) 6 | chai.use(chaiAsPromised).use(chaiBigNumber).use(chaiStats).should() 7 | 8 | import { getAddress, 9 | expectInvalidOpcode } from '../scripts/helpers.js' 10 | 11 | import { transferOwnership } from '../scripts/ownershipHelpers.js' 12 | 13 | const assert = chai.assert 14 | const should = chai.should() 15 | const expect = chai.expect 16 | 17 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 18 | const ProofToken = artifacts.require('./ProofToken.sol') 19 | const TokenSale = artifacts.require('./TokenSale.sol') 20 | 21 | contract('Crowdsale', (accounts) => { 22 | let fund = accounts[0] 23 | let tokenSale 24 | let proofToken 25 | let proofPresaleToken 26 | let proofPresaleTokenAddress 27 | let proofTokenAddress 28 | let receiver = accounts[2] 29 | let hacker1 = accounts[3] 30 | let hacker2 = accounts[4] 31 | let wallet = accounts[5] 32 | let proofWalletAddress = accounts[9] 33 | 34 | let startBlock 35 | let endBlock 36 | 37 | beforeEach(async function() { 38 | 39 | startBlock = web3.eth.blockNumber + 10 40 | endBlock = web3.eth.blockNumber + 20 41 | 42 | proofPresaleToken = await ProofPresaleToken.new() 43 | proofPresaleTokenAddress = await getAddress(proofPresaleToken) 44 | 45 | proofToken = await ProofToken.new(proofPresaleTokenAddress, proofWalletAddress) 46 | proofTokenAddress = await getAddress(proofToken) 47 | 48 | proofToken = await ProofToken.new( 49 | '0x0', 50 | '0x0', 51 | 0, 52 | 'Proof Token', 53 | 18, 54 | 'PRFT', 55 | true) 56 | 57 | tokenSale = await TokenSale.new( 58 | proofTokenAddress, 59 | startBlock, 60 | endBlock) 61 | }) 62 | 63 | describe('Ownership', function () { 64 | it('should initially belong to contract caller', async function() { 65 | let owner = await tokenSale.owner.call() 66 | assert.equal(owner, fund) 67 | }) 68 | 69 | it('should be transferable to another account', async function() { 70 | let owner = await tokenSale.owner.call() 71 | await transferOwnership(tokenSale, owner, receiver) 72 | let newOwner = await tokenSale.owner.call() 73 | assert.equal(newOwner, receiver) 74 | }) 75 | 76 | it('should not be transferable by non-owner', async function() { 77 | let owner = await tokenSale.owner.call() 78 | await expectInvalidOpcode(transferOwnership(tokenSale, hacker1, hacker2)) 79 | const newOwner = await tokenSale.owner.call() 80 | assert.equal(owner, newOwner) 81 | }) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/pausable.js: -------------------------------------------------------------------------------- 1 | const BigNumber = web3.BigNumber 2 | let chai = require('chai') 3 | var chaiAsPromised = require('chai-as-promised') 4 | var chaiStats = require('chai-stats') 5 | var chaiBigNumber = require('chai-bignumber')(BigNumber) 6 | chai.use(chaiAsPromised).use(chaiBigNumber).use(chaiStats).should() 7 | 8 | import moment from 'moment' 9 | 10 | import { 11 | ether, 12 | DEFAULT_GAS, 13 | DEFAULT_GAS_PRICE 14 | } from '../scripts/testConfig.js' 15 | 16 | import { 17 | getAddress, 18 | expectInvalidOpcode, 19 | latestTime, 20 | increaseTime 21 | } from '../scripts/helpers.js' 22 | 23 | import { 24 | pause, 25 | unpause 26 | } from '../scripts/pausableHelpers.js' 27 | 28 | import { 29 | getTokenBalance, 30 | baseUnits 31 | } from '../scripts/tokenHelpers.js' 32 | 33 | import { 34 | buyTokens, 35 | enableTransfers, 36 | lockTransers, 37 | enableMasterTransfers, 38 | lockMasterTransfers 39 | } from '../scripts/tokenSaleHelpers.js' 40 | 41 | import { 42 | transferOwnership 43 | } from '../scripts/ownershipHelpers.js' 44 | 45 | import { 46 | transferControl 47 | } from '../scripts/controlHelpers.js' 48 | 49 | const assert = chai.assert 50 | const should = chai.should() 51 | const expect = chai.expect 52 | 53 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 54 | const ProofToken = artifacts.require('./ProofToken.sol') 55 | const TokenSale = artifacts.require('./TokenSale.sol') 56 | 57 | contract('Crowdsale', (accounts) => { 58 | let fund = accounts[0] 59 | let tokenSale 60 | let tokenSaleAddress 61 | let proofToken 62 | let proofPresaleToken 63 | let proofPresaleTokenAddress 64 | let proofTokenAddress 65 | let sender = accounts[1] 66 | let hacker1 = accounts[3] 67 | let wallet = accounts[5] 68 | let proofWalletAddress = accounts[9] 69 | 70 | let startTime 71 | let endTime 72 | let contractUploadTime 73 | 74 | beforeEach(async function() { 75 | 76 | 77 | proofPresaleToken = await ProofPresaleToken.new() 78 | proofPresaleTokenAddress = await getAddress(proofPresaleToken) 79 | 80 | proofToken = await ProofToken.new( 81 | '0x0', 82 | '0x0', 83 | 0, 84 | 'Proof Token Test', 85 | 'PRFT Test' 86 | ) 87 | 88 | proofTokenAddress = await getAddress(proofToken) 89 | 90 | contractUploadTime = latestTime() 91 | startTime = contractUploadTime.add(1, 'day').unix() 92 | endTime = contractUploadTime.add(31, 'day').unix() 93 | 94 | tokenSale = await TokenSale.new( 95 | proofTokenAddress, 96 | startTime, 97 | endTime 98 | ) 99 | 100 | tokenSaleAddress = await getAddress(tokenSale) 101 | }) 102 | 103 | describe('Pause', function () { 104 | after(async function() { 105 | let crowdsalePaused = await tokenSale.paused.call() 106 | let owner = await tokenSale.owner.call() 107 | if (crowdsalePaused) { 108 | await unpause(tokenSale, owner) 109 | } 110 | }) 111 | 112 | beforeEach(async function() { 113 | await transferControl(proofToken, fund, tokenSaleAddress) 114 | await enableTransfers(tokenSale, fund) 115 | await increaseTime(moment.duration(1.01, 'day')) 116 | }) 117 | 118 | it('can be paused and unpaused by the owner', async function() { 119 | let crowdsalePaused = await tokenSale.paused.call() 120 | let owner = await tokenSale.owner.call() 121 | 122 | if (crowdsalePaused) { 123 | await unpause(tokenSale, owner) 124 | crowdsalePaused = await tokenSale.paused.call() 125 | expect(crowdsalePaused).to.be.false 126 | } 127 | 128 | await pause(tokenSale, owner) 129 | crowdsalePaused = await tokenSale.paused.call() 130 | expect(crowdsalePaused).to.be.true 131 | 132 | await unpause(tokenSale, owner) 133 | crowdsalePaused = await tokenSale.paused.call() 134 | expect(crowdsalePaused).to.be.false 135 | }) 136 | 137 | it('can not be paused non-owner', async function() { 138 | let crowdsalePaused = await tokenSale.paused.call() 139 | let owner = await tokenSale.owner.call() 140 | 141 | // we initially unpause the contract before we carry out the test 142 | if (crowdsalePaused) { 143 | await unpause(tokenSale, owner) 144 | crowdsalePaused = await tokenSale.paused.call() 145 | expect(crowdsalePaused).to.be.false 146 | } 147 | 148 | await expectInvalidOpcode(pause(tokenSale, hacker1)) 149 | crowdsalePaused = await tokenSale.paused.call() 150 | expect(crowdsalePaused).to.be.false 151 | }) 152 | 153 | it('can not be unpaused non-owner', async function() { 154 | let crowdsalePaused = await tokenSale.paused.call() 155 | let owner = await tokenSale.owner.call() 156 | 157 | // we initially unpause the contract before we carry out the test 158 | if (!crowdsalePaused) { 159 | await pause(tokenSale, owner) 160 | crowdsalePaused = await tokenSale.paused.call() 161 | expect(crowdsalePaused).to.be.true 162 | } 163 | 164 | await expectInvalidOpcode(pause(tokenSale, hacker1)) 165 | crowdsalePaused = await tokenSale.paused.call() 166 | expect(crowdsalePaused).to.be.true 167 | }) 168 | 169 | it('buying tokens should not be possible if the contract is paused', async function() { 170 | await pause(tokenSale, fund) 171 | let initialBalance = await getTokenBalance(proofToken, sender) 172 | 173 | let params = { from: sender, gas: DEFAULT_GAS, gasPrice: DEFAULT_GAS_PRICE } 174 | await expectInvalidOpcode(tokenSale.buyTokens(1, params)) 175 | 176 | let balance = await getTokenBalance(proofToken, sender) 177 | balance.should.be.equal(initialBalance) 178 | }) 179 | 180 | it('buying tokens should be possible if the contract is paused and unpaused', async function() { 181 | let initialBalance = await getTokenBalance(proofToken, sender) 182 | 183 | await buyTokens(tokenSale, sender, 1 * ether) 184 | await pause(tokenSale, fund) 185 | await unpause(tokenSale, fund) 186 | await buyTokens(tokenSale, sender, 1 * ether) 187 | 188 | let balance = await getTokenBalance(proofToken, sender) 189 | let balanceIncrease = await baseUnits(proofToken, balance - initialBalance) 190 | expect(balanceIncrease).to.almost.equal(26.737967914) 191 | }) 192 | }) 193 | }) 194 | 195 | -------------------------------------------------------------------------------- /test/tokenSale.js: -------------------------------------------------------------------------------- 1 | const BigNumber = web3.BigNumber 2 | let chai = require('chai') 3 | var chaiAsPromised = require('chai-as-promised') 4 | var chaiStats = require('chai-stats') 5 | var chaiBigNumber = require('chai-bignumber')(BigNumber) 6 | chai.use(chaiAsPromised).use(chaiBigNumber).use(chaiStats).should() 7 | 8 | import moment from 'moment' 9 | import { TOKENS_ALLOCATED_TO_PROOF, ether } from '../scripts/testConfig.js' 10 | import { getAddress, advanceToBlock, expectInvalidOpcode, waitUntilTransactionsMined, latestTime, increaseTime } from '../scripts/helpers.js' 11 | import { baseUnits, mintToken, getTokenBalance, getTotalSupply } from '../scripts/tokenHelpers.js' 12 | import { transferControl } from '../scripts/controlHelpers.js' 13 | import { enableTransfers, buyTokens, finalize, getCap, getPrice, getPriceInWei, getBasePrice, getBasePriceInWei } from '../scripts/tokenSaleHelpers.js' 14 | import { pause, unpause } from '../scripts/pausableHelpers' 15 | 16 | const assert = chai.assert 17 | const should = chai.should() 18 | const expect = chai.expect 19 | 20 | const ProofPresaleToken = artifacts.require('./ProofPresaleToken.sol') 21 | const ProofToken = artifacts.require('./ProofToken.sol') 22 | const TokenSale = artifacts.require('./TokenSale.sol') 23 | 24 | contract('Crowdsale', (accounts) => { 25 | let fund = accounts[0] 26 | let tokenSale 27 | let tokenSaleAddress 28 | let proofToken 29 | let proofPresaleToken 30 | let proofPresaleTokenAddress 31 | let proofTokenAddress 32 | let sender = accounts[1] 33 | let receiver = accounts[2] 34 | let hacker = accounts[3] 35 | let wallet = accounts[5] 36 | let proofWalletAddress = accounts[9] 37 | 38 | let startTime 39 | let endTime 40 | let contractUploadTime 41 | 42 | beforeEach(async function() { 43 | 44 | 45 | proofPresaleToken = await ProofPresaleToken.new() 46 | proofPresaleTokenAddress = await getAddress(proofPresaleToken) 47 | 48 | proofToken = await ProofToken.new( 49 | '0x0', 50 | '0x0', 51 | 0, 52 | 'Proof Token Test', 53 | 'PRFT Test' 54 | ) 55 | 56 | proofTokenAddress = await getAddress(proofToken) 57 | 58 | contractUploadTime = latestTime() 59 | startTime = contractUploadTime.add(1, 'day').unix() 60 | endTime = contractUploadTime.add(31, 'day').unix() 61 | 62 | tokenSale = await TokenSale.new( 63 | proofTokenAddress, 64 | startTime, 65 | endTime) 66 | 67 | tokenSaleAddress = await getAddress(tokenSale) 68 | }) 69 | 70 | describe('Token Information', async function() { 71 | beforeEach(async function() { 72 | await transferControl(proofToken, fund, tokenSaleAddress) 73 | await enableTransfers(tokenSale, fund) 74 | await increaseTime(moment.duration(1.1, 'day')) 75 | }) 76 | 77 | it('should return the correct token supply', async function() { 78 | await buyTokens(tokenSale, sender, 1 * ether) 79 | 80 | let supply = await getTotalSupply(proofToken) 81 | let tokenSaleDisplaySupply = await getTotalSupply(tokenSale) 82 | supply.should.be.equal(tokenSaleDisplaySupply) 83 | }) 84 | 85 | // the token balance of each token holder can also be displayed via the token sale contract - by routing towards the proof token balanceOf() method 86 | // we verify both balances are equal 87 | it('should return the correct token balance (tokenSale.balanceOf must be equal to proofToken.balanceOf)', async function() { 88 | await buyTokens(tokenSale, sender, 1 * ether) 89 | let senderBalance = await getTokenBalance(proofToken, sender) 90 | let senderDisplayBalance = await getTokenBalance(tokenSale, sender) 91 | senderBalance.should.be.equal(senderDisplayBalance) 92 | }) 93 | }) 94 | 95 | describe('Initial State', function () { 96 | beforeEach(async function() { 97 | await transferControl(proofToken, fund, tokenSaleAddress) 98 | await increaseTime(moment.duration(1.01, 'day')) 99 | }) 100 | 101 | it('should initially set the multisig', async function() { 102 | let tokenSaleWallet = await tokenSale.proofMultiSig.call() 103 | tokenSaleWallet.should.be.equal('0x99892ac6da1b3851167cb959fe945926bca89f09') 104 | }) 105 | 106 | it('should initially be linked to the Proof token', async function() { 107 | let tokenSaleToken = await tokenSale.proofToken.call() 108 | tokenSaleToken.should.be.equal(proofTokenAddress) 109 | }) 110 | 111 | it('Initial Price should be equal to 0.0748 ether', async function() { 112 | let price = await getPrice(tokenSale) 113 | expect(price).almost.equal(0.85 * 0.088) 114 | }) 115 | 116 | it('Base Price should be equal to 0.088 ether', async function() { 117 | let price = await getBasePrice(tokenSale) 118 | price.should.be.equal(0.088) 119 | }) 120 | 121 | it('cap should be equal to remaining tokens adjusted to multiplier', async function() { 122 | let cap = await getCap(tokenSale) 123 | cap.should.be.equal(1068644) 124 | }) 125 | }) 126 | 127 | describe('Finalized state', function () { 128 | beforeEach(async function() { 129 | 130 | contractUploadTime = latestTime() 131 | startTime = contractUploadTime.add(1, 'day').unix() 132 | endTime = contractUploadTime.add(31, 'day').unix() 133 | proofPresaleToken = await ProofPresaleToken.new() 134 | proofPresaleTokenAddress = await getAddress(proofPresaleToken) 135 | 136 | proofToken = await ProofToken.new( 137 | '0x0', 138 | '0x0', 139 | 0, 140 | 'Proof Token Test', 141 | 'PRFT Test' 142 | ) 143 | 144 | proofTokenAddress = await getAddress(proofToken) 145 | 146 | tokenSale = await TokenSale.new( 147 | proofTokenAddress, 148 | startTime, 149 | endTime) 150 | 151 | tokenSaleAddress = await getAddress(tokenSale) 152 | transferControl(proofToken, fund, tokenSaleAddress) 153 | }) 154 | 155 | it('should initially not be finalized', async function() { 156 | let finalized = await tokenSale.finalized.call() 157 | finalized.should.be.false 158 | }) 159 | 160 | it('should not be finalizeable if the token sale is not paused', async function() { 161 | await expectInvalidOpcode(finalize(tokenSale, fund)) 162 | let finalized = await tokenSale.finalized.call() 163 | finalized.should.be.false 164 | }) 165 | 166 | it('should be finalizeable if the token sale is paused', async function() { 167 | await pause(tokenSale, fund) 168 | await finalize(tokenSale, fund) 169 | let finalized = await tokenSale.finalized.call() 170 | finalized.should.be.true 171 | }) 172 | 173 | it('should not be finalizeable if the token sale is paused/unpaused', async function() { 174 | await pause(tokenSale, fund) 175 | await unpause(tokenSale, fund) 176 | await expectInvalidOpcode(finalize(tokenSale, fund)) 177 | let finalized = await tokenSale.finalized.call() 178 | finalized.should.be.false 179 | }) 180 | 181 | it('should not be finalizeable by non-owner', async function() { 182 | await pause(tokenSale, fund) 183 | await expectInvalidOpcode(finalize(tokenSale, hacker)) 184 | let finalized = await tokenSale.finalized.call() 185 | finalized.should.be.false 186 | }) 187 | 188 | }) 189 | }) 190 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | var config = require('./config') 2 | 3 | require('babel-register') 4 | require('babel-polyfill') 5 | 6 | const LightWalletProvider = require('@digix/truffle-lightwallet-provider') 7 | 8 | module.exports = { 9 | networks: { 10 | development: { 11 | host: 'localhost', 12 | port: 8545, 13 | network_id: '*', 14 | gas: config.constants.MAX_GAS, 15 | from: '0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39' // testprc main account here 16 | }, 17 | ethereum: { 18 | provider: new LightWalletProvider({ 19 | keystore: '/Users/davidvanisacker/.sigmate/sigmate-v3-tokensale-mainnet.json', 20 | password: 'popcorn123!', 21 | rpcUrl: config.infura.ethereum 22 | }), 23 | network_id: '1', 24 | gas: config.constants.MAX_GAS, 25 | gasPrice: config.constants.DEFAULT_GAS_PRICE 26 | }, 27 | ropsten: { 28 | provider: new LightWalletProvider({ 29 | keystore: '/Users/davidvanisacker/.sigmate/sigmate-v3-tokensale-ropsten.json', 30 | password: 'popcorn123!', 31 | rpcUrl: config.infura.ropsten 32 | }), 33 | network_id: '3' 34 | }, 35 | rinkeby: { 36 | provider: new LightWalletProvider({ 37 | keystore: '/Users/davidvanisacker/.sigmate/sigmate-v3-tokensale-rinkeby.json', 38 | password: 'popcorn123!', 39 | rpcUrl: config.infura.rinkeby 40 | }), 41 | network_id: '4' 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './app/javascripts/app.js', 6 | output: { 7 | path: path.resolve(__dirname, 'build'), 8 | filename: 'app.js' 9 | }, 10 | plugins: [ 11 | // Copy our app's index.html to the build folder. 12 | new CopyWebpackPlugin([ 13 | { from: './app/index.html', to: "index.html" } 14 | ]) 15 | ], 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.css$/, 20 | use: [ 'style-loader', 'css-loader' ] 21 | } 22 | ], 23 | loaders: [ 24 | { test: /\.json$/, use: 'json-loader' }, 25 | { 26 | test: /\.js$/, 27 | exclude: /(node_modules|bower_components)/, 28 | loader: 'babel-loader', 29 | query: { 30 | presets: ['es2015'], 31 | plugins: ['transform-runtime'] 32 | } 33 | } 34 | ] 35 | } 36 | } 37 | --------------------------------------------------------------------------------