BitPay Demo for TabConf 2019 using Create React App, Material-UI, and Bitcore-Node
How to use Bitcoin-node's sockets and API endpoints to create a payment notification system.
Install create-react-app
npm install -g create-react-app
NPM packages used:
-
@material-ui/core - For Material-UI components
-
@material-ui/icons - Icons from Material-UI
-
notistack - Stacked snackbars
-
axios - To GET Bitcore-node APIs
-
socket.io-client - To use Bitcore-node sockets
Create a create-react-app called bitpay-demo and install the npm packages above
create-react-app bitpay-demo
yarn add @material-ui/core @material-ui/icons notistack axios socket.io-client
Add proxy settings to connect to Bitcore Node and change start script PORT=3006 in package.json
{
"name": "bitcore-charts",
"version": "0.1.0",
"private": false,
"proxy": "http://localhost:3000",
"dependencies": {
"@material-ui/core": "^3.9.0",
"@material-ui/icons": "^3.0.2",
"axios": "^0.18.0",
"notistack": "^0.4.1",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-scripts": "2.1.3",
"socket.io-client": "^2.2.0"
},
"scripts": {
"start": "PORT=3006 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
Delete all the files inside src folder except index.js and App.js.
We are going to use Material-UI component demos to quickly make a nice notification system.
Material-UI notistack Snackbar Component
Copy the notistack component into App.js.
Update index.js to render IntegrationNotistack component
import React from 'react';
import ReactDOM from 'react-dom';
import IntegrationNotistack from './App';
ReactDOM.render(<IntegrationNotistack />, document.getElementById('root'));
import axios from 'axios';
import io from 'socket.io-client';
const socket = io.connect('http://localhost:3000', { transports: ['websocket'] });
Under class App extends React.Component
state = {
message: 'This is a success message!',
balance: 0,
}
In the handleClickVariant function, change the static notification message to dynamically render based on the message state that we set above.
handleClickVariant = variant => () => {
this.props.enqueueSnackbar(this.state.message, { variant });
};
Change 'warning' to 'success' and edit the button text.
<Button onClick={this.handleClickVariant('success')}>Generate a Block on Bitcore RegTest!</Button>
Create a function called componentDidMount that connects to the Bitcore-node socket when the page is loaded.
componentDidMount = () => {
socket.on('connect', () => {
console.log('Connected to socket');
socket.emit('room', '/BTC/regtest/inv');
});
}
Next create a function called handleBlockEvent to get a block object from the Bitcore-node socket when a block is generated. The socket event name is 'block'.
Update the message in state to the block reward recieved using setState() function, then call the handleClickVariant function to trigger the notification.
handleBlockEvent = () => {
socket.on('block', block => {
let message = `Recieved Block Reward ${block.reward/100000000} BTC at ${new Date(block.time).toLocaleString()}`;
this.setState({message});
this.handleClickVariant('success')();
})
}
Try to generate some blocks in the Bitcoin Core debug console to see the notification!
generate 3
Next subscribe to the 'tx' socket to get data on a transaction payment, update the message state, then trigger the notification.
handleTxEvent = () => {
socket.on('tx', payload => {
let message = `Fee of -${payload.fee/100000000} BTC at ${new Date(payload.blockTimeNormalized).toLocaleString()}`;
this.setState({message: message});
this.handleClickVariant('error')();
message = `Recieved ${payload.value/100000000} BTC at ${new Date(payload.blockTimeNormalized.toLocaleString()}`;
this.setState({message: message});
this.handleClickVariant('success')();
})
}
Update componentDidMount to call handleTxEvent() and handleBlockEvent() on page load.
componentDidMount = () => {
socket.on('connect', () => {
console.log('Connected to socket');
socket.emit('room', '/BTC/regtest/inv');
this.handleBlockEvent();
this.handleTxEvent();
});
}
You should have a working notification system!!
We are going to create a card component that displays the balance of an address.
Make a new component file called BalanceCard.js inside src folder.
Get MediaControlCard component from @Material-UI
Copy the MediaControlCard component code into BalanceCard.js, then customize the card to include a dynamic balance.
function MediaControlCard(props) {
const { classes, theme, balance } = props;
return (
<Card className={classes.card}>
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography component="h5" variant="h5">
My Address Balance
</Typography>
<Typography variant="subtitle1" color="textSecondary">
{`${balance} BTC`}
</Typography>
</CardContent>
</div>
</Card>
);
}
import MediaControlCard from './BalanceCard';
Add our new MediaControlCard component in our render function of App component. Then pass the balance on state to MediaControlCard component as a prop.
render() {
return (
<React.Fragment>
<Button onClick={this.handleClickVariant('success')}>Generate a Block on Bitcore RegTest!</Button>
<MediaControlCard balance={this.state.balance} />
</React.Fragment>
);
}
Using axios, call a GET request to get the balance of an address. Then update the balance in state using setState().
To get your address in Bitcoin-core -> debug console
getaccountaddress ""
Change {your-address-here} to the account address.
handleGetBalance = () => {
axios.get('http://localhost:3000/api/BTC/regtest/address/{your-address-here}/balance')
.then(res => {
this.setState({balance: res.data.balance/100000000})
})
}
Add this.getBalance() in componentDidMount, handleTxEvent, and handleBlockEvent.
componentDidMount = () => {
socket.on('connect', () => {
console.log('Connected to socket');
socket.emit('room', '/BTC/regtest/inv');
this.handleGetBalance();
this.handleBlockEvent();
this.handleTxEvent();
});
}
handleBlockEvent = () => {
socket.on('block', payload => {
let message = `Recieved Block Reward ${payload.reward/100000000} BTC at ${new Date(payload.time).toLocaleString()}`;
this.setState({message: message});
this.handleClickVariant('success')();
this.handleGetBalance();
})
}
handleTxEvent = () => {
socket.on('tx', payload => {
let message = `Fee of -${payload.fee/100000000} BTC at ${new Date(payload.blockTimeNormalized).toLocaleString()}`;
this.setState({message: message});
this.handleClickVariant('error')();
message = `Recieved ${payload.value/100000000} BTC at ${new Date(payload.blockTimeNormalized).toLocaleString()}`;
this.setState({message: message});
this.handleClickVariant('success')();
this.handleGetBalance();
})
}
Finally a working card with a balance!
Check block, balance, or transaction information by running Insight - Bitcore's blockchain explorer.
Inside bitcore/packages/insight-previous folder run
npm start
Thanks for reading!