├── .gitignore ├── LICENSE ├── README.md ├── contracts ├── MetaCoin.sol ├── Migrations.sol └── SafeMath.sol ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── package.json ├── test └── metacoin.js ├── truffle-config.js └── yarn.lock /.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 (https://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 | # next.js build output 61 | .next 62 | 63 | # compiled contracts 64 | .0x-artifacts 65 | artifacts 66 | 67 | # coverage/profiler report 68 | coverage 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 ZeroEx Intl. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dev-tools-truffle-example 2 | 3 | An example truffle project showing how to use 0x dev tools with the Truffle framework. 4 | 5 | - [@0x/sol-trace](https://www.sol-trace.com) 6 | - [@0x/sol-profiler](https://www.sol-profiler.com) 7 | - [@0x/sol-coverage](https://www.sol-coverage.com) 8 | - [@0x/sol-compiler](https://www.sol-compiler.com) 9 | 10 | ## Intro 11 | 12 | First run `yarn` or `npm i` to install the dependencies. 13 | 14 | This project contains two contracts. `MetaCoin` and `SafeMath`. `MetaCoin` is a coin-like contract where the owner of it initially has 1 token. `SafeMath` is called by `MetaCoin` to perform addition and substraction safely. 15 | 16 | ## Tests 17 | 18 | There is a single test that tries to send 2 tokens from the owner. Owner doesn't have enough tokens so it reverts. 19 | 20 | Now we're ready to run the tests: 21 | 22 | ```bash 23 | yarn test 24 | ``` 25 | 26 | They will fail and we'll get an error: 27 | 28 | ```bash 29 | Error: Transaction: 0x012d87cbbd7799ef361872571e75d37b7e53192161a1955ef5d6c97f1531d613 exited with an error (status 0). 30 | Please check that the transaction: 31 | - satisfies all conditions set by Solidity `require` statements. 32 | - does not trigger a Solidity `revert` statement. 33 | ``` 34 | 35 | Not very helpful. Now let's run it using [@0x/sol-trace](http://sol-trace.com) 36 | 37 | ## Sol-trace 38 | 39 | ```bash 40 | yarn trace 41 | ``` 42 | 43 | ```bash 44 | dev-tools-truffle-example/contracts/SafeMath.sol:12:8: 45 | require(b <= a) 46 | dev-tools-truffle-example/contracts/MetaCoin.sol:13:25: 47 | SafeMath(safeMath).sub(balances[msg.sender], amount) 48 | ``` 49 | 50 | That's better. Now we don't need to check all the require and revert statements but we know exactly which one reverted and who called it. 51 | 52 | ## Sol-coverage 53 | 54 | ```bash 55 | yarn coverage 56 | ``` 57 | 58 | It will generate the HTML report and open it in the default browser. You can use any other istanbul reporter too. (text, json, etc.). 59 | 60 | ## Sol-profiler 61 | 62 | ```bash 63 | yarn profile 64 | ``` 65 | 66 | ## Sol-compiler 67 | 68 | To test [sol-compiler](https://sol-compiler.com) run 69 | 70 | ```bash 71 | yarn sol-compiler 72 | ``` 73 | 74 | or 75 | 76 | ```bash 77 | yarn sol-compiler:watch 78 | ``` 79 | 80 | ## Licence 81 | 82 | Apache 2.0 83 | -------------------------------------------------------------------------------- /contracts/MetaCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import {SafeMath} from "./SafeMath.sol"; 4 | 5 | contract MetaCoin { 6 | mapping (address => uint) public balances; 7 | 8 | constructor() public { 9 | balances[tx.origin] = 1; 10 | } 11 | 12 | function sendCoin(address safeMath, address receiver, uint amount) public { 13 | balances[msg.sender] = SafeMath(safeMath).sub(balances[msg.sender], amount); 14 | balances[receiver] = SafeMath(safeMath).add(balances[receiver], amount); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.25 <0.6.0; 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 | constructor() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @title SafeMath 5 | * @dev Unsigned math operations with safety checks that revert on error 6 | */ 7 | contract SafeMath { 8 | /** 9 | * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). 10 | */ 11 | function sub(uint256 a, uint256 b) public pure returns (uint256) { 12 | require(b <= a); 13 | uint256 c = a - b; 14 | return c; 15 | } 16 | 17 | /** 18 | * @dev Adds two unsigned integers, reverts on overflow. 19 | */ 20 | function add(uint256 a, uint256 b) public pure returns (uint256) { 21 | uint256 c = a + b; 22 | require(c >= a); 23 | return c; 24 | } 25 | } -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const SafeMath = artifacts.require("SafeMath"); 2 | const MetaCoin = artifacts.require("MetaCoin"); 3 | 4 | module.exports = function(deployer) { 5 | deployer.deploy(SafeMath); 6 | deployer.deploy(MetaCoin); 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "trace": "MODE=trace truffle test", 4 | "coverage": "npm run clean && MODE=coverage truffle test; istanbul report html && opn coverage/index.html", 5 | "profile": "npm run clean && MODE=profile truffle test; istanbul report html && opn coverage/index.html", 6 | "clean": "shx rm -rf coverage", 7 | "test": "truffle test", 8 | "compile": "truffle compile", 9 | "migrate": "truffle migrate", 10 | "sol-compiler": "sol-compiler", 11 | "sol-compiler:watch": "sol-compiler -w" 12 | }, 13 | "dependencies": { 14 | "@0x/sol-compiler": "^3.1.0", 15 | "@0x/sol-coverage": "^3.0.0", 16 | "@0x/sol-profiler": "^3.0.0", 17 | "@0x/sol-trace": "^2.0.5", 18 | "@0x/subproviders": "^2.1.11", 19 | "istanbul": "^0.4.5", 20 | "opn-cli": "^4.0.0", 21 | "shx": "^0.3.2", 22 | "truffle": "^5.0.2", 23 | "web3-provider-engine": "^14.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/metacoin.js: -------------------------------------------------------------------------------- 1 | const MetaCoin = artifacts.require("MetaCoin"); 2 | const SafeMath = artifacts.require("SafeMath"); 3 | 4 | const mode = process.env.MODE; 5 | 6 | contract("MetaCoin", accounts => { 7 | after("write coverage/profiler output", async () => { 8 | if (mode === "profile") { 9 | await global.profilerSubprovider.writeProfilerOutputAsync(); 10 | } else if (mode === "coverage") { 11 | await global.coverageSubprovider.writeCoverageAsync(); 12 | } 13 | }); 14 | it("should send coin correctly", async () => { 15 | const metaCoinInstance = await MetaCoin.deployed(); 16 | const safeMathInstance = await SafeMath.deployed(); 17 | const accountOne = accounts[0]; 18 | const accountTwo = accounts[1]; 19 | const amount = 2; // The balance of the owner (account[0]) is 1 (assigned in the constructor), so the transaction sending 2 will fail. 20 | if (mode === "profile") { 21 | global.profilerSubprovider.start(); 22 | } 23 | await metaCoinInstance.sendCoin( 24 | safeMathInstance.address, 25 | accountTwo, 26 | amount, 27 | { from: accountOne } 28 | ); 29 | if (mode === "profile") { 30 | global.profilerSubprovider.stop(); 31 | } 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | const ProviderEngine = require("web3-provider-engine"); 2 | const WebsocketSubprovider = require("web3-provider-engine/subproviders/websocket.js") 3 | const { TruffleArtifactAdapter } = require("@0x/sol-trace"); 4 | const { ProfilerSubprovider } = require("@0x/sol-profiler"); 5 | const { CoverageSubprovider } = require("@0x/sol-coverage"); 6 | const { RevertTraceSubprovider } = require("@0x/sol-trace"); 7 | 8 | const mode = process.env.MODE; 9 | 10 | const projectRoot = ""; 11 | const solcVersion = "0.5.0"; 12 | const defaultFromAddress = "0x5409ed021d9299bf6814279a6a1411a7e866a631"; 13 | const isVerbose = true; 14 | const artifactAdapter = new TruffleArtifactAdapter(projectRoot, solcVersion); 15 | const provider = new ProviderEngine(); 16 | 17 | if (mode === "profile") { 18 | global.profilerSubprovider = new ProfilerSubprovider( 19 | artifactAdapter, 20 | defaultFromAddress, 21 | isVerbose 22 | ); 23 | global.profilerSubprovider.stop(); 24 | provider.addProvider(global.profilerSubprovider); 25 | provider.addProvider(new WebsocketSubprovider({ rpcUrl: "http://localhost:8545" })); 26 | } else { 27 | if (mode === "coverage") { 28 | global.coverageSubprovider = new CoverageSubprovider( 29 | artifactAdapter, 30 | defaultFromAddress, 31 | { 32 | isVerbose, 33 | } 34 | ); 35 | provider.addProvider(global.coverageSubprovider); 36 | } else if (mode === "trace") { 37 | const revertTraceSubprovider = new RevertTraceSubprovider( 38 | artifactAdapter, 39 | defaultFromAddress, 40 | isVerbose 41 | ); 42 | provider.addProvider(revertTraceSubprovider); 43 | } 44 | 45 | provider.addProvider(new WebsocketSubprovider({ rpcUrl: "http://localhost:8545" })) 46 | } 47 | provider.start(err => { 48 | if (err !== undefined) { 49 | console.log(err); 50 | process.exit(1); 51 | } 52 | }); 53 | /** 54 | * HACK: Truffle providers should have `send` function, while `ProviderEngine` creates providers with `sendAsync`, 55 | * but it can be easily fixed by assigning `sendAsync` to `send`. 56 | */ 57 | provider.send = provider.sendAsync.bind(provider); 58 | 59 | module.exports = { 60 | networks: { 61 | development: { 62 | provider, 63 | network_id: "*" 64 | } 65 | } 66 | }; 67 | --------------------------------------------------------------------------------