Search code examples
javascriptnode.jsexpressethereumweb3js

Update user balance in realtime in the browser from private ethereum blockchain


I would like to have a website that updates live the user's wealth from a private Ethereum blockchain.

Current Solution (broken)

I opened a websocket to a private Ethereum blockchain that is mining, I would like to update my Coinbase balance on the front end. My code is as follow:

const express    = require("express");
const Web3       = require("web3");


var app  = express();

app.get("/", (req, res) => res.send("hello world from ping ether application"));
app.get("/ping-ether", function(req, res){


    var web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8546'));

    var event_newBlockHeaders = web3.eth.subscribe("newBlockHeaders", function(err, result){

        if (err){ 
         
            console.log(err) 

        } else {

            let acctPromise = web3.eth.getAccounts().then(function(accts){

                let balance = web3.eth.getBalance(accts[0]).then(function(bal){

                    console.log("user: ", accts[0]);
                    console.log("balance: ", bal);

                    res.end("new balance for user: " + bal)

                });

            });
        }

    });


});


// run the server
app.listen(3000, () => console.log("web app listening on port 3000"));

Clearly this is not updating live in the frontend even though the inner most callback is firing constantly as I can confirm on the console. I would like three things:

  1. How should I change this code so that the front end has a live ticker of the coinbase balance

  2. The code in general just smells bad with its nested promises. How can I refactor it so that I do not have to establish a websocket connection each time I navigate to /ping-ether?


Solution

  • Untested, but something like this should work:

    const express    = require("express");
    const Web3       = require("web3");
    
    var app  = express();
    var web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8546'));
    var balance = -1;
    
    web3.eth.getAccounts().then(accounts => {
      return web3.eth.subscribe("newBlockHeaders", (err, result) => {
        if (err) {
          console.log(err);
        } else {
          web3.eth.getBalance(accounts[0]).then(bal => {
            console.log("user: ", accounts[0]);
            console.log("balance: ", bal);
            balance = bal;
          });
        }
      })
    }).then(() => {
      app.listen(3000, () => console.log("web app listening on port 3000"));
    });
    
    app.get("/", (req, res) => res.send("hello world from ping ether application"));
    app.get("/ping-ether", function (req, res) {
      res.end("new balance for user: " + balance);
    });
    

    The main idea is to set up the websocket connection and subscription once, and then just respond to incoming web requests with the current balance. I also tried to clean up the nested promises by returning the subscription promise.