Added sntcontroller page and updated the setup to set the sntcontroller

This commit is contained in:
Richard Ramos 2018-08-09 17:23:32 -04:00
parent 73b9c447e7
commit 9a222c0561
13 changed files with 702 additions and 11 deletions

View File

@ -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.

View File

@ -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 = {};

View File

@ -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 <Fragment>
<Tabs value={tab} onChange={this.handleChange}>
<Tab label="Transfer SNT" />
<Tab label="????" />
</Tabs>
{tab === 0 && <Container><TransferSNT clearMessages={this.clearMessages} web3={web3js} kid={kid} skid={skid} nonce={nonce} /></Container>}
{tab === 1 && <Container>TODO</Container>}
<Divider />
<Container>
<Status message={message} nonceUpdateFunction={this.updateNonce} nonce={nonce} walletAddress={walletAddress} />
</Container>
</Fragment>;
}
}
function Container(props) {
return <Typography component="div" style={{padding: 8 * 3}}>
{props.children}
</Typography>;
}
Container.propTypes = {
children: PropTypes.node.isRequired
};
Body.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(Body);

View File

@ -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 <Fragment>
<Card className={classes.card}>
<CardContent>
<Typography>
Whisper Messages:
</Typography>
<pre>{this.props.message}</pre>
</CardContent>
</Card>
<div className={classes.container}>
{ (submitState.createIdentity || submitState.etherSend || submitState.generateSTT) && <LinearProgress /> }
<List dense={true}>
<ListItem>
<Typography variant="display1">
Address
</Typography>
{
<Button className={classes.button} color="primary" aria-label="Generate STT" onClick={this.generateSTT} disabled={submitState.generateSTT}>
<AddIcon className={classes.icon} />
Generate 5K STT (only on dev)
</Button>
}
</ListItem>
<ListItem className={classes.root}>
<ListItemIcon>
<KeyIcon />
</ListItemIcon>
<ListItemText
primary={walletAddress}
secondary="Address"
/>
</ListItem>
<ListItem className={classes.root}>
<ListItemIcon>
<NumberIcon />
</ListItemIcon>
<ListItemText
primary={nonce}
secondary="Nonce"
/>
</ListItem>
<ListItem className={classes.root}>
<ListItemIcon>
<BalanceIcon />
</ListItemIcon>
<ListItemText
primary={addressETHBalance}
secondary="ETH Balance (wei)"
/>
<ListItemText
primary={addressSTTBalance}
secondary="STT Balance"
/>
</ListItem>
</List>
<List dense={true}>
<ListItem>
<Typography variant="display1">
Relayer
</Typography>
<Button className={classes.button} color="primary" aria-label="Add ether" onClick={this.sendEther} disabled={submitState.etherSend}>
<AddIcon className={classes.icon} />
Send ether
</Button>
</ListItem>
<ListItem className={classes.root}>
<ListItemIcon>
<KeyIcon />
</ListItemIcon>
<ListItemText
primary={relayerAddress}
secondary="Address"
/>
</ListItem>
<ListItem className={classes.root}>
<ListItemIcon>
<BalanceIcon />
</ListItemIcon>
<ListItemText
primary={relayerEthBalance}
secondary="ETH Balance (wei)"
/>
<ListItemText
primary={relayerSTTBalance}
secondary="STT Balance (wei)"
/>
</ListItem>
</List>
<div className={classes.right}>
<CircularProgress />
<Typography>
Block<br />#{block}
</Typography>
</div>
</div>
</Fragment>;
}
}
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);

View File

@ -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 <div>
{ this.state.transactionError && <MySnackbarContentWrapper variant="error" message={this.state.transactionError} /> }
<Card className={classes.card}>
<CardHeader title="1. Transaction Data" />
<CardContent>
<form noValidate autoComplete="off">
<Grid container spacing={24}>
<Grid item xs={5}>
<TextField
id="to"
label="To"
value={this.state.to}
onChange={this.handleChange('to')}
margin="normal"
fullWidth
/>
</Grid>
<Grid item xs={2}>
<TextField
id="amount"
label="Amount"
value={this.state.amount}
onChange={this.handleChange('amount')}
margin="normal"
fullWidth
/>
</Grid>
<Grid item xs={2}>
<TextField
id="nonce"
label="Nonce"
value={this.props.nonce}
margin="normal"
fullWidth
InputProps={{
readOnly: true
}}
/>
</Grid>
<Grid item xs={2}>
<TextField
id="gasPrice"
label="Gas Price"
value={this.state.gasPrice}
onChange={this.handleChange('gasPrice')}
margin="normal"
fullWidth
/>
</Grid>
</Grid>
</form>
</CardContent>
<CardActions>
<Button color="primary" onClick={this.sign}>
Sign Message
</Button>
</CardActions>
</Card>
{ this.state.messagingError && <MySnackbarContentWrapper variant="error" message={this.state.messagingError} /> }
<Card className={classes.card}>
<CardHeader title="2. Message" />
<CardContent>
<TextField
id="signature"
label="Signed Message"
value={this.state.signature}
margin="normal"
fullWidth
InputProps={{
readOnly: true
}}
/>
<TextField
id="symKey"
label="Symmetric Key"
value={config.relaySymKey}
margin="normal"
fullWidth
InputProps={{
readOnly: true
}}
/>
<TextField
id="topic"
label="Whisper Topic"
value={this.state.topic}
margin="normal"
InputProps={{
readOnly: true
}}
/>
</CardContent>
<CardActions>
<Button size="small" color="primary" onClick={this.sendMessage} disabled={this.state.submitting}>
Send Message
</Button>
</CardActions>
</Card>
</div>;
}
}
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);

View File

@ -14,7 +14,7 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<script type="text/javascript" src="js/dapp.js"></script>
<script type="text/javascript" src="js/identity.js"></script>
</body>
</html>

View File

@ -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';

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="css/dapp.css">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<script type="text/javascript" src="js/sntcontroller.js"></script>
</body>
</html>

View File

@ -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 <Fragment>
<Header />
<BodySNTController />
</Fragment>;
}
}
ReactDOM.render(<App></App>, document.getElementById('app'));

View File

@ -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/**"]
},

View File

@ -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
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

1
test-dapp/test2_pk Normal file
View File

@ -0,0 +1 @@
da7cd207df98f7244dd4f92368c1a50e166b26b66bebb5275fc314e8acf8f570