6-Figure Blockchain Developer

11. Ethereum accounts: EOA vs contracts

In Ethereum, there are 2 kind of addresses:

  • Externally owned accounts (EOA)
  • Contract accounts

EOA Accounts

EOA accounts are controlled by humans, or by code outside the Blockchain. They have a private key that allow to sign transactions.

Contract Accounts

Contract accounts are smart contracts.

They don’t have private keys. Instead, they are controlled by their code.

Contrary to EOA, they can’t initiate transactions. It’s always an EOA that first sends a transaction and put in motion a contract account. The contract accounts react to transactions according to the code of the smart contract.

Account fields

These EOA and contract accounts are associated to these fields:

  • balance: That’s the balance of Ether associated to the address. Both EOA and contract accounts can own some Ether
  • data: For contract accounts, that’s the data of the smart contract. For EOA, that’s empty
  • code: For contract accounts, that’s the code of the smart contract, For EOA, that’s empty
  • nonce: For EOA, that’s the number of transactions that was sent by the address. This prevents what we call replay attack where the same transaction is sent multiple times until the funds of an address are drained.

And for contract accounts, a nonce is the number of smart contracts that were created by this address. Just to be clear, that means a smart contract can create other smart contracts.

12. Ethereum networks: Local, public testnets & mainnet

Ethereum can run on several networks:

  • These networks are completely isolated from each other. What happens on a network has 0 impact on others
  • The same address can be re-used on several networks
  • The balance of Ether you have of one network is completely independent from the balance of Ether on another network
  • You can’t transfer Ether between these different networks

Each network has a different purpose, and it’s important to know them as a Blockchain developer.

Let’s see this!

Ethereum clients

To run the different Ethereum network, you need to use a software called an “Ethereum client”.

There are several Ethereum clients, but the main ones are:

  • Geth , the Go implementation (that’s the most popular) - Link
  • Parity , the Rust implementation (focused on enterprises needs - now called “Open Ethereum”) - Link
  • Hyperledger Besu , a Java implementation (focused on entreprises needs) - Link

By the way, why do we need several clients? This is a very subjective, but the Ethereum community thinks that it makes Ethereum more robust. If a bug is found in one client, the rest of the network running on other clients can still run.

Mainnet

The main Ethereum network is called “mainnet”. That’s the “production” network where we have real Ether. End-users use smart contracts deployed on this network.

Because we manipulate real money on this network, you should make sure that your smart contract is well tested before deploying to mainnet.

Anybody can run a node on mainnet, and most Ethereum clients can be used, like Geth or Parity. However it’s not easy to run a node on mainnet because of the huge hardware requirements (disk, cpu usage etc…). We’ll talk about this more later in this course on the lecture about Infura

Public Testnets (Ropsten, Kovan, Rinkeby, etc…)

Public testnets are sandbox where you can safely test your smart contracts.

These testnets are supposed to reproduce accurately the conditions of mainnet, however they don’t manipulate real Ether. Instead they use “fake” ether. Don’t be afraid to spend or loose all your fake Ether, it does not matter at all.

In order to get some Fake Ether you need to use a “Faucet”, i.e a web app that gives free fake Ether to anybody:

Some testnets use the same Proof-Of-Work (POW) algorithm than mainnet and everybody can run his / her node. For other testnets that use the Proof-Of-Authority (POA) algorithm, you can’t run your own testnet node. But in general, developers don’t need to run their own testnet node.

If you wonder which public testnet you should choose, I recommend:

  • Rinkeby for testing done frequently
  • Ropsten, once in while. It’s slower than Rinkeby but closer to mainnet in terms of environment for running smart contracts

On this video I discuss more in details these different public testnets.

Private networks

It’s also possible to setup multi-node private networks. The use case is mainly for consortium of big companies like in the supply chain industry or banking. They are interested in the Blockchain technology but don’t want to sacrifice privacy, control, and scaling.

You can’t setup private networks with any Ethereum clients. Hyperledger Besu is one of the few clients that allows you to do that.

On this course we will not deploy smart contracts on a private network. However be aware that if you want to do this, you absolutely can. All the development tools we will use are compatible with private networks.

If you are interested in how to setup a private network with Hyperledger Besu, I have a tutorial series about it.

Local network

A local network is used for local development on your machine. It generally has a single node, which starts a fresh Ethereum Blockchain every time you resume your work.

It also uses some fake Ether. Contrary to public testnets, you don’t need to use any faucet to get some fake Ether. It’s much more simple. When you start your local network, you can setup some addresses to be pre-funded with some fake Ether.

The most popular implementation for local networks is Ganache. Ganache is a NodeJS Ethereum client developed by the creators of the Truffle framework. It’s very easy to install it with npm, it’s very easy to use, and it does not a lot of ressources to run on your computer.

PS: there are tutorials that show you how to setup a local network with “mainnet” implementations like Geth or Parity. But that’s more complicated than Ganache and less flexible. In most cases, I recommend sticking to Ganache.

3. NodeJS & npm

Most of the development tools we need are built with NodeJS, so we need to install it.

You will need NodeJS v12 or above. If you have a lower version, it might still work, but if you encounter problem please update your installation to v12 or above.

To install NodeJS, go to the website of NodeJS and download the latest “LTS” (Long-term support) installer.

When you install NodeJS, it also instalsl npm at the same time. npm is the package manager of NodeJS. We will need it to install all the required Blockchain development tools.

Instead of installing NodeJS from the official website, you can alternatively use tools like asdf or nvm to easily switch between different versions of NodeJS.

PS: we will only use NodeJS to install development tools and dependencies. We won’t code any backend in NodeJS. So if you don’t have a lot of experience in NodeJS, that’s fine.

4. Ganache

What is Ganache?

For local development, we need a local Ethereum Blockchain.

The most simple tool is Ganache, from the creators of Truffle (see next lecture for an introduction to Truffle). Ganache is a developer-friendly Ethereum client specially designed to run local Ethereum Blockchains.

The Blockchain of Ganache runs locally on your machine, and is isolated from mainnet and public testnets. This is a safe sandbox where you are free to experiment, make mistakes, without any real-life consequences.

It starts with 10 Ethereum addresses pre-funded with 10 “fake” Ether for you to play with.

Ganache CLI vs Ganache GUI

Ganache comes with a CLI version and a GUI version. The GUI version offers a nice visual interface on top of the CLI. But it also requires slightly more work to integrate to your Truffle project, and it’s less flexible.

So in our course we will use the CLI version.

Installing Ganache

We will use the version of Ganache CLI that comes already installed with Truffle (more on that framework in the next lecture). So we don’t need to install Ganache.

This being said, if you want to install separately Ganache (for more flexibility), you have 2 options:

  • Installing Ganache GUI with the npm package: npm install -g ganache-cli
  • Install Ganache GUI with the installer for your OS

Advanced features of Ganache

Ganache has some interesting advanced features like chain forking. We will not use these advanced features in this course but if you want to learn about this, you can check out this series on my youtube channel.

5. Truffle

What is Truffle?

Truffle is a framework for developing Ethereum smart contracts.

It is compatible with Solidity and Vyper, the 2 main languages for Ethereum smart contracts.

It is written in NodeJS, which makes it easily installable with npm.

You might wonder why do we need this framework, if we already have Remix, the online IDE for Solidity. That’s because Remix becomes too limited when we want to:

  • develop more advanced smart contracts
  • test smart contracts
  • develop full decentralized applications, with a smart contract AND a frontend

You can develop decentralized applications using a Truffle project BUT Truffle itself does not manage the frontend. It’s up to you to build your own frontend. More on how to do this integration later in this course.

In this course, we will use Truffle to build our decentralized applications.

Typical workflow

Truffle is a CLI tool. The workflow is like this:

  • Start a Truffle project with truffle init
  • Run a local development Blockchain with truffle develop (Truffle comes with its own version of Ganache)
  • Deploy your smart contract with migrate --reset (the reset flag is necessary to avoid some caching problems)
  • Deploy your frontend

We will see this in action when we will build our first decentralized application later in the course.

Installing Truffle

To install Truffle, go to your terminal and run npm install -g truffle.

You will need Truffle v5 to follow this course. You can checkout your version of Truffle and make sure everything works fine with truffle --version

Truffle documentation

You can find the official documentation of Truffle here. Careful not to be mistaken with the documentation of Ganache, Drizzle or Truffle teams. These are different projects.

6. Metamask

What is Metamask?

Metamask is the most popular Ethereum wallet. Users keep their Ether and also their tokens (ERC20, ERC721) on it. Most decentralized applications expect users to have Metamask. That’s the industry standard.

Metamask keeps the private keys of users and allow them to sign (i.e approve) Ethereum transactions. Your Dapp never touches directly these private keys, but only ask the user to confirm a transaction with a pop-up.

Metamask is distributed as a browser extension. It exists on Chrome or Firefox. On this course, we will use the chrome version, but Firefox probably also works fine.

What about decentralized applications on mobile? On mobile, browsers don’t allow browser extensions, so we can’t use “browser Metamask”.

However, recently, Metamask launched a mobile app on iOS, but that’s still in beta. When it will be ready, it will have a web view (i.e a browser embedded in a mobile app) where users will access your Dapp. You don’t have anything special to do on the web frontend for this to work, except make it responsive.

Installing Metamask

Go to the Metamask page on the chrome extension store and install it.

You might be prompted to setup your Metamask account, but you can ignore this for now. We will do this later in this course when we build our first decentralized application.

7. Infura

What is Infura?

Infura is an Ethereum API. It allows to easily deploy smart contracts to mainnet and public tesnets (Ropsten, Kovan, Rinkeby, etc…).

When you use Infura, it gives you access to an Ethereum node running on mainnet or on public testnets. You can send your transactions to these nodes, which will relay your transaction to the rest of the network.

It might not seem like much, but it saves from the huge pain of running your own Ethereum nodes:

  • This requires more than 1TB of hard drive storage on mainnet (ouch!)
  • And more than 30GB of hard drive storage on testnet

And I am not even mentioning a zillion other headaches. So really, Infura is a major help for Ethereum developers.

Infura offers free and paid accounts. For us (and for most developers), the free account is enough.

How to use Infura?

To deploy a smart contract to Infura you need to:

  • Create a project in Infura
  • Fund the deployment address (using a faucet in the case of testnet)
  • Add a configuration for this deployment in the configuration file of your Truffle project
  • Run the deployment with Truffle (ex: truffle migrate --reset --network ropsten)

We will see this in details later in this course in the section on smart contract deployment.

Create your Infura account

Go to the website of Infura and create an account if you don’t have one. We will use it later in this course.

8. Etherscan

What is Etherscan?

Etherscan is a blockchain explorer for Ethereum. It allows to explore visually what’s happening in the Ethereum Blockchain.

With Etherscan, you can:

  • Inspect address balances
  • Inspect transactions
  • Read the data of smart contracts
  • Verify that the code of a smart contract is what developers say it is
  • And more!

They also offers an API for developers.

Our of all these features, the most important one is the transaction inspection feature. Most users expect to be able to independently verify that a transaction has been processed by the Ethereum network. So it’s a standard practice to show a link to the transaction on Etherscan after sending a transaction. Metamask already does this default.

How to use Etherscan?

We won’t directly use this tool in this course, but as a Blockchain developer developer you should be able to at least:

  • inspect the balance of an address => just type an address in the search input
  • inspect transactions => same thing
  • Read the data of a smart contract => Just type the address of a smart contract, and you will see a “read” tab

Bonus: you can also use Etherscan on some public testnets by appending the name of the testnet to the url. Example: https://ropsten.etherscan.com.

2. Compilation

Before we start, make sure you activate the auto-compile feature in Remix. In the compile tab:

  • Select Solidity 0.6.0
  • Activate “auto-compile”

17. Challenge

Congrats for having finished this section! That was a big step forward! Now you know how to build a full decentralized application on Ethereum.

There are still some potential improvements we could bring to our Dapp. If you want to further train yourself, as a challenge try to fix these problems:

  • When we create or approve a transfer, we need to manually refresh the frontend to see the up-do-date data on the UI. That would be better if it could reload automatically. TIP: in createTransfer() and approveTransfer(), re-fetch transfers from smart contract and update the transfers state.
  • After we approve a transaction, we can still click on the approve button, even though the smart contract only allows one approval per address for each transfer. We could gray out the approve button after an approval. TIP: after we fetch the list of transfers in App.js, we could also fetch the approvals for each transfer, for the current address, and pass this info to the TransferList component.

18. Update to Solidity 0.8

1. Truffle config

Update Solidity version to 0.8.0

2. Migrations.sol

Update pragma statement to:


pragma solidity >=0.4.21 <0.9.0

3. Dex.sol

  • Update pragma statement Solidity 0.8
  • Remove pragma for ABIEncoderV2
  • Remove public from constructor

19. Update to Metamask

  • In client folder, install a new library to detect Metamask provider: npm install @metamask/detect-provider
  • In client/src/utils.js:
  • import detect-provider: import detectEthereumProvider from '@metamask/detect-provider';
  • Replace getWeb3() function by this one:

const getWeb3 = () =>

new Promise( async (resolve, reject) => {

let provider = await detectEthereumProvider();

if(provider) {

await provider.request({ method: 'eth_requestAccounts' });

try {

const web3 = new Web3(window.ethereum);

resolve(web3);

} catch(error) {

reject(error);

}

} reject('Install Metamask');

});

The detectEthereumProvider() function detects the web3 provider object injected by Metamask. Recent versions of Metamask fire a specific event when it’s loaded. The detectEthereumProvider() function leverages this instead of waiting for the more generic load event fired by the browser, which may or may not indicate that Metamask is loaded.

The enable() function of window.ethereumwas deprecated. Instead we useprovider.request({ method: ‘eth_requestAccounts’ })` to ask users permission to connect Metamask to our Dapp.

9. Deploying the Dapp to mainnet

(optional)

So we have deployed the smart contract and the frontend to Kovan, a public testnet.

It’s time to deploy to mainnet!

The process is exactly the same:

  • Deploy smart contract
  • Run the build for the frontend code
  • Upload the build folder to a server like netlify
  • Boom! You can use the Dapp on mainnet

The only differences are;

  • In Infura, select “mainnet” to get the correct endpoint
  • In your truffle-config.js file, you need to update the Infura endpoint url to the one for mainnet
  • This time you can’t get free ether with a mainnet faucet (that would be free money!). Instead, you need to first buy some Ether at an exchange like Cex or Coinbase. You don’t need much to get started. I would recommend buying for 5-10 USD of Ether. Each deployment should cost you between 20-30 cents.

2. How a decentralized exchange work

There are many kind of decentralized exchanges on Ethereum:

  • The more simple ones only allow to trade ERC20 tokens
  • while the most sophisticated ones allow you to trade more assets, with more advanced trading options like margin trading.

For your first DEX, we will keep it simple. You will soon realize that even a “simple” DEX is not so “simple”.

Let’s see the different parts we need to build.

Architecture of DEX

A DEX has the same architecture as any other decentralized application on Ethereum:

  • User wallet
  • Frontend (Web)
  • Smart contract (Blockchain)

The logic of the DEX is inside one or several smart contracts. In our case there will be just one smart contract.

A frontend is connected to the smart contract, so that end-user have a nice and easy-to-use interface to the smart contract. This frontend is most often a web frontend, but it can also be a mobile app.

The frontend connects to the user wallet. The user wallet will prompt the user for confirmation before sending any transaction to the smart contract. The user wallet can be any Ethereum wallet, like Metamask. It’s better if this Wallet is ERC20-aware, but it doesn’t have to.

When you build your DEX, to make it easier, I recommend to assume that users will have Metamask installed. In a production DEX, you can also support other wallets, like Ledger or Trust Wallet.

Full trading sequence

We have 2 traders, Bob and Alice:

  • Bob wants to buy 1 ABC token, at a price of up to 2 Ethers
  • Alice wants to sell 1 ABC token, for whatever price

This is the whole trading sequence:

  • Bob sends 2 Ethers to the DEX smart contract.
  • Bob creates a buy limit order (explained later) for a limit price of 2 Ethers, amount of 1 ABC token, and send it to DEX smart contract
  • Alice sends 1 ABC token to the DEX smart contract
  • Alice creates a sell market order (explained later) for an amount of 1 ABC token, and send it to DEX smart contract
  • The smart contract matches Bob and Alice order, and carry out the trade. Bob now owns 1 ABC token and Alice 2 Ethers
  • Bob withdraws his 1 ABC token from the DEX smart contract
  • Alice withdraws her 2 Ethers from the DEX smart contract

Tada! The trade is finished!

Traded Tokens

It’s possible to trade ERC20, ERC721, or even more advanced tokens like ERC1155. It does not impact much the logic of the contract. In all cases, we will only mostly need to interact with the functions to transfer tokens.

For your first DEX, it’s better to keep it simple and stick to ERC20 tokens only.

Additionally, you also need to decide what is the quote currency, i.e which asset will be used to quote the price of ERC20. Example: if the quote currency is Ether and the token ABC trades for 2, that means that you need 2 Ethers to buy 1 token.

The first DEXes only offered Ether, but more recent exchanges also quote prices with the Dai stablecoin.

But for your first DEX, again let’s keep it simple and use Ether.

Wallet

Before users can trade, they need to transfer their ERC20 tokens / Ether to the smart contract of the DEX.

They will:

  • click on a button in the frontend that it will initiate the transfer,
  • confirm the transaction with their wallet
  • and the Ethers / tokens will be sent at the address of the smart contract

You will also need to have a functionality that allow users to withdraw their tokens / Ether from the DEX smart contract, once they have finished to trade.

In your DEX smart contract, you will need to implement your own ledger to track the ownership of Tokens & Ether. The ledger will be incremented when assets are added, and decremented when assets are withdrawn.

Order types

Traders express their intent to trade by creating an order. An order has several fields:

  • Currency (the asset you want to trade)
  • Amount: how much you want to trade
  • Type: see below

There are 2 main types of orders:

  • Limit order, which specify a max / min limit price for buy / sell order.
  • Market order, where you agree to whatever price is on the market

Optionally, you can also implement an order cancellation feature. That means that after a trader send a limit order, it can be cancelled, provided it hasn’t been executed before.

Orderbook

The orderbook is the core part of the DEX. It:

  • Lists all limit orders
  • Matches incoming market orders against existing limit orders
  • Remove limit orders that were executed

Orderbooks follow a price-time algorithm. When an incoming market order arrive, the orderbook will try to match it with the market order that has the best price. If several limit orders have the same price, the one that was created first get matched in priority.

What if the amount of the market and limit order don’t match? Actually, that’s what will happen most of the time.

In this case, there are 2 possibilities:

  • Case 1: Amount of market order < amount of limit order. In this case the market order will be fully excuted, but the limit order will only be partially executed, with the unexecuted part remaining in the orderbook.
  • Case 2: Amount of market order > amount of limit order. In this case the limit order will be fully executed, and the orderbook will continue to try to match the remaining of the market order with other limit orders

Note 1: For case 2, it’s actually possible that there is not enough liquidity to match the remaining of the market order. You could decide to disallow partial matching of market order by rejecting any market order that can’t be matched entirely.

Note 2: To make your orderbook more simple, you can disallow limit orders that “cross the books”, i.e for example a buy order with a limit price so high that it would be matched instantly against limit sell orders on the other side of the orderbook.

Settlement

After a market and a limit order have been matched, you need to complete the transaction by sending the assets to their new owners:

  • Tokens need to be transferred from the seller to the buyer
  • and Ether needs to be transferred from the buyer to the seller

The trick is that these transfers only take place symbolically. As we discussed before, all the assets are held in the wallet of the DEX smart contract at the moment of the trade. So we only have to update the internal ledger of the DEX smart contract to reflect the asset transfer.

After the trade, the 2 traders will have to use the withdraw function of the wallet of the DEX smart contract to get back their asset.

he frontend will need to get the list of orders of the orderbook. So we need to create a function getOrders() for that.

JAVASCRIPT

`//Below solidity pragma statement (needed because we return an array of struct in function):
pragma experimental ABIEncoderV2;

//below the constructor:
function getOrders(
bytes32 ticker,
Side side)
external
view
returns(Order[] memory) {
return orderBook[ticker][uint(side)];
}`

12. List tokens

The frontend will need to get the list of tokens that can be traded. So we need to create a function getTokens() for that.

JAVASCRIPT

`//Add this below getOrders()

function getTokens() 
  external 
  view 
  returns(Token[] memory) {
  Token[] memory _tokens = new Token[](tokenList.length);
  for (uint i = 0; i < tokenList.length; i++) {
    _tokens[i] = Token(
      tokens[tokenList[i]].id,
      tokens[tokenList[i]].symbol,
      tokens[tokenList[i]].at
    );
  }
  return _tokens;
}`

13. Faucet for ERC20 tokens & Dai

We will need a mechanism to get some free tokens for testing our smart contract. We call this a faucet. Technically, it’s simply a function to add to our token contracts. Add the below function to the smart contracts of our 4 tokens: DAI, REP, ZRX and BAT.

JAVASCRIPT

function faucet(address to, uint amount) external { _mint(to, amount); }

23. Update to Solidity 0.8

1. Truffle config

Update Solidity version to 0.8.0

2. OpenZeppelin

Update Openzeppelin to version 4.0:


npm uninstall @openzepplin/contracts

npm install @openzepplin/contracts

2. Migrations.sol

Update pragma statement to:


pragma solidity >=0.4.21 <0.9.0

3. Dex.sol

  • Update pragma statement to Solidity 0.8
  • Remove pragma for ABIEncoderV2
  • Remove import to SafeMaths, since this behavior is now built-in in Solidity. You will also have to replace all the instances of .add(), .sub(), .mul(), .div() to standard arithmetic operators +, -, *, /
  • Remove public from constructor
  • Replace now by block.timestamp

4. Mocks

For each of the mock token:

  • Update pragma statement to Solidity 0.8
  • Remove import to ERC20Detailed, that’s removed from OpenZeppelin v3
  • Remove any mention of ERC20Detailed
  • Remove the decimals argument (last argument) in the constructor of ERC20(). OpenZeppelin ERC20 now defaults to decimals = 18
  • Remove the public keyword in the constructor

5. Test folder

Remove the 2 files that were there, I forgot to remove them and one of them is a Solidity file with a conflicting version of Solidity

24. Update to Metamask

  • In client folder, install a new library to detect Metamask provider: npm install @metamask/detect-provider
  • In client/src/utils.js:
  • import detect-provider: import detectEthereumProvider from '@metamask/detect-provider';
  • Replace getWeb3() function by this one:

const getWeb3 = () =>

new Promise( async (resolve, reject) => {

let provider = await detectEthereumProvider();

if(provider) {

await provider.request({ method: 'eth_requestAccounts' });

try {

const web3 = new Web3(window.ethereum);

resolve(web3);

} catch(error) {

reject(error);

}

} reject('Install Metamask');

});

Bonus 1: Useful Solidity snippets

In this gist you will find a bunch of useful Solidity snippets. Save yourself hours of googling and copy paste practical solutions from there!

Bonus 2: Directory of top Blockchain companies

Here is a list of high quality Blockchain companies on this godogle doc. This covers these specialities:

  • Developer Tools
  • DeFi
  • Game
  • Blockchain Development (i.e consultancies that build Blockchain applications for other companies)
  • Blockchain core developement (i.e companies that develop Blockchain tech itself)

There are also some notes to give you some context about each company.

Feel free to contact these companies for your job search, and good luck!