The future of Dapps programming with lightwallet & INFURA

I have been so busy lately ! 

I finally find some time to write an article that, I hope, will help a lot of you guys creating great DApps. 

How to use Web3js with INFURA 

So this is a new trend that I personally find pretty cool. The idea is to do 100% of the identity / transaction management in the browser and sending the signed transactions directly. 

Although in theory this approach is pretty awesome, it is still very cutting edge and the tools are not mature yet to work with them without any issues. 

Disclaimer

This is my experience with doing this. I'm not pretending my approach is the best and actually I would love to hear your feedback. How can I improve that? 

Dependencies

eth-lightwallet: This is the library I have used to generate the accounts. Right now it was used to generate accounts by using a user generated seed but hopefully in the future it will be able to use other means.
ethereumjs-tx: This library is needed to stringify the transaction so you can sign it.  
web3: This is the web3js library
hooked-web3-provider: This library is used to add code so you can sign the transactions locally instead of the Ethereum node
web3-provider-engine: This is also needed to tweak web3js so that most of the handling is done locally.

Configuration

const Transaction = require('ethereumjs-tx');
const ProviderEngine = require("web3-provider-engine");
const Web3Subprovider = require("web3-provider-engine/subproviders/web3.js");
const Web3 = require("web3");
const CacheSubprovider = require('web3-provider-engine/subproviders/cache.js');
const FixtureSubprovider = require('web3-provider-engine/subproviders/fixture.js');
const FilterSubprovider = require('web3-provider-engine/subproviders/filters.js');
const VmSubprovider = require('web3-provider-engine/subproviders/vm.js');
const HookedWeb3Provider = require("hooked-web3-provider");
const NonceSubprovider = require('web3-provider-engine/subproviders/nonce-tracker.js');
const RpcSubprovider = require('web3-provider-engine/subproviders/rpc.js');

Here are all the dependencies you require to use it all. 

Now it is time to put all of it together:


var seed = // put here your selected seed
    const accountPwd = 'this is my password'; //you can set a password here
    lightwallet.keystore.deriveKeyFromPassword(accountPwd, function (err, pwDerivedKey) {
        if (err) {
            console.error(err);
        }
        var ks = new lightwallet.keystore(seed, pwDerivedKey);
        // generate five new address/private key pairs
        // the corresponding private keys are also encrypted
        ks.generateNewAddress(pwDerivedKey, 1);
        self.accounts = ks.getAddresses();
        self.ks = ks;
        self.pwDerivedKey = pwDerivedKey;
        web3.eth.defaultAccount = add0x(self.accounts[0]);
    });
//lightwallet result can be saved as a JSON in the local storage or anywhere else. I haven't done that for my tests
// configure web3js
function add0x (input) {
      if (typeof(input) !== 'string') {
        return input;
      }
      else if (input.length < 2 || input.slice(0,2) !== '0x') {
        return '0x' + input;
      }
      else {
        return input;
      }
}

engine.addProvider(new FixtureSubprovider({
        web3_clientVersion: 'ProviderEngine/v0.0.0/javascript',
        net_listening: true,
        eth_hashrate: '0x00',
        eth_mining: false,
        eth_syncing: true
    }));

    // cache layer
    engine.addProvider(new CacheSubprovider());

    // filters
    engine.addProvider(new FilterSubprovider());

    // pending nonce
    engine.addProvider(new NonceSubprovider());

    // vm
    engine.addProvider(new VmSubprovider());
    var web3;
    // id mgmt
    const provider = new HookedWeb3Provider({
        host: nodeUrl, //this would be your INFURA url Morden or Main
        transaction_signer: {
            // Can be any object that implements the following methods:
            hasAddress: function (address, callback) {
                callback(null, true); //This means that every address is accepted. You could put some security here if needed
            },
            signTransaction: function (tx_params, callback) {
                var txOptions = {
                    gasPrice: web3.eth.gasPrice.toNumber(),
                  //For test purposes, this is the limit I have put. You could evaluate the call and set the estimated value here.
                    gasLimit: 3000000,
                    to: add0x(tx_params.to),
                    nonce: add0x(tx_params.nonce),
                    data: add0x(tx_params.data),
                    value: add0x(tx_params.value)
                };
                var rawTx = (new Transaction(txOptions)).serialize().toString('hex');
                var signedTx = lightwallet.signing.signTx(self.ks, self.pwDerivedKey, add0x(rawTx), self.web3.eth.defaultAccount);
                callback(null, add0x(signedTx));
            }
        }
    });

    web3 = new Web3(provider);

    // data source
    engine.addProvider(new RpcSubprovider({
        rpcUrl: nodeUrl
    }));

    // log new blocks
    engine.on('block', function (block) {
        console.log('================================')
        console.log('BLOCK CHANGED:', '#' + block.number.toString('hex'), '0x' + block.hash.toString('hex'))
        console.log('================================')        
    });

    // network connectivity error
    engine.on('error', function (err) {
        // report connectivity errors
        console.error(err.stack)
    });

// start polling for blocks
engine.start();

Important points

As you can see, I'm using a function called add0x. I had issues because sometimes, some values (such as addresses) where missing the "0x" in the beginning. The issue is that the error message is very misleading. So beware of where "0x" is needed and where it isn't. 

P.S: There are differences between Parity and Geth on this subject

Let's use web3js

And now you can use web3js :) 

First you need to create an interface for you smart contract

//the ABI has to be an object, not a string
const MyContract = web3.eth.contract(abi);
const MyContractInstance = MyContract.at(MyContractAddress);

Now you can use web3js as if you were using a "normal" node with RPC. 

Important Point

Again there is something to be aware of here, with this hooked web3js, you cannot do blocking calls. If you try, you will get an exception telling you that it is not possible.

Now what I didn't know is what does it mean exactly. It simply means that you HAVE TO give a callback function as the last parameter of your function call.

//here is an example of an integration with Angular and how I translate callback functions into Promises
function callMyConstantFunction() {
 var defer = $q.defer(); //This could be translated into a more standard Promise creation
     MyContractInstance.callMyConstantFunction.call(web3.eth.defaultAccount, function (err, result) {
            if(result.toNumber() === 0) {
                defer.resolve(null);
            }else {
                defer.resolve(result.toNumber());    
            }
        });
return defer.promise; 
}

Another point I tend to forget, "uint" returns a special object called BigNumber and you have to call toNumber() to get the actual value. 

Et voilà

And now you can have fun with your smart contracts and INFURA without worrying about your local node and more importantly, being able to create mobile web applications !!

Credits

I would like to thank Dan Finlay who helped me tremendously understanding how it works. I wouldn't have made it without him and his experience.