diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..7ad43c2
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,6 @@
+node_modules/
+.vscode/
+phoenix.db
+examples/
+test/
+.editorconfig
diff --git a/examples/react/README.md b/examples/react/README.md
index 4d79b21..014433e 100644
--- a/examples/react/README.md
+++ b/examples/react/README.md
@@ -1,11 +1,34 @@
-#
-```
-In the parent folder:
-yarn link
+phoenix - react example
+===
+Simple application using a react observable component to receive a stream of emitted events. This app will deploy a test contract to **Ganache**.
-In the current folder:
+## Requirements
+- `ganache-cli`
+- `yarn` or `npm` installed.
+
+## Install
+In the parent folder, link the package with `yarn` or `npm`
+```
+yarn link
+```
+Then in the current folder link `phoenix`, and install the packages
+```
yarn link phoenix
yarn
-ganache-cli
+```
+
+## Usage
+In a terminal execute
+```
+ganache-cli
+```
+
+In a different session, execute
+```
yarn run start
-```
\ No newline at end of file
+```
+
+Browse the DApp in [http://localhost:3000](http://localhost:3000)
+
+
+*Note*: this is a simple example application that does not include error handling for the web3 connection. Be sure `ganache-cli` is running in `localhost:8545` before browsing the dapp.
\ No newline at end of file
diff --git a/examples/react/src/App.js b/examples/react/src/App.js
index d654953..735167f 100644
--- a/examples/react/src/App.js
+++ b/examples/react/src/App.js
@@ -1,138 +1,50 @@
import React from "react";
import Phoenix from "phoenix";
-import Web3 from "web3";
+import { web3, MyContract, onWeb3Available } from "./ethService";
+import { scan } from 'rxjs/operators';
+
import MyComponentObserver from "./MyComponentObserver";
-const web3 = new Web3("ws://localhost:8545");
+let MyContractInstance;
class App extends React.Component {
state = {
- escrowObservable: null
+ myEventObservable$: null
};
- constructor(props) {
- super(props);
- this.EscrowContract = null;
- }
-
componentDidMount() {
- (async () => {
- let accounts = await web3.eth.getAccounts();
- this.EscrowContract = await deployContract();
-
- await this.EscrowContract.methods.createEscrow(1, accounts[0], accounts[1]).send({ from: accounts[0] });
- await this.EscrowContract.methods.createEscrow(1, accounts[1], accounts[2]).send({ from: accounts[0] });
- await this.EscrowContract.methods.createEscrow(1, accounts[1], accounts[0]).send({ from: accounts[0] });
- await this.EscrowContract.methods.createEscrow(1, accounts[0], accounts[2]).send({ from: accounts[0] });
+ // Verify if web3 connection is available
+ onWeb3Available(async () => {
+ MyContractInstance = await MyContract.deploy().send({ from: web3.eth.defaultAccount });
const eventSyncer = new Phoenix(web3.currentProvider);
- await eventSyncer.init();
+ eventSyncer.init().then(() => {
+ const myEventObservable$ = eventSyncer.trackEvent(MyContractInstance, "MyEvent", {filter: {}, fromBlock: 1 });
- this.setState({
- escrowObservable: eventSyncer.trackEvent(this.EscrowContract, "Created", { filter: { buyer: accounts[0] }, fromBlock: 1 })
+ // If you want to return all the events in an array, you can pipe the scan operator to the observable
+ // const myEventObservable$ = eventSyncer.trackEvent(MyContractInstance, "MyEvent", {filter: {}, fromBlock: 1 })
+ // .pipe(scan((accum, val) => [...accum, val], []));
+ // Your observable component would receive the eventData as an array instead of an object
+
+ this.setState({ myEventObservable$ });
});
-
- })();
+ });
}
- createTrx = async () => {
- let accounts = await web3.eth.getAccounts();
- await this.EscrowContract.methods
- .createEscrow(Date.now(), accounts[0], accounts[1])
- .send({ from: accounts[0] });
+ createTrx = () => {
+ MyContractInstance.methods
+ .myFunction()
+ .send({ from: web3.eth.defaultAccount });
};
render() {
- const {escrowObservable} = this.state;
-
return (
-
-
+
+
);
}
}
export default App;
-
-
-
-
-
-
-
-
-
-
-
-
-
-async function deployContract() {
- let accounts = await web3.eth.getAccounts();
-
- // pragma solidity >=0.4.22 <0.6.0;
- // contract Escrow {
- // event Created(uint indexed escrowId, address buyer, address seller);
- // function createEscrow(uint escrowId, address buyer, address seller) external {
- // emit Created(escrowId, buyer, seller);
- // }
- // }
-
- let abi = [
- {
- "constant": false,
- "inputs": [
- {
- "name": "escrowId",
- "type": "uint256"
- },
- {
- "name": "buyer",
- "type": "address"
- },
- {
- "name": "seller",
- "type": "address"
- }
- ],
- "name": "createEscrow",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "name": "escrowId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "name": "buyer",
- "type": "address"
- },
- {
- "indexed": false,
- "name": "seller",
- "type": "address"
- }
- ],
- "name": "Created",
- "type": "event"
- }
- ]
-
- var contract = new web3.eth.Contract(abi)
- let instance = await contract.deploy({
- data: '0x608060405234801561001057600080fd5b50610184806100206000396000f3fe60806040526004361061003b576000357c01000000000000000000000000000000000000000000000000000000009004806378015cf414610040575b600080fd5b34801561004c57600080fd5b506100b96004803603606081101561006357600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100bb565b005b827fcbd6f84bfed2ee8cc01ea152b5d9f7126a72c410dbc5ab04c486a5800627b1908383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a250505056fea165627a7a72305820cc868ec126578f5508ee248fb823cd9f1ac6deb0562091cdf31843840b2a56410029',
- arguments: []
- }).send({
- from: accounts[0],
- gas: '4700000'
- })
- return instance
-}
diff --git a/examples/react/src/MyComponentObserver.js b/examples/react/src/MyComponentObserver.js
index 32b0976..aebb2fe 100644
--- a/examples/react/src/MyComponentObserver.js
+++ b/examples/react/src/MyComponentObserver.js
@@ -1,17 +1,20 @@
import React from "react";
-import {observe} from "phoenix/react";
+import { observe } from "phoenix/react";
-const MyComponent = props => {
- const { escrow, myCustomProperty } = props;
- if(!escrow) return Loading...
;
- return (
-
- -
- {escrow.buyer} {escrow.seller} - EscrowID:{escrow.escrowId}{" "}
- {myCustomProperty}
-
-
- );
+const MyComponent = ({ eventData }) => {
+ // Handle initial state when no data is available
+ if (!eventData) {
+ return No data
;
+ }
+
+ console.log("Data received", eventData);
+
+ return
+ - someValue: {eventData.someValue}
+ - anotherValue: {eventData.anotherValue}
+
;
};
+// MyComponent will now observe any observable prop it receives
+// and update its state whenever the observable emits an event
export default observe(MyComponent);
diff --git a/examples/react/src/ethService.js b/examples/react/src/ethService.js
new file mode 100644
index 0000000..f19b5e1
--- /dev/null
+++ b/examples/react/src/ethService.js
@@ -0,0 +1,67 @@
+import Web3 from 'web3';
+
+export const web3 = new Web3("ws://localhost:8545");
+
+let web3AvailableCB;
+export const onWeb3Available = (cb) => {
+ web3AvailableCB = cb;
+}
+
+web3.eth.getAccounts().then(async accounts => {
+ web3.eth.defaultAccount = accounts[0];
+ if(web3AvailableCB){
+ web3AvailableCB();
+ }
+ /*SimpleStorageContract.deploy().send({ from: web3.eth.defaultAccount }).then(instance => {
+ SimpleStorage = instance;
+ });*/
+});
+
+
+
+// pragma solidity ^0.5.0;
+//
+// contract MyContract {
+// event MyEvent(uint someValue, bytes32 anotherValue);
+//
+// function myFunction() public {
+// uint a = block.timestamp * block.number; // Just to have a pseudo random value
+// bytes32 b = keccak256(abi.encodePacked(a));
+//
+// emit MyEvent(a, b);
+// }
+// }
+
+
+const abi = [
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "myFunction",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "name": "someValue",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "anotherValue",
+ "type": "bytes32"
+ }
+ ],
+ "name": "MyEvent",
+ "type": "event"
+ }
+];
+
+const data = "0x6080604052348015600f57600080fd5b5060f38061001e6000396000f3fe6080604052600436106039576000357c010000000000000000000000000000000000000000000000000000000090048063c3780a3a14603e575b600080fd5b348015604957600080fd5b5060506052565b005b60004342029050600081604051602001808281526020019150506040516020818303038152906040528051906020012090507fc3d6130248b5b68a864c047b2f68d895d420924130388d02d64b648005fe9ac78282604051808381526020018281526020019250505060405180910390a1505056fea165627a7a72305820613e35c5d1e8684ef5b31a7d993a139f1b5bbb409039d92db0fe78ed571d2ce20029";
+
+export const MyContract = new web3.eth.Contract(abi, {data, gas: "470000"});