sbt-ethereum runs in the context of a repository, a folder which potentially represents source code for a particular project or application. But in order just to interact with the ethereum blockchain, or to manage or create wallets and addresses, no partcular project or application is required.
For this purpose, a simple sbt-ethereum repository called eth-command-line is available. Let’s start with that.
Make sure that you have git installed and available on your command line. Then try the following command:
$ git clone https://github.com/swaldman/eth-command-line.git --branch 0.5.3
You should see something like
Cloning into 'eth-command-line'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 108 (delta 1), reused 6 (delta 1), pack-reused 101
Receiving objects: 100% (108/108), 22.62 KiB | 609.00 KiB/s, done.
Resolving deltas: 100% (50/50), done.
When it is done, you should have a directory called eth-command-line
.
Go into that directory and list the directory’s contents.
$ cd eth-command-line
$ ls
LICENSE README.md build.sbt project sbtw
The file sbtw
is an sbt wrapper script. You can run it, and it will download and install sbt as needed.
Alternatively, if you already have sbt
installed on your machine, you can just run that. However you launch sbt
, you will need to have a Java VM installed on your machine.
If you are running on Windows, please preinstall sbt.
The wrapper script won’t work!
You can install sbt from here.
Once sbt is installed, just cd
into the eth-command-line
directory and type sbt
.
On Mac or Linux, we’ll try the wrapper script.
$ ./sbtw
If this is the first time you are running the script, expect it to take a few minutes and print a lot of stuff.
(While it is all downloading, consider read Method to the Madness, if you have not!)
Downloading sbt launcher for 1.2.7:
From http://repo.scala-sbt.org/scalasbt/maven-releases/org/scala-sbt/sbt-launch/1.2.7/sbt-launch.jar
To /Users/testuser/.sbt/launchers/1.2.7/sbt-launch.jar
Getting org.scala-sbt sbt 1.2.7 (this may take some time)...
downloading https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.2.7/sbt-1.2.7.jar ...
[SUCCESSFUL ] org.scala-sbt#sbt;1.2.7!sbt.jar (141ms)
downloading https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.7/scala-library-2.12.7.jar ...
[SUCCESSFUL ] org.scala-lang#scala-library;2.12.7!scala-library.jar (1353ms)
...
...
[info] [SUCCESSFUL ] org.slf4j#slf4j-api;1.7.20!slf4j-api.jar (111ms)
[info] [SUCCESSFUL ] ch.qos.logback#logback-core;1.1.7!logback-core.jar (206ms)
[info] downloading https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.11.1/log4j-core-2.11.1-tests.jar ...
[info] [SUCCESSFUL ] org.apache.logging.log4j#log4j-core;2.11.1!log4j-core.jar(test-jar) (337ms)
[info] [SUCCESSFUL ] com.mchange#sbt-ethereum;0.1.7-SNAPSHOT!sbt-ethereum.jar (5574ms)
[info] [SUCCESSFUL ] com.mchange#consuela_2.12;0.0.11-SNAPSHOT!consuela_2.12.jar (10602ms)
[info] Done updating.
[warn] There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings.
[info] Loading settings for project eth-command-line from build.sbt ...
[info] Set current project to eth-command-line (in build file:/Users/testuser/eth-command-line/)
There are no wallets in the sbt-ethereum keystore. Would you like to generate one? [y/n] y
sbt-ethereum presents an interactive, text-based user interface. When it’s done bootstrapping itself, it notices that it knows of no “wallets”, which are a combination of an Ethereum “address” (analogous to a bank account number), and the secret that unlocks it, encrypted so that only you can access it.
For now, let’s go ahead and make one. sbt-ethereum will ask you to type a passphrase.
There are no wallets in the sbt-ethereum keystore. Would you like to generate one? [y/n] y
[info] Generated keypair for address '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2'
[info] Generating V3 wallet, alogorithm=scrypt, n=262144, r=8, p=1, dklen=32
Enter passphrase for new wallet: ***************
Please retype to confirm: ***************
[info] Wallet generated into sbt-ethereum shoebox: '/Users/testuser/Library/Application Support/sbt-ethereum'. Please backup, via 'ethShoeboxBackup' or manually.
[info] Consider validating the wallet using 'ethKeystoreWalletV3Validate 0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2'.
Your “wallet” is protected both by the passphrase that you choose, and by files that sbt-ethereum stores in its shoebox directory. You need both of these pieces to access your new ethereum account, so if you lose the wallet files or forget your passphrase, you will lose all value and privileges associated with the wallet’s address. So back them up!
You can back the shoebox directory shown above manually, or use the command ethShoeboxBackup
, which will save important shoebox data in a zip file from which sbt-ethereum can automatically restore.
Anyone who discovers the passphrase you have chosen and who has access to the wallet file in your shoebox can take control of your wallet’s address and all value and privileges associated with it.
Keep the wallet files in your shoebox database (including any backups!) and the passphrase you have chosen to unlock those files secret and secure!
After generating a wallet for address 0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2
— your address will be different! — sbt-etherum asks
Would you like the new address '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2' to be the default sender on chain with ID 1? [y/n] y
When you wish to interact with the Ethereum blockchain, sbt-ethereum needs to know an address representing on whose behalf it is interacting. At Any time, you can set this to any address you like, but it is convenient to have a default address present. Let’s answer yes.
Would you like the new address '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2' to be the default sender on chain with ID 1? [y/n] y
[info] Successfully set default sender address for chain with ID 1 to '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2'.
[info] You can use the synthetic alias 'default-sender' to refer to this address.
[info] Refreshing caches.
The current default solidity compiler ['0.4.24'] is not installed. Install? [y/n] y
sbt-ethereum notices that it does not have the latest supported version of the “solidity compiler” available, and will ask if you would like to insall it. A solidity compiler is useful if you are developing your own smart contract applications, rather than just interacting with applications that others have deployed. Regardless, it doesn’t hurt to have one. Let’s say yes.
[info] Installed local solcJ compiler, version 0.4.24 in '/Users/testuser/Library/Application Support/sbt-ethereum/solcJ'.
[info] Testing newly installed compiler... ok.
[info] Updating available solidity compiler set.
[info] sbt-ethereum-0.1.7-SNAPSHOT successfully initialized (built Sun, 9 Dec 2018 22:12:18 -0800)
sbt:eth-command-line>
sbt-ethereum installs the compiler, tests it, and brings us to a command prompt. Finally we are ready to go!
The set-up steps we’ve completed were a one-time thing.
Next time we enter an sbt-ethereum repository, we won’t have to go through all this set up, and we won’t download so much stuff on startup. We will arrive pretty directly at the command prompt.
When we generated our new address, you may have noticed the message
[info] Consider validating the wallet using 'ethKeystoreWalletV3Validate 0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2'.
Let’s try that!
You could just copy and paste the (very long!) command, but please don’t! sbt-ethereum by design relies on tab completion to make typing long but descriptive commands easy. If you are familiar with tab completion, you should be able to enter this long command very quickly. Start with ethK<tab>
and go from there. For the address part, remember that the address alias default-sender
was defined for this address, so instead of copying and pasting the long hex address, consider just typing d<tab>
. sbt-ethereum tab-completes address aliases!
Detailed tab completion tutorial
In “tutorial” form, tab completion seems long and cumbersome, but it’s not. It becomes muscle memory, second nature, very quickly. (TL; DR: When in doubt, press <tab><tab>
.)
Start by typing eth
and then hitting the tab key. (We’ll write that from now on as eth<tab>
. The angle-braced <tab>
does not mean you should type out <
, t
, etc. — don’t do that! — but instead means “hit the tab key”.)
When you type eth<tab>
you should see a rather useless and intimidatingly long list, something like…
sbt:eth-command-line> eth
Display all 119 possibilities? (y or n)
ethAddressAliasCheck ethAddressAliasDrop ethAddressAliasList
ethAddressAliasSet ethAddressBalance ethAddressSenderDefaultDrop
ethAddressSenderDefaultPrint ethAddressSenderDefaultSet ethAddressSenderOverride
ethAddressSenderOverrideDrop ethAddressSenderOverridePrint ethAddressSenderOverrideSet
ethAddressSenderPrint ethContractAbiAliasDrop ethContractAbiAliasList
ethContractAbiAliasSet ethContractAbiCallDecode ethContractAbiCallEncode
ethContractAbiDefaultDrop ethContractAbiDefaultImport ethContractAbiDefaultList
ethContractAbiDefaultSet ethContractAbiOverride ethContractAbiOverrideDrop
ethContractAbiOverrideDropAll ethContractAbiOverrideList ethContractAbiOverridePrint
ethContractAbiOverrideSet ethContractAbiPrint ethContractAbiPrintCompact
ethContractAbiPrintPretty ethContractCompilationCull ethContractCompilationInspect
ethContractCompilationList ethDebugGanacheHalt ethDebugGanacheRestart
ethDebugGanacheStart ethDebugGanacheTest ethKeystoreList
ethKeystorePrivateKeyReveal ethKeystoreWalletV3Create ethKeystoreWalletV3FromJsonImport
ethKeystoreWalletV3FromPrivateKeyImport ethKeystoreWalletV3Print ethKeystoreWalletV3Validate
ethLanguageSolidityCompilerInstall ethLanguageSolidityCompilerPrint ethLanguageSolidityCompilerSelect
ethNodeBlockNumberPrint ethNodeChainId ethNodeChainIdDefaultDrop
ethNodeChainIdDefaultPrint ethNodeChainIdDefaultSet ethNodeChainIdOverride
ethNodeChainIdOverrideDrop ethNodeChainIdOverridePrint ethNodeChainIdOverrideSet
ethNodeChainIdPrint ethNodeUrl ethNodeUrlDefaultDrop
ethNodeUrlDefaultPrint ethNodeUrlDefaultSet ethNodeUrlOverride
ethNodeUrlOverrideDrop ethNodeUrlOverridePrint ethNodeUrlOverrideSet
ethNodeUrlPrint ethShoeboxBackup ethShoeboxDatabaseDumpCreate
ethShoeboxDatabaseDumpRestore ethShoeboxRestore ethTransactionDeploy
ethTransactionGasLimitOverride ethTransactionGasLimitOverrideDrop ethTransactionGasLimitOverridePrint
ethTransactionGasLimitOverrideSet ethTransactionGasPriceOverride ethTransactionGasPriceOverrideDrop
ethTransactionGasPriceOverridePrint ethTransactionGasPriceOverrideSet ethTransactionInvoke
ethTransactionLookup ethTransactionMock ethTransactionNonceOverride
ethTransactionNonceOverrideDrop ethTransactionNonceOverridePrint ethTransactionNonceOverrideSet
ethTransactionPing ethTransactionRaw ethTransactionSend
ethTransactionView ethcfgAddressSender ethcfgAutoDeployContracts
ethcfgBaseCurrencyCode ethcfgEntropySource ethcfgGasLimitCap
ethcfgGasLimitFloor ethcfgGasLimitMarkup ethcfgGasPriceCap
ethcfgGasPriceFloor ethcfgGasPriceMarkup ethcfgIncludeLocations
ethcfgKeystoreAutoImportLocationsV3 ethcfgKeystoreAutoRelockSeconds ethcfgNetcompileUrl
ethcfgNodeChainId ethcfgNodeUrl ethcfgScalaStubsPackage
ethcfgSolidityCompilerOptimize ethcfgSolidityCompilerOptimizerRuns ethcfgSolidityDestination
ethcfgSoliditySource ethcfgTargetDir ethcfgTransactionReceiptPollPeriod
ethcfgTransactionReceiptTimeout ethcfgUseReplayAttackProtection etherscanApiKeyDrop
etherscanApiKeyPrint etherscanApiKeySet
Wow. That’s a lot. That’s a list of (almost) all of the ethereum-related commands available to you. But it’s too much information to be useful.
The command that we wanted to try was ethKeystoreWalletV3Validate
. But instead of typing that out, type ethK<tab>
. You should see that the tab now automatically completes to
sbt:eth-command-line> ethKeystore
How did that work? In the giant list of commands above, you’ll notice that the only commands that begin with capital K
are those that begin with ethKeystore
. So sbt could “fill in the blanks” for you, there was no other choice.
Now type <tab>
again. You should see something like this:
sbt:eth-command-line> ethKeystore
ethKeystoreList ethKeystorePrivateKeyReveal ethKeystoreWalletV3Create
ethKeystoreWalletV3FromJsonImport ethKeystoreWalletV3FromPrivateKeyImport ethKeystoreWalletV3Print
ethKeystoreWalletV3Validate
sbt:eth-command-line> ethKeystore
sbt has given you a much more manageable list of possible commands, beginning with ethKeystore
. The one we want is ethKeystoreWalletV3Validate
, so we could type WalletV3Validate
from here. Don’t do that! Instead, just type W<tab><tab>
, The first time you hit <tab>
, sbt will do its autocomplete thing, which will yield ethKeystoreWalletV3
. The second <tab>
asks sbt to show remaining completions.
sbt:eth-command-line> ethKeystoreWalletV3
ethKeystoreWalletV3Create ethKeystoreWalletV3FromJsonImport ethKeystoreWalletV3FromPrivateKeyImport
ethKeystoreWalletV3Print ethKeystoreWalletV3Validate
sbt:eth-command-line> ethKeystoreWalletV3
We’re getting close! Now sbt is showing us just the “WalletV3” related commands. We want Validate
so we just type V<tab>
to finally complete the command. Hooray!
sbt:eth-command-line> ethKeystoreWalletV3Validate
(If you hit tab again, when the command is complete, you’ll get a list of completions that begins with / ::
. This means that you have entered a complete command.)
Once a command is complete, it will often require “arguments” — more information about how to run the command. In our case, ethKeystoreWalletV3Validate
requires the address whose wallet it is supposed to “validate”. We could copy and paste the long hex address we generated upon intiaization, but let’s see if we can avoid that.
Once a command is complete, try pressing<space><tab>
(where <space>
means “press the space bar”).
sbt:eth-command-line> ethKeystoreWalletV3Validate
<address-hex> <ens-name>.eth default-sender
sbt:eth-command-line> ethKeystoreWalletV3Validate
Just as in this tutorial, items in angle braces are description of what you might type, rather than things that you literally should type. The first two items are telling you that you could type out (or really paste) the full, long hex address, you could provide an ens name that is bound to an address. Or, you could literally write default-sender
, which you will note is not in angle braces.
But don’t type default-sender
. Stay foolish, be lazy. Just type d<tab>
and let sbt-ethereum
do the typing for you!
Hopefully we’ve tab-completed our way to this command:
sbt:eth-command-line> ethKeystoreWalletV3Validate default-sender
Now hit <return>
, and follow the instructions. Let’s hope you remember the passphrase you provided for your defaultSender
address!
sbt:eth-command-line> ethKeystoreWalletV3Validate default-sender
[info] V3 wallet(s) found for '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2' (aliases ['default-sender'])
Enter passphrase or hex private key for address '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2': ***************
[info] A wallet for address '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2' is valid and decodable with the credential supplied.
[success] Total time: 7 s, completed Dec 9, 2018 11:22:46 PM
Hooray! We’ve successfully run an sbt-ethereum command, and (hopefully!) successfully validated our wallet.
With far less fanfare, let’s try running the command ethKeystoreList
. Again, we don’t want to actually type it out. It’s looong! We’re lazy! Just type ethK<tab>L<tab>
. If you type tab again, you’ll see this one doesn’t take any arguments. (The available completions are just / ::
, which refer to ways of qualifying sbt tasks that are not likely to be useful to you.) So, just hit <return>
!
sbt:eth-command-line> ethKeystoreList
+--------------------------------------------+
| Keystore Addresses |
+--------------------------------------------+
| 0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2 | <-- default-sender
+--------------------------------------------+
[success] Total time: 0 s, completed Dec 9, 2018 11:23:25 PM
sbt-ethereum knows about precisely one address, to which has been linked the special address alias default-sender
. You can define as many address aliases as you want for any ethereum address. Type ethA<tab>A<tab><tab>
to see the relevant commands, which all begin with ethAddressAlias
.
Your wallets and address aliases, as well as other information such as your transaction history, smart contract compilations, and ABIs of contracts you interact with are stored in the sbt-ethereum “shoebox”, which you will want to get into the habit of backing up. So, using our usual tab-completion skillz, let’s run the command ethShoeboxBackup
(which takes no arguments).
The command is interactive. It will ask us for a directory in which we like to store backups (which will be retained — in the shoebox! — for optional future reuse). Provide a directory appropriate to your system. You may want to backup to a thumbdrive or external disk, so if something happens to your computer’s hard disk, you have a copy elsewhere.
sbt:eth-command-line> ethShoeboxBackup
[warn] No default shoebox backup directory has been selected. Please select a new shoebox backup directory.
Enter the path of the directory into which you wish to create a backup: /Volumes/thumbdrive/sbt-ethereum-backups
Use directory '/Volumes/thumbdrive/sbt-ethereum-backups' as the default sbt-ethereum shoebox backup directory? [y/n] y
[info] Creating SQL dump of sbt-ethereum shoebox database...
[info] Successfully created SQL dump of the sbt-ethereum shoebox database: '/Users/testuser/Library/Application Support/sbt-ethereum/database/h2-dumps/sbt-ethereum-v7-20181204T12h55m24s459msPST.sql'
[info] Backing up sbt-ethereum shoebox. Reinstallable compilers will be excluded.
[info] sbt-ethereum shoebox successfully backed up to '/Volumes/thumbdrive/sbt-ethereum-backups/sbt-ethereum-shoebox-backup-20181204T12h55m24s475msPST.zip'.
Voila! We have backed up our shoebox. Verify that there is a backup file in the directory you have selected. We can restore our shoebox, if something bad happens, from the generated file using ethShoeboxRestore
. (Try it if you like! It’s easy!)
If you lose the wallet files in your shoebox, or the passcodes that unlock them, you will lose any money or value in them, irrecoverably and forever!
sbt-ethereum stores wallet files in its internal shoebox directory. You can back up that directory by hand, or use the command ethShoeboxBackup
.
sbt-ethereum does not store the passcodes that unlock these wallets. You need to store these yourself, preferably somewhere offline, and be sure not to lose them.
If you lose either one of a wallet file or its passcode, all of the value stored in that wallet’s associated address will likely be lost forever.
Ouch.
So far, neither of the commands that we’ve tried have needed to communicate with a node on the Ethereum network. Let’s try that now. (Don’t type it out! Use the tab completion, Luke.)
sbt:eth-command-line> ethNodeBlockNumberPrint
It might work! If it does work, you should see something like this:
sbt:eth-command-line> ethNodeBlockNumberPrint
[info] The current blocknumber is 6859638, according to node at 'https://ethnode.somewhere.com/'.
[success] Total time: 0 s, completed Dec 9, 2018 11:44:09 PM
But it might not work! In which case, you will see something like this:
sbt:eth-command-line> ethNodeBlockNumberPrint
[error] java.net.UnknownHostException: ethnode.somewhere.com: nodename nor servname provided, or not known
[error] at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method)
[error] at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:928)
[error] at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1323)
[error] at java.net.InetAddress.getAllByName0(InetAddress.java:1276)
[error] at java.net.InetAddress.getAllByName(InetAddress.java:1192)
[error] at java.net.InetAddress.getAllByName(InetAddress.java:1126)
[error] at org.eclipse.jetty.util.SocketAddressResolver$Async.lambda$resolve$1(SocketAddressResolver.java:167)
[error] at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:762)
[error] at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:680)
[error] at java.lang.Thread.run(Thread.java:748)
[error] (Compile / ethNodeBlockNumberPrint) java.net.UnknownHostException: ethnode.somewhere.com: nodename nor servname provided, or not known
[error] Total time: 0 s, completed Dec 9, 2018 11:40:17 PM
Oops! Don’t panic!
You only have to do this once!
To communicate with the Ethereum network, you need to have access to a node, a piece of software somewhere on the internet that participates in the network and serves as your gateway. sbt-ethereum may (or may not!) have a built-in default URL, and it might (or might not!) even work. But you really, really shouldn’t rely upon that. You need access to a reliable node.
An easy way to get that access is via infura.io. Create a login, and then a “project”. (It doesn’t matter what the project is, call it “stinky-socks” for all I care. Infura will only give you a URL these days if you tell them you have a project.)
Inside your project you’ll see an “Endpoint” for MAINNET
. Copy it. (Click on the little “clipboard icon”.) You’ll have copied a URL, something like https://mainnet.infura.io/v3/353e8352f0782b827d72757dab9cc946
, except with different gibberish at the end. That (your version, not mine!) can be your personal URL into the Ethereum network.
To set-up sbt-ethereum to use URL, just run the following command (but use your own gosh-danged URL not mine!)
sbt:eth-command-line> ethNodeUrlDefaultSet https://mainnet.infura.io/v3/353e8352f0782b827d72757dab9cc946
[info] Successfully set default node json-rpc URL for chain with ID 1 to https://mainnet.infura.io/v3/353e8352f0782b827d72757dab9cc946
The value you set with ethNodeUrlDefaultSet
is persistent.
It will remain in your “shoebox” until you replace it with a different value, or you explicitly remove it with ethNodeUrlDefaultDrop
. You can always check your current default node URL with ethNodeUrlDefaultPrint
. Try it!
Now you should be able to very reliably connect to a node:
sbt:eth-command-line> ethNodeBlockNumberPrint
[info] The current blocknumber is 6859824, according to node at 'https://mainnet.infura.io/v3/353e8352f0782b827d72757dab9cc946'.
[success] Total time: 0 s, completed Dec 10, 2018 12:31:24 AM
The “block number” that we have printed is useful information. It’s like the ticking clock of the Ethereum network, and you always want it to be current.
You can check websites like etherscan or ethstats to make sure that your blocknumber is close to theirs. (The block number changes every fifteen seconds or so, don’t worry if your the numbers are off by one or two.)
If the node you are working with — the one behind your URL — falls behind, it can lead to strange misbehavior. In particular, you will be looking “back in time”, at old data. This can lead to heart attacks, when, for example, you transfer money to an address but it seems not to show up, because you don’t realize you are looking at yesterday’s numbers.
Did you get a block number? Hooray! You are successfully connecting to a node.
Any time you are at an sbt-ethereum command line, you can get a quick glance at your current session’s status, just by typing eth
. Your session is usually associated with a sender address (if you sent a transaction, who would you be?), a chain ID, a node URL, and potentially a bunch of overrides that replace sbt-ethereum’s default behavior, just for this session.
Let’s try it.
> eth
[info] The session is now active on chain with ID 1, with node URL 'https://ethjsonrpc.mchange.com/'.
[info] The current session sender is '0x1144f4f7aad0c463c667e0f8d73fc13f1e7e86a2' (with aliases ['default-sender'] on chain with ID 1).
[success] Total time: 0 s, completed Oct 8, 2019, 3:21:46 PM
Kewl.