From 9a222c0561b3f60d6d25eac77c3ddea8127d9add Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Thu, 9 Aug 2018 17:23:32 -0400 Subject: [PATCH] Added sntcontroller page and updated the setup to set the sntcontroller --- README.md | 2 +- .../components/{body.js => body-identity.js} | 6 +- .../app/components/body-sntcontroller.js | 121 ++++++++ .../{status.js => status-identity.js} | 0 .../app/components/status-sntcontroller.js | 278 ++++++++++++++++++ test-dapp/app/components/transfersnt.js | 248 ++++++++++++++++ test-dapp/app/{dapp.html => identity.html} | 2 +- test-dapp/app/{dapp.js => identity.js} | 2 +- test-dapp/app/sntcontroller.html | 20 ++ test-dapp/app/sntcontroller.js | 16 + test-dapp/embark.json | 6 +- test-dapp/setup_dev_env.sh | 11 +- test-dapp/test2_pk | 1 + 13 files changed, 702 insertions(+), 11 deletions(-) rename test-dapp/app/components/{body.js => body-identity.js} (97%) create mode 100644 test-dapp/app/components/body-sntcontroller.js rename test-dapp/app/components/{status.js => status-identity.js} (100%) create mode 100644 test-dapp/app/components/status-sntcontroller.js create mode 100644 test-dapp/app/components/transfersnt.js rename test-dapp/app/{dapp.html => identity.html} (90%) rename test-dapp/app/{dapp.js => identity.js} (86%) create mode 100644 test-dapp/app/sntcontroller.html create mode 100644 test-dapp/app/sntcontroller.js create mode 100644 test-dapp/test2_pk diff --git a/README.md b/README.md index 1146a5b..34eb3e4 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ nodemon src/service.js ``` ## Test DApp -To run the test dapp, use `embark run` and then browse `http://localhost:8000/dapp.html`. +To run the test dapp, use `embark run` and then browse `http://localhost:8000/identity.html`. The gas relayer service needs to be running, and configured correctly to process the transactions. Things to take in account are: the account used in embark, and the contract addresses. diff --git a/test-dapp/app/components/body.js b/test-dapp/app/components/body-identity.js similarity index 97% rename from test-dapp/app/components/body.js rename to test-dapp/app/components/body-identity.js index 948b0de..077fe7b 100644 --- a/test-dapp/app/components/body.js +++ b/test-dapp/app/components/body-identity.js @@ -6,13 +6,13 @@ import EmbarkJS from 'Embark/EmbarkJS'; import IdentityFactory from 'Embark/contracts/IdentityFactory'; import IdentityGasRelay from 'Embark/contracts/IdentityGasRelay'; import PropTypes from 'prop-types'; -import Status from './status'; +import Status from './status-identity'; import Tab from '@material-ui/core/Tab'; import Tabs from '@material-ui/core/Tabs'; +import Typography from '@material-ui/core/Typography'; import Web3 from 'web3'; -import {Typography} from '@material-ui/core'; -import {withStyles} from '@material-ui/core/styles'; import config from '../config'; +import {withStyles} from '@material-ui/core/styles'; const styles = {}; diff --git a/test-dapp/app/components/body-sntcontroller.js b/test-dapp/app/components/body-sntcontroller.js new file mode 100644 index 0000000..c2b316e --- /dev/null +++ b/test-dapp/app/components/body-sntcontroller.js @@ -0,0 +1,121 @@ +import React, {Component, Fragment} from 'react'; +import Divider from '@material-ui/core/Divider'; +import EmbarkJS from 'Embark/EmbarkJS'; +import PropTypes from 'prop-types'; +import Status from './status-sntcontroller'; +import Tab from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; +import TransferSNT from './transfersnt'; +import Typography from '@material-ui/core/Typography'; +import Web3 from 'web3'; +import config from '../config'; +import web3 from 'Embark/web3'; +import {withStyles} from '@material-ui/core/styles'; + + +const styles = {}; + +class Body extends Component { + + constructor(props){ + super(props); + this.state = { + tab: 0, + walletAddress: null, + nonce: '0', + kid: null, + skid: null, + message: '' + }; + } + + componentDidMount(){ + EmbarkJS.onReady(err => { + if(err) { + console.error(err); + return; + } + + const web3js = new Web3('ws://localhost:8546'); + + web3js.shh.newKeyPair() + .then((kid) => { + web3js.shh.addSymKey(config.relaySymKey) + .then((skid) => { + this.setState({kid, skid}); + + web3js.shh.subscribe('messages', { + "privateKeyID": kid, + "ttl": 1000, + "minPow": 0.1, + "powTime": 1000 + }, (error, message) => { + console.log(message); + if(error){ + console.error(error); + } else { + this.setState({message: web3js.utils.toAscii(message.payload)}); + } + }); + + return true; + }); + }); + + this.setState({ + web3js + }); + + web3.eth.getAccounts() + .then(accounts => { + this.setState({walletAddress: accounts[2]}); + }); + + }); + } + + handleChange = (event, tab) => { + this.setState({tab}); + }; + + updateNonce = (newNonce) => { + this.setState({nonce: newNonce}); + } + + clearMessages = () => { + this.setState({message: ''}); + } + + render(){ + const {tab, walletAddress, nonce, web3js, message, kid, skid} = this.state; + + return + + + + + {tab === 0 && } + {tab === 1 && TODO} + + + + + ; + } +} + +function Container(props) { + return + {props.children} + ; +} + +Container.propTypes = { + children: PropTypes.node.isRequired +}; + +Body.propTypes = { + classes: PropTypes.object.isRequired +}; + +export default withStyles(styles)(Body); diff --git a/test-dapp/app/components/status.js b/test-dapp/app/components/status-identity.js similarity index 100% rename from test-dapp/app/components/status.js rename to test-dapp/app/components/status-identity.js diff --git a/test-dapp/app/components/status-sntcontroller.js b/test-dapp/app/components/status-sntcontroller.js new file mode 100644 index 0000000..2919f47 --- /dev/null +++ b/test-dapp/app/components/status-sntcontroller.js @@ -0,0 +1,278 @@ +import React, {Component, Fragment} from 'react'; +import AddIcon from '@material-ui/icons/Add'; +import BalanceIcon from '@material-ui/icons/AccountBalance'; +import Button from '@material-ui/core/Button'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import EmbarkJS from 'Embark/EmbarkJS'; +import KeyIcon from '@material-ui/icons/VpnKey'; +import LinearProgress from '@material-ui/core/LinearProgress'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import NumberIcon from '@material-ui/icons/ConfirmationNumber'; +import PropTypes from 'prop-types'; +import STT from 'Embark/contracts/STT'; +import Typography from '@material-ui/core/Typography'; +import config from '../config'; +import web3 from 'Embark/web3'; +import {withStyles} from '@material-ui/core/styles'; + +const styles = theme => ({ + button: { + marginRight: theme.spacing.unit * 2 + }, + card: { + marginBottom: theme.spacing.unit * 2 + }, + icon: { + marginRight: theme.spacing.unit + }, + container: { + width: '100%', + position: 'relative' + }, + root: { + width: '100%', + maxWidth: 420, + backgroundColor: theme.palette.background.paper + }, + right: { + position: 'absolute', + top: theme.spacing.unit * 4, + right: theme.spacing.unit * 2 + } + }); + +class Status extends Component { + + constructor(props){ + super(props); + this.state = { + 'addressETHBalance': 0, + 'addressSTTBalance': 0, + 'relayerAddress': null, + 'relayerEthBalance': 0, + 'relayerSTTBalance': 0, + 'block': 0, + 'submitState': { + 'etherSend': false, + 'generateSTT': false + } + }; + } + + componentDidMount(){ + EmbarkJS.onReady(err => { + if(err) { + console.error(err); + return; + } + + this.setState({ + relayerAddress: config.relayAccount + }); + + this.getBlock(); + }); + } + + getBlock = () => { + web3.eth.subscribe('newBlockHeaders') + .on('data', (block) => { + this.setState({block: block.number}); + this.readChain(); + return true; + }); + } + + readChain = () => { + if(this.props.walletAddress){ + web3.eth.getBalance(this.props.walletAddress) + .then(addressETHBalance => { + this.setState({addressETHBalance}); + }); + + STT.methods.balanceOf(this.props.walletAddress) + .call() + .then(addressSTTBalance => { + this.setState({addressSTTBalance: web3.utils.fromWei(addressSTTBalance, 'ether')}); + }); + + /*Identity.options.address = this.props.walletAddress; + Identity.methods.nonce() + .call() + .then((nonce) => { + this.props.nonceUpdateFunction(nonce); + }) + .catch(() => { + console.log("Address " + this.props.walletAddress + " is not a valid wallet"); + });*/ + } + + web3.eth.getBalance(this.state.relayerAddress) + .then(relayerEthBalance => { + this.setState({relayerEthBalance}); + }); + + STT.methods.balanceOf(this.state.relayerAddress) + .call() + .then(relayerSTTBalance => { + this.setState({relayerSTTBalance: web3.utils.fromWei(relayerSTTBalance, 'ether')}); + }); + } + + generateSTT = (event) => { + event.preventDefault(); + + let submitState = this.state.submitState; + submitState.generateSTT = true; + this.setState({submitState}); + + let toSend = STT.methods.generateTokens(this.props.walletAddress, web3.utils.toWei('5000', 'ether')); + toSend.estimateGas() + .then(estimatedGas => { + return toSend.send({gas: estimatedGas + 10000}); + }) + .then((receipt) => { + console.log(receipt); + + submitState = this.state.submitState; + submitState.generateSTT = false; + this.setState({submitState}); + }); + } + + sendEther = (event) => { + event.preventDefault(); + + let submitState = this.state.submitState; + submitState.etherSend = false; + this.setState({submitState}); + + web3.eth.sendTransaction({from: web3.eth.defaultAccount, to: this.state.relayerAddress, value: web3.utils.toWei('1', "ether")}) + .then((receipt) => { + console.log(receipt); + submitState = this.state.submitState; + submitState.etherSend = false; + this.setState({submitState}); + }); + } + + render(){ + const {classes, walletAddress, nonce} = this.props; + const {addressETHBalance, relayerAddress, relayerEthBalance, addressSTTBalance, relayerSTTBalance, submitState, block} = this.state; + + return + + + + Whisper Messages: + +
{this.props.message}
+
+
+
+ { (submitState.createIdentity || submitState.etherSend || submitState.generateSTT) && } + + + + + Address + + { + + } + + + + + + + + + + + + + + + + + + + + + + + + + + Relayer + + + + + + + + + + + + + + + + + +
+ + + Block
#{block} +
+
+
+
; + } + +} + +Status.propTypes = { + classes: PropTypes.object.isRequired, + walletAddress: PropTypes.string, + nonce: PropTypes.string.isRequired, + nonceUpdateFunction: PropTypes.func.isRequired, + message: PropTypes.string +}; + +export default withStyles(styles)(Status); diff --git a/test-dapp/app/components/transfersnt.js b/test-dapp/app/components/transfersnt.js new file mode 100644 index 0000000..8ba208e --- /dev/null +++ b/test-dapp/app/components/transfersnt.js @@ -0,0 +1,248 @@ +import React, {Component} from 'react'; +import Button from '@material-ui/core/Button'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader from '@material-ui/core/CardHeader'; +import Grid from '@material-ui/core/Grid'; +import MySnackbarContentWrapper from './snackbar'; +import PropTypes from 'prop-types'; +import SNTController from 'Embark/contracts/SNTController'; +import TestContract from 'Embark/contracts/TestContract'; +import TextField from '@material-ui/core/TextField'; +import config from '../config'; +import web3 from 'Embark/web3'; +import {withStyles} from '@material-ui/core/styles'; +const styles = theme => ({ + root: { + width: '100%', + backgroundColor: theme.palette.background.paper + }, + card: { + marginBottom: theme.spacing.unit * 3 + } + }); + +window.TestContract = TestContract; +class TransferSNT extends Component { + + constructor(props){ + super(props); + this.state = { + topic: '0x534e5443', + to: '0x0000000000000000000000000000000000000000', + amount: 0, + gasPrice: 0, + signature: '', + kid: null, + skid: null, + msgSent: '', + payload: '', + message: '', + web3js: null, + transactionError: '', + messagingError: '', + submitting: false + }; + } + + handleChange = name => event => { + this.setState({ + [name]: event.target.value + }); + }; + + sign = (event) => { + if(event) event.preventDefault(); + + this.setState({ + msgSent: false, + transactionError: '' + }); + + SNTController.options.address = this.props.identityAddress; + + try { + let message = ""; + SNTController.methods.getTransferSNTHash( + this.state.to, + this.state.amount, + this.props.nonce, + this.state.gasPrice + ) + .call() + .then(result => { + message = result; + return web3.eth.getAccounts(); + }) + .then(accounts => { + return web3.eth.sign(message, accounts[2]); + }) + .then(signature => { + this.setState({signature}); + }); + } catch(error){ + this.setState({transactionError: error.message}); + } + } + + sendMessage = event => { + event.preventDefault(); + + const {web3, kid, skid} = this.props; + + this.setState({ + messagingError: '', + submitting: true + }); + this.props.clearMessages(); + + try { + let jsonAbi = SNTController._jsonInterface.filter(x => x.name == "transferSNT")[0]; + let funCall = web3.eth.abi.encodeFunctionCall(jsonAbi, [ + this.state.to, + this.state.amount, + this.props.nonce, + this.state.gasPrice, + this.state.signature + ]); + const sendOptions = { + ttl: 1000, + sig: kid, + powTarget: 1, + powTime: 20, + topic: this.state.topic, + symKeyID: skid, + payload: web3.utils.toHex({ + 'address': SNTController.options.address, + 'encodedFunctionCall': funCall + }) + }; + + web3.shh.post(sendOptions) + .then(() => { + this.setState({submitting: false}); + console.log("Message sent"); + return true; + }); + } catch(error){ + this.setState({messagingError: error.message, submitting: false}); + } + } + + render(){ + const {classes} = this.props; + return
+ { this.state.transactionError && } + + + +
+ + + + + + + + + + + + + + +
+
+ + + +
+ + { this.state.messagingError && } + + + + + + + + + + + +
; + } +} + +TransferSNT.propTypes = { + classes: PropTypes.object.isRequired, + nonce: PropTypes.string.isRequired, + identityAddress: PropTypes.string, + web3: PropTypes.object, + kid: PropTypes.string, + skid: PropTypes.string, + clearMessages: PropTypes.func +}; + +export default withStyles(styles)(TransferSNT); diff --git a/test-dapp/app/dapp.html b/test-dapp/app/identity.html similarity index 90% rename from test-dapp/app/dapp.html rename to test-dapp/app/identity.html index e36570f..93c48ef 100644 --- a/test-dapp/app/dapp.html +++ b/test-dapp/app/identity.html @@ -14,7 +14,7 @@
- + \ No newline at end of file diff --git a/test-dapp/app/dapp.js b/test-dapp/app/identity.js similarity index 86% rename from test-dapp/app/dapp.js rename to test-dapp/app/identity.js index 9af5af1..b089de2 100644 --- a/test-dapp/app/dapp.js +++ b/test-dapp/app/identity.js @@ -1,5 +1,5 @@ import React, {Component, Fragment} from 'react'; -import Body from './components/body'; +import Body from './components/body-identity'; import Header from './components/header'; import ReactDOM from 'react-dom'; diff --git a/test-dapp/app/sntcontroller.html b/test-dapp/app/sntcontroller.html new file mode 100644 index 0000000..512cca1 --- /dev/null +++ b/test-dapp/app/sntcontroller.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/test-dapp/app/sntcontroller.js b/test-dapp/app/sntcontroller.js new file mode 100644 index 0000000..bb7adb0 --- /dev/null +++ b/test-dapp/app/sntcontroller.js @@ -0,0 +1,16 @@ +import React, {Component, Fragment} from 'react'; +import BodySNTController from './components/body-sntcontroller'; +import Header from './components/header'; +import ReactDOM from 'react-dom'; + + +class App extends Component { + render(){ + return +
+ + ; + } +} + +ReactDOM.render(, document.getElementById('app')); diff --git a/test-dapp/embark.json b/test-dapp/embark.json index 64433f2..d422429 100644 --- a/test-dapp/embark.json +++ b/test-dapp/embark.json @@ -1,8 +1,10 @@ { "contracts": ["contracts/**"], "app": { - "js/dapp.js": ["app/dapp.js"], - "dapp.html": "app/dapp.html", + "js/identity.js": ["app/identity.js"], + "identity.html": "app/identity.html", + "js/sntcontroller.js": ["app/sntcontroller.js"], + "sntcontroller.html": "app/sntcontroller.html", "css/dapp.css": ["app/css/**"], "images/": ["app/images/**"] }, diff --git a/test-dapp/setup_dev_env.sh b/test-dapp/setup_dev_env.sh index 92ca74d..79d50cc 100755 --- a/test-dapp/setup_dev_env.sh +++ b/test-dapp/setup_dev_env.sh @@ -1,12 +1,17 @@ #!/bin/bash geth account import test_pk --password config/development/password --datadir .embark/development/datadir/ +geth account import test2_pk --password config/development/password --datadir .embark/development/datadir/ + echo 'Waiting 5s before unlocking the account' sleep 5s geth --exec "personal.unlockAccount('0xb8d851486d1c953e31a44374aca11151d49b8bb3', 'dev_password', 0)" attach .embark/development/datadir/geth.ipc +geth --exec "personal.unlockAccount('0xacB57b896A80FCA848E279e87043d52e84f2c6Db', 'dev_password', 0)" attach .embark/development/datadir/geth.ipc STT=`cat chains.json | awk /STT/,/}/ | grep -Pzo "0x[0-9A-Za-z]+"` IdentityFactory=`cat chains.json | awk /IdentityFactory/,/}/ | grep -Pzo "0x[0-9A-Za-z]+"` +SNTController=`cat chains.json | awk /SNTController/,/}/ | grep -Pzo "0x[0-9A-Za-z]+"` -git checkout ../gas-relayer/config/config.json -sed -i 's/%STTAddress%/'"$STT"'/g' ../gas-relayer/config/config.json -sed -i 's/%IdentityFactoryAddress%/'"$IdentityFactory"'/g' ../gas-relayer/config/config.json \ No newline at end of file +git checkout ../gas-relayer/config/config.js +sed -i 's/%STTAddress%/'"$STT"'/g' ../gas-relayer/config/config.js +sed -i 's/%IdentityFactoryAddress%/'"$IdentityFactory"'/g' ../gas-relayer/config/config.js +sed -i 's/%SNTController%/'"$SNTController"'/g' ../gas-relayer/config/config.js \ No newline at end of file diff --git a/test-dapp/test2_pk b/test-dapp/test2_pk new file mode 100644 index 0000000..6b835b6 --- /dev/null +++ b/test-dapp/test2_pk @@ -0,0 +1 @@ +da7cd207df98f7244dd4f92368c1a50e166b26b66bebb5275fc314e8acf8f570