snt-gas-relay/test-dapp/app/components/callgasrelayed.js

393 lines
14 KiB
JavaScript

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 EmbarkJS from 'Embark/EmbarkJS';
import ErrorIcon from '@material-ui/icons/Error';
import Grid from '@material-ui/core/Grid';
import IdentityGasRelay from 'Embark/contracts/IdentityGasRelay';
import PropTypes from 'prop-types';
import STT from 'Embark/contracts/STT';
import SnackbarContent from '@material-ui/core/SnackbarContent';
import TestContract from 'Embark/contracts/TestContract';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import classNames from 'classnames';
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 CallGasRelayed extends Component {
constructor(props){
super(props);
this.state = {
topic: '0x4964656e',
to: '0x0000000000000000000000000000000000000000',
value: 0,
data: '0x00',
gasPrice: 0,
gasLimit: 0,
gasToken: "0x0000000000000000000000000000000000000000",
signature: '',
kid: null,
skid: null,
msgSent: '',
payload: '',
message: '',
web3W: null,
transactionError: '',
messagingError: '',
submitting: false
};
}
componentDidMount(){
EmbarkJS.onReady(() => {
web3.shh.addSymKey(config.relaySymKey)
.then((skid) => {
this.setState({skid});
const subsOptions = {
topic: [this.state.topic],
symKeyID: skid
};
EmbarkJS.Messages.listenTo(subsOptions, (error, message) => {
if(error){
console.error(error);
} else {
console.groupCollapsed("Message Sent");
console.log(message);
console.groupEnd();
}
this.setState({submitting: false});
});
EmbarkJS.Messages.listenTo({usePrivateKey: true}, (error, message) => {
if(error){
console.error(error);
} else {
this.setState({message: JSON.stringify(message.data, null, " ")});
}
});
});
});
}
handleChange = name => event => {
this.setState({
[name]: event.target.value
});
};
sign = (event) => {
if(event) event.preventDefault();
this.setState({
msgSent: false,
payload: '',
message: '',
transactionError: ''
});
IdentityGasRelay.options.address = this.props.identityAddress;
try {
IdentityGasRelay.methods.callGasRelayHash(
this.state.to,
this.state.value,
web3.utils.soliditySha3({t: 'bytes', v: this.state.data}),
this.props.nonce,
this.state.gasPrice,
this.state.gasLimit,
this.state.gasToken
)
.call()
.then(message => {
return web3.eth.sign(message, web3.eth.defaultAccount);
})
.then(signature => {
this.setState({signature});
});
} catch(error){
this.setState({transactionError: error.message});
}
}
sendMessage = event => {
event.preventDefault();
this.setState({
message: '',
messagingError: '',
submitting: true
});
try {
let jsonAbi = IdentityGasRelay._jsonInterface.filter(x => x.name == "callGasRelayed")[0];
let funCall = web3.eth.abi.encodeFunctionCall(jsonAbi, [
this.state.to,
this.state.value,
this.state.data,
this.props.nonce,
this.state.gasPrice,
this.state.gasLimit,
this.state.gasToken,
this.state.signature
]);
const sendOptions = {
ttl: 1000,
powTarget: 1,
powTime: 20,
topic: this.state.topic,
symKeyID: this.state.skid,
data: {
'address': this.props.identityAddress,
'encodedFunctionCall': funCall
}
};
EmbarkJS.Messages.sendMessage(sendOptions);
} catch(error){
this.setState({messagingError: error.message, submitting: false});
}
}
testContractDataSend = () => {
let jsonAbi = TestContract._jsonInterface.filter(x => x.name == "test")[0];
let funCall = web3.eth.abi.encodeFunctionCall(jsonAbi, []);
this.setState({data: funCall, to: TestContract.options.address});
}
testContractDataCall = () => {
TestContract.methods.val().call().then(value => this.setState({message: "TestContract.val(): " + value}));
}
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="value"
label="Value"
value={this.state.value}
onChange={this.handleChange('value')}
margin="normal"
fullWidth
/>
</Grid>
<Grid item xs={5}>
<TextField
id="data"
label="Data"
value={this.state.data}
onChange={this.handleChange('data')}
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={6}>
<TextField
id="gasToken"
label="Gas Token"
value={this.state.gasToken}
onChange={this.handleChange('gasToken')}
margin="normal"
fullWidth
select
SelectProps={{
native: true
}}
>
<option key={STT.options.address} value={STT.options.address}>
{STT.options.address} (STT)
</option>
<option key="0x0000000000000000000000000000000000000000" value="0x0000000000000000000000000000000000000000">
0x0000000000000000000000000000000000000000 (ETH)
</option>
</TextField>
</Grid>
<Grid item xs={2}>
<TextField
id="gasPrice"
label="Gas Price"
value={this.state.gasPrice}
onChange={this.handleChange('gasPrice')}
margin="normal"
fullWidth
/>
</Grid>
<Grid item xs={2}>
<TextField
id="gasLimit"
label="Gas Limit"
value={this.state.gasLimit}
onChange={this.handleChange('gasLimit')}
margin="normal"
fullWidth
/>
</Grid>
</Grid>
</form>
</CardContent>
<CardActions>
<Button color="primary" onClick={this.sign}>
Sign Message
</Button>
<Button size="small" onClick={this.testContractDataSend}>TestContract.methods.test().send()</Button>
<Button size="small" onClick={this.testContractDataCall}>TestContract.methods.test().call()</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>
<Card className={classes.card}>
<CardContent>
<Typography>
Message Received:
</Typography>
<pre>{this.state.message}</pre>
</CardContent>
</Card>
</div>;
}
}
CallGasRelayed.propTypes = {
classes: PropTypes.object.isRequired,
nonce: PropTypes.string.isRequired,
identityAddress: PropTypes.string
};
const variantIcon = {
error: ErrorIcon
};
const styles1 = theme => ({
error: {
backgroundColor: theme.palette.error.dark
},
icon: {
fontSize: 20
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing.unit
},
message: {
display: 'flex',
alignItems: 'center'
}
});
function MySnackbarContent(props) {
const {classes, className, message, variant, ...other} = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={classNames(classes[variant], className)}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<Icon className={classNames(classes.icon, classes.iconVariant)} />
{message}
</span>
}
{...other}
/>
);
}
MySnackbarContent.propTypes = {
classes: PropTypes.object.isRequired,
className: PropTypes.string,
message: PropTypes.node,
onClose: PropTypes.func,
variant: PropTypes.oneOf(['success', 'warning', 'error', 'info']).isRequired
};
const MySnackbarContentWrapper = withStyles(styles1)(MySnackbarContent);
export default withStyles(styles)(CallGasRelayed);