The one factor that drives change is the ever-changing ecosystem, and even though I think it causes concern among people who are new to the change, I find that a little bit of curiosity and understanding can change the way we use technology.
I consistently create web applications that use Ethereum and I do take advantage of the wonderful toolset I use to build this on a daily basis. Ethereum is a technology that even in its primitive stage and not being around for it to filter through with time, is just as great to use. Through this article I want people to see that the use of Ethereum technology can indeed work in tandem with new age web developer workflows. I can show you how simple it is to merge the functions of Ethereum into developing any web application – I assure you, you can start today!
I am indeed a huge pro-Ethereum technology fan and find myself championing a cause to shine a light on many hardcore web developers the usefulness of Ethereum, by putting together bits of informative knowledge in the best way possible. I recommend that you go through the correct citations when you reach each step for a more in-depth understanding as you work your way through it. I aim at making this article reflect how things fit it and everything that goes into it.
Let’s embark on this new learning by me being your guide, and together with the Ethereum technology – we can create a better world.
Step 1: Get a blockchain
There isn’t a dearth of clients and so I would like to tell you to keep the worry about geth vs parity vs pyethapp cliental for later. I recommend testrpc for everyone who just wants to start developing and who needs a blockchain that suits all your needs. Here are your next steps after the installing testrpc:
testrpc
Well done! You are the owner of blockchain. Bear in mind that by default testrpc will not mine blocks, but you can specify the block interval when the –b flag shows. This is a useful configuration and one that I like for reasons that I would rather not touch upon now, nevertheless it is good to know that this is available.
Step 2: Talk to the blockchain
With the blockchain all set, you need to simply find a way of communicating with it. If you haven’t already downloaded web3.js, it does mean you are a novice. Not to worry, I am your guide and now would be the time to install web3 after which you need to open up the aconfig.js file and have this put in:
[code]
var web3 = require(‘web3’);
var web3_provider = ‘http://localhost:8545’;
var _web3 = new web3();
_web3.setProvider(new web3.providers.HttpProvider(web3_provider));
exports.web3 = _web3;
[/code]
Should you want to go back and check in with blockchain on the backend server, here is what you need to do:
[code]
var config = require(‘./config.js’);
config.web3.eth.X
[/code]
The X (i.e. whatever web3 API function you want) can be found here.
Step 3: Write some smart contracts
The one thing I could tell you right now is that you will be using solidity to write smart contracts. And no, smart contracting is not as scary as it sounds. Just follow the one thumb rule of keeping your contract simple and it will be easy from there on end.
I highly recommend that you always must keep the contracts you make as simple as you possibly can because:
In simple terms, everything you compute or store will mean using ether/gas which will burn your wallet. In terms of money value and something, I think you should pay attention to is the difference of $0.05 and $1.50, to make your contract. I strongly feel that the point of Ethereum should not change the database, and so I think it’s wise to keep the storage the least that you possibly can.
When things get more twisted and complicated it heralds for things to go south. It is certainly never a good thing when an error in the code can be detrimental to someone’s money which can’t be reverted. The last sentence should burn into your ethos when creating a contract.
There that’s done – Simple contracts mean business. Next step here we come.
Step 4: Deploy those smart contracts
I recommend checking out truffle, if you haven’t already, that is. I manage my tester contracts in a truffle directory and I simply thing this is great. What makes this awesome is that you can simply use it into your framework for testing. Consider this script in package.json:
[code]
“scripts”: {
“test”: “cd truffle && truffle deploy && truffle test ./myTruffleTest.js && cd .. && npm run myOtherTests”
}
[/code]
This just makes sure it is engages your contracts, runs your truffle test, and also runs regular tests — all in the same script!
Do I need to mention how cool the truffle tests are, as they infuse a sleuth of radical blockchain elements into the scope of your testing – awesome right? And you can channel this knowledge or information to your test suite in a number of ways. What I do – is save contract addresses in a config file and then transfer into my regular mocha tests by using a truffle test. I can interact with any test via web3.js, once I have the proper addresses. I just think that its best to learn on the go and see what is best for you.
On track again, use the step below to deploy your smart contract on the truffle directory:
truffle deploy
Always keep testrpc running on another window! What this does is print the address of your newly engaged contract, which will come in useful later. I did mention earlier that you can save this address by programming in on a truffle test, but in the meantime just copy and paste it into the config.js file you created. Do this:
[code]
exports.contract_addr = ‘0xe73e8e0a4442e140aea87a4b150ef07b82492500’
[/code]
Step 5: Make a smart contract call
With the contract done and set, the next step will be to call the contract. These contracts are with pure hex strings. There are libraries that make it simpler but I am simply a stickler for the old school and I aim to put you on the right path.
Things need to be in hex and I advise you to ensure that it is, you can check the appendix for details(Numbers, strings, etc.). Then check if the words in Ethereum are 256 bits, in doing this you would have to left-pad all in zeros to 64 characters. Then check to see if the types are declared canonically into the definition of the functions.
Here’s an instance just to make you understand things better:
[code]
function add(uint x, uint y) public constant returns (uint) {
return x + y;
}
[/code]
If you want to put 1 and 2 together, then here’s what you can do to call this function:
1: Take the first 4 bytes of the keccak 256 hash of your tightly packed, canonical function definition.
Say what? Well, I didn’t make this up, but you can just type your function declaration into this website and take the first 8 characters. What do I mean by canonical? Well, in Ethereum there are canonical types and shorthand types (e.g. uint256 is uint’s canonical type). I actually don’t know where they’re all defined, but check out the Ethereum ABI definition for examples as well as this post.
This is what the declaration would look like:
[code]
add(uint256,uint256)
Which returns the keccak256 hash:
771602f7f25ce61b0d4f2430f7e4789bfd9e6e4029613fda01b7f2c89fbf44ad
Of which the first 4 bytes (8 characters) are:
771602f7
[/code]
2: Pad your parameters to 256 bits
This one’s a little easier to get a grasp on:
[code]
x=1 is:
0000000000000000000000000000000000000000000000000000000000000001
y=2 is:
0000000000000000000000000000000000000000000000000000000000000002
And together they are:
00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002
3: Pack everything together and add a 0x prefix
Pretty self explanatory:
0x771602f700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002
—
Now that we have our payload, we can call the contract with web3:
var config = require(‘./config.js’);
var call = ‘0x771602f700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002′
var to = config.contract_addr;
var res = config.web3.eth.call({ to: to, data: call });
After that, you should get back 3 for res. Actually, you’ll get a BigNumberobject:
res.toString()
>’3’
[/code]
I think you need to read this to understand the use of BigNumbers throughout your app.
And if this isn’t cutting it out, use the library I mentioned earlier.
Hold on a moment, I am not done yet! This was only a way of letting you call a contract, not write it. If you want to write it like update state, this will definitely not work. For that, you will need to use your private key and sign a transaction, and for that, you will need some ether.
Step 6: Setup your account
To setup account go back to truffle. Add this to the test:
[code]
var keys = require(`${process.cwd()}/../test/keys.json`);
it(‘Should send me some ether.’, function() {
assert.notEqual(keys.me.addr, null);
var eth = 1*Math.pow(10, 18);
var sendObj = {
from: accounts[0],
value: eth,
to: keys.me.addr
}
Promise.resolve(web3.eth.sendTransaction(sendObj))
.then(function(txHash) {
assert.notEqual(txHash, null);
return web3.eth.getBalance(keys.me.addr)
})
.then(function(balance) {
assert.notEqual(balance.toNumber(), 0);
})
})
[/code]
Important note: we are actually sending 1 ether which is the same as 10^18 wei. We always make calls/transactions with wei values.
I think with all that done I am going to digress and skip a step. You now need to get an Ethereum account, which can be got from a private/public key pair that you generate. For management on the backend, I absolutely recommend using eth-light wallet. (Check the appendix for more details on the subject).
I will play this down by introducing a pretend variable hardcoded in my config.js:
[code]
exports.me = {
addr: “0x29f2f6405e6a307baded0b0672745691358e3ee6”,
pkey: “8c2bcfce3d9c4f215fcae9b215eb7c95831da0219ebfe0bb909eb951c3134515”
}
[/code]
Mandatory reminder: Upload your private key to github and be sure to never let anyone know of this. You can also publish it on Medium if it has money on it.
And with that reminder, once you check back to the test you will notice ether is being moved back from accounts, which already has a cluster of ether, me.addr, which can be found in your config file.
Step 7: Transacting with your smart contracts
With ether available and accessible in your account, go on and spend it. Here are 3 steps to spend it:
Transfer it as value to another address:
Calling a contract function that updates the state of the network which in turn requires gas to incentivize the miner who will process your update.
Call a contract that updates state, but also accepts ether as payment (FYI there is a payable modifier in solidity) — value will be sent and you will also have to pay for gas.
I would like to state here that the next steps that are done will fall in Category #2. Hypothetically if we have the following function that keeps track of a user’s balance of something:
[code]
function addUserBalance(uint balance)
public returns (bool) {
if (!accounts[msg.sender]) { throw; }
if (accounts[msg.sender].balance + balance < accounts[msg.sender].balance) { throw; }
accounts[msg.sender].balance += balance;
return true;
}
[/code]
Note the second if statement, which is necessary because adding and subtracting in solidity can lead to numerical overflow and underflow — be careful! Also note the undeclared msg object living in your function scope. This has all sorts of neat stuff you can reference in your function.
By sending a transaction we call this function to update the global state of the network to state the below:
The balance of msg.sender’s account, within the scope of this contract, has been increased by balance.
It is not within our reach to update the state ourselves, so we need to rely on a miner to do it. For their services, one can make a wager by paying with gas, aka ether.
We need to use ABI once again to call on this function appropriately:
[code]
addUserBalance(uint256) –> 22526328 –> 0x225263280000000000000000000000000000000000000000000000000000000000000001
[/code]
This data must be used from an unsigned transaction:
[code]
var data = ‘0x225263280000000000000000000000000000000000000000000000000000000000000001’;
var nonce = config.web3.eth.getTransactionCount(keys.me.addr);
var gasPrice = 20 * Math.pow(10, 9);
var gasLimit = 100000;
var txn = {
from: config.me.addr,
to: config.contract_address,
gas: `0x${gasLimit.toString(16)}`,
gasPrice: `0x${gasPrice.toString(16)}`,
data: data,
nonce: `0x${nonce.toString(16)}`,
value: ‘0x0’
}
[/code]
I made it crystal clear above that gas which is ether is required to make a transaction (i.e. update the state). One can describe gas * gasPrice as the amount of wei a miner can spend to execute your transaction. I must warn you that if your transaction overseeds the operational cost, the miner will not be able to update the state and you can say bye to the ether as well. On the brighter side if a lesser amount of your ether is used you will be reimbursed duly.
If this object is submitted to the network, it will not be successful for there is no evidence that I am Okaying this transaction. One never knows if some unknown person can add a billion to my balance – and my guess is that we have many generous souls waiting to do that (NOT LIKELY)!
That being said I now use my private key to sign the transaction. I go back jolting your memory into recollecting the information you would now have in your config file that I warned you never to hold dear to yourself – Here what you do with it:
[code]
var Tx = require(‘ethereumjs-tx’);
var privateKey = Buffer.from(config.me.pkey, ‘hex’)
var tx = new Tx(txn);
tx.sign(privateKey);
var serializedTx = tx.serialize();
[/code]
Sign a transaction object with that private key by using one of my favorite libraries . What should reflect is this:
[code]
0xf8aa808504a817c800830f424094a0f68379088f9aee95ba5c9d178693b874c4cd6880b844a9059cbb000000000000000000000000053b2188b0b100e68299708864e2ccecb62cdf0d000000000000000000000000000000000000000000000000000000746a5288001ca01f683f083c2d7c741a1218efc0144adc1749125a9ca53134b06353a8e4ef72afa07c50fb59647ff8b8895b75795b0f51de745fa5987b985f7d1025eb346755bca0
[/code]
Voila, we can now submit this to the blockchain via web3. What this will now do is return with a hash of the transaction given (I would like you to note that this is not a proof that the transaction was a success!!).
[code]
var txHash = config.web3.eth.sendRawTransaction(raw_txn);
[/code]
And it will look much like this:
[code]
0xac8914ecb06b333a9e655a85a0cd0cccddb8ac627098e7c40877d27a130a7293
[/code]
An optional last step, but one that I deem as significant as it verifies if your transaction has been accepted and furthermore processed: it’s called getting an acknowledgment!
[code]
var txReceipt = config.web3.eth.getTransactionReceipt(txHash);
[/code]
If you notice that your transaction reflects a null then there is a high likelihood your transaction was not picked up – in all intents and purposes you may have used the wrong private key. I do not want to go into the various reasons that your transaction may have failed, for there are several.
Maybe just another one to justify this process you have embarked on— if your gasUsed is equal to the total gas sent, it means your function call failed. What this means is that your contract may have overthrown or that you simply have not provided enough ether.
That about sums it up!
Conclusion
I am aware that this was a plethora of information to have gone through.
My suggestion is not be overwhelmed by the new concept and the volume of the article, but by rendering some time to each step, experimenting and going through the documentation for the same. In all use this article as a check point if you will.
In my opinion, to be a well versed Ethereum developer it is necessary to master the sum of what I have drawn out for you in this article. This is most of the battle won!
Start dabbling and pique your interest further! Needless to say, that life is getting easier with the amazing set of tools being made available to us. I welcome each of you to this wonderful platform.
Appendix:
Hex strings:
I realized I didn’t choose the best example for hexadecimal numbers (since 1 is the same value in both hex and base10 notation). If you were to instead call our add function with this as one of the parameters:
[code]
0000000000000000000000000000000000000000000000000000000000000100
[/code]
You would actually be adding 256. So probably what’s good practice for any function call is to do the following:
[code]
var _x = 100;
var x = `${x}.toString(16)`;
[/code]
This will ensure you are always calling with hex values.
Keys and profiles
If you’re stuck on how to generate keys, you can go ahead and look at this file I use in my apps:
[code]
/**
* generate a test keypair
*/
var keystore = require(‘eth-lightwallet‘).keystore;
var Promise = require(‘bluebird’).Promise;
var crypto = require(‘crypto’);
var fs = Promise.promisifyAll(require(‘fs’));
var jsonfile = Promise.promisifyAll(require(‘jsonfile’));
var KEYSTORE_DIRECTORY = `${process.cwd()}/test`;
var name = process.argv[2];
var password = “test”;
/**
* Run the generator
*/
createKeystore(password)
.then(function(ks) {
return saveProfile(name, ks.keystore, ks.privateKey, ks.address); })
.then(function(saved) { console.log(‘saved’, saved); })
.catch(function(error) { console.log(error); });
/**
* Utility Functions
*/
function createKeystore(_password) {
return new Promise(function(resolve, reject) {
var password = Buffer(_password).toString(‘hex’);
keystore.createVault({ password: password }, function(error, ks) {
if (error) { reject(error); }
ks.keyFromPassword(password, function(error, dKey) {
if (error) { reject(error); }
ks.generateNewAddress(dKey, 1);
var address = `0x${ks.getAddresses()[0]}`;
var privateKey = ks.exportPrivateKey(address, dKey);
var keystore = JSON.parse(ks.serialize());
resolve ({ address, privateKey, keystore });
});
});
});
}
function saveProfile(name, keystore, privateKey, address) {
return new Promise((resolve, reject) => {
jsonfile.readFileAsync(`${KEYSTORE_DIRECTORY}/keys.json`, {throws: false})
.then(function(PROFILES) {
var profiles = PROFILES || {};
profiles[`${name}`] = {
keystore,
privateKey,
address
};
console.log(‘profiles’, profiles)
return profiles;
})
.then(function(_profiles) {
return jsonfile.writeFileAsync(`${KEYSTORE_DIRECTORY}/keys.json`, _profiles, {spaces: 2});
})
.then(function() { resolve(true); })
.catch(function(error) { reject(error); });
})
}
[/code]
This will save a keystore that you name in a file called test/keys.json. You can run it with plain old node keygen.js <account_name> and you should be good. I use this for test accounts, but you can reuse the functions if you need to e.g. automatically generate accounts.
Leave a Reply