Discover Ethereum & Solidity

Back to All Courses

Lesson 16

Running on testnet

Now that we have a fully working DApp, we should start preparing to release it! Before wrecklessly deploying our contracts to production though, it's always a good idea to upload them to one of Ethereum's test networks first.

These test network are different from the ganache-cli that you're used to, since they are publicly shared with everyone. What separates them from the mainnet though is that the transactions are slower, the security isn't very good, and the ether is basically free. In summary, it's a bit like a staging server -- it emulates the production environment as closely as possible.

There are multiple test networks to choose from in Ethereum, but we're going to use the most popular one, called "Ropsten".

Fun fact: both the Ropsten and Rinkeby test networks are named after Swedish underground stations.

Syncing the testnet blockchain

In order to connect to Ropsten, you're going to need Geth -- an Ethereum client built in the Go programming language.

We can install Geth on Mac using Homebrew (for other operating systems, check out this page).

brew tap ethereum/ethereum
brew install ethereum

Once installed, we're going to have to sync the testnet blockchain to our computer. Now, just a heads up -- this process is pretty long and resource-intensive, so your computer's fans will probably turn on.

Open a new terminal window and run:

geth --testnet --syncmode "fast"

This will start syncing the blockchain, and you should be able to follow the progress in real time. We use the "fast" syncmode to sync through state downloads instead of replaying all transactions since the beginning -- this will make the initial download of the blockchain much faster!

A list of the synced blocks.

Just let the process run for a while (it took me ~2 hours), and then come back to the tutorial.

If you want to get an estimate for how much time there's left while waiting, you can run the following command to open the Geth JavaScript console (make sure you replace YOUR_USER with your own username).

geth attach ipc:/Users/{YOUR_USER}/Library/Ethereum/testnet/geth.ipc

When inside the console, run web3.eth.syncing to see how many blocks you have left to sync.

In this example, you can see that I've synced 2821480 blocks out of 2821946. Almost there!

Once the command returns false, that means you've fully synced the blockchain. You can exit the Geth console simply by typing exit.

Creating and funding your account

Once the blockchain is fully synced, you'll officially be a Ropsten user. However, unless we have some funds in our Geth wallet, we won't be able to deploy our contracts, so let's fix that!

The first step is to stop the Geth server and relaunch it with some options so that you also get a fully working HTTP JSON-RPC. We'll use port 8546 in order not to confuse it with our local testrpc which usually runs on 8545.

geth --testnet --rpc --rpcapi db,eth,net,web3,personal --rpcport 8546

Thanks to this, we can open a new Geth JavaScript console by connecting to port 8546.

geth attach http://127.0.0.1:8546

If you see this after running the command, you're in!

Inside this interface, we can see all of our accounts (private keys) that are usable in the network by running eth.accounts. Right now, this should only return an empty list. Since we need an address from which we can deploy our contracts, we're going to create a new account:

> personal.newAccount()

You'll be prompted to pick a password for your account. Make sure it's something that you remember (you're going to need it later), but don't worry about it not being super secure -- this is still only the testnet after all.

After choosing a password, you should see the public address for your brand new account!

You can actually check out your new address' funds on Etherscan's Ropsten explorer.

Unsurprisingly, the balance of a brand new address is zero.

So where do we get the ethers to top up this account? You can actually mine them pretty easily, but an even quicker way is to use the MetaMask Ether Faucetwhere test ethers are regularly given out for free upon request.

First, make sure that your MetaMask extension uses the Ropsten network:

Then go to the faucet page, click the green "request 1 ether from faucet" button, and you should see a transaction appear at the bottom of the page.

You can click this transaction link to follow its progress on Etherscan. It might not show up immediately, but after a couple of seconds it should be sent and confirmed.

Once it says "Success", your requested ether should be visible in your MetaMask wallet!

Sweet! We now have some test ethers in our MetaMask wallet. What we really need though is to have them in our Geth wallet (since that's where we'll deploy them from) so let's make a quick transaction.

Open MetaMask, click the "Send" button and paste in the address of the Geth account that we recently created with personal.newAccount():

In this example, I'm sending 0.5 ether, but you can of course choose whatever amount you want.

Once the transaction has been sent and confirmed, you should be able to verify your balance in the Geth JavaScript console (displayed in Wei) using eth.getBalance(YOUR_ADDRESS).

Deploying your contracts

The last step is to deploy your contracts with Truffle! In order for Truffle to understand that you want to deploy to Ropsten (and not your local testrpc server) we need to first add it as a network in our truffle.js file.

// truffle.js

module.exports = {
  networks: {
    // ...

    // Add this:
    ropsten: {
      host: "localhost",
      port: 8546,
      network_id: "3",
      from: "{YOUR_ADDRESS}",
      gas: 4600000,
    }
  }
};

The {YOUR_ADDRESS}-part should again be replaced with the address that you generated in the Geth JavaScript console.

If we now want to deploy to this newly added network, we simply run the following command:

truffle migrate --network ropsten

Let's try it and see what happens!

Hmm, something went wrong.

As you can see, the migration fails, and among the error messages is the phrase: "authentication needed: password or unlock".

Ah, so we need to unlock our Geth wallet (using the password we picked earlier) in order for Truffle to be able to use it!

This can be done by, again, launching the Geth JavaScript console (geth attach http://127.0.0.1:8546), and then running the following command:

> personal.unlockAccount("{YOUR_ADDRESS}", "{YOUR_PASSWORD}", 0);

The last parameter specifies for how many seconds we want to account to stay unlocked. By passing 0, the account will stay unlocked indefinitely, which is okay for us right now, since we're not very concerned with security.

If you entered your address and password correctly, the console should return true.

Now run the migration command again and it should this time be successful. Also, keep in mind that the migration to the testnet will take much longer than what it takes to deploy contracts to your local Ethereum instance. Expect to wait for minutes instead of seconds.

It's deploying...

Optimizing our DApp for Ropsten

Once the contracts have been deployed to the testnet, it's time to try them out in the browser! Remember, we can still run our frontend completely locally even though it connects to the Ropsten network.

In order to connect to Ropsten, we'll create a new script in our package.json which works similarly to the dev script:

// package.json

{
  // ...
  "scripts": {
    // ...
    "ropsten": "truffle migrate --network ropsten && npm run artifacts && next client",
  },
  // ...
}

If we now run npm run ropsten, we should be able to go to localhost:3000 and interact with our DApp on the Ropsten network.

Again, make sure your MetaMask extension uses the Ropsten network!

Now let's try to create a user the same way we did before. You will probably notice that, after you send the transaction on MetaMask, it will take a much longer time for it to confirm and display the "Your user has been created" message. This is expected, and is simply the harsh reality of working with a large public blockchain (no matter if it's the mainnet or the testnet) -- block times can be quite slow.

In order to at least make this slow process a bit more pleasant for our users, we can make some small UI adjustments.

We will use a little library called react-toastify to give our users instant feedback whenever they perform a blockchain action, so that they know that there's something happening.

The type of feedback that we want our app to have.

npm install react-toastify --save

After this, we create a new Toast component where our little toast messages will be rendered.

// client/components/Toast.js

import { ToastContainer } from 'react-toastify'

export default () => (
  <div className="toasts">
    <ToastContainer
      autoClose={false}
    />

    <style jsx>{`
      .toasts {
        position: relative;
        z-index: 1000;
      }
    `}</style>
  </div>
)

We'll then render this Toast component in our Layout, so that it's readily available for all our pages. Don't worry though, the toast is always hidden by default.

// client/components/Layout.js

// ...

import Toast from "./Toast.js" // <-- Add this line!

export const Page = ({ children }) => (
  <div>
    {/* ... */}

    <main>
      {children}
    </main>

    <Toast /> {/* <-- Add this */}

    <style global jsx>{/*
      ...
    */}</style>
  </div>
)

// ...

Finally, we tweak the post method in our TweetComposer a little bit so that, instead of showing a standard alert message, it uses react-toastify:

// client/components/TweetComposer.js

// ...

import { toast } from 'react-toastify' // <-- Add this line

export default class ComposeModal extends React.Component {

  // ...

  post = async () => {
    const { text } = this.state
    const { onClose } = this.props

    let toastId = null

    try {
      toastId = toast.info("Your tweet is being posted. This will take a couple of seconds...")

      await createTweet(text)

      toast.update(toastId, {
        render: "Your tweet has been posted!",
        type: toast.TYPE.SUCCESS,
        autoClose: 4000,
      })

      onClose()
    } catch (err) {
      toast.update(toastId, {
        render: "Sorry, we couldn't post your tweet!",
        type: toast.TYPE.ERROR,
        autoClose: 4000,
      })
    }
  }

  render() {
    // ...
  }
}

// ...

That's all! You can now try posting a tweet on the Ropsten network, and you'll see the benefit of having instant feedback in action!

There are still some adjustments you could make to increase the usability of your app (show a toaster during the account creation for example), but we won't go through all of them here, since you probably get the gist of it already!