├── .babelrc
├── .eslintrc
├── mocha-webpack.opts
├── dapp
├── js
│ ├── main.jsx
│ └── component
│ │ ├── MyToken.jsx
│ │ ├── Web3.jsx
│ │ └── App.jsx
└── static
│ └── index.html
├── .travis.yml
├── .editorconfig
├── webpack.config.test.js
├── .gitignore
├── test
└── contract
│ └── MyToken.test.js
├── webpack.config.js
├── package.json
├── README.md
└── contract
└── MyToken.sol
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"]
3 | }
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "env": {
4 | "mocha": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/mocha-webpack.opts:
--------------------------------------------------------------------------------
1 | --colors
2 | --webpack-config webpack.config.test.js
3 | --require source-map-support/register
4 | --timeout 5000
5 | test/**/*.js
6 |
--------------------------------------------------------------------------------
/dapp/js/main.jsx:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill';
2 | import React from 'react';
3 | import { render } from 'react-dom';
4 | import App from './component/App';
5 |
6 | render(
7 | ,
8 | document.getElementById('root')
9 | );
10 |
--------------------------------------------------------------------------------
/dapp/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sample React Ethereum Dapp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6"
4 | - "5"
5 | - "4"
6 | env:
7 | - CXX=g++-4.8
8 | addons:
9 | apt:
10 | sources:
11 | - ubuntu-toolchain-r-test
12 | packages:
13 | - g++-4.8
14 | before_script:
15 | - npm install -g ethereumjs-testrpc
16 | - testrpc &
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain
2 | # consistent coding styles between different editors and IDEs.
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [*.md]
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/webpack.config.test.js:
--------------------------------------------------------------------------------
1 | const nodeExternals = require('webpack-node-externals');
2 | const config = require('./webpack.config.js');
3 |
4 | config.output = {
5 | devtoolModuleFilenameTemplate: '[absolute-resource-path]',
6 | devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
7 | };
8 |
9 | // Set target to node to be runable within Mocha
10 | config.target = 'node';
11 |
12 | config.externals = [nodeExternals()];
13 |
14 | // Enable source maps
15 | config.devtool = 'cheap-module-source-map';
16 |
17 | module.exports = config;
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | /lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | /coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | /build
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 | .tmp
29 |
--------------------------------------------------------------------------------
/dapp/js/component/MyToken.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { MyToken } from '../../../contract/MyToken.sol';
3 |
4 | export default function Web3() {
5 | return (
6 |
7 |
MyToken
8 |
Instantiated (deployed) MyToken is available as MyToken.
9 |
10 | - Token name
11 | - {MyToken.name()}
12 | - Token symbol
13 | - {MyToken.symbol()}
14 | - Total supply of token
15 | - {MyToken.totalSupply().toString()}
16 | - Version
17 | - {MyToken.version()}
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/dapp/js/component/Web3.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { web3 } from '../../../contract/MyToken.sol';
3 |
4 | export default function Web3() {
5 | return (
6 |
7 |
Web3
8 |
Initialized Web3 object for easy interfacing of Ethereum JavaScript API.
9 |
10 | - Connected Ethereum node (Web3 provider)
11 | - {web3.currentProvider.host}
12 | - Latest block
13 | - {web3.eth.blockNumber}
14 | - Accounts
15 | -
16 | {web3.eth.accounts.map(
17 | (account) =>
{account}
18 | )}
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/test/contract/MyToken.test.js:
--------------------------------------------------------------------------------
1 | import { MyToken, web3 } from '../../contract/MyToken.sol';
2 |
3 | const assert = require('chai').assert;
4 |
5 | describe('MyToken', () => {
6 | describe('totalSupply()', () => {
7 | it('should return the correct value', () => {
8 | assert.equal(MyToken.totalSupply().toNumber(), 250000);
9 | });
10 | });
11 |
12 | describe('transfer()', () => {
13 | it('should successfully transfer tokens', () => {
14 | const account1 = web3.eth.accounts[0];
15 | const account2 = web3.eth.accounts[1];
16 | MyToken.transfer(account2, 100, { from: account1, gas: 100000 });
17 |
18 | assert.equal(MyToken.balanceOf(account2).toNumber(), 100);
19 | assert.equal(MyToken.balanceOf(account1).toNumber(), 250000 - 100);
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/dapp/js/component/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Web3 from './Web3';
3 | import MyToken from './MyToken';
4 |
5 |
6 | export default function App() {
7 | return (
8 |
9 |
Sample Ethereum Dapp with Webpack
10 |
Example The Coin Dapp with smart contract code from https://www.ethereum.org/token
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
tokenRecipient
20 |
21 | If a .sol file contains more than one contracts,
22 | individual instantiated contracts are also available.
23 | See console.log(tokenRecipient).
24 |
25 |
26 |
27 |
You can find more information on this sample dapp at its GitHub repository and @uzyn.
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const CleanWebpackPlugin = require('clean-webpack-plugin');
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: path.resolve(__dirname, 'dapp/js/main'),
7 | devServer: {
8 | outputPath: path.join(__dirname, 'build'),
9 | },
10 | resolve: {
11 | extensions: ['', '.js', '.jsx'],
12 | },
13 | output: {
14 | path: path.resolve(__dirname, 'build/js'),
15 | publicPath: '/js/',
16 | filename: 'bundle.js',
17 | },
18 | web3Loader: {
19 | constructorParams: {
20 | MyToken: [ 250000, 'The Coin', 2, 'TC$', '1.0.0' ],
21 | }
22 | },
23 | plugins: [
24 | new CleanWebpackPlugin(['build']),
25 | new CopyWebpackPlugin([
26 | {
27 | context: path.resolve(__dirname, 'dapp/static'),
28 | from: '**/*',
29 | to: path.resolve(__dirname, 'build'),
30 | },
31 | ]),
32 | ],
33 | module: {
34 | preLoaders: [
35 | {
36 | test: /\.jsx?$/,
37 | exclude: /(node_modules|bower_components)/,
38 | loaders: ['eslint'],
39 | },
40 | ],
41 | loaders: [
42 | {
43 | test: /\.sol$/,
44 | loaders: ['web3', 'solc'],
45 | },
46 | {
47 | test: /\.json$/,
48 | loaders: ['json'],
49 | },
50 | {
51 | test: /\.jsx?$/,
52 | exclude: /(node_modules|bower_components)/,
53 | loaders: ['babel'],
54 | },
55 | ],
56 | },
57 | };
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ethereum-dapp-template",
3 | "version": "0.0.1",
4 | "description": "Template for Ethereum Dapp based on React with test suite",
5 | "scripts": {
6 | "start": "webpack-dev-server --inline --content-base build/",
7 | "lint": "eslint --ext=js --ext=jsx --ignore-path .gitignore .",
8 | "build": "webpack",
9 | "test": "mocha-webpack"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/uzyn/react-ethereum-dapp-template.git"
14 | },
15 | "author": "U-Zyn Chua ",
16 | "license": "UNLICENSED",
17 | "devDependencies": {
18 | "babel-core": "^6.7.7",
19 | "babel-loader": "^6.2.4",
20 | "babel-preset-es2015": "^6.6.0",
21 | "babel-preset-react": "^6.11.1",
22 | "chai": "^3.5.0",
23 | "clean-webpack-plugin": "^0.1.8",
24 | "copy-webpack-plugin": "^2.1.1",
25 | "eslint": "^2.8.0",
26 | "eslint-config-airbnb": "^7.0.0",
27 | "eslint-loader": "^1.3.0",
28 | "eslint-plugin-jsx-a11y": "^0.6.2",
29 | "eslint-plugin-react": "^4.3.0",
30 | "json-loader": "^0.5.4",
31 | "mocha": "^2.5.3",
32 | "mocha-webpack": "^0.4.0",
33 | "solc-loader": "1.x",
34 | "source-map-support": "^0.4.1",
35 | "web3": "^0.15.3",
36 | "web3-loader": "1.x",
37 | "webpack": "^1.13.0",
38 | "webpack-node-externals": "^1.2.0"
39 | },
40 | "dependencies": {
41 | "babel-polyfill": "^6.7.4",
42 | "react": "^15.0.1",
43 | "react-dom": "^15.0.1",
44 | "webpack-dev-server": "^1.14.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Ethereum Dapp Template
2 |
3 | [](https://travis-ci.org/uzyn/react-ethereum-dapp-template)
4 |
5 | Template for React-based Ethereum decentralized app (Dapp).
6 |
7 | This is an **opinionated version** of [ethereum-webpack-example-dapp](https://github.com/uzyn/ethereum-webpack-example-dapp) and largely intended for personal use, unless you share the same opinion as mine.
8 |
9 | It is largely a combination of:
10 |
11 | - [react-webpack-airbnbjs-boilerplate](https://github.com/uzyn/react-webpack-airbnbjs-boilerplate), and
12 | - [ethereum-webpack-example-dapp](https://github.com/uzyn/ethereum-webpack-example-dapp)
13 |
14 | ## What does this include
15 |
16 | - Webpack build script with Webpack dev server
17 | - ES2015/ES6
18 | - ESlint for ES2015 using Airbnb JS style guide
19 | - React for front-end view
20 | - Solidity for Ethereum smart contracts
21 | - Test suite for smart contract testing
22 |
23 |
24 | ## How to run
25 |
26 | 1. Run a local Ethereum node with JSON-RPC listening at port 8545 _(default)_. [testrpc](https://github.com/ethereumjs/testrpc) would be the most straight-forward method.
27 |
28 | ```bash
29 | # Using testrpc (recommended)
30 | testrpc
31 |
32 | # If you are running Geth,
33 | # make sure to run in testnet or private net and enable rpc
34 | geth --testnet --rpc
35 | ```
36 |
37 | 1. Install dependencies
38 |
39 | ```bash
40 | npm install
41 | ```
42 |
43 | 1. Start the dev server, code and enjoy! Browser should automatically refresh if you make any changes to the code.
44 |
45 | ```bash
46 | npm start
47 | ```
48 |
49 | Load [http://localhost:8080/](http://localhost:8080/) on your web browser.
50 |
51 | 1. For deployment, run `npm build` and upload `build/` to your server.
52 |
53 |
--------------------------------------------------------------------------------
/contract/MyToken.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * This file has 2 contracts: tokenRecipient and MyToken
3 | * and is reproduced directly from https://www.ethereum.org/token
4 | */
5 | contract tokenRecipient {
6 | function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData);
7 | }
8 |
9 | contract MyToken {
10 | /* Public variables of the token */
11 | string public name;
12 | string public symbol;
13 | string public version;
14 | uint8 public decimals;
15 | uint256 public totalSupply;
16 |
17 | /* This creates an array with all balances */
18 | mapping (address => uint256) public balanceOf;
19 | mapping (address => mapping (address => uint256)) public allowance;
20 | mapping (address => mapping (address => uint256)) public spentAllowance;
21 |
22 | /* This generates a public event on the blockchain that will notify clients */
23 | event Transfer(address indexed from, address indexed to, uint256 value);
24 |
25 | /* Initializes contract with initial supply tokens to the creator of the contract */
26 | function MyToken(
27 | uint256 initialSupply,
28 | string tokenName,
29 | uint8 decimalUnits,
30 | string tokenSymbol,
31 | string versionOfTheCode
32 | ) {
33 | balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
34 | totalSupply = initialSupply; // Update total supply
35 | name = tokenName; // Set the name for display purposes
36 | symbol = tokenSymbol; // Set the symbol for display purposes
37 | decimals = decimalUnits; // Amount of decimals for display purposes
38 | version = versionOfTheCode;
39 | }
40 |
41 | /* Send coins */
42 | function transfer(address _to, uint256 _value) {
43 | if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough
44 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
45 | balanceOf[msg.sender] -= _value; // Subtract from the sender
46 | balanceOf[_to] += _value; // Add the same to the recipient
47 | Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place
48 | }
49 |
50 | /* Allow another contract to spend some tokens in your behalf */
51 | function approveAndCall(address _spender, uint256 _value, bytes _extraData)
52 | returns (bool success) {
53 | allowance[msg.sender][_spender] = _value;
54 | tokenRecipient spender = tokenRecipient(_spender);
55 | spender.receiveApproval(msg.sender, _value, this, _extraData);
56 | return true;
57 | }
58 |
59 | /* A contract attempts to get the coins */
60 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
61 | if (balanceOf[_from] < _value) throw; // Check if the sender has enough
62 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
63 | if (spentAllowance[_from][msg.sender] + _value > allowance[_from][msg.sender]) throw; // Check allowance
64 | balanceOf[_from] -= _value; // Subtract from the sender
65 | balanceOf[_to] += _value; // Add the same to the recipient
66 | spentAllowance[_from][msg.sender] += _value;
67 | Transfer(_from, _to, _value);
68 | return true;
69 | }
70 |
71 | /* This unnamed function is called whenever someone tries to send ether to it */
72 | function () {
73 | throw; // Prevents accidental sending of ether
74 | }
75 | }
--------------------------------------------------------------------------------