├── frontend ├── .gitignore ├── .babelrc ├── dist │ └── index.html ├── README.md ├── webpack.config.js ├── package.json └── src │ └── index.jsx ├── diagram.png ├── example.gif ├── contract └── ping.cpp └── README.md /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react", "stage-2"] 3 | } -------------------------------------------------------------------------------- /diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosasia/ping-eos/HEAD/diagram.png -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosasia/ping-eos/HEAD/example.gif -------------------------------------------------------------------------------- /frontend/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EOS Ping 5 | 6 | 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /contract/ping.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class hello_world : public eosio::contract { 5 | public: 6 | using eosio::contract::contract; 7 | void ping(account_name receiver) { 8 | eosio::print("Received ping"); 9 | } 10 | }; 11 | 12 | EOSIO_ABI( hello_world, (ping) ) 13 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Ping EOS with React 2 | 3 | A simple demonstration of an interacting between the browser and a EOS blockchain. The demo uses [React](https://reactjs.org/) and [EOS.js](https://github.com/EOSIO/eosjs). 4 | 5 | ## Getting started 6 | 7 | - Get the code: `git clone repo && cd repo/frontend` 8 | - Download/install dependencies: `npm install` 9 | - Modify `const config` in the `src/index.jsx` file. 10 | - Start it up: `npm start`, then open `http://localhost:8080/` to try it out. 11 | -------------------------------------------------------------------------------- /frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: [ 5 | 'react-hot-loader/patch', 6 | './src/index.jsx' 7 | ], 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.(js|jsx)$/, 12 | exclude: /node_modules/, 13 | use: ['babel-loader'] 14 | } 15 | ] 16 | }, 17 | resolve: { 18 | extensions: ['*', '.js', '.jsx'] 19 | }, 20 | output: { 21 | path: __dirname + '/dist', 22 | publicPath: '/', 23 | filename: 'bundle.js' 24 | }, 25 | plugins: [ 26 | new webpack.HotModuleReplacementPlugin() 27 | ], 28 | devServer: { 29 | contentBase: './dist', 30 | hot: true 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eos-react-ping", 3 | "version": "1.0.0", 4 | "description": "Ping EOS blockchain with EOS.js and React", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --config webpack.config.js --mode development", 8 | "test": "echo \"No test specified\" && exit 0" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "eos", 13 | "eos.js" 14 | ], 15 | "author": "Tyler Diaz", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "babel-core": "^6.23.1", 19 | "babel-loader": "^7.1.2", 20 | "babel-preset-react": "^6.23.0", 21 | "babel-preset-stage-2": "^6.22.0", 22 | "react-hot-loader": "^3.1.3", 23 | "webpack": "^4.6.0", 24 | "webpack-cli": "^2.0.10", 25 | "webpack-dev-server": "^3.1.0" 26 | }, 27 | "dependencies": { 28 | "babel-preset-env": "^1.6.1", 29 | "eosjs": "^8.0.1", 30 | "react": "^16.3.2", 31 | "react-dom": "^16.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import EOS from 'eosjs' 4 | 5 | const EOS_CONFIG = { 6 | contractName: "ping.ctr", // Contract name 7 | contractSender: "tester", // User executing the contract (should be paired with private key) 8 | clientConfig: { 9 | keyProvider: ['private_key'], // Your private key 10 | httpEndpoint: 'http://127.0.0.1:8888' // EOS http endpoint 11 | } 12 | } 13 | 14 | class Pingdemo extends React.Component { 15 | constructor(props) { 16 | super(props) 17 | this.state = { pingStatus: false } 18 | } 19 | 20 | sendPing() { 21 | this.setState({ pingStatus: 'loading' }) 22 | let eosClient = EOS.Localnet(EOS_CONFIG.clientConfig) 23 | 24 | eosClient.contract(EOS_CONFIG.contractName) 25 | .then((contract) => { 26 | contract.ping(EOS_CONFIG.contractSender, { authorization: [EOS_CONFIG.contractSender] }) 27 | .then((res) => { this.setState({ pingStatus: 'success' }) }) 28 | .catch((err) => { this.setState({ pingStatus: 'fail' }); console.log(err) }) 29 | }) 30 | } 31 | 32 | render() { 33 | if (!this.state.pingStatus){ 34 | return () 35 | } else if (this.state.pingStatus == "loading") { 36 | return (Pinging EOS...) 37 | } else if (this.state.pingStatus == "success") { 38 | return (Ping successful!) 39 | } else if (this.state.pingStatus == "fail") { 40 | return (Ping unsuccessful) 41 | } 42 | } 43 | } 44 | 45 | ReactDOM.render(, document.getElementById('app')); 46 | 47 | module.hot.accept(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ping with EOS and React 2 | 3 | ![Example gif](./example.gif) 4 | 5 | With all the excitement around EOS, an area that remains challenging for most developers looking to become involved is getting started with smart contracts. There are generally two hurdles for new developers to overcome: getting your tooling setup, and knowing how to write the smart contract itself. 6 | 7 | EOS smart contracts are written in C++ and compile into Web Assembly. [Dan Larimer](https://steemit.com/eos/@dan/eos-example-exchange-contract-and-benefits-of-c) chose C++ to take advantage of its type and templating system which makes for safer contracts, and adds that because smart-contracts have short runtimes, most of the memory concerns fall away. 8 | 9 | ## Setting up 10 | Part of the challenge in working with EOS is setting up the local blockchain to work against. Luckily, EOS offers some facilities for [setting up your local EOS environment](https://github.com/EOSIO/eos/wiki/Local-Environment#getting-the-code). For this guide, we’ll be using `EOSIO Dawn 3.0`. 11 | 12 | A summary of that guide can be condensed into a few key commands: 13 | ```bash 14 | $ git clone https://github.com/EOSIO/eos --recursive 15 | $ cd eos 16 | $ ./eosio_build.sh 17 | $ cd build && make install 18 | $ cd programs/nodeos 19 | $ ./nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::account_history_api_plugin --access-control-allow-origin=* 20 | ``` 21 | 22 | The installation will take some time, but is straightforward. Once you have a local EOS blockchain up and running locally, you’re ready to start. Throughout the guide, we’ll reference some utilities like `cleos` and `eosiocpp`. You can find these inside your `eos/programs` folder. 23 | 24 | ## Building a ping smart contract 25 | For this tutorial we’ll create and deploy the “Hello World” of distributed systems: ping/pong. For the uninitiated, we will send the server a “ping” command, and it will respond with “pong”. A smart contract is composed of a few things: C++ code, an ABI (Application Binary Interface), and a WAST (Web Assembly Text file) based on the C++ code. Here’s what that looks like: 26 | 27 | ![Code graph diagram](./diagram.png) 28 | 29 | ### Implementing ping 30 | After we’ve got our tooling environment setup, let’s get into the contract! To write the ping smart contract, we only need a contract that implements one action: `ping`. All this method needs to do is print “Pong” in return. 31 | 32 | Create a folder called `ping` inside `contracts`, and a file `ping/ping.cpp`: 33 | ```c++ 34 | #include 35 | #include 36 | 37 | class ping_contract : public eosio::contract { 38 | public: 39 | using eosio::contract::contract; 40 | void ping(account_name receiver) { 41 | eosio::print("Pong"); 42 | } 43 | }; 44 | 45 | EOSIO_ABI( ping_contract, (ping) ) 46 | ``` 47 | 48 | The idea here is to have a simple and small example you can test with to become more familiar with the moving pieces. Let’s break down what’s going on here: 49 | 1. We include some definitions to write our contract. 50 | 2. We create a new contract class that inherits from the [eos::contract](https://github.com/EOSIO/eos/blob/8425ff88a7f712c7df46b979de0e6e7de512f569/contracts/eosiolib/contract.hpp). 51 | 3. We create a method that prints “Pong” 52 | 4. The last line is a [macro that was recently added](https://github.com/EOSIO/eos/pull/2051), it saves us the effort of maintaining our own hand-written ABIs by generating one based on the methods we pass into the second parameter. 53 | 54 | ### Building your contract 55 | The EOS block producers don’t run C++ code when executing smart contracts, they expect web-assembly. EOS offers a tool called `eosiocpp` for converting your C++ code to Web Assembly Text. Let’s do that now with 56 | `eosiocpp -o ping.wast ping.cpp`. This will compilation step will generate some warnings, but we can disregard those for now. 57 | 58 | Next, we need the Application Binary Interface. Essentially, the API for your smart contract that will describe the methods and their corresponding signatures. Since we added the `EOSIO_ABI` macro at the end of our file, instead of writing one by hand, we can simply generate it with: `eosiocpp -g ping.abi ping.cpp` 59 | 60 | At this point, your folder should look like: 61 | ``` 62 | ├── ping.abi 63 | ├── ping.cpp 64 | └── ping.wast 65 | ``` 66 | 67 | ## Deploying to your local network 68 | Now that we have all the materials necessary for our smart contract, let’s get to deploying it. Make sure you’ve got a wallet created `cleos wallet create` and ensure it’s unlocked by running `cleos wallet unlock` and typing your wallet password if prompted. We’ll deploy our smart contract under another account. 69 | 70 | For this, we’ll need to create a new key-pair, let's do that by running: `cleos create key`. This will generate a random public and private key for you. Throughout the rest of the tutorial, be sure to replace any indications of [public_key]/[private_key] with the values you've just received. 71 | 72 | Import the private key to your current unlocked account wallet: `cleos wallet import [private_key]` 73 | Set up an account for the contract with the public key: `cleos create account eosio ping.ctr [owner_key: public_key] [active_key: public_key]` 74 | - Link the contract with your newly created account `cleos set contract ping.ctr ../ping -p ping.ctr` 75 | 76 | ## Interacting with ping 77 | Once your new contract is deployed, it’s time to interact with it! Let's create a tester account with the same keys to run the transaction: `cleos create account eosio tester [public_key] [public_key]` 78 | 79 | Now we can test it out on the command line: 80 | ```bash 81 | $ cleos push action ping.ctr ping '["tester"]' -p tester 82 | executed transaction: e89ebeaad8f3623e42d966f62d4d0adbf6a7412b6bb4d7be61f04a22d3cd485e 232 bytes 102400 cycles 83 | # ping.ctr <= ping.ctr::ping {"account":"tester"} 84 | >> Received ping 85 | ``` 86 | 87 | It works! 88 | 89 | This is exciting for us programmers, but most of your users won’t setup their command line to interact with your smart contract. So let’s bring this interaction to an interface they’re more familiar with: their browser. 90 | 91 | ### Interacting through the browser 92 | To interact with EOS from the frontend, we’ll use [EOS.js](https://github.com/EOSIO/eosjs). Since we’re using dawn3 on our EOS backed, we need to make sure to use the `dawn3` branch when installing: `npm install eosjs@dawn3`. 93 | 94 | We start off by configuring: 95 | ```javascript 96 | Eos = require('eosjs') 97 | 98 | eos = EOS.Localnet({ 99 | keyProvider: ['{replace_with_your_private_key}'], 100 | httpEndpoint: 'http://127.0.0.1:8888' 101 | }) 102 | ``` 103 | 104 | Once configured, we have to specify a few details: 105 | ```javascript 106 | eos.contract('ping.ctr').then((contract) => { 107 | contract.ping("tester", { authorization: ['tester'] }).then((res) => { 108 | console.log(res) 109 | }) 110 | }) 111 | ``` 112 | 113 | Notice `ping.ctr`, which matches the name of the contract we deployed earlier. Once we fetch the contract interface (or ABI) we can interact with it as though it were native Javascript. The contract returns a promise with the transaction details included in the resolve function. This is the core idea of interacting with our ping smart contract from the frontend. 114 | 115 | To see this in a larger context with a working example, check out the [frontend code in this repository](https://github.com/tylerdiaz/ping-eos/tree/master/frontend). 116 | --------------------------------------------------------------------------------