Open blockchains like Ethereum are secure because they are decentralized. That means that each user of Ethereum should control their own keys, which control access to their funds and contracts. Some users choose to give up control over their keys by using a third party custodian, such as an exchange wallet. In this book, we will teach you how to take control and manage your own keys.
With that control comes a big responsibility. If you lose your keys, you lose access to funds and contracts. No one can help you regain access - your funds will be locked forever. Here are a few tips to help you manage this responsibility:
-
When you are prompted to choose a password: make it strong, back it up and don’t share it. If you don’t have a password manager, write it down and store it in a locked drawer or safe.
-
When you are prompted to back up a key or mnemonic words, use pen and paper to make a physical backup. Do not leave that task for "later", you will forget.
-
Do not store key material (encrypted or not) in digital documents, digital photos, screenshots, online drives, encrypted PDFs, etc. Don’t improvise security. Use a password manager or pen and paper.
-
Before transferring any large amounts, first do a small test transaction (e.g., $1 value). Once you receive the test transaction, try sending it from that wallet. If you forgot your password or can’t send the funds for any other reason, it is better to find out with a small loss.
-
Do not send money to any of the addresses shown in this book. The private keys are listed in the book and someone will immediately take that money.
Ethereum’s currency unit is called ether, identified also as ETH or with the symbols Ξ (from the Greek letter "Xi" that looks like a stylized capital E) or (less often) ♦, for example 1 ether, or 1 ETH, or Ξ1, or ♦1
Tip
|
Use Unicode character 926 for Ξ and 9830 for ♦. |
Ether is subdivided into smaller units, down to the smallest unit possible, which is named wei. One ether is 1 quintillion wei (1 x 1018 or 1,000,000,000,000,000,000). You may hear people refer to the currency "Ethereum" too, but this is a common beginner’s mistake. Ethereum is the system, ether is the currency.
The value of ether is always represented internally in Ethereum as an unsigned integer value denominated in wei. When you transact 1 ether, the transaction encodes 10000000000000000000 wei as the value.
Ether’s various denominations have both a scientific name using the International System of units (SI), and a colloquial name that pays homage to many of the great minds of computing and cryptography.
Table Ether Denominations and Unit Names shows the various units, their colloquial (common) name and their SI name. In keeping with the internal representation of value, the table shows all denominations in wei (first row), with ether shown as 1018 wei in the 7th row:
Value (in wei) | Exponent | Common Name | SI Name |
---|---|---|---|
1 |
1 |
wei |
wei |
1,000 |
103 |
babbage |
kilowei or femtoether |
1,000,000 |
106 |
lovelace |
megawei or picoether |
1,000,000,000 |
109 |
shannon |
gigawei or nanoether |
1,000,000,000,000 |
1012 |
szabo |
microether or micro |
1,000,000,000,000,000 |
1015 |
finney |
milliether or milli |
1,000,000,000,000,000,000 |
1018 |
ether |
ether |
1,000,000,000,000,000,000,000 |
1021 |
grand |
kiloether |
1,000,000,000,000,000,000,000,000 |
1024 |
megaether |
An Ethereum wallet is your gateway to the Ethereum system. It holds your keys and can create and broadcast transactions on your behalf. Choosing an Ethereum wallet is difficult. There are many different options, with different features and designs. Some are more suitable for beginners and some are more suitable for experts. Even if you choose well now, you might decide to change your choice a year from now. Ethereum itself is constantly changing and the "best" wallets change as some manage to keep up and others don’t.
Don’t worry! If you choose a wallet and don’t like how it works, you can change wallets quite easily. All you have to do is make a transaction that sends your funds from the old wallet to the new wallet, or move the keys by exporting and importing. In Ethereum (as in all open blockchains), a transaction between your own wallets is indistinguishable from, and just as easy as, any other transaction.
To get started, we will choose three different wallets that we will be using throughout many of the examples: one mobile wallet, one desktop wallet and one web-based wallet. These three wallets are good choices for the examples in this book, as they represent a broad range of complexity and features. The choice of these wallets is not an endorsement of their quality or security. They are good enough for demonstration and testing and we have to start somewhere.
Starter wallets:
- MetaMask
-
MetaMask is a browser extension wallet that runs in your browser (Chrome, Firefox, Opera or Brave Browser). It is easy to use and convenient for testing, as it is able to connect to a variety of Ethereum nodes and test blockchains (see [testnets]).
- Jaxx
-
Jaxx is a multi-platform and multi-currency wallet that runs on a variety of operating systems including Android, iOS, Windows, Mac and Linux. It is often a good choice for new users as it is designed for simplicity and ease of use.
- MyEtherWallet (MEW)
-
MyEtherWallet is a web page-based wallet, that runs in any browser. It has multiple sophisticated features, which we will explore in many of our examples.
We’ll start by installing MetaMask on our desktop.
Open the Google Chrome browser and navigate to:
Search for "MetaMask" and click on the result showing the logo of a fox. You should see the extension’s detail page like this:
It’s important that you download the real MetaMask extension, as sometimes people are able to sneak malicious extensions past Google’s filters. The real one:
-
Shows the ID nkbihfbeogaeaoehlefnkodbefgpgknn in the address bar
-
Is offered by https://metamask.io
-
Has more than 500 reviews
-
Has more than 800,000 users
Once you confirm you are looking at the correct extension, click "Add to Chrome", to install it.
Once MetaMask is installed you should see a new icon (head of a fox) in your browser’s toolbar. Click on it to get started. You will be asked to accept the terms and conditions and then to create your new Ethereum wallet by entering a password:
Tip
|
The password controls access to MetaMask, so that it can’t be used by anyone with access to your browser. |
Once you’ve set a password, MetaMask will generate a wallet for you and show you a mnemonic backup, consisting of 12 English words. This backup can be used in any compatible wallet to recover access to your funds, should something happen to MetaMask or your computer. You do not need the password for this recovery, the 12 words are sufficient.
Tip
|
Backup your mnemonic (12 words) on paper, twice. Store the two paper backups in two distant secure locations, such as a fire resistant safe, a locked drawer or a safe deposit box. Treat the paper backups like cash of equivalent value as what you store in your Ethereum wallet. Anyone with access to these words gets your money. |
Once you have confirmed that you have stored the mnemonic securely, MetaMask will display your Ethereum account details:
Your account page shows the name of your account ("Account 1" by default), an Ethereum address (0x9E713… in the example) and a colorful icon to help you visually distinguish this account from other accounts. At the top of the account page you can see which Ethereum network you are currently working on ("Main Network" in the example).
Congratulations! You have set up your first Ethereum wallet!
As you can see on the MetaMask account page, you can choose between multiple Ethereum networks. By default, MetaMask will try to connect to the "Main Network". The other choices are public testnets, any Ethereum node of your choice, or nodes running private blockchains on your own computer (localhost):
- Main Ethereum Network
-
The main, public, Ethereum blockchain. Real ETH, real value, real consequences.
- Ropsten Test Network
-
Ethereum public test blockchain and network, using Proof-of-Work consensus (mining). ETH on this network has no value.
- Kovan Test Network
-
Ethereum public test blockchain and network, using Proof-of-Authority consensus (federated signing). ETH on this network has no value.
- Rinkeby Test Network
-
Ethereum public test blockchain and network, using Proof-of-Authority consensus (federated signing). ETH on this network has no value.
- Localhost 8545
-
Connect to a node running on the same computer as the browser. The node can be part of any public blockchain (main or testnet), or a private testnet (see [ganache]).
- Custom RPC
-
Allows you to connect MetaMask to any node with a geth-compatible Remote Procedure Call (RPC) interface. The node can be part of any public or private blockchain.
For more information about the various Ethereum testnets and how to choose between them, see [testnets].
Tip
|
Your MetaMask wallet uses the same private key and Ethereum address on all the networks it can connect to. Your Ethereum address balance on each Ethereum network will be different. Your keys may control ether and contracts on Ropsten, for example, but not on the Main Network. |
Our first task is to get our wallet funded. We won’t be doing that on the Main Network, because real ether costs money and handling it requires a bit more experience. For now, we will load our wallet with some testnet ether.
Switch MetaMask to the Ropsten Test Network. Then click "Buy", and click "Ropsten Test Faucet". MetaMask will open a new web page:
You may notice that the web page already contains your MetaMask wallet’s Ethereum address. MetaMask integrates Ethereum enabled web pages (see [dapps]) with your MetaMask wallet. MetaMask can "see" Ethereum addresses on the web page, allowing you, for example, to send a payment to an online shop displaying an Ethereum address. MetaMask can also populate the web page with your own wallet’s address as a recipient address if the web page requests it. In this page, the faucet application is asking MetaMask for a wallet address to send test ether to.
Press the green "request 1 ether from faucet" button. You will see a transaction ID appear in the lower part of the page. The faucet app has created a transaction - a payment to you. The transaction ID looks like this:
0x7c7ad5aaea6474adccf6f5c5d6abed11b70a350fbc6f9590109e099568090c57
In a few seconds the new transaction will be mined by the Ropsten miners and your MetaMask wallet will show a balance of 1 ETH. Click on the transaction ID and your browser will take you to a block explorer, which is a web site that allows you to visualize and explore blocks, addresses and transactions. MetaMask uses the etherscan.io block explorer, one of the more popular Ethereum block explorers. The transaction containing our payment from the Ropsten Test Faucet is shown in Etherscan Ropsten Block Explorer
The transaction has been recorded on the Ropsten blockchain and can be viewed at anytime by anyone, simply by searching for the transaction ID, or visiting the link:
Try visiting that link, or entering the transaction hash into the ropsten.etherscan.io website, to see it for yourself.
Once we’ve received our first test ether from the Ropsten Test Faucet, we will experiment with sending ether, by trying to send some back to the faucet. As you can see on the Ropsten Test Faucet page, there is an option to "donate" 1 ETH to the faucet. This option is available so that once you’re done testing, you can return the remainder of your test ether, so that someone else can use it next. Even though test ether has no value, some people hoard it, making it difficult for everyone else to use the test networks. Hoarding test ether is frowned upon!
Fortunately, we are not test ether hoarders and we want practice sending ether anyway.
Click on the orange "1 ether" button to tell MetaMask to create a transaction paying the faucet 1 ether. MetaMask will prepare a transaction and pop-up a window with the confirmation:
Oops! You probably noticed you can’t complete the transaction. MetaMask says "Insufficient balance for transaction". At first this will seem confusing: we have 1 ETH, we want to send 1 ETH, why is MetaMask saying we have insufficient funds?
The answer is because of the cost of gas. Every Ethereum transaction requires payment of a fee, which is collected by the miners to validate the transaction. The fees in Ethereum are charged in a virtual currency called gas. You pay the gas with ether, as part of the transaction.
Tip
|
Fees are required on the test networks too. Without fees, a test network would behave differently from the main network, making it an inadequate testing platform. Fees also protect the test networks from Denial-of-Service attacks and poorly constructed contracts (e.g. infinite loops), much like they protect the main network. |
MetaMask calculates that this transaction will consume 3 GWEI, which stands for 3 gigawei. Wei is the smallest subdivision of the ether currency, as we discussed in Ether currency units.
All this to say: to make a 1 ETH transaction costs 1.000063 ETH. MetaMask confusingly rounds that down to 1 ETH when showing the total, but the actual amount you need is 1.000063 ETH and you only have 1 ETH. Click "Reject" to cancel this transaction.
Let’s get some more test ether! Click on the green "request 1 ether from the faucet" button again and wait a few seconds. Don’t worry, the faucet should have plenty of ether and will give you more if you ask.
Once you have a balance of 2 ETH, you can again. This time, when you click on the orange "1 ether" donation button, you have sufficient balance to complete the transaction. Click "Submit" when MetaMask pops-up the payment window. After all of this, you should see a balance of 0.999937 ETH, accounting for the 1 ETH you sent to the faucet and the 0.000063 in gas costs.
By now you have become an expert in using MetaMask to send and receive test ether. Your wallet has received at least two payments and sent at least one. Let’s see all these transactions, using the ropsten.etherscan.io block explorer. You can either copy your wallet address and paste it into the block explorer’s search box, or you can have MetaMask open the page for you. Next to your account icon in MetaMask, you will see a button showing three dots. Click on it to show a menu of account-related options:
Select "View Account on Etherscan", to open a web page in the block explorer, showing your account’s transaction history:
Here you can see all the transaction history of your Ethereum address. It shows all transactions recorded on the Ropsten blockchain, where your address is the sender or recipient in the transaction. Click on a few of these transactions to see more details.
You can explore the transaction history of any address. See if you can explore the transaction history of the Ropsten Test Faucet address (Hint: it is the "sender" address listed in the oldest payment to your address). You can see all the test ether sent from the faucet to you and to other addresses. Every transaction you see can lead you to more addresses and more transactions. Before long you will be lost in the maze of interconnected data. Public blockchains contain an enormous wealth of information, all of which can be explored programmaticaly, as we will see in the future examples.
We’ve got a wallet and we’ve sent and received ether. So far, we’ve treated Ethereum as a cryptocurrency. But Ethereum is much, much more. The cryptocurrency function is, in fact, subservient to Ethereum’s function as a world computer, a decentralized smart contract platform. Ether is meant to be used to pay for running smart contracts, which are computer programs that run inside an emulated computer called the Ethereum Virtual Machine (EVM).
The EVM is a global singleton, meaning that it operates as if it was a global, single-instance computer, running everywhere. Each node on the Ethereum network runs a local copy of the EVM to validate contract execution, while the Ethereum blockchain records the changing state of this world computer as it processes transactions and smart contracts.
The type of account we created in the MetaMask wallet is called an Externally Owned Account (EOA). Externally owned accounts are those that have a private key, which controls access to funds or contracts. Now, you’re probably guessing there is another type of account. The other type of account is a contract account. A contract account is owned (and controlled) by the logic of a software program recorded on the Ethereum blockchain and executed by the EVM.
In the future, all Ethereum wallets might be running as Ethereum contracts, blurring the distinction between Externally Owned Accounts and contracts. The important distinction that will always remain is this: A human makes decisions (EOA), or a piece of software makes decisions (contract).
Contracts have an address, just like EOAs (wallets). Contracts can send and receive ether, just like wallets. When a transaction destination is a contract address, it causes that contract to run in the EVM, using the transaction as its input.
In addition to ether, transactions can contain data indicating which specific function in the contract to run and what parameters to pass to that function. This way, transactions call functions within contracts. Finally, contracts can generate transactions calling other contracts, building complex execution paths. One typical use of this is Contract A calling Contract B in order to maintain a shared state across users of Contract A.
In the next few sections we will write our first contract. We will then create, fund, and use that contract with our MetaMask wallet and test ether on the Ropsten test network.
Ethereum has many different high-level languages, all of which can be used to write a contract and produce EVM bytecode. You can read about many of the most prominent and interesting ones in [high_level_languages]. One high-level language is by far the dominant language for smart contract programming: Solidity. Solidity was created by Gavin Wood, the co-author of this book and has become the most widely used language in Ethereum and beyond. We’ll use Solidity to write our first contract.
For our first example, we will write a contract that controls a faucet. We’ve already used a faucet to get test ether on the Ropsten test network. A faucet is a relatively simple thing: it gives out ether to any address that asks, and can be refilled periodically. You can implement a faucet as a wallet controlled by a human (or a web server), but instead we will write a Solidity contract that implements a faucet:
link:code/Faucet.sol[role=include]
Download Faucet.sol from: https://github.com/ethereumbook/ethereumbook/blob/first_edition/code/Faucet.sol
This is a very simple contract, about as simple as we can make it. It is also a flawed contract, demonstrating a number of bad practices and security vulnerabilities. We will learn by examining all its flaws in later sections. For now, however, let’s look at what this contract does and how it works, line by line.
The first line is a comment:
// Version of Solidity compiler this program was written for
Comments are for humans to read and are not included in the executable EVM bytecode. We usually put them on the line before the code we are trying to explain, or sometimes on the same line. Comments start with two forward slashes //. Everything from the slashes and beyond, until the end of that line, is treated the same as a blank line and ignored.
The second line is a version pragma, which declares what compiler version this program was written for:
pragma solidity ^0.4.19;
The Solidity compiler reads it and will produce an error if it is the wrong compiler version. In this case our pragma directive says that this program can be compiled by a Solidity compiler with a minimum version 0.4.19. The symbol ^ states, however, that we allow compilation of any minor revisions above that, e.g., 0.4.20, but not 0.5.0. Pragma directives are not compiled into EVM bytecode. They are only used by the compiler to check compatibility.
Ok, the next lines are where our actual contract starts:
contract Faucet {
This line declares a contract object, similar to a class declaration in other object-oriented languages like JavaScript, Java or C++. The contract definition includes all the lines between the curly braces {} which define a scope, much the same as curly braces are used in many other programming languages.
Next, we declare the first function of the Faucet contract:
function withdraw(uint withdraw_amount) public {
The function is named withdraw, which takes one unsigned-integer (uint) argument named withdraw_amount. It is declared as a public function, meaning it can be called by other contracts. The function definition follows between curly braces:
require(withdraw_amount <= 100000000000000000);
The first part of the withdraw function sets a limit on withdrawals. It uses the built-in Solidity function require to test a precondition, that the withdraw_amount is less than or equal to 100000000000000000 wei, which is the base unit of ether (see Ether Denominations and Unit Names) and equivalent to 0.1 ether. If the withdraw function is called with a withdraw_amount greater than that amount, the require function here will cause contract execution to stop and fail with an exception.
This part of the contract is the main logic of our faucet. It controls the flow of funds out of the contract by placing a limit on withdrawals. It’s a very simple control, but gives you a glimpse of the power of a programmable blockchain: decentralized software controlling money.
Next comes the actual withdrawal:
msg.sender.transfer(withdraw_amount);
A couple of interesting things are happening here. The msg object is one of the inputs that all contracts can access. It represents the transaction that triggered the execution of this contract. The attribute sender is the sender address from the transaction. The function transfer is a built-in function that transfers ether from the contract to the address it is called from. Reading it backwards, this means transfer to the sender of the msg that triggered this contract execution. The transfer function takes an amount as its only argument. We pass the withdraw_amount value that was the parameter to the withdraw function declared a few lines above.
The very next line is the closing curly brace, indicating the end of the definition of our withdraw function.
Below we declare one more function:
function () public payable {}
This function is a so called "fallback" or default function, which is called if the transaction that triggered the contract didn’t name any of the declared functions in the contract, or any function at all, or didn’t contain data. Contracts can have one such default function (without a name) and it is usually the one that receives ether. That’s why it is defined as a public and payable function, which means it can accept ether into the contract. It doesn’t do anything, other than accept the ether, as indicated by the empty definition in the curly brackets {}. If we make a transaction that sends ether to the contract address, as if it were a wallet, this function will handle it.
Right below our default function is the final closing curly bracket, which closes the definition of the contract Faucet. That’s it!
Now that we have our first example contract, we need to use a Solidity compiler to convert the Solidity code into EVM bytecode, so it can be executed by the EVM.
The Solidity compiler comes as a standalone executable, as part of different frameworks, and also bundled in an Integrated Development Environment (IDE). To keep things simple, we will use one of the more popular IDEs, called Remix.
Use your Chrome browser (with the MetaMask wallet we installed earlier) to navigate to the Remix IDE at:
When you first load Remix, it will start with a sample contract caller ballot.sol. We don’t need that, so let’s close it, clicking on the x on the corner of the tab:
Now, add a new tab by clicking on the circular-plus-sign in the left toolbar, naming the new file Faucet.sol:
Once you have a new tab open, copy and paste the code from our example Faucet.sol:
Now we have loaded the Faucet.sol contract into the Remix IDE, the IDE will automatically compile the code. If all goes well, you will see a green box with "Faucet" in it appear on the right, under the Compile tab, confirming the successful compilation:
If something goes wrong, the most likely problem is that Remix IDE is using a version of the Solidity compiler that is different from 0.4.19. In that case, our pragma directive will prevent Faucet.sol from compiling. To change the compiler version, go to the "Settings" tab, set the compiler version to 0.4.19, and try again.
The Solidity compiler has now compiled our Faucet.sol into EVM bytecode. If you are curious, the bytecode looks like this:
PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH2 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0xE5 DUP1 PUSH2 0x1D PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3F JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x2E1A7D4D EQ PUSH1 0x41 JUMPI JUMPDEST STOP JUMPDEST CALLVALUE ISZERO PUSH1 0x4B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x5F PUSH1 0x4 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP2 SWAP1 POP POP PUSH1 0x61 JUMP JUMPDEST STOP JUMPDEST PUSH8 0x16345785D8A0000 DUP2 GT ISZERO ISZERO ISZERO PUSH1 0x77 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x8FC DUP3 SWAP1 DUP2 ISZERO MUL SWAP1 PUSH1 0x40 MLOAD PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 DUP9 DUP9 CALL SWAP4 POP POP POP POP ISZERO ISZERO PUSH1 0xB6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 PUSH9 0x13D1EA839A4438EF75 GASLIMIT CALLVALUE LOG4 0x5f PUSH24 0x7541F409787592C988A079407FB28B4AD000290000000000
Aren’t you glad you are using a high-level language like Solidity instead of programming directly in EVM bytecode? Me too!
So, we have a contract, we’ve compiled it into bytecode. Now, we need to "register" the contract on the Ethereum blockchain. We will be using the Ropsten testnet to test our contract, so that’s the blockchain we want to record it on.
Registering a contract on the blockchain involves creating a special transaction, whose destination is the address 0x0000000000000000000000000000000000000000, also known as the zero address. The zero address is a special address that tells the Ethereum blockchain that you want to register a contract. Fortunately, Remix IDE will handle all of that for you and send the transaction to MetaMask.
First, switch to the "Run" tab and select "Injected Web3" in the "Environment" dropdown selection box. This connects Remix IDE to the MetaMask wallet, and through MetaMask to the Ropsten Test Network. Once you do that, you can see "Ropsten" under Environment. Also, in the Account selection box it shows the address of your wallet:
Right below the "Run" settings we just confirmed, is the Faucet contract, ready to be created. Click on the "Create" button:
Remix IDE will construct the special "creation" transaction and MetaMask will ask you to approve it. As you can see from MetaMask, the contract creation transaction has no ether in it, but it has 258 bytes (the compiled contract) and will consume 10 Gwei in gas. Click "Submit" to approve it:
Now, wait: It will take about 15 to 30 seconds for the contract to be mined on Ropsten. Remix IDE won’t appear to be doing much, be patient.
Once the contract is created, it appears at the bottom of the Run tab:
Notice that the Faucet contract now has an address of its own: Remix shows it as Faucet at 0x72e....c7829. The small clipboard symbol to the right allows you to copy the contract address into your clipboard. We will use that in the next section.
Let’s recap what we’ve learned so far: Ethereum contracts are programs that control money, which run inside a virtual machine called the EVM. They are created by a special transaction that submits their bytecode to be recorded on the blockchain. Once they are created on the blockchain, they have an Ethereum address, just like wallets. Anytime someone sends a transaction to a contract address it causes the contract to run in the EVM, with the transaction as its input. Transactions sent to contract addresses may have ether or data or both in them. If they contain ether, it is "deposited" to the contract balance. If they contain data, the data can specify a named function in the contract and call it, passing arguments to the function.
Now, we have a contract recorded on the blockchain and we can see it has an Ethereum address. Let’s check it out on the ropsten.etherscan.io block explorer and see what a contract looks like. Copy the address of the contract by clicking on the clipboard icon next to its name:
Keep Remix open in a tab, we’ll come back to it again later. Now, navigate your browser to ropsten.etherscan.io and paste the address into the search box. You should see the contract’s Ethereum address history:
For now, the contract only has one transaction in its history: the contract creation transaction. As you can see, the contract also has no ether (zero balance). That’s because we didn’t send any ether to the contract in the creation transaction, even though we could have.
Let’s send some ether to the contract! You should still have the address of the contract in your clipboard (if not, copy it again from Remix). Open MetaMask, and send 1 ether to it, exactly as you would any other Ethereum address:
In a minute, if you reload the etherscan block explorer, it will show another transaction to the contract address and an updated balance of 1 ether.
Remember the unnamed default public payable function in our Faucet.sol code? It looked like this:
function () public payable {}
When you sent a transaction to the contract address, with no data specifying which function to call, it called this default function. Because we declared it as a payable, it accepted and deposited the 1 ether into the contract account balance. Your transaction caused the contract to run in the EVM, updating its balance. We have funded our faucet!
Next, let’s withdraw some funds from the faucet. To withdraw, we have to construct a transaction that calls the withdraw function and passes a withdraw_amount argument to it. To keep things simple for now, Remix will construct that transaction for us and MetaMask will present it for our approval.
Return to the Remix tab and look at the contract under the "Run" tab. You should see a red box labeled withdraw with a field entry labeled uint256 withdraw_amount :
This is the Remix interface to the contract. It allows us to construct transactions that call the functions defined in the contract. We will enter a withdraw_amount and click the withdraw button to generate the transaction.
First, let’s figure out the withdraw_amount. We want to try and withdraw 0.1 ether, which is the maximum amount allowed by our contract. Remember that all currency values in Ethereum are denominated in wei internally, and our withdraw function expects the withdraw_amount to be denominated in wei too. The amount we want is 0.1 ether, which is 100000000000000000 wei (1 followed by 17 zeros).
Tip
|
Due to a limitation in JavaScript, a number as large as 10^17 cannot be processed by Remix. Instead, we enclose it in double quotes, to allow Remix to receive it as a string and manipulate it as a BigNumber. If we don’t enclose it in quotes, the Remix IDE will fail to process it and display "Error encoding arguments: Error: Assertion failed" |
Type "100000000000000000" (with the quotes) into the withdraw_amount box and click on the withdraw button:
MetaMask will pop-up a transaction window for you to approve. Click "Submit" to send your withdrawal call to the contract:
Wait a minute and then reload the etherscan block explorer to see the transaction reflected in the Faucet contract address history:
We now see a new transaction with the contract address as the destination and zero ether. The contract balance has changed and is now 0.9 ether, because it sent us 0.1 ether as requested. But we don’t see an "OUT" transaction in the contract address history.
Where’s the outgoing withdrawal? A new tab has appeared in the contract’s address history page, named "Internal Transactions". Because the 0.1 ether transfer originated from the contract code, it is an internal transaction (also called a message). Click on the "Internal Transactions" tab to see it:
This "internal transaction" was sent by the contract in this line of code (from the withdraw function in Faucet.sol):
msg.sender.transfer(withdraw_amount);
To recap: We sent a transaction from our MetaMask wallet that contained data instructions to call the withdraw function with a withdraw_amount argument of 0.1 ether. That transaction caused the contract to run inside the EVM. As the EVM ran the Faucet contract’s withdraw function, first it called the require function and validated that our amount was less than or equal to the maximum allowed withdrawal of 0.1 ether. Then it called the transfer function to send us the ether. Running the transfer function generated an internal transaction that deposited 0.1 ether into our wallet address, from the contract’s balance. That’s the one shown in the "Internal Transactions" tab in etherscan.
In this chapter, we’ve set up a wallet using MetaMask and we’ve funded it using a faucet on the Ropsten Test Network. We received ether into our wallet’s Ethereum address. Then we sent ether to the faucet’s Ethereum address.
Next, we wrote a faucet contract in Solidity. We used the Remix IDE to compile the contract into EVM bytecode. We used Remix to form a transaction and recorded the faucet contract on the Ropsten blockchain. Once recorded, the faucet contract had an Ethereum address and we sent it some ether. Finally, we constructed a transaction to call the withdraw function and successfully asked for 0.1 ether. The contract checked our request and sent us 0.1 ether with an internal transaction.
It may not seem like much, but we’ve just successfully interacted with software that controls money on a decentralized world computer.
We will do a lot more smart contract programming in [smart contracts] and learn about best practices and security considerations.