Seth: A Blockchain CLI Tool
09 Apr 2024Seth is a very useful command line interface (CLI) tool from DappTools that is useful for poking around the blockchain. Seth pulls a lot of the same blockchain data that Etherscan does, and a few small pieces of data come from the Etherscan API. But the CLI tool format makes it easy to include seth for scripting and automation purposes. Let’s see what it can do.
Installing Seth
To use seth, you need to install dapptools. The official instructions using Nix are quite straightforward, so I defer to them. The other important setup step is to set up the “ETH_RPC_URL” environment variable with an RPC endpoint. You can create a free account with Alchemy or Infura to get your own RPC endpoint URL.
Fetching Contract Source Code
Seth connects to the Etherscan API to provide a simple way to download contract source code. If you prefer reading code in your local editor like VS Code instead of reading from etherscan or etherscan.deth.net, this one’s for you. First make sure you have a free Etherscan account so you can get an Etherscan API key. Then store the Etherscan API key in your ETHERSCAN_API_KEY environment variable, because this variable name is where seth is configured to look for the key. Afterwards, you can download any contract source code if the code is verified on Etherscan. For example, to download the WETH contract source code, use the following (and wait a bit if the Etherscan API is slow):
seth etherscan-source 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | jq .SourceCode -r
To show the potential of packaging this single feature in a CLI, you could use this feature to build a bug bounty automation platform. Although such a system is common for web2 bug bounties, no public example of this type of project for web3 has been published yet. If you want to start building, the system could consist of:
- A project like changedetection.io to monitor the list of bug bounty target contracts from Immunefi, Hackenproof, Hats.finance, or other another website
- When a change is detected, either when a new project is added or when a new contract is added to the scope of an existing project, download the source code of the contract using seth
- Run your code analysis process on the new contract source code (hint: this step is the hard part of the whole process)
- Receive automated alerts from your system when it has found something (for an easy option, consider the poor man’s monitoring setup)
- Submit findings to bounty platforms & profit
Zooming In On The Latest Block
We can analyze individual blocks with the seth block <block number>
command and the most recent block number is queried with seth block-number
. We can chain these two commands together and receive data about the most recent block with seth block $(seth block-number)
, which gives output like:
baseFeePerGas 28841430027
difficulty 13016799566303853
extraData 1936027442
gasLimit 30000000
gasUsed 8255948
hash 0xb22491dfccef3296e776d94f6ddb738a6f9a6738917589dced7488c4de11a07c
logsBloom 0xa424015a83a6c914902a74028012293d801080081d1100290209810b5401212514b00102c4060040066470c021088d0102a48970ca203a8034808ad0d332298f0043024803445128c85a141e9801442814a5100f0c5192803c5aa421c0331b0166c0888406225412e044d00900a1cc5c265e442924081fc08004533901081050451021610400104a848921410191004386480001c580449b0260ab4c4a1418300280011b2caa2c290e80efd89d000206020c820044b026200b10c02d906c4c31500044020d8008a203901c0046c1400428185600038b52581c52a12e064220004a39252b50081140a19602084d34488400d281512394c050004088a24c207007
miner 0x3ecef08d0e2dad803847e052249bb4f8bff2d5bb
mixHash 0x38133a0f95886cdae8fa9da3055dc409b5677398cde2de386c135c2186959df1
nonce 5683548311596532688
number 14558180
parentHash 0x8981b0db340d5ce814fd3e4d4004d8072078b2e32878cd6cbbef7bc4f40f49aa
receiptsRoot 0x02132c612be2bf5d7d218a94e0e80d2054bb436bd9acd3cfd9bf6f2bda8c03a6
sha3Uncles 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
size 57834
stateRoot 0xcce2182138ef57aca2595ef47272939f385f172300e2d2c5f817e9c06857e5d7
timestamp 1649596875
totalDifficulty 0x9be29f08d671e01090e
transactions ["0xe99c8f16acfe4c30ea1d62fc009c52abda866a209265fd0231125b3f5829bd41","0x0607ceb4e5fccf32785905accd2e0b5337f24e756a64d5b9f71b1bc88763971e","0xa56fdefd60068407dce5b794d6e35fd6175fd4afbe118a4adc4cb4145b737f6e","0x53a31f2dea2e57094185a4e6eba9d56f743a4b9acc66e452904f6eeef69e78d9",
...truncated transactions list...
"0x0572abcfdcf3df03e47cfdae391bc6c27daf9a2e1636a78601a6f926ed1a6cb7","0xafaeb7f5caf3458ef0531763642511df8b73078d6d1bd2352977e552d304a4fe","0x75ce9178abcdcd92f30549524a259594cd85d13f0aacfa45100896d769573aca"]
transactionsRoot 0xf5c242a4f103b556008866961ab0962982d26f62420fd3a6aa512c6f454379e9
uncles []
The data for this block lists the transactions, gasLimit, gasUsed, and other data about the block. To examine the first transaction in the list, use seth tx 0xe99c8f16acfe4c30ea1d62fc009c52abda866a209265fd0231125b3f5829bd41
.
accessList [{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","storageKeys":["0x14edb3f2ae9217ac1062a3663ff296e62d08cb5e4c1652d16c28cc4aede04f59","0xa16f6733e9a6e575701fc996707206adb879e7981d0e768b0d54351c053ccb2b"]},{"address":"0xcb9fe6b9f0411ace824d8bf4bba29f03387ff459","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000008","0x9b637a02e6f8cc8aa1e3935c0b27bde663b11428c7707039634076a3fb8a0c48","0x855faaa6e0188a0cdf37dd9705e806a3371b9f2e5713754b396eb30e702b980a","0x855faaa6e0188a0cdf37dd9705e806a3371b9f2e5713754b396eb30e702b9807","0x0000000000000000000000000000000000000000000000000000000000000004","0x855faaa6e0188a0cdf37dd9705e806a3371b9f2e5713754b396eb30e702b9808","0x855faaa6e0188a0cdf37dd9705e806a3371b9f2e5713754b396eb30e702b9809","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000"]},{"address":"0x6f80310ca7f2c654691d1383149fa1a57d8ab1f8","storageKeys":["0xb3fae41a68fb6884ab6ce157e11f00eb8a737b57cf3936718e61999fd88b0185","0x20f0618767b04ed4c26dde2cc235e173c785c75b16fd723e08eacce0b2479536","0xfe3b247b4660499a8b482ec4c25c60ab4e77c2245c27088eb7b038386c6a9842","0xfa52f1eec9dfd6d84689cfc1c47c7527cf9b4ed5dbbecc964f3259310912525b"]}]
blockHash 0xb22491dfccef3296e776d94f6ddb738a6f9a6738917589dced7488c4de11a07c
blockNumber 14558180
chainId 1
from 0x5aa17fc7f2950eca85376c3a8cb1509e8e4b39df
gas 179307
gasPrice 28841430027
hash 0xe99c8f16acfe4c30ea1d62fc009c52abda866a209265fd0231125b3f5829bd41
input 0x040012de32ce7fb9c00000000000012346efe5f60f679dbbcb9fe6b9f0411ace824d8bf4bba29f03387ff45913432bb9cc0cb5cd04abdb5c35b7ecd2c4597192a147a3fd138dd38d65064a95
maxFeePerGas 28841430027
maxPriorityFeePerGas 0
nonce 48597
r 0x6a88a78634e50895ea891d5561a9ab1d03d83fcb1c8c567d80bb14940cee83b6
s 0x771e073dc88668ab4d6107b11897e58af131fa478addcb244ea7e33c5ce773bf
to 0x01ff6318440f7d5553a82294d78262d5f5084eff
transactionIndex 0
type 2
v 0
value 14558180
This data is a bit hard for a human to decrypt on the spot (though maybe not for you, anon), so we can turn to our friend ethtx.info to examine this transaction more closely. If we compare the seth data above to the screenshot below, we find the seth “from” field matches the ethtx.info “Sender” field, the seth “to” field matches the ethtx.info “Receiver” field, etc. The list of values in the seth “accessList” row refer to the state variables stored at those addresses. This is easier to see with the 2nd address in the seth “accessList” row, where storage slot values of 0x2, 0x8, 0x4, 0x1, and 0x0 exist. The “accessList” values that contain a large hex value likely the hashes of functions, mappings, or other data. We can retrieve the data in these storage slots using seth.
To see the value stored in storage slot 2 of address 0xcb9fe6b9f0411ace824d8bf4bba29f03387ff459, use seth --to-dec $(seth storage 0xcb9fe6b9f0411ace824d8bf4bba29f03387ff459 2)
. The value returned is identical to the value of the feeGrowthGlobal1x128 uint256 value visible on Etherscan. While it’s often easier for a human to browse Etherscan where the data is human readable, seth provides the data in a more machine-readable format for scripting and automation purposes. Additionally, when Etherscan is unavailable, using seth to connect directly to an RPC endpoint can provide an alternative way to get the same information.
Switching chains
Seth can easily query data from Ethereum testnet chains if you change the JSON RPC endpoint in the ETH_RPC_URL environment variable to an endpoint for your preferred blockchain.
Real-World Examples
The seth documentation one-pager has a long list of CLI flags and arguments with descriptions, but I think it’s more fun to summarize seth features with real-world use cases:
- To check the keccak hash of a value, use
seth keccak "totalSupply()"
- Forgot the value of
type(uint32).max
ortype(int32).max
? Useseth --to-dec $(seth --max-uint 32)
andseth --to-dec $(seth --max-int 32)
- To solve Ethernaut level 8 by performing a storage slot lookup, first set your “ETH_RPC_URL” environment variable to a rinkeby RPC endpoint (Infura or Alchemix provide endpoints if you make a free account), then run this one-liner:
seth --to-ascii $(seth storage 0xB32Ef1e8b55FE270Bc665F52076bc677B0D02f8f 1)
- To check the balance of vitalik.eth in ETH units (Note: this requires Etherscan API key for ENS address resolution)
seth --from-wei $(seth balance $(seth resolve-name vitalik.eth))
- Query the totalSupply of WETH and convert the return hex value to decimal, then convert the decimal value from wei units to ether units
seth --to-dec $(seth call 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 "totalSupply()")
- Querying the nonce for an address should return the number of transactions this address has sent using. To check this value, use
seth nonce 0x5aa17fc7f2950eca85376c3a8cb1509e8e4b39df
. At the time of writing, the output was 48603. - You can manually perform the same contract verification process that Etherscan does to validate that the source code of a contract matches the deployed bytecode. First, download the deployed bytecode of a contract using
seth code <address>
. Second, download the contract source code using the method from before ofseth etherscan-source <address> | jq .SourceCode -r
. Finally, compile the contract code usingsolc --bin --optimize filename.sol
(replace the filename with the filename you chose for the local copy of the contract code). You can then diff the hex binary output of the locally compiled code with the contract’s hex binary data downloaded using seth. One open question I had after trying this out is why the hex binary value of a contract viewed on Etherscan is different from the hex binary value I downloaded using seth. Both values came from the same blockchain, so what gives? If you find the answer, let me know and I can add an explanation here. seth send
is very useful when you want to send transactions to the blockchain. See the official documentation.
There are many further features of seth, but to avoid rehashing the official documentation, I want to keep this short. Lastly, seth has many further use cases when paired with the rest of the dapptools suite, including hevm, dapp, and ethsign.
Concluding Remarks
If you’ve used Etherscan a lot, you’ll notice you can get similar information using seth. This makes sense, because both tools query data from the same blockchain, so there should be a lot of overlap. If you prefer working in the terminal to working in a web browser, seth is a great option. And if you are building an automated pipeline that needs blockchain data, seth is a great tool for this use case.