The async/await patern works with JavaScript Promises.
In order to make web3.js functions asynchronous, you need to pass a callback parameter:
As this API is designed to work with a local RPC node, all its
functions use synchronous HTTP requests by default.
If you want to make an asynchronous request, you can pass an optional
callback as the last parameter to most functions. All callbacks are
using an error first callback style:
web3.eth.getBlock(48, function(error, result){
if(!error)
console.log(JSON.stringify(result));
else
console.error(error);
})
If you want to then make this work with the async/await pattern, you will need to then wrap this asynchronous function into a promise, which is of the format:
new Promise( /* executor */ function(resolve, reject) { ... } );
Rather than do this over an over for each web3.js function, you can automate this process by creating a wrapper for javascript functions which includes the callback parameter and wraps it in a promise as so:
const promisify = (inner) =>
new Promise((resolve, reject) =>
inner((err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
})
);
This can then be used really simply with nearly all existing web3.js functions:
async function getBalance() {
var address, wei, balance
address = document.getElementById("address").value;
wei = promisify(cb => web3.eth.getBalance(address, cb)) // simply wrap web3.js functions with the promisify function, also passing in a callback parameter
try {
balance = web3.fromWei(await wei, 'ether')
document.getElementById("output").innerHTML = balance + " ETH";
} catch (error) {
document.getElementById("output").innerHTML = error;
}
}
All of this information can be found on my blog where you can also find other small projects I have done which use this pattern.
You have lots of questions listed. It's better to post one question at a time, to increase your chances of having them answered.
Let me address the most important ones.
Q1. Is there a way to include hardware wallets into truffle development network?
Yes, by using truffle console
and configure it to connect to testrpc
. With testrpc
, you can have whatever account you want funded (edit: this is not true - the accounts are actually private keys, which are not available using a HW wallet), by starting it like:
testrpc --account="0x8414315fe005b8f294020dfc61cfd13749fbc045b0c6abc31fbd1ee3f4ff3b41, 10000000000000000000" --account="0x566a9022cd3f0dfcc3dff657a6c578897d4b0300e335fa569a082b637e6bb273, 70000000000000000000" --account="0x90b4e47ca43b66fab5dbebfee464087b51923f73f649701ca485da313574fd5b, 80000000000000000000" --account="0x5d47b245c405d706fecbc5eb213819d20a2168ad696b352644ad0ffc87aef18e, 90000000000000000000"
Where the addresses are your trezor addresses.
Or you can start it with the seed of your trezor (I don't recommend this, unless you know for sure that the trezor is used on the live network):
testrpc -m 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'
Q2: Do you guys see anything wrong in the signing and sending process that we have followed?
I managed to sign eth transactions programmatically using the Ledger Nano S hardware wallet. The ledgerco js library that I used for signing the transactions, also returns the V, R and S parameters. I assume they are in the same format as the ones returned by Trezor's library, but I can't be sure of that.
Anyway, this is how I use V, R, S to create valid transactions:
console.log('Please sign transaction on device...');
//the signature is an object with keys v,r and s
const signature = await eth_utils.ledger.signTransaction_async(argv['derivation_path'], tx.serialize().toString('hex'));
//"hexify" the keys
Object.keys(signature).map( (key, index) => {
signature[key] = '0x'+signature[key];
});
//tx_raw is a js object that contains all the tx params, the one that was signed on the hw device
//(equivalent of your tx from your sendTransaction() function)
const tx_obj = { ...tx_raw, ...signature};
//re-create the Transaction using ethereumjs-tx
const signed_tx = new Transaction( tx_obj );
//signed_tx_hex needs to be broadcasted
const signed_tx_hex = '0x'+signed_tx.serialize().toString('hex');
And that's it.
Best Answer
Actually the answer can be found in the ethers.js doc.
Ethers.js supports web3 providers, so to get it to work metamask on browser is quite simple.