Testnet is short for test network. Image from Pixabay.

Set Up a Local EOS Testnet

While public test networks such as Jungle and CryptoKylin exist for EOS, you can set up your own local testnet for EOS development

Edson Ayllon
Jul 8, 2019 · 16 min read

Bitcoin, the first decentralized digital currency, saw its first transfer from one account to another January 2009. But it wasn’t until a year later when a public market allowing the trading of Bitcoin emerged. The first cryptocurrency exchange wasn’t Bitstamp or Mt. Gox, but Bitcoinmarket.com, a now dead platform. What an exchange did was bring consensus on what Bitcoin was worth as a currency. But with the rise of Bitcoinmarket followed an influx of scammers. Paypal, which allowed the trading of Bitcoin with fiat, was removed from the site.

Bitcoinmarket’s market successor, Mt. Gox, began handling 70% of all Bitcoin volume by January 2014. One month later Mt. Gox became bankrupt due to a hack. Mt. Gox had previously seen a large hack in 2011, however, still became the largest exchange before their hack in 2014.

Bitcoin, around the ages of Bitcoinmarket, started trading at ~$0.003 per Bitcoin. Before 2014, that number wavered around $100 per Bitcoin. Right before the Mt. Gox 2014 hack, Bitcoin’s price reached over $1,000, a 10x in a few months. Bitcoin, at the time of this writing in Summer 2019, trades over $10,000.

Bitcoin Price for 2014 via coinmarketcap.com.

Cryptocurrency created a digital FOREX trading market online. Whereby normal FOREX deals with trading centralized currencies backed by the economies of countries, cryptocurrency trading typically has no government affiliation, and are run by incentivized volunteers, making these currencies decentralized currencies. Think Uber but finance.

The true evolution of these decentralized currencies came with smart contract enabled platforms, the first of which is known as Ethereum. Ethereum sprung into the public summer 2015, lead by Vitalik Buterin, co-founder of Bitcoin Magazine. Ethereum enabled the addition of code handling the transfer of funds, enabling applications interacting and storing data to the blockchain.

With the introduction of smart contracts, entire backends could now be hosted on the blockchain instead of servers. Each volunteer running the Ethereum blockchain would collectively become one computer, creating one super-server without a single point of failure.

Even more so, with the introduction of Ethereum, entirely new decentralized currencies could be launched using Ethereum as a base.

Like in traditional startups and the stock market, cryptocurrency projects also seek out venture capitalist (VC) funding. This funding comes in seed rounds, as well as VC rounds before launching into exchanges. This VC funding method was previously known as the Initial Coin Offering (ICO). One of these ICOs, however, led to the forking of the Ethereum cryptocurrency.

An ICO called the DAO, short for Decentralized Autonomous Organization, had raised $150 million in ICO funding. Before funding had finished, $50 million worth of ETH was stolen.

The attacker managed to combine two exploits: (1) calling the split DAO function recursively and (2) splitting without destroying the tokens in the original DAO. […] The combination of both exploits multiplied the effect.

Summary from Consensys

These $50 million wouldn’t have been stolen were it for stricter testing and smart contract auditing. This situation highlights the most important aspect of smart contract development: testing.

Why Run your Own Testnet?

EOS is a much faster smart contract platform than Ethereum. While Ethereum offers 15 transactions per seconds, EOS transactions per second reach the thousands. Seeing as each smart contract function call acts as a transaction, this difference is night and day for the end user. However, as a smart contract platform, EOS also requires auditing of smart contracts.

The benefit of running your own EOS testnet is that you can deploy EOS smart contracts for free for testing. You can generate as many tokens as you’d like for testing, as well as generate as many accounts as you’d like, and transfer tokens between those accounts.

Generating tokens and freely trading them is helpful when making your decentralized application dealing with money, ensuring your application is secure.

The EOS stack has 3 parts:

  1. The Command Line Interface (CLEOS)
  2. The instance or node (NODEOS)
  3. And the key and wallet manager (KEOSD)

For most of this tutorial, we’ll be using cleos to configure the testing environment, but nodeos and keosd will be used to initiate the environment.

This guide will be broken down to these sections:

Let’s Get the Basics Up and Running

To begin the process, we first have to install EOS binaries. If you are on Mac, we can use Homebrew.

brew tap eosio/eosiobrew install eosio

Currently, EOS doesn’t have developer tools for native Windows operating systems. If using Windows, you can either download a Virtual Machine and run a Linux operating system such as Ubuntu or download the Ubuntu terminal in the Microsoft Store.

If using Ubuntu:

wget https://github.com/EOSIO/eos/releases/download/v1.7.0/eosio_1.7.0-1-ubuntu-16.04_amd64.deb
sudo apt install ./eosio_1.7.0-1-ubuntu-16.04_amd64.deb

If you recently installed Ubuntu via the Microsoft Store on Windows, right click allows you to paste.

Once installed, we can initiate keosd right away.

keosd &

If the eosio binaries were installed, you should see something like the following output:

info  2018-11-26T06:54:24.789 thread-0  wallet_plugin.cpp:42          plugin_initialize    ] initializing wallet plugininfo  2018-11-26T06:54:24.795 thread-0  http_plugin.cpp:554           add_handler          ] add api url: /v1/keosd/stopinfo  2018-11-26T06:54:24.796 thread-0  wallet_api_plugin.cpp:73      plugin_startup       ] starting wallet_api_plugininfo  2018-11-26T06:54:24.796 thread-0  http_plugin.cpp:554           add_handler          ] add api url: /v1/wallet/createinfo  2018-11-26T06:54:24.796 thread-0  http_plugin.cpp:554           add_handler          ] add api url: /v1/wallet/create_keyinfo  2018-11-26T06:54:24.796 thread-0  http_plugin.cpp:554           add_handler          ] add api url: /v1/wallet/get_public_keys

In a new tab in your terminal, we can also initiate a nodeos instance.

For a local testnet, initialize using these settings.

nodeos -e -p eosio \--plugin eosio::producer_plugin \--plugin eosio::chain_api_plugin \--plugin eosio::http_plugin \--plugin eosio::history_plugin \--plugin eosio::history_api_plugin \--access-control-allow-origin='*' \--contracts-console \--http-validate-host=false \--verbose-http-errors \--filter-on='*' >> nodeos.log 2>&1 &

And there we go. Your local testnet should be running. But…

Is Everything Working Correctly?

We can check if nodeos is producing blocks.

tail -f nodeos.log

You should see a log. If everything is working correctly, you should see something like the following.

1929001ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366974ce4e2a... #13929 @ 2018-05-23T16:32:09.000 signed by eosio [trxs: 0, lib: 13928, confirmed: 0]1929502ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366aea085023... #13930 @ 2018-05-23T16:32:09.500 signed by eosio [trxs: 0, lib: 13929, confirmed: 0]1930002ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366b7f074fdd... #13931 @ 2018-05-23T16:32:10.000 signed by eosio [trxs: 0, lib: 13930, confirmed: 0]1930501ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366cd8222adb... #13932 @ 2018-05-23T16:32:10.500 signed by eosio [trxs: 0, lib: 13931, confirmed: 0]1931002ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366d5c1ec38d... #13933 @ 2018-05-23T16:32:11.000 signed by eosio [trxs: 0, lib: 13932, confirmed: 0]1931501ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366e45c1f235... #13934 @ 2018-05-23T16:32:11.500 signed by eosio [trxs: 0, lib: 13933, confirmed: 0]1932001ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000366f98adb324... #13935 @ 2018-05-23T16:32:12.000 signed by eosio [trxs: 0, lib: 13934, confirmed: 0]1932501ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 00003670a0f01daa... #13936 @ 2018-05-23T16:32:12.500 signed by eosio [trxs: 0, lib: 13935, confirmed: 0]1933001ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 00003671e8b36e1e... #13937 @ 2018-05-23T16:32:13.000 signed by eosio [trxs: 0, lib: 13936, confirmed: 0]1933501ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 0000367257fe1623... #13938 @ 2018-05-23T16:32:13.500 signed by eosio [trxs: 0, lib: 13937, confirmed: 0]

This nodeos instance is your local EOS testnet. If it isn’t running, you’ll see so here. Errors with nodeos occur if you close a terminal or turn off your device without cleaning exiting nodeos and keosd. To terminate nodeos and keosd cleanly, and prevent these issues, press Ctrl + C in their windows, or use pkill nodeos and pkill koesd, once you choose to terminate your local testnet.

Press Ctrl + C to close the nodeos log.

Next, we check our wallets with cleos.

cleos wallet list

If this is your first time dealing with eosio, you should see an empty list.


Finally, we check our nodeos endpoint. We have two options:

  1. Visit http://localhost:8888/v1/chain/get_info in your browser
  2. Print out http://localhost:8888/v1/chain/get_info in the terminal
curl http://localhost:8888/v1/chain/get_info

This address, the nodeos endpoint, is needed for connecting any app you make and any EOS wallet provider, such as Scatter Wallet, with your EOS testnet. Since we have that, this means we can now…

Connect your Testnet to Scatter Wallet

To add your nodeos instance to Scatter, you first need to have Scatter installed. You can download Scatter for your respective operating system on their website.

Once you have Scatter installed and ready with the password you created for your account, you can begin to add your nodeos instance to the wallet, so Scatter can interface a frontend you design with your local EOS testnet.

From the home screen, enter settings.

Settings is found in the top right of the app, in the header.

In settings, you’ll see a list of options included a set of options outlined in red saying danger. Those are the options we want to look at. Within the “Danger Zone” select Networks.

The Networks settings are needed to adding any EOS nodeos instance that isn’t the EOS mainnet. It can be dangerous territory for a regular user who isn’t a developer and doesn’t understand EOS, but you’ll be fine.

Networks will allow you to add different EOS nodeos instances. It’s required to add any local testnet, as Scatter defaults to the mainnet.

Once there, we enter the required information. All the info you need is found inside of the http://localhost:8888/v1/chain/get_info json text. There, the most important piece is the Chain ID. Everything else, including the ports, is also found there. If your nodeos instance is running, you’ll have access to that json text.

All the information you need for this section is found in the address http://localhost:8888/v1/chain/get_info, which you can access if nodeos is running. As you can see, much of the information is in the address name itself.

Name your network settings. I named mine localhost, since I am running it locally on my computer.

What this allows you to do is add accounts that are made within this testnet into Scatter wallet.

We don’t have any accounts, so we’ll need to create some.

Create Testnet EOS Accounts

To ready our testnet with EOS accounts, we need to install the EOSIO Contract Development Toolkit (CDT). The EOS CDT is a collection of tools used in compiling smart contracts.

If you’re on MacOS, you can use Homebrew.

brew tap eosio/eosio.cdt 
brew install eosio.cdt

If using Ubuntu:

wget https://github.com/EOSIO/eosio.cdt/releases/download/v1.6.1/eosio.cdt_1.6.1-1_amd64.deb
sudo apt install ./eosio.cdt_1.6.1-1_amd64.deb

Now that we have all the required tools, let’s make a development wallet. An EOS wallet contains the private key you need to sign transactions on the EOS blockchain. Recently, EOS wallets have served more as Authenticators, allowing you to log into a dApp in addition to interacting with the EOS blockchain on your behalf. We will be making a development wallet to publish local smart contracts and create accounts for your testnet.

When we create the development wallet, a password will be returned. We specify whether this password returns to the console or is saved in a file.

cleos wallet create --to-console || cleos wallet create --to-file <path/filename.txt>

Whichever way you requested the password, save it, as you’ll need it.

Now that the wallet is created, all wallets are closed by default, so to use it, we have to open it.

cleos wallet open

You can also see a list of wallets with the following:

cleos wallet list

Opening a wallet does not unlock it. Once opened, you must still unlock the wallet to use it.

cleos wallet unlock

When attempting to unlock, you will be prompted to

Expected output:

Wallets: [ “default *” ]

An asterisk (*) next to a wallet indicates the wallet is unlocked.

With a wallet, you need to create a private-public key pairing. You can do so with cleos.

cleos wallet create_key

Now, every EOS chain, in every EOS network, needs an eosio account to sign transactions limited to only the eosio account. For testnet use, EOS provides a development key for importing a development eosio account. This key is the same everywhere for development.

# eosio development key

We’ll need to import the eosio development account.

cleos wallet import
# then enter the eosio development key

Never use the development key for any production account! Doing so will result in the loss of access to your account, as this private key is publicly known.

Now that all the requirements are in place, we can set up test accounts for us to use in our testnet.

In EOS, accounts aren’t just associated with a public key, they are associated with a username that is linked to a private/public key pairing. This username is required for sending transaction and interacting with EOS smart contracts.

EOS accounts are flexible, in so that they can be owned and controlled by an individual, or by a set of individuals who control their own accounts by means of voting.

We’ll create two accounts, bob and alice . When creating an account, we specify the public key associated with that account. I’ve include example public keys below, but in order to import these accounts into Scatter, you must know the associated private keys. You can create your own EOS public key private key pairing using cleos, cleos wallet create_key --to-console, using an EOS key pairing generator online, or by going the long route, and creating your own EOS key generator with its own frontend you host locally.

We use the cleos command create account, using the eosio account, by specifying the account name and public key associated with that name.

cleos create account eosio bob EOS5T6c3EbR59YLEW3RXUyoy7rcqLd3hShkZJy8NfHdaP7EeEwVCJ  
cleos create account eosio alice EOS5T6c3EbR59YLEW3RXUyoy7rcqLd3hShkZJy8NfHdaP7EeEwVCJ

You should output similar to the following:

powershell executed transaction: 40c605006de… 200 bytes 153 us # eosio <= eosio::newaccount {“creator”:”eosio”,”name”:”alice”,”owner”:{“threshold”:1,”keys”:[{“key”:”EOS5rti4LTL53xptjgQBXv9HxyU… warning: transaction executed locally, but may not be confirmed by the network yet ]

You can create as many accounts as you’d like this way in your testnet, up until you decide to add the eosio.system contract to your local testnet. Once you do upload that contract, your testnet will behave more like the mainnet, in so that creating a new account costs EOS tokens. What complicates things even further, once this smart contract is deployed to your testnet, all account names must be 12 characters, like in the mainnet. Account names of less than 12 characters must be acquired through the auction process as it is in the mainnet. There are ways to reset your testnet as to remove the eosio.system contract, but the way I know makes you start from zero, a process that may take maybe ~30 minutes if you’ve done it before, depending on what you need to deploy.

We can check to see what key is associated with an account using cleos. To check the ownership of alice, for example, we do the following:

cleos get account alice

If you get an error while creating the account, make sure your wallet is unlocked. If everything went fine, you should see something like the following:

1: 1 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
1: 1 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
2.66 KiB
net bandwidth:
cpu bandwidth:

Ownership of an account is associated with an account name, not associated public key. You can change the public key of an account, however, all funds and actions allowed by that account are tied to the name.

Notice that alice has both owner and active keys. You can minimize exposure, and increase your security, by using your active private and public key, while keeping your owner private and public key cold. If your active key ever gets compromised, you can change it and still retain ownership of your account name using your owner keys. If your owner keys are stolen, that’s it for your account, therefore, always use your active key, not owner key.

Now that we have named EOS accounts active in our testnet, we can…

Add Our EOS Testnet Accounts to Scatter

The process is relatively simple, import the private key and enter the correct name for the account.

In Scatter, you import an account with the “Add Keys” button in the lower left corner.

The “Add Keys” button is found in the lower left corner of Scatter.

Select “Import existing key.”

Import an existing key should be after “Create a new key,” but before “Create a new EOS account.”

Select “Import private key as text.”

“Import private key as text” should be the first option.

We import the private key for the account we created.

You should have saved the private key for the named account. If not, you’ll have to create a new account with a private key you do have access to, as detailed in previous sections.

Now, we configure the account to use our testnet. Enter the correct account name for the account, and select your testnet name which was configured in Networks. Mine is called localhost.

We add the account name and save it. Here, I have the Key Name set to the name of the account.

We now have access to our testnet account within Scatter. You can repeat this process to add as many testnet accounts as you have and would like to.

After importing, you should notice that your account balance is empty.

Well, we haven’t added any tokens to our address yet. In fact, we don’t even have the token contract deployed within our testnet at the moment. We’ll have to.

Create and Transfer Tokens to your Testnet Accounts

The tools we need to create and transfer tokens in EOS is within the eosio.token smart contract. The eosio.token contract is available on EOS’s Github.

We’ll clone it.

# Enter the directory you'd like to keep your contracts and clone
git clone https://github.com/EOSIO/eosio.contracts — branch v1.5.2 — single-branch

This repository contains several contracts. We’ll use the token contract first.

cd eosio.contracts/eosio.token

To deploy a contract, an account must exist that it’ll be deployed to. We’ll create a new one called eosio.token.

cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

We then compile the contract.

eosio-cpp -I include -o eosio.token.wasm src/eosio.token.cpp --abigen

And deploy it within our testnet.

# you must enter the full path to the contract directory
cleos set contract eosio.token CONTRACTS_DIR/eosio.contracts/eosio.token --abi eosio.token.abi -p eosio.token@active

If successful, you should see something like the following.

Reading WASM from ... Publishing contract... executed transaction: 69c68b1bd5d61a0cc146b11e89e11f02527f24e4b240731c4003ad1dc0c87c2c  9696 bytes  6290 us #         eosio <= eosio::setcode               {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d0100000001aa011c60037f7e7f0060047f... #         eosio <= eosio::setabi                {"account":"eosio.token","abi":"0e656f73696f3a3a6162692f312e30000605636c6f73650002056f776e6572046e61... warning: transaction executed locally, but may not be confirmed by the network yet         ]

Great! You know how to deploy.

Let’s start using it.

Create Tokens

If you look inside the tokens contract, you’ll see a collection of functions. We’ll use the one that creates a token supply. This function takes in one argument containing both the supply and token symbol.

cleos push action eosio.token create ‘[ “eosio”, “1000000000.0000 SYS”]’ -p eosio.token@active

If successful, you should see something like the following:

executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12  120 bytes  1000 cycles #   eosio.token <= eosio.token::create          {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}

The token symbol can actually be any symbol, including EOS. We’ll create an EOS token supply. EOS has a total supply of 1,018,971,167.0000. Let’s use that.

cleos push action eosio.token create ‘[ “eosio”, “1018971167.0000 EOS”]’ -p eosio.token@active

Now, we have an EOS supply in our testnet. Let’s some circulate.

Issue Tokens

The issue function takes in the account we’re issuing to, the amount, and a message or memo.

cleos push action eosio.token issue '[ "alice", "10000.0000 EOS", "memo" ]' -p eosio@active

The amount issued is our circulating supply. The circulating supply of EOS in the mainnet at the time of writing is 922,271,161 EOS, much higher than 10,000 EOS. The circulating supply affects RAM prices, which may be important later once if you choose to deploy the system contract which deals with RAM. A low supply means a lot of RAM can be bought at very low EOS amounts. There are 4 decimals to EOS, meaning if the supply was low enough, you’d run into an error buying RAM, where it would be impossible to buy smaller amounts of RAM, as doing so, would take less than 4 decimals of EOS.

Regardless, now that we have a circulating supply, we can move EOS around.

Transfer Tokens

We previously made another account named bob. Let us transfer to that account from alice’s account.

cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 EOS", "m" ]' -p alice@active

Now, if you added both alice and bob inside of Scatter, you should see their accounts containing EOS.


We have a testnet with accounts containing EOS available within Scatter. Now, if we develop a dApp with EOS, we can test it in our testnet for free before we commit to deploying on the mainnet. You can look up the Scatter documentation for learning how to have a client interact with Scatter, so you can interact with smart contracts on EOS with something like React and React-Native.

That’s everything you’ll need, unless what you do requires the remaining eosio.contracts.

Additional Contracts for the Testnet

Now, our testnet isn’t exactly the same as the mainnet. This can be good or bad, depending on your needs. The mainnet, for example, requires a fee for creating new accounts. Right now, we can create as many free named accounts as we’d like with names of any character count. However, the mainnet limits even paid account creation to a fixed number of characters.

But some projects require more realistic configurations. For example, if you plan on creating an app that creates new accounts, you’ll need account creation functionality that mimics the mainnet, which means you’ll need to deploy the eosio.system smart contract.

The process for deploying another smart contract at this stage is the same as it was for the token contract. For this example, we’ll deploy the system contract, as it’s needed for dApp creation of accounts.

First, we need all required named accounts for the eosio.contracts.

Create an account for each of these names.

  • eosio.bpay
  • eosio.msig
  • eosio.names
  • eosio.ram
  • eosio.ramfee
  • eosio.saving
  • eosio.stake
  • eosio.token
  • eosio.vpay
  • eosio.rex

You can do so with this code, replacing the name with each.

cleos create account eosio eosio.<name> EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

For example, for eosio.ram:

cleos create account eosio eosio.ram EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

We’ll then deploy the eosio.system contract. The repository eosio.contracts contains a build.sh file, which builds all the contracts in that directory. We can run that and all contracts will be built for us.

For MacOS, this build requires cmake, which can be installed with Homebrew.

brew install cmake

Once we have the contract built, we can deploy it to our testnet. We set the system smart contract to the name eosio.

# needs the full path
cleos set contract eosio CONTRACTS_DIR/build/contracts/eosio.system --abi eosio.system.abi -p eosio@active

The eosio.system contract must be initialized after being deployed.

cleos push action eosio init '[0, "4,EOS"]' -p eosio@active

This can be undone if you’d like, but you’ll need to re-initiate your testnet almost from scratch.

Either way, our testnet is more like the mainnet.


You have the tools now to test your smart contracts for free, equipped so you don’t run into a DAO situation.



A journey of coding ideas and programming experiences.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

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