Ethereum, tokens & smart contracts.

Notes on getting started Part 9. Dapps & MetaMask

Previous notes in case you are just joining us:Part 1. Setting up.
Part 2. Web3.js/node.
Part 3. Solidity.
Part 4. Smart Contracts.
Part 5. Smarter Contracts.
Part 6. Tokens & Inheritance.
Part 7. ERC20 Token Standard.
Part 8. Crowdfunding and ICOs.

Ethereum Dapps ( These are early days ) :

Enter MetaMask

Note: As of this writing adding tokens on metamask is not supported, but it might be in the future.  I would recommend parity or mist for now for token management via UI.
window.addEventListener("load", function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== "undefined") {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
} else {
console.log("No web3? You should consider trying MetaMask!");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(
new Web3.providers.HttpProvider("https://localhost:8545")
);
}
// APP >web3.eth.getAccounts(function(error, accounts) {
if (!error) {
web3.eth.getBalance(accounts[0], function(error, balance) {
if (!error) {
console.log(
"Your account: " +
accounts[0] +
" has a balance of: " +
balance.toNumber() / 1000000000000000000 +
"Ether"
);
} else {
console.error(error);
}
});
} else {
console.error(error);
}
});
});
// "MetaMask - injected web3"
// "Your account: 0xbfca6ecdcb1a21d99befea4fd8cde79bc81e2c57 has a balance of: 0.799386868Ether"

ETHJS

window.addEventListener("load", function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== "undefined") {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
} else {
console.log("No web3? You should consider trying MetaMask!");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(
new Web3.providers.HttpProvider("https://localhost:8545")
);
}
// APPconst eth = new Eth(window.web3.currentProvider);
eth.accounts(function(error, accounts) {
if (!error) {
eth.getBalance(accounts[0], function(error, balance) {
if (!error) {
console.log(
"Your account: " +
accounts[0] +
" has a balance of: " +
Eth.fromWei(balance, "ether") +
"Ether"
);
} else {
console.log(error);
}
});
} else {
console.log(error);
}
});
});
// "MetaMask - injected web3"// "Your account: 0xbfca6ecdcb1a21d99befea4fd8cde79bc81e2c57 has a balance of: 0.799386868Ether"Note that unlike the first example, we have to include the ethjs library.
Note:  Calling the blockchain via meta Mask and other libraries is an Asynchronous affair (query the blockchain, wait for a response, do something with the result).In our examples we dealt with this with callbacks, but depending on your library and programming tastes you could deal with the async part with promises,here's an example chaining promises ( and using fat arrows ) in ethjs:const eth = new Eth(window.web3.currentProvider);
eth
.coinbase()
.then(coinbase => eth.getBalance(coinbase))
.then(balance =>
console.log(
"Your coinbase has a balance of: " + Eth.fromWei(balance, "ether")
)
)
.catch(error => {});
// "Your coinbase has a balance of: 0.799386868"Codepen hosted sample: https://codepen.io/k3no/pen/mBvvzO

Call a contract :

Note: As a reminder, here's the BLIPS Token address on the Kovan network: 0xCCeEF52Df5Ff1B80e63E3f211021bd0bAe5323D6
window.addEventListener("load", function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== "undefined") {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
} else {
console.log("No web3? You should consider trying MetaMask!");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(
new Web3.providers.HttpProvider("https://localhost:8545")
);
}
const tokenABI = [...]; // See Codepen for full ABI.
const eth = new Eth(window.web3.currentProvider);
const token = eth
.contract(tokenABI)
.at("0xCCeEF52Df5Ff1B80e63E3f211021bd0bAe5323D6");
token.symbol().then(function(tokenSymbol) {
console.log("Token Symbol :" + tokenSymbol[0]);
});
token.name().then(function(tokenName) {
console.log("Token Name :" + tokenName[0]);
});
token.totalSupply().then(function(supply) {
console.log("Total Supply :" + supply[0]);
});
eth
.coinbase()
.then(coinbase => token.balanceOf(coinbase))
.then(balance =>
console.log("Your coinbase has a balance of: " + balance[0] + " tokens")
)
.catch(error => {});
});

// Console :
// "MetaMask - injected web3"// "Token Symbol :BLIPS"// "Token Name :Blips Token"// "Total Supply :5200"// "Your coinbase has a balance of: 2000 tokens"
window.addEventListener("load", function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== "undefined") {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
} else {
console.log("No web3? You should consider trying MetaMask!");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(
new Web3.providers.HttpProvider("https://localhost:8545")
);
}

const coinbase = '0xbfca6ECdcB1a21d99bEfeA4Fd8CDE79Bc81e2c57';
const tokenAccount = '0xCCeEF52Df5Ff1B80e63E3f211021bd0bAe5323D6';
const eth = new Eth(window.web3.currentProvider);
// not meant for production since it would fire every time you load the page... eth
.sendTransaction({
from: coinbase,
to: tokenAccount,
value: Eth.toWei(0.05, 'ether'),
gas: '3000000',
data: '0x',
})
.then(result => {
console.log("Tx:" + result);
})
.catch(error => {});
});
"Tx:0x23fa03376136f7f23048b87affc0f28a1ea167ccce81833bf9417af2275c6896"

BLIPS DAPP

let tokenABI = [...]; // check the source code for the full ABI
let tokenAddress = "0xCCeEF52Df5Ff1B80e63E3f211021bd0bAe5323D6";
let token;
$("#mmAlert").hide();
$("#txSuccess").hide();
// Connect to MetaMask :
window.addEventListener("load", function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== "undefined") {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
} else {
console.log("No web3? You should consider trying MetaMask!");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(
new Web3.providers.HttpProvider("https://localhost:8545")
);
}
Dapp();
});
function Dapp() {
// Init eth:
const eth = new Eth(window.web3.currentProvider);
const token = eth.contract(tokenABI).at(tokenAddress);
// Are we logged into MetaMask :
window.web3.eth.getAccounts(function(err, accounts) {
if (err != null) console.error("An error occurred: " + err);
else if (accounts.length == 0) {
$("#mmAlert").show();
// Prompting for reload, but ideally you should check for account changes
} else {
console.log("User is logged in to MetaMask");
$("#mmAlert").hide();
}
});
// Coinbase Account balance:
eth
.coinbase()
.then(coinbase => eth.getBalance(coinbase))
.then(
balance =>
(document.getElementById("accountBalance").innerHTML = Eth.fromWei(
balance,
"ether"
))
)
.catch(error => {});
// BLIPS supply:token.totalSupply().then(function(supply) {
document.getElementById("supply").innerHTML = supply[0];
});
// BLIPS Balance:
eth
.coinbase()
.then(coinbase => token.balanceOf(coinbase))
.then(
balance =>
(document.getElementById("blipsBalance").innerHTML = balance[0])
)
.catch(error => {});
}
// Get Some BLIPS ! // vanilla JS...
document.getElementById("buy").onclick = function(e) {
e.preventDefault();
var eth = document.getElementById("amount").value;
// Note: Offloading balance validation to metaMask,
// but ideally you should also validate here
// Against Balance.
// Some basic number validation...
if ($.isNumeric(eth) && eth > 0) {
$("#amount").removeClass("is-invalid");
getBlips(eth);
} else {
$("#amount").addClass("is-invalid");
}
};
// Jquery ...
$("#reload").click(function() {
Dapp();
});
function getBlips(amount) {
console.log("Will try to exchange" + amount + "for Blips");
const eth = new Eth(window.web3.currentProvider);
eth
.coinbase()
.then(function(coinbase) {
eth
.sendTransaction({
from: coinbase,
to: tokenAddress,
value: Eth.toWei(amount, "ether"),
gas: "3000000",
data: "0x"
})
.then(result => {
console.log("Tx:" + result);
$("#receipt").html(
'<a href="https://kovan.etherscan.io/tx/' +
result +
'" target="_blank">' +
result +
"</a>"
);
$();
$("#txSuccess").show();
});
})
.catch(error => {});
}
Dependencies:- MetaMask Chrome Plugin.
- CSS: bootstrap 4, font-awesome
- JS: (Babel); Jquery-min, ethjs(from their repo), bootstrap 4
Disclaimer: This is a minimal Dapp for educational purposes, it could be improved in a number of ways( modularity,security,UI,UX,features).

Dapp Developing Considerations :

https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#ear-listening-for-selected-account-changesvar accountInterval = setInterval(function() {
if (web3.eth.accounts[0] !== account) {
account = web3.eth.accounts[0];
updateInterface();
}
}, 100);

Better DAPPs

Note: The future might be brighter, the biggest pain point with MetaMask is that it is a chrome plugin, so it requires your users to use chrome and download a plugin, a new library: Mascara , is in the works. This library or others like it would allow you to simply include a light Ethereum client you could interact via javascript, making integration with the blockcahin dramatically easier. 

SERVER SIDE DAPPS :

Friendly Recap:

✨✨  Now a Book !  ✨✨If you are looking for an introduction to Ethereum, Solidity and Smart Contracts these notes were edited and revised into a convenient book !
Available in ebook and paperback:
https://www.amazon.com/dp/B078CQ8L7V

AI, Software Developer, Designer : www.k3no.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store